mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Rip out mocks
This commit is contained in:
parent
4e7318b964
commit
d6112bd2dd
26 changed files with 4 additions and 1086 deletions
|
@ -1,183 +0,0 @@
|
||||||
require "../mocks"
|
|
||||||
|
|
||||||
module Spectator::DSL
|
|
||||||
module Mocks
|
|
||||||
macro double(name = "Anonymous", **stubs, &block)
|
|
||||||
{% if name.is_a?(StringLiteral) || name.is_a?(StringInterpolation) %}
|
|
||||||
anonymous_double({{name}}, {{stubs.double_splat}})
|
|
||||||
{% else %}
|
|
||||||
{%
|
|
||||||
safe_name = name.id.symbolize.gsub(/\W/, "_").id
|
|
||||||
type_name = "Double#{safe_name}".id
|
|
||||||
%}
|
|
||||||
|
|
||||||
{% if block %}
|
|
||||||
define_double({{type_name}}, {{name}}, {{stubs.double_splat}}) {{block}}
|
|
||||||
{% else %}
|
|
||||||
create_double({{type_name}}, {{name}}, {{stubs.double_splat}})
|
|
||||||
{% end %}
|
|
||||||
{% end %}
|
|
||||||
end
|
|
||||||
|
|
||||||
macro create_double(type_name, name, **stubs)
|
|
||||||
{% if type_name.resolve? %}
|
|
||||||
{{type_name}}.new.tap do |%double|
|
|
||||||
{% for name, value in stubs %}
|
|
||||||
allow(%double).to receive({{name.id}}).and_return({{value}})
|
|
||||||
{% end %}
|
|
||||||
end
|
|
||||||
{% elsif @def %}
|
|
||||||
anonymous_double({{name ? name.stringify : "Anonymous"}}, {{stubs.double_splat}})
|
|
||||||
{% else %}
|
|
||||||
{% raise "Block required for double definition" %}
|
|
||||||
{% end %}
|
|
||||||
end
|
|
||||||
|
|
||||||
macro define_double(type_name, name, **stubs, &block)
|
|
||||||
{% begin %}
|
|
||||||
{% if (name.is_a?(Path) || name.is_a?(Generic)) && (resolved = name.resolve?) %}
|
|
||||||
verify_double({{name}})
|
|
||||||
class {{type_name}} < ::Spectator::Mocks::VerifyingDouble(::{{resolved.id}})
|
|
||||||
{% else %}
|
|
||||||
class {{type_name}} < ::Spectator::Mocks::Double
|
|
||||||
def initialize(null = false)
|
|
||||||
super({{name.id.stringify}}, null)
|
|
||||||
end
|
|
||||||
{% end %}
|
|
||||||
|
|
||||||
def as_null_object
|
|
||||||
{{type_name}}.new(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: Do something with **stubs?
|
|
||||||
|
|
||||||
{{block.body}}
|
|
||||||
end
|
|
||||||
{% end %}
|
|
||||||
end
|
|
||||||
|
|
||||||
def anonymous_double(name = "Anonymous", **stubs)
|
|
||||||
::Spectator::Mocks::AnonymousDouble.new(name, stubs)
|
|
||||||
end
|
|
||||||
|
|
||||||
macro null_double(name, **stubs, &block)
|
|
||||||
{% if name.is_a?(StringLiteral) || name.is_a?(StringInterpolation) %}
|
|
||||||
anonymous_null_double({{name}}, {{stubs.double_splat}})
|
|
||||||
{% else %}
|
|
||||||
{%
|
|
||||||
safe_name = name.id.symbolize.gsub(/\W/, "_").id
|
|
||||||
type_name = "Double#{safe_name}".id
|
|
||||||
%}
|
|
||||||
|
|
||||||
{% if block.is_a?(Nop) %}
|
|
||||||
create_null_double({{type_name}}, {{name}}, {{stubs.double_splat}})
|
|
||||||
{% else %}
|
|
||||||
define_null_double({{type_name}}, {{name}}, {{stubs.double_splat}}) {{block}}
|
|
||||||
{% end %}
|
|
||||||
{% end %}
|
|
||||||
end
|
|
||||||
|
|
||||||
macro create_null_double(type_name, name, **stubs)
|
|
||||||
{% type_name.resolve? || raise("Could not find a double labeled #{name}") %}
|
|
||||||
|
|
||||||
{{type_name}}.new(true).tap do |%double|
|
|
||||||
{% for name, value in stubs %}
|
|
||||||
allow(%double).to receive({{name.id}}).and_return({{value}})
|
|
||||||
{% end %}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
macro define_null_double(type_name, name, **stubs, &block)
|
|
||||||
class {{type_name}} < ::Spectator::Mocks::Double
|
|
||||||
def initialize(null = true)
|
|
||||||
super({{name.id.stringify}}, null)
|
|
||||||
end
|
|
||||||
|
|
||||||
def as_null_object
|
|
||||||
{{type_name}}.new(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: Do something with **stubs?
|
|
||||||
|
|
||||||
{{block.body}}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def anonymous_null_double(name = "Anonymous", **stubs)
|
|
||||||
::Spectator::Mocks::AnonymousNullDouble.new(name, stubs)
|
|
||||||
end
|
|
||||||
|
|
||||||
macro mock(name, &block)
|
|
||||||
{% resolved = name.resolve
|
|
||||||
type = if resolved < Reference
|
|
||||||
:class
|
|
||||||
elsif resolved < Value
|
|
||||||
:struct
|
|
||||||
else
|
|
||||||
:module
|
|
||||||
end %}
|
|
||||||
{% begin %}
|
|
||||||
{{type.id}} ::{{resolved.id}}
|
|
||||||
include ::Spectator::Mocks::Stubs
|
|
||||||
|
|
||||||
{{block.body}}
|
|
||||||
end
|
|
||||||
{% end %}
|
|
||||||
end
|
|
||||||
|
|
||||||
macro verify_double(name, &block)
|
|
||||||
{% resolved = name.resolve
|
|
||||||
type = if resolved < Reference
|
|
||||||
:class
|
|
||||||
elsif resolved < Value
|
|
||||||
:struct
|
|
||||||
else
|
|
||||||
:module
|
|
||||||
end %}
|
|
||||||
{% begin %}
|
|
||||||
{{type.id}} ::{{resolved.id}}
|
|
||||||
include ::Spectator::Mocks::Reflection
|
|
||||||
|
|
||||||
macro finished
|
|
||||||
_spectator_reflect
|
|
||||||
end
|
|
||||||
end
|
|
||||||
{% end %}
|
|
||||||
end
|
|
||||||
|
|
||||||
def allow(thing)
|
|
||||||
::Spectator::Mocks::Allow.new(thing)
|
|
||||||
end
|
|
||||||
|
|
||||||
def allow_any_instance_of(type : T.class) forall T
|
|
||||||
::Spectator::Mocks::AllowAnyInstance(T).new
|
|
||||||
end
|
|
||||||
|
|
||||||
macro expect_any_instance_of(type, _source_file = __FILE__, _source_line = __LINE__)
|
|
||||||
%location = ::Spectator::Location.new({{_source_file}}, {{_source_line}})
|
|
||||||
::Spectator::Mocks::ExpectAnyInstance({{type}}).new(%location)
|
|
||||||
end
|
|
||||||
|
|
||||||
macro receive(method_name, _source_file = __FILE__, _source_line = __LINE__, &block)
|
|
||||||
%location = ::Spectator::Location.new({{_source_file}}, {{_source_line}})
|
|
||||||
{% if block %}
|
|
||||||
::Spectator::Mocks::ProcMethodStub.create({{method_name.id.symbolize}}, %location) { {{block.body}} }
|
|
||||||
{% else %}
|
|
||||||
::Spectator::Mocks::NilMethodStub.new({{method_name.id.symbolize}}, %location)
|
|
||||||
{% end %}
|
|
||||||
end
|
|
||||||
|
|
||||||
macro receive_messages(_source_file = __FILE__, _source_line = __LINE__, **stubs)
|
|
||||||
%location = ::Spectator::Location.new({{_source_file}}, {{_source_line}})
|
|
||||||
%stubs = [] of ::Spectator::Mocks::MethodStub
|
|
||||||
{% for name, value in stubs %}
|
|
||||||
%stubs << ::Spectator::Mocks::ValueMethodStub.new({{name.id.symbolize}}, %location, {{value}})
|
|
||||||
{% end %}
|
|
||||||
%stubs
|
|
||||||
end
|
|
||||||
|
|
||||||
def no_args
|
|
||||||
::Spectator::Mocks::NoArguments.new
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -4,12 +4,6 @@ require "./system_exit"
|
||||||
module Spectator
|
module Spectator
|
||||||
# Functionality for mocking existing types.
|
# Functionality for mocking existing types.
|
||||||
module Mocks
|
module Mocks
|
||||||
def self.run(context : TestContext)
|
|
||||||
Registry.prepare(context)
|
|
||||||
yield
|
|
||||||
ensure
|
|
||||||
Registry.reset
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -17,8 +11,8 @@ end
|
||||||
# This captures *most* (technically not all) attempts to exit the process.
|
# This captures *most* (technically not all) attempts to exit the process.
|
||||||
# This stub only takes effect in example code.
|
# This stub only takes effect in example code.
|
||||||
# It intercepts `exit` calls and raises `Spectator::SystemExit` to prevent killing the test.
|
# It intercepts `exit` calls and raises `Spectator::SystemExit` to prevent killing the test.
|
||||||
class ::Process
|
# class ::Process
|
||||||
include ::Spectator::Mocks::Stubs
|
# include ::Spectator::Mocks::Stubs
|
||||||
|
|
||||||
stub self.exit(code) { raise ::Spectator::SystemExit.new }
|
# stub self.exit(code) { raise ::Spectator::SystemExit.new }
|
||||||
end
|
# end
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
require "./registry"
|
|
||||||
|
|
||||||
module Spectator::Mocks
|
|
||||||
struct Allow(T)
|
|
||||||
def initialize(@mock : T)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to(stub : MethodStub) : Nil
|
|
||||||
Harness.current.mocks.add_stub(@mock, stub)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to(stubs : Enumerable(MethodStub)) : Nil
|
|
||||||
stubs.each do |stub|
|
|
||||||
Harness.current.mocks.add_stub(@mock, stub)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,15 +0,0 @@
|
||||||
require "./registry"
|
|
||||||
|
|
||||||
module Spectator::Mocks
|
|
||||||
struct AllowAnyInstance(T)
|
|
||||||
def to(stub : MethodStub) : Nil
|
|
||||||
Harness.current.mocks.add_type_stub(T, stub)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to(stubs : Enumerable(MethodStub)) : Nil
|
|
||||||
stubs.each do |stub|
|
|
||||||
Harness.current.mocks.add_type_stub(T, stub)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,26 +0,0 @@
|
||||||
module Spectator::Mocks
|
|
||||||
class AnonymousDouble(T)
|
|
||||||
def initialize(@name : String, @values : T)
|
|
||||||
end
|
|
||||||
|
|
||||||
def as_null_object
|
|
||||||
AnonymousNullDouble.new(@name, @values)
|
|
||||||
end
|
|
||||||
|
|
||||||
macro method_missing(call)
|
|
||||||
args = ::Spectator::Mocks::GenericArguments.create({{call.args.splat}})
|
|
||||||
call = ::Spectator::Mocks::MethodCall.new({{call.name.symbolize}}, args)
|
|
||||||
::Spectator::Harness.current.mocks.record_call(self, call)
|
|
||||||
if (stub = ::Spectator::Harness.current.mocks.find_stub(self, call))
|
|
||||||
stub.call!(args) do
|
|
||||||
@values.fetch({{call.name.symbolize}}) { raise "Consistency error - method stubbed with no implementation"; nil }
|
|
||||||
end
|
|
||||||
else
|
|
||||||
@values.fetch({{call.name.symbolize}}) do
|
|
||||||
return nil if ::Spectator::Harness.current.mocks.expected?(self, call)
|
|
||||||
raise ::Spectator::Mocks::UnexpectedMessageError.new("#{self} received unexpected message {{call.name}}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,17 +0,0 @@
|
||||||
module Spectator::Mocks
|
|
||||||
class AnonymousNullDouble(T)
|
|
||||||
def initialize(@name : String, @values : T)
|
|
||||||
end
|
|
||||||
|
|
||||||
macro method_missing(call)
|
|
||||||
args = ::Spectator::Mocks::GenericArguments.create({{call.args.splat}})
|
|
||||||
call = ::Spectator::Mocks::MethodCall.new({{call.name.symbolize}}, args)
|
|
||||||
::Spectator::Harness.current.mocks.record_call(self, call)
|
|
||||||
if (stub = ::Spectator::Harness.current.mocks.find_stub(self, call))
|
|
||||||
stub.call!(args) { @values.fetch({{call.name.symbolize}}) { self } }
|
|
||||||
else
|
|
||||||
@values.fetch({{call.name.symbolize}}) { self }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,4 +0,0 @@
|
||||||
module Spectator::Mocks
|
|
||||||
abstract class Arguments
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,130 +0,0 @@
|
||||||
require "./generic_method_stub"
|
|
||||||
require "./method_call"
|
|
||||||
require "./unexpected_message_error"
|
|
||||||
|
|
||||||
module Spectator::Mocks
|
|
||||||
abstract class Double
|
|
||||||
def initialize(@spectator_double_name : String, @null = false)
|
|
||||||
end
|
|
||||||
|
|
||||||
private macro stub(definition, *types, return_type = :undefined, &block)
|
|
||||||
{%
|
|
||||||
name = nil
|
|
||||||
params = nil
|
|
||||||
args = nil
|
|
||||||
body = nil
|
|
||||||
if definition.is_a?(Call) # stub foo { :bar }
|
|
||||||
named = false
|
|
||||||
name = definition.name.id
|
|
||||||
params = definition.args
|
|
||||||
|
|
||||||
# Possibly a weird compiler bug, but syntax like this:
|
|
||||||
# stub instance.==(other) { true }
|
|
||||||
# Results in `other` being the call `other { true }`.
|
|
||||||
# This works around the issue by pulling out the block
|
|
||||||
# and setting the parameter to just the name.
|
|
||||||
if params.last.is_a?(Call)
|
|
||||||
body = params.last.block
|
|
||||||
params[-1] = params.last.name
|
|
||||||
end
|
|
||||||
|
|
||||||
args = params.map do |p|
|
|
||||||
n = p.is_a?(TypeDeclaration) ? p.var : p.id
|
|
||||||
r = named ? "#{n}: #{n}".id : n
|
|
||||||
named = true if n.starts_with?('*')
|
|
||||||
r
|
|
||||||
end
|
|
||||||
|
|
||||||
# The unless is here because `||=` can't be used in macros @_@
|
|
||||||
unless body
|
|
||||||
body = definition.block.is_a?(Nop) ? block : definition.block
|
|
||||||
end
|
|
||||||
elsif definition.is_a?(TypeDeclaration) # stub foo : Symbol
|
|
||||||
name = definition.var
|
|
||||||
params = [] of MacroId
|
|
||||||
args = [] of MacroId
|
|
||||||
body = block
|
|
||||||
elsif definition.is_a?(SymbolLiteral) # stub :foo, arg : Int32
|
|
||||||
name = definition.id
|
|
||||||
named = false
|
|
||||||
params = types
|
|
||||||
if params.last.is_a?(Call)
|
|
||||||
body = params.last.block
|
|
||||||
params[-1] = params.last.name
|
|
||||||
end
|
|
||||||
args = params.map do |p|
|
|
||||||
n = p.is_a?(TypeDeclaration) ? p.var : p.id
|
|
||||||
r = named ? "#{n}: #{n}".id : n
|
|
||||||
named = true if n.starts_with?('*')
|
|
||||||
r
|
|
||||||
end
|
|
||||||
body = block unless body
|
|
||||||
else
|
|
||||||
raise "Unrecognized stub format"
|
|
||||||
end
|
|
||||||
%}
|
|
||||||
|
|
||||||
def {{name}}({{params.splat}}){% if return_type.is_a?(ArrayLiteral) %} : {{return_type.type}}{% elsif return_type != :undefined %} : {{return_type.id}}{% elsif definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
|
|
||||||
%args = ::Spectator::Mocks::GenericArguments.create({{args.splat}})
|
|
||||||
%call = ::Spectator::Mocks::MethodCall.new({{name.symbolize}}, %args)
|
|
||||||
::Spectator::Harness.current.mocks.record_call(self, %call)
|
|
||||||
if (%stub = ::Spectator::Harness.current.mocks.find_stub(self, %call))
|
|
||||||
%stub.call!(%args) { %method({{args.splat}}) }
|
|
||||||
else
|
|
||||||
%method({{args.splat}})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def {{name}}({{params.splat}}){% if return_type.is_a?(ArrayLiteral) %} : {{return_type.type}}{% elsif return_type != :undefined %} : {{return_type.id}}{% elsif definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
|
|
||||||
%args = ::Spectator::Mocks::GenericArguments.create({{args.splat}})
|
|
||||||
%call = ::Spectator::Mocks::MethodCall.new({{name.symbolize}}, %args)
|
|
||||||
::Spectator::Harness.current.mocks.record_call(self, %call)
|
|
||||||
if (%stub = ::Spectator::Harness.current.mocks.find_stub(self, %call))
|
|
||||||
%stub.call!(%args) { %method({{args.splat}}) { |*%ya| yield *%ya } }
|
|
||||||
else
|
|
||||||
%method({{args.splat}}) do |*%yield_args|
|
|
||||||
yield *%yield_args
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def %method({{params.splat}}){% if return_type.is_a?(ArrayLiteral) %} : {{return_type.type}}{% elsif return_type != :undefined %} : {{return_type.id}}{% elsif definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
|
|
||||||
{% if body && !body.is_a?(Nop) %}
|
|
||||||
{{body.body}}
|
|
||||||
{% elsif return_type.is_a?(ArrayLiteral) %}
|
|
||||||
{{return_type.splat}}
|
|
||||||
{% else %}
|
|
||||||
%args = ::Spectator::Mocks::GenericArguments.create({{args.splat}})
|
|
||||||
%call = ::Spectator::Mocks::MethodCall.new({{name.symbolize}}, %args)
|
|
||||||
unless ::Spectator::Harness.current.mocks.expected?(self, %call)
|
|
||||||
raise ::Spectator::Mocks::UnexpectedMessageError.new("#{self} received unexpected message {{name}}")
|
|
||||||
end
|
|
||||||
|
|
||||||
# This code shouldn't be reached, but makes the compiler happy to have a matching return type.
|
|
||||||
{% if return_type != :undefined %}
|
|
||||||
%x = uninitialized {{return_type}}
|
|
||||||
{% elsif definition.is_a?(TypeDeclaration) %}
|
|
||||||
%x = uninitialized {{definition.type}}
|
|
||||||
{% else %}
|
|
||||||
nil
|
|
||||||
{% end %}
|
|
||||||
{% end %}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
macro method_missing(call)
|
|
||||||
args = ::Spectator::Mocks::GenericArguments.create({{call.args.splat}})
|
|
||||||
call = ::Spectator::Mocks::MethodCall.new({{call.name.symbolize}}, args)
|
|
||||||
::Spectator::Harness.current.mocks.record_call(self, call)
|
|
||||||
|
|
||||||
return self if @null
|
|
||||||
return self if ::Spectator::Harness.current.mocks.expected?(self, call)
|
|
||||||
|
|
||||||
raise ::Spectator::Mocks::UnexpectedMessageError.new("#{self} received unexpected message {{call.name}}")
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s(io)
|
|
||||||
io << "Double(" << @spectator_double_name << ')'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,14 +0,0 @@
|
||||||
require "./generic_arguments"
|
|
||||||
require "./generic_method_stub"
|
|
||||||
|
|
||||||
module Spectator::Mocks
|
|
||||||
class ExceptionMethodStub(ExceptionType) < GenericMethodStub(Nil)
|
|
||||||
def initialize(name, location, @exception : ExceptionType, args = nil)
|
|
||||||
super(name, location, args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(_args : GenericArguments(T, NT), &_original : -> RT) forall T, NT, RT
|
|
||||||
raise @exception
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,23 +0,0 @@
|
||||||
require "./registry"
|
|
||||||
|
|
||||||
module Spectator::Mocks
|
|
||||||
struct ExpectAnyInstance(T)
|
|
||||||
def initialize(@location : Location)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to(stub : MethodStub) : Nil
|
|
||||||
actual = Value.new(T)
|
|
||||||
Harness.current.mocks.expect(T, stub)
|
|
||||||
value = Value.new(stub.name, stub.to_s)
|
|
||||||
matcher = Matchers::ReceiveTypeMatcher.new(value, stub.arguments?)
|
|
||||||
target = Expectation::Target.new(actual, @location)
|
|
||||||
target.to_eventually(matcher)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to(stubs : Enumerable(MethodStub)) : Nil
|
|
||||||
stubs.each do |stub|
|
|
||||||
to(stub)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,41 +0,0 @@
|
||||||
require "./arguments"
|
|
||||||
|
|
||||||
module Spectator::Mocks
|
|
||||||
class GenericArguments(T, NT) < Arguments
|
|
||||||
protected getter args
|
|
||||||
protected getter opts
|
|
||||||
|
|
||||||
def initialize(@args : T, @opts : NT)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.create(*args, **opts)
|
|
||||||
GenericArguments.new(args, opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def pass_to(dispatcher)
|
|
||||||
dispatcher.call(*@args, **@opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def ===(other) : Bool
|
|
||||||
return false unless @args === other.args
|
|
||||||
return false unless @opts.size === other.opts.size
|
|
||||||
|
|
||||||
@opts.keys.all? do |key|
|
|
||||||
other.opts.has_key?(key) && @opts[key] === other.opts[key]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s(io)
|
|
||||||
@args.each_with_index do |arg, i|
|
|
||||||
arg.inspect(io)
|
|
||||||
io << ", " if i < @args.size - 1
|
|
||||||
end
|
|
||||||
io << ", " unless @args.empty? || @opts.empty?
|
|
||||||
@opts.each_with_index do |key, value, i|
|
|
||||||
io << key << ": "
|
|
||||||
value.inspect(io)
|
|
||||||
io << ", " if i < @opts.size - 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,26 +0,0 @@
|
||||||
require "./arguments"
|
|
||||||
require "./generic_arguments"
|
|
||||||
require "./method_call"
|
|
||||||
require "./method_stub"
|
|
||||||
|
|
||||||
module Spectator::Mocks
|
|
||||||
abstract class GenericMethodStub(ReturnType) < MethodStub
|
|
||||||
getter! arguments : Arguments
|
|
||||||
|
|
||||||
def initialize(name, location, @args : Arguments? = nil)
|
|
||||||
super(name, location)
|
|
||||||
end
|
|
||||||
|
|
||||||
def callable?(call : MethodCall) : Bool
|
|
||||||
super && (!@args || @args === call.args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s(io)
|
|
||||||
super(io)
|
|
||||||
if @args
|
|
||||||
io << '(' << @args << ')'
|
|
||||||
end
|
|
||||||
io << " : " << ReturnType << " at " << @location
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,13 +0,0 @@
|
||||||
module Spectator::Mocks
|
|
||||||
class MethodCall
|
|
||||||
getter name : Symbol
|
|
||||||
getter args : Arguments
|
|
||||||
|
|
||||||
def initialize(@name : Symbol, @args : Arguments)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s(io)
|
|
||||||
io << '#' << @name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,34 +0,0 @@
|
||||||
require "../location"
|
|
||||||
require "./method_call"
|
|
||||||
|
|
||||||
module Spectator::Mocks
|
|
||||||
abstract class MethodStub
|
|
||||||
getter name : Symbol
|
|
||||||
|
|
||||||
getter location : Location
|
|
||||||
|
|
||||||
def initialize(@name, @location)
|
|
||||||
end
|
|
||||||
|
|
||||||
def callable?(call : MethodCall) : Bool
|
|
||||||
@name == call.name
|
|
||||||
end
|
|
||||||
|
|
||||||
abstract def call(args : GenericArguments(T, NT), &original : -> RT) forall T, NT, RT
|
|
||||||
|
|
||||||
def call!(args : GenericArguments(T, NT), &original : -> RT) : RT forall T, NT, RT
|
|
||||||
value = call(args) { |*ya| yield *ya }
|
|
||||||
if value.is_a?(RT)
|
|
||||||
value.as(RT)
|
|
||||||
elsif value.nil? && RT == NoReturn
|
|
||||||
raise SystemExit.new
|
|
||||||
else
|
|
||||||
raise TypeCastError.new("The return type of stub #{self} doesn't match the expected type #{RT}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s(io)
|
|
||||||
io << '#' << @name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,19 +0,0 @@
|
||||||
require "./generic_arguments"
|
|
||||||
require "./generic_method_stub"
|
|
||||||
|
|
||||||
module Spectator::Mocks
|
|
||||||
class MultiValueMethodStub(ReturnType) < GenericMethodStub(ReturnType)
|
|
||||||
@index = 0
|
|
||||||
|
|
||||||
def initialize(name, location, @values : ReturnType, args = nil)
|
|
||||||
super(name, location, args)
|
|
||||||
raise ArgumentError.new("Values must have at least one item") if @values.size < 1
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(_args : GenericArguments(T, NT), &_original : -> RT) forall T, NT, RT
|
|
||||||
value = @values[@index]
|
|
||||||
@index += 1 if @index < @values.size - 1
|
|
||||||
value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,52 +0,0 @@
|
||||||
require "./generic_arguments"
|
|
||||||
require "./generic_method_stub"
|
|
||||||
require "./value_method_stub"
|
|
||||||
|
|
||||||
module Spectator::Mocks
|
|
||||||
class NilMethodStub < GenericMethodStub(Nil)
|
|
||||||
def call(_args : GenericArguments(T, NT), &_original : -> RT) forall T, NT, RT
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def and_return
|
|
||||||
self
|
|
||||||
end
|
|
||||||
|
|
||||||
def and_return(value)
|
|
||||||
ValueMethodStub.new(@name, @location, value, @args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def and_return(*values)
|
|
||||||
MultiValueMethodStub.new(@name, @location, values.to_a, @args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def and_raise(exception_type : Exception.class)
|
|
||||||
ExceptionMethodStub.new(@name, @location, exception_type.new, @args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def and_raise(exception : Exception)
|
|
||||||
ExceptionMethodStub.new(@name, @location, exception, @args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def and_raise(message : String)
|
|
||||||
ExceptionMethodStub.new(@name, @location, Exception.new(message), @args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def and_raise(exception_type : Exception.class, *args) forall T
|
|
||||||
ExceptionMethodStub.new(@name, @location, exception_type.new(*args), @args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def with(*args : *T, **opts : **NT) forall T, NT
|
|
||||||
args = GenericArguments.new(args, opts)
|
|
||||||
NilMethodStub.new(@name, @location, args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def with(args : Arguments)
|
|
||||||
NilMethodStub.new(@name, @location, @args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def and_call_original
|
|
||||||
OriginalMethodStub.new(@name, @location, @args)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,21 +0,0 @@
|
||||||
require "./arguments"
|
|
||||||
|
|
||||||
module Spectator::Mocks
|
|
||||||
class NoArguments < Arguments
|
|
||||||
def args
|
|
||||||
Tuple.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def opts
|
|
||||||
NamedTuple.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def ===(other : Arguments) : Bool
|
|
||||||
other.args.empty? && other.opts.empty?
|
|
||||||
end
|
|
||||||
|
|
||||||
def ===(other) : Bool
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,10 +0,0 @@
|
||||||
require "./generic_arguments"
|
|
||||||
require "./generic_method_stub"
|
|
||||||
|
|
||||||
module Spectator::Mocks
|
|
||||||
class OriginalMethodStub < GenericMethodStub(Nil)
|
|
||||||
def call(_args : GenericArguments(T, NT), &_original : -> RT) forall T, NT, RT
|
|
||||||
yield
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,18 +0,0 @@
|
||||||
require "./arguments"
|
|
||||||
require "./generic_method_stub"
|
|
||||||
|
|
||||||
module Spectator::Mocks
|
|
||||||
class ProcMethodStub(ReturnType) < GenericMethodStub(ReturnType)
|
|
||||||
def initialize(name, location, @proc : -> ReturnType, args = nil)
|
|
||||||
super(name, location, args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.create(name, location, args = nil, &block : -> T) forall T
|
|
||||||
ProcMethodStub.new(name, location, block, args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(_args : GenericArguments(T, NT), &_original : -> RT) forall T, NT, RT
|
|
||||||
@proc.call
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,26 +0,0 @@
|
||||||
require "../anything"
|
|
||||||
|
|
||||||
module Spectator::Mocks
|
|
||||||
module Reflection
|
|
||||||
private macro _spectator_reflect
|
|
||||||
{% for meth in @type.methods %}
|
|
||||||
%location = ::Spectator::Location.new({{meth.filename}}, {{meth.line_number}})
|
|
||||||
%args = ::Spectator::Mocks::GenericArguments.create(
|
|
||||||
{% for arg, i in meth.args %}
|
|
||||||
{% matcher = if arg.restriction
|
|
||||||
if arg.restriction == :self.id
|
|
||||||
@type.id
|
|
||||||
else
|
|
||||||
arg.restriction
|
|
||||||
end
|
|
||||||
else
|
|
||||||
"::Spectator::Anything.new".id
|
|
||||||
end %}
|
|
||||||
{{matcher}}{% if i < meth.args.size %},{% end %}
|
|
||||||
{% end %}
|
|
||||||
)
|
|
||||||
::Spectator::Mocks::TypeRegistry.add({{@type.id.stringify}}, {{meth.name.symbolize}}, %location, %args)
|
|
||||||
{% end %}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,110 +0,0 @@
|
||||||
module Spectator::Mocks
|
|
||||||
class Registry
|
|
||||||
alias Key = Tuple(String, UInt64)
|
|
||||||
|
|
||||||
private struct Entry
|
|
||||||
getter stubs = Deque(MethodStub).new
|
|
||||||
getter calls = Deque(MethodCall).new
|
|
||||||
getter expected = Set(MethodStub).new
|
|
||||||
end
|
|
||||||
|
|
||||||
@all_instances = {} of String => Entry
|
|
||||||
@entries = {} of Key => Entry
|
|
||||||
|
|
||||||
def reset : Nil
|
|
||||||
@entries.clear
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_stub(object, stub : MethodStub) : Nil
|
|
||||||
# Stubs are added in reverse order,
|
|
||||||
# so that later-defined stubs override previously defined ones.
|
|
||||||
fetch_instance(object).stubs.unshift(stub)
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_stub(type : T.class, stub : MethodStub) : Nil forall T
|
|
||||||
add_type_stub(type, stub)
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_type_stub(type, stub : MethodStub) : Nil
|
|
||||||
fetch_type(type).stubs.unshift(stub)
|
|
||||||
end
|
|
||||||
|
|
||||||
def stubbed?(object, method_name : Symbol) : Bool
|
|
||||||
fetch_instance(object).stubs.any? { |stub| stub.name == method_name } ||
|
|
||||||
fetch_type(object.class).stubs.any? { |stub| stub.name == method_name }
|
|
||||||
end
|
|
||||||
|
|
||||||
def find_stub(object, call : MethodCall)
|
|
||||||
fetch_instance(object).stubs.find(&.callable?(call)) ||
|
|
||||||
fetch_type(object.class).stubs.find(&.callable?(call))
|
|
||||||
end
|
|
||||||
|
|
||||||
def find_stub(type : T.class, call : MethodCall) forall T
|
|
||||||
fetch_type(type).stubs.find(&.callable?(call))
|
|
||||||
end
|
|
||||||
|
|
||||||
def record_call(object, call : MethodCall) : Nil
|
|
||||||
fetch_instance(object).calls << call
|
|
||||||
fetch_type(object.class).calls << call
|
|
||||||
end
|
|
||||||
|
|
||||||
def calls_for(object, method_name : Symbol)
|
|
||||||
fetch_instance(object).calls.select { |call| call.name == method_name }
|
|
||||||
end
|
|
||||||
|
|
||||||
def calls_for_type(type : T.class, method_name : Symbol) forall T
|
|
||||||
fetch_type(type).calls.select { |call| call.name == method_name }
|
|
||||||
end
|
|
||||||
|
|
||||||
def expected?(object, call : MethodCall) : Bool
|
|
||||||
fetch_instance(object).expected.any?(&.callable?(call)) ||
|
|
||||||
fetch_type(object.class).expected.any?(&.callable?(call))
|
|
||||||
end
|
|
||||||
|
|
||||||
def exit_handled? : Bool
|
|
||||||
# Lazily check if an `exit` method was called and it was expected.
|
|
||||||
# This is okay since an `expect().to receive(:exit)` should check the details of the call.
|
|
||||||
(@entries.any? { |_key, entry| entry.expected.any? { |stub| stub.name == :exit } } ||
|
|
||||||
@all_instances.any? { |_key, entry| entry.expected.any? { |stub| stub.name == :exit } }) &&
|
|
||||||
@entries.any? { |_key, entry| entry.calls.any? { |call| call.name == :exit } }
|
|
||||||
end
|
|
||||||
|
|
||||||
def expect(object, stub : MethodStub) : Nil
|
|
||||||
entry = fetch_instance(object)
|
|
||||||
entry.expected.add(stub)
|
|
||||||
entry.stubs.unshift(stub)
|
|
||||||
end
|
|
||||||
|
|
||||||
def expect(type : T.class, stub : MethodStub) : Nil forall T
|
|
||||||
entry = fetch_type(type)
|
|
||||||
entry.expected.add(stub)
|
|
||||||
entry.stubs.unshift(stub)
|
|
||||||
end
|
|
||||||
|
|
||||||
private def fetch_instance(object)
|
|
||||||
key = unique_key(object)
|
|
||||||
if @entries.has_key?(key)
|
|
||||||
@entries[key]
|
|
||||||
else
|
|
||||||
@entries[key] = Entry.new
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private def fetch_type(type)
|
|
||||||
key = type.name
|
|
||||||
if @all_instances.has_key?(key)
|
|
||||||
@all_instances[key]
|
|
||||||
else
|
|
||||||
@all_instances[key] = Entry.new
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private def unique_key(reference : ::Reference)
|
|
||||||
{reference.class.name, reference.object_id}
|
|
||||||
end
|
|
||||||
|
|
||||||
private def unique_key(value : ::Value)
|
|
||||||
{value.class.name, value.hash}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,127 +0,0 @@
|
||||||
module Spectator::Mocks
|
|
||||||
module Stubs
|
|
||||||
private macro stub(definition, *types, _file = __FILE__, _line = __LINE__, return_type = :undefined, &block)
|
|
||||||
{%
|
|
||||||
receiver = nil
|
|
||||||
name = nil
|
|
||||||
params = nil
|
|
||||||
args = nil
|
|
||||||
body = nil
|
|
||||||
if definition.is_a?(Call) # stub foo { :bar }
|
|
||||||
receiver = definition.receiver.id
|
|
||||||
named = false
|
|
||||||
name = definition.name.id
|
|
||||||
params = definition.args
|
|
||||||
if params.last.is_a?(Call)
|
|
||||||
body = params.last.block
|
|
||||||
params[-1] = params.last.name
|
|
||||||
end
|
|
||||||
args = params.map do |p|
|
|
||||||
n = p.is_a?(TypeDeclaration) ? p.var : p.id
|
|
||||||
r = named ? "#{n}: #{n}".id : n
|
|
||||||
named = true if n.starts_with?('*')
|
|
||||||
r
|
|
||||||
end
|
|
||||||
unless body
|
|
||||||
body = definition.block.is_a?(Nop) ? block : definition.block
|
|
||||||
end
|
|
||||||
elsif definition.is_a?(TypeDeclaration) # stub foo : Symbol
|
|
||||||
name = definition.var
|
|
||||||
params = [] of MacroId
|
|
||||||
args = [] of MacroId
|
|
||||||
body = block
|
|
||||||
elsif definition.is_a?(SymbolLiteral) # stub :foo, arg : Int32
|
|
||||||
name = definition.id
|
|
||||||
named = false
|
|
||||||
params = types
|
|
||||||
if params.last.is_a?(Call)
|
|
||||||
body = params.last.block
|
|
||||||
params[-1] = params.last.name
|
|
||||||
end
|
|
||||||
args = params.map do |p|
|
|
||||||
n = p.is_a?(TypeDeclaration) ? p.var : p.id
|
|
||||||
r = named ? "#{n}: #{n}".id : n
|
|
||||||
named = true if n.starts_with?('*')
|
|
||||||
r
|
|
||||||
end
|
|
||||||
body = block unless body
|
|
||||||
else
|
|
||||||
raise "Unrecognized stub format"
|
|
||||||
end
|
|
||||||
|
|
||||||
t = @type
|
|
||||||
receiver = if receiver == :self.id
|
|
||||||
t = t.class
|
|
||||||
"self."
|
|
||||||
else
|
|
||||||
""
|
|
||||||
end.id
|
|
||||||
original = if (name == :new.id && receiver == "self.".id) ||
|
|
||||||
(t.superclass && t.superclass.has_method?(name) && !t.overrides?(t.superclass, name))
|
|
||||||
:super
|
|
||||||
elsif t.has_method?(name)
|
|
||||||
:previous_def
|
|
||||||
else
|
|
||||||
name
|
|
||||||
end.id
|
|
||||||
fallback = if original == :super.id || original == :previous_def.id
|
|
||||||
original
|
|
||||||
else
|
|
||||||
"::#{original}(#{args.splat})".id
|
|
||||||
end
|
|
||||||
%}
|
|
||||||
|
|
||||||
{% if body && !body.is_a?(Nop) %}
|
|
||||||
def {{receiver}}%method({{params.splat}}){% if return_type.is_a?(ArrayLiteral) %} : {{return_type.type}}{% elsif return_type != :undefined %} : {{return_type.id}}{% elsif definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
|
|
||||||
{{body.body}}
|
|
||||||
end
|
|
||||||
{% elsif return_type.is_a?(ArrayLiteral) %}
|
|
||||||
def {{receiver}}%method({{params.splat}}) : {{return_type.type}}
|
|
||||||
{{return_type.splat}}
|
|
||||||
end
|
|
||||||
{% end %}
|
|
||||||
|
|
||||||
def {{receiver}}{{name}}({{params.splat}}){% if return_type.is_a?(ArrayLiteral) %} : {{return_type.type}}{% elsif return_type != :undefined %} : {{return_type.id}}{% elsif definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
|
|
||||||
if (%harness = ::Spectator::Harness.current?)
|
|
||||||
%args = ::Spectator::Mocks::GenericArguments.create({{args.splat}})
|
|
||||||
%call = ::Spectator::Mocks::MethodCall.new({{name.symbolize}}, %args)
|
|
||||||
%harness.mocks.record_call(self, %call)
|
|
||||||
if (%stub = %harness.mocks.find_stub(self, %call))
|
|
||||||
return %stub.call!(%args) { {{fallback}} }
|
|
||||||
end
|
|
||||||
|
|
||||||
{% if body && !body.is_a?(Nop) || return_type.is_a?(ArrayLiteral) %}
|
|
||||||
%method({{args.splat}})
|
|
||||||
{% else %}
|
|
||||||
{{fallback}}
|
|
||||||
{% end %}
|
|
||||||
else
|
|
||||||
{{fallback}}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def {{receiver}}{{name}}({{params.splat}}){% if return_type.is_a?(ArrayLiteral) %} : {{return_type.type}}{% elsif return_type != :undefined %} : {{return_type.id}}{% elsif definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
|
|
||||||
if (%harness = ::Spectator::Harness.current?)
|
|
||||||
%args = ::Spectator::Mocks::GenericArguments.create({{args.splat}})
|
|
||||||
%call = ::Spectator::Mocks::MethodCall.new({{name.symbolize}}, %args)
|
|
||||||
%harness.mocks.record_call(self, %call)
|
|
||||||
if (%stub = %harness.mocks.find_stub(self, %call))
|
|
||||||
return %stub.call!(%args) { {{fallback}} { |*%ya| yield *%ya } }
|
|
||||||
end
|
|
||||||
|
|
||||||
{% if body && !body.is_a?(Nop) || return_type.is_a?(ArrayLiteral) %}
|
|
||||||
%method({{args.splat}}) { |*%ya| yield *%ya }
|
|
||||||
{% else %}
|
|
||||||
{{fallback}} do |*%yield_args|
|
|
||||||
yield *%yield_args
|
|
||||||
end
|
|
||||||
{% end %}
|
|
||||||
else
|
|
||||||
{{fallback}} do |*%yield_args|
|
|
||||||
yield *%yield_args
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,25 +0,0 @@
|
||||||
module Spectator::Mocks
|
|
||||||
module TypeRegistry
|
|
||||||
extend self
|
|
||||||
|
|
||||||
alias Key = Tuple(String, Symbol)
|
|
||||||
|
|
||||||
@@entries = {} of Key => Deque(MethodStub)
|
|
||||||
|
|
||||||
def add(type_name : String, method_name : Symbol, location : Location, args : Arguments) : Nil
|
|
||||||
key = {type_name, method_name}
|
|
||||||
list = if @@entries.has_key?(key)
|
|
||||||
@@entries[key]
|
|
||||||
else
|
|
||||||
@@entries[key] = Deque(MethodStub).new
|
|
||||||
end
|
|
||||||
list << NilMethodStub.new(method_name, location, args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def exists?(type_name : String, call : MethodCall) : Bool
|
|
||||||
key = {type_name, call.name}
|
|
||||||
list = @@entries.fetch(key) { return false }
|
|
||||||
list.any?(&.callable?(call))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,4 +0,0 @@
|
||||||
module Spectator::Mocks
|
|
||||||
class UnexpectedMessageError < Exception
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,14 +0,0 @@
|
||||||
require "./generic_arguments"
|
|
||||||
require "./generic_method_stub"
|
|
||||||
|
|
||||||
module Spectator::Mocks
|
|
||||||
class ValueMethodStub(ReturnType) < GenericMethodStub(ReturnType)
|
|
||||||
def initialize(name, location, @value : ReturnType, args = nil)
|
|
||||||
super(name, location, args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(_args : GenericArguments(T, NT), &_original : -> RT) forall T, NT, RT
|
|
||||||
@value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,106 +0,0 @@
|
||||||
module Spectator::Mocks
|
|
||||||
abstract class VerifyingDouble(T)
|
|
||||||
def initialize(@null = false)
|
|
||||||
end
|
|
||||||
|
|
||||||
private macro stub(definition, &block)
|
|
||||||
{%
|
|
||||||
name = nil
|
|
||||||
params = nil
|
|
||||||
args = nil
|
|
||||||
body = nil
|
|
||||||
if definition.is_a?(Call) # stub foo { :bar }
|
|
||||||
named = false
|
|
||||||
name = definition.name.id
|
|
||||||
params = definition.args
|
|
||||||
args = params.map do |p|
|
|
||||||
n = p.is_a?(TypeDeclaration) ? p.var : p.id
|
|
||||||
r = named ? "#{n}: #{n}".id : n
|
|
||||||
named = true if n.starts_with?('*')
|
|
||||||
r
|
|
||||||
end
|
|
||||||
body = definition.block.is_a?(Nop) ? block : definition.block
|
|
||||||
elsif definition.is_a?(TypeDeclaration) # stub foo : Symbol
|
|
||||||
name = definition.var
|
|
||||||
params = [] of MacroId
|
|
||||||
args = [] of MacroId
|
|
||||||
body = block
|
|
||||||
else
|
|
||||||
raise "Unrecognized stub format"
|
|
||||||
end
|
|
||||||
%}
|
|
||||||
|
|
||||||
def {{name}}({{params.splat}}){% if definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
|
|
||||||
%args = ::Spectator::Mocks::GenericArguments.create({{args.splat}})
|
|
||||||
%call = ::Spectator::Mocks::MethodCall.new({{name.symbolize}}, %args)
|
|
||||||
::Spectator::Harness.current.mocks.record_call(self, %call)
|
|
||||||
|
|
||||||
unless ::Spectator::Mocks::TypeRegistry.exists?(T.to_s, %call)
|
|
||||||
raise ::Spectator::Mocks::UnexpectedMessageError.new("#{self} received unexpected message {{name}} - #{T} does not respond to #{%call}")
|
|
||||||
end
|
|
||||||
|
|
||||||
if (%stub = ::Spectator::Harness.current.mocks.find_stub(self, %call))
|
|
||||||
%stub.call!(%args) { %method({{args.splat}}) }
|
|
||||||
else
|
|
||||||
%method({{args.splat}})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def {{name}}({{params.splat}}){% if definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
|
|
||||||
%args = ::Spectator::Mocks::GenericArguments.create({{args.splat}})
|
|
||||||
%call = ::Spectator::Mocks::MethodCall.new({{name.symbolize}}, %args)
|
|
||||||
::Spectator::Harness.current.mocks.record_call(self, %call)
|
|
||||||
|
|
||||||
unless ::Spectator::Mocks::TypeRegistry.exists?(T.to_s, %call)
|
|
||||||
raise ::Spectator::Mocks::UnexpectedMessageError.new("#{self} received unexpected message {{name}} - #{T} does not respond to #{%call}")
|
|
||||||
end
|
|
||||||
|
|
||||||
if (%stub = ::Spectator::Harness.current.mocks.find_stub(self, %call))
|
|
||||||
%stub.call!(%args) { %method({{args.splat}}) { |*%ya| yield *%ya } }
|
|
||||||
else
|
|
||||||
%method({{args.splat}}) do |*%yield_args|
|
|
||||||
yield *%yield_args
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def %method({{params.splat}}){% if definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %}
|
|
||||||
{% if body && !body.is_a?(Nop) %}
|
|
||||||
{{body.body}}
|
|
||||||
{% else %}
|
|
||||||
%args = ::Spectator::Mocks::GenericArguments.create({{args.splat}})
|
|
||||||
%call = ::Spectator::Mocks::MethodCall.new({{name.symbolize}}, %args)
|
|
||||||
unless ::Spectator::Harness.current.mocks.expected?(self, %call)
|
|
||||||
raise ::Spectator::Mocks::UnexpectedMessageError.new("#{self} received unexpected message {{name}}")
|
|
||||||
end
|
|
||||||
|
|
||||||
# This code shouldn't be reached, but makes the compiler happy to have a matching return type.
|
|
||||||
{% if definition.is_a?(TypeDeclaration) %}
|
|
||||||
%x = uninitialized {{definition.type}}
|
|
||||||
{% else %}
|
|
||||||
nil
|
|
||||||
{% end %}
|
|
||||||
{% end %}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
macro method_missing(call)
|
|
||||||
args = ::Spectator::Mocks::GenericArguments.create({{call.args.splat}})
|
|
||||||
call = ::Spectator::Mocks::MethodCall.new({{call.name.symbolize}}, args)
|
|
||||||
::Spectator::Harness.current.mocks.record_call(self, call)
|
|
||||||
|
|
||||||
unless ::Spectator::Mocks::TypeRegistry.exists?(T.to_s, call)
|
|
||||||
raise ::Spectator::Mocks::UnexpectedMessageError.new("#{self} received unexpected message {{call.name}} - #{T} does not respond to #{call}")
|
|
||||||
end
|
|
||||||
|
|
||||||
return self if @null
|
|
||||||
return self if ::Spectator::Harness.current.mocks.expected?(self, call)
|
|
||||||
|
|
||||||
raise ::Spectator::Mocks::UnexpectedMessageError.new("#{self} received unexpected message {{call.name}}")
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s(io)
|
|
||||||
io << "Double(" << T << ')'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Add table
Add a link
Reference in a new issue