From ee7a91c3dd0cdbd33a5dc9a7a33a35119fe7d23e Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Wed, 6 Mar 2019 21:39:28 -0700 Subject: [PATCH] Add negation wrappers Fixes the reported expected values when the expectation is negated. --- src/spectator/expectations/expectation.cr | 8 ++++- src/spectator/matchers/alternative_value.cr | 28 ++++++++++++++++ src/spectator/matchers/attributes_matcher.cr | 2 +- src/spectator/matchers/case_matcher.cr | 2 +- src/spectator/matchers/contain_matcher.cr | 2 +- src/spectator/matchers/empty_matcher.cr | 2 +- src/spectator/matchers/end_with_matcher.cr | 2 +- src/spectator/matchers/equality_matcher.cr | 2 +- .../matchers/greater_than_equal_matcher.cr | 2 +- .../matchers/greater_than_matcher.cr | 2 +- src/spectator/matchers/have_key_matcher.cr | 2 +- src/spectator/matchers/have_matcher.cr | 2 +- src/spectator/matchers/have_value_matcher.cr | 2 +- src/spectator/matchers/inequality_matcher.cr | 2 +- .../matchers/less_than_equal_matcher.cr | 2 +- src/spectator/matchers/less_than_matcher.cr | 2 +- .../matchers/negatable_prefixed_value.cr | 27 ++++++++++++++++ src/spectator/matchers/negatable_value.cr | 32 +++++++++++++++++++ src/spectator/matchers/nil_matcher.cr | 2 +- src/spectator/matchers/range_matcher.cr | 6 ++-- src/spectator/matchers/regex_matcher.cr | 2 +- src/spectator/matchers/start_with_matcher.cr | 2 +- src/spectator/matchers/truthy_matcher.cr | 4 ++- src/spectator/matchers/type_matcher.cr | 2 +- 24 files changed, 118 insertions(+), 23 deletions(-) create mode 100644 src/spectator/matchers/alternative_value.cr create mode 100644 src/spectator/matchers/negatable_prefixed_value.cr create mode 100644 src/spectator/matchers/negatable_value.cr diff --git a/src/spectator/expectations/expectation.cr b/src/spectator/expectations/expectation.cr index 91079a9..b7ad88d 100644 --- a/src/spectator/expectations/expectation.cr +++ b/src/spectator/expectations/expectation.cr @@ -21,7 +21,13 @@ module Spectator::Expectations # Information about the match. # Returned value and type will something that has key-value pairs (like a `NamedTuple`). def values - @match_data.values + @match_data.values.tap do |values| + if @negated + values.each_value do |value| + value.negate if value.responds_to?(:negate) + end + end + end end # Text that indicates the condition that must be met for the expectation to be satisifed. diff --git a/src/spectator/matchers/alternative_value.cr b/src/spectator/matchers/alternative_value.cr new file mode 100644 index 0000000..a094ea7 --- /dev/null +++ b/src/spectator/matchers/alternative_value.cr @@ -0,0 +1,28 @@ +module Spectator::Matchers + # Selects a value based on whether the value is negated. + # This is used when a matcher is negated. + private class AlternativeValue(T1, T2) + # Negatable value. + getter value + + # Creates the wrapper. + def initialize(@value1 : T1, @value2 : T2) + @negated = false + end + + # Negates (toggles) the value. + def negate + @negated = !@negated + end + + # Produces a stringified value. + def to_s(io) + io << @negated ? @value1 : @value2 + end + + # Produces a stringified value with additional information. + def inspect(io) + (@negated ? @value1 : @value2).inspect(io) + end + end +end diff --git a/src/spectator/matchers/attributes_matcher.cr b/src/spectator/matchers/attributes_matcher.cr index 2b798fa..9545856 100644 --- a/src/spectator/matchers/attributes_matcher.cr +++ b/src/spectator/matchers/attributes_matcher.cr @@ -49,7 +49,7 @@ module Spectator::Matchers {% begin %} { {% for attribute in ExpectedType.keys %} - {{"expected " + attribute.stringify}}: @values.expected[{{attribute.symbolize}}], + {{"expected " + attribute.stringify}}: NegatableValue.new(@values.expected[{{attribute.symbolize}}]), {{"actual " + attribute.stringify}}: @values.actual[{{attribute.symbolize}}], {% end %} } diff --git a/src/spectator/matchers/case_matcher.cr b/src/spectator/matchers/case_matcher.cr index 6863351..7757a9e 100644 --- a/src/spectator/matchers/case_matcher.cr +++ b/src/spectator/matchers/case_matcher.cr @@ -26,7 +26,7 @@ module Spectator::Matchers # Information about the match. def values { - expected: @values.expected, + expected: NegatableValue.new(@values.expected), actual: @values.actual, } end diff --git a/src/spectator/matchers/contain_matcher.cr b/src/spectator/matchers/contain_matcher.cr index 1f769b0..5feaf12 100644 --- a/src/spectator/matchers/contain_matcher.cr +++ b/src/spectator/matchers/contain_matcher.cr @@ -28,7 +28,7 @@ module Spectator::Matchers # Information about the match. def values { - subset: @values.expected, + subset: NegatableValue.new(@values.expected), superset: @values.actual, } end diff --git a/src/spectator/matchers/empty_matcher.cr b/src/spectator/matchers/empty_matcher.cr index 3a4df27..74196eb 100644 --- a/src/spectator/matchers/empty_matcher.cr +++ b/src/spectator/matchers/empty_matcher.cr @@ -27,7 +27,7 @@ module Spectator::Matchers # Information about the match. def values { - expected: [] of Nil, + expected: NegatableValue.new([] of Nil), actual: @actual, } end diff --git a/src/spectator/matchers/end_with_matcher.cr b/src/spectator/matchers/end_with_matcher.cr index d4130f8..b1781f4 100644 --- a/src/spectator/matchers/end_with_matcher.cr +++ b/src/spectator/matchers/end_with_matcher.cr @@ -39,7 +39,7 @@ module Spectator::Matchers # Information about the match. def values { - expected: @values.expected, + expected: NegatableValue.new(@values.expected), actual: @values.actual, } end diff --git a/src/spectator/matchers/equality_matcher.cr b/src/spectator/matchers/equality_matcher.cr index 63a5354..b208063 100644 --- a/src/spectator/matchers/equality_matcher.cr +++ b/src/spectator/matchers/equality_matcher.cr @@ -26,7 +26,7 @@ module Spectator::Matchers # Information about the match. def values { - expected: @values.expected, + expected: NegatableValue.new(@values.expected), actual: @values.actual, } end diff --git a/src/spectator/matchers/greater_than_equal_matcher.cr b/src/spectator/matchers/greater_than_equal_matcher.cr index 2983dd3..73ffb54 100644 --- a/src/spectator/matchers/greater_than_equal_matcher.cr +++ b/src/spectator/matchers/greater_than_equal_matcher.cr @@ -26,7 +26,7 @@ module Spectator::Matchers # Information about the match. def values { - expected: PrefixedValue.new(">=", @values.expected), + expected: NegatablePrefixedValue.new(">=", "<", @values.expected), actual: PrefixedValue.new(actual_operator, @values.actual), } end diff --git a/src/spectator/matchers/greater_than_matcher.cr b/src/spectator/matchers/greater_than_matcher.cr index 8c46281..596f085 100644 --- a/src/spectator/matchers/greater_than_matcher.cr +++ b/src/spectator/matchers/greater_than_matcher.cr @@ -26,7 +26,7 @@ module Spectator::Matchers # Information about the match. def values { - expected: PrefixedValue.new(">", @values.expected), + expected: NegatablePrefixedValue.new(">", "<=", @values.expected), actual: PrefixedValue.new(actual_operator, @values.actual), } end diff --git a/src/spectator/matchers/have_key_matcher.cr b/src/spectator/matchers/have_key_matcher.cr index df5080a..2e97efc 100644 --- a/src/spectator/matchers/have_key_matcher.cr +++ b/src/spectator/matchers/have_key_matcher.cr @@ -27,7 +27,7 @@ module Spectator::Matchers def values actual = @values.actual { - key: @values.expected, + key: NegatableValue.new(@values.expected), actual: actual.responds_to?(:keys) ? actual.keys : actual, } end diff --git a/src/spectator/matchers/have_matcher.cr b/src/spectator/matchers/have_matcher.cr index 4637a04..b89fbda 100644 --- a/src/spectator/matchers/have_matcher.cr +++ b/src/spectator/matchers/have_matcher.cr @@ -51,7 +51,7 @@ module Spectator::Matchers # Information about the match. def values { - subset: @values.expected, + subset: NegatableValue.new(@values.expected), superset: @values.actual, } end diff --git a/src/spectator/matchers/have_value_matcher.cr b/src/spectator/matchers/have_value_matcher.cr index fe6b66a..e95b66f 100644 --- a/src/spectator/matchers/have_value_matcher.cr +++ b/src/spectator/matchers/have_value_matcher.cr @@ -27,7 +27,7 @@ module Spectator::Matchers def values actual = @values.actual { - value: @values.expected, + value: NegatableValue.new(@values.expected), actual: actual.responds_to?(:values) ? actual.values : actual, } end diff --git a/src/spectator/matchers/inequality_matcher.cr b/src/spectator/matchers/inequality_matcher.cr index 51d7aa1..e39ec20 100644 --- a/src/spectator/matchers/inequality_matcher.cr +++ b/src/spectator/matchers/inequality_matcher.cr @@ -26,7 +26,7 @@ module Spectator::Matchers # Information about the match. def values { - expected: PrefixedValue.new("Not", @values.expected), + expected: NegatablePrefixedValue.new("Not", "", @values.expected), actual: @values.actual, } end diff --git a/src/spectator/matchers/less_than_equal_matcher.cr b/src/spectator/matchers/less_than_equal_matcher.cr index 3b8bb12..0fee904 100644 --- a/src/spectator/matchers/less_than_equal_matcher.cr +++ b/src/spectator/matchers/less_than_equal_matcher.cr @@ -26,7 +26,7 @@ module Spectator::Matchers # Information about the match. def values { - expected: PrefixedValue.new("<=", @values.expected), + expected: NegatablePrefixedValue.new("<=", ">", @values.expected), actual: PrefixedValue.new(actual_operator, @values.actual), } end diff --git a/src/spectator/matchers/less_than_matcher.cr b/src/spectator/matchers/less_than_matcher.cr index c9866f6..09094ff 100644 --- a/src/spectator/matchers/less_than_matcher.cr +++ b/src/spectator/matchers/less_than_matcher.cr @@ -26,7 +26,7 @@ module Spectator::Matchers # Information about the match. def values { - expected: PrefixedValue.new("<", @values.expected), + expected: NegatablePrefixedValue.new("<", ">=", @values.expected), actual: PrefixedValue.new(actual_operator, @values.actual), } end diff --git a/src/spectator/matchers/negatable_prefixed_value.cr b/src/spectator/matchers/negatable_prefixed_value.cr new file mode 100644 index 0000000..b8d41a9 --- /dev/null +++ b/src/spectator/matchers/negatable_prefixed_value.cr @@ -0,0 +1,27 @@ +module Spectator::Matchers + # Wraps a prefixed value that can be negated. + # This is used when a matcher is negated. + private class NegatablePrefixedValue(T) + # 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 + + # Produces a stringified value. + def to_s(io) + io << @negated ? @negative_prefix : @positive_prefix + io << @value + end + + # Produces a stringified value with additional information. + def inspect(io) + io << @negated ? @negative_prefix : @positive_prefix + @value.inspect(io) + end + end +end diff --git a/src/spectator/matchers/negatable_value.cr b/src/spectator/matchers/negatable_value.cr new file mode 100644 index 0000000..77b82e4 --- /dev/null +++ b/src/spectator/matchers/negatable_value.cr @@ -0,0 +1,32 @@ +module Spectator::Matchers + # Wraps an expected value that can be negated. + # This is used when a matcher is negated. + private class NegatableValue(T) + # 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 + io << @value + 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/nil_matcher.cr b/src/spectator/matchers/nil_matcher.cr index fcf0c9f..a69e2ba 100644 --- a/src/spectator/matchers/nil_matcher.cr +++ b/src/spectator/matchers/nil_matcher.cr @@ -27,7 +27,7 @@ module Spectator::Matchers # Information about the match. def values { - expected: nil, + expected: NegatableValue.new(nil), actual: @actual, } end diff --git a/src/spectator/matchers/range_matcher.cr b/src/spectator/matchers/range_matcher.cr index 9d31516..86bac32 100644 --- a/src/spectator/matchers/range_matcher.cr +++ b/src/spectator/matchers/range_matcher.cr @@ -67,8 +67,8 @@ module Spectator::Matchers # Information about the match. def values { - lower: PrefixedValue.new(">=", range.begin), - upper: PrefixedValue.new(exclusive? ? "<" : "<=", range.end), + lower: NegatablePrefixedValue.new(">=", "<", range.begin), + upper: NegatablePrefixedValue.new(exclusive? ? "<" : "<=", exclusive? ? ">=" : ">", range.end), actual: @values.actual, } end @@ -112,7 +112,7 @@ module Spectator::Matchers # Information about the match. def values { - set: @values.expected, + set: NegatableValue.new(@values.expected), actual: @values.actual, } end diff --git a/src/spectator/matchers/regex_matcher.cr b/src/spectator/matchers/regex_matcher.cr index 39c92ef..6bc5181 100644 --- a/src/spectator/matchers/regex_matcher.cr +++ b/src/spectator/matchers/regex_matcher.cr @@ -26,7 +26,7 @@ module Spectator::Matchers # Information about the match. def values { - expected: @values.expected, + expected: NegatableValue.new(@values.expected), actual: @values.actual, } end diff --git a/src/spectator/matchers/start_with_matcher.cr b/src/spectator/matchers/start_with_matcher.cr index c9c03fd..5f4afbb 100644 --- a/src/spectator/matchers/start_with_matcher.cr +++ b/src/spectator/matchers/start_with_matcher.cr @@ -39,7 +39,7 @@ module Spectator::Matchers # Information about the match. def values { - expected: @values.expected, + expected: NegatableValue.new(@values.expected), actual: @values.actual, } end diff --git a/src/spectator/matchers/truthy_matcher.cr b/src/spectator/matchers/truthy_matcher.cr index 81f3449..c1cf1e1 100644 --- a/src/spectator/matchers/truthy_matcher.cr +++ b/src/spectator/matchers/truthy_matcher.cr @@ -96,8 +96,10 @@ module Spectator::Matchers # Information about the match. def values + truthy = "Not false or nil" + falsey = "false or nil" { - expected: @truthy ? "Not false or nil" : "false or nil", + expected: AlternativeValue.new(@truthy ? truthy : falsey, @truthy ? falsey : truthy), actual: @actual, truthy: !!@actual, } diff --git a/src/spectator/matchers/type_matcher.cr b/src/spectator/matchers/type_matcher.cr index 3d79970..16c26fa 100644 --- a/src/spectator/matchers/type_matcher.cr +++ b/src/spectator/matchers/type_matcher.cr @@ -32,7 +32,7 @@ module Spectator::Matchers # Information about the match. def values { - expected: ExpectedType, + expected: NegatableValue.new(ExpectedType), actual: ActualType, } end