diff --git a/spec/expectation_failed_spec.cr b/spec/expectation_failed_spec.cr index 152967e..54c5d1d 100644 --- a/spec/expectation_failed_spec.cr +++ b/spec/expectation_failed_spec.cr @@ -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 diff --git a/spec/expectations/block_expectation_partial_spec.cr b/spec/expectations/block_expectation_partial_spec.cr deleted file mode 100644 index ea54f3a..0000000 --- a/spec/expectations/block_expectation_partial_spec.cr +++ /dev/null @@ -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 diff --git a/spec/expectations/expectation_spec.cr b/spec/expectations/expectation_spec.cr index 176fd72..06540fc 100644 --- a/spec/expectations/expectation_spec.cr +++ b/spec/expectations/expectation_spec.cr @@ -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 diff --git a/spec/expectations/value_expectation_partial_spec.cr b/spec/expectations/value_expectation_partial_spec.cr deleted file mode 100644 index b7422fb..0000000 --- a/spec/expectations/value_expectation_partial_spec.cr +++ /dev/null @@ -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 diff --git a/spec/helpers/expectations_helper.cr b/spec/helpers/expectations_helper.cr index ff7c339..0096f84 100644 --- a/spec/helpers/expectations_helper.cr +++ b/spec/helpers/expectations_helper.cr @@ -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 diff --git a/spec/matchers/array_matcher_spec.cr b/spec/matchers/array_matcher_spec.cr deleted file mode 100644 index 9376ccf..0000000 --- a/spec/matchers/array_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/attributes_matcher_spec.cr b/spec/matchers/attributes_matcher_spec.cr deleted file mode 100644 index 36bedc5..0000000 --- a/spec/matchers/attributes_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/case_matcher_spec.cr b/spec/matchers/case_matcher_spec.cr deleted file mode 100644 index 573a30b..0000000 --- a/spec/matchers/case_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/collection_matcher_spec.cr b/spec/matchers/collection_matcher_spec.cr deleted file mode 100644 index f848792..0000000 --- a/spec/matchers/collection_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/contain_matcher_spec.cr b/spec/matchers/contain_matcher_spec.cr deleted file mode 100644 index a10af48..0000000 --- a/spec/matchers/contain_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/empty_matcher_spec.cr b/spec/matchers/empty_matcher_spec.cr deleted file mode 100644 index eea43bc..0000000 --- a/spec/matchers/empty_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/end_with_matcher_spec.cr b/spec/matchers/end_with_matcher_spec.cr deleted file mode 100644 index d9894d5..0000000 --- a/spec/matchers/end_with_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/equality_matcher_spec.cr b/spec/matchers/equality_matcher_spec.cr deleted file mode 100644 index b997a0f..0000000 --- a/spec/matchers/equality_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/exception_matcher_spec.cr b/spec/matchers/exception_matcher_spec.cr deleted file mode 100644 index b5c31b4..0000000 --- a/spec/matchers/exception_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/greater_than_equal_matcher_spec.cr b/spec/matchers/greater_than_equal_matcher_spec.cr deleted file mode 100644 index c76641d..0000000 --- a/spec/matchers/greater_than_equal_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/greater_than_matcher_spec.cr b/spec/matchers/greater_than_matcher_spec.cr deleted file mode 100644 index 1375cff..0000000 --- a/spec/matchers/greater_than_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/have_key_matcher_spec.cr b/spec/matchers/have_key_matcher_spec.cr deleted file mode 100644 index cbcf6f6..0000000 --- a/spec/matchers/have_key_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/have_matcher_spec.cr b/spec/matchers/have_matcher_spec.cr deleted file mode 100644 index f4e917f..0000000 --- a/spec/matchers/have_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/have_predicate_matcher_spec.cr b/spec/matchers/have_predicate_matcher_spec.cr deleted file mode 100644 index 5b49382..0000000 --- a/spec/matchers/have_predicate_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/have_value_matcher_spec.cr b/spec/matchers/have_value_matcher_spec.cr deleted file mode 100644 index 78ba281..0000000 --- a/spec/matchers/have_value_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/inequality_matcher_spec.cr b/spec/matchers/inequality_matcher_spec.cr deleted file mode 100644 index 1dcdcfc..0000000 --- a/spec/matchers/inequality_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/less_than_equal_matcher_spec.cr b/spec/matchers/less_than_equal_matcher_spec.cr deleted file mode 100644 index 75d648f..0000000 --- a/spec/matchers/less_than_equal_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/less_than_matcher_spec.cr b/spec/matchers/less_than_matcher_spec.cr deleted file mode 100644 index df5acaf..0000000 --- a/spec/matchers/less_than_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/nil_matcher_spec.cr b/spec/matchers/nil_matcher_spec.cr deleted file mode 100644 index 3250d02..0000000 --- a/spec/matchers/nil_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/predicate_matcher_spec.cr b/spec/matchers/predicate_matcher_spec.cr deleted file mode 100644 index 031556b..0000000 --- a/spec/matchers/predicate_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/range_matcher_spec.cr b/spec/matchers/range_matcher_spec.cr deleted file mode 100644 index 5f473a5..0000000 --- a/spec/matchers/range_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/reference_matcher_spec.cr b/spec/matchers/reference_matcher_spec.cr deleted file mode 100644 index b029090..0000000 --- a/spec/matchers/reference_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/respond_matcher_spec.cr b/spec/matchers/respond_matcher_spec.cr deleted file mode 100644 index 3685464..0000000 --- a/spec/matchers/respond_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/size_matcher_spec.cr b/spec/matchers/size_matcher_spec.cr deleted file mode 100644 index e50432d..0000000 --- a/spec/matchers/size_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/size_of_matcher_spec.cr b/spec/matchers/size_of_matcher_spec.cr deleted file mode 100644 index eb1cbb0..0000000 --- a/spec/matchers/size_of_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/start_with_matcher_spec.cr b/spec/matchers/start_with_matcher_spec.cr deleted file mode 100644 index de626a5..0000000 --- a/spec/matchers/start_with_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/truthy_matcher_spec.cr b/spec/matchers/truthy_matcher_spec.cr deleted file mode 100644 index 7ef3970..0000000 --- a/spec/matchers/truthy_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/type_matcher_spec.cr b/spec/matchers/type_matcher_spec.cr deleted file mode 100644 index 7489b28..0000000 --- a/spec/matchers/type_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/spec/matchers/unordered_array_matcher_spec.cr b/spec/matchers/unordered_array_matcher_spec.cr deleted file mode 100644 index 5cea6f6..0000000 --- a/spec/matchers/unordered_array_matcher_spec.cr +++ /dev/null @@ -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 diff --git a/src/spectator/dsl/example_dsl.cr b/src/spectator/dsl/example_dsl.cr index f518b53..dfc3b2b 100644 --- a/src/spectator/dsl/example_dsl.cr +++ b/src/spectator/dsl/example_dsl.cr @@ -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. diff --git a/src/spectator/dsl/matcher_dsl.cr b/src/spectator/dsl/matcher_dsl.cr index 18feb87..18b30ab 100644 --- a/src/spectator/dsl/matcher_dsl.cr +++ b/src/spectator/dsl/matcher_dsl.cr @@ -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 diff --git a/src/spectator/expectation_failed.cr b/src/spectator/expectation_failed.cr index 35d7d5f..05246da 100644 --- a/src/spectator/expectation_failed.cr +++ b/src/spectator/expectation_failed.cr @@ -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 diff --git a/src/spectator/expectations/block_expectation_partial.cr b/src/spectator/expectations/block_expectation_partial.cr deleted file mode 100644 index 3adbec7..0000000 --- a/src/spectator/expectations/block_expectation_partial.cr +++ /dev/null @@ -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("", source_file, source_line) - end - end -end diff --git a/src/spectator/expectations/expectation.cr b/src/spectator/expectations/expectation.cr index 536aff0..300f134 100644 --- a/src/spectator/expectations/expectation.cr +++ b/src/spectator/expectations/expectation.cr @@ -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) - json.field("values") do - json.object do - values.each do |labeled_value| - json.field(labeled_value.label, labeled_value.value.to_s) - end + if (failed = @match_data.as?(Matchers::FailedMatchData)) + failed_to_json(failed, json) + end + end + end + + # Adds failure information to a JSON structure. + private def failed_to_json(failed : Matchers::FailedMatchData, json : ::JSON::Builder) + json.field("failure", failed.failure_message) + json.field("values") do + json.object do + failed.values.each do |pair| + json.field(pair.first, pair.last) end end end diff --git a/src/spectator/expectations/expectation_partial.cr b/src/spectator/expectations/expectation_partial.cr index 0238a8e..1cc8831 100644 --- a/src/spectator/expectations/expectation_partial.cr +++ b/src/spectator/expectations/expectation_partial.cr @@ -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 diff --git a/src/spectator/expectations/value_expectation_partial.cr b/src/spectator/expectations/value_expectation_partial.cr deleted file mode 100644 index 791bce5..0000000 --- a/src/spectator/expectations/value_expectation_partial.cr +++ /dev/null @@ -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 diff --git a/src/spectator/formatting/failure_block.cr b/src/spectator/formatting/failure_block.cr index 4b7276e..b542775 100644 --- a/src/spectator/formatting/failure_block.cr +++ b/src/spectator/formatting/failure_block.cr @@ -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) diff --git a/src/spectator/formatting/failure_junit_test_case.cr b/src/spectator/formatting/failure_junit_test_case.cr index d2f9d2d..964e673 100644 --- a/src/spectator/formatting/failure_junit_test_case.cr +++ b/src/spectator/formatting/failure_junit_test_case.cr @@ -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 diff --git a/src/spectator/formatting/match_data_value_pair.cr b/src/spectator/formatting/match_data_value_pair.cr index 95fa57f..dd7f255 100644 --- a/src/spectator/formatting/match_data_value_pair.cr +++ b/src/spectator/formatting/match_data_value_pair.cr @@ -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 diff --git a/src/spectator/formatting/match_data_values.cr b/src/spectator/formatting/match_data_values.cr index 51b5f1e..bf194a9 100644 --- a/src/spectator/formatting/match_data_values.cr +++ b/src/spectator/formatting/match_data_values.cr @@ -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 diff --git a/src/spectator/matchers/alternative_match_data_value.cr b/src/spectator/matchers/alternative_match_data_value.cr deleted file mode 100644 index b600312..0000000 --- a/src/spectator/matchers/alternative_match_data_value.cr +++ /dev/null @@ -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 diff --git a/src/spectator/matchers/array_matcher.cr b/src/spectator/matchers/array_matcher.cr index df29b9a..f3c3940 100644 --- a/src/spectator/matchers/array_matcher.cr +++ b/src/spectator/matchers/array_matcher.cr @@ -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 - end - IdenticalMatchData.new(values) - else - SizeMatchData.new(values) + struct ArrayMatcher(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 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) - end + # 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, - } - 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}" + # 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 + 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) - 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 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 - # 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 + # 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 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 diff --git a/src/spectator/matchers/attributes_matcher.cr b/src/spectator/matchers/attributes_matcher.cr index 12e7d49..a447d4a 100644 --- a/src/spectator/matchers/attributes_matcher.cr +++ b/src/spectator/matchers/attributes_matcher.cr @@ -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) - end + # 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 %} - # Information about the match. - def named_tuple - {% begin %} - { - {% for attribute in ExpectedType.keys %} - {{"expected " + attribute.stringify}}: NegatableMatchDataValue.new(@values.expected[{{attribute.symbolize}}]), - {{"actual " + attribute.stringify}}: @values.actual[{{attribute.symbolize}}], - {% end %} - } + # At this point, none of the checks failed, so the match was successful. + true + end + + # 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}}: expected.value[{{attribute.symbolize}}].inspect, + {{"actual " + attribute.stringify}}: snapshot[{{attribute.symbolize}}].inspect, {% 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 end diff --git a/src/spectator/matchers/case_matcher.cr b/src/spectator/matchers/case_matcher.cr index d321468..3ba55f3 100644 --- a/src/spectator/matchers/case_matcher.cr +++ b/src/spectator/matchers/case_matcher.cr @@ -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) - 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 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 diff --git a/src/spectator/matchers/collection_matcher.cr b/src/spectator/matchers/collection_matcher.cr index f57d46c..bfa88f2 100644 --- a/src/spectator/matchers/collection_matcher.cr +++ b/src/spectator/matchers/collection_matcher.cr @@ -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 diff --git a/src/spectator/matchers/contain_matcher.cr b/src/spectator/matchers/contain_matcher.cr index 739d1e9..53389fc 100644 --- a/src/spectator/matchers/contain_matcher.cr +++ b/src/spectator/matchers/contain_matcher.cr @@ -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) - 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} contains #{expected.label}" + end - # Information about the match. - def named_tuple - { - subset: NegatableMatchDataValue.new(@values.expected), - superset: @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}" - 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 + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. + private def values(actual) + { + subset: expected.value.inspect, + superset: actual.value.inspect, + } end end end diff --git a/src/spectator/matchers/dummy_match_data.cr b/src/spectator/matchers/dummy_match_data.cr deleted file mode 100644 index 4a6a693..0000000 --- a/src/spectator/matchers/dummy_match_data.cr +++ /dev/null @@ -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 diff --git a/src/spectator/matchers/empty_matcher.cr b/src/spectator/matchers/empty_matcher.cr index b477ec4..a8ccfe9 100644 --- a/src/spectator/matchers/empty_matcher.cr +++ b/src/spectator/matchers/empty_matcher.cr @@ -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) - 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 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 diff --git a/src/spectator/matchers/end_with_matcher.cr b/src/spectator/matchers/end_with_matcher.cr index 92655d5..ed76381 100644 --- a/src/spectator/matchers/end_with_matcher.cr +++ b/src/spectator/matchers/end_with_matcher.cr @@ -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 diff --git a/src/spectator/matchers/equality_matcher.cr b/src/spectator/matchers/equality_matcher.cr index 0e97e60..5d58454 100644 --- a/src/spectator/matchers/equality_matcher.cr +++ b/src/spectator/matchers/equality_matcher.cr @@ -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) - 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 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 diff --git a/src/spectator/matchers/exception_matcher.cr b/src/spectator/matchers/exception_matcher.cr index 066922c..981845c 100644 --- a/src/spectator/matchers/exception_matcher.cr +++ b/src/spectator/matchers/exception_matcher.cr @@ -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 diff --git a/src/spectator/matchers/expected_actual.cr b/src/spectator/matchers/expected_actual.cr deleted file mode 100644 index d4efe63..0000000 --- a/src/spectator/matchers/expected_actual.cr +++ /dev/null @@ -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 diff --git a/src/spectator/matchers/failed_match_data.cr b/src/spectator/matchers/failed_match_data.cr new file mode 100644 index 0000000..f1925ce --- /dev/null +++ b/src/spectator/matchers/failed_match_data.cr @@ -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 diff --git a/src/spectator/matchers/generic_match_data_value.cr b/src/spectator/matchers/generic_match_data_value.cr deleted file mode 100644 index 9069489..0000000 --- a/src/spectator/matchers/generic_match_data_value.cr +++ /dev/null @@ -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 diff --git a/src/spectator/matchers/greater_than_equal_matcher.cr b/src/spectator/matchers/greater_than_equal_matcher.cr index 4ac07aa..5f4e20b 100644 --- a/src/spectator/matchers/greater_than_equal_matcher.cr +++ b/src/spectator/matchers/greater_than_equal_matcher.cr @@ -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) - 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 less than #{expected.label}" + end - # Information about the match. - def named_tuple - { - expected: NegatablePrefixedMatchDataValue.new(">=", "<", @values.expected), - actual: @values.actual, - } - 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 greater than or equal to #{expected.label}" + 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 + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. + private def values(actual) + { + expected: ">= #{expected.value.inspect}", + actual: 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 - "#{@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 diff --git a/src/spectator/matchers/greater_than_matcher.cr b/src/spectator/matchers/greater_than_matcher.cr index a96947e..c51303b 100644 --- a/src/spectator/matchers/greater_than_matcher.cr +++ b/src/spectator/matchers/greater_than_matcher.cr @@ -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) - 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 less than or equal to #{expected.label}" + end - # Information about the match. - def named_tuple - { - expected: NegatablePrefixedMatchDataValue.new(">", "<=", @values.expected), - actual: @values.actual, - } - 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 greater than #{expected.label}" + 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 + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. + private def values(actual) + { + expected: "> #{expected.value.inspect}", + actual: 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 - "#{@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 diff --git a/src/spectator/matchers/have_key_matcher.cr b/src/spectator/matchers/have_key_matcher.cr index c27743a..e3f2560 100644 --- a/src/spectator/matchers/have_key_matcher.cr +++ b/src/spectator/matchers/have_key_matcher.cr @@ -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) - 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 key #{expected.label}" + end - # Information about the match. - def named_tuple - actual = @values.actual - { - key: NegatableMatchDataValue.new(@values.expected), - actual: actual.responds_to?(:keys) ? actual.keys : actual, - } - 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 key #{expected.label}" + 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 + # 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: expected.value.inspect, + actual: set.inspect, + } end end end diff --git a/src/spectator/matchers/have_matcher.cr b/src/spectator/matchers/have_matcher.cr index 7fb2a99..5dd0151 100644 --- a/src/spectator/matchers/have_matcher.cr +++ b/src/spectator/matchers/have_matcher.cr @@ -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) - 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} includes #{expected.label}" + end - # Information about the match. - def named_tuple - { - subset: NegatableMatchDataValue.new(@values.expected), - superset: @values.actual, - } - 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 + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. + private def values(actual) + { + subset: expected.value.inspect, + superset: actual.value.inspect, + } end end end diff --git a/src/spectator/matchers/have_predicate_matcher.cr b/src/spectator/matchers/have_predicate_matcher.cr index bba32ae..dedc70e 100644 --- a/src/spectator/matchers/have_predicate_matcher.cr +++ b/src/spectator/matchers/have_predicate_matcher.cr @@ -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 diff --git a/src/spectator/matchers/have_value_matcher.cr b/src/spectator/matchers/have_value_matcher.cr index a59d1f4..9c722f5 100644 --- a/src/spectator/matchers/have_value_matcher.cr +++ b/src/spectator/matchers/have_value_matcher.cr @@ -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) - 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 value #{expected.label}" + end - # Information about the match. - def named_tuple - actual = @values.actual - { - value: NegatableMatchDataValue.new(@values.expected), - actual: actual.responds_to?(:values) ? actual.values : actual, - } - 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 value #{expected.label}" + 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 + # 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: expected.value.inspect, + actual: set.inspect, + } end end end diff --git a/src/spectator/matchers/inequality_matcher.cr b/src/spectator/matchers/inequality_matcher.cr index 71010fb..7f175f1 100644 --- a/src/spectator/matchers/inequality_matcher.cr +++ b/src/spectator/matchers/inequality_matcher.cr @@ -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) - 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 equal to #{expected.label}" + end - # Information about the match. - def named_tuple - { - expected: NegatablePrefixedMatchDataValue.new("Not", "", @values.expected), - actual: @values.actual, - } - 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 not equal to #{expected.label}" + 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 + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. + private def values(actual) + { + expected: "Not #{expected.value.inspect}", + actual: 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 - "#{@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 diff --git a/src/spectator/matchers/less_than_equal_matcher.cr b/src/spectator/matchers/less_than_equal_matcher.cr index ff3fc10..900c7a4 100644 --- a/src/spectator/matchers/less_than_equal_matcher.cr +++ b/src/spectator/matchers/less_than_equal_matcher.cr @@ -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) - 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 greater than #{expected.label}" + end - # Information about the match. - def named_tuple - { - expected: NegatablePrefixedMatchDataValue.new("<=", ">", @values.expected), - actual: @values.actual, - } - 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 less than or equal to #{expected.label}" + 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 + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. + private def values(actual) + { + expected: "<= #{expected.value.inspect}", + actual: 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 - "#{@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 diff --git a/src/spectator/matchers/less_than_matcher.cr b/src/spectator/matchers/less_than_matcher.cr index 3cb12e6..5f2ec2c 100644 --- a/src/spectator/matchers/less_than_matcher.cr +++ b/src/spectator/matchers/less_than_matcher.cr @@ -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) - 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 greater than or equal to #{expected.label}" + end - # Information about the match. - def named_tuple - { - expected: NegatablePrefixedMatchDataValue.new("<", ">=", @values.expected), - actual: @values.actual, - } - 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 less than #{expected.label}" + 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 + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. + private def values(actual) + { + expected: "< #{expected.value.inspect}", + actual: 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 - "#{@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 diff --git a/src/spectator/matchers/match_data.cr b/src/spectator/matchers/match_data.cr index 4595e33..47b444c 100644 --- a/src/spectator/matchers/match_data.cr +++ b/src/spectator/matchers/match_data.cr @@ -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 diff --git a/src/spectator/matchers/match_data_labeled_value.cr b/src/spectator/matchers/match_data_labeled_value.cr deleted file mode 100644 index 2a39095..0000000 --- a/src/spectator/matchers/match_data_labeled_value.cr +++ /dev/null @@ -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 diff --git a/src/spectator/matchers/match_data_value.cr b/src/spectator/matchers/match_data_value.cr deleted file mode 100644 index 21629fd..0000000 --- a/src/spectator/matchers/match_data_value.cr +++ /dev/null @@ -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 diff --git a/src/spectator/matchers/matcher.cr b/src/spectator/matchers/matcher.cr index 8586918..f5d0d4c 100644 --- a/src/spectator/matchers/matcher.cr +++ b/src/spectator/matchers/matcher.cr @@ -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 diff --git a/src/spectator/matchers/negatable_match_data_value.cr b/src/spectator/matchers/negatable_match_data_value.cr deleted file mode 100644 index 62abf49..0000000 --- a/src/spectator/matchers/negatable_match_data_value.cr +++ /dev/null @@ -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 diff --git a/src/spectator/matchers/negatable_prefixed_match_data_value.cr b/src/spectator/matchers/negatable_prefixed_match_data_value.cr deleted file mode 100644 index 9cc6138..0000000 --- a/src/spectator/matchers/negatable_prefixed_match_data_value.cr +++ /dev/null @@ -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 diff --git a/src/spectator/matchers/nil_matcher.cr b/src/spectator/matchers/nil_matcher.cr index fd5021b..343fe79 100644 --- a/src/spectator/matchers/nil_matcher.cr +++ b/src/spectator/matchers/nil_matcher.cr @@ -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) - 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 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 diff --git a/src/spectator/matchers/predicate_matcher.cr b/src/spectator/matchers/predicate_matcher.cr index 1abd791..46d3d94 100644 --- a/src/spectator/matchers/predicate_matcher.cr +++ b/src/spectator/matchers/predicate_matcher.cr @@ -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 diff --git a/src/spectator/matchers/prefixed_match_data_value.cr b/src/spectator/matchers/prefixed_match_data_value.cr deleted file mode 100644 index feea8cc..0000000 --- a/src/spectator/matchers/prefixed_match_data_value.cr +++ /dev/null @@ -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 diff --git a/src/spectator/matchers/range_matcher.cr b/src/spectator/matchers/range_matcher.cr index ad05da3..6eba568 100644 --- a/src/spectator/matchers/range_matcher.cr +++ b/src/spectator/matchers/range_matcher.cr @@ -4,75 +4,86 @@ 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) - end + # 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 - { - lower: NegatablePrefixedMatchDataValue.new(">=", "<", range.begin), - upper: NegatablePrefixedMatchDataValue.new(exclusive? ? "<" : "<=", exclusive? ? ">=" : ">", range.end), - actual: @values.actual, - } - 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} (#{exclusivity})" + 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 + # 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 - # 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})" - end + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. + private def 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 - 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) + { + lower: "< #{range.begin.inspect}", + upper: "#{exclusive? ? ">=" : ">"} #{range.end.inspect}", + actual: actual.value.inspect, + } + end - # Indicates whether the range is inclusive or exclusive. - private def exclusive? - range.exclusive? - end + # Gets the expected range. + private def range + expected.value + end - # Produces a string "inclusive" or "exclusive" based on the range. - private def exclusivity - exclusive? ? "exclusive" : "inclusive" - end + # Indicates whether the range is inclusive or exclusive. + private def exclusive? + range.exclusive? + end + + # Produces a string "inclusive" or "exclusive" based on the range. + private def exclusivity + exclusive? ? "exclusive" : "inclusive" end end end diff --git a/src/spectator/matchers/reference_matcher.cr b/src/spectator/matchers/reference_matcher.cr index 1463a2a..633e619 100644 --- a/src/spectator/matchers/reference_matcher.cr +++ b/src/spectator/matchers/reference_matcher.cr @@ -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) - 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 - # 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 diff --git a/src/spectator/matchers/respond_matcher.cr b/src/spectator/matchers/respond_matcher.cr index 4c0e4a0..d842167 100644 --- a/src/spectator/matchers/respond_matcher.cr +++ b/src/spectator/matchers/respond_matcher.cr @@ -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 diff --git a/src/spectator/matchers/size_matcher.cr b/src/spectator/matchers/size_matcher.cr index 7295acf..1ab6e3d 100644 --- a/src/spectator/matchers/size_matcher.cr +++ b/src/spectator/matchers/size_matcher.cr @@ -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) - end + # 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 - { - expected: NegatableMatchDataValue.new(@values.expected), - actual: @values.actual, - } - 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} elements" + 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 + # 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 - # 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. + # The return value is a NamedTuple with Strings for each value. + private def values(actual) + { + expected: expected.value.inspect, + actual: actual.value.size.inspect, + } + 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 diff --git a/src/spectator/matchers/size_of_matcher.cr b/src/spectator/matchers/size_of_matcher.cr index 334a93d..3e9f1cf 100644 --- a/src/spectator/matchers/size_of_matcher.cr +++ b/src/spectator/matchers/size_of_matcher.cr @@ -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) - end + # 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 - { - expected: NegatableMatchDataValue.new(@values.expected), - actual: @values.actual, - } - 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 the same size as #{expected.label}" + 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 + # 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 - # 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. + # The return value is a NamedTuple with Strings for each value. + private def values(actual) + { + expected: expected.value.size.inspect, + actual: actual.value.size.inspect, + } + 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 diff --git a/src/spectator/matchers/standard_matcher.cr b/src/spectator/matchers/standard_matcher.cr new file mode 100644 index 0000000..414e491 --- /dev/null +++ b/src/spectator/matchers/standard_matcher.cr @@ -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 diff --git a/src/spectator/matchers/start_with_matcher.cr b/src/spectator/matchers/start_with_matcher.cr index 6ece475..067f52a 100644 --- a/src/spectator/matchers/start_with_matcher.cr +++ b/src/spectator/matchers/start_with_matcher.cr @@ -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 diff --git a/src/spectator/matchers/successful_match_data.cr b/src/spectator/matchers/successful_match_data.cr new file mode 100644 index 0000000..25a94ce --- /dev/null +++ b/src/spectator/matchers/successful_match_data.cr @@ -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 diff --git a/src/spectator/matchers/truthy_matcher.cr b/src/spectator/matchers/truthy_matcher.cr index 7eac541..34aea3b 100644 --- a/src/spectator/matchers/truthy_matcher.cr +++ b/src/spectator/matchers/truthy_matcher.cr @@ -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) - end + # 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" - { - expected: AlternativeMatchDataValue.new(@truthy ? truthy : falsey, @truthy ? falsey : truthy), - actual: @actual, - truthy: !!@actual, - } - 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 #{negated_label}" + end - # Describes the condition that satisfies the matcher. - # This is informational and displayed to the end-user. - def message - "#{@actual_label} is #{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 #{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 + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. + private def values(actual) + { + expected: @truthy ? "Not false or nil" : "false or nil", + actual: actual.value.inspect, + truthy: !!actual.value.inspect, + } + end - # Textual representation of what the matcher expects. - private def expected_label - @truthy ? "truthy" : "falsey" - 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: @truthy ? "false or nil" : "Not false or nil", + actual: actual.value.inspect, + truthy: !!actual.value.inspect, + } + end + + # 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 diff --git a/src/spectator/matchers/type_matcher.cr b/src/spectator/matchers/type_matcher.cr index a072b6d..2b44296 100644 --- a/src/spectator/matchers/type_matcher.cr +++ b/src/spectator/matchers/type_matcher.cr @@ -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) - 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 a #{Expected}" + end - # Information about the match. - def named_tuple - { - expected: NegatableMatchDataValue.new(ExpectedType), - actual: @actual.class, - } - end + # Additional information about the match failure. + # The return value is a NamedTuple with Strings for each value. + private def values(actual) + { + 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 diff --git a/src/spectator/matchers/unordered_array_matcher.cr b/src/spectator/matchers/unordered_array_matcher.cr index d84adad..823ee16 100644 --- a/src/spectator/matchers/unordered_array_matcher.cr +++ b/src/spectator/matchers/unordered_array_matcher.cr @@ -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 diff --git a/src/spectator/matchers/value_matcher.cr b/src/spectator/matchers/value_matcher.cr index 9ffa6d8..5049320 100644 --- a/src/spectator/matchers/value_matcher.cr +++ b/src/spectator/matchers/value_matcher.cr @@ -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 diff --git a/src/spectator/should.cr b/src/spectator/should.cr index 2cfcf2d..de75c85 100644 --- a/src/spectator/should.cr +++ b/src/spectator/should.cr @@ -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 diff --git a/src/spectator/test_block.cr b/src/spectator/test_block.cr new file mode 100644 index 0000000..b0dc53d --- /dev/null +++ b/src/spectator/test_block.cr @@ -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("") + 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 diff --git a/src/spectator/test_expression.cr b/src/spectator/test_expression.cr new file mode 100644 index 0000000..d5e3cdd --- /dev/null +++ b/src/spectator/test_expression.cr @@ -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 diff --git a/src/spectator/test_value.cr b/src/spectator/test_value.cr new file mode 100644 index 0000000..b621562 --- /dev/null +++ b/src/spectator/test_value.cr @@ -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