mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Merge branch 'matcher-refactor-split' into matcher-refactor
This commit is contained in:
commit
43ba4de202
7 changed files with 141 additions and 79 deletions
23
src/spectator/expectations/actual.cr
Normal file
23
src/spectator/expectations/actual.cr
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
module Spectator::Expectations
|
||||||
|
# Base class for capturing an actual value - the thing being checked/tested.
|
||||||
|
abstract struct Actual
|
||||||
|
# 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 actual value.
|
||||||
|
def initialize(@label)
|
||||||
|
end
|
||||||
|
|
||||||
|
# String representation of the actual value.
|
||||||
|
def to_s(io)
|
||||||
|
io << label
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
30
src/spectator/expectations/block_actual.cr
Normal file
30
src/spectator/expectations/block_actual.cr
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
require "./actual"
|
||||||
|
|
||||||
|
module Spectator::Expectations
|
||||||
|
# Captures an actual block and its label.
|
||||||
|
struct BlockActual(ReturnType) < Actual
|
||||||
|
# Calls the block and retrieves the value.
|
||||||
|
def value : ReturnType
|
||||||
|
@proc.call
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates the actual with a custom label.
|
||||||
|
# Typically the label is the code in the block/proc.
|
||||||
|
def initialize(label : String, @proc : -> ReturnType)
|
||||||
|
super(label)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates the actual with a generic label.
|
||||||
|
# This is used for the "should" syntax and when the label doesn't matter.
|
||||||
|
def initialize(@proc : -> ReturnType)
|
||||||
|
super("<PROC>")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Reports complete information about the actual value.
|
||||||
|
def inspect(io)
|
||||||
|
io << label
|
||||||
|
io << " -> "
|
||||||
|
io << value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,25 +0,0 @@
|
||||||
require "./expectation_partial"
|
|
||||||
|
|
||||||
module Spectator::Expectations
|
|
||||||
# Expectation partial variation that operates on a block.
|
|
||||||
struct BlockExpectationPartial(ReturnType) < ExpectationPartial
|
|
||||||
# Actual value produced by calling the block.
|
|
||||||
def actual
|
|
||||||
@block.call
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates the expectation partial.
|
|
||||||
# The label should be a string representation of the block.
|
|
||||||
# The block is stored for later use.
|
|
||||||
def initialize(@block : Proc(ReturnType), label, source_file, source_line)
|
|
||||||
super(label, source_file, source_line)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates the expectation partial.
|
|
||||||
# The label is generated by calling to_s on the block.
|
|
||||||
# The block is stored for later use.
|
|
||||||
def initialize(@block : Proc(ReturnType), source_file, source_line)
|
|
||||||
super("<Proc>", source_file, source_line)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,39 +1,34 @@
|
||||||
|
require "../matchers/match_data"
|
||||||
|
require "../source"
|
||||||
|
require "./actual"
|
||||||
|
|
||||||
module Spectator::Expectations
|
module Spectator::Expectations
|
||||||
# Base class for all expectation partials.
|
# Stores part of an expectation (obviously).
|
||||||
# An "expectation partial" stores part of an expectation (obviously).
|
# The part of the expectation this type covers is the actual value and source.
|
||||||
# The part of the expectation this class covers is the actual value.
|
|
||||||
# This can also cover a block's behavior.
|
# This can also cover a block's behavior.
|
||||||
# Sub-types of this class are returned by the `DSL::ExampleDSL.expect` call.
|
struct ExpectationPartial
|
||||||
abstract struct ExpectationPartial
|
# The actual value being tested.
|
||||||
# User-friendly string displayed for the actual expression being tested.
|
# This also contains its label.
|
||||||
# For instance, in the expectation:
|
getter actual : Actual
|
||||||
# ```
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# Source file the expectation originated from.
|
# Location where this expectation was defined.
|
||||||
getter source_file : String
|
getter source : Source
|
||||||
|
|
||||||
# Line number in the source file the expectation originated from.
|
# Creates the partial.
|
||||||
getter source_line : Int32
|
def initialize(@actual, @source)
|
||||||
|
|
||||||
# Creates the base of the partial.
|
|
||||||
private def initialize(@label, @source_file, @source_line)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Asserts that some criteria defined by the matcher is satisfied.
|
# Asserts that some criteria defined by the matcher is satisfied.
|
||||||
def to(matcher) : Nil
|
def to(matcher) : Nil
|
||||||
report(matcher.match(self, false))
|
match_data = matcher.match(@actual)
|
||||||
|
report(match_data)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Asserts that some criteria defined by the matcher is not satisfied.
|
# Asserts that some criteria defined by the matcher is not satisfied.
|
||||||
# This is effectively the opposite of `#to`.
|
# This is effectively the opposite of `#to`.
|
||||||
def to_not(matcher) : Nil
|
def to_not(matcher) : Nil
|
||||||
report(matcher.match(self, true))
|
match_data = matcher.negated_match(@actual)
|
||||||
|
report(match_data)
|
||||||
end
|
end
|
||||||
|
|
||||||
# ditto
|
# ditto
|
||||||
|
@ -43,7 +38,8 @@ module Spectator::Expectations
|
||||||
end
|
end
|
||||||
|
|
||||||
# Reports an expectation to the current harness.
|
# 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)
|
Internals::Harness.current.report_expectation(expectation)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
27
src/spectator/expectations/value_actual.cr
Normal file
27
src/spectator/expectations/value_actual.cr
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
require "./actual"
|
||||||
|
|
||||||
|
module Spectator::Expectations
|
||||||
|
# Captures an actual value and its label.
|
||||||
|
struct ValueActual(T) < Actual
|
||||||
|
# Actual value.
|
||||||
|
getter value : T
|
||||||
|
|
||||||
|
# Creates the actual with a custom label.
|
||||||
|
def initialize(label : String, @value)
|
||||||
|
super(label)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates the actual with a stringified value.
|
||||||
|
# This is used for the "should" syntax and when the label doesn't matter.
|
||||||
|
def initialize(@value)
|
||||||
|
super(@value.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Reports complete information about the actual value.
|
||||||
|
def inspect(io)
|
||||||
|
io << label
|
||||||
|
io << '='
|
||||||
|
io << @value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -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
|
|
|
@ -5,12 +5,47 @@ module Spectator::Matchers
|
||||||
# A matcher looks at something produced by the SUT
|
# A matcher looks at something produced by the SUT
|
||||||
# and evaluates whether it is correct or not.
|
# and evaluates whether it is correct or not.
|
||||||
abstract struct Matcher
|
abstract struct Matcher
|
||||||
# Textual representation of what the matcher expects.
|
# Short text about the matcher's purpose.
|
||||||
# This shouldn't be used in the conditional logic,
|
# This explains what condition satisfies the matcher.
|
||||||
# but for verbose output to help the end-user.
|
# The description is used when the one-liner syntax is used.
|
||||||
abstract def label : String
|
# ```
|
||||||
|
# it { is_expected.to do_something }
|
||||||
|
# ```
|
||||||
|
# The phrasing should be such that it reads "it ___."
|
||||||
|
abstract def description : String
|
||||||
|
|
||||||
# Determines whether the matcher is satisfied with the partial given to it.
|
# Message displayed when the matcher isn't satisifed.
|
||||||
def match(partial, negated = false) : Expectation
|
# This is only called when `#matches?` returns false.
|
||||||
|
abstract def failure_message : String
|
||||||
|
|
||||||
|
# Message displayed when the matcher isn't satisifed and is 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.
|
||||||
|
def failure_message_when_negated : String
|
||||||
|
{% raise "Negation with #{@type.name} is not supported."}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Checks whether the matcher is satisifed.
|
||||||
|
private abstract def match?(actual) : Bool
|
||||||
|
|
||||||
|
# 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) : Bool
|
||||||
|
!matches?(actual)
|
||||||
|
end
|
||||||
|
|
||||||
|
def match(actual)
|
||||||
|
matched = match?(actual)
|
||||||
|
end
|
||||||
|
|
||||||
|
def negated_match(actual)
|
||||||
|
matched = does_not_match?(actual)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue