mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Cleanup and document double
This commit is contained in:
parent
7e65d94cee
commit
d7f8c2b958
1 changed files with 30 additions and 52 deletions
|
@ -1,7 +1,13 @@
|
||||||
require "./unexpected_message"
|
require "./unexpected_message"
|
||||||
|
|
||||||
module Spectator
|
module Spectator
|
||||||
|
# Stands in for an object for testing that a SUT calls expected methods.
|
||||||
|
#
|
||||||
|
# Handles all messages (method calls), but only responds to those configured.
|
||||||
|
# Methods called that were not configured will raise `UnexpectedMessage`.
|
||||||
class Double(Messages)
|
class Double(Messages)
|
||||||
|
# Creates a double with pre-configures responses.
|
||||||
|
# A *name* can be provided, otherwise it is considered an anonymous double.
|
||||||
def initialize(@name : String? = nil, **@messages : **Messages)
|
def initialize(@name : String? = nil, **@messages : **Messages)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -14,61 +20,17 @@ module Spectator
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: Define macro to redefine a type's method.
|
# Redefines all methods on a type to conditionally respond to messages.
|
||||||
|
# Methods will raise `UnexpectedMessage` if they're called when they shouldn't be.
|
||||||
|
# Otherwise, they'll return the configured response.
|
||||||
# TODO: Better error for type mismatch
|
# TODO: Better error for type mismatch
|
||||||
macro finished
|
private macro _spectator_mask_methods(type_name)
|
||||||
# Object
|
{% type = type_name.resolve %}
|
||||||
{% for meth in @type.superclass.superclass.methods %}
|
{% if type.superclass %}
|
||||||
{% if !meth.abstract? && !DSL::RESERVED_KEYWORDS.includes?(meth.name.symbolize) %}
|
_spectator_mask_methods({{type.superclass}})
|
||||||
{% if meth.visibility != :public %}{{meth.visibility.id}}{% end %} def {{meth.receiver}}{{meth.name}}(
|
|
||||||
{{meth.args.splat}}{% if !meth.args.empty? %}, {% end %}
|
|
||||||
{% if meth.double_splat %}**{{meth.double_splat}}, {% end %}
|
|
||||||
{% if meth.block_arg %}&{{meth.block_arg}}{% elsif meth.accepts_block? %}&{% end %}
|
|
||||||
){% if meth.return_type %} : {{meth.return_type}}{% end %}{% if !meth.free_vars.empty? %} forall {{meth.free_vars.splat}}{% end %}
|
|
||||||
\{% if type = Messages[{{meth.name.symbolize}}] %}
|
|
||||||
{% if meth.return_type %}
|
|
||||||
\{% if type <= {{meth.return_type}} %}
|
|
||||||
@messages[{{meth.name.symbolize}}].as({{meth.return_type}})
|
|
||||||
\{% else %}
|
|
||||||
raise "Type mismatch {{meth.name}} : {{meth.return_type}}"
|
|
||||||
\{% end %}
|
|
||||||
{% else %}
|
|
||||||
@messages[{{meth.name.symbolize}}]
|
|
||||||
{% end %}
|
|
||||||
\{% else %}
|
|
||||||
raise UnexpectedMessage.new("#{_spectator_double_name} received unexpected message :{{meth.name}} (masking ancestor) with (<TODO: ARGS>).")
|
|
||||||
\{% end %}
|
|
||||||
end
|
|
||||||
{% end %}
|
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
# Reference
|
{% for meth in type.methods %}
|
||||||
{% for meth in @type.superclass.methods %}
|
|
||||||
{% if !meth.abstract? && !DSL::RESERVED_KEYWORDS.includes?(meth.name.symbolize) %}
|
|
||||||
{% if meth.visibility != :public %}{{meth.visibility.id}}{% end %} def {{meth.receiver}}{{meth.name}}(
|
|
||||||
{{meth.args.splat}}{% if !meth.args.empty? %}, {% end %}
|
|
||||||
{% if meth.double_splat %}**{{meth.double_splat}}, {% end %}
|
|
||||||
{% if meth.block_arg %}&{{meth.block_arg}}{% elsif meth.accepts_block? %}&{% end %}
|
|
||||||
){% if meth.return_type %} : {{meth.return_type}}{% end %}{% if !meth.free_vars.empty? %} forall {{meth.free_vars.splat}}{% end %}
|
|
||||||
\{% if type = Messages[{{meth.name.symbolize}}] %}
|
|
||||||
{% if meth.return_type %}
|
|
||||||
\{% if type <= {{meth.return_type}} %}
|
|
||||||
@messages[{{meth.name.symbolize}}].as({{meth.return_type}})
|
|
||||||
\{% else %}
|
|
||||||
raise "Type mismatch {{meth.name}} : {{meth.return_type}}"
|
|
||||||
\{% end %}
|
|
||||||
{% else %}
|
|
||||||
@messages[{{meth.name.symbolize}}]
|
|
||||||
{% end %}
|
|
||||||
\{% else %}
|
|
||||||
raise UnexpectedMessage.new("#{_spectator_double_name} received unexpected message :{{meth.name}} (masking ancestor) with (<TODO: ARGS>).")
|
|
||||||
\{% end %}
|
|
||||||
end
|
|
||||||
{% end %}
|
|
||||||
{% end %}
|
|
||||||
|
|
||||||
# Double
|
|
||||||
{% for meth in @type.methods %}
|
|
||||||
{% if !meth.abstract? && !DSL::RESERVED_KEYWORDS.includes?(meth.name.symbolize) %}
|
{% if !meth.abstract? && !DSL::RESERVED_KEYWORDS.includes?(meth.name.symbolize) %}
|
||||||
{% if meth.visibility != :public %}{{meth.visibility.id}}{% end %} def {{meth.receiver}}{{meth.name}}(
|
{% if meth.visibility != :public %}{{meth.visibility.id}}{% end %} def {{meth.receiver}}{{meth.name}}(
|
||||||
{{meth.args.splat}}{% if !meth.args.empty? %}, {% end %}
|
{{meth.args.splat}}{% if !meth.args.empty? %}, {% end %}
|
||||||
|
@ -78,14 +40,21 @@ module Spectator
|
||||||
\{% if type = Messages[{{meth.name.symbolize}}] %}
|
\{% if type = Messages[{{meth.name.symbolize}}] %}
|
||||||
{% if meth.return_type %}
|
{% if meth.return_type %}
|
||||||
\{% if type <= {{meth.return_type}} %}
|
\{% if type <= {{meth.return_type}} %}
|
||||||
|
# Return type appears to match configured type.
|
||||||
|
# Respond with configured value.
|
||||||
@messages[{{meth.name.symbolize}}].as({{meth.return_type}})
|
@messages[{{meth.name.symbolize}}].as({{meth.return_type}})
|
||||||
\{% else %}
|
\{% else %}
|
||||||
|
# Return type doesn't match configured type.
|
||||||
|
# Can't return the configured response as the type mismatches (won't compile).
|
||||||
|
# Raise at runtime to provide additional information.
|
||||||
raise "Type mismatch {{meth.name}} : {{meth.return_type}}"
|
raise "Type mismatch {{meth.name}} : {{meth.return_type}}"
|
||||||
\{% end %}
|
\{% end %}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
# No return type restriction, return configured response.
|
||||||
@messages[{{meth.name.symbolize}}]
|
@messages[{{meth.name.symbolize}}]
|
||||||
{% end %}
|
{% end %}
|
||||||
\{% else %}
|
\{% else %}
|
||||||
|
# Response not configured for this method/message.
|
||||||
raise UnexpectedMessage.new("#{_spectator_double_name} received unexpected message :{{meth.name}} (masking ancestor) with (<TODO: ARGS>).")
|
raise UnexpectedMessage.new("#{_spectator_double_name} received unexpected message :{{meth.name}} (masking ancestor) with (<TODO: ARGS>).")
|
||||||
\{% end %}
|
\{% end %}
|
||||||
end
|
end
|
||||||
|
@ -93,10 +62,19 @@ module Spectator
|
||||||
{% end %}
|
{% end %}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# "Hide" existing methods and methods from ancestors by overriding them.
|
||||||
|
macro finished
|
||||||
|
_spectator_mask_methods({{@type.name(generic_args: false)}})
|
||||||
|
end
|
||||||
|
|
||||||
|
# Handle all methods but only respond to configured messages.
|
||||||
|
# Raises an `UnexpectedMessage` error for non-configures messages.
|
||||||
macro method_missing(call)
|
macro method_missing(call)
|
||||||
\{% if Messages.keys.includes?({{call.name.symbolize}}.id) %}
|
\{% if Messages.keys.includes?({{call.name.symbolize}}.id) %}
|
||||||
|
# Return configured response.
|
||||||
@messages[{{call.name.symbolize}}]
|
@messages[{{call.name.symbolize}}]
|
||||||
\{% else %}
|
\{% else %}
|
||||||
|
# Response not configured for this method/message.
|
||||||
raise UnexpectedMessage.new("#{_spectator_double_name} received unexpected message :{{call.name}} with (<TODO: ARGS>).")
|
raise UnexpectedMessage.new("#{_spectator_double_name} received unexpected message :{{call.name}} with (<TODO: ARGS>).")
|
||||||
\{% end %}
|
\{% end %}
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue