mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Merge branch 'example-restructure' of gitlab.com:arctic-fox/spectator into example-restructure
This commit is contained in:
commit
a100191875
9 changed files with 63 additions and 208 deletions
22
src/spectator/builders/example_group_stack.cr
Normal file
22
src/spectator/builders/example_group_stack.cr
Normal file
|
@ -0,0 +1,22 @@
|
|||
module Spectator::Builders
|
||||
struct ExampleGroupStack
|
||||
getter root = RootExampleGroupBuilder.new
|
||||
|
||||
@stack = Deque(ExampleGroupBuilder).new(1, @root)
|
||||
|
||||
def current
|
||||
@stack.last
|
||||
end
|
||||
|
||||
def push(group : NestedExampleGroupBuilder)
|
||||
current.add_child(group)
|
||||
@stack.push(group)
|
||||
end
|
||||
|
||||
def pop
|
||||
raise "Attempted to pop root example group from stack" if current == root
|
||||
|
||||
@stack.pop
|
||||
end
|
||||
end
|
||||
end
|
|
@ -6,26 +6,7 @@ module Spectator::DSL
|
|||
module Builder
|
||||
extend self
|
||||
|
||||
# Root group that contains all examples and groups in the spec.
|
||||
private class_getter root_group = RootExampleGroupBuilder.new
|
||||
|
||||
# Stack for tracking the current group the spec is working in.
|
||||
# The last item (top of the stack) is the current group.
|
||||
# The first item (bottom of the stack) is the root group (`#root_group`).
|
||||
# The root group should never be popped.
|
||||
@@group_stack = Array(ExampleGroupBuilder).new(1, root_group)
|
||||
|
||||
# Retrieves the current group the spec is working in.
|
||||
private def current_group
|
||||
@@group_stack.last
|
||||
end
|
||||
|
||||
# Adds a new group to the stack.
|
||||
# Calling this method indicates the spec has entered a nested group.
|
||||
private def push_group(group : NestedExampleGroupBuilder)
|
||||
current_group.add_child(group)
|
||||
@@group_stack.push(group)
|
||||
end
|
||||
@@stack = Builders::ExampleGroupStack.new
|
||||
|
||||
# Begins a new nested group in the spec.
|
||||
# A corresponding `#end_group` call must be made
|
||||
|
@ -34,7 +15,7 @@ module Spectator::DSL
|
|||
# as arguments to this method are passed directly to it.
|
||||
def start_group(*args) : Nil
|
||||
group = NestedExampleGroupBuilder.new(*args)
|
||||
push_group(group)
|
||||
@@stack.push(group)
|
||||
end
|
||||
|
||||
# Begins a new sample group in the spec -
|
||||
|
@ -45,7 +26,7 @@ module Spectator::DSL
|
|||
# as arguments to this method are passed directly to it.
|
||||
def start_sample_group(*args) : Nil
|
||||
group = SampleExampleGroupBuilder.new(*args)
|
||||
push_group(group)
|
||||
@@stack.push(group)
|
||||
end
|
||||
|
||||
# Marks the end of a group in the spec.
|
||||
|
@ -53,58 +34,61 @@ module Spectator::DSL
|
|||
# It is also important to line up the start and end calls.
|
||||
# Otherwise examples might get placed into wrong groups.
|
||||
def end_group : Nil
|
||||
@@group_stack.pop
|
||||
@@stack.pop
|
||||
end
|
||||
|
||||
# Adds an example type to the current group.
|
||||
# The class name of the example should be passed as an argument.
|
||||
# The example will be instantiated later.
|
||||
def add_example(example_type : Example.class) : Nil
|
||||
factory = ExampleFactory.new(example_type)
|
||||
current_group.add_child(factory)
|
||||
def add_example(description : String, source : Source,
|
||||
example_type : ::SpectatorTest.class, &runner : ::SpectatorTest ->) : Nil
|
||||
builder = ->{ example_type.new.as(::SpectatorTest) }
|
||||
wrapper = TestWrapper.new(description, source, builder, runner)
|
||||
example = Example.new(current_group, wrapper)
|
||||
# TODO: Add to stack.
|
||||
end
|
||||
|
||||
# Adds a block of code to run before all examples in the current group.
|
||||
def add_before_all_hook(&block : ->) : Nil
|
||||
current_group.add_before_all_hook(block)
|
||||
@@stack.current.add_before_all_hook(block)
|
||||
end
|
||||
|
||||
# Adds a block of code to run before each example in the current group.
|
||||
def add_before_each_hook(&block : ->) : Nil
|
||||
current_group.add_before_each_hook(block)
|
||||
@@stack.current.add_before_each_hook(block)
|
||||
end
|
||||
|
||||
# Adds a block of code to run after all examples in the current group.
|
||||
def add_after_all_hook(&block : ->) : Nil
|
||||
current_group.add_after_all_hook(block)
|
||||
@@stack.current.add_after_all_hook(block)
|
||||
end
|
||||
|
||||
# Adds a block of code to run after each example in the current group.
|
||||
def add_after_each_hook(&block : ->) : Nil
|
||||
current_group.add_after_each_hook(block)
|
||||
@@stack.current.add_after_each_hook(block)
|
||||
end
|
||||
|
||||
# Adds a block of code to run before and after each example in the current group.
|
||||
# The block of code will be given another proc as an argument.
|
||||
# It is expected that the block will call the proc.
|
||||
def add_around_each_hook(&block : Proc(Nil) ->) : Nil
|
||||
current_group.add_around_each_hook(block)
|
||||
@@stack.current.add_around_each_hook(block)
|
||||
end
|
||||
|
||||
# Adds a pre-condition to run at the start of every example in the current group.
|
||||
def add_pre_condition(&block : ->) : Nil
|
||||
current_group.add_pre_condition(block)
|
||||
@@stack.current.add_pre_condition(block)
|
||||
end
|
||||
|
||||
# Adds a post-condition to run at the end of every example in the current group.
|
||||
def add_post_condition(&block : ->) : Nil
|
||||
current_group.add_post_condition(block)
|
||||
@@stack.current.add_post_condition(block)
|
||||
end
|
||||
|
||||
# Builds the entire spec and returns it as a test suite.
|
||||
# This should be called only once after the entire spec has been defined.
|
||||
protected def build(filter : ExampleFilter) : TestSuite
|
||||
group = root_group.build(Internals::SampleValues.empty)
|
||||
group = @@stack.root.build(Internals::SampleValues.empty)
|
||||
TestSuite.new(group, filter)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1416,17 +1416,20 @@ module Spectator::DSL
|
|||
# Create the wrapper class for the test code.
|
||||
{% if block.is_a?(Nop) %}
|
||||
{% if what.is_a?(Call) %}
|
||||
_spectator_test(Test%example, %run) do
|
||||
def %run
|
||||
{{what}}
|
||||
end
|
||||
{% else %}
|
||||
{% raise "Unrecognized syntax: `it #{what}`" %}
|
||||
{% end %}
|
||||
{% else %}
|
||||
_spectator_test(Test%example, %run) {{block}}
|
||||
def %run
|
||||
{{block}}
|
||||
end
|
||||
{% end %}
|
||||
|
||||
# TODO
|
||||
%source = ::Spectator::Source.new({{_source_file}}, {{_source_line}})
|
||||
::Spectator::DSL::Builder.add_example({{what.stringify}}, %source, {{@type.name}}) { |test| test.as({{@type.name}}).%run }
|
||||
end
|
||||
|
||||
# Creates an example, or a test case.
|
||||
|
|
|
@ -2,7 +2,7 @@ require "./example_component"
|
|||
|
||||
module Spectator
|
||||
# Base class for all types of examples.
|
||||
# Concrete types must implement the `#run_impl, `#what`, `#instance`, and `#source` methods.
|
||||
# Concrete types must implement the `#run_impl` method.
|
||||
abstract class Example < ExampleComponent
|
||||
# Indicates whether the example has already been run.
|
||||
getter? finished = false
|
||||
|
@ -11,10 +11,16 @@ module Spectator
|
|||
getter group : ExampleGroup
|
||||
|
||||
# Retrieves the internal wrapped instance.
|
||||
abstract def instance
|
||||
private getter @test_wrapper : TestWrapper
|
||||
|
||||
# Source where the example originated from.
|
||||
abstract def source : Source
|
||||
def source
|
||||
@test_wrapper.source
|
||||
end
|
||||
|
||||
def what
|
||||
@test_wrapper.description
|
||||
end
|
||||
|
||||
# Runs the example code.
|
||||
# A result is returned, which represents the outcome of the test.
|
||||
|
@ -32,7 +38,7 @@ module Spectator
|
|||
|
||||
# Creates the base of the example.
|
||||
# The group should be the example group the example belongs to.
|
||||
def initialize(@group)
|
||||
def initialize(@group, @test_wrapper)
|
||||
end
|
||||
|
||||
# Indicates there is only one example to run.
|
||||
|
|
|
@ -3,6 +3,7 @@ module Spectator
|
|||
# This is used as the base node type for the composite design pattern.
|
||||
abstract class ExampleComponent
|
||||
# Text that describes the context or test.
|
||||
# TODO: Rename to description.
|
||||
abstract def what : String
|
||||
|
||||
# Indicates whether the example (or group) has been completely run.
|
||||
|
|
|
@ -15,13 +15,8 @@ module Spectator
|
|||
include Enumerable(ExampleComponent)
|
||||
include Iterable(ExampleComponent)
|
||||
|
||||
getter sample_values : Internals::SampleValues
|
||||
|
||||
# Creates the example group.
|
||||
# The hooks are stored to be triggered later.
|
||||
def initialize(@hooks : ExampleHooks, @conditions : ExampleConditions, @sample_values)
|
||||
@before_all_hooks_run = false
|
||||
@after_all_hooks_run = false
|
||||
def initialize
|
||||
end
|
||||
|
||||
# Retrieves the children in the group.
|
||||
|
@ -123,72 +118,5 @@ module Spectator
|
|||
def finished?
|
||||
children.all?(&.finished?)
|
||||
end
|
||||
|
||||
# Runs all of the "before-each" and "before-all" hooks.
|
||||
# This should run prior to every example in the group.
|
||||
def run_before_hooks
|
||||
run_before_all_hooks
|
||||
run_before_each_hooks
|
||||
end
|
||||
|
||||
# Runs all of the "before-all" hooks.
|
||||
# This should run prior to any examples in the group.
|
||||
# The hooks will be run only once.
|
||||
# Subsequent calls to this method will do nothing.
|
||||
protected def run_before_all_hooks : Nil
|
||||
return if @before_all_hooks_run
|
||||
@hooks.run_before_all
|
||||
@before_all_hooks_run = true
|
||||
end
|
||||
|
||||
# Runs all of the "before-each" hooks.
|
||||
# This method should run prior to every example in the group.
|
||||
protected def run_before_each_hooks : Nil
|
||||
@hooks.run_before_each
|
||||
end
|
||||
|
||||
# Runs all of the "after-all" and "after-each" hooks.
|
||||
# This should run following every example in the group.
|
||||
def run_after_hooks
|
||||
run_after_each_hooks
|
||||
run_after_all_hooks
|
||||
end
|
||||
|
||||
# Runs all of the "after-all" hooks.
|
||||
# This should run following all examples in the group.
|
||||
# The hooks will be run only once,
|
||||
# and only after all examples in the group have finished.
|
||||
# Subsequent calls after the hooks have been run will do nothing.
|
||||
protected def run_after_all_hooks(ignore_unfinished = false) : Nil
|
||||
return if @after_all_hooks_run
|
||||
return unless ignore_unfinished || finished?
|
||||
|
||||
@hooks.run_after_all
|
||||
@after_all_hooks_run = true
|
||||
end
|
||||
|
||||
# Runs all of the "after-each" hooks.
|
||||
# This method should run following every example in the group.
|
||||
protected def run_after_each_hooks : Nil
|
||||
@hooks.run_after_each
|
||||
end
|
||||
|
||||
# Creates a proc that runs the "around-each" hooks
|
||||
# in addition to a block passed to this method.
|
||||
# To call the block and all "around-each" hooks,
|
||||
# just invoke `Proc#call` on the returned proc.
|
||||
def wrap_around_each_hooks(&block : ->) : ->
|
||||
@hooks.wrap_around_each(&block)
|
||||
end
|
||||
|
||||
# Runs all of the pre-conditions for an example.
|
||||
def run_pre_conditions
|
||||
@conditions.run_pre_conditions
|
||||
end
|
||||
|
||||
# Runs all of the post-conditions for an example.
|
||||
def run_post_conditions
|
||||
@conditions.run_post_conditions
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,8 +18,7 @@ module Spectator
|
|||
# The parent's children must contain this group,
|
||||
# otherwise there may be unexpected behavior.
|
||||
# The *hooks* are stored to be triggered later.
|
||||
def initialize(@what, @parent, hooks : ExampleHooks, conditions : ExampleConditions, sample_values)
|
||||
super(hooks, conditions, sample_values)
|
||||
def initialize(@what, @parent)
|
||||
end
|
||||
|
||||
# Indicates wheter the group references a type.
|
||||
|
@ -27,69 +26,6 @@ module Spectator
|
|||
@what.is_a?(Symbol)
|
||||
end
|
||||
|
||||
# Runs all of the "before-all" hooks.
|
||||
# This should run prior to any examples in the group.
|
||||
# The hooks will be run only once.
|
||||
# Subsequent calls to this method will do nothing.
|
||||
# Parent "before-all" hooks will be run first.
|
||||
protected def run_before_all_hooks : Nil
|
||||
parent.run_before_all_hooks
|
||||
super
|
||||
end
|
||||
|
||||
# Runs all of the "before-each" hooks.
|
||||
# This method should run prior to every example in the group.
|
||||
# Parent "before-each" hooks will be run first.
|
||||
protected def run_before_each_hooks : Nil
|
||||
parent.run_before_each_hooks
|
||||
super
|
||||
end
|
||||
|
||||
# Runs all of the "after-all" hooks.
|
||||
# This should run following all examples in the group.
|
||||
# The hooks will be run only once,
|
||||
# and only after all examples in the group have finished.
|
||||
# Subsequent calls after the hooks have been run will do nothing.
|
||||
# Parent "after-all" hooks will be run last.
|
||||
protected def run_after_all_hooks(ignore_unfinished = false) : Nil
|
||||
super
|
||||
parent.run_after_all_hooks(ignore_unfinished)
|
||||
end
|
||||
|
||||
# Runs all of the "after-each" hooks.
|
||||
# This method should run following every example in the group.
|
||||
# Parent "after-each" hooks will be run last.
|
||||
protected def run_after_each_hooks : Nil
|
||||
super
|
||||
parent.run_after_each_hooks
|
||||
end
|
||||
|
||||
# Creates a proc that runs the "around-each" hooks
|
||||
# in addition to a block passed to this method.
|
||||
# To call the block and all `around_each` hooks,
|
||||
# just invoke `Proc#call` on the returned proc.
|
||||
# Parent "around-each" hooks will be in the outermost wrappings.
|
||||
def wrap_around_each_hooks(&block : ->) : ->
|
||||
wrapper = super(&block)
|
||||
parent.wrap_around_each_hooks(&wrapper)
|
||||
end
|
||||
|
||||
# Runs all of the pre-condition checks.
|
||||
# This method should run prior to every example in the group.
|
||||
# Parent pre-conditions will be checked first.
|
||||
def run_pre_conditions : Nil
|
||||
parent.run_pre_conditions
|
||||
super
|
||||
end
|
||||
|
||||
# Runs all of the post-condition checks.
|
||||
# This method should run following every example in the group.
|
||||
# Parent post-conditions will be checked last.
|
||||
def run_post_conditions : Nil
|
||||
super
|
||||
parent.run_post_conditions
|
||||
end
|
||||
|
||||
# Creates a string representation of the group.
|
||||
# The string consists of `#what` appended to the parent.
|
||||
# This results in a string like:
|
||||
|
|
|
@ -3,7 +3,7 @@ require "./example"
|
|||
module Spectator
|
||||
# Common class for all examples marked as pending.
|
||||
# This class will not run example code.
|
||||
abstract class PendingExample < Example
|
||||
class PendingExample < Example
|
||||
# Returns a pending result.
|
||||
private def run_impl
|
||||
PendingResult.new(self)
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
require "./example"
|
||||
|
||||
module Spectator
|
||||
# Common base for all examples that can be run.
|
||||
# This class includes all the logic for running example hooks,
|
||||
# Includes all the logic for running example hooks,
|
||||
# the example code, and capturing a result.
|
||||
# Sub-classes need to implement the `#what` and `#run_instance` methods.
|
||||
abstract class RunnableExample < Example
|
||||
class RunnableExample < Example
|
||||
# Runs the example, hooks, and captures the result
|
||||
# and translates to a usable result.
|
||||
def run_impl
|
||||
|
@ -14,25 +12,6 @@ module Spectator
|
|||
translate_result(result, expectations)
|
||||
end
|
||||
|
||||
# Runs the actual test code.
|
||||
private abstract def run_instance
|
||||
|
||||
# Runs the hooks that should be performed before starting the test code.
|
||||
private def run_before_hooks
|
||||
group.run_before_hooks
|
||||
rescue ex
|
||||
# If an error occurs in the before hooks, skip running the example.
|
||||
raise Exception.new("Error encountered while running before hooks", ex)
|
||||
end
|
||||
|
||||
# Runs the hooks that should be performed after the test code finishes.
|
||||
private def run_after_hooks
|
||||
group.run_after_hooks
|
||||
rescue ex
|
||||
# If an error occurs in the after hooks, elevate it to abort testing.
|
||||
raise Exception.new("Error encountered while running after hooks", ex)
|
||||
end
|
||||
|
||||
# Runs all hooks and the example code.
|
||||
# A captured result is returned.
|
||||
private def capture_result
|
||||
|
@ -40,9 +19,7 @@ module Spectator
|
|||
# Get the proc that will call around-each hooks and the example.
|
||||
wrapper = wrap_run_example(result)
|
||||
|
||||
run_before_hooks
|
||||
run_wrapper(wrapper)
|
||||
run_after_hooks
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -70,9 +47,7 @@ module Spectator
|
|||
# Capture how long it takes to run the test code.
|
||||
result.elapsed = Time.measure do
|
||||
begin
|
||||
group.run_pre_conditions
|
||||
run_instance # Actually run the example code.
|
||||
group.run_post_conditions
|
||||
test_wrapper.run {} # Actually run the example code.
|
||||
rescue ex # Catch all errors and handle them later.
|
||||
result.error = ex
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue