mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Merge branch 'example-api' of gitlab.com:arctic-fox/spectator into example-api
This commit is contained in:
commit
f97e5365e3
64 changed files with 584 additions and 553 deletions
|
@ -1,15 +0,0 @@
|
||||||
require "./block"
|
|
||||||
require "./expression"
|
|
||||||
|
|
||||||
module Spectator
|
|
||||||
class Assertion
|
|
||||||
struct Target(T)
|
|
||||||
@expression : Expression(T) | Block(T)
|
|
||||||
@source : Source?
|
|
||||||
|
|
||||||
def initialize(@expression : Expression(T) | Block(T), @source)
|
|
||||||
puts "TARGET: #{@expression} @ #{@source}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -12,7 +12,7 @@ module Spectator
|
||||||
# The *proc* will be called to evaluate the value of the expression.
|
# The *proc* will be called to evaluate the value of the expression.
|
||||||
# The *label* is usually the Crystal code for the *proc*.
|
# The *label* is usually the Crystal code for the *proc*.
|
||||||
# It can be nil if it isn't available.
|
# It can be nil if it isn't available.
|
||||||
def initialize(@block : -> T, label : Label)
|
def initialize(@block : -> T, label : Label = nil)
|
||||||
super(label)
|
super(label)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ module Spectator
|
||||||
# The block will be called to evaluate the value of the expression.
|
# The block will be called to evaluate the value of the expression.
|
||||||
# The *label* is usually the Crystal code for the *block*.
|
# The *label* is usually the Crystal code for the *block*.
|
||||||
# It can be nil if it isn't available.
|
# It can be nil if it isn't available.
|
||||||
def initialize(label : Label, &@block : -> T)
|
def initialize(label : Label = nil, &@block : -> T)
|
||||||
super(label)
|
super(label)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
# require "./dsl/*"
|
# require "./dsl/*"
|
||||||
require "./dsl/assertions"
|
|
||||||
require "./dsl/builder"
|
require "./dsl/builder"
|
||||||
require "./dsl/examples"
|
require "./dsl/examples"
|
||||||
|
require "./dsl/expectations"
|
||||||
require "./dsl/groups"
|
require "./dsl/groups"
|
||||||
require "./dsl/hooks"
|
require "./dsl/hooks"
|
||||||
|
require "./dsl/matchers"
|
||||||
require "./dsl/top"
|
require "./dsl/top"
|
||||||
require "./dsl/values"
|
require "./dsl/values"
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,12 @@ module Spectator::DSL
|
||||||
@@builder.after_each(hook)
|
@@builder.after_each(hook)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Defines a block of code to execute around every example in the current group.
|
||||||
|
def around_each(source = nil, label = "around_each", &block : Example::Procsy ->)
|
||||||
|
hook = ExampleProcsyHook.new(source: source, label: label, &block)
|
||||||
|
@@builder.around_each(hook)
|
||||||
|
end
|
||||||
|
|
||||||
# Sets the configuration of the spec.
|
# Sets the configuration of the spec.
|
||||||
#
|
#
|
||||||
# See `Spec::Builder#config=` for usage details.
|
# See `Spec::Builder#config=` for usage details.
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
require "../assertion"
|
|
||||||
require "../assertion_failed"
|
require "../assertion_failed"
|
||||||
require "../block"
|
require "../block"
|
||||||
|
require "../expectation"
|
||||||
require "../source"
|
require "../source"
|
||||||
require "../value"
|
require "../value"
|
||||||
|
|
||||||
module Spectator::DSL
|
module Spectator::DSL
|
||||||
# Methods and macros for asserting that conditions are met.
|
# Methods and macros for asserting that conditions are met.
|
||||||
module Assertions
|
module Expectations
|
||||||
# Immediately fail the current test.
|
# Immediately fail the current test.
|
||||||
# A reason can be specified with *message*.
|
# A reason can be specified with *message*.
|
||||||
def fail(message = "Example failed", *, _file = __FILE__, _line = __LINE__)
|
def fail(message = "Example failed", *, _file = __FILE__, _line = __LINE__)
|
||||||
|
@ -53,7 +53,7 @@ module Spectator::DSL
|
||||||
|
|
||||||
%expression = ::Spectator::Value.new(%actual, {{actual.stringify}})
|
%expression = ::Spectator::Value.new(%actual, {{actual.stringify}})
|
||||||
%source = ::Spectator::Source.new({{actual.filename}}, {{actual.line_number}})
|
%source = ::Spectator::Source.new({{actual.filename}}, {{actual.line_number}})
|
||||||
::Spectator::Assertion::Target.new(%expression, %source)
|
::Spectator::Expectation::Target.new(%expression, %source)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Starts an expectation.
|
# Starts an expectation.
|
||||||
|
@ -105,7 +105,7 @@ module Spectator::DSL
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
%source = ::Spectator::Source.new({{block.filename}}, {{block.line_number}})
|
%source = ::Spectator::Source.new({{block.filename}}, {{block.line_number}})
|
||||||
::Spectator::Assertion::Target.new(%block, %source)
|
::Spectator::Expectation::Target.new(%block, %source)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Short-hand for expecting something of the subject.
|
# Short-hand for expecting something of the subject.
|
|
@ -66,5 +66,15 @@ module Spectator::DSL
|
||||||
# The block will be run in the context of the current running example.
|
# The block will be run in the context of the current running example.
|
||||||
# This means that values defined by `let` and `subject` are available.
|
# This means that values defined by `let` and `subject` are available.
|
||||||
define_example_hook :after_each
|
define_example_hook :after_each
|
||||||
|
|
||||||
|
# Defines a block of code that will be invoked around every example in the group.
|
||||||
|
# The block will be run in the context of the current running example.
|
||||||
|
# This means that values defined by `let` and `subject` are available.
|
||||||
|
#
|
||||||
|
# The block will execute before the example.
|
||||||
|
# An `Example::Procsy` is passed to the block.
|
||||||
|
# The `Example::Procsy#run` method should be called to ensure the example runs.
|
||||||
|
# More code can run afterwards (in the block).
|
||||||
|
define_example_hook :around_each
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
require "../block"
|
||||||
require "../matchers"
|
require "../matchers"
|
||||||
require "../test_block"
|
require "../value"
|
||||||
require "../test_value"
|
|
||||||
|
|
||||||
module Spectator
|
module Spectator::DSL
|
||||||
module DSL
|
module Matchers
|
||||||
# Indicates that some value should equal another.
|
# Indicates that some value should equal another.
|
||||||
# The == operator is used for this check.
|
# The == operator is used for this check.
|
||||||
# The value passed to this method is the expected value.
|
# The value passed to this method is the expected value.
|
||||||
|
@ -13,8 +13,8 @@ module Spectator
|
||||||
# expect(1 + 2).to eq(3)
|
# expect(1 + 2).to eq(3)
|
||||||
# ```
|
# ```
|
||||||
macro eq(expected)
|
macro eq(expected)
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.stringify}})
|
||||||
::Spectator::Matchers::EqualityMatcher.new(%test_value)
|
::Spectator::Matchers::EqualityMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some value should not equal another.
|
# Indicates that some value should not equal another.
|
||||||
|
@ -26,8 +26,8 @@ module Spectator
|
||||||
# expect(1 + 2).to ne(5)
|
# expect(1 + 2).to ne(5)
|
||||||
# ```
|
# ```
|
||||||
macro ne(expected)
|
macro ne(expected)
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.stringify}})
|
||||||
::Spectator::Matchers::InequalityMatcher.new(%test_value)
|
::Spectator::Matchers::InequalityMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some value when compared to another satisfies an operator.
|
# Indicates that some value when compared to another satisfies an operator.
|
||||||
|
@ -61,8 +61,8 @@ module Spectator
|
||||||
# expect(obj.dup).to_not be(obj)
|
# expect(obj.dup).to_not be(obj)
|
||||||
# ```
|
# ```
|
||||||
macro be(expected)
|
macro be(expected)
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.stringify}})
|
||||||
::Spectator::Matchers::ReferenceMatcher.new(%test_value)
|
::Spectator::Matchers::ReferenceMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some value should be of a specified type.
|
# Indicates that some value should be of a specified type.
|
||||||
|
@ -173,8 +173,8 @@ module Spectator
|
||||||
# expect(3 - 1).to be_lt(3)
|
# expect(3 - 1).to be_lt(3)
|
||||||
# ```
|
# ```
|
||||||
macro be_lt(expected)
|
macro be_lt(expected)
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.stringify}})
|
||||||
::Spectator::Matchers::LessThanMatcher.new(%test_value)
|
::Spectator::Matchers::LessThanMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some value should be less than or equal to another.
|
# Indicates that some value should be less than or equal to another.
|
||||||
|
@ -186,8 +186,8 @@ module Spectator
|
||||||
# expect(3 - 1).to be_le(3)
|
# expect(3 - 1).to be_le(3)
|
||||||
# ```
|
# ```
|
||||||
macro be_le(expected)
|
macro be_le(expected)
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.stringify}})
|
||||||
::Spectator::Matchers::LessThanEqualMatcher.new(%test_value)
|
::Spectator::Matchers::LessThanEqualMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some value should be greater than another.
|
# Indicates that some value should be greater than another.
|
||||||
|
@ -199,8 +199,8 @@ module Spectator
|
||||||
# expect(3 + 1).to be_gt(3)
|
# expect(3 + 1).to be_gt(3)
|
||||||
# ```
|
# ```
|
||||||
macro be_gt(expected)
|
macro be_gt(expected)
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.stringify}})
|
||||||
::Spectator::Matchers::GreaterThanMatcher.new(%test_value)
|
::Spectator::Matchers::GreaterThanMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some value should be greater than or equal to another.
|
# Indicates that some value should be greater than or equal to another.
|
||||||
|
@ -212,8 +212,8 @@ module Spectator
|
||||||
# expect(3 + 1).to be_ge(3)
|
# expect(3 + 1).to be_ge(3)
|
||||||
# ```
|
# ```
|
||||||
macro be_ge(expected)
|
macro be_ge(expected)
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.stringify}})
|
||||||
::Spectator::Matchers::GreaterThanEqualMatcher.new(%test_value)
|
::Spectator::Matchers::GreaterThanEqualMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some value should match another.
|
# Indicates that some value should match another.
|
||||||
|
@ -230,8 +230,8 @@ module Spectator
|
||||||
# expect({:foo, 5}).to match({Symbol, Int32})
|
# expect({:foo, 5}).to match({Symbol, Int32})
|
||||||
# ```
|
# ```
|
||||||
macro match(expected)
|
macro match(expected)
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.stringify}})
|
||||||
::Spectator::Matchers::CaseMatcher.new(%test_value)
|
::Spectator::Matchers::CaseMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some value should be true.
|
# Indicates that some value should be true.
|
||||||
|
@ -321,8 +321,8 @@ module Spectator
|
||||||
# NOTE: Do not attempt to mix the two use cases.
|
# NOTE: Do not attempt to mix the two use cases.
|
||||||
# It likely won't work and will result in a compilation error.
|
# It likely won't work and will result in a compilation error.
|
||||||
macro be_within(expected)
|
macro be_within(expected)
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.stringify}})
|
||||||
::Spectator::Matchers::CollectionMatcher.new(%test_value)
|
::Spectator::Matchers::CollectionMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some value should be between a lower and upper-bound.
|
# Indicates that some value should be between a lower and upper-bound.
|
||||||
|
@ -344,8 +344,8 @@ module Spectator
|
||||||
macro be_between(min, max)
|
macro be_between(min, max)
|
||||||
%range = Range.new({{min}}, {{max}})
|
%range = Range.new({{min}}, {{max}})
|
||||||
%label = [{{min.stringify}}, {{max.stringify}}].join(" to ")
|
%label = [{{min.stringify}}, {{max.stringify}}].join(" to ")
|
||||||
%test_value = ::Spectator::TestValue.new(%range, %label)
|
%value = ::Spectator::Value.new(%range, %label)
|
||||||
::Spectator::Matchers::RangeMatcher.new(%test_value)
|
::Spectator::Matchers::RangeMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some value should be within a delta of an expected value.
|
# Indicates that some value should be within a delta of an expected value.
|
||||||
|
@ -403,8 +403,8 @@ module Spectator
|
||||||
# expect(%w[foo bar]).to start_with(/foo/)
|
# expect(%w[foo bar]).to start_with(/foo/)
|
||||||
# ```
|
# ```
|
||||||
macro start_with(expected)
|
macro start_with(expected)
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.stringify}})
|
||||||
::Spectator::Matchers::StartWithMatcher.new(%test_value)
|
::Spectator::Matchers::StartWithMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some value or set should end with another value.
|
# Indicates that some value or set should end with another value.
|
||||||
|
@ -426,8 +426,8 @@ module Spectator
|
||||||
# expect(%w[foo bar]).to end_with(/bar/)
|
# expect(%w[foo bar]).to end_with(/bar/)
|
||||||
# ```
|
# ```
|
||||||
macro end_with(expected)
|
macro end_with(expected)
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.stringify}})
|
||||||
::Spectator::Matchers::EndWithMatcher.new(%test_value)
|
::Spectator::Matchers::EndWithMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some value or set should contain another value.
|
# Indicates that some value or set should contain another value.
|
||||||
|
@ -451,11 +451,11 @@ module Spectator
|
||||||
# ```
|
# ```
|
||||||
macro contain(*expected)
|
macro contain(*expected)
|
||||||
{% if expected.id.starts_with?("{*") %}
|
{% if expected.id.starts_with?("{*") %}
|
||||||
%test_value = ::Spectator::TestValue.new({{expected.id[2...-1]}}, {{expected.splat.stringify}})
|
%value = ::Spectator::Value.new({{expected.id[2...-1]}}, {{expected.splat.stringify}})
|
||||||
::Spectator::Matchers::ContainMatcher.new(%test_value)
|
::Spectator::Matchers::ContainMatcher.new(%value)
|
||||||
{% else %}
|
{% else %}
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.splat.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.splat.stringify}})
|
||||||
::Spectator::Matchers::ContainMatcher.new(%test_value)
|
::Spectator::Matchers::ContainMatcher.new(%value)
|
||||||
{% end %}
|
{% end %}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -475,8 +475,8 @@ module Spectator
|
||||||
# expect(%i[a b c]).to contain_elements(%i[a b])
|
# expect(%i[a b c]).to contain_elements(%i[a b])
|
||||||
# ```
|
# ```
|
||||||
macro contain_elements(expected)
|
macro contain_elements(expected)
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.stringify}})
|
||||||
::Spectator::Matchers::ContainMatcher.new(%test_value)
|
::Spectator::Matchers::ContainMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some range (or collection) should contain another value.
|
# Indicates that some range (or collection) should contain another value.
|
||||||
|
@ -497,11 +497,11 @@ module Spectator
|
||||||
# ```
|
# ```
|
||||||
macro cover(*expected)
|
macro cover(*expected)
|
||||||
{% if expected.id.starts_with?("{*") %}
|
{% if expected.id.starts_with?("{*") %}
|
||||||
%test_value = ::Spectator::TestValue.new({{expected.id[2...-1]}}, {{expected.splat.stringify}})
|
%value = ::Spectator::Value.new({{expected.id[2...-1]}}, {{expected.splat.stringify}})
|
||||||
::Spectator::Matchers::ContainMatcher.new(%test_value)
|
::Spectator::Matchers::ContainMatcher.new(%value)
|
||||||
{% else %}
|
{% else %}
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.splat.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.splat.stringify}})
|
||||||
::Spectator::Matchers::ContainMatcher.new(%test_value)
|
::Spectator::Matchers::ContainMatcher.new(%value)
|
||||||
{% end %}
|
{% end %}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -532,11 +532,11 @@ module Spectator
|
||||||
# ```
|
# ```
|
||||||
macro have(*expected)
|
macro have(*expected)
|
||||||
{% if expected.id.starts_with?("{*") %}
|
{% if expected.id.starts_with?("{*") %}
|
||||||
%test_value = ::Spectator::TestValue.new({{expected.id[2...-1]}}, {{expected.splat.stringify}})
|
%value = ::Spectator::Value.new({{expected.id[2...-1]}}, {{expected.splat.stringify}})
|
||||||
::Spectator::Matchers::HaveMatcher.new(%test_value)
|
::Spectator::Matchers::HaveMatcher.new(%value)
|
||||||
{% else %}
|
{% else %}
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.splat.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.splat.stringify}})
|
||||||
::Spectator::Matchers::HaveMatcher.new(%test_value)
|
::Spectator::Matchers::HaveMatcher.new(%value)
|
||||||
{% end %}
|
{% end %}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -559,8 +559,8 @@ module Spectator
|
||||||
# expect([1, 2, 3, :a, :b, :c]).to have_elements([Int32, Symbol])
|
# expect([1, 2, 3, :a, :b, :c]).to have_elements([Int32, Symbol])
|
||||||
# ```
|
# ```
|
||||||
macro have_elements(expected)
|
macro have_elements(expected)
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.stringify}})
|
||||||
::Spectator::Matchers::HaveMatcher.new(%test_value)
|
::Spectator::Matchers::HaveMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some set, such as a `Hash`, has a given key.
|
# Indicates that some set, such as a `Hash`, has a given key.
|
||||||
|
@ -572,8 +572,8 @@ module Spectator
|
||||||
# expect({"lucky" => 7}).to have_key("lucky")
|
# expect({"lucky" => 7}).to have_key("lucky")
|
||||||
# ```
|
# ```
|
||||||
macro have_key(expected)
|
macro have_key(expected)
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.stringify}})
|
||||||
::Spectator::Matchers::HaveKeyMatcher.new(%test_value)
|
::Spectator::Matchers::HaveKeyMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# :ditto:
|
# :ditto:
|
||||||
|
@ -590,8 +590,8 @@ module Spectator
|
||||||
# expect({"lucky" => 7}).to have_value(7)
|
# expect({"lucky" => 7}).to have_value(7)
|
||||||
# ```
|
# ```
|
||||||
macro have_value(expected)
|
macro have_value(expected)
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.stringify}})
|
||||||
::Spectator::Matchers::HaveValueMatcher.new(%test_value)
|
::Spectator::Matchers::HaveValueMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# :ditto:
|
# :ditto:
|
||||||
|
@ -607,11 +607,11 @@ module Spectator
|
||||||
# ```
|
# ```
|
||||||
macro contain_exactly(*expected)
|
macro contain_exactly(*expected)
|
||||||
{% if expected.id.starts_with?("{*") %}
|
{% if expected.id.starts_with?("{*") %}
|
||||||
%test_value = ::Spectator::TestValue.new(({{expected.id[2...-1]}}).to_a, {{expected.stringify}})
|
%value = ::Spectator::Value.new(({{expected.id[2...-1]}}).to_a, {{expected.stringify}})
|
||||||
::Spectator::Matchers::ArrayMatcher.new(%test_value)
|
::Spectator::Matchers::ArrayMatcher.new(%value)
|
||||||
{% else %}
|
{% else %}
|
||||||
%test_value = ::Spectator::TestValue.new(({{expected}}).to_a, {{expected.stringify}})
|
%value = ::Spectator::Value.new(({{expected}}).to_a, {{expected.stringify}})
|
||||||
::Spectator::Matchers::ArrayMatcher.new(%test_value)
|
::Spectator::Matchers::ArrayMatcher.new(%value)
|
||||||
{% end %}
|
{% end %}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -623,8 +623,8 @@ module Spectator
|
||||||
# expect([1, 2, 3]).to match_array([3, 2, 1])
|
# expect([1, 2, 3]).to match_array([3, 2, 1])
|
||||||
# ```
|
# ```
|
||||||
macro match_array(expected)
|
macro match_array(expected)
|
||||||
%test_value = ::Spectator::TestValue.new(({{expected}}).to_a, {{expected.stringify}})
|
%value = ::Spectator::Value.new(({{expected}}).to_a, {{expected.stringify}})
|
||||||
::Spectator::Matchers::ArrayMatcher.new(%test_value)
|
::Spectator::Matchers::ArrayMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some set should have a specified size.
|
# Indicates that some set should have a specified size.
|
||||||
|
@ -634,8 +634,8 @@ module Spectator
|
||||||
# expect([1, 2, 3]).to have_size(3)
|
# expect([1, 2, 3]).to have_size(3)
|
||||||
# ```
|
# ```
|
||||||
macro have_size(expected)
|
macro have_size(expected)
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.stringify}})
|
||||||
::Spectator::Matchers::SizeMatcher.new(%test_value)
|
::Spectator::Matchers::SizeMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some set should have the same size (number of elements) as another set.
|
# Indicates that some set should have the same size (number of elements) as another set.
|
||||||
|
@ -645,8 +645,8 @@ module Spectator
|
||||||
# expect([1, 2, 3]).to have_size_of(%i[x y z])
|
# expect([1, 2, 3]).to have_size_of(%i[x y z])
|
||||||
# ```
|
# ```
|
||||||
macro have_size_of(expected)
|
macro have_size_of(expected)
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.stringify}})
|
||||||
::Spectator::Matchers::SizeOfMatcher.new(%test_value)
|
::Spectator::Matchers::SizeOfMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some value should have a set of attributes matching some conditions.
|
# Indicates that some value should have a set of attributes matching some conditions.
|
||||||
|
@ -661,11 +661,11 @@ module Spectator
|
||||||
# ```
|
# ```
|
||||||
macro have_attributes(**expected)
|
macro have_attributes(**expected)
|
||||||
{% if expected.id.starts_with?("{**") %}
|
{% if expected.id.starts_with?("{**") %}
|
||||||
%test_value = ::Spectator::TestValue.new({{expected.id[3...-1]}}, {{expected.double_splat.stringify}})
|
%value = ::Spectator::Value.new({{expected.id[3...-1]}}, {{expected.double_splat.stringify}})
|
||||||
::Spectator::Matchers::AttributesMatcher.new(%test_value)
|
::Spectator::Matchers::AttributesMatcher.new(%value)
|
||||||
{% else %}
|
{% else %}
|
||||||
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.double_splat.stringify}})
|
%value = ::Spectator::Value.new({{expected}}, {{expected.double_splat.stringify}})
|
||||||
::Spectator::Matchers::AttributesMatcher.new(%test_value)
|
::Spectator::Matchers::AttributesMatcher.new(%value)
|
||||||
{% end %}
|
{% end %}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -718,37 +718,18 @@ module Spectator
|
||||||
# expect { subject << :foo }.to change(&.size).by(1)
|
# expect { subject << :foo }.to change(&.size).by(1)
|
||||||
# ```
|
# ```
|
||||||
macro change(&expression)
|
macro change(&expression)
|
||||||
{% if expression.is_a?(Nop) %}
|
{% if block.args.size == 1 && block.args[0] =~ /^__arg\d+$/ && block.body.is_a?(Call) && block.body.id =~ /^__arg\d+\./ %}
|
||||||
{% raise "Block must be provided to change matcher" %}
|
{% method_name = block.body.id.split('.')[1..-1].join('.') %}
|
||||||
{% end %}
|
%block = ::Spectator::Block.new({{"#" + method_name}}) do
|
||||||
|
subject.{{method_name.id}}
|
||||||
# Check if the short-hand method syntax is used.
|
end
|
||||||
# This is a hack, since macros don't get this as a "literal" or something similar.
|
{% elsif block.args.empty? %}
|
||||||
# The Crystal compiler will translate:
|
%block = ::Spectator::Block.new({{"`" + block.body.stringify + "`"}}) {{block}}
|
||||||
# ```
|
|
||||||
# &.foo
|
|
||||||
# ```
|
|
||||||
# to:
|
|
||||||
# ```
|
|
||||||
# { |__arg0| __arg0.foo }
|
|
||||||
# ```
|
|
||||||
# The hack used here is to check if it looks like a compiler-generated block.
|
|
||||||
{% if expression.args == ["__arg0".id] && expression.body.is_a?(Call) && expression.body.id =~ /^__arg0\./ %}
|
|
||||||
# Extract the method name to make it clear to the user what is tested.
|
|
||||||
# The raw block can't be used because it's not clear to the user.
|
|
||||||
{% method_name = expression.body.id.split('.')[1..-1].join('.') %}
|
|
||||||
%proc = ->{ subject.{{method_name.id}} }
|
|
||||||
%test_block = ::Spectator::TestBlock.create(%proc, {{"#" + method_name}})
|
|
||||||
{% elsif expression.args.empty? %}
|
|
||||||
# In this case, it looks like the short-hand method syntax wasn't used.
|
|
||||||
# Capture the block as a proc and pass along.
|
|
||||||
%proc = ->{{expression}}
|
|
||||||
%test_block = ::Spectator::TestBlock.create(%proc, {{"`" + expression.body.stringify + "`"}})
|
|
||||||
{% else %}
|
{% else %}
|
||||||
{% raise "Unexpected block arguments in change matcher" %}
|
{% raise "Unexpected block arguments in 'expect' call" %}
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
::Spectator::Matchers::ChangeMatcher.new(%test_block)
|
::Spectator::Matchers::ChangeMatcher.new(%block)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some block should raise an error.
|
# Indicates that some block should raise an error.
|
||||||
|
@ -828,8 +809,8 @@ module Spectator
|
||||||
end
|
end
|
||||||
|
|
||||||
macro have_received(method)
|
macro have_received(method)
|
||||||
%test_value = ::Spectator::TestValue.new(({{method.id.symbolize}}), {{method.id.stringify}})
|
%value = ::Spectator::Value.new(({{method.id.symbolize}}), {{method.id.stringify}})
|
||||||
::Spectator::Matchers::ReceiveMatcher.new(%test_value)
|
::Spectator::Matchers::ReceiveMatcher.new(%value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Used to create predicate matchers.
|
# Used to create predicate matchers.
|
||||||
|
@ -872,8 +853,8 @@ module Spectator
|
||||||
{% end %}
|
{% end %}
|
||||||
label << ')'
|
label << ')'
|
||||||
{% end %}
|
{% end %}
|
||||||
test_value = ::Spectator::TestValue.new(descriptor, label.to_s)
|
value = ::Spectator::Value.new(descriptor, label.to_s)
|
||||||
::Spectator::Matchers::{{matcher.id}}.new(test_value)
|
::Spectator::Matchers::{{matcher.id}}.new(value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -46,26 +46,35 @@ module Spectator
|
||||||
# Returns the result of the execution.
|
# Returns the result of the execution.
|
||||||
# The result will also be stored in `#result`.
|
# The result will also be stored in `#result`.
|
||||||
def run : Result
|
def run : Result
|
||||||
@@current = self
|
|
||||||
Log.debug { "Running example #{self}" }
|
Log.debug { "Running example #{self}" }
|
||||||
Log.warn { "Example #{self} already ran" } if @finished
|
Log.warn { "Example #{self} already ran" } if @finished
|
||||||
@result = Harness.run do
|
|
||||||
if (parent = group?)
|
|
||||||
parent.call_once_before_all
|
|
||||||
parent.call_before_each(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
@entrypoint.call(self)
|
previous_example = @@current
|
||||||
|
@@current = self
|
||||||
|
|
||||||
|
begin
|
||||||
|
@result = Harness.run do
|
||||||
|
group?.try(&.call_once_before_all)
|
||||||
|
if (parent = group?)
|
||||||
|
parent.call_around_each(self) { run_internal }
|
||||||
|
else
|
||||||
|
run_internal
|
||||||
|
end
|
||||||
|
if (parent = group?)
|
||||||
|
parent.call_once_after_all if parent.finished?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
@@current = previous_example
|
||||||
@finished = true
|
@finished = true
|
||||||
|
|
||||||
if (parent = group?)
|
|
||||||
parent.call_after_each(self)
|
|
||||||
parent.call_once_after_all if parent.finished?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
ensure
|
end
|
||||||
@@current = nil
|
|
||||||
|
private def run_internal
|
||||||
|
group?.try(&.call_before_each(self))
|
||||||
|
@entrypoint.call(self)
|
||||||
@finished = true
|
@finished = true
|
||||||
|
group?.try(&.call_after_each(self))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Executes code within the example's test context.
|
# Executes code within the example's test context.
|
||||||
|
@ -76,12 +85,38 @@ module Spectator
|
||||||
# An error will be raised if *klass* doesn't match the test context's type.
|
# An error will be raised if *klass* doesn't match the test context's type.
|
||||||
# The block given to this method will be executed within the test context.
|
# The block given to this method will be executed within the test context.
|
||||||
#
|
#
|
||||||
|
# The context casted to an instance of *klass* is provided as a block argument.
|
||||||
|
#
|
||||||
# TODO: Benchmark compiler performance using this method versus client-side casting in a proc.
|
# TODO: Benchmark compiler performance using this method versus client-side casting in a proc.
|
||||||
def with_context(klass)
|
protected def with_context(klass)
|
||||||
context = klass.cast(@context)
|
context = klass.cast(@context)
|
||||||
with context yield
|
with context yield
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Casts the example's test context to a specific type.
|
||||||
|
# This is an advanced method intended for internal usage only.
|
||||||
|
#
|
||||||
|
# The *klass* defines the type of the test context.
|
||||||
|
# This is typically only known by the code constructing the example.
|
||||||
|
# An error will be raised if *klass* doesn't match the test context's type.
|
||||||
|
#
|
||||||
|
# The context casted to an instance of *klass* is returned.
|
||||||
|
#
|
||||||
|
# TODO: Benchmark compiler performance using this method versus client-side casting in a proc.
|
||||||
|
protected def cast_context(klass)
|
||||||
|
klass.cast(@context)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Constructs the full name or description of the example.
|
||||||
|
# This prepends names of groups this example is part of.
|
||||||
|
def to_s(io)
|
||||||
|
if name?
|
||||||
|
super
|
||||||
|
else
|
||||||
|
io << "<anonymous>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Exposes information about the example useful for debugging.
|
# Exposes information about the example useful for debugging.
|
||||||
def inspect(io)
|
def inspect(io)
|
||||||
# Full example name.
|
# Full example name.
|
||||||
|
@ -97,5 +132,51 @@ module Spectator
|
||||||
|
|
||||||
io << result
|
io << result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Wraps an example to behave like a `Proc`.
|
||||||
|
# This is typically used for an *around_each* hook.
|
||||||
|
# Invoking `#call` or `#run` will run the example.
|
||||||
|
struct Procsy
|
||||||
|
# Underlying example that will run.
|
||||||
|
getter example : Example
|
||||||
|
|
||||||
|
# Creates the example proxy.
|
||||||
|
# The *example* should be run eventually.
|
||||||
|
# The *proc* defines the block of code to run when `#call` or `#run` is invoked.
|
||||||
|
def initialize(@example : Example, &@proc : ->)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Invokes the proc.
|
||||||
|
def call : Nil
|
||||||
|
@proc.call
|
||||||
|
end
|
||||||
|
|
||||||
|
# Invokes the proc.
|
||||||
|
def run : Nil
|
||||||
|
@proc.call
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates a new procsy for a block and the example from this instance.
|
||||||
|
def wrap(&block : ->) : self
|
||||||
|
self.class.new(@example, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Executes code within the example's test context.
|
||||||
|
# This is an advanced method intended for internal usage only.
|
||||||
|
#
|
||||||
|
# The *klass* defines the type of the test context.
|
||||||
|
# This is typically only known by the code constructing the example.
|
||||||
|
# An error will be raised if *klass* doesn't match the test context's type.
|
||||||
|
# The block given to this method will be executed within the test context.
|
||||||
|
#
|
||||||
|
# TODO: Benchmark compiler performance using this method versus client-side casting in a proc.
|
||||||
|
protected def with_context(klass)
|
||||||
|
context = @example.cast_context(klass)
|
||||||
|
with context yield
|
||||||
|
end
|
||||||
|
|
||||||
|
# Allow instance to behave like an example.
|
||||||
|
forward_missing_to @example
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
require "./events"
|
require "./events"
|
||||||
require "./spec_node"
|
require "./spec_node"
|
||||||
|
require "./example_procsy_hook"
|
||||||
|
|
||||||
module Spectator
|
module Spectator
|
||||||
# Collection of examples and sub-groups.
|
# Collection of examples and sub-groups.
|
||||||
|
@ -101,5 +102,51 @@ module Spectator
|
||||||
@nodes << node
|
@nodes << node
|
||||||
node.group = self
|
node.group = self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@around_hooks = [] of ExampleProcsyHook
|
||||||
|
|
||||||
|
# Adds a hook to be invoked when the *around_each* event occurs.
|
||||||
|
def add_around_each_hook(hook : ExampleProcsyHook) : Nil
|
||||||
|
@around_hooks << hook
|
||||||
|
end
|
||||||
|
|
||||||
|
# Defines a hook for the *around_each* event.
|
||||||
|
# The block of code given to this method is invoked when the event occurs.
|
||||||
|
# The current example is provided as a block argument.
|
||||||
|
def around_each(&block : Example::Procsy ->) : Nil
|
||||||
|
hook = ExampleProcsyHook.new(label: "around_each", &block)
|
||||||
|
add_around_each_hook(hook)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Signals that the *around_each* event has occurred.
|
||||||
|
# All hooks associated with the event will be called.
|
||||||
|
def call_around_each(example : Example, &block : -> _) : Nil
|
||||||
|
# Avoid overhead if there's no hooks.
|
||||||
|
return yield if @around_hooks.empty?
|
||||||
|
|
||||||
|
# Start with a procsy that wraps the original code.
|
||||||
|
procsy = Example::Procsy.new(example, &block)
|
||||||
|
procsy = wrap_around_each(procsy)
|
||||||
|
procsy.call
|
||||||
|
end
|
||||||
|
|
||||||
|
# Wraps a procsy with the *around_each* hooks from this example group.
|
||||||
|
# The returned procsy will call each hook then *procsy*.
|
||||||
|
protected def wrap_around_each(procsy : Example::Procsy) : Example::Procsy
|
||||||
|
# Avoid overhead if there's no hooks.
|
||||||
|
return procsy if @around_hooks.empty?
|
||||||
|
|
||||||
|
# Wrap each hook with the next.
|
||||||
|
outer = procsy
|
||||||
|
@around_hooks.each do |hook|
|
||||||
|
outer = hook.wrap(outer)
|
||||||
|
end
|
||||||
|
|
||||||
|
# If there's a parent, wrap the procsy with its hooks.
|
||||||
|
# Otherwise, return the outermost procsy.
|
||||||
|
return outer unless (parent = group?)
|
||||||
|
|
||||||
|
parent.wrap_around_each(outer)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
56
src/spectator/example_procsy_hook.cr
Normal file
56
src/spectator/example_procsy_hook.cr
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
require "./label"
|
||||||
|
require "./source"
|
||||||
|
|
||||||
|
module Spectator
|
||||||
|
# Information about a hook tied to an example and a proc to invoke it.
|
||||||
|
class ExampleProcsyHook
|
||||||
|
# Location of the hook in source code.
|
||||||
|
getter! source : Source
|
||||||
|
|
||||||
|
# User-defined description of the hook.
|
||||||
|
getter! label : Label
|
||||||
|
|
||||||
|
@proc : Example::Procsy ->
|
||||||
|
|
||||||
|
# Creates the hook with a proc.
|
||||||
|
# The *proc* will be called when the hook is invoked.
|
||||||
|
# A *source* and *label* can be provided for debugging.
|
||||||
|
def initialize(@proc : (Example::Procsy ->), *, @source : Source? = nil, @label : Label = nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates the hook with a block.
|
||||||
|
# The block must take a single argument - the current example wrapped in a procsy.
|
||||||
|
# The block will be executed when the hook is invoked.
|
||||||
|
# A *source* and *label* can be provided for debugging.
|
||||||
|
def initialize(*, @source : Source? = nil, @label : Label = nil, &block : Example::Procsy -> _)
|
||||||
|
@proc = block
|
||||||
|
end
|
||||||
|
|
||||||
|
# Invokes the hook.
|
||||||
|
# The *example* refers to the current example.
|
||||||
|
def call(procsy : Example::Procsy) : Nil
|
||||||
|
@proc.call(procsy)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates an example procsy that invokes this hook.
|
||||||
|
def wrap(procsy : Example::Procsy) : Example::Procsy
|
||||||
|
procsy.wrap { call(procsy) }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Produces the string representation of the hook.
|
||||||
|
# Includes the source and label if they're not nil.
|
||||||
|
def to_s(io)
|
||||||
|
io << "example hook"
|
||||||
|
|
||||||
|
if (label = @label)
|
||||||
|
io << ' '
|
||||||
|
io << label
|
||||||
|
end
|
||||||
|
|
||||||
|
if (source = @source)
|
||||||
|
io << " @ "
|
||||||
|
io << source
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
113
src/spectator/expectation.cr
Normal file
113
src/spectator/expectation.cr
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
require "./expression"
|
||||||
|
require "./source"
|
||||||
|
|
||||||
|
module Spectator
|
||||||
|
# Result of evaluating a matcher on a target.
|
||||||
|
# Contains information about the match,
|
||||||
|
# such as whether it was successful and a description of the operation.
|
||||||
|
struct Expectation
|
||||||
|
# Location of the expectation in source code.
|
||||||
|
# This can be nil if the source isn't capturable,
|
||||||
|
# for instance using the *should* syntax or dynamically created expectations.
|
||||||
|
getter source : Source?
|
||||||
|
|
||||||
|
# Creates the expectation.
|
||||||
|
# The *match_data* comes from the result of calling `Matcher#match`.
|
||||||
|
# The *source* is the location of the expectation in source code, if available.
|
||||||
|
def initialize(@match_data : Matchers::MatchData, @source : Source? = nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Stores part of an expectation.
|
||||||
|
# This covers the actual value (or block) being inspected and its source.
|
||||||
|
# This is the type returned by an `expect` block in the DSL.
|
||||||
|
# It is not intended to be used directly, but instead by chaining methods.
|
||||||
|
# Typically `#to` and `#not_to` are used.
|
||||||
|
struct Target(T)
|
||||||
|
# Creates the expectation target.
|
||||||
|
# The *expression* is the actual value being tested and its label.
|
||||||
|
# The *source* is the location of where this expectation was defined.
|
||||||
|
def initialize(@expression : Expression(T), @source : Source)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Asserts that some criteria defined by the matcher is satisfied.
|
||||||
|
def to(matcher) : Nil
|
||||||
|
match_data = matcher.match(@expression)
|
||||||
|
report(match_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to(stub : Mocks::MethodStub) : Nil
|
||||||
|
Harness.current.mocks.expect(@expression.value, stub)
|
||||||
|
value = Value.new(stub.name, stub.to_s)
|
||||||
|
matcher = Matchers::ReceiveMatcher.new(value, stub.arguments?)
|
||||||
|
to_eventually(matcher)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to(stubs : Enumerable(Mocks::MethodStub)) : Nil
|
||||||
|
stubs.each { |stub| to(stub) }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Asserts that some criteria defined by the matcher is not satisfied.
|
||||||
|
# This is effectively the opposite of `#to`.
|
||||||
|
def to_not(matcher) : Nil
|
||||||
|
match_data = matcher.negated_match(@expression)
|
||||||
|
report(match_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_not(stub : Mocks::MethodStub) : Nil
|
||||||
|
value = Value.new(stub.name, stub.to_s)
|
||||||
|
matcher = Matchers::ReceiveMatcher.new(value, stub.arguments?)
|
||||||
|
to_never(matcher)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_not(stubs : Enumerable(Mocks::MethodStub)) : Nil
|
||||||
|
stubs.each { |stub| to_not(stub) }
|
||||||
|
end
|
||||||
|
|
||||||
|
# :ditto:
|
||||||
|
@[AlwaysInline]
|
||||||
|
def not_to(matcher) : Nil
|
||||||
|
to_not(matcher)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Asserts that some criteria defined by the matcher is eventually satisfied.
|
||||||
|
# The expectation is checked after the example finishes and all hooks have run.
|
||||||
|
def to_eventually(matcher) : Nil
|
||||||
|
Harness.current.defer { to(matcher) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_eventually(stub : Mocks::MethodStub) : Nil
|
||||||
|
to(stub)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_eventually(stubs : Enumerable(Mocks::MethodStub)) : Nil
|
||||||
|
to(stub)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Asserts that some criteria defined by the matcher is never satisfied.
|
||||||
|
# The expectation is checked after the example finishes and all hooks have run.
|
||||||
|
def to_never(matcher) : Nil
|
||||||
|
Harness.current.defer { to_not(matcher) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_never(stub : Mocks::MethodStub) : Nil
|
||||||
|
to_not(stub)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_never(stub : Enumerable(Mocks::MethodStub)) : Nil
|
||||||
|
to_not(stub)
|
||||||
|
end
|
||||||
|
|
||||||
|
# :ditto:
|
||||||
|
@[AlwaysInline]
|
||||||
|
def never_to(matcher) : Nil
|
||||||
|
to_never(matcher)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Reports an expectation to the current harness.
|
||||||
|
private def report(match_data : Matchers::MatchData)
|
||||||
|
expectation = Expectation.new(match_data, @source)
|
||||||
|
Harness.current.report(expectation)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +0,0 @@
|
||||||
require "./expectations/*"
|
|
||||||
|
|
||||||
module Spectator
|
|
||||||
# Namespace that contains all expectations, partials, and handling of them.
|
|
||||||
module Expectations
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,62 +0,0 @@
|
||||||
require "./expectation"
|
|
||||||
|
|
||||||
module Spectator::Expectations
|
|
||||||
# Collection of expectations from an example.
|
|
||||||
class ExampleExpectations
|
|
||||||
include Enumerable(Expectation)
|
|
||||||
|
|
||||||
# Creates the collection.
|
|
||||||
def initialize(@expectations : Array(Expectation))
|
|
||||||
end
|
|
||||||
|
|
||||||
# Iterates through all expectations.
|
|
||||||
def each
|
|
||||||
@expectations.each do |expectation|
|
|
||||||
yield expectation
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns a collection of only the satisfied expectations.
|
|
||||||
def satisfied : Enumerable(Expectation)
|
|
||||||
@expectations.select(&.satisfied?)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Iterates over only the satisfied expectations.
|
|
||||||
def each_satisfied
|
|
||||||
@expectations.each do |expectation|
|
|
||||||
yield expectation if expectation.satisfied?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns a collection of only the unsatisfied expectations.
|
|
||||||
def unsatisfied : Enumerable(Expectation)
|
|
||||||
@expectations.reject(&.satisfied?)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Iterates over only the unsatisfied expectations.
|
|
||||||
def each_unsatisfied
|
|
||||||
@expectations.each do |expectation|
|
|
||||||
yield expectation unless expectation.satisfied?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Determines whether the example was successful
|
|
||||||
# based on if all expectations were satisfied.
|
|
||||||
def successful?
|
|
||||||
@expectations.all?(&.satisfied?)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Determines whether the example failed
|
|
||||||
# based on if any expectations were not satisfied.
|
|
||||||
def failed?
|
|
||||||
!successful?
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates the JSON representation of the expectations.
|
|
||||||
def to_json(json : ::JSON::Builder)
|
|
||||||
json.array do
|
|
||||||
each &.to_json(json)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,74 +0,0 @@
|
||||||
require "../matchers/failed_match_data"
|
|
||||||
require "../matchers/match_data"
|
|
||||||
require "../source"
|
|
||||||
|
|
||||||
module Spectator::Expectations
|
|
||||||
# Result of evaluating a matcher on an expectation partial.
|
|
||||||
struct Expectation
|
|
||||||
# Location where this expectation was defined.
|
|
||||||
getter source : Source
|
|
||||||
|
|
||||||
# Creates the expectation.
|
|
||||||
def initialize(@match_data : Matchers::MatchData, @source : Source)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Indicates whether the matcher was satisified.
|
|
||||||
def satisfied?
|
|
||||||
@match_data.matched?
|
|
||||||
end
|
|
||||||
|
|
||||||
# Indicates that the expectation was not satisified.
|
|
||||||
def failure?
|
|
||||||
!satisfied?
|
|
||||||
end
|
|
||||||
|
|
||||||
# Description of why the match failed.
|
|
||||||
# If nil, then the match was successful.
|
|
||||||
def failure_message?
|
|
||||||
@match_data.as?(Matchers::FailedMatchData).try(&.failure_message)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Description of why the match failed.
|
|
||||||
def failure_message
|
|
||||||
failure_message?.not_nil!
|
|
||||||
end
|
|
||||||
|
|
||||||
# Additional information about the match, useful for debug.
|
|
||||||
# If nil, then the match was successful.
|
|
||||||
def values?
|
|
||||||
@match_data.as?(Matchers::FailedMatchData).try(&.values)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Additional information about the match, useful for debug.
|
|
||||||
def values
|
|
||||||
values?.not_nil!
|
|
||||||
end
|
|
||||||
|
|
||||||
def description
|
|
||||||
@match_data.description
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates the JSON representation of the expectation.
|
|
||||||
def to_json(json : ::JSON::Builder)
|
|
||||||
json.object do
|
|
||||||
json.field("source") { @source.to_json(json) }
|
|
||||||
json.field("satisfied", satisfied?)
|
|
||||||
if (failed = @match_data.as?(Matchers::FailedMatchData))
|
|
||||||
failed_to_json(failed, json)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds failure information to a JSON structure.
|
|
||||||
private def failed_to_json(failed : Matchers::FailedMatchData, json : ::JSON::Builder)
|
|
||||||
json.field("failure", failed.failure_message)
|
|
||||||
json.field("values") do
|
|
||||||
json.object do
|
|
||||||
failed.values.each do |pair|
|
|
||||||
json.field(pair.first, pair.last)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,101 +0,0 @@
|
||||||
require "../matchers/match_data"
|
|
||||||
require "../source"
|
|
||||||
require "../test_expression"
|
|
||||||
|
|
||||||
module Spectator::Expectations
|
|
||||||
# Stores part of an expectation (obviously).
|
|
||||||
# The part of the expectation this type covers is the actual value and source.
|
|
||||||
# This can also cover a block's behavior.
|
|
||||||
struct ExpectationPartial(T)
|
|
||||||
# The actual value being tested.
|
|
||||||
# This also contains its label.
|
|
||||||
getter actual : TestExpression(T)
|
|
||||||
|
|
||||||
# Location where this expectation was defined.
|
|
||||||
getter source : Source
|
|
||||||
|
|
||||||
# Creates the partial.
|
|
||||||
def initialize(@actual : TestExpression(T), @source : Source)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Asserts that some criteria defined by the matcher is satisfied.
|
|
||||||
def to(matcher) : Nil
|
|
||||||
match_data = matcher.match(@actual)
|
|
||||||
report(match_data)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to(stub : Mocks::MethodStub) : Nil
|
|
||||||
Harness.current.mocks.expect(@actual.value, stub)
|
|
||||||
value = TestValue.new(stub.name, stub.to_s)
|
|
||||||
matcher = Matchers::ReceiveMatcher.new(value, stub.arguments?)
|
|
||||||
to_eventually(matcher)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to(stubs : Enumerable(Mocks::MethodStub)) : Nil
|
|
||||||
stubs.each { |stub| to(stub) }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Asserts that some criteria defined by the matcher is not satisfied.
|
|
||||||
# This is effectively the opposite of `#to`.
|
|
||||||
def to_not(matcher) : Nil
|
|
||||||
match_data = matcher.negated_match(@actual)
|
|
||||||
report(match_data)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_not(stub : Mocks::MethodStub) : Nil
|
|
||||||
value = TestValue.new(stub.name, stub.to_s)
|
|
||||||
matcher = Matchers::ReceiveMatcher.new(value, stub.arguments?)
|
|
||||||
to_never(matcher)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_not(stubs : Enumerable(Mocks::MethodStub)) : Nil
|
|
||||||
stubs.each { |stub| to_not(stub) }
|
|
||||||
end
|
|
||||||
|
|
||||||
# :ditto:
|
|
||||||
@[AlwaysInline]
|
|
||||||
def not_to(matcher) : Nil
|
|
||||||
to_not(matcher)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Asserts that some criteria defined by the matcher is eventually satisfied.
|
|
||||||
# The expectation is checked after the example finishes and all hooks have run.
|
|
||||||
def to_eventually(matcher) : Nil
|
|
||||||
Harness.current.defer { to(matcher) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_eventually(stub : Mocks::MethodStub) : Nil
|
|
||||||
to(stub)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_eventually(stubs : Enumerable(Mocks::MethodStub)) : Nil
|
|
||||||
to(stub)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Asserts that some criteria defined by the matcher is never satisfied.
|
|
||||||
# The expectation is checked after the example finishes and all hooks have run.
|
|
||||||
def to_never(matcher) : Nil
|
|
||||||
Harness.current.defer { to_not(matcher) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_never(stub : Mocks::MethodStub) : Nil
|
|
||||||
to_not(stub)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_never(stub : Enumerable(Mocks::MethodStub)) : Nil
|
|
||||||
to_not(stub)
|
|
||||||
end
|
|
||||||
|
|
||||||
# :ditto:
|
|
||||||
@[AlwaysInline]
|
|
||||||
def never_to(matcher) : Nil
|
|
||||||
to_never(matcher)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Reports an expectation to the current harness.
|
|
||||||
private def report(match_data : Matchers::MatchData)
|
|
||||||
expectation = Expectation.new(match_data, @source)
|
|
||||||
Harness.current.report_expectation(expectation)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,34 +0,0 @@
|
||||||
module Spectator::Expectations
|
|
||||||
# Tracks the expectations and their outcomes in an example.
|
|
||||||
# A single instance of this class should be associated with one example.
|
|
||||||
class ExpectationReporter
|
|
||||||
# All expectations are stored in this array.
|
|
||||||
# The initial capacity is set to one,
|
|
||||||
# as that is the typical (and recommended)
|
|
||||||
# number of expectations per example.
|
|
||||||
@expectations = Array(Expectation).new(1)
|
|
||||||
|
|
||||||
# Creates the reporter.
|
|
||||||
# When the *raise_on_failure* flag is set to true,
|
|
||||||
# which is the default, an exception will be raised
|
|
||||||
# on the first failure that is reported.
|
|
||||||
# To store failures and continue, set the flag to false.
|
|
||||||
def initialize(@raise_on_failure = true)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Stores the outcome of an expectation.
|
|
||||||
# If the raise on failure flag is set to true,
|
|
||||||
# then this method will raise an exception
|
|
||||||
# when a failing result is given.
|
|
||||||
def report(expectation : Expectation) : Nil
|
|
||||||
@expectations << expectation
|
|
||||||
raise ExpectationFailed.new(expectation) if !expectation.satisfied? && @raise_on_failure
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns the reported expectations from the example.
|
|
||||||
# This should be run after the example has finished.
|
|
||||||
def expectations : ExampleExpectations
|
|
||||||
ExampleExpectations.new(@expectations)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -15,18 +15,20 @@ module Spectator
|
||||||
# This sets up the harness so that the test code can use it.
|
# This sets up the harness so that the test code can use it.
|
||||||
# The framework does the following:
|
# The framework does the following:
|
||||||
# ```
|
# ```
|
||||||
# result = Harness.run { delegate.call(example) }
|
# result = Harness.run { run_example_code }
|
||||||
# # Do something with the result.
|
# # Do something with the result.
|
||||||
# ```
|
# ```
|
||||||
|
#
|
||||||
# Then from the test code, the harness can be accessed via `.current` like so:
|
# Then from the test code, the harness can be accessed via `.current` like so:
|
||||||
# ```
|
# ```
|
||||||
# harness = ::Spectator::Harness.current
|
# harness = ::Spectator::Harness.current
|
||||||
# # Do something with the harness.
|
# # Do something with the harness.
|
||||||
# ```
|
# ```
|
||||||
|
#
|
||||||
# Of course, the end-user shouldn't see this or work directly with the harness.
|
# Of course, the end-user shouldn't see this or work directly with the harness.
|
||||||
# Instead, methods the user calls can access it.
|
# Instead, methods the test calls can access it.
|
||||||
# For instance, an assertion reporting a result.
|
# For instance, an expectation reporting a result.
|
||||||
private class Harness
|
class Harness
|
||||||
# Retrieves the harness for the current running example.
|
# Retrieves the harness for the current running example.
|
||||||
class_getter! current : self
|
class_getter! current : self
|
||||||
|
|
||||||
|
@ -54,6 +56,10 @@ module Spectator
|
||||||
translate(*outcome)
|
translate(*outcome)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def report(expectation)
|
||||||
|
# TODO
|
||||||
|
end
|
||||||
|
|
||||||
# Stores a block of code to be executed later.
|
# Stores a block of code to be executed later.
|
||||||
# All deferred blocks run just before the `#run` method completes.
|
# All deferred blocks run just before the `#run` method completes.
|
||||||
def defer(&block) : Nil
|
def defer(&block) : Nil
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
require "../test_value"
|
require "../value"
|
||||||
require "./failed_match_data"
|
require "./failed_match_data"
|
||||||
require "./matcher"
|
require "./matcher"
|
||||||
require "./successful_match_data"
|
require "./successful_match_data"
|
||||||
|
@ -21,8 +21,8 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Actually performs the test against the expression.
|
# Actually performs the test against the expression.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
found = test_values(actual).each do |element|
|
found = values(actual).each do |element|
|
||||||
match_data = matcher.match(element)
|
match_data = matcher.match(element)
|
||||||
break match_data unless match_data.matched?
|
break match_data unless match_data.matched?
|
||||||
end
|
end
|
||||||
|
@ -39,18 +39,18 @@ module Spectator::Matchers
|
||||||
# What if the collection is empty?
|
# What if the collection is empty?
|
||||||
#
|
#
|
||||||
# RSpec doesn't support this syntax either.
|
# RSpec doesn't support this syntax either.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
{% raise "The `expect { }.to_not all()` syntax is not supported (ambiguous)." %}
|
{% raise "The `expect { }.to_not all()` syntax is not supported (ambiguous)." %}
|
||||||
end
|
end
|
||||||
|
|
||||||
# Maps all values in the test collection to their own test values.
|
# Maps all values in the test collection to their own test values.
|
||||||
# Each value is given their own label,
|
# Each value is given their own label,
|
||||||
# which is the original label with an index appended.
|
# which is the original label with an index appended.
|
||||||
private def test_values(actual)
|
private def values(actual)
|
||||||
label_prefix = actual.label
|
label_prefix = actual.label
|
||||||
actual.value.map_with_index do |value, index|
|
actual.value.map_with_index do |value, index|
|
||||||
label = "#{label_prefix}[#{index}]"
|
label = "#{label_prefix}[#{index}]"
|
||||||
TestValue.new(value, label)
|
Value.new(value, label)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,7 +11,7 @@ module Spectator::Matchers
|
||||||
private getter expected
|
private getter expected
|
||||||
|
|
||||||
# Creates the matcher with an expected value.
|
# Creates the matcher with an expected value.
|
||||||
def initialize(@expected : TestValue(Array(ExpectedType)))
|
def initialize(@expected : Value(Array(ExpectedType)))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Short text about the matcher's purpose.
|
# Short text about the matcher's purpose.
|
||||||
|
@ -22,7 +22,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Actually performs the test against the expression.
|
# Actually performs the test against the expression.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
actual_value = actual.value
|
actual_value = actual.value
|
||||||
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:to_a)
|
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:to_a)
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Performs the test against the expression, but inverted.
|
# Performs the test against the expression, but inverted.
|
||||||
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
actual_value = actual.value
|
actual_value = actual.value
|
||||||
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:to_a)
|
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:to_a)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
require "../test_value"
|
require "../value"
|
||||||
require "./failed_match_data"
|
require "./failed_match_data"
|
||||||
require "./matcher"
|
require "./matcher"
|
||||||
require "./successful_match_data"
|
require "./successful_match_data"
|
||||||
|
@ -14,7 +14,7 @@ module Spectator::Matchers
|
||||||
private getter expected
|
private getter expected
|
||||||
|
|
||||||
# Creates the matcher with an expected value.
|
# Creates the matcher with an expected value.
|
||||||
def initialize(@expected : TestValue(ExpectedType))
|
def initialize(@expected : Value(ExpectedType))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Short text about the matcher's purpose.
|
# Short text about the matcher's purpose.
|
||||||
|
@ -25,7 +25,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Actually performs the test against the expression.
|
# Actually performs the test against the expression.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
snapshot = snapshot_values(actual.value)
|
snapshot = snapshot_values(actual.value)
|
||||||
if match?(snapshot)
|
if match?(snapshot)
|
||||||
SuccessfulMatchData.new(description)
|
SuccessfulMatchData.new(description)
|
||||||
|
@ -36,7 +36,7 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Performs the test against the expression, but inverted.
|
# Performs the test against the expression, but inverted.
|
||||||
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
snapshot = snapshot_values(actual.value)
|
snapshot = snapshot_values(actual.value)
|
||||||
if match?(snapshot)
|
if match?(snapshot)
|
||||||
FailedMatchData.new(description, "#{actual.label} has attributes #{expected.label}", negated_values(snapshot).to_a)
|
FailedMatchData.new(description, "#{actual.label} has attributes #{expected.label}", negated_values(snapshot).to_a)
|
||||||
|
|
|
@ -12,13 +12,13 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
expected.value === actual.value
|
expected.value === actual.value
|
||||||
end
|
end
|
||||||
|
|
||||||
# Overload that takes a regex so that the operands are flipped.
|
# Overload that takes a regex so that the operands are flipped.
|
||||||
# This mimics RSpec's behavior.
|
# This mimics RSpec's behavior.
|
||||||
private def match?(actual : TestExpression(Regex)) : Bool forall T
|
private def match?(actual : Expression(Regex)) : Bool forall T
|
||||||
actual.value === expected.value
|
actual.value === expected.value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ module Spectator::Matchers
|
||||||
private getter expected_after
|
private getter expected_after
|
||||||
|
|
||||||
# Creates a new change matcher.
|
# Creates a new change matcher.
|
||||||
def initialize(@expression : TestBlock(ExpressionType), @expected_before : FromType, @expected_after : ToType)
|
def initialize(@expression : Block(ExpressionType), @expected_before : FromType, @expected_after : ToType)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Short text about the matcher's purpose.
|
# Short text about the matcher's purpose.
|
||||||
|
@ -26,7 +26,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Actually performs the test against the expression.
|
# Actually performs the test against the expression.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
before, after = change(actual)
|
before, after = change(actual)
|
||||||
if expected_before == before
|
if expected_before == before
|
||||||
if before == after
|
if before == after
|
||||||
|
@ -53,7 +53,7 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Performs the test against the expression, but inverted.
|
# Performs the test against the expression, but inverted.
|
||||||
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
before, after = change(actual)
|
before, after = change(actual)
|
||||||
if expected_before == before
|
if expected_before == before
|
||||||
if expected_after == after
|
if expected_after == after
|
||||||
|
|
|
@ -13,7 +13,7 @@ module Spectator::Matchers
|
||||||
private getter expected
|
private getter expected
|
||||||
|
|
||||||
# Creates a new change matcher.
|
# Creates a new change matcher.
|
||||||
def initialize(@expression : TestBlock(ExpressionType), @expected : FromType)
|
def initialize(@expression : Block(ExpressionType), @expected : FromType)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Short text about the matcher's purpose.
|
# Short text about the matcher's purpose.
|
||||||
|
@ -24,7 +24,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Actually performs the test against the expression.
|
# Actually performs the test against the expression.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
before, after = change(actual)
|
before, after = change(actual)
|
||||||
if expected != before
|
if expected != before
|
||||||
FailedMatchData.new(description, "#{expression.label} was not initially #{expected}",
|
FailedMatchData.new(description, "#{expression.label} was not initially #{expected}",
|
||||||
|
@ -44,7 +44,7 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Performs the test against the expression, but inverted.
|
# Performs the test against the expression, but inverted.
|
||||||
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
before, after = change(actual)
|
before, after = change(actual)
|
||||||
if expected != before
|
if expected != before
|
||||||
FailedMatchData.new(description, "#{expression.label} was not initially #{expected}",
|
FailedMatchData.new(description, "#{expression.label} was not initially #{expected}",
|
||||||
|
|
|
@ -11,7 +11,7 @@ module Spectator::Matchers
|
||||||
private getter expression
|
private getter expression
|
||||||
|
|
||||||
# Creates a new change matcher.
|
# Creates a new change matcher.
|
||||||
def initialize(@expression : TestBlock(ExpressionType))
|
def initialize(@expression : Block(ExpressionType))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Short text about the matcher's purpose.
|
# Short text about the matcher's purpose.
|
||||||
|
@ -22,7 +22,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Actually performs the test against the expression.
|
# Actually performs the test against the expression.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
before, after = change(actual)
|
before, after = change(actual)
|
||||||
if before == after
|
if before == after
|
||||||
FailedMatchData.new(description, "#{actual.label} did not change #{expression.label}",
|
FailedMatchData.new(description, "#{actual.label} did not change #{expression.label}",
|
||||||
|
@ -36,7 +36,7 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Performs the test against the expression, but inverted.
|
# Performs the test against the expression, but inverted.
|
||||||
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
before, after = change(actual)
|
before, after = change(actual)
|
||||||
if before == after
|
if before == after
|
||||||
SuccessfulMatchData.new(description)
|
SuccessfulMatchData.new(description)
|
||||||
|
|
|
@ -9,7 +9,7 @@ module Spectator::Matchers
|
||||||
private getter expression
|
private getter expression
|
||||||
|
|
||||||
# Creates a new change matcher.
|
# Creates a new change matcher.
|
||||||
def initialize(@expression : TestBlock(ExpressionType), @relativity : String,
|
def initialize(@expression : Block(ExpressionType), @relativity : String,
|
||||||
&evaluator : ExpressionType, ExpressionType -> Bool)
|
&evaluator : ExpressionType, ExpressionType -> Bool)
|
||||||
@evaluator = evaluator
|
@evaluator = evaluator
|
||||||
end
|
end
|
||||||
|
@ -22,7 +22,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Actually performs the test against the expression.
|
# Actually performs the test against the expression.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
before, after = change(actual)
|
before, after = change(actual)
|
||||||
if before == after
|
if before == after
|
||||||
FailedMatchData.new(description, "#{actual.label} did not change #{expression.label}",
|
FailedMatchData.new(description, "#{actual.label} did not change #{expression.label}",
|
||||||
|
@ -41,7 +41,7 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Performs the test against the expression, but inverted.
|
# Performs the test against the expression, but inverted.
|
||||||
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
{% raise "The `expect { }.to_not change { }.by_...()` syntax is not supported (ambiguous)." %}
|
{% raise "The `expect { }.to_not change { }.by_...()` syntax is not supported (ambiguous)." %}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ module Spectator::Matchers
|
||||||
private getter expected
|
private getter expected
|
||||||
|
|
||||||
# Creates a new change matcher.
|
# Creates a new change matcher.
|
||||||
def initialize(@expression : TestBlock(ExpressionType), @expected : ToType)
|
def initialize(@expression : Block(ExpressionType), @expected : ToType)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Short text about the matcher's purpose.
|
# Short text about the matcher's purpose.
|
||||||
|
@ -24,7 +24,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Actually performs the test against the expression.
|
# Actually performs the test against the expression.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
before, after = change(actual)
|
before, after = change(actual)
|
||||||
if before == after
|
if before == after
|
||||||
FailedMatchData.new(description, "#{actual.label} did not change #{expression.label}",
|
FailedMatchData.new(description, "#{actual.label} did not change #{expression.label}",
|
||||||
|
@ -52,7 +52,7 @@ module Spectator::Matchers
|
||||||
# but it is the expected value?
|
# but it is the expected value?
|
||||||
#
|
#
|
||||||
# RSpec doesn't support this syntax either.
|
# RSpec doesn't support this syntax either.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
{% raise "The `expect { }.to_not change { }.to()` syntax is not supported (ambiguous)." %}
|
{% raise "The `expect { }.to_not change { }.to()` syntax is not supported (ambiguous)." %}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
require "../test_value"
|
require "../value"
|
||||||
require "./range_matcher"
|
require "./range_matcher"
|
||||||
require "./value_matcher"
|
require "./value_matcher"
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
expected.value.includes?(actual.value)
|
expected.value.includes?(actual.value)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -55,8 +55,8 @@ module Spectator::Matchers
|
||||||
lower = center - diff
|
lower = center - diff
|
||||||
upper = center + diff
|
upper = center + diff
|
||||||
range = Range.new(lower, upper)
|
range = Range.new(lower, upper)
|
||||||
test_value = TestValue.new(range, "#{center} ± #{expected.label}")
|
value = Value.new(range, "#{center} ± #{expected.label}")
|
||||||
RangeMatcher.new(test_value)
|
RangeMatcher.new(value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ module Spectator::Matchers
|
||||||
private getter expected
|
private getter expected
|
||||||
|
|
||||||
# Creates the matcher with an expected value.
|
# Creates the matcher with an expected value.
|
||||||
def initialize(@expected : TestValue(ExpectedType))
|
def initialize(@expected : Value(ExpectedType))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Short text about the matcher's purpose.
|
# Short text about the matcher's purpose.
|
||||||
|
@ -19,7 +19,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Actually performs the test against the expression.
|
# Actually performs the test against the expression.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
actual_value = actual.value
|
actual_value = actual.value
|
||||||
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:includes?)
|
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:includes?)
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Performs the test against the expression, but inverted.
|
# Performs the test against the expression, but inverted.
|
||||||
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
actual_value = actual.value
|
actual_value = actual.value
|
||||||
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:includes?)
|
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:includes?)
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
actual_value = actual.value
|
actual_value = actual.value
|
||||||
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:empty?)
|
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:empty?)
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ module Spectator::Matchers
|
||||||
private getter expected
|
private getter expected
|
||||||
|
|
||||||
# Creates the matcher with an expected value.
|
# Creates the matcher with an expected value.
|
||||||
def initialize(@expected : TestValue(ExpectedType))
|
def initialize(@expected : Value(ExpectedType))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Short text about the matcher's purpose.
|
# Short text about the matcher's purpose.
|
||||||
|
@ -22,7 +22,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Actually performs the test against the expression.
|
# Actually performs the test against the expression.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
value = actual.value
|
value = actual.value
|
||||||
if value.is_a?(String) || value.responds_to?(:ends_with?)
|
if value.is_a?(String) || value.responds_to?(:ends_with?)
|
||||||
match_ends_with(value, actual.label)
|
match_ends_with(value, actual.label)
|
||||||
|
@ -33,7 +33,7 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Performs the test against the expression, but inverted.
|
# Performs the test against the expression, but inverted.
|
||||||
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
value = actual.value
|
value = actual.value
|
||||||
if value.is_a?(String) || value.responds_to?(:ends_with?)
|
if value.is_a?(String) || value.responds_to?(:ends_with?)
|
||||||
negated_match_ends_with(value, actual.label)
|
negated_match_ends_with(value, actual.label)
|
||||||
|
|
|
@ -12,7 +12,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
expected.value == actual.value
|
expected.value == actual.value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
require "../test_value"
|
require "../value"
|
||||||
require "./failed_match_data"
|
require "./failed_match_data"
|
||||||
require "./matcher"
|
require "./matcher"
|
||||||
require "./successful_match_data"
|
require "./successful_match_data"
|
||||||
|
@ -11,11 +11,11 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Creates the matcher with no expectation of the message.
|
# Creates the matcher with no expectation of the message.
|
||||||
def initialize
|
def initialize
|
||||||
@expected = TestValue.new(nil, ExceptionType.to_s)
|
@expected = Value.new(nil, ExceptionType.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates the matcher with an expected message.
|
# Creates the matcher with an expected message.
|
||||||
def initialize(@expected : TestValue(ExpectedType))
|
def initialize(@expected : Value(ExpectedType))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Short text about the matcher's purpose.
|
# Short text about the matcher's purpose.
|
||||||
|
@ -30,7 +30,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Actually performs the test against the expression.
|
# Actually performs the test against the expression.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
exception = capture_exception { actual.value }
|
exception = capture_exception { actual.value }
|
||||||
if exception.nil?
|
if exception.nil?
|
||||||
FailedMatchData.new(description, "#{actual.label} did not raise", expected: ExceptionType.inspect)
|
FailedMatchData.new(description, "#{actual.label} did not raise", expected: ExceptionType.inspect)
|
||||||
|
@ -61,7 +61,7 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Performs the test against the expression, but inverted.
|
# Performs the test against the expression, but inverted.
|
||||||
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
exception = capture_exception { actual.value }
|
exception = capture_exception { actual.value }
|
||||||
if exception.nil?
|
if exception.nil?
|
||||||
SuccessfulMatchData.new(description)
|
SuccessfulMatchData.new(description)
|
||||||
|
@ -91,7 +91,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_message(message : T) forall T
|
def with_message(message : T) forall T
|
||||||
value = TestValue.new(message)
|
value = Value.new(message)
|
||||||
ExceptionMatcher(ExceptionType, T).new(value)
|
ExceptionMatcher(ExceptionType, T).new(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -114,13 +114,13 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Creates a new exception matcher with a message check.
|
# Creates a new exception matcher with a message check.
|
||||||
def self.create(value, label : String)
|
def self.create(value, label : String)
|
||||||
expected = TestValue.new(value, label)
|
expected = Value.new(value, label)
|
||||||
ExceptionMatcher(Exception, typeof(value)).new(expected)
|
ExceptionMatcher(Exception, typeof(value)).new(expected)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates a new exception matcher with a type and message check.
|
# Creates a new exception matcher with a type and message check.
|
||||||
def self.create(exception_type : T.class, value, label : String) forall T
|
def self.create(exception_type : T.class, value, label : String) forall T
|
||||||
expected = TestValue.new(value, label)
|
expected = Value.new(value, label)
|
||||||
ExceptionMatcher(T, typeof(value)).new(expected)
|
ExceptionMatcher(T, typeof(value)).new(expected)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
actual.value >= expected.value
|
actual.value >= expected.value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
actual.value > expected.value
|
actual.value > expected.value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
actual_value = actual.value
|
actual_value = actual.value
|
||||||
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:has_key?)
|
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:has_key?)
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ module Spectator::Matchers
|
||||||
private getter expected
|
private getter expected
|
||||||
|
|
||||||
# Creates the matcher with an expected value.
|
# Creates the matcher with an expected value.
|
||||||
def initialize(@expected : TestValue(ExpectedType))
|
def initialize(@expected : Value(ExpectedType))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Short text about the matcher's purpose.
|
# Short text about the matcher's purpose.
|
||||||
|
@ -20,7 +20,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Entrypoint for the matcher, forwards to the correct method for string or enumerable.
|
# Entrypoint for the matcher, forwards to the correct method for string or enumerable.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
if (value = actual.value).is_a?(String)
|
if (value = actual.value).is_a?(String)
|
||||||
match_string(value, actual.label)
|
match_string(value, actual.label)
|
||||||
else
|
else
|
||||||
|
@ -70,7 +70,7 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Performs the test against the expression, but inverted.
|
# Performs the test against the expression, but inverted.
|
||||||
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
if (value = actual.value).is_a?(String)
|
if (value = actual.value).is_a?(String)
|
||||||
negated_match_string(value, actual.label)
|
negated_match_string(value, actual.label)
|
||||||
else
|
else
|
||||||
|
|
|
@ -15,7 +15,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Actually performs the test against the expression.
|
# Actually performs the test against the expression.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
snapshot = snapshot_values(actual.value)
|
snapshot = snapshot_values(actual.value)
|
||||||
if match?(snapshot)
|
if match?(snapshot)
|
||||||
SuccessfulMatchData.new(description)
|
SuccessfulMatchData.new(description)
|
||||||
|
@ -26,7 +26,7 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Performs the test against the expression, but inverted.
|
# Performs the test against the expression, but inverted.
|
||||||
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
snapshot = snapshot_values(actual.value)
|
snapshot = snapshot_values(actual.value)
|
||||||
if match?(snapshot)
|
if match?(snapshot)
|
||||||
FailedMatchData.new(description, "#{actual.label} has #{expected.label}", values(snapshot).to_a)
|
FailedMatchData.new(description, "#{actual.label} has #{expected.label}", values(snapshot).to_a)
|
||||||
|
|
|
@ -12,7 +12,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
actual_value = actual.value
|
actual_value = actual.value
|
||||||
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:has_value?)
|
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:has_value?)
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
expected.value != actual.value
|
expected.value != actual.value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
actual.value.class == Expected
|
actual.value.class == Expected
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
actual.value <= expected.value
|
actual.value <= expected.value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
actual.value < expected.value
|
actual.value < expected.value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,10 @@ module Spectator::Matchers
|
||||||
abstract def description : String
|
abstract def description : String
|
||||||
|
|
||||||
# Actually performs the test against the expression (value or block).
|
# Actually performs the test against the expression (value or block).
|
||||||
abstract def match(actual : TestExpression(T)) : MatchData forall T
|
abstract def match(actual : Expression(T)) : MatchData forall T
|
||||||
|
|
||||||
# Performs the test against the expression (value or block), but inverted.
|
# Performs the test against the expression (value or block), but inverted.
|
||||||
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
abstract def negated_match(actual : TestExpression(T)) : MatchData forall T
|
abstract def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
actual.value.nil?
|
actual.value.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ module Spectator::Matchers
|
||||||
private getter expected
|
private getter expected
|
||||||
|
|
||||||
# Creates the matcher with a expected values.
|
# Creates the matcher with a expected values.
|
||||||
def initialize(@expected : TestValue(ExpectedType))
|
def initialize(@expected : Value(ExpectedType))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Short text about the matcher's purpose.
|
# Short text about the matcher's purpose.
|
||||||
|
@ -21,7 +21,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Actually performs the test against the expression.
|
# Actually performs the test against the expression.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
snapshot = snapshot_values(actual.value)
|
snapshot = snapshot_values(actual.value)
|
||||||
if match?(snapshot)
|
if match?(snapshot)
|
||||||
SuccessfulMatchData.new(description)
|
SuccessfulMatchData.new(description)
|
||||||
|
@ -32,7 +32,7 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Performs the test against the expression, but inverted.
|
# Performs the test against the expression, but inverted.
|
||||||
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
snapshot = snapshot_values(actual.value)
|
snapshot = snapshot_values(actual.value)
|
||||||
if match?(snapshot)
|
if match?(snapshot)
|
||||||
FailedMatchData.new(description, "#{actual.label} is #{expected.label}", values(snapshot).to_a)
|
FailedMatchData.new(description, "#{actual.label} is #{expected.label}", values(snapshot).to_a)
|
||||||
|
|
|
@ -15,7 +15,7 @@ module Spectator::Matchers
|
||||||
def inclusive
|
def inclusive
|
||||||
label = expected.label
|
label = expected.label
|
||||||
new_range = Range.new(range.begin, range.end, exclusive: false)
|
new_range = Range.new(range.begin, range.end, exclusive: false)
|
||||||
expected = TestValue.new(new_range, label)
|
expected = Value.new(new_range, label)
|
||||||
RangeMatcher.new(expected)
|
RangeMatcher.new(expected)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -23,12 +23,12 @@ module Spectator::Matchers
|
||||||
def exclusive
|
def exclusive
|
||||||
label = expected.label
|
label = expected.label
|
||||||
new_range = Range.new(range.begin, range.end, exclusive: true)
|
new_range = Range.new(range.begin, range.end, exclusive: true)
|
||||||
expected = TestValue.new(new_range, label)
|
expected = Value.new(new_range, label)
|
||||||
RangeMatcher.new(expected)
|
RangeMatcher.new(expected)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
expected.value.includes?(actual.value)
|
expected.value.includes?(actual.value)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ module Spectator::Matchers
|
||||||
struct ReceiveMatcher < StandardMatcher
|
struct ReceiveMatcher < StandardMatcher
|
||||||
alias Range = ::Range(Int32, Int32) | ::Range(Nil, Int32) | ::Range(Int32, Nil)
|
alias Range = ::Range(Int32, Int32) | ::Range(Nil, Int32) | ::Range(Int32, Nil)
|
||||||
|
|
||||||
def initialize(@expected : TestExpression(Symbol), @args : Mocks::Arguments? = nil, @range : Range? = nil)
|
def initialize(@expected : Expression(Symbol), @args : Mocks::Arguments? = nil, @range : Range? = nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
def description : String
|
def description : String
|
||||||
|
@ -13,7 +13,7 @@ module Spectator::Matchers
|
||||||
"received message #{@expected.label} #{range ? "#{humanize_range(range)} time(s)" : "At least once"} with #{@args || "any arguments"}"
|
"received message #{@expected.label} #{range ? "#{humanize_range(range)} time(s)" : "At least once"} with #{@args || "any arguments"}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def match?(actual : TestExpression(T)) : Bool forall T
|
def match?(actual : Expression(T)) : Bool forall T
|
||||||
calls = Harness.current.mocks.calls_for(actual.value, @expected.value)
|
calls = Harness.current.mocks.calls_for(actual.value, @expected.value)
|
||||||
calls.select! { |call| @args === call.args } if @args
|
calls.select! { |call| @args === call.args } if @args
|
||||||
if (range = @range)
|
if (range = @range)
|
||||||
|
@ -23,7 +23,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def failure_message(actual : TestExpression(T)) : String forall T
|
def failure_message(actual : Expression(T)) : String forall T
|
||||||
range = @range
|
range = @range
|
||||||
"#{actual.label} did not receive #{@expected.label} #{range ? "#{humanize_range(range)} time(s)" : "at least once"} with #{@args || "any arguments"}"
|
"#{actual.label} did not receive #{@expected.label} #{range ? "#{humanize_range(range)} time(s)" : "at least once"} with #{@args || "any arguments"}"
|
||||||
end
|
end
|
||||||
|
@ -33,7 +33,7 @@ module Spectator::Matchers
|
||||||
"#{actual.label} received #{@expected.label} #{range ? "#{humanize_range(range)} time(s)" : "at least once"} with #{@args || "any arguments"}"
|
"#{actual.label} received #{@expected.label} #{range ? "#{humanize_range(range)} time(s)" : "at least once"} with #{@args || "any arguments"}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def values(actual : TestExpression(T)) forall T
|
def values(actual : Expression(T)) forall T
|
||||||
calls = Harness.current.mocks.calls_for(actual.value, @expected.value)
|
calls = Harness.current.mocks.calls_for(actual.value, @expected.value)
|
||||||
calls.select! { |call| @args === call.args } if @args
|
calls.select! { |call| @args === call.args } if @args
|
||||||
range = @range
|
range = @range
|
||||||
|
@ -43,7 +43,7 @@ module Spectator::Matchers
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def negated_values(actual : TestExpression(T)) forall T
|
def negated_values(actual : Expression(T)) forall T
|
||||||
calls = Harness.current.mocks.calls_for(actual.value, @expected.value)
|
calls = Harness.current.mocks.calls_for(actual.value, @expected.value)
|
||||||
calls.select! { |call| @args === call.args } if @args
|
calls.select! { |call| @args === call.args } if @args
|
||||||
range = @range
|
range = @range
|
||||||
|
@ -115,7 +115,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
private struct Count
|
private struct Count
|
||||||
def initialize(@expected : TestExpression(Symbol), @args : Mocks::Arguments?, @range : Range)
|
def initialize(@expected : Expression(Symbol), @args : Mocks::Arguments?, @range : Range)
|
||||||
end
|
end
|
||||||
|
|
||||||
def times
|
def times
|
||||||
|
|
|
@ -5,7 +5,7 @@ module Spectator::Matchers
|
||||||
struct ReceiveTypeMatcher < StandardMatcher
|
struct ReceiveTypeMatcher < StandardMatcher
|
||||||
alias Range = ::Range(Int32, Int32) | ::Range(Nil, Int32) | ::Range(Int32, Nil)
|
alias Range = ::Range(Int32, Int32) | ::Range(Nil, Int32) | ::Range(Int32, Nil)
|
||||||
|
|
||||||
def initialize(@expected : TestExpression(Symbol), @args : Mocks::Arguments? = nil, @range : Range? = nil)
|
def initialize(@expected : Expression(Symbol), @args : Mocks::Arguments? = nil, @range : Range? = nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
def description : String
|
def description : String
|
||||||
|
@ -13,7 +13,7 @@ module Spectator::Matchers
|
||||||
"received message #{@expected.label} #{range ? "#{humanize_range(range)} time(s)" : "At least once"} with #{@args || "any arguments"}"
|
"received message #{@expected.label} #{range ? "#{humanize_range(range)} time(s)" : "At least once"} with #{@args || "any arguments"}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def match?(actual : TestExpression(T)) : Bool forall T
|
def match?(actual : Expression(T)) : Bool forall T
|
||||||
calls = Harness.current.mocks.calls_for_type(actual.value, @expected.value)
|
calls = Harness.current.mocks.calls_for_type(actual.value, @expected.value)
|
||||||
calls.select! { |call| @args === call.args } if @args
|
calls.select! { |call| @args === call.args } if @args
|
||||||
if (range = @range)
|
if (range = @range)
|
||||||
|
@ -23,17 +23,17 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def failure_message(actual : TestExpression(T)) : String forall T
|
def failure_message(actual : Expression(T)) : String forall T
|
||||||
range = @range
|
range = @range
|
||||||
"#{actual.label} did not receive #{@expected.label} #{range ? "#{humanize_range(range)} time(s)" : "at least once"} with #{@args || "any arguments"}"
|
"#{actual.label} did not receive #{@expected.label} #{range ? "#{humanize_range(range)} time(s)" : "at least once"} with #{@args || "any arguments"}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def failure_message_when_negated(actual : TestExpression(T)) : String forall T
|
def failure_message_when_negated(actual : Expression(T)) : String forall T
|
||||||
range = @range
|
range = @range
|
||||||
"#{actual.label} received #{@expected.label} #{range ? "#{humanize_range(range)} time(s)" : "at least once"} with #{@args || "any arguments"}"
|
"#{actual.label} received #{@expected.label} #{range ? "#{humanize_range(range)} time(s)" : "at least once"} with #{@args || "any arguments"}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def values(actual : TestExpression(T)) forall T
|
def values(actual : Expression(T)) forall T
|
||||||
calls = Harness.current.mocks.calls_for_type(T, @expected.value)
|
calls = Harness.current.mocks.calls_for_type(T, @expected.value)
|
||||||
calls.select! { |call| @args === call.args } if @args
|
calls.select! { |call| @args === call.args } if @args
|
||||||
range = @range
|
range = @range
|
||||||
|
@ -43,7 +43,7 @@ module Spectator::Matchers
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def negated_values(actual : TestExpression(T)) forall T
|
def negated_values(actual : Expression(T)) forall T
|
||||||
calls = Harness.current.mocks.calls_for_type(T, @expected.value)
|
calls = Harness.current.mocks.calls_for_type(T, @expected.value)
|
||||||
calls.select! { |call| @args === call.args } if @args
|
calls.select! { |call| @args === call.args } if @args
|
||||||
range = @range
|
range = @range
|
||||||
|
@ -115,7 +115,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
private struct Count
|
private struct Count
|
||||||
def initialize(@expected : TestExpression(Symbol), @args : Mocks::Arguments?, @range : Range)
|
def initialize(@expected : Expression(Symbol), @args : Mocks::Arguments?, @range : Range)
|
||||||
end
|
end
|
||||||
|
|
||||||
def times
|
def times
|
||||||
|
|
|
@ -12,7 +12,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
value = expected.value
|
value = expected.value
|
||||||
if value && value.responds_to?(:same?)
|
if value && value.responds_to?(:same?)
|
||||||
value.same?(actual.value)
|
value.same?(actual.value)
|
||||||
|
|
|
@ -14,7 +14,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Actually performs the test against the expression.
|
# Actually performs the test against the expression.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
snapshot = snapshot_values(actual.value)
|
snapshot = snapshot_values(actual.value)
|
||||||
if snapshot.values.all?
|
if snapshot.values.all?
|
||||||
SuccessfulMatchData.new(description)
|
SuccessfulMatchData.new(description)
|
||||||
|
@ -25,7 +25,7 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Performs the test against the expression, but inverted.
|
# Performs the test against the expression, but inverted.
|
||||||
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
snapshot = snapshot_values(actual.value)
|
snapshot = snapshot_values(actual.value)
|
||||||
if snapshot.values.any?
|
if snapshot.values.any?
|
||||||
FailedMatchData.new(description, "#{actual.label} responds to #{label}", values(snapshot).to_a)
|
FailedMatchData.new(description, "#{actual.label} responds to #{label}", values(snapshot).to_a)
|
||||||
|
|
|
@ -12,7 +12,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
actual_value = actual.value
|
actual_value = actual.value
|
||||||
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:size?)
|
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:size?)
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
actual_value = actual.value
|
actual_value = actual.value
|
||||||
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:size?)
|
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:size?)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
require "../test_value"
|
require "../expression"
|
||||||
require "./failed_match_data"
|
require "./failed_match_data"
|
||||||
require "./matcher"
|
require "./matcher"
|
||||||
require "./successful_match_data"
|
require "./successful_match_data"
|
||||||
|
@ -23,7 +23,7 @@ module Spectator::Matchers
|
||||||
# If it returns true, then a `SuccessfulMatchData` instance is returned.
|
# If it returns true, then a `SuccessfulMatchData` instance is returned.
|
||||||
# Otherwise, a `FailedMatchData` instance is returned.
|
# Otherwise, a `FailedMatchData` instance is returned.
|
||||||
# Additionally, `#failure_message` and `#values` are called for a failed match.
|
# Additionally, `#failure_message` and `#values` are called for a failed match.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
if match?(actual)
|
if match?(actual)
|
||||||
SuccessfulMatchData.new(description)
|
SuccessfulMatchData.new(description)
|
||||||
else
|
else
|
||||||
|
@ -38,7 +38,7 @@ module Spectator::Matchers
|
||||||
# If it returns true, then a `SuccessfulMatchData` instance is returned.
|
# If it returns true, then a `SuccessfulMatchData` instance is returned.
|
||||||
# Otherwise, a `FailedMatchData` instance is returned.
|
# Otherwise, a `FailedMatchData` instance is returned.
|
||||||
# Additionally, `#failure_message_when_negated` and `#negated_values` are called for a failed match.
|
# Additionally, `#failure_message_when_negated` and `#negated_values` are called for a failed match.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
# TODO: Invert description.
|
# TODO: Invert description.
|
||||||
if does_not_match?(actual)
|
if does_not_match?(actual)
|
||||||
SuccessfulMatchData.new(description)
|
SuccessfulMatchData.new(description)
|
||||||
|
@ -53,7 +53,7 @@ module Spectator::Matchers
|
||||||
#
|
#
|
||||||
# The message should typically only contain the test expression labels.
|
# The message should typically only contain the test expression labels.
|
||||||
# Actual values should be returned by `#values`.
|
# Actual values should be returned by `#values`.
|
||||||
private abstract def failure_message(actual : TestExpression(T)) : String forall T
|
private abstract def failure_message(actual : Expression(T)) : String forall T
|
||||||
|
|
||||||
# Message displayed when the matcher isn't satisifed and is negated.
|
# Message displayed when the matcher isn't satisifed and is negated.
|
||||||
# This is essentially what would satisfy the matcher if it wasn't negated.
|
# This is essentially what would satisfy the matcher if it wasn't negated.
|
||||||
|
@ -66,12 +66,12 @@ module Spectator::Matchers
|
||||||
#
|
#
|
||||||
# The message should typically only contain the test expression labels.
|
# The message should typically only contain the test expression labels.
|
||||||
# Actual values should be returned by `#values`.
|
# Actual values should be returned by `#values`.
|
||||||
private def failure_message_when_negated(actual : TestExpression(T)) : String forall T
|
private def failure_message_when_negated(actual : Expression(T)) : String forall T
|
||||||
raise "Negation with #{self.class} is not supported."
|
raise "Negation with #{self.class} is not supported."
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private abstract def match?(actual : TestExpression(T)) : Bool forall T
|
private abstract def match?(actual : Expression(T)) : Bool forall T
|
||||||
|
|
||||||
# If the expectation is negated, then this method is called instead of `#match?`.
|
# If the expectation is negated, then this method is called instead of `#match?`.
|
||||||
#
|
#
|
||||||
|
@ -79,7 +79,7 @@ module Spectator::Matchers
|
||||||
# If the matcher requires custom handling of negated matches,
|
# If the matcher requires custom handling of negated matches,
|
||||||
# then this method should be overriden.
|
# then this method should be overriden.
|
||||||
# Remember to override `#failure_message_when_negated` as well.
|
# Remember to override `#failure_message_when_negated` as well.
|
||||||
private def does_not_match?(actual : TestExpression(T)) : Bool forall T
|
private def does_not_match?(actual : Expression(T)) : Bool forall T
|
||||||
!match?(actual)
|
!match?(actual)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ module Spectator::Matchers
|
||||||
#
|
#
|
||||||
# The values should typically only contain the test expression values, not the labels.
|
# The values should typically only contain the test expression values, not the labels.
|
||||||
# Labeled should be returned by `#failure_message`.
|
# Labeled should be returned by `#failure_message`.
|
||||||
private def values(actual : TestExpression(T)) forall T
|
private def values(actual : Expression(T)) forall T
|
||||||
{actual: actual.value.inspect}
|
{actual: actual.value.inspect}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ module Spectator::Matchers
|
||||||
#
|
#
|
||||||
# The values should typically only contain the test expression values, not the labels.
|
# The values should typically only contain the test expression values, not the labels.
|
||||||
# Labeled should be returned by `#failure_message_when_negated`.
|
# Labeled should be returned by `#failure_message_when_negated`.
|
||||||
private def negated_values(actual : TestExpression(T)) forall T
|
private def negated_values(actual : Expression(T)) forall T
|
||||||
values(actual)
|
values(actual)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,7 +10,7 @@ module Spectator::Matchers
|
||||||
private getter expected
|
private getter expected
|
||||||
|
|
||||||
# Creates the matcher with an expected value.
|
# Creates the matcher with an expected value.
|
||||||
def initialize(@expected : TestValue(ExpectedType))
|
def initialize(@expected : Value(ExpectedType))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Short text about the matcher's purpose.
|
# Short text about the matcher's purpose.
|
||||||
|
@ -21,7 +21,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Actually performs the test against the expression.
|
# Actually performs the test against the expression.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
value = actual.value
|
value = actual.value
|
||||||
if value.is_a?(String) || value.responds_to?(:starts_with?)
|
if value.is_a?(String) || value.responds_to?(:starts_with?)
|
||||||
match_starts_with(value, actual.label)
|
match_starts_with(value, actual.label)
|
||||||
|
@ -32,7 +32,7 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Performs the test against the expression, but inverted.
|
# Performs the test against the expression, but inverted.
|
||||||
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
value = actual.value
|
value = actual.value
|
||||||
if value.is_a?(String) || value.responds_to?(:starts_with?)
|
if value.is_a?(String) || value.responds_to?(:starts_with?)
|
||||||
negated_match_starts_with(value, actual.label)
|
negated_match_starts_with(value, actual.label)
|
||||||
|
|
|
@ -28,7 +28,7 @@ module Spectator::Matchers
|
||||||
# expect(0).to be < 1
|
# expect(0).to be < 1
|
||||||
# ```
|
# ```
|
||||||
def <(value)
|
def <(value)
|
||||||
expected = TestValue.new(value)
|
expected = Value.new(value)
|
||||||
LessThanMatcher.new(expected)
|
LessThanMatcher.new(expected)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ module Spectator::Matchers
|
||||||
# expect(0).to be <= 1
|
# expect(0).to be <= 1
|
||||||
# ```
|
# ```
|
||||||
def <=(value)
|
def <=(value)
|
||||||
expected = TestValue.new(value)
|
expected = Value.new(value)
|
||||||
LessThanEqualMatcher.new(expected)
|
LessThanEqualMatcher.new(expected)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ module Spectator::Matchers
|
||||||
# expect(2).to be > 1
|
# expect(2).to be > 1
|
||||||
# ```
|
# ```
|
||||||
def >(value)
|
def >(value)
|
||||||
expected = TestValue.new(value)
|
expected = Value.new(value)
|
||||||
GreaterThanMatcher.new(expected)
|
GreaterThanMatcher.new(expected)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ module Spectator::Matchers
|
||||||
# expect(2).to be >= 1
|
# expect(2).to be >= 1
|
||||||
# ```
|
# ```
|
||||||
def >=(value)
|
def >=(value)
|
||||||
expected = TestValue.new(value)
|
expected = Value.new(value)
|
||||||
GreaterThanEqualMatcher.new(expected)
|
GreaterThanEqualMatcher.new(expected)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ module Spectator::Matchers
|
||||||
# expect(0).to be == 0
|
# expect(0).to be == 0
|
||||||
# ```
|
# ```
|
||||||
def ==(value)
|
def ==(value)
|
||||||
expected = TestValue.new(value)
|
expected = Value.new(value)
|
||||||
EqualityMatcher.new(expected)
|
EqualityMatcher.new(expected)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -78,12 +78,12 @@ module Spectator::Matchers
|
||||||
# expect(0).to be != 1
|
# expect(0).to be != 1
|
||||||
# ```
|
# ```
|
||||||
def !=(value)
|
def !=(value)
|
||||||
expected = TestValue.new(value)
|
expected = Value.new(value)
|
||||||
InequalityMatcher.new(expected)
|
InequalityMatcher.new(expected)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
@truthy == !!actual.value
|
@truthy == !!actual.value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks whether the matcher is satisifed with the expression given to it.
|
# Checks whether the matcher is satisifed with the expression given to it.
|
||||||
private def match?(actual : TestExpression(T)) : Bool forall T
|
private def match?(actual : Expression(T)) : Bool forall T
|
||||||
actual.value.is_a?(Expected)
|
actual.value.is_a?(Expected)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ module Spectator::Matchers
|
||||||
private getter expected
|
private getter expected
|
||||||
|
|
||||||
# Creates the matcher with an expected value.
|
# Creates the matcher with an expected value.
|
||||||
def initialize(@expected : TestValue(Array(ExpectedType)))
|
def initialize(@expected : Value(Array(ExpectedType)))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Short text about the matcher's purpose.
|
# Short text about the matcher's purpose.
|
||||||
|
@ -19,7 +19,7 @@ module Spectator::Matchers
|
||||||
end
|
end
|
||||||
|
|
||||||
# Actually performs the test against the expression.
|
# Actually performs the test against the expression.
|
||||||
def match(actual : TestExpression(T)) : MatchData forall T
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
actual_value = actual.value
|
actual_value = actual.value
|
||||||
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:to_a)
|
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:to_a)
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Performs the test against the expression, but inverted.
|
# Performs the test against the expression, but inverted.
|
||||||
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
actual_value = actual.value
|
actual_value = actual.value
|
||||||
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:to_a)
|
return unexpected(actual_value, actual.label) unless actual_value.responds_to?(:to_a)
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ module Spectator::Matchers
|
||||||
|
|
||||||
# Creates the value matcher.
|
# Creates the value matcher.
|
||||||
# The expected value is stored for later use.
|
# The expected value is stored for later use.
|
||||||
def initialize(@expected : TestValue(ExpectedType))
|
def initialize(@expected : Value(ExpectedType))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Additional information about the match failure.
|
# Additional information about the match failure.
|
||||||
|
@ -40,7 +40,7 @@ module Spectator::Matchers
|
||||||
# actual: "bar",
|
# actual: "bar",
|
||||||
# }
|
# }
|
||||||
# ```
|
# ```
|
||||||
private def values(actual : TestExpression(T)) forall T
|
private def values(actual : Expression(T)) forall T
|
||||||
super.merge(expected: expected.value.inspect)
|
super.merge(expected: expected.value.inspect)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ module Spectator::Matchers
|
||||||
# actual: "bar",
|
# actual: "bar",
|
||||||
# }
|
# }
|
||||||
# ```
|
# ```
|
||||||
private def negated_values(actual : TestExpression(T)) forall T
|
private def negated_values(actual : Expression(T)) forall T
|
||||||
super.merge(expected: "Not #{expected.value.inspect}")
|
super.merge(expected: "Not #{expected.value.inspect}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,9 +6,9 @@ module Spectator::Mocks
|
||||||
end
|
end
|
||||||
|
|
||||||
def to(stub : MethodStub) : Nil
|
def to(stub : MethodStub) : Nil
|
||||||
actual = TestValue.new(T)
|
actual = Value.new(T)
|
||||||
Harness.current.mocks.expect(T, stub)
|
Harness.current.mocks.expect(T, stub)
|
||||||
value = TestValue.new(stub.name, stub.to_s)
|
value = Value.new(stub.name, stub.to_s)
|
||||||
matcher = Matchers::ReceiveTypeMatcher.new(value, stub.arguments?)
|
matcher = Matchers::ReceiveTypeMatcher.new(value, stub.arguments?)
|
||||||
partial = Expectations::ExpectationPartial.new(actual, @source)
|
partial = Expectations::ExpectationPartial.new(actual, @source)
|
||||||
partial.to_eventually(matcher)
|
partial.to_eventually(matcher)
|
||||||
|
|
|
@ -17,21 +17,19 @@ class Object
|
||||||
# require "spectator/should"
|
# require "spectator/should"
|
||||||
# ```
|
# ```
|
||||||
def should(matcher)
|
def should(matcher)
|
||||||
# First argument of the `Expectation` initializer is the expression label.
|
actual = ::Spectator::Value.new(self)
|
||||||
# However, since this isn't a macro and we can't "look behind" this method call
|
match_data = matcher.match(actual)
|
||||||
# to see what it was invoked on, the argument is an empty string.
|
expectation = ::Spectator::Expectation.new(match_data)
|
||||||
# Additionally, the source file and line can't be obtained.
|
::Spectator::Harness.current.report(expectation)
|
||||||
actual = ::Spectator::TestValue.new(self)
|
|
||||||
source = ::Spectator::Source.new(__FILE__, __LINE__)
|
|
||||||
::Spectator::Expectations::ExpectationPartial.new(actual, source).to(matcher)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Works the same as `#should` except the condition is inverted.
|
# Works the same as `#should` except the condition is inverted.
|
||||||
# When `#should` succeeds, this method will fail, and vice-versa.
|
# When `#should` succeeds, this method will fail, and vice-versa.
|
||||||
def should_not(matcher)
|
def should_not(matcher)
|
||||||
actual = ::Spectator::TestValue.new(self)
|
actual = ::Spectator::Value.new(self)
|
||||||
source = ::Spectator::Source.new(__FILE__, __LINE__)
|
match_data = matcher.negated_match(actual)
|
||||||
::Spectator::Expectations::ExpectationPartial.new(actual, source).to_not(matcher)
|
expectation = ::Spectator::Expectation.new(match_data)
|
||||||
|
::Spectator::Harness.current.report(expectation)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Works the same as `#should` except that the condition check is postphoned.
|
# Works the same as `#should` except that the condition check is postphoned.
|
||||||
|
@ -51,17 +49,19 @@ struct Proc(*T, R)
|
||||||
# Extension method to create an expectation for a block of code (proc).
|
# Extension method to create an expectation for a block of code (proc).
|
||||||
# Depending on the matcher, the proc may be executed multiple times.
|
# Depending on the matcher, the proc may be executed multiple times.
|
||||||
def should(matcher)
|
def should(matcher)
|
||||||
actual = ::Spectator::TestBlock.new(self)
|
actual = ::Spectator::Block.new(self)
|
||||||
source = ::Spectator::Source.new(__FILE__, __LINE__)
|
match_data = matcher.match(actual)
|
||||||
::Spectator::Expectations::ExpectationPartial.new(actual, source).to(matcher)
|
expectation = ::Spectator::Expectation.new(match_data)
|
||||||
|
::Spectator::Harness.current.report(expectation)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Works the same as `#should` except the condition is inverted.
|
# Works the same as `#should` except the condition is inverted.
|
||||||
# When `#should` succeeds, this method will fail, and vice-versa.
|
# When `#should` succeeds, this method will fail, and vice-versa.
|
||||||
def should_not(matcher)
|
def should_not(matcher)
|
||||||
actual = ::Spectator::TestBlock.new(self)
|
actual = ::Spectator::Block.new(self)
|
||||||
source = ::Spectator::Source.new(__FILE__, __LINE__)
|
match_data = matcher.negated_match(actual)
|
||||||
::Spectator::Expectations::BlockExpectationPartial.new(actual, source).to_not(matcher)
|
expectation = ::Spectator::Expectation.new(match_data)
|
||||||
|
::Spectator::Harness.current.report(expectation)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -149,6 +149,20 @@ module Spectator
|
||||||
current_group.after_each(&block)
|
current_group.after_each(&block)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Attaches a hook to be invoked around every example in the current group.
|
||||||
|
# The current example in procsy form is provided as a block argument.
|
||||||
|
def around_each(hook)
|
||||||
|
Log.trace { "Add around_each hook #{hook}" }
|
||||||
|
current_group.add_around_each_hook(hook)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Defines a block of code to execute around every example in the current group.
|
||||||
|
# The current example in procsy form is provided as a block argument.
|
||||||
|
def around_each(&block : Example -> _)
|
||||||
|
Log.trace { "Add around_each hook" }
|
||||||
|
current_group.around_each(&block)
|
||||||
|
end
|
||||||
|
|
||||||
# Builds the configuration to use for the spec.
|
# Builds the configuration to use for the spec.
|
||||||
# A `ConfigBuilder` is yielded to the block provided to this method.
|
# A `ConfigBuilder` is yielded to the block provided to this method.
|
||||||
# That builder will be used to create the configuration.
|
# That builder will be used to create the configuration.
|
||||||
|
|
|
@ -53,7 +53,7 @@ module Spectator
|
||||||
|
|
||||||
# Prefix with group's full name if the node belongs to a group.
|
# Prefix with group's full name if the node belongs to a group.
|
||||||
if (group = @group)
|
if (group = @group)
|
||||||
io << group
|
group.to_s(io)
|
||||||
|
|
||||||
# Add padding between the node names
|
# Add padding between the node names
|
||||||
# only if the names don't appear to be symbolic.
|
# only if the names don't appear to be symbolic.
|
||||||
|
@ -63,7 +63,7 @@ module Spectator
|
||||||
(name.starts_with?('#') || name.starts_with?('.')))
|
(name.starts_with?('#') || name.starts_with?('.')))
|
||||||
end
|
end
|
||||||
|
|
||||||
io << name
|
name.to_s(io)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,12 +6,15 @@ require "./dsl"
|
||||||
# This type is intentionally outside the `Spectator` module.
|
# This type is intentionally outside the `Spectator` module.
|
||||||
# The reason for this is to prevent name collision when using the DSL to define a spec.
|
# The reason for this is to prevent name collision when using the DSL to define a spec.
|
||||||
class SpectatorTestContext < SpectatorContext
|
class SpectatorTestContext < SpectatorContext
|
||||||
include ::Spectator::DSL::Assertions
|
|
||||||
include ::Spectator::DSL::Examples
|
include ::Spectator::DSL::Examples
|
||||||
|
include ::Spectator::DSL::Expectations
|
||||||
include ::Spectator::DSL::Groups
|
include ::Spectator::DSL::Groups
|
||||||
include ::Spectator::DSL::Hooks
|
include ::Spectator::DSL::Hooks
|
||||||
|
include ::Spectator::DSL::Matchers
|
||||||
include ::Spectator::DSL::Values
|
include ::Spectator::DSL::Values
|
||||||
|
|
||||||
|
@subject = ::Spectator::LazyWrapper.new
|
||||||
|
|
||||||
# Initial implicit subject for tests.
|
# Initial implicit subject for tests.
|
||||||
# This method should be overridden by example groups when an object is described.
|
# This method should be overridden by example groups when an object is described.
|
||||||
private def _spectator_implicit_subject
|
private def _spectator_implicit_subject
|
||||||
|
@ -21,8 +24,7 @@ class SpectatorTestContext < SpectatorContext
|
||||||
# Initial subject for tests.
|
# Initial subject for tests.
|
||||||
# Returns the implicit subject.
|
# Returns the implicit subject.
|
||||||
# This method should be overridden when an explicit subject is defined by the DSL.
|
# This method should be overridden when an explicit subject is defined by the DSL.
|
||||||
# TODO: Subject needs to be cached.
|
|
||||||
private def subject
|
private def subject
|
||||||
_spectator_implicit_subject
|
@subject.get { _spectator_implicit_subject }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,9 +14,16 @@ module Spectator
|
||||||
# Creates the value.
|
# Creates the value.
|
||||||
# Expects the *value* of the expression and a *label* describing it.
|
# Expects the *value* of the expression and a *label* describing it.
|
||||||
# The *label* is usually the Crystal code evaluating to the *value*.
|
# The *label* is usually the Crystal code evaluating to the *value*.
|
||||||
# It can be nil if it isn't available.
|
|
||||||
def initialize(@value : T, label : Label)
|
def initialize(@value : T, label : Label)
|
||||||
super(label)
|
super(label)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Creates the value.
|
||||||
|
# Expects the *value* of the expression.
|
||||||
|
# It can be nil if it isn't available.
|
||||||
|
# A label is generated by calling `#inspect` on the *value*.
|
||||||
|
def initialize(@value : T)
|
||||||
|
super(@value.inspect)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue