diff --git a/src/spectator/abstract_expression.cr b/src/spectator/abstract_expression.cr new file mode 100644 index 0000000..2a58e06 --- /dev/null +++ b/src/spectator/abstract_expression.cr @@ -0,0 +1,52 @@ +require "./label" + +module Spectator + # Represents an expression from a test. + # This is typically captured by an `expect` macro. + # It consists of a label and the value of the expression. + # The label should be a string recognizable by the user, + # or nil if one isn't available. + # This base class is provided so that all generic sub-classes can be stored as this one type. + # The value of the expression can be retrieved by downcasting to the expected type with `#cast`. + abstract class AbstractExpression + # User recognizable string for the expression. + # This can be something like a variable name or a snippet of Crystal code. + getter label : Label + + # Creates the expression. + # The *label* is usually the Crystal code evaluating to the `#value`. + # It can be nil if it isn't available. + def initialize(@label : Label) + end + + # Retrieves the real value of the expression. + abstract def value + + # Attempts to cast `#value` to the type *T* and return it. + def cast(type : T.class) : T forall T + value.as(T) + end + + # Produces a string representation of the expression. + # This consists of the label (if one is available) and the value. + def to_s(io) + if (label = @label) + io << label + io << ':' + io << ' ' + end + io << value + end + + # Produces a detailed string representation of the expression. + # This consists of the label (if one is available) and the value. + def inspect(io) + if (label = @label) + io << @label + io << ':' + io << ' ' + end + value.inspect(io) + end + end +end diff --git a/src/spectator/expression.cr b/src/spectator/expression.cr new file mode 100644 index 0000000..b12efce --- /dev/null +++ b/src/spectator/expression.cr @@ -0,0 +1,22 @@ +require "./abstract_expression" +require "./label" + +module Spectator + # Represents an expression from a test. + # This is typically captured by an `expect` macro. + # It consists of a label and the value of the expression. + # The label should be a string recognizable by the user, + # or nil if one isn't available. + class Expression(T) < AbstractExpression + # Raw value of the expression. + getter value + + # Creates the expression. + # Expects the *value* of the expression and a *label* describing it. + # The *label* is usually the Crystal code evaluating to the *value*. + # It can be nil if it isn't available. + def initialize(@value : T, label : Label) + super(label) + end + end +end diff --git a/src/spectator/label.cr b/src/spectator/label.cr new file mode 100644 index 0000000..2a86b34 --- /dev/null +++ b/src/spectator/label.cr @@ -0,0 +1,7 @@ +module Spectator + # Identifier used in the spec. + # Signficant to the user. + # When a label is a symbol, then it is referencing a type or method. + # A label is nil when one can't be provided or captured. + alias Label = String | Symbol | Nil +end diff --git a/src/spectator/test_block.cr b/src/spectator/test_block.cr deleted file mode 100644 index a6af675..0000000 --- a/src/spectator/test_block.cr +++ /dev/null @@ -1,48 +0,0 @@ -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_context.cr b/src/spectator/test_context.cr deleted file mode 100644 index bf2c612..0000000 --- a/src/spectator/test_context.cr +++ /dev/null @@ -1,82 +0,0 @@ -require "./example_hooks" -require "./test_values" - -module Spectator - class TestContext - getter! parent - - getter values - - getter stubs : Hash(String, Deque(Mocks::MethodStub)) - - def initialize(@parent : TestContext?, - @hooks : ExampleHooks, - @conditions : ExampleConditions, - @values : TestValues, - @stubs : Hash(String, Deque(Mocks::MethodStub))) - @before_all_hooks_run = false - @after_all_hooks_run = false - end - - def run_before_hooks(example : Example) - run_before_all_hooks - run_before_each_hooks(example) - end - - protected def run_before_all_hooks - return if @before_all_hooks_run - - @parent.try &.run_before_all_hooks - @hooks.run_before_all - ensure - @before_all_hooks_run = true - end - - protected def run_before_each_hooks(example : Example) - @parent.try &.run_before_each_hooks(example) - @hooks.run_before_each(example.test_wrapper, example) - end - - def run_after_hooks(example : Example) - run_after_each_hooks(example) - run_after_all_hooks(example.group) - end - - protected def run_after_all_hooks(group : ExampleGroup, *, ignore_unfinished = false) - return if @after_all_hooks_run - return unless ignore_unfinished || group.finished? - - @hooks.run_after_all - @parent.try do |parent_context| - parent_group = group.as(NestedExampleGroup).parent - parent_context.run_after_all_hooks(parent_group, ignore_unfinished: ignore_unfinished) - end - ensure - @after_all_hooks_run = true - end - - protected def run_after_each_hooks(example : Example) - @hooks.run_after_each(example.test_wrapper, example) - @parent.try &.run_after_each_hooks(example) - end - - def wrap_around_each_hooks(test, &block : ->) - wrapper = @hooks.wrap_around_each(test, block) - if (parent = @parent) - parent.wrap_around_each_hooks(test, &wrapper) - else - wrapper - end - end - - def run_pre_conditions(example) - @parent.try &.run_pre_conditions(example) - @conditions.run_pre_conditions(example.test_wrapper, example) - end - - def run_post_conditions(example) - @conditions.run_post_conditions(example.test_wrapper, example) - @parent.try &.run_post_conditions(example) - end - end -end diff --git a/src/spectator/test_expression.cr b/src/spectator/test_expression.cr deleted file mode 100644 index d5e3cdd..0000000 --- a/src/spectator/test_expression.cr +++ /dev/null @@ -1,25 +0,0 @@ -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 deleted file mode 100644 index b621562..0000000 --- a/src/spectator/test_value.cr +++ /dev/null @@ -1,29 +0,0 @@ -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 diff --git a/src/spectator/test_wrapper.cr b/src/spectator/test_wrapper.cr deleted file mode 100644 index 79d5265..0000000 --- a/src/spectator/test_wrapper.cr +++ /dev/null @@ -1,42 +0,0 @@ -require "../spectator_test" -require "./source" - -module Spectator - alias TestMethod = ::SpectatorTest -> - - # Stores information about a end-user test. - # Used to instantiate tests and run them. - struct TestWrapper - # Description the user provided for the test. - def description - @description || @source.to_s - end - - # Location of the test in source code. - getter source - - # Creates a wrapper for the test. - def initialize(@description : String?, @source : Source, @test : ::SpectatorTest, @runner : TestMethod) - end - - def description? - !@description.nil? - end - - def run - call(@runner) - end - - def call(method : TestMethod) : Nil - method.call(@test) - end - - def call(method, *args) : Nil - method.call(@test, *args) - end - - def around_hook(context : TestContext) - context.wrap_around_each_hooks(@test) { run } - end - end -end diff --git a/src/spectator/typed_value_wrapper.cr b/src/spectator/typed_value_wrapper.cr deleted file mode 100644 index 8131d82..0000000 --- a/src/spectator/typed_value_wrapper.cr +++ /dev/null @@ -1,17 +0,0 @@ -require "./value_wrapper" - -module Spectator - # Implementation of a value wrapper for a specific type. - # Instances of this class should be created to wrap values. - # Then the wrapper should be stored as a `ValueWrapper` - # so that the type is deferred to runtime. - # This trick allows the DSL to store values without explicitly knowing their type. - class TypedValueWrapper(T) < ValueWrapper - # Wrapped value. - getter value : T - - # Creates a new wrapper for a value. - def initialize(@value : T) - end - end -end diff --git a/src/spectator/value_wrapper.cr b/src/spectator/value_wrapper.cr deleted file mode 100644 index 170951d..0000000 --- a/src/spectator/value_wrapper.cr +++ /dev/null @@ -1,7 +0,0 @@ -module Spectator - # Base class for proxying test values to examples. - # This abstraction is required for inferring types. - # The DSL makes heavy use of this to defer types. - abstract class ValueWrapper - end -end