mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Move test handling code to Harness
This commit is contained in:
parent
dba2e23750
commit
4230ec70a0
2 changed files with 76 additions and 72 deletions
|
@ -1,7 +1,7 @@
|
||||||
require "./example_context_delegate"
|
require "./example_context_delegate"
|
||||||
require "./example_group"
|
require "./example_group"
|
||||||
require "./example_node"
|
require "./example_node"
|
||||||
require "./pass_result"
|
require "./harness"
|
||||||
require "./pending_result"
|
require "./pending_result"
|
||||||
require "./result"
|
require "./result"
|
||||||
require "./source"
|
require "./source"
|
||||||
|
@ -9,6 +9,9 @@ require "./source"
|
||||||
module Spectator
|
module Spectator
|
||||||
# Standard example that runs a test case.
|
# Standard example that runs a test case.
|
||||||
class Example < ExampleNode
|
class Example < ExampleNode
|
||||||
|
# Currently running example.
|
||||||
|
class_getter! current : Example
|
||||||
|
|
||||||
# Indicates whether the example already ran.
|
# Indicates whether the example already ran.
|
||||||
getter? finished : Bool = false
|
getter? finished : Bool = false
|
||||||
|
|
||||||
|
@ -41,9 +44,13 @@ module Spectator
|
||||||
# Returns the result of the execution.
|
# Returns the result of the execution.
|
||||||
# The result will also be stored in `#result`.
|
# The result will also be stored in `#result`.
|
||||||
def run : Result
|
def run : Result
|
||||||
runner = Runner.new(self, @delegate)
|
@@current = self
|
||||||
|
Log.debug { "Running example #{self}" }
|
||||||
|
Log.warn { "Example #{self} running more than once" } if @finished
|
||||||
|
@result = Harness.run { @delegate.call(self) }
|
||||||
|
ensure
|
||||||
|
@@current = nil
|
||||||
@finished = true
|
@finished = true
|
||||||
@result = runner.run
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Exposes information about the example useful for debugging.
|
# Exposes information about the example useful for debugging.
|
||||||
|
@ -61,24 +68,5 @@ module Spectator
|
||||||
|
|
||||||
io << result
|
io << result
|
||||||
end
|
end
|
||||||
|
|
||||||
# Logic dedicated to running an example and necessary hooks.
|
|
||||||
# This type does not directly modify or mutate state in the `Example` class.
|
|
||||||
private struct Runner
|
|
||||||
# Creates the runner.
|
|
||||||
# *example* is the example being tested.
|
|
||||||
# The *delegate* is the entrypoint of the example's test code.
|
|
||||||
def initialize(@example : Example, @delegate : ExampleContextDelegate)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Executes the example's test code and produces a result.
|
|
||||||
def run : Result
|
|
||||||
Log.debug { "Running example #{@example}" }
|
|
||||||
elapsed = Time.measure do
|
|
||||||
@delegate.call(@example)
|
|
||||||
end
|
|
||||||
PassResult.new(elapsed)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,78 +1,94 @@
|
||||||
require "./mocks/registry"
|
require "./error_result"
|
||||||
|
require "./pass_result"
|
||||||
|
require "./result"
|
||||||
|
|
||||||
module Spectator
|
module Spectator
|
||||||
# Helper class that acts as a gateway between example code and the test framework.
|
# Helper class that acts as a gateway between test code and the framework.
|
||||||
# Every example must be invoked by passing it to `#run`.
|
# This is essentially an "example runner."
|
||||||
# This sets up the harness so that the example code can use it.
|
#
|
||||||
# The test framework does the following:
|
# Test code should be wrapped with a call to `.run`.
|
||||||
|
# This class will catch all errors raised by the test code.
|
||||||
|
# Errors caused by failed assertions (`AssertionFailed`) are translated to failed results (`FailResult`).
|
||||||
|
# Errors not caused by assertions are translated to error results (`ErrorResult`).
|
||||||
|
#
|
||||||
|
# Every runnable example should invoke the test code by calling `.run`.
|
||||||
|
# This sets up the harness so that the test code can use it.
|
||||||
|
# The framework does the following:
|
||||||
# ```
|
# ```
|
||||||
# result = Harness.run(example)
|
# result = Harness.run { delegate.call(example) }
|
||||||
# # Do something with the result.
|
# # Do something with the result.
|
||||||
# ```
|
# ```
|
||||||
# Then from the example code, the harness can be accessed via `#current` like so:
|
# Then from the test code, the harness can be accessed via `.current` like so:
|
||||||
# ```
|
# ```
|
||||||
# harness = ::Spectator::Harness.current
|
# harness = ::Spectator::Harness.current
|
||||||
# # Do something with the harness.
|
# # Do something with the harness.
|
||||||
# ```
|
# ```
|
||||||
# Of course, the end-user shouldn't see this or work directly with the harness.
|
# Of course, the end-user shouldn't see this or work directly with the harness.
|
||||||
# Instead, methods the user calls can access it.
|
# Instead, methods the user calls can access it.
|
||||||
# For instance, an expectation reporting a result.
|
# For instance, an assertion reporting a result.
|
||||||
class Harness
|
private class Harness
|
||||||
# Retrieves the harness for the current running example.
|
# Retrieves the harness for the current running example.
|
||||||
class_getter! current : self
|
class_getter! current : self
|
||||||
|
|
||||||
# Wraps an example with a harness and runs the example.
|
# Wraps an example with a harness and runs test code.
|
||||||
# The `#current` harness will be set
|
# A block provided to this method is considered to be the test code.
|
||||||
# prior to running the example, and reset after.
|
# The value of `.current` is set to the harness for the duration of the test.
|
||||||
# The *example* argument will be the example to run.
|
# It will be reset after the test regardless of the outcome.
|
||||||
# The result returned from `Example#run` will be returned.
|
# The result of running the test code will be returned.
|
||||||
def self.run(example : Example) : Result
|
def self.run : Result
|
||||||
@@current = new(example)
|
harness = new
|
||||||
example.run
|
previous = @@current
|
||||||
ensure
|
@@current = harness
|
||||||
@@current = nil
|
result = harness.run { yield }
|
||||||
|
@@current = previous
|
||||||
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
# Retrieves the current running example.
|
@deferred = Deque(->).new
|
||||||
getter example : Example
|
|
||||||
|
|
||||||
getter mocks : Mocks::Registry
|
# Runs test code and produces a result based on the outcome.
|
||||||
|
# The test code should be called from within the block given to this method.
|
||||||
# Retrieves the group for the current running example.
|
def run : Result
|
||||||
def group
|
outcome = capture { yield }
|
||||||
example.group
|
run_deferred # TODO: Handle errors in deferred blocks.
|
||||||
|
translate(*outcome)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Reports the outcome of an expectation.
|
# Stores a block of code to be executed later.
|
||||||
# An exception will be raised when a failing result is given.
|
# All deferred blocks run just before the `#run` method completes.
|
||||||
def report_expectation(expectation : Expectations::Expectation) : Nil
|
def defer(&block) : Nil
|
||||||
@example.description = expectation.description unless @example.test_wrapper.description?
|
|
||||||
@reporter.report(expectation)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Generates the reported expectations from the example.
|
|
||||||
# This should be run after the example has finished.
|
|
||||||
def expectations : Expectations::ExampleExpectations
|
|
||||||
@reporter.expectations
|
|
||||||
end
|
|
||||||
|
|
||||||
# Marks a block of code to run later.
|
|
||||||
def defer(&block : ->) : Nil
|
|
||||||
@deferred << block
|
@deferred << block
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Yields to run the test code and returns information about the outcome.
|
||||||
|
# Returns a tuple with the elapsed time and an error if one occurred (otherwise nil).
|
||||||
|
private def capture
|
||||||
|
error = nil.as(Exception?)
|
||||||
|
elapsed = Time.measure do
|
||||||
|
begin
|
||||||
|
yield
|
||||||
|
rescue e
|
||||||
|
error = e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
{elapsed, error}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Translates the outcome of running a test to a result.
|
||||||
|
# Takes the *elapsed* time and a possible *error* from the test.
|
||||||
|
# Returns a type of `Result`.
|
||||||
|
private def translate(elapsed, error) : Result
|
||||||
|
if error
|
||||||
|
ErrorResult.new(elapsed, error)
|
||||||
|
else
|
||||||
|
PassResult.new(elapsed)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Runs all deferred blocks.
|
# Runs all deferred blocks.
|
||||||
def run_deferred : Nil
|
private def run_deferred : Nil
|
||||||
@deferred.each(&.call)
|
@deferred.each(&.call)
|
||||||
@deferred.clear
|
@deferred.clear
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates a new harness.
|
|
||||||
# The example the harness is for should be passed in.
|
|
||||||
private def initialize(@example)
|
|
||||||
@reporter = Expectations::ExpectationReporter.new
|
|
||||||
@mocks = Mocks::Registry.new(@example.group.context)
|
|
||||||
@deferred = Deque(->).new
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue