Merge branch 'matcher-refactor' into 'release/0.8'

Matcher refactor

See merge request arctic-fox/spectator!13
This commit is contained in:
Mike Miller 2019-08-10 17:14:10 +00:00
commit 1521107934
93 changed files with 1932 additions and 9380 deletions

View file

@ -13,7 +13,7 @@ describe Spectator::ExpectationFailed do
it "is the same as the expectation's #actual_message" do
expectation = new_unsatisfied_expectation
error = Spectator::ExpectationFailed.new(expectation)
error.message.should eq(expectation.actual_message)
error.message.should eq(expectation.failure_message)
end
end
end

View file

@ -1,168 +0,0 @@
require "../spec_helper"
describe Spectator::Expectations::BlockExpectationPartial do
describe "#actual" do
context "with a label" do
it "contains the value passed to the constructor" do
actual = ->{ 777 }
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, actual.to_s, __FILE__, __LINE__)
partial.actual.should eq(actual.call)
end
end
context "without a label" do
it "contains the value passed to the constructor" do
actual = ->{ 777 }
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
partial.actual.should eq(actual.call)
end
end
end
describe "#label" do
context "when provided" do
it "contains the value passed to the constructor" do
actual = ->{ 777 }
label = "lucky"
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, label, __FILE__, __LINE__)
partial.label.should eq(label)
end
end
context "when omitted" do
it "contains \"proc\"" do
actual = ->{ 777 }
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
partial.label.should match(/proc/i)
end
end
end
describe "#source_file" do
it "is the expected value" do
block = ->{ 42 }
file = __FILE__
partial = Spectator::Expectations::BlockExpectationPartial.new(block, file, __LINE__)
partial.source_file.should eq(file)
end
end
describe "#source_line" do
it "is the expected value" do
block = ->{ 42 }
line = __LINE__
partial = Spectator::Expectations::BlockExpectationPartial.new(block, __FILE__, line)
partial.source_line.should eq(line)
end
end
describe "#to" do
it "reports an expectation" do
spy = SpyExample.create do
actual = ->{ 777 }
expected = 777
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
partial.to(matcher)
end
Spectator::Internals::Harness.run(spy)
spy.harness.expectations.size.should eq(1)
end
it "reports multiple expectations" do
spy = SpyExample.create do
actual = ->{ 777 }
expected = 777
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
5.times { partial.to(matcher) }
end
Spectator::Internals::Harness.run(spy)
spy.harness.expectations.size.should eq(5)
end
context "with a met condition" do
it "reports a satisifed expectation" do
spy = SpyExample.create do
actual = ->{ 777 }
expected = 777
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
partial.to(matcher)
end
Spectator::Internals::Harness.run(spy)
spy.harness.expectations.first.satisfied?.should be_true
end
end
context "with an unmet condition" do
it "reports an unsatisfied expectation" do
spy = SpyExample.create do
actual = ->{ 777 }
expected = 42
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
partial.to(matcher)
end
Spectator::Internals::Harness.run(spy)
spy.harness.expectations.first.satisfied?.should be_false
end
end
end
{% for method in %i[to_not not_to] %}
describe "#" + {{method.id.stringify}} do
it "reports an expectation" do
spy = SpyExample.create do
actual = ->{ 777 }
expected = 777
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
partial.{{method.id}}(matcher)
end
Spectator::Internals::Harness.run(spy)
spy.harness.expectations.size.should eq(1)
end
it "reports multiple expectations" do
spy = SpyExample.create do
actual = ->{ 777 }
expected = 42
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
5.times { partial.{{method.id}}(matcher) }
end
Spectator::Internals::Harness.run(spy)
spy.harness.expectations.size.should eq(5)
end
context "with a met condition" do
it "reports an unsatisifed expectation" do
spy = SpyExample.create do
actual = ->{ 777 }
expected = 777
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
partial.{{method.id}}(matcher)
end
Spectator::Internals::Harness.run(spy)
spy.harness.expectations.first.satisfied?.should be_false
end
end
context "with an unmet condition" do
it "reports an satisfied expectation" do
spy = SpyExample.create do
actual = ->{ 777 }
expected = 42
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
partial.{{method.id}}(matcher)
end
Spectator::Internals::Harness.run(spy)
spy.harness.expectations.first.satisfied?.should be_true
end
end
end
{% end %}
end

View file

@ -5,18 +5,22 @@ describe Spectator::Expectations::Expectation do
context "with a successful match" do
it "is true" do
value = 42
match_data = new_matcher(value).match(new_partial(value))
matcher = new_matcher(value)
partial = new_partial(value)
match_data = matcher.match(partial.actual)
match_data.matched?.should be_true # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, false)
expectation = Spectator::Expectations::Expectation.new(match_data, partial.source)
expectation.satisfied?.should be_true
end
context "when negated" do
it "is false" do
value = 42
match_data = new_matcher(value).match(new_partial(value))
match_data.matched?.should be_true # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, true)
matcher = new_matcher(value)
partial = new_partial(value)
match_data = matcher.negated_match(partial.actual)
match_data.matched?.should be_false # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, partial.source)
expectation.satisfied?.should be_false
end
end
@ -24,126 +28,24 @@ describe Spectator::Expectations::Expectation do
context "with an unsuccessful match" do
it "is false" do
match_data = new_matcher(42).match(new_partial(777))
matcher = new_matcher(42)
partial = new_partial(777)
match_data = matcher.match(partial.actual)
match_data.matched?.should be_false # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, false)
expectation = Spectator::Expectations::Expectation.new(match_data, partial.source)
expectation.satisfied?.should be_false
end
context "when negated" do
it "is true" do
match_data = new_matcher(42).match(new_partial(777))
match_data.matched?.should be_false # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, true)
matcher = new_matcher(42)
partial = new_partial(777)
match_data = matcher.negated_match(partial.actual)
match_data.matched?.should be_true # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, partial.source)
expectation.satisfied?.should be_true
end
end
end
end
describe "#values" do
it "is the same as the match data values" do
value = 42
match_data = new_matcher(value).match(new_partial(value))
expectation = Spectator::Expectations::Expectation.new(match_data, false)
expectation_values = expectation.values
match_data.values.zip(expectation_values).each do |m, e|
m.label.should eq(e.label)
m.value.value.should eq(e.value.value)
end
end
context "when negated" do
it "negates all negatable values" do
value = 42
match_data = new_matcher(value).match(new_partial(value))
expectation = Spectator::Expectations::Expectation.new(match_data, true)
expectation.values.each do |labeled_value|
label = labeled_value.label
value = labeled_value.value
value.to_s.should start_with(/not/i) if label == :expected
end
end
end
end
describe "#actual_message" do
context "with a successful match" do
it "equals the matcher's #message" do
value = 42
match_data = new_matcher(value).match(new_partial(value))
match_data.matched?.should be_true # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, false)
expectation.actual_message.should eq(match_data.message)
end
context "when negated" do
it "equals the matcher's #message" do
value = 42
match_data = new_matcher(value).match(new_partial(value))
match_data.matched?.should be_true # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, true)
expectation.actual_message.should eq(match_data.message)
end
end
end
context "with an unsuccessful match" do
it "equals the matcher's #negated_message" do
match_data = new_matcher(42).match(new_partial(777))
match_data.matched?.should be_false # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, false)
expectation.actual_message.should eq(match_data.negated_message)
end
context "when negated" do
it "equals the matcher's #negated_message" do
match_data = new_matcher(42).match(new_partial(777))
match_data.matched?.should be_false # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, true)
expectation.actual_message.should eq(match_data.negated_message)
end
end
end
end
describe "#expected_message" do
context "with a successful match" do
it "equals the matcher's #message" do
value = 42
match_data = new_matcher(value).match(new_partial(value))
match_data.matched?.should be_true # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, false)
expectation.expected_message.should eq(match_data.message)
end
context "when negated" do
it "equals the matcher's #negated_message" do
value = 42
match_data = new_matcher(value).match(new_partial(value))
match_data.matched?.should be_true # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, true)
expectation.expected_message.should eq(match_data.negated_message)
end
end
end
context "with an unsuccessful match" do
it "equals the matcher's #message" do
match_data = new_matcher(42).match(new_partial(777))
match_data.matched?.should be_false # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, false)
expectation.expected_message.should eq(match_data.message)
end
context "when negated" do
it "equals the matcher's #negated_message" do
match_data = new_matcher(42).match(new_partial(777))
match_data.matched?.should be_false # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, true)
expectation.expected_message.should eq(match_data.negated_message)
end
end
end
end
end

View file

@ -1,166 +0,0 @@
require "../spec_helper"
describe Spectator::Expectations::ValueExpectationPartial do
describe "#actual" do
context "with a label" do
it "contains the value passed to the constructor" do
actual = 777
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, actual.to_s, __FILE__, __LINE__)
partial.actual.should eq(actual)
end
end
context "without a label" do
it "contains the value passed to the constructor" do
actual = 777
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
partial.actual.should eq(actual)
end
end
end
describe "#label" do
context "when provided" do
it "contains the value passed to the constructor" do
actual = 777
label = "lucky"
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, label, __FILE__, __LINE__)
partial.label.should eq(label)
end
end
context "when omitted" do
it "contains a stringified version of #actual" do
actual = 777
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
partial.label.should eq(actual.to_s)
end
end
end
describe "#source_file" do
it "is the expected value" do
file = __FILE__
partial = Spectator::Expectations::ValueExpectationPartial.new(42, file, __LINE__)
partial.source_file.should eq(file)
end
end
describe "#source_line" do
it "is the expected value" do
line = __LINE__
partial = Spectator::Expectations::ValueExpectationPartial.new(42, __FILE__, line)
partial.source_line.should eq(line)
end
end
describe "#to" do
it "reports an expectation" do
spy = SpyExample.create do
actual = 777
expected = 777
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
partial.to(matcher)
end
Spectator::Internals::Harness.run(spy)
spy.harness.expectations.size.should eq(1)
end
it "reports multiple expectations" do
spy = SpyExample.create do
actual = 777
expected = 777
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
5.times { partial.to(matcher) }
end
Spectator::Internals::Harness.run(spy)
spy.harness.expectations.size.should eq(5)
end
context "with a met condition" do
it "reports a satisifed expectation" do
spy = SpyExample.create do
actual = 777
expected = 777
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
partial.to(matcher)
end
Spectator::Internals::Harness.run(spy)
spy.harness.expectations.first.satisfied?.should be_true
end
end
context "with an unmet condition" do
it "reports an unsatisfied expectation" do
spy = SpyExample.create do
actual = 777
expected = 42
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
partial.to(matcher)
end
Spectator::Internals::Harness.run(spy)
spy.harness.expectations.first.satisfied?.should be_false
end
end
end
{% for method in %i[to_not not_to] %}
describe "#" + {{method.id.stringify}} do
it "reports an expectation" do
spy = SpyExample.create do
actual = 777
expected = 777
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
partial.{{method.id}}(matcher)
end
Spectator::Internals::Harness.run(spy)
spy.harness.expectations.size.should eq(1)
end
it "reports multiple expectations" do
spy = SpyExample.create do
actual = 777
expected = 42
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
5.times { partial.{{method.id}}(matcher) }
end
Spectator::Internals::Harness.run(spy)
spy.harness.expectations.size.should eq(5)
end
context "with a met condition" do
it "reports an unsatisifed expectation" do
spy = SpyExample.create do
actual = 777
expected = 777
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
partial.{{method.id}}(matcher)
end
Spectator::Internals::Harness.run(spy)
spy.harness.expectations.first.satisfied?.should be_false
end
end
context "with an unmet condition" do
it "reports an satisfied expectation" do
spy = SpyExample.create do
actual = 777
expected = 42
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
partial.{{method.id}}(matcher)
end
Spectator::Internals::Harness.run(spy)
spy.harness.expectations.first.satisfied?.should be_true
end
end
end
{% end %}
end

View file

@ -1,30 +1,38 @@
# Utility methods for creating expectations, partials, and matchers.
def new_partial(actual : T, label : String) forall T
Spectator::Expectations::ValueExpectationPartial.new(actual, label, __FILE__, __LINE__)
test_value = Spectator::TestValue.new(actual, label)
source = Spectator::Source.new(__FILE__, __LINE__)
Spectator::Expectations::ExpectationPartial.new(test_value, source)
end
def new_partial(actual : T = 123) forall T
Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
test_value = Spectator::TestValue.new(actual)
source = Spectator::Source.new(__FILE__, __LINE__)
Spectator::Expectations::ExpectationPartial.new(test_value, source)
end
def new_block_partial(label = "BLOCK", &block)
Spectator::Expectations::BlockExpectationPartial.new(block, label, __FILE__, __LINE__)
test_block = Spectator::TestBlock.new(block, label)
source = Spectator::Source.new(__FILE__, __LINE__)
Spectator::Expectations::ExpectationPartial.new(test_block, source)
end
def new_matcher(expected : T, label : String) forall T
Spectator::Matchers::EqualityMatcher.new(expected, label)
test_value = Spectator::TestValue.new(expected, label)
Spectator::Matchers::EqualityMatcher.new(test_value)
end
def new_matcher(expected : T = 123) forall T
Spectator::Matchers::EqualityMatcher.new(expected)
test_value = Spectator::TestValue.new(expected)
Spectator::Matchers::EqualityMatcher.new(test_value)
end
def new_expectation(expected : ExpectedType = 123, actual : ActualType = 123) forall ExpectedType, ActualType
partial = new_partial(actual, "foo")
matcher = new_matcher(expected, "bar")
match_data = matcher.match(partial)
Spectator::Expectations::Expectation.new(match_data, false)
match_data = matcher.match(partial.actual)
Spectator::Expectations::Expectation.new(match_data, partial.source)
end
def new_satisfied_expectation(value : T = 123) forall T

View file

@ -1,405 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::ArrayMatcher do
describe "#in_any_order" do
it "returns an unordered matcher" do
array = %i[x y z]
matcher = Spectator::Matchers::ArrayMatcher.new(array)
matcher.in_any_order.should be_a(Spectator::Matchers::UnorderedArrayMatcher(Symbol))
end
it "maintains the expected array" do
array = %i[x y z]
matcher = Spectator::Matchers::ArrayMatcher.new(array)
unordered_matcher = matcher.in_any_order
unordered_matcher.expected.should eq(array)
end
it "maintains the expected label" do
array = %i[x y z]
label = "some_array"
matcher = Spectator::Matchers::ArrayMatcher.new(array, label)
unordered_matcher = matcher.in_any_order
unordered_matcher.label.should eq(label)
end
end
describe "#match" do
context "returned MatchData" do
context "with identical arrays" do
describe "#matched?" do
it "is true" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::ArrayMatcher.new(array)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
describe "#values" do
context "expected" do
it "is the expected array" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::ArrayMatcher.new(array)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(array)
end
end
context "actual" do
it "is the actual array" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::ArrayMatcher.new(array)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(array)
end
end
end
describe "#message" do
it "contains the actual label" do
array = %i[a b c]
label = "everything"
partial = new_partial(array, label)
matcher = Spectator::Matchers::ArrayMatcher.new(array)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
array = %i[a b c]
label = "everything"
partial = new_partial(array)
matcher = Spectator::Matchers::ArrayMatcher.new(array, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected array" do
array1 = %i[a b c]
array2 = [1, 2, 3]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.message.should contain(array2.to_s)
end
end
end
describe "#negated_message" do
it "contains the actual label" do
array = %i[a b c]
label = "everything"
partial = new_partial(array, label)
matcher = Spectator::Matchers::ArrayMatcher.new(array)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
array = %i[a b c]
label = "everything"
partial = new_partial(array)
matcher = Spectator::Matchers::ArrayMatcher.new(array, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected array" do
array1 = %i[a b c]
array2 = [1, 2, 3]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(array2.to_s)
end
end
end
end
context "with arrays differing in size" do
describe "#matched?" do
it "is false" do
array1 = %i[a b c d e]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
describe "#values" do
context "expected" do
it "is the expected array" do
array1 = %i[a b c d e]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(array2)
end
end
context "actual" do
it "is the actual array" do
array1 = %i[a b c d e]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(array1)
end
end
context "expected size" do
it "is the expected size" do
array1 = %i[a b c d e]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :"expected size")[:value].should eq(array2.size)
end
end
context "actual size" do
it "is the actual size" do
array1 = %i[a b c d e]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :"actual size")[:value].should eq(array1.size)
end
end
end
describe "#message" do
it "contains the actual label" do
array1 = %i[a b c d e]
array2 = %i[x y z]
label = "everything"
partial = new_partial(array1, label)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
array1 = %i[a b c d e]
array2 = %i[x y z]
label = "everything"
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected array" do
array1 = %i[a b c d e]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.message.should contain(array2.to_s)
end
end
end
describe "#negated_message" do
it "mentions size" do
array1 = %i[a b c d e]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.negated_message.should contain("size")
end
it "contains the actual label" do
array1 = %i[a b c d e]
array2 = %i[x y z]
label = "everything"
partial = new_partial(array1, label)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
array1 = %i[a b c d e]
array2 = %i[x y z]
label = "everything"
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected array" do
array1 = %i[a b c d e]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(array2.to_s)
end
end
end
end
context "with arrays differing in content" do
describe "#matched?" do
it "is false" do
array1 = %i[a b c]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
describe "#values" do
context "expected" do
it "is the expected array" do
array1 = %i[a b c]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(array2)
end
end
context "actual" do
it "is the actual array" do
array1 = %i[a b c]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(array1)
end
end
context "expected element" do
it "is the first mismatch" do
array1 = %i[a b c]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :"expected element")[:value].should eq(array2.first)
end
end
context "actual element" do
it "is the first mismatch" do
array1 = %i[a b c]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :"actual element")[:value].should eq(array1.first)
end
end
context "index" do
it "is the mismatched index" do
array1 = %i[a b c]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :index)[:value].should eq(0)
end
end
end
describe "#message" do
it "contains the actual label" do
array1 = %i[a b c]
array2 = %i[x y z]
label = "everything"
partial = new_partial(array1, label)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
array1 = %i[a b c]
array2 = %i[x y z]
label = "everything"
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected array" do
array1 = %i[a b c]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.message.should contain(array2.to_s)
end
end
end
describe "#negated_message" do
it "mentions content" do
array1 = %i[a b c]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.negated_message.should contain("content")
end
it "contains the actual label" do
array1 = %i[a b c]
array2 = %i[x y z]
label = "everything"
partial = new_partial(array1, label)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
array1 = %i[a b c]
array2 = %i[x y z]
label = "everything"
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected array" do
array1 = %i[a b c]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::ArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(array2.to_s)
end
end
end
end
end
end
end

View file

@ -1,358 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::AttributesMatcher do
describe "#match" do
it "uses ===" do
array = %i[a b c]
spy = SpySUT.new
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new({first: spy})
matcher.match(partial)
spy.case_eq_call_count.should be > 0
end
context "returned MatchData" do
describe "#matched?" do
context "one argument" do
context "against an equal value" do
it "is true" do
array = %i[a b c]
attributes = {first: :a}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "against a different value" do
it "is false" do
array = %i[a b c]
attributes = {first: :z}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a matching type" do
it "is true" do
array = %i[a b c]
attributes = {first: Symbol}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "against a non-matching type" do
it "is false" do
array = %i[a b c]
attributes = {first: Int32}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a matching regex" do
it "is true" do
array = %w[FOO BAR BAZ]
attributes = {first: /foo/i}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "against a non-matching regex" do
it "is false" do
array = %w[FOO BAR BAZ]
attributes = {first: /qux/i}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "multiple attributes" do
context "against equal values" do
it "is true" do
array = %i[a b c]
attributes = {first: :a, last: :c}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "matching type" do
context "matching regex" do
it "is true" do
array = [:a, 42, "FOO"]
attributes = {first: Symbol, last: /foo/i}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "non-matching regex" do
it "is false" do
array = [:a, 42, "FOO"]
attributes = {first: Symbol, last: /bar/i}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "non-matching type" do
context "matching regex" do
it "is false" do
array = [:a, 42, "FOO"]
attributes = {first: Float32, last: /foo/i}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "non-matching regex" do
it "is false" do
array = [:a, 42, "FOO"]
attributes = {first: Float32, last: /bar/i}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
end
context "against one equal value" do
it "is false" do
array = %i[a b c]
attributes = {first: :a, last: :d}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against no equal values" do
it "is false" do
array = %i[a b c]
attributes = {first: :d, last: :e}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against matching types" do
it "is true" do
array = [:a, 42, "FOO"]
attributes = {first: Symbol, last: String}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "against one matching type" do
it "is false" do
array = [:a, 42, "FOO"]
attributes = {first: Symbol, last: Float32}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against no matching types" do
it "is false" do
array = [:a, 42, "FOO"]
attributes = {first: Float32, last: Bytes}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against matching regexes" do
it "is true" do
array = %w[FOO BAR BAZ]
attributes = {first: /foo/i, last: /baz/i}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "against one matching regex" do
it "is false" do
array = %w[FOO BAR BAZ]
attributes = {first: /foo/i, last: /qux/i}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against no matching regexes" do
it "is false" do
array = %w[FOO BAR]
attributes = {first: /baz/i, last: /qux/i}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against equal and matching type and regex" do
it "is true" do
array = [:a, 42, "FOO"]
attributes = {first: Symbol, last: /foo/i, size: 3}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
end
end
describe "#values" do
it "contains a key for each expected attribute" do
array = [:a, 42, "FOO"]
attributes = {first: Symbol, last: /foo/i, size: 3}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data_has_key?(match_data.values, :"expected first").should be_true
match_data_has_key?(match_data.values, :"expected last").should be_true
match_data_has_key?(match_data.values, :"expected size").should be_true
end
it "contains a key for each actual value" do
array = [:a, 42, "FOO"]
attributes = {first: Symbol, last: /foo/i, size: 3}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data_has_key?(match_data.values, :"actual first").should be_true
match_data_has_key?(match_data.values, :"actual last").should be_true
match_data_has_key?(match_data.values, :"actual size").should be_true
end
it "has the expected values" do
array = [:a, 42, "FOO"]
attributes = {first: Symbol, last: /foo/i, size: 3}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :"expected first")[:value].should eq(attributes[:first])
match_data_value_sans_prefix(match_data.values, :"expected last")[:value].should eq(attributes[:last])
match_data_value_sans_prefix(match_data.values, :"expected size")[:value].should eq(attributes[:size])
end
it "has the actual values" do
array = [:a, 42, "FOO"]
attributes = {first: Symbol, last: /foo/i, size: 3}
partial = new_partial(array)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :"actual first")[:value].should eq(array.first)
match_data_value_sans_prefix(match_data.values, :"actual last")[:value].should eq(array.last)
match_data_value_sans_prefix(match_data.values, :"actual size")[:value].should eq(array.size)
end
end
describe "#message" do
it "contains the actual label" do
value = "foobar"
attributes = {size: 6}
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
value = "foobar"
attributes = {size: 6}
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value = "foobar"
attributes = {size: 6}
partial = new_partial(value)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.message.should contain(attributes.to_s)
end
end
end
describe "#negated_message" do
it "contains the actual label" do
value = "foobar"
attributes = {size: 6}
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
value = "foobar"
attributes = {size: 6}
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value = "foobar"
attributes = {size: 6}
partial = new_partial(value)
matcher = Spectator::Matchers::AttributesMatcher.new(attributes)
match_data = matcher.match(partial)
match_data.negated_message.should contain(attributes.to_s)
end
end
end
end
end
end

View file

@ -1,203 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::CaseMatcher do
describe "#match" do
it "compares using #===" do
spy = SpySUT.new
partial = new_partial(42)
matcher = Spectator::Matchers::CaseMatcher.new(spy)
matcher.match(partial)
spy.case_eq_call_count.should be > 0
end
context "returned MatchData" do
describe "#matched?" do
context "with identical values" do
it "is true" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::CaseMatcher.new(value)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with different values" do
it "is false" do
value1 = 42
value2 = 777
partial = new_partial(value1)
matcher = Spectator::Matchers::CaseMatcher.new(value2)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with the same instance" do
it "is true" do
# Box is used because it is a reference type and doesn't override the == method.
ref = Box.new([] of Int32)
partial = new_partial(ref)
matcher = Spectator::Matchers::CaseMatcher.new(ref)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with different instances" do
context "with same contents" do
it "is true" do
array1 = [1, 2, 3]
array2 = [1, 2, 3]
partial = new_partial(array1)
matcher = Spectator::Matchers::CaseMatcher.new(array2)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with different contents" do
it "is false" do
array1 = [1, 2, 3]
array2 = [4, 5, 6]
partial = new_partial(array1)
matcher = Spectator::Matchers::CaseMatcher.new(array2)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with the same type" do
it "is true" do
value1 = "foobar"
value2 = String
partial = new_partial(value1)
matcher = Spectator::Matchers::CaseMatcher.new(value2)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with a different type" do
it "is false" do
value1 = "foobar"
value2 = Array
partial = new_partial(value1)
matcher = Spectator::Matchers::CaseMatcher.new(value2)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with a matching regex" do
it "is true" do
value = "foobar"
pattern = /foo/
partial = new_partial(value)
matcher = Spectator::Matchers::CaseMatcher.new(pattern)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with a non-matching regex" do
it "is false" do
value = "foo"
pattern = /bar/
partial = new_partial(value)
matcher = Spectator::Matchers::CaseMatcher.new(pattern)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
end
describe "#values" do
context "expected" do
it "is the expected value" do
actual = "foobar"
expected = /foo/
partial = new_partial(actual)
matcher = Spectator::Matchers::CaseMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(expected)
end
end
context "actual" do
it "is the actual value" do
actual = "foobar"
expected = /foo/
partial = new_partial(actual)
matcher = Spectator::Matchers::CaseMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(actual)
end
end
end
describe "#message" do
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::CaseMatcher.new(value)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
value = 42
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::CaseMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value1 = 42
value2 = 777
partial = new_partial(value1)
matcher = Spectator::Matchers::CaseMatcher.new(value2)
match_data = matcher.match(partial)
match_data.message.should contain(value2.to_s)
end
end
end
describe "#negated_message" do
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::CaseMatcher.new(value)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
value = 42
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::CaseMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value1 = 42
value2 = 777
partial = new_partial(value1)
matcher = Spectator::Matchers::CaseMatcher.new(value2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(value2.to_s)
end
end
end
end
end
end

View file

@ -1,391 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::CollectionMatcher do
describe "#match" do
it "compares using #includes?" do
spy = SpySUT.new
partial = new_partial(5)
matcher = Spectator::Matchers::CollectionMatcher.new(spy)
matcher.match(partial)
spy.includes_call_count.should be > 0
end
context "returned MatchData" do
describe "#matched?" do
context "given a Range" do
context "inclusive" do
it "is true for lower-bound" do
lower = 3
upper = 9
value = lower
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for lower-bound minus 1" do
lower = 3
upper = 9
value = lower - 1
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
it "is true for mid-range" do
lower = 3
upper = 9
value = 5
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is true for upper-bound" do
lower = 3
upper = 9
value = upper
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for upper-bound plus 1" do
lower = 3
upper = 9
value = upper + 1
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "exclusive" do
it "is true for lower-bound" do
lower = 3
upper = 9
value = lower
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for lower-bound minus 1" do
lower = 3
upper = 9
value = lower - 1
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
it "is true for mid-range" do
lower = 3
upper = 9
value = 5
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for upper-bound" do
lower = 3
upper = 9
value = upper
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
it "is false for upper-bound plus 1" do
lower = 3
upper = 9
value = upper + 1
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "given an Enumerable" do
it "is true for an existing item" do
array = %i[a b c]
value = :b
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(array)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for a non-existing item" do
array = %i[a b c]
value = :z
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(array)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
describe "#values" do
context "given a Range" do
context "collection" do
it "is the expected value" do
value = 5
range = Range.new(3, 9)
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(range)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :collection)[:value].should eq(range)
end
end
context "actual" do
it "is the actual value" do
value = 5
range = Range.new(3, 9)
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(range)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(value)
end
end
end
context "given an Enumerable" do
context "collection" do
it "is the expected value" do
array = %i[a b c]
value = :z
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(array)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :collection)[:value].should eq(array)
end
end
context "actual" do
it "is the actual value" do
array = %i[a b c]
value = :z
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(array)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(value)
end
end
end
end
describe "#message" do
it "contains the actual label" do
range = 1..10
value = 5
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::CollectionMatcher.new(range)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
range = 1..10
value = 5
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(range, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
range = 1..10
value = 5
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(range)
match_data = matcher.match(partial)
match_data.message.should contain(range.to_s)
end
end
end
describe "#negated_message" do
it "contains the actual label" do
range = 1..10
value = 5
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::CollectionMatcher.new(range)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
range = 1..10
value = 5
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(range, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
range = 1..10
value = 5
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(range)
match_data = matcher.match(partial)
match_data.negated_message.should contain(range.to_s)
end
end
end
end
end
describe "#of" do
it "is true for lower-bound" do
center = 5
diff = 4
lower = center - diff
value = lower
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for lower-bound minus 1" do
center = 5
diff = 4
lower = center - diff
value = lower - 1
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
it "is true for mid-range" do
center = 5
diff = 4
value = center
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is true for upper-bound" do
center = 5
diff = 4
upper = center + diff
value = upper
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for upper-bound plus 1" do
center = 5
diff = 4
upper = center + diff
value = upper + 1
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
describe "#message" do
it "contains the original label" do
center = 5
diff = 4
value = 3
label = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(diff, label).of(center)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the center" do
center = 5
diff = 4
value = 3
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center)
match_data = matcher.match(partial)
match_data.message.should contain(center.to_s)
end
it "contains the diff" do
center = 5
diff = 4
value = 3
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center)
match_data = matcher.match(partial)
match_data.message.should contain(diff.to_s)
end
end
describe "#negated_message" do
it "contains the original label" do
center = 5
diff = 4
value = 3
label = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(diff, label).of(center)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the center" do
center = 5
diff = 4
value = 3
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center)
match_data = matcher.match(partial)
match_data.negated_message.should contain(center.to_s)
end
it "contains the diff" do
center = 5
diff = 4
value = 3
partial = new_partial(value)
matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center)
match_data = matcher.match(partial)
match_data.negated_message.should contain(diff.to_s)
end
end
end
end

View file

@ -1,386 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::ContainMatcher do
describe "#match" do
context "returned MatchData" do
describe "#matched?" do
context "with a String" do
context "one argument" do
context "against a matching string" do
it "is true" do
value = "foobarbaz"
search = "bar"
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "at the beginning" do
it "is true" do
value = "foobar"
search = "foo"
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "at the end" do
it "is true" do
value = "foobar"
search = "bar"
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
end
context "against a different string" do
it "is false" do
value = "foobar"
search = "baz"
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a matching character" do
it "is true" do
value = "foobar"
search = 'o'
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "at the beginning" do
it "is true" do
value = "foobar"
search = 'f'
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "at the end" do
it "is true" do
value = "foobar"
search = 'r'
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
end
context "against a different character" do
it "is false" do
value = "foobar"
search = 'z'
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "multiple arguments" do
context "against matching strings" do
it "is true" do
value = "foobarbaz"
search = {"foo", "bar", "baz"}
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "against one matching string" do
it "is false" do
value = "foobarbaz"
search = {"foo", "qux"}
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against no matching strings" do
it "is false" do
value = "foobar"
search = {"baz", "qux"}
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against matching characters" do
it "is true" do
value = "foobarbaz"
search = {'f', 'b', 'z'}
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "against one matching character" do
it "is false" do
value = "foobarbaz"
search = {'f', 'c', 'd'}
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against no matching characters" do
it "is false" do
value = "foobarbaz"
search = {'c', 'd', 'e'}
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a matching string and character" do
it "is true" do
value = "foobarbaz"
search = {"foo", 'z'}
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "against a matching string and non-matching character" do
it "is false" do
value = "foobarbaz"
search = {"foo", 'c'}
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a non-matching string and matching character" do
it "is false" do
value = "foobarbaz"
search = {"qux", 'f'}
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a non-matching string and character" do
it "is false" do
value = "foobarbaz"
search = {"qux", 'c'}
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
end
context "with an Enumberable" do
context "one argument" do
context "against an equal value" do
it "is true" do
array = %i[a b c]
search = :b
partial = new_partial(array)
matcher = Spectator::Matchers::ContainMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "at the beginning" do
it "is true" do
array = %i[a b c]
search = :a
partial = new_partial(array)
matcher = Spectator::Matchers::ContainMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "at the end" do
it "is true" do
array = %i[a b c]
search = :c
partial = new_partial(array)
matcher = Spectator::Matchers::ContainMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
end
context "against a different value" do
it "is false" do
array = %i[a b c]
search = :z
partial = new_partial(array)
matcher = Spectator::Matchers::ContainMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "multiple arguments" do
context "against equal values" do
it "is true" do
array = %i[a b c]
search = {:a, :b}
partial = new_partial(array)
matcher = Spectator::Matchers::ContainMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "against one equal value" do
it "is false" do
array = %i[a b c]
search = {:a, :d}
partial = new_partial(array)
matcher = Spectator::Matchers::ContainMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against no equal values" do
it "is false" do
array = %i[a b c]
search = {:d, :e}
partial = new_partial(array)
matcher = Spectator::Matchers::ContainMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
end
end
describe "#values" do
describe "subset" do
it "is the expected set" do
array = %i[a b c]
search = {:d, :e}
partial = new_partial(array)
matcher = Spectator::Matchers::ContainMatcher.new(search)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :subset)[:value].should eq(search)
end
end
describe "superset" do
it "is the actual set" do
array = %i[a b c]
search = {:d, :e}
partial = new_partial(array)
matcher = Spectator::Matchers::ContainMatcher.new(search)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :superset)[:value].should eq(array)
end
end
end
describe "#message" do
it "contains the actual label" do
value = "foobar"
search = "baz"
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::ContainMatcher.new({search})
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
value = "foobar"
search = "baz"
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new({search}, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value = "foobar"
search = "baz"
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new({search})
match_data = matcher.match(partial)
match_data.message.should contain(search)
end
end
end
describe "#negated_message" do
it "contains the actual label" do
value = "foobar"
search = "baz"
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::ContainMatcher.new({search})
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
value = "foobar"
search = "baz"
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new({search}, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value = "foobar"
search = "baz"
partial = new_partial(value)
matcher = Spectator::Matchers::ContainMatcher.new({search})
match_data = matcher.match(partial)
match_data.negated_message.should contain(search)
end
end
end
end
end
end

View file

@ -1,73 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::EmptyMatcher do
describe "#match" do
context "returned MatchData" do
describe "#matched?" do
context "with an empty set" do
it "is true" do
array = [] of Symbol
partial = new_partial(array)
matcher = Spectator::Matchers::EmptyMatcher.new
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with a filled set" do
it "is false" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::EmptyMatcher.new
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
describe "#values" do
context "expected" do
it "is an empty set" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::EmptyMatcher.new
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:to_s].should eq("[]")
end
end
context "actual" do
it "is the actual set" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::EmptyMatcher.new
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(array)
end
end
end
describe "#message" do
it "contains the actual label" do
array = %i[a b c]
label = "everything"
partial = new_partial(array, label)
matcher = Spectator::Matchers::EmptyMatcher.new
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
end
describe "#negated_message" do
it "contains the actual label" do
array = %i[a b c]
label = "everything"
partial = new_partial(array, label)
matcher = Spectator::Matchers::EmptyMatcher.new
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
end
end
end
end

View file

@ -1,393 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::EndWithMatcher do
describe "#match" do
context "returned MatchData" do
describe "#matched?" do
context "with a String" do
context "against a matching string" do
it "is true" do
value = "foobar"
last = "bar"
partial = new_partial(value)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "not at end" do
it "is false" do
value = "foobar"
last = "foo"
partial = new_partial(value)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "against a different string" do
it "is false" do
value = "foobar"
last = "baz"
partial = new_partial(value)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a matching character" do
it "is true" do
value = "foobar"
last = 'r'
partial = new_partial(value)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "not at end" do
it "is false" do
value = "foobar"
last = 'b'
partial = new_partial(value)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "against a different character" do
it "is false" do
value = "foobar"
last = 'z'
partial = new_partial(value)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a matching regex" do
it "is true" do
value = "FOOBAR"
last = /bar/i
partial = new_partial(value)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "not at end" do
it "is false" do
value = "FOOBAR"
last = /foo/i
partial = new_partial(value)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "against a non-matching regex" do
it "is false" do
value = "FOOBAR"
last = /baz/i
partial = new_partial(value)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "with an Enumberable" do
context "against an equal value" do
it "is true" do
array = %i[a b c]
last = :c
partial = new_partial(array)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "not at end" do
it "is false" do
array = %i[a b c]
last = :b
partial = new_partial(array)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "against a different value" do
it "is false" do
array = %i[a b c]
last = :z
partial = new_partial(array)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against matching element type" do
it "is true" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::EndWithMatcher.new(Symbol)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "not at end" do
it "is false" do
array = [1, 2, 3, :a, :b, :c]
partial = new_partial(array)
matcher = Spectator::Matchers::EndWithMatcher.new(Int32)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "against different element type" do
it "is false" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::EndWithMatcher.new(Int32)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a matching regex" do
it "is true" do
array = %w[FOO BAR BAZ]
last = /baz/i
partial = new_partial(array)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "not at end" do
it "is false" do
array = %w[FOO BAR BAZ]
last = /bar/i
partial = new_partial(array)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "against a non-matching regex" do
it "is false" do
array = %w[FOO BAR BAZ]
last = /qux/i
partial = new_partial(array)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
end
describe "#values" do
context "with a String" do
context "expected" do
it "is the expected value" do
value = "FOOBAR"
last = /baz/i
partial = new_partial(value)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(last)
end
end
context "actual" do
it "is the actual value" do
value = "FOOBAR"
last = /baz/i
partial = new_partial(value)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(value)
end
end
end
context "with an Indexable" do
context "expected" do
it "is the expected value" do
array = %w[FOO BAR BAZ]
last = /qux/i
partial = new_partial(array)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(last)
end
end
context "actual" do
it "is the last element" do
array = %w[FOO BAR BAZ]
last = /qux/i
partial = new_partial(array)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(array.last)
end
end
context "list" do
it "is the full actual list" do
array = %w[FOO BAR BAZ]
last = /qux/i
partial = new_partial(array)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :list)[:value].should eq(array)
end
end
end
end
describe "#message" do
context "with a String" do
it "mentions #ends_with?" do
value = "foobar"
last = "baz"
partial = new_partial(value)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.message.should contain("#ends_with?")
end
end
context "with an Indexable" do
it "mentions ===" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::EndWithMatcher.new(array.last)
match_data = matcher.match(partial)
match_data.message.should contain("===")
end
it "mentions last" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::EndWithMatcher.new(array.last)
match_data = matcher.match(partial)
match_data.message.should contain("last")
end
end
it "contains the actual label" do
value = "foobar"
last = "baz"
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
value = "foobar"
last = "baz"
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::EndWithMatcher.new(last, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value = "foobar"
last = "baz"
partial = new_partial(value)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.message.should contain(last)
end
end
end
describe "#negated_message" do
context "with a String" do
it "mentions #starts_with?" do
value = "foobar"
last = "baz"
partial = new_partial(value)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.negated_message.should contain("#ends_with?")
end
end
context "with an Indexable" do
it "mentions ===" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::EndWithMatcher.new(array.last)
match_data = matcher.match(partial)
match_data.negated_message.should contain("===")
end
it "mentions last" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::EndWithMatcher.new(array.last)
match_data = matcher.match(partial)
match_data.negated_message.should contain("last")
end
end
it "contains the actual label" do
value = "foobar"
last = "baz"
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
value = "foobar"
last = "baz"
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::EndWithMatcher.new(last, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value = "foobar"
last = "baz"
partial = new_partial(value)
matcher = Spectator::Matchers::EndWithMatcher.new(last)
match_data = matcher.match(partial)
match_data.negated_message.should contain(last)
end
end
end
end
end
end

View file

@ -1,173 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::EqualityMatcher do
describe "#match" do
it "compares using #==" do
spy = SpySUT.new
partial = new_partial(spy)
matcher = Spectator::Matchers::EqualityMatcher.new(42)
matcher.match(partial)
spy.eq_call_count.should be > 0
end
context "returned MatchData" do
describe "#matched?" do
context "with identical values" do
it "is true" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::EqualityMatcher.new(value)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with different values" do
it "is false" do
value1 = 42
value2 = 777
partial = new_partial(value1)
matcher = Spectator::Matchers::EqualityMatcher.new(value2)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with the same instance" do
it "is true" do
# Box is used because it is a reference type and doesn't override the == method.
ref = Box.new([] of Int32)
partial = new_partial(ref)
matcher = Spectator::Matchers::EqualityMatcher.new(ref)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with different instances" do
context "with same contents" do
it "is true" do
array1 = [1, 2, 3]
array2 = [1, 2, 3]
partial = new_partial(array1)
matcher = Spectator::Matchers::EqualityMatcher.new(array2)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with different contents" do
it "is false" do
array1 = [1, 2, 3]
array2 = [4, 5, 6]
partial = new_partial(array1)
matcher = Spectator::Matchers::EqualityMatcher.new(array2)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
end
describe "#values" do
context "expected" do
it "is the expected value" do
expected, actual = 42, 777
partial = new_partial(actual)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(expected)
end
end
context "actual" do
it "is the actual value" do
expected, actual = 42, 777
partial = new_partial(actual)
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(actual)
end
end
end
describe "#message" do
it "mentions ==" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::EqualityMatcher.new(value)
match_data = matcher.match(partial)
match_data.message.should contain("==")
end
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::EqualityMatcher.new(value)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
value = 42
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::EqualityMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value1 = 42
value2 = 777
partial = new_partial(value1)
matcher = Spectator::Matchers::EqualityMatcher.new(value2)
match_data = matcher.match(partial)
match_data.message.should contain(value2.to_s)
end
end
end
describe "#negated_message" do
it "mentions ==" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::EqualityMatcher.new(value)
match_data = matcher.match(partial)
match_data.negated_message.should contain("==")
end
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::EqualityMatcher.new(value)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
value = 42
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::EqualityMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value1 = 42
value2 = 777
partial = new_partial(value1)
matcher = Spectator::Matchers::EqualityMatcher.new(value2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(value2.to_s)
end
end
end
end
end
end

View file

@ -1,205 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::ExceptionMatcher do
describe "#match" do
it "compares the message using #===" do
spy = SpySUT.new
partial = new_block_partial { raise "foobar" }
matcher = Spectator::Matchers::ExceptionMatcher(Exception, SpySUT).new(spy, "foo")
matcher.match(partial)
spy.case_eq_call_count.should be > 0
end
context "returned MatchData" do
describe "#matched?" do
context "with no exception" do
it "is false" do
partial = new_block_partial { 42 }
matcher = Spectator::Matchers::ExceptionMatcher(Exception, Nil).new
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with an exception" do
context "of the same type" do
it "is true" do
partial = new_block_partial { raise ArgumentError.new }
matcher = Spectator::Matchers::ExceptionMatcher(ArgumentError, Nil).new
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "of a different type" do
it "is false" do
partial = new_block_partial { raise ArgumentError.new }
matcher = Spectator::Matchers::ExceptionMatcher(KeyError, Nil).new
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "of a sub-type" do
it "is true" do
partial = new_block_partial { raise ArgumentError.new }
matcher = Spectator::Matchers::ExceptionMatcher(Exception, Nil).new
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "and an equal message" do
it "is true" do
message = "foobar"
partial = new_block_partial { raise ArgumentError.new(message) }
matcher = Spectator::Matchers::ExceptionMatcher(ArgumentError, String).new(message, "label")
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "and a different message" do
it "is false" do
partial = new_block_partial { raise ArgumentError.new("foobar") }
matcher = Spectator::Matchers::ExceptionMatcher(ArgumentError, String).new("different", "label")
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "and a matching regex" do
it "is true" do
partial = new_block_partial { raise ArgumentError.new("foobar") }
matcher = Spectator::Matchers::ExceptionMatcher(ArgumentError, Regex).new(/foo/, "label")
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "and a non-matching regex" do
it "is false" do
partial = new_block_partial { raise ArgumentError.new("foobar") }
matcher = Spectator::Matchers::ExceptionMatcher(ArgumentError, Regex).new(/baz/, "label")
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
end
describe "#values" do
describe "expected type" do
it "is the exception type" do
partial = new_block_partial { raise ArgumentError.new }
matcher = Spectator::Matchers::ExceptionMatcher(KeyError, Nil).new
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :"expected type")[:value].should eq(KeyError)
end
end
describe "actual type" do
it "is the raised type" do
partial = new_block_partial { raise ArgumentError.new }
matcher = Spectator::Matchers::ExceptionMatcher(KeyError, Nil).new
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :"actual type")[:value].should eq(ArgumentError)
end
context "when nothing is raised" do
it "is Nil" do
partial = new_block_partial { 42 }
matcher = Spectator::Matchers::ExceptionMatcher(KeyError, Nil).new
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :"actual type")[:value].should eq(Nil)
end
end
end
describe "expected message" do
it "is the expected value" do
regex = /baz/
partial = new_block_partial { raise ArgumentError.new("foobar") }
matcher = Spectator::Matchers::ExceptionMatcher(KeyError, Regex).new(regex, "label")
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :"expected message")[:value].should eq(regex)
end
end
describe "actual message" do
it "is the raised exception's message" do
message = "foobar"
partial = new_block_partial { raise ArgumentError.new(message) }
matcher = Spectator::Matchers::ExceptionMatcher(KeyError, Regex).new(/baz/, "label")
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :"actual message")[:value].should eq(message)
end
end
end
describe "#message" do
it "mentions raise" do
partial = new_block_partial { raise "foobar" }
matcher = Spectator::Matchers::ExceptionMatcher(Exception, Nil).new
match_data = matcher.match(partial)
match_data.message.should contain("raise")
end
it "contains the actual label" do
label = "everything"
partial = new_block_partial(label) { raise "foobar" }
matcher = Spectator::Matchers::ExceptionMatcher(Exception, Nil).new
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
label = "everything"
partial = new_block_partial { raise "foobar" }
matcher = Spectator::Matchers::ExceptionMatcher(Exception, Regex).new(/foobar/, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the exception type" do
partial = new_block_partial { raise "foobar" }
matcher = Spectator::Matchers::ExceptionMatcher(ArgumentError, Nil).new
match_data = matcher.match(partial)
match_data.message.should contain("ArgumentError")
end
end
describe "#negated_message" do
it "mentions raise" do
partial = new_block_partial { raise "foobar" }
matcher = Spectator::Matchers::ExceptionMatcher(Exception, Nil).new
match_data = matcher.match(partial)
match_data.negated_message.should contain("raise")
end
it "contains the actual label" do
label = "everything"
partial = new_block_partial(label) { raise "foobar" }
matcher = Spectator::Matchers::ExceptionMatcher(Exception, Nil).new
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
label = "everything"
partial = new_block_partial { raise "foobar" }
matcher = Spectator::Matchers::ExceptionMatcher(Exception, Regex).new(/foobar/, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the exception type" do
partial = new_block_partial { raise "foobar" }
matcher = Spectator::Matchers::ExceptionMatcher(ArgumentError, Nil).new
match_data = matcher.match(partial)
match_data.negated_message.should contain("ArgumentError")
end
end
end
end
end

View file

@ -1,160 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::GreaterThanEqualMatcher do
describe "#match" do
it "compares using #>=" do
spy = SpySUT.new
partial = new_partial(spy)
matcher = Spectator::Matchers::GreaterThanEqualMatcher.new(42)
matcher.match(partial)
spy.ge_call_count.should be > 0
end
context "returned MatchData" do
describe "#matched?" do
context "with a larger value" do
it "is false" do
actual = 42
expected = 777
partial = new_partial(actual)
matcher = Spectator::Matchers::GreaterThanEqualMatcher.new(expected)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with a smaller value" do
it "is true" do
actual = 777
expected = 42
partial = new_partial(actual)
matcher = Spectator::Matchers::GreaterThanEqualMatcher.new(expected)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with an equal value" do
it "is true" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::GreaterThanEqualMatcher.new(value)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
end
describe "#values" do
context "expected" do
it "is the expected value" do
actual = 777
expected = 42
partial = new_partial(actual)
matcher = Spectator::Matchers::GreaterThanEqualMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(expected)
end
it "is prefixed with >=" do
actual = 777
expected = 42
partial = new_partial(actual)
matcher = Spectator::Matchers::GreaterThanEqualMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:to_s].should start_with(">=")
end
end
context "actual" do
it "is the actual value" do
actual = 777
expected = 42
partial = new_partial(actual)
matcher = Spectator::Matchers::GreaterThanEqualMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(actual)
end
end
end
describe "#message" do
it "mentions >=" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::GreaterThanEqualMatcher.new(value)
match_data = matcher.match(partial)
match_data.message.should contain(">=")
end
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::GreaterThanEqualMatcher.new(value)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
value = 42
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::GreaterThanEqualMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value1 = 42
value2 = 777
partial = new_partial(value1)
matcher = Spectator::Matchers::GreaterThanEqualMatcher.new(value2)
match_data = matcher.match(partial)
match_data.message.should contain(value2.to_s)
end
end
end
describe "#negated_message" do
it "mentions >=" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::GreaterThanEqualMatcher.new(value)
match_data = matcher.match(partial)
match_data.negated_message.should contain(">=")
end
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::GreaterThanEqualMatcher.new(value)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
value = 42
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::GreaterThanEqualMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value1 = 42
value2 = 777
partial = new_partial(value1)
matcher = Spectator::Matchers::GreaterThanEqualMatcher.new(value2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(value2.to_s)
end
end
end
end
end
end

View file

@ -1,160 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::GreaterThanMatcher do
describe "#match" do
it "compares using #>" do
spy = SpySUT.new
partial = new_partial(spy)
matcher = Spectator::Matchers::GreaterThanMatcher.new(42)
matcher.match(partial)
spy.gt_call_count.should be > 0
end
context "returned MatchData" do
describe "#matched?" do
context "with a larger value" do
it "is false" do
actual = 42
expected = 777
partial = new_partial(actual)
matcher = Spectator::Matchers::GreaterThanMatcher.new(expected)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with a smaller value" do
it "is true" do
actual = 777
expected = 42
partial = new_partial(actual)
matcher = Spectator::Matchers::GreaterThanMatcher.new(expected)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with an equal value" do
it "is false" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::GreaterThanMatcher.new(value)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
describe "#values" do
context "expected" do
it "is the expected value" do
actual = 777
expected = 42
partial = new_partial(actual)
matcher = Spectator::Matchers::GreaterThanMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(expected)
end
it "is prefixed with >" do
actual = 777
expected = 42
partial = new_partial(actual)
matcher = Spectator::Matchers::GreaterThanMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:to_s].should start_with(">")
end
end
context "actual" do
it "is the actual value" do
actual = 777
expected = 42
partial = new_partial(actual)
matcher = Spectator::Matchers::GreaterThanMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(actual)
end
end
end
describe "#message" do
it "mentions >" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::GreaterThanMatcher.new(value)
match_data = matcher.match(partial)
match_data.message.should contain(">")
end
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::GreaterThanMatcher.new(value)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
value = 42
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::GreaterThanMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value1 = 42
value2 = 777
partial = new_partial(value1)
matcher = Spectator::Matchers::GreaterThanMatcher.new(value2)
match_data = matcher.match(partial)
match_data.message.should contain(value2.to_s)
end
end
end
describe "#negated_message" do
it "mentions >" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::GreaterThanMatcher.new(value)
match_data = matcher.match(partial)
match_data.negated_message.should contain(">")
end
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::GreaterThanMatcher.new(value)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
value = 42
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::GreaterThanMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value1 = 42
value2 = 777
partial = new_partial(value1)
matcher = Spectator::Matchers::GreaterThanMatcher.new(value2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(value2.to_s)
end
end
end
end
end
end

View file

@ -1,166 +0,0 @@
require "../spec_helper"
private struct FakeKeySet
def has_key?(key)
true
end
end
describe Spectator::Matchers::HaveKeyMatcher do
describe "#match" do
context "returned MatchData" do
describe "#matched?" do
context "against a Hash" do
context "with an existing key" do
it "is true" do
hash = Hash{"foo" => "bar"}
key = "foo"
partial = new_partial(hash)
matcher = Spectator::Matchers::HaveKeyMatcher.new(key)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with a non-existent key" do
it "is false" do
hash = Hash{"foo" => "bar"}
key = "baz"
partial = new_partial(hash)
matcher = Spectator::Matchers::HaveKeyMatcher.new(key)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "against a NamedTuple" do
context "with an existing key" do
it "is true" do
tuple = {foo: "bar"}
key = :foo
partial = new_partial(tuple)
matcher = Spectator::Matchers::HaveKeyMatcher.new(key)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with a non-existent key" do
it "is false" do
tuple = {foo: "bar"}
key = :baz
partial = new_partial(tuple)
matcher = Spectator::Matchers::HaveKeyMatcher.new(key)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
end
describe "#values" do
context "key" do
it "is the expected key" do
tuple = {foo: "bar"}
key = :baz
partial = new_partial(tuple)
matcher = Spectator::Matchers::HaveKeyMatcher.new(key)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :key)[:value].should eq(key)
end
end
context "actual" do
context "when #keys is available" do
it "is the list of keys" do
tuple = {foo: "bar"}
key = :baz
partial = new_partial(tuple)
matcher = Spectator::Matchers::HaveKeyMatcher.new(key)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(tuple.keys)
end
end
context "when #keys isn't available" do
it "is the actual value" do
actual = FakeKeySet.new
key = :baz
partial = new_partial(actual)
matcher = Spectator::Matchers::HaveKeyMatcher.new(key)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(actual)
end
end
end
end
describe "#message" do
it "contains the actual label" do
tuple = {foo: "bar"}
key = :foo
label = "blah"
partial = new_partial(tuple, label)
matcher = Spectator::Matchers::HaveKeyMatcher.new(key)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
tuple = {foo: "bar"}
key = :foo
label = "blah"
partial = new_partial(tuple)
matcher = Spectator::Matchers::HaveKeyMatcher.new(key, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when the expected label is omitted" do
it "contains the stringified key" do
tuple = {foo: "bar"}
key = :foo
partial = new_partial(tuple)
matcher = Spectator::Matchers::HaveKeyMatcher.new(key)
match_data = matcher.match(partial)
match_data.message.should contain(key.to_s)
end
end
end
describe "#negated_message" do
it "contains the actual label" do
tuple = {foo: "bar"}
key = :foo
label = "blah"
partial = new_partial(tuple, label)
matcher = Spectator::Matchers::HaveKeyMatcher.new(key)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
tuple = {foo: "bar"}
key = :foo
label = "blah"
partial = new_partial(tuple)
matcher = Spectator::Matchers::HaveKeyMatcher.new(key, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when the expected label is omitted" do
it "contains the stringified key" do
tuple = {foo: "bar"}
key = :foo
partial = new_partial(tuple)
matcher = Spectator::Matchers::HaveKeyMatcher.new(key)
match_data = matcher.match(partial)
match_data.negated_message.should contain(key.to_s)
end
end
end
end
end
end

View file

@ -1,604 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::HaveMatcher do
describe "#match" do
it "uses ===" do
array = %i[a b c]
spy = SpySUT.new
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new({spy})
matcher.match(partial)
spy.case_eq_call_count.should be > 0
end
context "returned MatchData" do
describe "#matched?" do
context "with a String" do
context "one argument" do
context "against a matching string" do
it "is true" do
value = "foobarbaz"
search = "bar"
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "at the beginning" do
it "is true" do
value = "foobar"
search = "foo"
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "at the end" do
it "is true" do
value = "foobar"
search = "bar"
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
end
context "against a different string" do
it "is false" do
value = "foobar"
search = "baz"
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a matching character" do
it "is true" do
value = "foobar"
search = 'o'
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "at the beginning" do
it "is true" do
value = "foobar"
search = 'f'
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "at the end" do
it "is true" do
value = "foobar"
search = 'r'
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
end
context "against a different character" do
it "is false" do
value = "foobar"
search = 'z'
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "multiple arguments" do
context "against matching strings" do
it "is true" do
value = "foobarbaz"
search = {"foo", "bar", "baz"}
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "against one matching string" do
it "is false" do
value = "foobarbaz"
search = {"foo", "qux"}
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against no matching strings" do
it "is false" do
value = "foobar"
search = {"baz", "qux"}
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against matching characters" do
it "is true" do
value = "foobarbaz"
search = {'f', 'b', 'z'}
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "against one matching character" do
it "is false" do
value = "foobarbaz"
search = {'f', 'c', 'd'}
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against no matching characters" do
it "is false" do
value = "foobarbaz"
search = {'c', 'd', 'e'}
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a matching string and character" do
it "is true" do
value = "foobarbaz"
search = {"foo", 'z'}
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "against a matching string and non-matching character" do
it "is false" do
value = "foobarbaz"
search = {"foo", 'c'}
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a non-matching string and matching character" do
it "is false" do
value = "foobarbaz"
search = {"qux", 'f'}
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a non-matching string and character" do
it "is false" do
value = "foobarbaz"
search = {"qux", 'c'}
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
end
context "with an Enumberable" do
context "one argument" do
context "against an equal value" do
it "is true" do
array = %i[a b c]
search = :b
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "at the beginning" do
it "is true" do
array = %i[a b c]
search = :a
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "at the end" do
it "is true" do
array = %i[a b c]
search = :c
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
end
context "against a different value" do
it "is false" do
array = %i[a b c]
search = :z
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a matching type" do
it "is true" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new({Symbol})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "at the beginning" do
it "is true" do
array = [:a, 1, 2]
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new({Symbol})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "at the end" do
it "is true" do
array = [0, 1, :c]
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new({Symbol})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
end
context "against a non-matching type" do
it "is false" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new({Int32})
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a matching regex" do
it "is true" do
array = %w[FOO BAR BAZ]
search = /bar/i
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "at the beginning" do
it "is true" do
array = %w[FOO BAR BAZ]
search = /foo/i
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "at the end" do
it "is true" do
array = %w[FOO BAR BAZ]
search = /baz/i
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
end
context "against a non-matching regex" do
it "is false" do
array = %w[FOO BAR BAZ]
search = /qux/i
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "multiple arguments" do
context "against equal values" do
it "is true" do
array = %i[a b c]
search = {:a, :b}
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "matching type" do
context "matching regex" do
it "is true" do
array = [:a, 42, "FOO"]
search = {:a, Int32, /foo/i}
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "non-matching regex" do
it "is false" do
array = [:a, 42, "FOO"]
search = {:a, Int32, /bar/i}
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "non-matching type" do
context "matching regex" do
it "is false" do
array = [:a, 42, "FOO"]
search = {:a, Float32, /foo/i}
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "non-matching regex" do
it "is false" do
array = [:a, 42, "FOO"]
search = {:a, Float32, /bar/i}
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
end
context "against one equal value" do
it "is false" do
array = %i[a b c]
search = {:a, :d}
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against no equal values" do
it "is false" do
array = %i[a b c]
search = {:d, :e}
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against matching types" do
it "is true" do
array = [:a, 42, "FOO"]
search = {Symbol, String}
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "against one matching type" do
it "is false" do
array = [:a, 42, "FOO"]
search = {Symbol, Float32}
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against no matching types" do
it "is false" do
array = [:a, 42, "FOO"]
search = {Float32, Bytes}
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against matching regexes" do
it "is true" do
array = %w[FOO BAR BAZ]
search = {/foo/i, /bar/i}
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "against one matching regex" do
it "is false" do
array = %w[FOO BAR BAZ]
search = {/foo/i, /qux/i}
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against no matching regexes" do
it "is false" do
array = %w[FOO BAR]
search = {/baz/i, /qux/i}
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against equal and matching type and regex" do
it "is true" do
array = [:a, 42, "FOO"]
search = {:a, Int32, /foo/i}
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
end
end
end
describe "#values" do
context "subset" do
it "has the expected value" do
array = [:a, 42, "FOO"]
search = {:a, Int32, /foo/i}
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :subset)[:value].should eq(search)
end
end
context "superset" do
it "has the actual value" do
array = [:a, 42, "FOO"]
search = {:a, Int32, /foo/i}
partial = new_partial(array)
matcher = Spectator::Matchers::HaveMatcher.new(search)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :superset)[:value].should eq(array)
end
end
end
describe "#message" do
it "contains the actual label" do
value = "foobar"
search = "baz"
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
value = "foobar"
search = "baz"
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new({search}, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value = "foobar"
search = "baz"
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.message.should contain(search)
end
end
end
describe "#negated_message" do
it "contains the actual label" do
value = "foobar"
search = "baz"
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
value = "foobar"
search = "baz"
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new({search}, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value = "foobar"
search = "baz"
partial = new_partial(value)
matcher = Spectator::Matchers::HaveMatcher.new({search})
match_data = matcher.match(partial)
match_data.negated_message.should contain(search)
end
end
end
end
end
end

View file

@ -1,87 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::HavePredicateMatcher do
describe "#match" do
context "returned MatchData" do
describe "#match?" do
context "with a true predicate" do
it "is true" do
value = "foo\\bar"
partial = new_partial(value)
matcher = Spectator::Matchers::HavePredicateMatcher.new({back_references: Tuple.new}, "back_references")
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with a false predicate" do
it "is false" do
value = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::HavePredicateMatcher.new({back_references: Tuple.new}, "back_references")
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
describe "#values" do
it "contains a key for each expected attribute" do
value = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::HavePredicateMatcher.new({back_references: Tuple.new}, "back_references")
match_data = matcher.match(partial)
match_data_has_key?(match_data.values, :back_references).should be_true
end
it "has the actual values" do
value = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::HavePredicateMatcher.new({back_references: Tuple.new}, "back_references")
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :back_references)[:value].should eq(value.has_back_references?)
end
end
describe "#message" do
it "contains the actual label" do
value = "foobar"
label = "blah"
partial = new_partial(value, label)
matcher = Spectator::Matchers::HavePredicateMatcher.new({back_references: Tuple.new}, "back_references")
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
value = "foobar"
label = "blah"
partial = new_partial(value)
matcher = Spectator::Matchers::HavePredicateMatcher.new({back_references: Tuple.new}, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
end
describe "#negated_message" do
it "contains the actual label" do
value = "foobar"
label = "blah"
partial = new_partial(value, label)
matcher = Spectator::Matchers::HavePredicateMatcher.new({back_references: Tuple.new}, "back_references")
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
value = "foobar"
label = "blah"
partial = new_partial(value)
matcher = Spectator::Matchers::HavePredicateMatcher.new({back_references: Tuple.new}, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
end
end
end
end

View file

@ -1,140 +0,0 @@
require "../spec_helper"
private struct FakeValueSet
def has_value?(value)
true
end
end
describe Spectator::Matchers::HaveValueMatcher do
describe "#match" do
context "returned MatchData" do
describe "#matched?" do
context "with an existing value" do
it "is true" do
hash = Hash{"foo" => "bar"}
value = "bar"
partial = new_partial(hash)
matcher = Spectator::Matchers::HaveValueMatcher.new(value)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with a non-existent value" do
it "is false" do
hash = Hash{"foo" => "bar"}
value = "baz"
partial = new_partial(hash)
matcher = Spectator::Matchers::HaveValueMatcher.new(value)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
describe "#values" do
context "value" do
it "is the expected value" do
hash = {"foo" => "bar"}
value = "baz"
partial = new_partial(hash)
matcher = Spectator::Matchers::HaveValueMatcher.new(value)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :value)[:value].should eq(value)
end
end
context "actual" do
context "when #values is available" do
it "is the list of values" do
hash = Hash{"foo" => "bar"}
value = "baz"
partial = new_partial(hash)
matcher = Spectator::Matchers::HaveValueMatcher.new(value)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(hash.values)
end
end
context "when #values isn't available" do
it "is the actual value" do
actual = FakeValueSet.new
value = "baz"
partial = new_partial(actual)
matcher = Spectator::Matchers::HaveValueMatcher.new(value)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(actual)
end
end
end
end
describe "#message" do
it "contains the actual label" do
hash = Hash{"foo" => "bar"}
value = "bar"
label = "blah"
partial = new_partial(hash, label)
matcher = Spectator::Matchers::HaveValueMatcher.new(value)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
hash = Hash{"foo" => "bar"}
value = "bar"
label = "blah"
partial = new_partial(hash)
matcher = Spectator::Matchers::HaveValueMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when the expected label is omitted" do
it "contains the stringified key" do
hash = Hash{"foo" => "bar"}
value = "bar"
partial = new_partial(hash)
matcher = Spectator::Matchers::HaveValueMatcher.new(value)
match_data = matcher.match(partial)
match_data.message.should contain(value.to_s)
end
end
end
describe "#negated_message" do
it "contains the actual label" do
hash = Hash{"foo" => "bar"}
value = "bar"
label = "blah"
partial = new_partial(hash, label)
matcher = Spectator::Matchers::HaveValueMatcher.new(value)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
hash = Hash{"foo" => "bar"}
value = "bar"
label = "blah"
partial = new_partial(hash)
matcher = Spectator::Matchers::HaveValueMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when the expected label is omitted" do
it "contains the stringified key" do
hash = Hash{"foo" => "bar"}
value = "bar"
partial = new_partial(hash)
matcher = Spectator::Matchers::HaveValueMatcher.new(value)
match_data = matcher.match(partial)
match_data.negated_message.should contain(value.to_s)
end
end
end
end
end
end

View file

@ -1,181 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::InequalityMatcher do
describe "#match" do
it "compares using #!=" do
spy = SpySUT.new
partial = new_partial(spy)
matcher = Spectator::Matchers::InequalityMatcher.new(42)
matcher.match(partial)
spy.ne_call_count.should be > 0
end
context "returned MatchData" do
describe "#matched?" do
context "with identical values" do
it "is false" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::InequalityMatcher.new(value)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with different values" do
it "is true" do
value1 = 42
value2 = 777
partial = new_partial(value1)
matcher = Spectator::Matchers::InequalityMatcher.new(value2)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with the same instance" do
it "is false" do
# Box is used because it is a reference type and doesn't override the == method.
ref = Box.new([] of Int32)
partial = new_partial(ref)
matcher = Spectator::Matchers::InequalityMatcher.new(ref)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with different instances" do
context "with same contents" do
it "is false" do
array1 = [1, 2, 3]
array2 = [1, 2, 3]
partial = new_partial(array1)
matcher = Spectator::Matchers::InequalityMatcher.new(array2)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with different contents" do
it "is true" do
array1 = [1, 2, 3]
array2 = [4, 5, 6]
partial = new_partial(array1)
matcher = Spectator::Matchers::InequalityMatcher.new(array2)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
end
end
describe "#values" do
context "expected" do
it "is the expected value" do
expected, actual = 42, 777
partial = new_partial(actual)
matcher = Spectator::Matchers::InequalityMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(expected)
end
it "is prefixed with 'Not'" do
expected, actual = 42, 777
partial = new_partial(actual)
matcher = Spectator::Matchers::InequalityMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:to_s].should start_with("Not")
end
end
context "actual" do
it "is the actual value" do
expected, actual = 42, 777
partial = new_partial(actual)
matcher = Spectator::Matchers::InequalityMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(actual)
end
end
end
describe "#message" do
it "mentions !=" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::InequalityMatcher.new(value)
match_data = matcher.match(partial)
match_data.message.should contain("!=")
end
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::InequalityMatcher.new(value)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
value = 42
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::InequalityMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value1 = 42
value2 = 777
partial = new_partial(value1)
matcher = Spectator::Matchers::InequalityMatcher.new(value2)
match_data = matcher.match(partial)
match_data.message.should contain(value2.to_s)
end
end
end
describe "#negated_message" do
it "mentions !=" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::InequalityMatcher.new(value)
match_data = matcher.match(partial)
match_data.negated_message.should contain("!=")
end
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::InequalityMatcher.new(value)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
value = 42
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::InequalityMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value1 = 42
value2 = 777
partial = new_partial(value1)
matcher = Spectator::Matchers::InequalityMatcher.new(value2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(value2.to_s)
end
end
end
end
end
end

View file

@ -1,160 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::LessThanEqualMatcher do
describe "#match" do
it "compares using #<=" do
spy = SpySUT.new
partial = new_partial(spy)
matcher = Spectator::Matchers::LessThanEqualMatcher.new(42)
matcher.match(partial)
spy.le_call_count.should be > 0
end
context "returned MatchData" do
describe "#matched?" do
context "with a larger value" do
it "is true" do
actual = 42
expected = 777
partial = new_partial(actual)
matcher = Spectator::Matchers::LessThanEqualMatcher.new(expected)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with a smaller value" do
it "is false" do
actual = 777
expected = 42
partial = new_partial(actual)
matcher = Spectator::Matchers::LessThanEqualMatcher.new(expected)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with an equal value" do
it "is true" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::LessThanEqualMatcher.new(value)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
end
describe "#values" do
context "expected" do
it "is the expected value" do
actual = 42
expected = 777
partial = new_partial(actual)
matcher = Spectator::Matchers::LessThanEqualMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(expected)
end
it "is prefixed with <=" do
actual = 42
expected = 777
partial = new_partial(actual)
matcher = Spectator::Matchers::LessThanEqualMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:to_s].should start_with("<=")
end
end
context "actual" do
it "is the actual value" do
actual = 42
expected = 777
partial = new_partial(actual)
matcher = Spectator::Matchers::LessThanEqualMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(actual)
end
end
end
describe "#message" do
it "mentions <=" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::LessThanEqualMatcher.new(value)
match_data = matcher.match(partial)
match_data.message.should contain("<=")
end
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::LessThanEqualMatcher.new(value)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
value = 42
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::LessThanEqualMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value1 = 42
value2 = 777
partial = new_partial(value1)
matcher = Spectator::Matchers::LessThanEqualMatcher.new(value2)
match_data = matcher.match(partial)
match_data.message.should contain(value2.to_s)
end
end
end
describe "#negated_message" do
it "mentions <=" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::LessThanEqualMatcher.new(value)
match_data = matcher.match(partial)
match_data.negated_message.should contain("<=")
end
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::LessThanEqualMatcher.new(value)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
value = 42
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::LessThanEqualMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value1 = 42
value2 = 777
partial = new_partial(value1)
matcher = Spectator::Matchers::LessThanEqualMatcher.new(value2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(value2.to_s)
end
end
end
end
end
end

View file

@ -1,160 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::LessThanMatcher do
describe "#match" do
it "compares using #<" do
spy = SpySUT.new
partial = new_partial(spy)
matcher = Spectator::Matchers::LessThanMatcher.new(42)
matcher.match(partial)
spy.lt_call_count.should be > 0
end
context "returned MatchData" do
describe "#matched?" do
context "with a larger value" do
it "is true" do
actual = 42
expected = 777
partial = new_partial(actual)
matcher = Spectator::Matchers::LessThanMatcher.new(expected)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with a smaller value" do
it "is false" do
actual = 777
expected = 42
partial = new_partial(actual)
matcher = Spectator::Matchers::LessThanMatcher.new(expected)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with an equal value" do
it "is false" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::LessThanMatcher.new(value)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
describe "#values" do
context "expected" do
it "is the expected value" do
actual = 42
expected = 777
partial = new_partial(actual)
matcher = Spectator::Matchers::LessThanMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(expected)
end
it "is prefixed with <" do
actual = 42
expected = 777
partial = new_partial(actual)
matcher = Spectator::Matchers::LessThanMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:to_s].should start_with("<")
end
end
context "actual" do
it "is the actual value" do
actual = 42
expected = 777
partial = new_partial(actual)
matcher = Spectator::Matchers::LessThanMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(actual)
end
end
end
describe "#message" do
it "mentions <" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::LessThanMatcher.new(value)
match_data = matcher.match(partial)
match_data.message.should contain("<")
end
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::LessThanMatcher.new(value)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
value = 42
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::LessThanMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value1 = 42
value2 = 777
partial = new_partial(value1)
matcher = Spectator::Matchers::LessThanMatcher.new(value2)
match_data = matcher.match(partial)
match_data.message.should contain(value2.to_s)
end
end
end
describe "#negated_message" do
it "mentions <" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::LessThanMatcher.new(value)
match_data = matcher.match(partial)
match_data.negated_message.should contain("<")
end
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::LessThanMatcher.new(value)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
value = 42
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::LessThanMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value1 = 42
value2 = 777
partial = new_partial(value1)
matcher = Spectator::Matchers::LessThanMatcher.new(value2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(value2.to_s)
end
end
end
end
end
end

View file

@ -1,86 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::NilMatcher do
describe "#match" do
context "returned MatchData" do
describe "#matched?" do
context "with nil" do
it "is true" do
value = nil.as(Bool?)
partial = new_partial(value)
matcher = Spectator::Matchers::NilMatcher.new
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with not nil" do
it "is false" do
value = true.as(Bool?)
partial = new_partial(value)
matcher = Spectator::Matchers::NilMatcher.new
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
describe "#values" do
context "expected" do
it "is nil" do
partial = new_partial(42)
matcher = Spectator::Matchers::NilMatcher.new
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(nil)
end
end
context "actual" do
it "is the actual value" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::NilMatcher.new
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(value)
end
end
end
describe "#message" do
it "mentions nil" do
partial = new_partial(42)
matcher = Spectator::Matchers::NilMatcher.new
match_data = matcher.match(partial)
match_data.message.should contain("nil")
end
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::NilMatcher.new
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
end
describe "#negated_message" do
it "mentions nil" do
partial = new_partial(42)
matcher = Spectator::Matchers::NilMatcher.new
match_data = matcher.match(partial)
match_data.negated_message.should contain("nil")
end
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::NilMatcher.new
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
end
end
end
end

View file

@ -1,89 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::PredicateMatcher do
describe "#match" do
context "returned MatchData" do
describe "#match?" do
context "with a true predicate" do
it "is true" do
value = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::PredicateMatcher.new({ascii_only: Tuple.new}, "ascii_only")
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with a false predicate" do
it "is false" do
value = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::PredicateMatcher.new({empty: Tuple.new}, "empty")
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
describe "#values" do
it "contains a key for each expected attribute" do
value = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::PredicateMatcher.new({empty: Tuple.new, ascii_only: Tuple.new}, "empty, ascii_only")
match_data = matcher.match(partial)
match_data_has_key?(match_data.values, :empty).should be_true
match_data_has_key?(match_data.values, :ascii_only).should be_true
end
it "has the actual values" do
value = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::PredicateMatcher.new({empty: Tuple.new, ascii_only: Tuple.new}, "empty, ascii_only")
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :empty)[:value].should eq(value.empty?)
match_data_value_sans_prefix(match_data.values, :ascii_only)[:value].should eq(value.ascii_only?)
end
end
describe "#message" do
it "contains the actual label" do
value = "foobar"
label = "blah"
partial = new_partial(value, label)
matcher = Spectator::Matchers::PredicateMatcher.new({ascii_only: Tuple.new}, "ascii_only")
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
value = "foobar"
label = "blah"
partial = new_partial(value)
matcher = Spectator::Matchers::PredicateMatcher.new({ascii_only: Tuple.new}, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
end
describe "#negated_message" do
it "contains the actual label" do
value = "foobar"
label = "blah"
partial = new_partial(value, label)
matcher = Spectator::Matchers::PredicateMatcher.new({ascii_only: Tuple.new}, "ascii_only")
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
value = "foobar"
label = "blah"
partial = new_partial(value)
matcher = Spectator::Matchers::PredicateMatcher.new({ascii_only: Tuple.new}, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
end
end
end
end

View file

@ -1,686 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::RangeMatcher do
describe "#match" do
context "returned MatchData" do
describe "#matched?" do
context "inclusive" do
it "is true for lower-bound" do
lower = 3
upper = 9
value = lower
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for lower-bound minus 1" do
lower = 3
upper = 9
value = lower - 1
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
it "is true for mid-range" do
lower = 3
upper = 9
value = 5
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is true for upper-bound" do
lower = 3
upper = 9
value = upper
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for upper-bound plus 1" do
lower = 3
upper = 9
value = upper + 1
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "exclusive" do
it "is true for lower-bound" do
lower = 3
upper = 9
value = lower
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for lower-bound minus 1" do
lower = 3
upper = 9
value = lower - 1
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
it "is true for mid-range" do
lower = 3
upper = 9
value = 5
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for upper-bound" do
lower = 3
upper = 9
value = upper
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
it "is false for upper-bound plus 1" do
lower = 3
upper = 9
value = upper + 1
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
describe "#values" do
context "lower" do
it "is #begin from the expected range" do
range = Range.new(3, 9)
partial = new_partial(5)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :lower)[:value].should eq(range.begin)
end
it "is prefixed with >=" do
range = Range.new(3, 9)
partial = new_partial(5)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :lower)[:to_s].should start_with(">=")
end
end
context "upper" do
it "is #end from the expected range" do
range = Range.new(3, 9)
partial = new_partial(5)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :upper)[:value].should eq(range.end)
end
context "when inclusive" do
it "is prefixed with <=" do
range = Range.new(3, 9, exclusive: false)
partial = new_partial(5)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :upper)[:to_s].should start_with("<=")
end
end
context "when exclusive" do
it "is prefixed with <" do
range = Range.new(3, 9, exclusive: false)
partial = new_partial(5)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :upper)[:to_s].should start_with("<")
end
end
end
context "actual" do
it "is the actual value" do
value = 5
range = Range.new(3, 9)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(value)
end
end
end
describe "#message" do
it "contains the actual label" do
range = 1..10
value = 5
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
range = 1..10
value = 5
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
range = 1..10
value = 5
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data.message.should contain(range.to_s)
end
end
end
describe "#negated_message" do
it "contains the actual label" do
range = 1..10
value = 5
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
range = 1..10
value = 5
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
range = 1..10
value = 5
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial)
match_data.negated_message.should contain(range.to_s)
end
end
end
end
end
describe "#inclusive" do
context "initially exclusive" do
it "is true for lower-bound" do
lower = 3
upper = 9
value = lower
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).inclusive
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for lower-bound minus 1" do
lower = 3
upper = 9
value = lower - 1
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).inclusive
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
it "is true for mid-range" do
lower = 3
upper = 9
value = 5
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).inclusive
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is true for upper-bound" do
lower = 3
upper = 9
value = upper
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).inclusive
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for upper-bound plus 1" do
lower = 3
upper = 9
value = upper + 1
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).inclusive
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
describe "#message" do
it "mentions inclusive" do
range = 1...10
value = 5
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).inclusive
match_data = matcher.match(partial)
match_data.message.should contain("inclusive")
end
it "does not mention exclusive" do
range = 1...10
value = 5
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).inclusive
match_data = matcher.match(partial)
match_data.message.should_not contain("exclusive")
end
it "contains the original label" do
range = 1...10
value = 5
label = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range, label).inclusive
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
end
describe "#negated_message" do
it "mentions inclusive" do
range = 1...10
value = 5
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).inclusive
match_data = matcher.match(partial)
match_data.negated_message.should contain("inclusive")
end
it "does not mention exclusive" do
range = 1...10
value = 5
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).inclusive
match_data = matcher.match(partial)
match_data.negated_message.should_not contain("exclusive")
end
it "contains the original label" do
range = 1...10
value = 5
label = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range, label).inclusive
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
end
end
context "initially inclusive" do
it "is true for lower-bound" do
lower = 3
upper = 9
value = lower
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).inclusive
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for lower-bound minus 1" do
lower = 3
upper = 9
value = lower - 1
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).inclusive
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
it "is true for mid-range" do
lower = 3
upper = 9
value = 5
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).inclusive
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is true for upper-bound" do
lower = 3
upper = 9
value = upper
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).inclusive
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for upper-bound plus 1" do
lower = 3
upper = 9
value = upper + 1
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).inclusive
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
describe "#message" do
it "mentions inclusive" do
range = 1...10
value = 5
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).inclusive
match_data = matcher.match(partial)
match_data.message.should contain("inclusive")
end
it "contains the original label" do
range = 1..10
value = 5
label = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range, label).inclusive
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
end
describe "#negated_message" do
it "mentions inclusive" do
range = 1..10
value = 5
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).inclusive
match_data = matcher.match(partial)
match_data.negated_message.should contain("inclusive")
end
it "contains the original label" do
range = 1...10
value = 5
label = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range, label).inclusive
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
end
end
end
describe "#exclusive" do
context "initially inclusive" do
it "is true for lower-bound" do
lower = 3
upper = 9
value = lower
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).exclusive
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for lower-bound minus 1" do
lower = 3
upper = 9
value = lower - 1
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).exclusive
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
it "is true for mid-range" do
lower = 3
upper = 9
value = 5
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).exclusive
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for upper-bound" do
lower = 3
upper = 9
value = upper
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).exclusive
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
it "is false for upper-bound plus 1" do
lower = 3
upper = 9
value = upper + 1
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).exclusive
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
describe "#message" do
it "mentions exclusive" do
range = 1..10
value = 5
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).exclusive
match_data = matcher.match(partial)
match_data.message.should contain("exclusive")
end
it "does not mention inclusive" do
range = 1..10
value = 5
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).exclusive
match_data = matcher.match(partial)
match_data.message.should_not contain("inclusive")
end
it "contains the original label" do
range = 1..10
value = 5
label = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range, label).exclusive
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
end
describe "#negated_message" do
it "mentions exclusive" do
range = 1..10
value = 5
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).exclusive
match_data = matcher.match(partial)
match_data.negated_message.should contain("exclusive")
end
it "does not mention inclusive" do
range = 1..10
value = 5
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).exclusive
match_data = matcher.match(partial)
match_data.negated_message.should_not contain("inclusive")
end
it "contains the original label" do
range = 1..10
value = 5
label = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range, label).exclusive
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
end
end
context "initially exclusive" do
it "is true for lower-bound" do
lower = 3
upper = 9
value = lower
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).exclusive
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for lower-bound minus 1" do
lower = 3
upper = 9
value = lower - 1
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).exclusive
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
it "is true for mid-range" do
lower = 3
upper = 9
value = 5
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).exclusive
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
it "is false for upper-bound" do
lower = 3
upper = 9
value = upper
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).exclusive
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
it "is false for upper-bound plus 1" do
lower = 3
upper = 9
value = upper + 1
range = Range.new(lower, upper, exclusive: true)
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).exclusive
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
describe "#message" do
it "mentions exclusive" do
range = 1...10
value = 5
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).exclusive
match_data = matcher.match(partial)
match_data.message.should contain("exclusive")
end
it "contains the original label" do
range = 1...10
value = 5
label = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range, label).exclusive
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
end
describe "#negated_message" do
it "mentions exclusive" do
range = 1...10
value = 5
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range).exclusive
match_data = matcher.match(partial)
match_data.negated_message.should contain("exclusive")
end
it "contains the original label" do
range = 1...10
value = 5
label = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(range, label).exclusive
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
end
end
end
end

View file

@ -1,152 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::ReferenceMatcher do
describe "#match" do
context "returned MatchData" do
describe "#matched?" do
context "with the same instance" do
it "is true" do
# Box is used because it is a reference type and doesn't override the == method.
ref = Box.new([] of Int32)
partial = new_partial(ref)
matcher = Spectator::Matchers::ReferenceMatcher.new(ref)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with different instances" do
context "with same contents" do
it "is false" do
array1 = [1, 2, 3]
array2 = [1, 2, 3]
partial = new_partial(array1)
matcher = Spectator::Matchers::ReferenceMatcher.new(array2)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with a duplicated instance" do
it "is false" do
array1 = [1, 2, 3]
array2 = array1.dup
partial = new_partial(array1)
matcher = Spectator::Matchers::ReferenceMatcher.new(array2)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with the same type" do
it "is false" do
obj1 = "foo"
obj2 = "bar"
partial = new_partial(obj1)
matcher = Spectator::Matchers::ReferenceMatcher.new(obj2)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with a different type" do
it "is false" do
obj1 = "foobar"
obj2 = [1, 2, 3]
partial = new_partial(obj1)
matcher = Spectator::Matchers::ReferenceMatcher.new(obj2)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
end
describe "#values" do
context "expected" do
it "is the expected value" do
actual = "foobar"
expected = /foo/
partial = new_partial(actual)
matcher = Spectator::Matchers::ReferenceMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(expected)
end
end
context "actual" do
it "is the actual value" do
actual = "foobar"
expected = /foo/
partial = new_partial(actual)
matcher = Spectator::Matchers::ReferenceMatcher.new(expected)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(actual)
end
end
end
describe "#message" do
it "contains the actual label" do
value = "foobar"
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::ReferenceMatcher.new(value)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
value = "foobar"
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::ReferenceMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
obj1 = "foo"
obj2 = "bar"
partial = new_partial(obj1)
matcher = Spectator::Matchers::ReferenceMatcher.new(obj2)
match_data = matcher.match(partial)
match_data.message.should contain(obj2.to_s)
end
end
end
describe "#negated_message" do
it "contains the actual label" do
value = "foobar"
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::ReferenceMatcher.new(value)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
value = "foobar"
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::ReferenceMatcher.new(value, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
obj1 = "foo"
obj2 = "bar"
partial = new_partial(obj1)
matcher = Spectator::Matchers::ReferenceMatcher.new(obj2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(obj2.to_s)
end
end
end
end
end
end

View file

@ -1,123 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::RespondMatcher do
describe "#match" do
context "returned MatchData" do
describe "#matched?" do
context "one method" do
context "with a responding method" do
it "is true" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil)).new
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "against a non-responding method" do
it "is false" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(downcase: Nil)).new
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "multiple methods" do
context "with one responding method" do
it "is false" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil, downcase: Nil)).new
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with all responding methods" do
it "is true" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil, to_a: Nil)).new
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with no responding methods" do
it "is false" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(downcase: Nil, upcase: Nil)).new
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
end
describe "#values" do
it "contains a key for each expected method" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil, downcase: Nil)).new
match_data = matcher.match(partial)
match_data_has_key?(match_data.values, :"responds to #size").should be_true
match_data_has_key?(match_data.values, :"responds to #downcase").should be_true
end
it "has the actual values" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil, downcase: Nil)).new
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :"responds to #size")[:value].should be_true
match_data_value_sans_prefix(match_data.values, :"responds to #downcase")[:value].should be_false
end
end
describe "#message" do
it "contains the actual label" do
value = "foobar"
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil, downcase: Nil)).new
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the method names" do
value = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil, downcase: Nil)).new
match_data = matcher.match(partial)
match_data.message.should contain("#size")
match_data.message.should contain("#downcase")
end
end
describe "#negated_message" do
it "contains the actual label" do
value = "foobar"
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil, downcase: Nil)).new
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the method names" do
value = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::RespondMatcher(NamedTuple(size: Nil, downcase: Nil)).new
match_data = matcher.match(partial)
match_data.message.should contain("#size")
match_data.negated_message.should contain("#downcase")
end
end
end
end
end

View file

@ -1,121 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::SizeMatcher do
describe "#match" do
context "returned MatchData" do
describe "#matched?" do
context "with a matching number of items" do
it "is true" do
array = %i[a b c]
size = array.size
partial = new_partial(array)
matcher = Spectator::Matchers::SizeMatcher.new(size)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with a different number of items" do
it "is false" do
array = %i[a b c]
size = array.size + 1
partial = new_partial(array)
matcher = Spectator::Matchers::SizeMatcher.new(size)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
describe "#values" do
context "expected" do
it "is the size given" do
array = %i[a b c]
size = array.size + 1
partial = new_partial(array)
matcher = Spectator::Matchers::SizeMatcher.new(size)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(size)
end
end
context "actual" do
it "is the size of the set" do
array = %i[a b c]
size = array.size + 1
partial = new_partial(array)
matcher = Spectator::Matchers::SizeMatcher.new(size)
match_data = matcher.match(partial)
match_data_value_with_key(match_data.values, :actual).value.should eq(array.size)
end
end
end
describe "#message" do
it "contains the actual label" do
array = %i[a b c]
size = array.size + 1
label = "foobar"
partial = new_partial(array, label)
matcher = Spectator::Matchers::SizeMatcher.new(size)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
array = %i[a b c]
size = array.size + 1
label = "foobar"
partial = new_partial(array)
matcher = Spectator::Matchers::SizeMatcher.new(size, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
array = %i[a b c]
size = array.size + 1
partial = new_partial(array)
matcher = Spectator::Matchers::SizeMatcher.new(size)
match_data = matcher.match(partial)
match_data.message.should contain(size.to_s)
end
end
end
describe "#negated_message" do
it "contains the actual label" do
array = %i[a b c]
size = array.size + 1
label = "foobar"
partial = new_partial(array, label)
matcher = Spectator::Matchers::SizeMatcher.new(size)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
array = %i[a b c]
size = array.size + 1
label = "foobar"
partial = new_partial(array)
matcher = Spectator::Matchers::SizeMatcher.new(size, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
array = %i[a b c]
size = array.size + 1
partial = new_partial(array)
matcher = Spectator::Matchers::SizeMatcher.new(size)
match_data = matcher.match(partial)
match_data.negated_message.should contain(size.to_s)
end
end
end
end
end
end

View file

@ -1,121 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::SizeOfMatcher do
describe "#match" do
context "returned MatchData" do
describe "#matched?" do
context "with a matching number of items" do
it "is true" do
array = %i[a b c]
other = [1, 2, 3]
partial = new_partial(array)
matcher = Spectator::Matchers::SizeOfMatcher.new(other)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with a different number of items" do
it "is false" do
array = %i[a b c]
other = [1, 2, 3, 4]
partial = new_partial(array)
matcher = Spectator::Matchers::SizeOfMatcher.new(other)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
describe "#values" do
context "expected" do
it "is the size given" do
array = %i[a b c]
other = [1, 2, 3, 4]
partial = new_partial(array)
matcher = Spectator::Matchers::SizeOfMatcher.new(other)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(other.size)
end
end
context "actual" do
it "is the size of the set" do
array = %i[a b c]
other = [1, 2, 3, 4]
partial = new_partial(array)
matcher = Spectator::Matchers::SizeOfMatcher.new(other)
match_data = matcher.match(partial)
match_data_value_with_key(match_data.values, :actual).value.should eq(array.size)
end
end
end
describe "#message" do
it "contains the actual label" do
array = %i[a b c]
other = [1, 2, 3, 4]
label = "foobar"
partial = new_partial(array, label)
matcher = Spectator::Matchers::SizeOfMatcher.new(other)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
array = %i[a b c]
other = [1, 2, 3, 4]
label = "foobar"
partial = new_partial(array)
matcher = Spectator::Matchers::SizeOfMatcher.new(other, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
array = %i[a b c]
other = [1, 2, 3, 4]
partial = new_partial(array)
matcher = Spectator::Matchers::SizeOfMatcher.new(other)
match_data = matcher.match(partial)
match_data.message.should contain(other.to_s)
end
end
end
describe "#negated_message" do
it "contains the actual label" do
array = %i[a b c]
other = [1, 2, 3, 4]
label = "foobar"
partial = new_partial(array, label)
matcher = Spectator::Matchers::SizeOfMatcher.new(other)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
array = %i[a b c]
other = [1, 2, 3, 4]
label = "foobar"
partial = new_partial(array)
matcher = Spectator::Matchers::SizeOfMatcher.new(other, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
array = %i[a b c]
other = [1, 2, 3, 4]
partial = new_partial(array)
matcher = Spectator::Matchers::SizeOfMatcher.new(other)
match_data = matcher.match(partial)
match_data.negated_message.should contain(other.to_s)
end
end
end
end
end
end

View file

@ -1,393 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::StartWithMatcher do
describe "#match" do
context "returned MatchData" do
describe "#matched?" do
context "with a String" do
context "against a matching string" do
it "is true" do
value = "foobar"
start = "foo"
partial = new_partial(value)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "not at start" do
it "is false" do
value = "foobar"
start = "bar"
partial = new_partial(value)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "against a different string" do
it "is false" do
value = "foobar"
start = "baz"
partial = new_partial(value)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a matching character" do
it "is true" do
value = "foobar"
start = 'f'
partial = new_partial(value)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "not at start" do
it "is false" do
value = "foobar"
start = 'b'
partial = new_partial(value)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "against a different character" do
it "is false" do
value = "foobar"
start = 'z'
partial = new_partial(value)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a matching regex" do
it "is true" do
value = "FOOBAR"
start = /foo/i
partial = new_partial(value)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "not at start" do
it "is false" do
value = "FOOBAR"
start = /bar/i
partial = new_partial(value)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "against a non-matching regex" do
it "is false" do
value = "FOOBAR"
start = /baz/i
partial = new_partial(value)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "with an Enumberable" do
context "against an equal value" do
it "is true" do
array = %i[a b c]
start = :a
partial = new_partial(array)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "not at start" do
it "is false" do
array = %i[a b c]
start = :b
partial = new_partial(array)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "against a different value" do
it "is false" do
array = %i[a b c]
start = :z
partial = new_partial(array)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against matching element type" do
it "is true" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::StartWithMatcher.new(Symbol)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "not at start" do
it "is false" do
array = [1, 2, 3, :a, :b, :c]
partial = new_partial(array)
matcher = Spectator::Matchers::StartWithMatcher.new(Symbol)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "against different element type" do
it "is false" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::StartWithMatcher.new(Int32)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "against a matching regex" do
it "is true" do
array = %w[FOO BAR BAZ]
start = /foo/i
partial = new_partial(array)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
context "not at start" do
it "is false" do
array = %w[FOO BAR BAZ]
start = /bar/i
partial = new_partial(array)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
context "against a non-matching regex" do
it "is false" do
array = %w[FOO BAR BAZ]
start = /qux/i
partial = new_partial(array)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
end
describe "#values" do
context "with a String" do
context "expected" do
it "is the expected value" do
value = "FOOBAR"
first = /baz/i
partial = new_partial(value)
matcher = Spectator::Matchers::StartWithMatcher.new(first)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(first)
end
end
context "actual" do
it "is the actual value" do
value = "FOOBAR"
first = /baz/i
partial = new_partial(value)
matcher = Spectator::Matchers::StartWithMatcher.new(first)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(value)
end
end
end
context "with an Indexable" do
context "expected" do
it "is the expected value" do
array = %w[FOO BAR BAZ]
first = /qux/i
partial = new_partial(array)
matcher = Spectator::Matchers::StartWithMatcher.new(first)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(first)
end
end
context "actual" do
it "is the first element" do
array = %w[FOO BAR BAZ]
first = /qux/i
partial = new_partial(array)
matcher = Spectator::Matchers::StartWithMatcher.new(first)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(array.first)
end
end
context "list" do
it "is the full actual list" do
array = %w[FOO BAR BAZ]
first = /qux/i
partial = new_partial(array)
matcher = Spectator::Matchers::StartWithMatcher.new(first)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :list)[:value].should eq(array)
end
end
end
end
describe "#message" do
context "with a String" do
it "mentions #starts_with?" do
value = "foobar"
start = "baz"
partial = new_partial(value)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.message.should contain("#starts_with?")
end
end
context "with an Enumerable" do
it "mentions ===" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::StartWithMatcher.new(array.first)
match_data = matcher.match(partial)
match_data.message.should contain("===")
end
it "mentions first" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::StartWithMatcher.new(array.first)
match_data = matcher.match(partial)
match_data.message.should contain("first")
end
end
it "contains the actual label" do
value = "foobar"
start = "baz"
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
value = "foobar"
start = "baz"
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::StartWithMatcher.new(start, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value = "foobar"
start = "baz"
partial = new_partial(value)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.message.should contain(start)
end
end
end
describe "#negated_message" do
context "with a String" do
it "mentions #starts_with?" do
value = "foobar"
start = "baz"
partial = new_partial(value)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.negated_message.should contain("#starts_with?")
end
end
context "with an Enumerable" do
it "mentions ===" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::StartWithMatcher.new(array.first)
match_data = matcher.match(partial)
match_data.negated_message.should contain("===")
end
it "mentions first" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::StartWithMatcher.new(array.first)
match_data = matcher.match(partial)
match_data.negated_message.should contain("first")
end
end
it "contains the actual label" do
value = "foobar"
start = "baz"
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
value = "foobar"
start = "baz"
label = "everything"
partial = new_partial(value)
matcher = Spectator::Matchers::StartWithMatcher.new(start, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected value" do
value = "foobar"
start = "baz"
partial = new_partial(value)
matcher = Spectator::Matchers::StartWithMatcher.new(start)
match_data = matcher.match(partial)
match_data.negated_message.should contain(start)
end
end
end
end
end
end

View file

@ -1,374 +0,0 @@
require "../spec_helper"
# This is a terrible hack,
# but I don't want to expose `ValueMatcher#expected` publicly
# just for this spec.
module Spectator::Matchers
struct ValueMatcher(ExpectedType)
def expected_value
expected
end
end
end
def be_comparison
Spectator::Matchers::TruthyMatcher.new(true)
end
describe Spectator::Matchers::TruthyMatcher do
describe "#match" do
context "returned MatchData" do
context "truthy" do
describe "#matched?" do
context "with a truthy value" do
it "is true" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(true)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with false" do
it "is false" do
value = false
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(true)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with nil" do
it "is false" do
value = nil
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(true)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
end
describe "#values" do
context "expected" do
it "contains the definition of falsey" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(true)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:to_s].should match(/false or nil/i)
end
it "is prefixed with \"Not\"" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(true)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:to_s].should start_with(/not/i)
end
end
context "actual" do
it "is the actual value" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(true)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(value)
end
end
context "truthy" do
context "when the actual value is truthy" do
it "is true" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(true)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :truthy)[:value].should be_true
end
end
context "when the actual value is false" do
it "is false" do
value = false
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(true)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :truthy)[:value].should be_false
end
end
context "when the actual value is nil" do
it "is false" do
value = nil
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(true)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :truthy)[:value].should be_false
end
end
end
end
describe "#message" do
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::TruthyMatcher.new(true)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the \"truthy\"" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(true)
match_data = matcher.match(partial)
match_data.message.should contain("truthy")
end
end
describe "#negated_message" do
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::TruthyMatcher.new(true)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the \"truthy\"" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(true)
match_data = matcher.match(partial)
match_data.negated_message.should contain("truthy")
end
end
end
context "falsey" do
describe "#matched?" do
context "with a truthy value" do
it "is false" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(false)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with false" do
it "is true" do
value = false
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(false)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with nil" do
it "is true" do
value = nil
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(false)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
end
describe "#values" do
context "expected" do
it "contains the definition of falsey" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(false)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:to_s].should match(/false or nil/i)
end
it "is not prefixed with \"Not\"" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(false)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:to_s].should_not start_with(/not/i)
end
end
context "actual" do
it "is the actual value" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(false)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(value)
end
end
context "truthy" do
context "when the actual value is truthy" do
it "is true" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(false)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :truthy)[:value].should be_true
end
end
context "when the actual value is false" do
it "is false" do
value = false
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(false)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :truthy)[:value].should be_false
end
end
context "when the actual value is nil" do
it "is false" do
value = nil
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(false)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :truthy)[:value].should be_false
end
end
end
end
describe "#message" do
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::TruthyMatcher.new(false)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the \"falsey\"" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(false)
match_data = matcher.match(partial)
match_data.message.should contain("falsey")
end
end
describe "#negated_message" do
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::TruthyMatcher.new(false)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the \"falsey\"" do
value = 42
partial = new_partial(value)
matcher = Spectator::Matchers::TruthyMatcher.new(false)
match_data = matcher.match(partial)
match_data.negated_message.should contain("falsey")
end
end
end
end
end
describe "#<" do
it "returns a LessThanMatcher" do
value = 0
matcher = be_comparison < value
matcher.should be_a(Spectator::Matchers::LessThanMatcher(typeof(value)))
end
it "passes along the expected value" do
value = 42
matcher = be_comparison < value
matcher.expected_value.should eq(value)
end
end
describe "#<=" do
it "returns a LessThanEqualMatcher" do
value = 0
matcher = be_comparison <= value
matcher.should be_a(Spectator::Matchers::LessThanEqualMatcher(typeof(value)))
end
it "passes along the expected value" do
value = 42
matcher = be_comparison <= value
matcher.expected_value.should eq(value)
end
end
describe "#>" do
it "returns a GreaterThanMatcher" do
value = 0
matcher = be_comparison > value
matcher.should be_a(Spectator::Matchers::GreaterThanMatcher(typeof(value)))
end
it "passes along the expected value" do
value = 42
matcher = be_comparison > value
matcher.expected_value.should eq(value)
end
end
describe "#>=" do
it "returns a GreaterThanEqualMatcher" do
value = 0
matcher = be_comparison >= value
matcher.should be_a(Spectator::Matchers::GreaterThanEqualMatcher(typeof(value)))
end
it "passes along the expected value" do
value = 42
matcher = be_comparison >= value
matcher.expected_value.should eq(value)
end
end
describe "#==" do
it "returns an EqualityMatcher" do
value = 0
matcher = be_comparison == value
matcher.should be_a(Spectator::Matchers::EqualityMatcher(typeof(value)))
end
it "passes along the expected value" do
value = 42
matcher = be_comparison == value
matcher.expected_value.should eq(value)
end
end
describe "#!=" do
it "returns an InequalityMatcher" do
value = 0
matcher = be_comparison != value
matcher.should be_a(Spectator::Matchers::InequalityMatcher(typeof(value)))
end
it "passes along the expected value" do
value = 42
matcher = be_comparison != value
matcher.expected_value.should eq(value)
end
end
end

View file

@ -1,125 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::TypeMatcher do
describe "#match" do
context "returned MatchData" do
describe "#matched?" do
context "with the same type" do
it "is true" do
value = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::TypeMatcher(String).new
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with a different type" do
it "is false" do
value = "foobar"
partial = new_partial(value)
matcher = Spectator::Matchers::TypeMatcher(Int32).new
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with a parent type" do
it "is true" do
value = IO::Memory.new
partial = new_partial(value)
matcher = Spectator::Matchers::TypeMatcher(IO).new
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
context "with a child type" do
it "is false" do
value = Exception.new("foobar")
partial = new_partial(value)
matcher = Spectator::Matchers::TypeMatcher(ArgumentError).new
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
context "with a mix-in" do
it "is true" do
value = %i[a b c]
partial = new_partial(value)
matcher = Spectator::Matchers::TypeMatcher(Enumerable(Symbol)).new
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
end
describe "#values" do
context "expected" do
it "is the expected type name" do
value = %i[a b c]
partial = new_partial(value)
matcher = Spectator::Matchers::TypeMatcher(String).new
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(String)
end
end
context "actual" do
it "is the actual type name" do
value = %i[a b c]
partial = new_partial(value)
matcher = Spectator::Matchers::TypeMatcher(String).new
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(typeof(value))
end
it "is the runtime type name" do
value = 42.as(Int32?)
partial = new_partial(value)
matcher = Spectator::Matchers::TypeMatcher(String).new
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(value.class)
end
end
end
describe "#message" do
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::TypeMatcher(String).new
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected type" do
partial = new_partial(42)
matcher = Spectator::Matchers::TypeMatcher(String).new
match_data = matcher.match(partial)
match_data.message.should contain("String")
end
end
describe "#negated_message" do
it "contains the actual label" do
value = 42
label = "everything"
partial = new_partial(value, label)
matcher = Spectator::Matchers::TypeMatcher(String).new
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected type" do
partial = new_partial(42)
matcher = Spectator::Matchers::TypeMatcher(String).new
match_data = matcher.match(partial)
match_data.negated_message.should contain("String")
end
end
end
end
end

View file

@ -1,474 +0,0 @@
require "../spec_helper"
describe Spectator::Matchers::UnorderedArrayMatcher do
describe "#match" do
context "returned MatchData" do
context "with identical arrays" do
describe "#matched?" do
it "is true" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
describe "#values" do
context "expected" do
it "is the expected array" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(array)
end
end
context "actual" do
it "is the actual array" do
array = %i[a b c]
partial = new_partial(array)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(array)
end
end
end
describe "#message" do
it "contains the actual label" do
array = %i[a b c]
label = "everything"
partial = new_partial(array, label)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
array = %i[a b c]
label = "everything"
partial = new_partial(array)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected array" do
array1 = %i[a b c]
array2 = [1, 2, 3]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.message.should contain(array2.to_s)
end
end
end
describe "#negated_message" do
it "contains the actual label" do
array = %i[a b c]
label = "everything"
partial = new_partial(array, label)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
array = %i[a b c]
label = "everything"
partial = new_partial(array)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected array" do
array1 = %i[a b c]
array2 = [1, 2, 3]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(array2.to_s)
end
end
end
end
context "with identical unordered arrays" do
describe "#matched?" do
it "is true" do
array1 = %i[a b c]
array2 = %i[c a b]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.matched?.should be_true
end
end
describe "#values" do
context "expected" do
it "is the expected array" do
array1 = %i[a b c]
array2 = %i[c a b]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(array2)
end
end
context "actual" do
it "is the actual array" do
array1 = %i[a b c]
array2 = %i[c a b]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(array1)
end
end
end
describe "#message" do
it "contains the actual label" do
array1 = %i[a b c]
array2 = %i[c a b]
label = "everything"
partial = new_partial(array1, label)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
array1 = %i[a b c]
array2 = %i[c a b]
label = "everything"
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected array" do
array1 = %i[a b c]
array2 = %i[c a b]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.message.should contain(array2.to_s)
end
end
end
describe "#negated_message" do
it "contains the actual label" do
array1 = %i[a b c]
array2 = %i[c a b]
label = "everything"
partial = new_partial(array1, label)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
array1 = %i[a b c]
array2 = %i[c a b]
label = "everything"
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected array" do
array1 = %i[a b c]
array2 = %i[c a b]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(array2.to_s)
end
end
end
end
context "with arrays differing in size" do
describe "#matched?" do
it "is false" do
array1 = %i[a b c d e]
array2 = %i[a c e f]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
describe "#values" do
context "expected" do
it "is the expected array" do
array1 = %i[a b c d e]
array2 = %i[a c e f]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(array2)
end
end
context "actual" do
it "is the actual array" do
array1 = %i[a b c d e]
array2 = %i[a c e f]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(array1)
end
end
context "missing" do
it "is the missing items" do
array1 = %i[a b c d e]
array2 = %i[a c e f]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
missing = match_data_value_sans_prefix(match_data.values, :missing)[:value].as(typeof(array2))
missing.should contain(:f)
end
end
context "extra" do
it "is the extra items" do
array1 = %i[a b c d e]
array2 = %i[a c e f]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
extra = match_data_value_sans_prefix(match_data.values, :extra)[:value].as(typeof(array1))
extra.should contain(:b)
extra.should contain(:d)
end
end
end
describe "#message" do
it "contains the actual label" do
array1 = %i[a b c d e]
array2 = %i[a c e f]
label = "everything"
partial = new_partial(array1, label)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
array1 = %i[a b c d e]
array2 = %i[a c e f]
label = "everything"
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected array" do
array1 = %i[a b c d e]
array2 = %i[a c e f]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.message.should contain(array2.to_s)
end
end
end
describe "#negated_message" do
it "contains the actual label" do
array1 = %i[a b c d e]
array2 = %i[a c e f]
label = "everything"
partial = new_partial(array1, label)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
array1 = %i[a b c d e]
array2 = %i[a c e f]
label = "everything"
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected array" do
array1 = %i[a b c d e]
array2 = %i[a c e f]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(array2.to_s)
end
end
end
end
context "with arrays differing in content" do
describe "#matched?" do
it "is false" do
array1 = %i[a b c]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.matched?.should be_false
end
end
describe "#values" do
context "expected" do
it "is the expected array" do
array1 = %i[a b c]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :expected)[:value].should eq(array2)
end
end
context "actual" do
it "is the actual array" do
array1 = %i[a b c]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(array1)
end
end
context "missing" do
it "is the missing items" do
array1 = %i[a b c]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
missing = match_data_value_sans_prefix(match_data.values, :missing)[:value].as(typeof(array2))
missing.should contain(:x)
missing.should contain(:y)
missing.should contain(:z)
end
end
context "extra" do
it "is the extra items" do
array1 = %i[a b c]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
extra = match_data_value_sans_prefix(match_data.values, :extra)[:value].as(typeof(array1))
extra.should contain(:a)
extra.should contain(:b)
extra.should contain(:c)
end
end
end
describe "#message" do
it "contains the actual label" do
array1 = %i[a b c]
array2 = %i[x y z]
label = "everything"
partial = new_partial(array1, label)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
it "contains the expected label" do
array1 = %i[a b c]
array2 = %i[x y z]
label = "everything"
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2, label)
match_data = matcher.match(partial)
match_data.message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected array" do
array1 = %i[a b c]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.message.should contain(array2.to_s)
end
end
end
describe "#negated_message" do
it "mentions content" do
array1 = %i[a b c]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.negated_message.should contain("content")
end
it "contains the actual label" do
array1 = %i[a b c]
array2 = %i[x y z]
label = "everything"
partial = new_partial(array1, label)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
it "contains the expected label" do
array1 = %i[a b c]
array2 = %i[x y z]
label = "everything"
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2, label)
match_data = matcher.match(partial)
match_data.negated_message.should contain(label)
end
context "when expected label is omitted" do
it "contains stringified form of expected array" do
array1 = %i[a b c]
array2 = %i[x y z]
partial = new_partial(array1)
matcher = Spectator::Matchers::UnorderedArrayMatcher.new(array2)
match_data = matcher.match(partial)
match_data.negated_message.should contain(array2.to_s)
end
end
end
end
end
end
end

View file

@ -1,3 +1,7 @@
require "../expectations/expectation_partial"
require "../source"
require "../test_block"
require "../test_value"
require "./matcher_dsl"
module Spectator::DSL
@ -19,7 +23,9 @@ module Spectator::DSL
# Where the actual value is returned by the system-under-test,
# and the expected value is what the actual value should be to satisfy the condition.
macro expect(actual, _source_file = __FILE__, _source_line = __LINE__)
::Spectator::Expectations::ValueExpectationPartial.new({{actual}}, {{actual.stringify}}, {{_source_file}}, {{_source_line}})
test_value = ::Spectator::TestValue.new({{actual}}, {{actual.stringify}})
source = ::Spectator::Source.new({{_source_file}}, {{_source_line}})
::Spectator::Expectations::ExpectationPartial.new(test_value, source)
end
# Starts an expectation on a block of code.
@ -68,12 +74,15 @@ module Spectator::DSL
# The raw block can't be used because it's not clear to the user.
{% method_name = block.body.id.split('.')[1..-1].join('.') %}
%partial = %proc.partial(subject)
::Spectator::Expectations::BlockExpectationPartial.new(%partial, {{"#" + method_name}}, {{_source_file}}, {{_source_line}})
test_block = ::Spectator::TestBlock.create(%partial, {{"#" + method_name}})
{% else %}
# In this case, it looks like the short-hand method syntax wasn't used.
# Just drop in the proc as-is.
::Spectator::Expectations::BlockExpectationPartial.new(%proc, "`" + {{block.body.stringify}} + "`", {{_source_file}}, {{_source_line}})
test_block = ::Spectator::TestBlock.create(%proc, {{"`" + block.body.stringify + "`"}})
{% end %}
source = ::Spectator::Source.new({{_source_file}}, {{_source_line}})
::Spectator::Expectations::ExpectationPartial.new(test_block, source)
end
# Starts an expectation.

View file

@ -1,4 +1,6 @@
require "../matchers"
require "../test_block"
require "../test_value"
module Spectator::DSL
# Methods for defining matchers for expectations.
@ -12,7 +14,7 @@ module Spectator::DSL
# expect(1 + 2).to eq(3)
# ```
macro eq(expected)
::Spectator::Matchers::EqualityMatcher.new({{expected}}, {{expected.stringify}})
::Spectator::Matchers::EqualityMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.stringify}}))
end
# Indicates that some value should not equal another.
@ -24,7 +26,7 @@ module Spectator::DSL
# expect(1 + 2).to ne(5)
# ```
macro ne(expected)
::Spectator::Matchers::InequalityMatcher.new({{expected}}, {{expected.stringify}})
::Spectator::Matchers::InequalityMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.stringify}}))
end
# Indicates that some value when compared to another satisfies an operator.
@ -44,7 +46,7 @@ module Spectator::DSL
# expect("foo").to be_truthy
# ```
macro be
::Spectator::Matchers::TruthyMatcher.new(true)
::Spectator::Matchers::TruthyMatcher.new
end
# Indicates that some object should be the same as another.
@ -58,7 +60,7 @@ module Spectator::DSL
# expect(obj.dup).to_not be(obj)
# ```
macro be(expected)
::Spectator::Matchers::ReferenceMatcher.new({{expected}}, {{expected.stringify}})
::Spectator::Matchers::ReferenceMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.stringify}}))
end
# Indicates that some value should be of a specified type.
@ -115,7 +117,7 @@ module Spectator::DSL
# expect(3 - 1).to be_lt(3)
# ```
macro be_lt(expected)
::Spectator::Matchers::LessThanMatcher.new({{expected}}, {{expected.stringify}})
::Spectator::Matchers::LessThanMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.stringify}}))
end
# Indicates that some value should be less than or equal to another.
@ -127,7 +129,7 @@ module Spectator::DSL
# expect(3 - 1).to be_le(3)
# ```
macro be_le(expected)
::Spectator::Matchers::LessThanEqualMatcher.new({{expected}}, {{expected.stringify}})
::Spectator::Matchers::LessThanEqualMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.stringify}}))
end
# Indicates that some value should be greater than another.
@ -139,7 +141,7 @@ module Spectator::DSL
# expect(3 + 1).to be_gt(3)
# ```
macro be_gt(expected)
::Spectator::Matchers::GreaterThanMatcher.new({{expected}}, {{expected.stringify}})
::Spectator::Matchers::GreaterThanMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.stringify}}))
end
# Indicates that some value should be greater than or equal to another.
@ -151,7 +153,7 @@ module Spectator::DSL
# expect(3 + 1).to be_ge(3)
# ```
macro be_ge(expected)
::Spectator::Matchers::GreaterThanEqualMatcher.new({{expected}}, {{expected.stringify}})
::Spectator::Matchers::GreaterThanEqualMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.stringify}}))
end
# Indicates that some value should match another.
@ -168,7 +170,7 @@ module Spectator::DSL
# expect({:foo, 5}).to match({Symbol, Int32})
# ```
macro match(expected)
::Spectator::Matchers::CaseMatcher.new({{expected}}, {{expected.stringify}})
::Spectator::Matchers::CaseMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.stringify}}))
end
# Indicates that some value should be true.
@ -202,7 +204,7 @@ module Spectator::DSL
# expect(true).to be_truthy
# ```
macro be_truthy
::Spectator::Matchers::TruthyMatcher.new(true)
::Spectator::Matchers::TruthyMatcher.new
end
# Indicates that some value should be falsey.
@ -258,7 +260,7 @@ module Spectator::DSL
# NOTE: Do not attempt to mix the two use cases.
# It likely won't work and will result in a compilation error.
macro be_within(expected)
::Spectator::Matchers::CollectionMatcher.new({{expected}}, {{expected.stringify}})
::Spectator::Matchers::CollectionMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.stringify}}))
end
# Indicates that some value should be between a lower and upper-bound.
@ -279,7 +281,7 @@ module Spectator::DSL
# ```
macro be_between(min, max)
:Spectator::Matchers::RangeMatcher.new(
Range.new({{min}}, {{max}}),
::Spectator::TestValue.new(Range.new({{min}}, {{max}})),
[{{min.stringify}}, {{max.stringify}}].join(" to ")
)
end
@ -339,7 +341,7 @@ module Spectator::DSL
# expect(%w[foo bar]).to start_with(/foo/)
# ```
macro start_with(expected)
::Spectator::Matchers::StartWithMatcher.new({{expected}}, {{expected.stringify}})
::Spectator::Matchers::StartWithMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.stringify}}))
end
# Indicates that some value or set should end with another value.
@ -361,7 +363,7 @@ module Spectator::DSL
# expect(%w[foo bar]).to end_with(/bar/)
# ```
macro end_with(expected)
::Spectator::Matchers::EndWithMatcher.new({{expected}}, {{expected.stringify}})
::Spectator::Matchers::EndWithMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.stringify}}))
end
# Indicates that some value or set should contain another value.
@ -384,7 +386,7 @@ module Spectator::DSL
# expect(%i[a b c]).to contain(:a, :b)
# ```
macro contain(*expected)
::Spectator::Matchers::ContainMatcher.new({{expected}}, {{expected.splat.stringify}})
::Spectator::Matchers::ContainMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.splat.stringify}}))
end
# Indicates that some value or set should contain another value.
@ -413,7 +415,7 @@ module Spectator::DSL
# expect(%w[FOO BAR BAZ]).to have(/foo/i, String)
# ```
macro have(*expected)
::Spectator::Matchers::HaveMatcher.new({{expected}}, {{expected.splat.stringify}})
::Spectator::Matchers::HaveMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.splat.stringify}}))
end
# Indicates that some set, such as a `Hash`, has a given key.
@ -425,7 +427,7 @@ module Spectator::DSL
# expect({"lucky" => 7}).to have_key("lucky")
# ```
macro have_key(expected)
::Spectator::Matchers::HaveKeyMatcher.new({{expected}}, {{expected.stringify}})
::Spectator::Matchers::HaveKeyMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.stringify}}))
end
# ditto
@ -442,7 +444,7 @@ module Spectator::DSL
# expect({"lucky" => 7}).to have_value(7)
# ```
macro have_value(expected)
::Spectator::Matchers::HaveValueMatcher.new({{expected}}, {{expected.stringify}})
::Spectator::Matchers::HaveValueMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.stringify}}))
end
# ditto
@ -457,7 +459,7 @@ module Spectator::DSL
# expect([1, 2, 3]).to contain_exactly(1, 2, 3)
# ```
macro contain_exactly(*expected)
::Spectator::Matchers::ArrayMatcher.new({{expected}}, {{expected.stringify}})
::Spectator::Matchers::ArrayMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.stringify}}))
end
# Indicates that some set should contain the same values in exact order as another set.
@ -467,7 +469,7 @@ module Spectator::DSL
# expect([1, 2, 3]).to match_array([1, 2, 3])
# ```
macro match_array(expected)
::Spectator::Matchers::ArrayMatcher.new({{expected}}, {{expected.stringify}})
::Spectator::Matchers::ArrayMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.stringify}}))
end
# Indicates that some set should have a specified size.
@ -477,7 +479,7 @@ module Spectator::DSL
# expect([1, 2, 3]).to have_size(3)
# ```
macro have_size(expected)
::Spectator::Matchers::SizeMatcher.new({{expected}}, {{expected.stringify}})
::Spectator::Matchers::SizeMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.stringify}}))
end
# Indicates that some set should have the same size (number of elements) as another set.
@ -487,7 +489,7 @@ module Spectator::DSL
# expect([1, 2, 3]).to have_size_of(%i[x y z])
# ```
macro have_size_of(expected)
::Spectator::Matchers::SizeOfMatcher.new(({{expected}}), {{expected.stringify}})
::Spectator::Matchers::SizeOfMatcher.new(::Spectator::TestValue.new(({{expected}}), {{expected.stringify}}))
end
# Indicates that some value should have a set of attributes matching some conditions.
@ -501,7 +503,7 @@ module Spectator::DSL
# expect(%i[a b c]).to have_attributes(size: 1..5, first: Symbol)
# ```
macro have_attributes(**expected)
::Spectator::Matchers::AttributesMatcher.new({{expected}}, {{expected.double_splat.stringify}})
::Spectator::Matchers::AttributesMatcher.new(::Spectator::TestValue.new({{expected}}, {{expected.double_splat.stringify}}))
end
# Indicates that some block should raise an error.
@ -620,7 +622,7 @@ module Spectator::DSL
{% end %}
label << ')'
{% end %}
::Spectator::Matchers::{{matcher.id}}.new(descriptor, label.to_s)
::Spectator::Matchers::{{matcher.id}}.new(::Spectator::TestValue.new(descriptor, label.to_s))
end
end
end

View file

@ -9,7 +9,7 @@ module Spectator
# Creates the exception.
# The exception string is generated from the expecation message.
def initialize(@expectation)
super(@expectation.actual_message)
super(@expectation.failure_message)
end
end
end

View file

@ -1,25 +0,0 @@
require "./expectation_partial"
module Spectator::Expectations
# Expectation partial variation that operates on a block.
struct BlockExpectationPartial(ReturnType) < ExpectationPartial
# Actual value produced by calling the block.
def actual
@block.call
end
# Creates the expectation partial.
# The label should be a string representation of the block.
# The block is stored for later use.
def initialize(@block : Proc(ReturnType), label, source_file, source_line)
super(label, source_file, source_line)
end
# Creates the expectation partial.
# The label is generated by calling to_s on the block.
# The block is stored for later use.
def initialize(@block : Proc(ReturnType), source_file, source_line)
super("<Proc>", source_file, source_line)
end
end
end

View file

@ -1,57 +1,67 @@
require "../matchers/failed_match_data"
require "../matchers/match_data"
require "../source"
module Spectator::Expectations
# Ties together a partial, matcher, and their outcome.
class Expectation
# Populates the base portiion of the expectation with values.
# The *negated* flag should be true if the expectation is inverted.
# The *match_data* is the value returned by `Spectator::Matcher#match`
# when the expectation was evaluated.
# The *negated* flag and `MatchData#matched?` flag
# are mutually-exclusive in this context.
def initialize(@match_data : Spectator::Matchers::MatchData, @negated : Bool)
# 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 expectation was satisifed.
# This is true if:
# - The matcher was satisified and the expectation is not negated.
# - The matcher wasn't satisfied and the expectation is negated.
# Indicates whether the matcher was satisified.
def satisfied?
@match_data.matched? ^ @negated
@match_data.matched?
end
# Information about the match.
# Returned value and type will something that has key-value pairs (like a `NamedTuple`).
# 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
@match_data.values.tap do |labeled_values|
if @negated
labeled_values.each do |labeled_value|
value = labeled_value.value
value.negate
end
end
end
end
# Text that indicates the condition that must be met for the expectation to be satisifed.
def expected_message
@negated ? @match_data.negated_message : @match_data.message
end
# Text that indicates what the outcome was.
def actual_message
@match_data.matched? ? @match_data.message : @match_data.negated_message
values?.not_nil!
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?)
json.field("expected", expected_message)
json.field("actual", actual_message)
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
values.each do |labeled_value|
json.field(labeled_value.label, labeled_value.value.to_s)
end
failed.values.each do |pair|
json.field(pair.first, pair.last)
end
end
end

View file

@ -1,39 +1,34 @@
require "../matchers/match_data"
require "../source"
require "../test_expression"
module Spectator::Expectations
# Base class for all expectation partials.
# An "expectation partial" stores part of an expectation (obviously).
# The part of the expectation this class covers is the actual value.
# 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.
# Sub-types of this class are returned by the `DSL::ExampleDSL.expect` call.
abstract struct ExpectationPartial
# User-friendly string displayed for the actual expression being tested.
# For instance, in the expectation:
# ```
# expect(foo).to eq(bar)
# ```
# This property will be "foo".
# It will be the literal string "foo",
# and not the actual value of the foo.
getter label : String
struct ExpectationPartial(T)
# The actual value being tested.
# This also contains its label.
getter actual : TestExpression(T)
# Source file the expectation originated from.
getter source_file : String
# Location where this expectation was defined.
getter source : Source
# Line number in the source file the expectation originated from.
getter source_line : Int32
# Creates the base of the partial.
private def initialize(@label, @source_file, @source_line)
# 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
report(eval(matcher))
match_data = matcher.match(@actual)
report(match_data)
end
# Asserts that some criteria defined by the matcher is not satisfied.
# This is effectively the opposite of `#to`.
def to_not(matcher) : Nil
report(eval(matcher, true))
match_data = matcher.negated_match(@actual)
report(match_data)
end
# ditto
@ -42,14 +37,9 @@ module Spectator::Expectations
to_not(matcher)
end
# Evaluates the expectation and returns it.
private def eval(matcher, negated = false)
match_data = matcher.match(self)
Expectation.new(match_data, negated)
end
# Reports an expectation to the current harness.
private def report(expectation : Expectation)
private def report(match_data : Matchers::MatchData)
expectation = Expectation.new(match_data, @source)
Internals::Harness.current.report_expectation(expectation)
end
end

View file

@ -1,24 +0,0 @@
require "./expectation_partial"
module Spectator::Expectations
# Expectation partial variation that operates on a value.
struct ValueExpectationPartial(ActualType) < ExpectationPartial
# Actual value produced by the test.
# This is the value passed to the `Spectator::DSL::ExampleDSL#expect` macro.
getter actual
# Creates the expectation partial.
# The label should be a string representation of the actual value.
# The actual value is stored for later use.
def initialize(@actual : ActualType, label, source_file, source_line)
super(label, source_file, source_line)
end
# Creates the expectation partial.
# The label is generated by calling `to_s` on the actual value.
# The actual value is stored for later use.
def initialize(@actual : ActualType, source_file, source_line)
super(@actual.to_s, source_file, source_line)
end
end
end

View file

@ -53,7 +53,7 @@ module Spectator::Formatting
# Produces a list of unsatisfied expectations and their values.
private def unsatisfied_expectations(indent)
@result.expectations.each_unsatisfied do |expectation|
indent.line(Color.failure(LabeledText.new("Failure", expectation.actual_message)))
indent.line(Color.failure(LabeledText.new("Failure", expectation.failure_message)))
indent.line
indent.increase do
matcher_values(indent, expectation)

View file

@ -19,7 +19,7 @@ module Spectator::Formatting
private def content(xml)
super
@result.expectations.each_unsatisfied do |expectation|
xml.element("failure", message: expectation.actual_message) do
xml.element("failure", message: expectation.failure_message) do
expectation_values(expectation.values, xml)
end
end
@ -28,8 +28,8 @@ module Spectator::Formatting
# Adds the expectation values to the failure block.
private def expectation_values(labeled_values, xml)
labeled_values.each do |entry|
label = entry.label
value = entry.value
label = entry.first
value = entry.last
xml.text("#{label}: #{value}\n")
end
end

View file

@ -1,8 +1,8 @@
module Spectator::Formatting
# A single labeled value from the `Spectator::Matchers::MatchData#value` method.
private struct MatchDataValuePair(T)
private struct MatchDataValuePair
# Creates the pair formatter.
def initialize(@key : Symbol, @value : T, @padding : Int32)
def initialize(@key : Symbol, @value : String, @padding : Int32)
end
# Appends the pair to the output.
@ -10,7 +10,7 @@ module Spectator::Formatting
@padding.times { io << ' ' }
io << @key
io << ": "
@value.inspect(io)
io << @value
end
end
end

View file

@ -2,22 +2,22 @@ module Spectator::Formatting
# Produces a `MatchDataValuePair` for each key-value pair
# from `Spectator::Matchers::MatchData#values`.
private struct MatchDataValues
include Enumerable(MatchDataValuePair)
include Enumerable(Tuple(Symbol, String))
@max_key_length : Int32
# Creates the values mapper.
def initialize(@values : Array(Spectator::Matchers::MatchDataLabeledValue))
@max_key_length = @values.map(&.label.to_s.size).max
def initialize(@values : Array(Tuple(Symbol, String)))
@max_key_length = @values.map(&.first.to_s.size).max
end
# Yields pairs that can be printed to output.
def each
@values.each do |labeled_value|
key = labeled_value.label
key = labeled_value.first
key_length = key.to_s.size
padding = @max_key_length - key_length
yield MatchDataValuePair.new(key, labeled_value.value, padding)
yield MatchDataValuePair.new(key, labeled_value.last, padding)
end
end
end

View file

@ -1,32 +0,0 @@
require "./match_data_value"
module Spectator::Matchers
# Selects a value based on whether the value is negated.
# This is used when a matcher is negated.
private class AlternativeMatchDataValue(T1, T2) < MatchDataValue
# Creates the wrapper.
def initialize(@value1 : T1, @value2 : T2)
@negated = false
end
# Negates (toggles) the value.
def negate
@negated = !@negated
end
# Returns the correct value based on the negated status.
def value
@negated ? @value2 : @value1
end
# Produces a stringified value.
def to_s(io)
io << value
end
# Produces a stringified value with additional information.
def inspect(io)
io << value
end
end
end

View file

@ -1,135 +1,113 @@
require "./value_matcher"
require "./failed_match_data"
require "./matcher"
require "./successful_match_data"
require "./unordered_array_matcher"
module Spectator::Matchers
# Matcher for checking that the contents of one array (or similar type)
# has the exact same contents as another and in the same order.
struct ArrayMatcher(ExpectedType) < ValueMatcher(Enumerable(ExpectedType))
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
actual = partial.actual.to_a
expected_elements = expected.to_a
values = ExpectedActual.new(expected_elements, label, actual, partial.label)
if values.expected.size == values.actual.size
index = 0
values.expected.zip(values.actual) do |expected, element|
return ContentMatchData.new(index, values) unless expected == element
index += 1
struct ArrayMatcher(ExpectedType) < Matcher
# Expected value and label.
private getter expected
# Creates the matcher with an expected value.
def initialize(@expected : TestValue(ExpectedType))
end
IdenticalMatchData.new(values)
else
SizeMatchData.new(values)
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"contains exactly #{expected.label}"
end
# Actually performs the test against the expression.
def match(actual : TestExpression(T)) : MatchData forall T
actual_elements = actual.value.to_a
expected_elements = expected.value.to_a
index = compare_arrays(expected_elements, actual_elements)
case index
when Int # Content differs.
failed_content_mismatch(expected_elements, actual_elements, index, actual.label)
when true # Contents are identical.
SuccessfulMatchData.new
else # Size differs.
failed_size_mismatch(expected_elements, actual_elements, actual.label)
end
end
# Creates the value matcher.
# The label should be a string representation of the expectation.
# The expected value is stored for later use.
def initialize(expected : Enumerable(ExpectedType), label : String)
super
# Performs the test against the expression, but inverted.
# A successful match with `#match` should normally fail for this method, and vice-versa.
def negated_match(actual : TestExpression(T)) : MatchData forall T
actual_elements = actual.value.to_a
expected_elements = expected.value.to_a
case compare_arrays(expected_elements, actual_elements)
when Int # Contents differ.
SuccessfulMatchData.new
when true # Contents are identical.
failed_content_identical(expected_elements, actual_elements, actual.label)
else # Size differs.
SuccessfulMatchData.new
end
end
# Creates the value matcher.
# The label is generated by calling `#to_s` on the expected value.
# The expected value is stored for later use.
def initialize(expected : Enumerable(ExpectedType))
super
end
# Returns a matcher that uses the same expected array, but allows unordered items.
def in_any_order
UnorderedArrayMatcher.new(@expected, @label)
end
# Returns self.
# Exists for syntax to ensure in-order matching is performed.
# Ensures the arrays elements are compared in order.
# This is the default behavior for the matcher.
def in_order
self
end
# Common functionality for all match data for this matcher.
private abstract struct CommonMatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(Array(ExpectedType), Array(ActualType)))
super(matched)
# Specifies that the arrays elements can be compared in any order.
# The elements can be in a different order, but both arrays must have the same elements.
def in_any_order
UnorderedArrayMatcher.new(expected)
end
# Basic information about the match.
def named_tuple
{
expected: NegatableMatchDataValue.new(@values.expected),
actual: @values.actual,
}
# Compares two arrays to determine whether they contain the same elements, and in the same order.
# If the arrays are the same, then `true` is returned.
# If they are different, `false` or an integer is returned.
# `false` is returned when the sizes of the arrays don't match.
# An integer is returned, that is the index of the mismatched elements in the arrays.
private def compare_arrays(expected_elements, actual_elements)
if expected_elements.size == actual_elements.size
index = 0
expected_elements.zip(actual_elements) do |expected_element, actual_element|
return index unless expected_element == actual_element
index += 1
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} contains exactly #{@values.expected_label}"
true
else
false
end
end
# Match data specific to this matcher.
# This type is used when the actual value matches the expected value.
private struct IdenticalMatchData(ExpectedType, ActualType) < CommonMatchData(ExpectedType, ActualType)
# Creates the match data.
def initialize(values : ExpectedActual(Array(ExpectedType), Array(ActualType)))
super(true, values)
# Produces match data for a failure when the array sizes differ.
private def failed_size_mismatch(expected_elements, actual_elements, actual_label)
FailedMatchData.new("#{actual_label} does not contain exactly #{expected.label} (size mismatch)",
expected: expected_elements.inspect,
actual: actual_elements.inspect,
"expected size": expected_elements.size.to_s,
"actual size": actual_elements.size.to_s
)
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not contain exactly #{@values.expected_label}"
end
# Produces match data for a failure when the array content is mismatched.
private def failed_content_mismatch(expected_elements, actual_elements, index, actual_label)
FailedMatchData.new("#{actual_label} does not contain exactly #{expected.label} (element mismatch)",
expected: expected_elements[index].inspect,
actual: actual_elements[index].inspect,
index: index.to_s
)
end
# Match data specific to this matcher.
# This type is used when the actual size differs from the expected size.
private struct SizeMatchData(ExpectedType, ActualType) < CommonMatchData(ExpectedType, ActualType)
# Creates the match data.
def initialize(values : ExpectedActual(Array(ExpectedType), Array(ActualType)))
super(false, values)
end
# Information about the match.
def named_tuple
super.merge({
"expected size": NegatableMatchDataValue.new(@values.expected.size),
"actual size": @values.actual.size,
})
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not contain exactly #{@values.expected_label} (size differs)"
end
end
# Match data specific to this matcher.
# This type is used when the actual contents differs from the expected contents.
private struct ContentMatchData(ExpectedType, ActualType) < CommonMatchData(ExpectedType, ActualType)
# Creates the match data.
def initialize(@index : Int32, values : ExpectedActual(Array(ExpectedType), Array(ActualType)))
super(false, values)
end
# Information about the match.
def named_tuple
super.merge({
index: @index,
"expected element": NegatableMatchDataValue.new(@values.expected[@index]),
"actual element": @values.actual[@index],
})
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not contain exactly #{@values.expected_label} (content differs)"
end
# Produces match data for a failure when the arrays are identical, but they shouldn't be (negation).
private def failed_content_identical(expected_elements, actual_elements, actual_label)
FailedMatchData.new("#{actual_label} contains exactly #{expected.label}",
expected: "Not #{expected_elements.inspect}",
actual: actual_elements.inspect
)
end
end
end

View file

@ -1,72 +1,83 @@
require "./value_matcher"
require "../test_value"
require "./failed_match_data"
require "./matcher"
require "./successful_match_data"
module Spectator::Matchers
# Matcher that tests that multiple attributes match specified conditions.
# The attributes are tested with the === operator.
# The `ExpectedType` type param should be a `NamedTuple`.
struct AttributesMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(actual)
# Test each attribute and immediately return false if a comparison fails.
{% for attribute in ExpectedType.keys %}
return false unless expected[{{attribute.symbolize}}] === actual[{{attribute.symbolize}}]
{% end %}
# Each key in the tuple is the attribute/method name,
# and the corresponding value is the expected value to match against.
struct AttributesMatcher(ExpectedType) < Matcher
# Expected value and label.
private getter expected
# All checks passed if this point is reached.
true
# Creates the matcher with an expected value.
def initialize(@expected : TestValue(ExpectedType))
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
actual_values = snapshot_values(partial.actual)
values = ExpectedActual.new(expected, label, actual_values, partial.label)
MatchData.new(match?(actual_values), values)
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"has attributes #{expected.label}"
end
# Actually performs the test against the expression.
def match(actual : TestExpression(T)) : MatchData forall T
snapshot = snapshot_values(actual.value)
if match?(snapshot)
SuccessfulMatchData.new
else
FailedMatchData.new("#{actual.label} does not have attributes #{expected.label}", **values(snapshot))
end
end
# Performs the test against the expression, but inverted.
# A successful match with `#match` should normally fail for this method, and vice-versa.
def negated_match(actual : TestExpression(T)) : MatchData forall T
snapshot = snapshot_values(actual.value)
if match?(snapshot)
FailedMatchData.new("#{actual.label} has attributes #{expected.label}", **values(snapshot))
else
SuccessfulMatchData.new
end
end
# Captures all of the actual values.
# A `NamedTuple` is returned,
# with each key being the attribute.
private def snapshot_values(actual)
# A `NamedTuple` is returned, with each key being the attribute.
private def snapshot_values(object)
{% begin %}
{
{% for attribute in ExpectedType.keys %}
{{attribute}}: actual.{{attribute}},
{{attribute}}: object.{{attribute}},
{% end %}
}
{% end %}
end
# Match data specific to this matcher.
private struct MatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
# Checks if all attributes from the snapshot of them are satisified.
private def match?(snapshot)
# Test that every attribute has the expected value.
{% for attribute in ExpectedType.keys %}
return false unless expected.value[{{attribute.symbolize}}] === snapshot[{{attribute.symbolize}}]
{% end %}
# At this point, none of the checks failed, so the match was successful.
true
end
# Information about the match.
def named_tuple
# Produces the tuple for the failed match data from a snapshot of the attributes.
private def values(snapshot)
{% begin %}
{
{% for attribute in ExpectedType.keys %}
{{"expected " + attribute.stringify}}: NegatableMatchDataValue.new(@values.expected[{{attribute.symbolize}}]),
{{"actual " + attribute.stringify}}: @values.actual[{{attribute.symbolize}}],
{{"expected " + attribute.stringify}}: expected.value[{{attribute.symbolize}}].inspect,
{{"actual " + attribute.stringify}}: snapshot[{{attribute.symbolize}}].inspect,
{% end %}
}
{% end %}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} has attributes #{@values.expected_label}"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not have attributes #{@values.expected_label}"
end
end
end
end

View file

@ -4,44 +4,37 @@ module Spectator::Matchers
# Common matcher that tests whether two values semantically equal each other.
# The values are compared with the === operator.
struct CaseMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(actual)
expected === actual
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"matches #{expected.label}"
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
values = ExpectedActual.new(partial, self)
MatchData.new(match?(values.actual), values)
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
expected.value === actual.value
end
# Match data specific to this matcher.
private struct MatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} does not match #{expected.label}"
end
# Information about the match.
def named_tuple
{
expected: NegatableMatchDataValue.new(@values.expected),
actual: @values.actual,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} matches #{@values.expected_label}"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not match #{@values.expected_label}"
end
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} matched #{expected.label}"
end
end
end

View file

@ -1,19 +1,41 @@
require "../test_value"
require "./range_matcher"
require "./value_matcher"
module Spectator::Matchers
# Matcher for checking that a value is in a collection of other values.
struct CollectionMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(actual)
expected.includes?(actual)
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"is in #{expected.label}"
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
actual = partial.actual
matched = match?(actual)
MatchData.new(matched, ExpectedActual.new(partial, self))
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
expected.value.includes?(actual.value)
end
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} is not in #{expected.label}"
end
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} is in #{expected.label}"
end
# Creates a new range matcher with bounds based off of *center*.
@ -21,7 +43,7 @@ module Spectator::Matchers
# This method expects that the original matcher was created with a "difference" value.
# That is:
# ```
# RangeMatcher.new(diff).of(center)
# CollectionMatcher.new(diff).of(center)
# ```
# This implies that the `#match` method would not work on the original matcher.
#
@ -29,39 +51,12 @@ module Spectator::Matchers
# and have upper and lower bounds equal to *center* plus and minus diff.
# The range will be inclusive.
def of(center)
diff = @expected
diff = @expected.value
lower = center - diff
upper = center + diff
range = Range.new(lower, upper)
RangeMatcher.new(range, "#{center} +/- #{label}")
end
# Match data specific to this matcher.
private struct MatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
end
# Information about the match.
def named_tuple
{
collection: NegatableMatchDataValue.new(@values.expected),
actual: @values.actual,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} is in #{@values.expected_label}"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} is not in #{@values.expected_label}"
end
test_value = TestValue.new(range, "#{center} +/- #{expected.label}")
RangeMatcher.new(test_value)
end
end
end

View file

@ -4,46 +4,48 @@ module Spectator::Matchers
# Matcher that tests whether a value, such as a `String` or `Array`, contains one or more values.
# The values are checked with the `includes?` method.
struct ContainMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(actual)
expected.all? do |item|
actual.includes?(item)
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"contains #{expected.label}"
end
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
expected.value.all? do |item|
actual.value.includes?(item)
end
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
values = ExpectedActual.new(partial, self)
MatchData.new(match?(values.actual), values)
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} does not match #{expected.label}"
end
# Match data specific to this matcher.
private struct MatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} contains #{expected.label}"
end
# Information about the match.
def named_tuple
# Additional information about the match failure.
# The return value is a NamedTuple with Strings for each value.
private def values(actual)
{
subset: NegatableMatchDataValue.new(@values.expected),
superset: @values.actual,
subset: expected.value.inspect,
superset: actual.value.inspect,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} contains #{@values.expected_label}"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not contain #{@values.expected_label}"
end
end
end
end

View file

@ -1,32 +0,0 @@
module Spectator::Matchers
# Match data that does nothing.
# This is to workaround a Crystal compiler bug.
# See: [Issue 4225](https://github.com/crystal-lang/crystal/issues/4225)
# If there are no concrete implementations of an abstract class,
# the compiler gives an error.
# The error indicates an abstract method is undefined.
# This class shouldn't be used, it's just to trick the compiler.
private struct DummyMatchData < MatchData
# Creates the match data.
def initialize
super(false)
end
# Dummy values.
def named_tuple
{
you: "shouldn't be calling this.",
}
end
# Dummy message.
def message
"You shouldn't be calling this."
end
# Dummy message
def negated_message
"You shouldn't be calling this."
end
end
end

View file

@ -3,46 +3,38 @@ require "./matcher"
module Spectator::Matchers
# Matcher that tests whether a collection is empty.
# The values are checked with the `empty?` method.
struct EmptyMatcher < Matcher
# Textual representation of what the matcher expects.
def label
"empty?"
struct EmptyMatcher < StandardMatcher
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"is empty"
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
actual = partial.actual
matched = actual.empty?
MatchData.new(matched, actual, partial.label)
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
actual.value.empty?
end
# Match data specific to this matcher.
private struct MatchData(T) < MatchData
# Creates the match data.
def initialize(matched, @actual : T, @actual_label : String)
super(matched)
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} is not empty"
end
# Information about the match.
def named_tuple
{
expected: NegatableMatchDataValue.new([] of Nil),
actual: @actual,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@actual_label} is empty"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@actual_label} is not empty"
end
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} is empty"
end
end
end

View file

@ -1,89 +1,102 @@
require "./value_matcher"
require "./failed_match_data"
require "./matcher"
require "./successful_match_data"
module Spectator::Matchers
# Matcher that tests whether a value, such as a `String` or `Array`, ends with a value.
# The `ends_with?` method is used if it's defined on the actual type.
# Otherwise, it is treated as an `Indexable` and the `last` value is compared against.
struct EndWithMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match_ends_with?(actual)
actual.ends_with?(expected)
struct EndWithMatcher(ExpectedType) < Matcher
# Expected value and label.
private getter expected
# Creates the matcher with an expected value.
def initialize(@expected : TestValue(ExpectedType))
end
# Determines whether the matcher is satisfied with the value given to it.
private def match_last?(actual)
expected === actual
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"ends with #{expected.label}"
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
values = ExpectedActual.new(partial, self)
actual = values.actual
if actual.responds_to?(:ends_with?)
EndsWithMatchData.new(match_ends_with?(actual), values)
# Actually performs the test against the expression.
def match(actual : TestExpression(T)) : MatchData forall T
if (value = actual.value).responds_to?(:ends_with?)
match_ends_with(value, actual.label)
else
last = actual.last
LastMatchData.new(match_last?(last), values, last)
match_last(value, actual.label)
end
end
# Match data specific to this matcher.
# This type is used when the actual value responds to `ends_with?`.
private struct EndsWithMatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
end
# Information about the match.
def named_tuple
{
expected: NegatableMatchDataValue.new(@values.expected),
actual: @values.actual,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} ends with #{@values.expected_label} (using #ends_with?)"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not end with #{@values.expected_label} (using #ends_with?)"
# Performs the test against the expression, but inverted.
# A successful match with `#match` should normally fail for this method, and vice-versa.
def negated_match(actual : TestExpression(T)) : MatchData forall T
if actual.value.responds_to?(:ends_with?)
negated_match_ends_with(actual)
else
negated_match_last(actual)
end
end
# Match data specific to this matcher.
# This type is used when the actual value does not respond to `ends_with?`.
private struct LastMatchData(ExpectedType, ActualType, LastType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType), @last : LastType)
super(matched)
# Checks whether the actual value ends with the expected value.
# This method expects (and uses) the `#ends_with?` method on the value.
private def match_ends_with(actual_value, actual_label)
if actual_value.ends_with?(expected.value)
SuccessfulMatchData.new
else
FailedMatchData.new("#{actual_label} does not end with #{expected.label} (using #ends_with?)",
expected: expected.value.inspect,
actual: actual_value.inspect
)
end
end
# Information about the match.
def named_tuple
{
expected: @values.expected,
actual: @last,
list: @values.actual,
}
# Checks whether the last element of the value is the expected value.
# This method expects that the actual value is a set (enumerable).
private def match_last(actual_value, actual_label)
list = actual_value.to_a
last = list.last
if expected.value === last
SuccessfulMatchData.new
else
FailedMatchData.new("#{actual_label} does not end with #{expected.label} (using expected === last)",
expected: expected.value.inspect,
actual: last.inspect,
list: list.inspect
)
end
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} ends with #{@values.expected_label} (using expected === actual.last)"
# Checks whether the actual value does not end with the expected value.
# This method expects (and uses) the `#ends_with?` method on the value.
private def negated_match_ends_with(actual)
if actual.value.ends_with?(expected.value)
FailedMatchData.new("#{actual.label} ends with #{expected.label} (using #ends_with?)",
expected: expected.value.inspect,
actual: actual.value.inspect
)
else
SuccessfulMatchData.new
end
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not end with #{@values.expected_label} (using expected === actual.last)"
# Checks whether the last element of the value is not the expected value.
# This method expects that the actual value is a set (enumerable).
private def negated_match_last(actual)
list = actual.value.to_a
last = list.last
if expected.value === last
FailedMatchData.new("#{actual.label} ends with #{expected.label} (using expected === last)",
expected: expected.value.inspect,
actual: last.inspect,
list: list.inspect
)
else
SuccessfulMatchData.new
end
end
end

View file

@ -4,44 +4,37 @@ module Spectator::Matchers
# Common matcher that tests whether two values equal each other.
# The values are compared with the == operator.
struct EqualityMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(actual)
actual == expected
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"equals #{expected.label}"
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
values = ExpectedActual.new(partial, self)
MatchData.new(match?(values.actual), values)
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
expected.value == actual.value
end
# Match data specific to this matcher.
private struct MatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} does not equal #{expected.label}"
end
# Information about the match.
def named_tuple
{
expected: NegatableMatchDataValue.new(@values.expected),
actual: @values.actual,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} is #{@values.expected_label} (using ==)"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} is not #{@values.expected_label} (using ==)"
end
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} equals #{expected.label}"
end
end
end

View file

@ -1,11 +1,93 @@
require "./value_matcher"
require "../test_value"
require "./failed_match_data"
require "./matcher"
require "./successful_match_data"
module Spectator::Matchers
# Matcher that tests whether an exception is raised.
struct ExceptionMatcher(ExceptionType, ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(exception)
exception.is_a?(ExceptionType) && (expected.nil? || expected === exception.message)
struct ExceptionMatcher(ExceptionType, ExpectedType) < Matcher
# Expected value and label.
private getter expected
# Creates the matcher with no expectation of the message.
def initialize
@expected = TestValue.new(nil, ExceptionType.to_s)
end
# Creates the matcher with an expected message.
def initialize(@expected : TestValue(ExpectedType))
end
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
if (message = @expected)
"raises #{ExceptionType} with message #{message}"
else
"raises #{ExceptionType}"
end
end
# Actually performs the test against the expression.
def match(actual : TestExpression(T)) : MatchData forall T
exception = capture_exception { actual.value }
if exception.nil?
FailedMatchData.new("#{actual.label} did not raise", expected: ExceptionType.inspect)
else
if exception.is_a?(ExceptionType)
if (value = expected.value).nil?
SuccessfulMatchData.new
else
if value === exception.message
SuccessfulMatchData.new
else
FailedMatchData.new("#{actual.label} raised #{exception.class}, but the message is not #{expected.label}",
"expected type": ExceptionType.inspect,
"actual type": exception.class.inspect,
"expected message": value.inspect,
"actual message": exception.message.to_s
)
end
end
else
FailedMatchData.new("#{actual.label} did not raise #{ExceptionType}",
expected: ExceptionType.inspect,
actual: exception.class.inspect
)
end
end
end
# Performs the test against the expression, but inverted.
# A successful match with `#match` should normally fail for this method, and vice-versa.
def negated_match(actual : TestExpression(T)) : MatchData forall T
exception = capture_exception { actual.value }
if exception.nil?
SuccessfulMatchData.new
else
if exception.is_a?(ExceptionType)
if (value = expected.value).nil?
FailedMatchData.new("#{actual.label} raised #{exception.class}",
expected: "Not #{ExceptionType}",
actual: exception.class.inspect
)
else
if value === exception.message
FailedMatchData.new("#{actual.label} raised #{exception.class} with message matching #{expected.label}",
"expected type": ExceptionType.inspect,
"actual type": exception.class.inspect,
"expected message": value.inspect,
"actual message": exception.message.to_s
)
else
SuccessfulMatchData.new
end
end
else
SuccessfulMatchData.new
end
end
end
# Runs a block of code and returns the exception it threw.
@ -20,104 +102,21 @@ module Spectator::Matchers
exception
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
exception = capture_exception { partial.actual }
matched = match?(exception)
if exception.nil?
MatchData.new(ExceptionType, matched, ExpectedActual.new(expected, label, exception, partial.label))
else
values = ExpectedActual.new(expected, label, exception, partial.label)
if expected.nil?
MatchData.new(ExceptionType, matched, values)
else
MessageMatchData.new(ExceptionType, matched, values)
end
end
end
# Creates a new exception matcher with no message check.
def initialize
super(nil, ExceptionType.to_s)
end
# Creates a new exception matcher with a message check.
def initialize(expected : ExpectedType, label : String)
super(expected, label)
end
# Creates a new exception matcher with no message check.
def self.create(exception_type : T.class, label : String) forall T
ExceptionMatcher(T, Nil).new
end
# Creates a new exception matcher with a message check.
def self.create(expected, label : String)
ExceptionMatcher(Exception, typeof(expected)).new(expected, label)
def self.create(value, label : String)
expected = TestValue.new(value, label)
ExceptionMatcher(Exception, typeof(value)).new(expected)
end
# Creates a new exception matcher with a type and message check.
def self.create(exception_type : T.class, expected, label : String) forall T
ExceptionMatcher(T, typeof(expected)).new(expected, label)
end
# Match data specific to this matcher.
private struct MatchData(ExceptionType, ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(t : ExceptionType.class, matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
end
# Information about the match.
def named_tuple
{
"expected type": NegatableMatchDataValue.new(ExceptionType),
"actual type": @values.actual.class,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} raises #{ExceptionType}"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not raise #{ExceptionType}"
end
end
# Match data specific to this matcher with an expected message.
private struct MessageMatchData(ExceptionType, ExpectedType) < ::Spectator::Matchers::MatchData
# Creates the match data.
def initialize(t : ExceptionType.class, matched, @values : ExpectedActual(ExpectedType, Exception))
super(matched)
end
# Information about the match.
def named_tuple
{
"expected type": NegatableMatchDataValue.new(ExceptionType),
"actual type": @values.actual.class,
"expected message": NegatableMatchDataValue.new(@values.expected),
"actual message": @values.actual.message,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} raises #{ExceptionType} with message #{@values.expected_label}"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not raise #{ExceptionType} with message #{@values.expected_label}"
end
def self.create(exception_type : T.class, value, label : String) forall T
expected = TestValue.new(value, label)
ExceptionMatcher(T, typeof(value)).new(expected)
end
end
end

View file

@ -1,30 +0,0 @@
module Spectator::Matchers
# Stores values and labels for expected and actual values.
private struct ExpectedActual(ExpectedType, ActualType)
# The expected value.
getter expected : ExpectedType
# The user label for the expected value.
getter expected_label : String
# The actual value.
getter actual : ActualType
# The user label for the actual value.
getter actual_label : String
# Creates the value and label store.
def initialize(@expected : ExpectedType, @expected_label, @actual : ActualType, @actual_label)
end
# Creates the value and label store.
# Attributes are pulled from an expectation partial and matcher.
def initialize(
partial : Spectator::Expectations::ValueExpectationPartial(ActualType) |
Spectator::Expectations::BlockExpectationPartial(ActualType),
matcher : ValueMatcher(ExpectedType)
)
initialize(matcher.expected, matcher.label, partial.actual, partial.label)
end
end
end

View file

@ -0,0 +1,22 @@
require "./match_data"
module Spectator::Matchers
# Information about a failed match.
struct FailedMatchData < MatchData
# Indicates that the match failed.
def matched?
false
end
# Description from the matcher as to why it failed.
getter failure_message : String
# Additional information from the match that can be used to debug the problem.
getter values : Array(Tuple(Symbol, String))
# Creates the match data.
def initialize(@failure_message, **values)
@values = values.to_a
end
end
end

View file

@ -1,23 +0,0 @@
require "./match_data_value"
module Spectator::Matchers
# Wraps a value for used in match data.
private class GenericMatchDataValue(T) < MatchDataValue
# Underlying value.
getter value
# Creates the wrapper.
def initialize(@value : T)
end
# Stringifies the value.
def to_s(io)
@value.inspect(io)
end
# Inspects the value.
def inspect(io)
@value.inspect(io)
end
end
end

View file

@ -4,44 +4,55 @@ module Spectator::Matchers
# Matcher that tests whether one value is greater than or equal to another.
# The values are compared with the >= operator.
struct GreaterThanEqualMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(actual)
actual >= expected
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"greater than or equal to #{expected.label}"
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
values = ExpectedActual.new(partial, self)
MatchData.new(match?(values.actual), values)
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
actual.value >= expected.value
end
# Match data specific to this matcher.
private struct MatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} is less than #{expected.label}"
end
# Information about the match.
def named_tuple
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} is greater than or equal to #{expected.label}"
end
# Additional information about the match failure.
# The return value is a NamedTuple with Strings for each value.
private def values(actual)
{
expected: NegatablePrefixedMatchDataValue.new(">=", "<", @values.expected),
actual: @values.actual,
expected: ">= #{expected.value.inspect}",
actual: actual.value.inspect,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} is greater than or equal to #{@values.expected_label} (using >=)"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} is less than #{@values.expected_label} (using >=)"
end
# Additional information about the match failure when negated.
# The return value is a NamedTuple with Strings for each value.
private def negated_values(actual)
{
expected: "< #{expected.value.inspect}",
actual: actual.value.inspect,
}
end
end
end

View file

@ -4,44 +4,55 @@ module Spectator::Matchers
# Matcher that tests whether one value is greater than another.
# The values are compared with the > operator.
struct GreaterThanMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(actual)
actual > expected
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"greater than #{expected.label}"
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
values = ExpectedActual.new(partial, self)
MatchData.new(match?(values.actual), values)
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
actual.value > expected.value
end
# Match data specific to this matcher.
private struct MatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} is less than or equal to #{expected.label}"
end
# Information about the match.
def named_tuple
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} is greater than #{expected.label}"
end
# Additional information about the match failure.
# The return value is a NamedTuple with Strings for each value.
private def values(actual)
{
expected: NegatablePrefixedMatchDataValue.new(">", "<=", @values.expected),
actual: @values.actual,
expected: "> #{expected.value.inspect}",
actual: actual.value.inspect,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} is greater than #{@values.expected_label} (using >)"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} is less than or equal to #{@values.expected_label} (using >)"
end
# Additional information about the match failure when negated.
# The return value is a NamedTuple with Strings for each value.
private def negated_values(actual)
{
expected: "<= #{expected.value.inspect}",
actual: actual.value.inspect,
}
end
end
end

View file

@ -4,45 +4,48 @@ module Spectator::Matchers
# Matcher that tests whether a `Hash` (or similar type) has a given key.
# The set is checked with the `has_key?` method.
struct HaveKeyMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(actual)
actual.has_key?(expected)
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"has key #{expected.label}"
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
values = ExpectedActual.new(partial, self)
MatchData.new(match?(values.actual), values)
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
actual.value.has_key?(expected.value)
end
# Match data specific to this matcher.
private struct MatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} does not have key #{expected.label}"
end
# Information about the match.
def named_tuple
actual = @values.actual
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} has key #{expected.label}"
end
# Additional information about the match failure.
# The return value is a NamedTuple with Strings for each value.
private def values(actual)
actual_value = actual.value
set = actual_value.responds_to?(:keys) ? actual_value.keys : actual_value
{
key: NegatableMatchDataValue.new(@values.expected),
actual: actual.responds_to?(:keys) ? actual.keys : actual,
key: expected.value.inspect,
actual: set.inspect,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} has key #{@values.expected_label}"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not have key #{@values.expected_label}"
end
end
end
end

View file

@ -5,68 +5,69 @@ module Spectator::Matchers
# For a `String`, the `includes?` method is used.
# Otherwise, it expects an `Enumerable` and iterates over each item until === is true.
struct HaveMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
# True is returned if the match was successful, false otherwise.
private def match?(actual)
if actual.is_a?(String)
match_string?(actual)
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"includes #{expected.label}"
end
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
if (value = actual.value).is_a?(String)
match_string?(value)
else
match_enumerable?(actual)
match_enumerable?(value)
end
end
# Checks if a `String` matches the expected values.
# The `includes?` method is used for this check.
private def match_string?(actual)
expected.all? do |item|
actual.includes?(item)
private def match_string?(value)
expected.value.all? do |item|
value.includes?(item)
end
end
# Checks if an `Enumerable` matches the expected values.
# The `===` operator is used on every item.
private def match_enumerable?(actual)
array = actual.to_a
expected.all? do |item|
array.any? do |elem|
item === elem
private def match_enumerable?(value)
array = value.to_a
expected.value.all? do |item|
array.any? do |element|
item === element
end
end
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
values = ExpectedActual.new(partial, self)
MatchData.new(match?(values.actual), values)
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} does not include #{expected.label}"
end
# Match data specific to this matcher.
private struct MatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} includes #{expected.label}"
end
# Information about the match.
def named_tuple
# Additional information about the match failure.
# The return value is a NamedTuple with Strings for each value.
private def values(actual)
{
subset: NegatableMatchDataValue.new(@values.expected),
superset: @values.actual,
subset: expected.value.inspect,
superset: actual.value.inspect,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} includes #{@values.expected_label}"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not include #{@values.expected_label}"
end
end
end
end

View file

@ -7,58 +7,94 @@ module Spectator::Matchers
# Each key in the tuple is a predicate (without the '?' and 'has_' prefix) to test.
# Each value is a a `Tuple` of arguments to pass to the predicate method.
struct HavePredicateMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(values)
# Expected value and label.
private getter expected
# Creates the matcher with a expected values.
def initialize(@expected : TestValue(ExpectedType))
end
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"has #{expected.label}"
end
# Actually performs the test against the expression.
def match(actual : TestExpression(T)) : MatchData forall T
snapshot = snapshot_values(actual.value)
if match?(snapshot)
SuccessfulMatchData.new
else
FailedMatchData.new("#{actual.label} does not have #{expected.label}", **values(snapshot))
end
end
# Performs the test against the expression, but inverted.
# A successful match with `#match` should normally fail for this method, and vice-versa.
def negated_match(actual : TestExpression(T)) : MatchData forall T
snapshot = snapshot_values(actual.value)
if match?(snapshot)
FailedMatchData.new("#{actual.label} has #{expected.label}", **values(snapshot))
else
SuccessfulMatchData.new
end
end
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} does not have #{expected.label}"
end
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} has #{expected.label}"
end
# Captures all of the actual values.
# A `NamedTuple` is returned, with each key being the attribute.
private def snapshot_values(object)
{% begin %}
{
{% for attribute in ExpectedType.keys %}
{{attribute}}: object.has_{{attribute}}?(*@expected.value[{{attribute.symbolize}}]),
{% end %}
}
{% end %}
end
# Checks if all predicate methods from the snapshot of them are satisified.
private def match?(snapshot)
# Test each predicate and immediately return false if one is false.
{% for attribute in ExpectedType.keys %}
return false unless values[{{attribute.symbolize}}]
return false unless snapshot[{{attribute.symbolize}}]
{% end %}
# All checks passed if this point is reached.
true
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial) : MatchData
values = snapshot_values(partial.actual)
MatchData.new(match?(values), values, partial.label, label)
end
# Captures all of the actual values.
# A `NamedTuple` is returned,
# with each key being the attribute.
private def snapshot_values(actual)
# Produces the tuple for the failed match data from a snapshot of the predicate methods.
private def values(snapshot)
{% begin %}
{
{% for attribute in ExpectedType.keys %}
{{attribute}}: actual.has_{{attribute}}?(*@expected[{{attribute.symbolize}}]),
{{attribute}}: snapshot[{{attribute.symbolize}}].inspect,
{% end %}
}
{% end %}
end
# Match data specific to this matcher.
private struct MatchData(ActualType) < MatchData
# Creates the match data.
def initialize(matched, @named_tuple : ActualType, @actual_label : String, @expected_label : String)
super(matched)
end
# Information about the match.
getter named_tuple
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@actual_label} has #{@expected_label}"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@actual_label} does not have #{@expected_label}"
end
end
end
end

View file

@ -4,45 +4,48 @@ module Spectator::Matchers
# Matcher that tests whether a `Hash` (or similar type) has a given value.
# The set is checked with the `has_value?` method.
struct HaveValueMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(actual)
actual.has_value?(expected)
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"has value #{expected.label}"
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
values = ExpectedActual.new(partial, self)
MatchData.new(match?(values.actual), values)
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
actual.value.has_value?(expected.value)
end
# Match data specific to this matcher.
private struct MatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} does not have value #{expected.label}"
end
# Information about the match.
def named_tuple
actual = @values.actual
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} has value #{expected.label}"
end
# Additional information about the match failure.
# The return value is a NamedTuple with Strings for each value.
private def values(actual)
actual_value = actual.value
set = actual_value.responds_to?(:values) ? actual_value.values : actual_value
{
value: NegatableMatchDataValue.new(@values.expected),
actual: actual.responds_to?(:values) ? actual.values : actual,
value: expected.value.inspect,
actual: set.inspect,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} has value #{@values.expected_label}"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not have value #{@values.expected_label}"
end
end
end
end

View file

@ -4,44 +4,55 @@ module Spectator::Matchers
# Matcher that tests whether two values do not equal each other.
# The values are compared with the != operator.
struct InequalityMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(actual)
actual != expected
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"is not equal to #{expected.label}"
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
values = ExpectedActual.new(partial, self)
MatchData.new(match?(values.actual), values)
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
expected.value != actual.value
end
# Match data specific to this matcher.
private struct MatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} is equal to #{expected.label}"
end
# Information about the match.
def named_tuple
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} is not equal to #{expected.label}"
end
# Additional information about the match failure.
# The return value is a NamedTuple with Strings for each value.
private def values(actual)
{
expected: NegatablePrefixedMatchDataValue.new("Not", "", @values.expected),
actual: @values.actual,
expected: "Not #{expected.value.inspect}",
actual: actual.value.inspect,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} is not #{@values.expected_label} (using !=)"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} is #{@values.expected_label} (using !=)"
end
# Additional information about the match failure when negated.
# The return value is a NamedTuple with Strings for each value.
private def negated_values(actual)
{
expected: expected.value.inspect,
actual: actual.value.inspect,
}
end
end
end

View file

@ -4,44 +4,55 @@ module Spectator::Matchers
# Matcher that tests whether one value is less than or equal to another.
# The values are compared with the <= operator.
struct LessThanEqualMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(actual)
actual <= expected
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"less than or equal to #{expected.label}"
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
values = ExpectedActual.new(partial, self)
MatchData.new(match?(values.actual), values)
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
actual.value <= expected.value
end
# Match data specific to this matcher.
private struct MatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} is greater than #{expected.label}"
end
# Information about the match.
def named_tuple
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} is less than or equal to #{expected.label}"
end
# Additional information about the match failure.
# The return value is a NamedTuple with Strings for each value.
private def values(actual)
{
expected: NegatablePrefixedMatchDataValue.new("<=", ">", @values.expected),
actual: @values.actual,
expected: "<= #{expected.value.inspect}",
actual: actual.value.inspect,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} is less than or equal to #{@values.expected_label} (using <=)"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} is greater than #{@values.expected_label} (using <=)"
end
# Additional information about the match failure when negated.
# The return value is a NamedTuple with Strings for each value.
private def negated_values(actual)
{
expected: "> #{expected.value.inspect}",
actual: actual.value.inspect,
}
end
end
end

View file

@ -4,44 +4,55 @@ module Spectator::Matchers
# Matcher that tests whether one value is less than another.
# The values are compared with the < operator.
struct LessThanMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(actual)
actual < expected
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"less than #{expected.label}"
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
values = ExpectedActual.new(partial, self)
MatchData.new(match?(values.actual), values)
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
actual.value < expected.value
end
# Match data specific to this matcher.
private struct MatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} is greater than or equal to #{expected.label}"
end
# Information about the match.
def named_tuple
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} is less than #{expected.label}"
end
# Additional information about the match failure.
# The return value is a NamedTuple with Strings for each value.
private def values(actual)
{
expected: NegatablePrefixedMatchDataValue.new("<", ">=", @values.expected),
actual: @values.actual,
expected: "< #{expected.value.inspect}",
actual: actual.value.inspect,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} is less than #{@values.expected_label} (using <)"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} is greater than or equal to #{@values.expected_label} (using <)"
end
# Additional information about the match failure when negated.
# The return value is a NamedTuple with Strings for each value.
private def negated_values(actual)
{
expected: ">= #{expected.value.inspect}",
actual: actual.value.inspect,
}
end
end
end

View file

@ -1,46 +1,7 @@
require "./match_data_labeled_value"
require "./match_data_value"
require "./generic_match_data_value"
module Spectator::Matchers
# Information regarding a expectation parial and matcher.
# `Matcher#match` will return a sub-type of this.
# Information about the outcome of a match.
abstract struct MatchData
# Indicates whether the matcher was satisified with the expectation partial.
getter? matched : Bool
# Creates the base of the match data.
# The *matched* argument indicates
# whether the matcher was satisified with the expectation partial.
def initialize(@matched)
end
# Information about the match.
# Returned elments will differ by matcher,
# but all will return a set of labeled values.
def values : Array(MatchDataLabeledValue)
named_tuple.map do |key, value|
if value.is_a?(MatchDataValue)
MatchDataLabeledValue.new(key, value)
else
wrapper = GenericMatchDataValue.new(value)
MatchDataLabeledValue.new(key, wrapper)
end
end
end
# Raw information about the match.
# Sub-types must implement this and return a `NamedTuple`
# containing the match data values.
# This will be transformed and returned by `#values`.
private abstract def named_tuple
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
abstract def message : String
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
abstract def negated_message : String
# Indicates whether the match as successful or not.
abstract def matched? : Bool
end
end

View file

@ -1,15 +0,0 @@
module Spectator::Matchers
# A value from match data with a label.
struct MatchDataLabeledValue
# Label tied to the value.
# This annotates what the value is.
getter label : Symbol
# The actual value from the match data.
getter value : MatchDataValue
# Creates a new labeled value.
def initialize(@label, @value)
end
end
end

View file

@ -1,10 +0,0 @@
module Spectator::Matchers
# Abstract base for all match data values.
# All sub-classes are expected to implement their own `#to_s`.
private abstract class MatchDataValue
# Placeholder for negating the value.
def negate
# ...
end
end
end

View file

@ -5,13 +5,21 @@ module Spectator::Matchers
# A matcher looks at something produced by the SUT
# and evaluates whether it is correct or not.
abstract struct Matcher
# Textual representation of what the matcher expects.
# This shouldn't be used in the conditional logic,
# but for verbose output to help the end-user.
abstract def label : String
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
# ```
# it { is_expected.to do_something }
# ```
# The phrasing should be such that it reads "it ___."
# where the blank is what is returned by this method.
abstract def description : String
# Determines whether the matcher is satisfied with the value given to it.
# True is returned if the match was successful, false otherwise.
abstract def match(partial) : MatchData
# Actually performs the test against the expression (value or block).
abstract def match(actual : TestExpression(T)) : MatchData forall T
# 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.
abstract def negated_match(actual : TestExpression(T)) : MatchData forall T
end
end

View file

@ -1,34 +0,0 @@
require "./match_data_value"
module Spectator::Matchers
# Wraps an expected value that can be negated.
# This is used when a matcher is negated.
private class NegatableMatchDataValue(T) < MatchDataValue
# Negatable value.
getter value
# Creates the wrapper.
def initialize(@value : T)
@negated = false
end
# Negates (toggles) the value.
def negate
@negated = !@negated
end
# Produces a stringified value.
# The string will be prefixed with "Not" when negated.
def to_s(io)
io << "Not " if @negated
@value.inspect(io)
end
# Produces a stringified value with additional information.
# The string will be prefixed with "Not" when negated.
def inspect(io)
io << "Not " if @negated
@value.inspect(io)
end
end
end

View file

@ -1,39 +0,0 @@
require "./match_data_value"
module Spectator::Matchers
# Wraps a prefixed value that can be negated.
# This is used when a matcher is negated.
private class NegatablePrefixedMatchDataValue(T) < MatchDataValue
# Negatable value.
getter value
# Creates the wrapper.
def initialize(@positive_prefix : String, @negative_prefix : String, @value : T)
@negated = false
end
# Negates (toggles) the value.
def negate
@negated = !@negated
end
# Returns the correct prefix based on the negated status.
private def prefix
@negated ? @negative_prefix : @positive_prefix
end
# Produces a stringified value.
def to_s(io)
io << prefix
io << ' '
@value.inspect(io)
end
# Produces a stringified value with additional information.
def inspect(io)
io << prefix
io << ' '
@value.inspect(io)
end
end
end

View file

@ -3,46 +3,38 @@ require "./matcher"
module Spectator::Matchers
# Common matcher that tests whether a value is nil.
# The `Object#nil?` method is used for this.
struct NilMatcher < Matcher
# Textual representation of what the matcher expects.
def label
"nil?"
struct NilMatcher < StandardMatcher
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"is nil"
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
actual = partial.actual
matched = actual.nil?
MatchData.new(matched, actual, partial.label)
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
actual.value.nil?
end
# Match data specific to this matcher.
private struct MatchData(T) < MatchData
# Creates the match data.
def initialize(matched, @actual : T, @actual_label : String)
super(matched)
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} is not nil"
end
# Information about the match.
def named_tuple
{
expected: NegatableMatchDataValue.new(nil),
actual: @actual,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@actual_label} is nil"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@actual_label} is not nil"
end
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} is nil"
end
end
end

View file

@ -5,59 +5,95 @@ module Spectator::Matchers
# The `ExpectedType` type param should be a `NamedTuple`.
# Each key in the tuple is a predicate (without the '?') to test.
# Each value is a a `Tuple` of arguments to pass to the predicate method.
struct PredicateMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(values)
struct PredicateMatcher(ExpectedType) < Matcher
# Expected value and label.
private getter expected
# Creates the matcher with a expected values.
def initialize(@expected : TestValue(ExpectedType))
end
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"is #{expected.label}"
end
# Actually performs the test against the expression.
def match(actual : TestExpression(T)) : MatchData forall T
snapshot = snapshot_values(actual.value)
if match?(snapshot)
SuccessfulMatchData.new
else
FailedMatchData.new("#{actual.label} is not #{expected.label}", **values(snapshot))
end
end
# Performs the test against the expression, but inverted.
# A successful match with `#match` should normally fail for this method, and vice-versa.
def negated_match(actual : TestExpression(T)) : MatchData forall T
snapshot = snapshot_values(actual.value)
if match?(snapshot)
FailedMatchData.new("#{actual.label} is #{expected.label}", **values(snapshot))
else
SuccessfulMatchData.new
end
end
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} is not #{expected.label}"
end
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} is #{expected.label}"
end
# Captures all of the actual values.
# A `NamedTuple` is returned, with each key being the attribute.
private def snapshot_values(object)
{% begin %}
{
{% for attribute in ExpectedType.keys %}
{{attribute}}: object.{{attribute}}?(*@expected.value[{{attribute.symbolize}}]),
{% end %}
}
{% end %}
end
# Checks if all predicate methods from the snapshot of them are satisified.
private def match?(snapshot)
# Test each predicate and immediately return false if one is false.
{% for attribute in ExpectedType.keys %}
return false unless values[{{attribute.symbolize}}]
return false unless snapshot[{{attribute.symbolize}}]
{% end %}
# All checks passed if this point is reached.
true
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial) : MatchData
values = snapshot_values(partial.actual)
MatchData.new(match?(values), values, partial.label, label)
end
# Captures all of the actual values.
# A `NamedTuple` is returned,
# with each key being the attribute.
private def snapshot_values(actual)
# Produces the tuple for the failed match data from a snapshot of the predicate methods.
private def values(snapshot)
{% begin %}
{
{% for attribute in ExpectedType.keys %}
{{attribute}}: actual.{{attribute}}?(*@expected[{{attribute.symbolize}}]),
{{attribute}}: snapshot[{{attribute.symbolize}}].inspect,
{% end %}
}
{% end %}
end
# Match data specific to this matcher.
private struct MatchData(ActualType) < MatchData
# Creates the match data.
def initialize(matched, @named_tuple : ActualType, @actual_label : String, @expected_label : String)
super(matched)
end
# Information about the match.
getter named_tuple
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@actual_label} is #{@expected_label}"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@actual_label} is not #{@expected_label}"
end
end
end
end

View file

@ -1,27 +0,0 @@
require "./match_data_value"
module Spectator::Matchers
# Prefixes (for output formatting) an actual or expected value.
private class PrefixedMatchDataValue(T) < MatchDataValue
# Value being prefixed.
getter value : T
# Creates the prefixed value.
def initialize(@prefix : String, @value : T)
end
# Outputs the formatted value with a prefix.
def to_s(io)
io << @prefix
io << ' '
@value.inspect(io)
end
# Outputs details of the formatted value with a prefix.
def inspect(io)
io << @prefix
io << ' '
@prefix.inspect(io)
end
end
end

View file

@ -4,64 +4,76 @@ module Spectator::Matchers
# Matcher that tests whether a value is in a given range.
# The `Range#includes?` method is used for this check.
struct RangeMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(actual)
expected.includes?(actual)
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
actual = partial.actual
matched = match?(actual)
expected_value = @expected
MatchData.new(matched, ExpectedActual.new(expected_value, label, actual, partial.label))
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"is in #{expected.label}"
end
# Returns a new matcher, with the same bounds, but uses an inclusive range.
def inclusive
range = Range.new(@expected.begin, @expected.end, exclusive: false)
RangeMatcher.new(range, label)
new_range = Range.new(range.begin, range.end, exclusive: false)
expected = TestValue.new(new_range, label)
RangeMatcher.new(expected)
end
# Returns a new matcher, with the same bounds, but uses an exclusive range.
def exclusive
range = Range.new(@expected.begin, @expected.end, exclusive: true)
RangeMatcher.new(range, label)
new_range = Range.new(range.begin, range.end, exclusive: true)
expected = TestValue.new(new_range, label)
RangeMatcher.new(expected)
end
# Match data specific to this matcher.
# This is used when the expected type is a `Range`.
private struct MatchData(B, E, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(Range(B, E), ActualType))
super(matched)
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
expected.value.includes?(actual.value)
end
# Information about the match.
def named_tuple
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} is not in #{expected.label} (#{exclusivity})"
end
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} is in #{expected.label} (#{exclusivity})"
end
# Additional information about the match failure.
# The return value is a NamedTuple with Strings for each value.
private def values(actual)
{
lower: NegatablePrefixedMatchDataValue.new(">=", "<", range.begin),
upper: NegatablePrefixedMatchDataValue.new(exclusive? ? "<" : "<=", exclusive? ? ">=" : ">", range.end),
actual: @values.actual,
lower: ">= #{range.begin.inspect}",
upper: "#{exclusive? ? "<" : "<="} #{range.end.inspect}",
actual: actual.value.inspect,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} is in #{@values.expected_label} (#{exclusivity})"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} is not in #{@values.expected_label} (#{exclusivity})"
# Additional information about the match failure when negated.
# The return value is a NamedTuple with Strings for each value.
private def negated_values(actual)
{
lower: "< #{range.begin.inspect}",
upper: "#{exclusive? ? ">=" : ">"} #{range.end.inspect}",
actual: actual.value.inspect,
}
end
# Gets the expected range.
private def range
@values.expected
expected.value
end
# Indicates whether the range is inclusive or exclusive.
@ -75,4 +87,3 @@ module Spectator::Matchers
end
end
end
end

View file

@ -4,44 +4,37 @@ module Spectator::Matchers
# Matcher that tests whether two references are the same.
# The values are compared with the `Reference#same?` method.
struct ReferenceMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match?(actual)
actual.same?(expected)
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"is #{expected.label}"
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
values = ExpectedActual.new(partial, self)
MatchData.new(match?(values.actual), values)
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
expected.value.same?(actual.value)
end
# Match data specific to this matcher.
private struct MatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} is not #{expected.label}"
end
# Information about the match.
def named_tuple
{
expected: NegatableMatchDataValue.new(@values.expected),
actual: @values.actual,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} is #{@values.expected_label}"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} is not #{@values.expected_label}"
end
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} is #{expected.label}"
end
end
end

View file

@ -6,68 +6,68 @@ module Spectator::Matchers
# The `ExpectedType` type param should be a `NamedTuple`,
# with each key being the method to check and the value is ignored.
struct RespondMatcher(ExpectedType) < Matcher
# Determines whether the matcher is satisfied with the value given to it.
private def match?(actual)
# The snapshot did the hard work.
# Here just check if all values are true.
actual.values.all?
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"responds to #{label}"
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
values = snapshot_values(partial.actual)
MatchData.new(match?(values), values, partial.label, label)
# Actually performs the test against the expression.
def match(actual : TestExpression(T)) : MatchData forall T
snapshot = snapshot_values(actual.value)
if match?(snapshot)
SuccessfulMatchData.new
else
FailedMatchData.new("#{actual.label} does not respond to #{label}", **values(snapshot))
end
end
# Performs the test against the expression, but inverted.
# A successful match with `#match` should normally fail for this method, and vice-versa.
def negated_match(actual : TestExpression(T)) : MatchData forall T
snapshot = snapshot_values(actual.value)
if match?(snapshot)
FailedMatchData.new("#{actual.label} responds to #{label}", **values(snapshot))
else
SuccessfulMatchData.new
end
end
# Captures all of the actual values.
# A `NamedTuple` is returned,
# with each key being the attribute.
private def snapshot_values(actual)
# A `NamedTuple` is returned, with each key being the attribute.
private def snapshot_values(object)
{% begin %}
{
{% for method in ExpectedType.keys %}
{{method.stringify}}: actual.responds_to?({{method.symbolize}}),
{% for attribute in ExpectedType.keys %}
{{attribute}}: object.responds_to?({{attribute.symbolize}}),
{% end %}
}
{% end %}
end
# Textual representation of what the matcher expects.
def label
# Checks if all results from the snapshot are satisified.
private def match?(snapshot)
# The snapshot did the hard work.
# Here just check if all values are true.
snapshot.values.all?
end
# Produces the tuple for the failed match data from a snapshot of the results.
private def values(snapshot)
{% begin %}
{
{% for attribute in ExpectedType.keys %}
{{attribute}}: snapshot[{{attribute.symbolize}}].inspect,
{% end %}
}
{% end %}
end
# Generated, user-friendly, string for the expected value.
private def label
# Prefix every method name with # and join them with commas.
{{ExpectedType.keys.map { |e| "##{e}".id }.splat.stringify}}
end
# Match data specific to this matcher.
private struct MatchData(ActualType) < MatchData
# Creates the match data.
def initialize(matched, @actual : ActualType, @actual_label : String, @expected_label : String)
super(matched)
end
# Information about the match.
def named_tuple
{% begin %}
{
{% for method in ActualType.keys %}
{{"responds to #" + method.stringify}}: @actual[{{method.symbolize}}],
{% end %}
}
{% end %}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@actual_label} responds to #{@expected_label}"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@actual_label} does not respond to #{@expected_label}"
end
end
end
end

View file

@ -4,40 +4,55 @@ module Spectator::Matchers
# Matcher that tests whether a set has a specified number of elements.
# The set's `#size` method is used for this check.
struct SizeMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
actual = partial.actual.size
values = ExpectedActual.new(expected, label, actual, partial.label)
MatchData.new(actual == expected, values)
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"has size #{expected.label}"
end
# Match data specific to this matcher.
private struct MatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
expected.value == actual.value.size
end
# Information about the match.
def named_tuple
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} does not have #{expected.label} elements"
end
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} has #{expected.label} elements"
end
# Additional information about the match failure.
# The return value is a NamedTuple with Strings for each value.
private def values(actual)
{
expected: NegatableMatchDataValue.new(@values.expected),
actual: @values.actual,
expected: expected.value.inspect,
actual: actual.value.size.inspect,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} has #{@values.expected_label} elements"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not have #{@values.expected_label} elements"
end
# Additional information about the match failure when negated.
# The return value is a NamedTuple with Strings for each value.
private def negated_values(actual)
{
expected: "Not #{expected.value.inspect}",
actual: actual.value.size.inspect,
}
end
end
end

View file

@ -4,41 +4,55 @@ module Spectator::Matchers
# Matcher that tests whether a set has the same number of elements as another set.
# The set's `#size` method is used for this check.
struct SizeOfMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
actual = partial.actual.size
size = expected.size
values = ExpectedActual.new(size, label, actual, partial.label)
MatchData.new(actual == size, values)
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"is the same size as #{expected.label}"
end
# Match data specific to this matcher.
private struct MatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
expected.value.size == actual.value.size
end
# Information about the match.
def named_tuple
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} is not the same size as #{expected.label}"
end
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} is the same size as #{expected.label}"
end
# Additional information about the match failure.
# The return value is a NamedTuple with Strings for each value.
private def values(actual)
{
expected: NegatableMatchDataValue.new(@values.expected),
actual: @values.actual,
expected: expected.value.size.inspect,
actual: actual.value.size.inspect,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} has the same number of elements as #{@values.expected_label}"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} has a different number of elements than #{@values.expected_label}"
end
# Additional information about the match failure when negated.
# The return value is a NamedTuple with Strings for each value.
private def negated_values(actual)
{
expected: "Not #{expected.value.size.inspect}",
actual: actual.value.size.inspect,
}
end
end
end

View file

@ -0,0 +1,129 @@
require "../test_value"
require "./failed_match_data"
require "./matcher"
require "./successful_match_data"
module Spectator::Matchers
# Provides common methods for matchers.
#
# The `#match` and `#negated_match` methods have an implementation
# that is suitable for most matchers.
# Matchers based on this class need to define `#match?` and `#failure_message`.
# If the matcher can be negated,
# the `#failure_message_when_negated` method needs to be overriden.
# Additionally, the `#does_not_match?` method can be specified
# if there's custom behavior for negated matches.
# If the matcher operates on or has extra data that is useful for debug,
# then the `#values` and `#negated_values` methods can be overriden.
# Finally, define a `#description` message that can be used for the one-liner "it" syntax.
abstract struct StandardMatcher < Matcher
# Actually performs the test against the expression (value or block).
#
# This method calls the abstract `#match?` method.
# If it returns true, then a `SuccessfulMatchData` instance is returned.
# Otherwise, a `FailedMatchData` instance is returned.
# Additionally, `#failure_message` and `#values` are called for a failed match.
def match(actual : TestExpression(T)) : MatchData forall T
if match?(actual)
SuccessfulMatchData.new
else
FailedMatchData.new(failure_message(actual), **values(actual))
end
end
# 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.
#
# This method calls the abstract `#does_not_match?` method.
# If it returns true, then a `SuccessfulMatchData` instance is returned.
# Otherwise, a `FailedMatchData` instance is returned.
# Additionally, `#failure_message_when_negated` and `#negated_values` are called for a failed match.
def negated_match(actual : TestExpression(T)) : MatchData forall T
if does_not_match?(actual)
SuccessfulMatchData.new
else
FailedMatchData.new(failure_message_when_negated(actual), **negated_values(actual))
end
end
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private abstract def failure_message(actual : TestExpression(T)) : String forall T
# 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 only called when `#does_not_match?` returns false.
#
# A default implementation of this method is provided,
# which causes compilation to fail.
# If the matcher supports negation, it must override this method.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual : TestExpression(T)) : String forall T
{% raise "Negation with #{@type.name} is not supported." %}
end
# Checks whether the matcher is satisifed with the expression given to it.
private abstract def match?(actual : TestExpression(T)) : Bool forall T
# If the expectation is negated, then this method is called instead of `#match?`.
#
# The default implementation of this method is to invert the result of `#match?`.
# If the matcher requires custom handling of negated matches,
# then this method should be overriden.
# Remember to override `#failure_message_when_negated` as well.
private def does_not_match?(actual : TestExpression(T)) : Bool forall T
!match?(actual)
end
# Additional information about the match failure.
#
# By default, just the actual value is produced.
# The return value must be a NamedTuple with Strings for each value.
# The tuple can be of any size,
# but the keys must be known at compile-time (as Symbols),
# and the values must be strings.
# Generally, the string values are produced by calling `#inspect` on the relevant object.
# It should look like this:
# ```
# {
# expected: "foo",
# actual: "bar",
# }
# ```
#
# The values should typically only contain the test expression values, not the labels.
# Labeled should be returned by `#failure_message`.
private def values(actual : TestExpression(T)) forall T
{actual: actual.value.inspect}
end
# Additional information about the match failure when negated.
#
# By default, just the actual value is produced (same as `#values`).
# The return value must be a NamedTuple with Strings for each value.
# The tuple can be of any size,
# but the keys must be known at compile-time (as Symbols),
# and the values must be strings.
# Generally, the string values are produced by calling `#inspect` on the relevant object.
# It should look like this:
# ```
# {
# expected: "Not foo",
# actual: "bar",
# }
# ```
#
# The values should typically only contain the test expression values, not the labels.
# Labeled should be returned by `#failure_message_when_negated`.
private def negated_values(actual : TestExpression(T)) forall T
values(actual)
end
end
end

View file

@ -1,89 +1,101 @@
require "./value_matcher"
# Checks whether the last element of the value is the expected value.
# This method expects that the actual value is a set (enumerable).require "./value_matcher"
module Spectator::Matchers
# Matcher that tests whether a value, such as a `String` or `Array`, starts with a value.
# The `starts_with?` method is used if it's defined on the actual type.
# Otherwise, it is treated as an `Enumerable` and the `first` value is compared against.
struct StartWithMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
private def match_starts_with?(actual)
actual.starts_with?(expected)
struct StartWithMatcher(ExpectedType) < Matcher
# Expected value and label.
private getter expected
# Creates the matcher with an expected value.
def initialize(@expected : TestValue(ExpectedType))
end
# Determines whether the matcher is satisfied with the value given to it.
private def match_first?(actual)
expected === actual
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"starts with #{expected.label}"
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
values = ExpectedActual.new(partial, self)
actual = values.actual
if actual.responds_to?(:starts_with?)
StartsWithMatchData.new(match_starts_with?(actual), values)
# Actually performs the test against the expression.
def match(actual : TestExpression(T)) : MatchData forall T
if (value = actual.value).responds_to?(:starts_with?)
match_starts_with(value, actual.label)
else
first = actual.first
FirstMatchData.new(match_first?(first), values, first)
match_first(value, actual.label)
end
end
# Match data specific to this matcher.
# This type is used when the actual value responds to `starts_with?`.
private struct StartsWithMatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType))
super(matched)
end
# Information about the match.
def named_tuple
{
expected: NegatableMatchDataValue.new(@values.expected),
actual: @values.actual,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} starts with #{@values.expected_label} (using #starts_with?)"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not start with #{@values.expected_label} (using #starts_with?)"
# Performs the test against the expression, but inverted.
# A successful match with `#match` should normally fail for this method, and vice-versa.
def negated_match(actual : TestExpression(T)) : MatchData forall T
if (value = actual.value).responds_to?(:starts_with?)
negated_match_starts_with(value, actual.label)
else
negated_match_first(value, actual.label)
end
end
# Match data specific to this matcher.
# This type is used when the actual value does not respond to `ends_with?`.
private struct FirstMatchData(ExpectedType, ActualType, FirstType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType), @first : FirstType)
super(matched)
# Checks whether the actual value starts with the expected value.
# This method expects (and uses) the `#starts_with?` method on the value.
private def match_starts_with(actual_value, actual_label)
if actual_value.starts_with?(expected.value)
SuccessfulMatchData.new
else
FailedMatchData.new("#{actual_label} does not start with #{expected.label} (using #starts_with?)",
expected: expected.value.inspect,
actual: actual_value.inspect
)
end
end
# Information about the match.
def named_tuple
{
expected: @values.expected,
actual: @first,
list: @values.actual,
}
# Checks whether the first element of the value is the expected value.
# This method expects that the actual value is a set (enumerable).
private def match_first(actual_value, actual_label)
list = actual_value.to_a
first = list.first
if expected.value === first
SuccessfulMatchData.new
else
FailedMatchData.new("#{actual_label} does not start with #{expected.label} (using expected === first)",
expected: expected.value.inspect,
actual: first.inspect,
list: list.inspect
)
end
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} starts with #{@values.expected_label} (using expected === actual.first)"
# Checks whether the actual value does not start with the expected value.
# This method expects (and uses) the `#starts_with?` method on the value.
private def negated_match_starts_with(actual_value, actual_label)
if actual_value.starts_with?(expected.value)
FailedMatchData.new("#{actual_label} starts with #{expected.label} (using #starts_with?)",
expected: expected.value.inspect,
actual: actual_value.inspect
)
else
SuccessfulMatchData.new
end
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not start with #{@values.expected_label} (using expected === actual.first)"
# Checks whether the first element of the value is not the expected value.
# This method expects that the actual value is a set (enumerable).
private def negated_match_first(actual_value, actual_label)
list = actual_value.to_a
first = list.first
if expected.value === first
FailedMatchData.new("#{actual_label} starts with #{expected.label} (using expected === first)",
expected: expected.value.inspect,
actual: first.inspect,
list: list.inspect
)
else
SuccessfulMatchData.new
end
end
end

View file

@ -0,0 +1,11 @@
require "./match_data"
module Spectator::Matchers
# Information about a successful match.
struct SuccessfulMatchData < MatchData
# Indicates that the match succeeded.
def matched?
true
end
end
end

View file

@ -1,4 +1,4 @@
require "./value_matcher"
require "./standard_matcher"
module Spectator::Matchers
# Matcher that tests whether a value is truthy or falsey.
@ -8,29 +8,18 @@ module Spectator::Matchers
#
# Additionally, different matchers can be created
# by using the `#<`, `#<=`, `#>`, `#>=`, `#==`, and `#!=` operators.
struct TruthyMatcher < Matcher
struct TruthyMatcher < StandardMatcher
# Creates the truthy matcher.
# The *truthy* argument should be true to match "truthy" values,
# and false to match "falsey" values.
def initialize(@truthy : Bool)
def initialize(@truthy : Bool = true)
end
# Textual representation of what the matcher expects.
def label
@truthy ? "truthy" : "falsey"
end
# Determines whether the matcher is satisfied with the value given to it.
private def match?(actual)
# Cast value to truthy value and compare.
@truthy == !!actual
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
actual = partial.actual
MatchData.new(match?(actual), @truthy, actual, partial.label)
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"is #{label}"
end
# Creates a matcher that checks if a value is less than an expected value.
@ -38,7 +27,8 @@ module Spectator::Matchers
# ```
# expect(0).to be < 1
# ```
def <(expected : ExpectedType) forall ExpectedType
def <(value : ExpectedType) forall ExpectedType
expected = TestValue.new(value)
LessThanMatcher.new(expected)
end
@ -47,7 +37,8 @@ module Spectator::Matchers
# ```
# expect(0).to be <= 1
# ```
def <=(expected : ExpectedType) forall ExpectedType
def <=(value : ExpectedType) forall ExpectedType
expected = TestValue.new(value)
LessThanEqualMatcher.new(expected)
end
@ -56,7 +47,8 @@ module Spectator::Matchers
# ```
# expect(2).to be > 1
# ```
def >(expected : ExpectedType) forall ExpectedType
def >(value : ExpectedType) forall ExpectedType
expected = TestValue.new(value)
GreaterThanMatcher.new(expected)
end
@ -65,7 +57,8 @@ module Spectator::Matchers
# ```
# expect(2).to be >= 1
# ```
def >=(expected : ExpectedType) forall ExpectedType
def >=(value : ExpectedType) forall ExpectedType
expected = TestValue.new(value)
GreaterThanEqualMatcher.new(expected)
end
@ -74,7 +67,8 @@ module Spectator::Matchers
# ```
# expect(0).to be == 0
# ```
def ==(expected : ExpectedType) forall ExpectedType
def ==(value : ExpectedType) forall ExpectedType
expected = TestValue.new(value)
EqualityMatcher.new(expected)
end
@ -83,44 +77,65 @@ module Spectator::Matchers
# ```
# expect(0).to be != 1
# ```
def !=(expected : ExpectedType) forall ExpectedType
def !=(value : ExpectedType) forall ExpectedType
expected = TestValue.new(value)
InequalityMatcher.new(expected)
end
# Match data specific to this matcher.
private struct MatchData(ActualType) < MatchData
# Creates the match data.
def initialize(matched, @truthy : Bool, @actual : ActualType, @actual_label : String)
super(matched)
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
@truthy == !!actual.value
end
# Information about the match.
def named_tuple
truthy = "Not false or nil"
falsey = "false or nil"
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} is #{negated_label}"
end
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} is #{label}"
end
# Additional information about the match failure.
# The return value is a NamedTuple with Strings for each value.
private def values(actual)
{
expected: AlternativeMatchDataValue.new(@truthy ? truthy : falsey, @truthy ? falsey : truthy),
actual: @actual,
truthy: !!@actual,
expected: @truthy ? "Not false or nil" : "false or nil",
actual: actual.value.inspect,
truthy: !!actual.value.inspect,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@actual_label} is #{expected_label}"
# Additional information about the match failure when negated.
# The return value is a NamedTuple with Strings for each value.
private def negated_values(actual)
{
expected: @truthy ? "false or nil" : "Not false or nil",
actual: actual.value.inspect,
truthy: !!actual.value.inspect,
}
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@actual_label} is not #{expected_label}"
end
# Textual representation of what the matcher expects.
private def expected_label
# Generated, user-friendly, string for the expected value.
private def label
@truthy ? "truthy" : "falsey"
end
# Generated, user-friendly, string for the unexpected value.
private def negated_label
@truthy ? "falsey" : "truthy"
end
end
end

View file

@ -3,51 +3,56 @@ require "./matcher"
module Spectator::Matchers
# Matcher that tests a value is of a specified type.
# The values are compared with the `Object#is_a?` method.
struct TypeMatcher(Expected) < Matcher
# Textual representation of what the matcher expects.
# The `Expected` type param will be used to populate the label.
def label
Expected.to_s
struct TypeMatcher(Expected) < StandardMatcher
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"is as #{Expected}"
end
# Determines whether the matcher is satisfied with the value given to it.
private def match?(actual)
actual.is_a?(Expected)
# Checks whether the matcher is satisifed with the expression given to it.
private def match?(actual : TestExpression(T)) forall T
actual.value.is_a?(Expected)
end
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
actual = partial.actual
MatchData(Expected, typeof(actual)).new(match?(actual), actual, partial.label)
# Message displayed when the matcher isn't satisifed.
#
# This is only called when `#match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message(actual)
"#{actual.label} is not a #{Expected}"
end
# Match data specific to this matcher.
private struct MatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @actual : ActualType, @actual_label : String)
super(matched)
# 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 only called when `#does_not_match?` returns false.
#
# The message should typically only contain the test expression labels.
# Actual values should be returned by `#values`.
private def failure_message_when_negated(actual)
"#{actual.label} is a #{Expected}"
end
# Information about the match.
def named_tuple
# Additional information about the match failure.
# The return value is a NamedTuple with Strings for each value.
private def values(actual)
{
expected: NegatableMatchDataValue.new(ExpectedType),
actual: @actual.class,
expected: Expected.to_s,
actual: actual.value.class.inspect,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@actual_label} is a #{ExpectedType}"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@actual_label} is not a #{ExpectedType}"
end
# Additional information about the match failure when negated.
# The return value is a NamedTuple with Strings for each value.
private def negated_values(actual)
{
expected: "Not #{Expected}",
actual: actual.value.class.inspect,
}
end
end
end

View file

@ -3,19 +3,53 @@ require "./value_matcher"
module Spectator::Matchers
# Matcher for checking that the contents of one array (or similar type)
# has the exact same contents as another, but in any order.
struct UnorderedArrayMatcher(ExpectedType) < ValueMatcher(Enumerable(ExpectedType))
# Determines whether the matcher is satisfied with the partial given to it.
# `MatchData` is returned that contains information about the match.
def match(partial)
expected_elements = expected.to_a
actual = partial.actual.to_a
missing, extra = array_diff(expected, actual)
struct UnorderedArrayMatcher(ExpectedType) < Matcher
# Expected value and label.
private getter expected
# Creates the matcher with an expected value.
def initialize(@expected : TestValue(ExpectedType))
end
# Short text about the matcher's purpose.
# This explains what condition satisfies the matcher.
# The description is used when the one-liner syntax is used.
def description
"contains #{expected.label} in any order"
end
# Actually performs the test against the expression.
def match(actual : TestExpression(T)) : MatchData forall T
actual_elements = actual.value.to_a
expected_elements = expected.value.to_a
missing, extra = array_diff(expected_elements, actual_elements)
values = ExpectedActual.new(expected_elements, label, actual, partial.label)
if missing.empty? && extra.empty?
IdenticalMatchData.new(values)
SuccessfulMatchData.new
else
ContentMatchData.new(values, missing, extra)
FailedMatchData.new("#{actual_label} does not contain #{expected.label} (unordered)",
expected: expected_elements.inspect,
actual: actual_elements.inspect,
missing: missing.inspect,
extra: extra.inspect,
)
end
end
# Performs the test against the expression, but inverted.
# A successful match with `#match` should normally fail for this method, and vice-versa.
def negated_match(actual : TestExpression(T)) : MatchData forall T
actual_elements = actual.value.to_a
expected_elements = expected.value.to_a
missing, extra = array_diff(expected_elements, actual_elements)
if missing.empty? && extra.empty?
FailedMatchData.new("#{actual_label} contains #{expected.label} (unordered)",
expected: "Not #{expected_elements.inspect}",
actual: actual_elements.inspect,
)
else
SuccessfulMatchData.new
end
end
@ -25,7 +59,7 @@ module Spectator::Matchers
extra = actual.dup
missing = [] of ExpectedType
# TODO: OPTIMIZE
# OPTIMIZE: Not very efficient at finding the difference.
expected.each do |item|
index = extra.index(item)
if index
@ -37,79 +71,5 @@ module Spectator::Matchers
{missing, extra}
end
# Creates the value matcher.
# The label should be a string representation of the expectation.
# The expected value is stored for later use.
def initialize(expected : Enumerable(ExpectedType), label : String)
super
end
# Creates the value matcher.
# The label is generated by calling `#to_s` on the expected value.
# The expected value is stored for later use.
def initialize(expected : Enumerable(ExpectedType))
super
end
# Common functionality for all match data for this matcher.
private abstract struct CommonMatchData(ExpectedType, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(Array(ExpectedType), Array(ActualType)))
super(matched)
end
# Basic information about the match.
def named_tuple
{
expected: NegatableMatchDataValue.new(@values.expected),
actual: @values.actual,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} contains #{@values.expected_label} (unordered)"
end
end
# Match data specific to this matcher.
# This type is used when the actual value matches the expected value.
private struct IdenticalMatchData(ExpectedType, ActualType) < CommonMatchData(ExpectedType, ActualType)
# Creates the match data.
def initialize(values : ExpectedActual(Array(ExpectedType), Array(ActualType)))
super(true, values)
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not contain #{@values.expected_label} (unordered)"
end
end
# Match data specific to this matcher.
# This type is used when the actual contents differs from the expected contents.
private struct ContentMatchData(ExpectedType, ActualType) < CommonMatchData(ExpectedType, ActualType)
# Creates the match data.
def initialize(values : ExpectedActual(Array(ExpectedType), Array(ActualType)), @missing : Array(ExpectedType), @extra : Array(ActualType))
super(false, values)
end
# Information about the match.
def named_tuple
super.merge({
missing: @missing,
extra: @extra,
})
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not contain #{@values.expected_label} (content differs)"
end
end
end
end

View file

@ -1,30 +1,67 @@
require "./matcher"
require "./standard_matcher"
module Spectator::Matchers
# Category of matcher that uses a value.
# Matchers of this type expect that a SUT applies to the value in some way.
# Sub-types must implement `Matcher#match?`, `Matcher#message`, and `Matcher#negated_message`.
abstract struct ValueMatcher(ExpectedType) < Matcher
# Textual representation of what the matcher expects.
# This shouldn't be used in the conditional logic,
# but for verbose output to help the end-user.
getter label
#
# Matchers based on this class need to define `#match?` and `#failure_message`.
# If the matcher can be negated,
# the `#failure_message_when_negated` method needs to be overriden.
# Additionally, the `#does_not_match?` method can be specified
# if there's custom behavior for negated matches.
# If the matcher operates on or has extra data that is useful for debug,
# then the `#values` and `#negated_values` methods can be overriden.
# Finally, define a `#description` message that can be used for the one-liner "it" syntax.
#
# The failure messages should typically only contain the test expression labels.
# Actual values should be returned by `#values` and `#negated_values`.
abstract struct ValueMatcher(ExpectedType) < StandardMatcher
# Expected value.
# Sub-types may use this value to test the expectation and generate message strings.
getter expected
private getter expected
# Creates the value matcher.
# The label should be a string representation of the expectation.
# The expected value is stored for later use.
def initialize(@expected : ExpectedType, @label : String)
def initialize(@expected : TestValue(ExpectedType))
end
# Creates the value matcher.
# The label is generated by calling `#to_s` on the expected value.
# The expected value is stored for later use.
def initialize(expected : ExpectedType)
initialize(expected, expected.to_s)
# Additional information about the match failure.
#
# By default, just the actual and expected values are produced.
# The return value must be a NamedTuple with Strings for each value.
# The tuple can be of any size,
# but the keys must be known at compile-time (as Symbols),
# and the values must be strings.
# Generally, the string values are produced by calling `#inspect` on the relevant object.
# It should look like this:
# ```
# {
# expected: "foo",
# actual: "bar",
# }
# ```
private def values(actual : TestExpression(T)) forall T
super.merge(expected: expected.value.inspect)
end
# Additional information about the match failure when negated.
#
# By default, just the actual and expected values are produced (same as `#values`).
# However, the expected value is prefixed with the word "Not".
# The return value must be a NamedTuple with Strings for each value.
# The tuple can be of any size,
# but the keys must be known at compile-time (as Symbols),
# and the values must be strings.
# Generally, the string values are produced by calling `#inspect` on the relevant object.
# It should look like this:
# ```
# {
# expected: "Not foo",
# actual: "bar",
# }
# ```
private def negated_values(actual : TestExpression(T)) forall T
super.merge(expected: "Not #{expected.value.inspect}")
end
end
end

View file

@ -21,13 +21,17 @@ class Object
# However, since this isn't a macro and we can't "look behind" this method call
# to see what it was invoked on, the argument is an empty string.
# Additionally, the source file and line can't be obtained.
::Spectator::Expectations::ValueExpectationPartial.new(self, __FILE__, __LINE__).to(matcher)
actual = ::Spectator::TestValue.new(self)
source = ::Spectator::Source.new(__FILE__, __LINE__)
::Spectator::Expectations::ExpectationPartial.new(actual, source).to(matcher)
end
# Works the same as `#should` except the condition is inverted.
# When `#should` succeeds, this method will fail, and vice-versa.
def should_not(matcher : ::Spectator::Matchers::Matcher)
::Spectator::Expectations::ValueExpectationPartial.new(self, __FILE__, __LINE__).to_not(matcher)
actual = ::Spectator::TestValue.new(self)
source = ::Spectator::Source.new(__FILE__, __LINE__)
::Spectator::Expectations::ExpectationPartial.new(actual, source).to_not(matcher)
end
end
@ -35,12 +39,16 @@ struct Proc(*T, R)
# Extension method to create an expectation for a block of code (proc).
# Depending on the matcher, the proc may be executed multiple times.
def should(matcher : ::Spectator::Matchers::Matcher)
::Spectator::Expectations::BlockExpectationPartial.new(self, __FILE__, __LINE__).to(matcher)
actual = ::Spectator::TestBlock.new(self)
source = ::Spectator::Source.new(__FILE__, __LINE__)
::Spectator::Expectations::ExpectationPartial.new(actual, source).to(matcher)
end
# Works the same as `#should` except the condition is inverted.
# When `#should` succeeds, this method will fail, and vice-versa.
def should_not(matcher : ::Spectator::Matchers::Matcher)
::Spectator::Expectations::BlockExpectationPartial.new(self, __FILE__, __LINE__).to_not(matcher)
actual = ::Spectator::TestBlock.new(self)
source = ::Spectator::Source.new(__FILE__, __LINE__)
::Spectator::Expectations::BlockExpectationPartial.new(actual, source).to_not(matcher)
end
end

View file

@ -0,0 +1,48 @@
require "./test_expression"
module Spectator
# Captures an block from a test and its label.
struct TestBlock(ReturnType) < TestExpression(ReturnType)
# Calls the block and retrieves the value.
def value : ReturnType
@proc.call
end
# Creates the block expression with a custom label.
# Typically the label is the code in the block/proc.
def initialize(@proc : -> ReturnType, label : String)
super(label)
end
def self.create(proc : -> T, label : String) forall T
{% if T.id == "ReturnType".id %}
wrapper = -> { proc.call; nil }
TestBlock(Nil).new(wrapper, label)
{% else %}
TestBlock(T).new(proc, label)
{% end %}
end
# Creates the block expression with a generic label.
# This is used for the "should" syntax and when the label doesn't matter.
def initialize(@proc : -> ReturnType)
super("<Proc>")
end
def self.create(proc : -> T) forall T
{% if T.id == "ReturnType".id %}
wrapper = -> { proc.call; nil }
TestBlock(Nil).new(wrapper)
{% else %}
TestBlock(T).new(proc)
{% end %}
end
# Reports complete information about the expression.
def inspect(io)
io << label
io << " -> "
io << value
end
end
end

View file

@ -0,0 +1,25 @@
module Spectator
# Base type for capturing an expression from a test.
abstract struct TestExpression(T)
# User-friendly string displayed for the actual expression being tested.
# For instance, in the expectation:
# ```
# expect(foo).to eq(bar)
# ```
# This property will be "foo".
# It will be the literal string "foo",
# and not the actual value of the foo.
getter label : String
# Creates the common base of the expression.
def initialize(@label)
end
abstract def value : T
# String representation of the expression.
def to_s(io)
io << label
end
end
end

View file

@ -0,0 +1,29 @@
require "./test_expression"
module Spectator
# Captures a value from a test and its label.
struct TestValue(T) < TestExpression(T)
# Actual value.
getter value : T
# Creates the expression value with a custom label.
def initialize(@value : T, label : String)
super(label)
end
# Creates the expression with a stringified value.
# This is used for the "should" syntax and when the label doesn't matter.
def initialize(@value : T)
super(@value.to_s)
end
# Reports complete information about the expression.
def inspect(io)
io << label
io << '='
io << @value
end
end
alias LabeledValue = TestValue(String)
end