2018-09-15 04:17:16 +00:00
|
|
|
require "./example"
|
|
|
|
|
|
|
|
module Spectator
|
2018-10-10 22:57:43 +00:00
|
|
|
# Common base for all examples that can be run.
|
|
|
|
# This class includes all the logic for running example hooks,
|
|
|
|
# the example code, and capturing a result.
|
2018-10-15 21:27:05 +00:00
|
|
|
# Sub-classes need to implement the `#what` and `#run_instance` methods.
|
2018-09-15 04:17:16 +00:00
|
|
|
abstract class RunnableExample < Example
|
2018-10-11 16:16:48 +00:00
|
|
|
# Runs the example, hooks, and captures the result
|
|
|
|
# and translates to a usable result.
|
2018-10-20 00:50:21 +00:00
|
|
|
def run_impl : Result
|
2018-10-11 16:16:48 +00:00
|
|
|
result = capture_result
|
2018-11-14 04:44:25 +00:00
|
|
|
expectations = Internals::Harness.current.expectations
|
2018-10-05 22:30:19 +00:00
|
|
|
translate_result(result, expectations)
|
2018-09-15 04:17:16 +00:00
|
|
|
end
|
|
|
|
|
2018-10-10 22:57:43 +00:00
|
|
|
# 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
|
2019-01-02 00:48:07 +00:00
|
|
|
group.run_before_hooks
|
2018-10-11 16:16:48 +00:00
|
|
|
rescue ex
|
|
|
|
# If an error occurs in the before hooks, skip running the example.
|
|
|
|
raise Exception.new("Error encountered while running before hooks", ex)
|
2018-10-10 22:57:43 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# Runs the hooks that should be performed after the test code finishes.
|
2018-12-08 05:57:31 +00:00
|
|
|
private def run_after_hooks
|
2019-01-02 00:48:07 +00:00
|
|
|
group.run_after_hooks
|
2018-10-10 22:57:43 +00:00
|
|
|
rescue ex
|
2018-12-08 05:43:26 +00:00
|
|
|
# If an error occurs in the after hooks, elevate it to abort testing.
|
|
|
|
raise Exception.new("Error encountered while running after hooks", ex)
|
2018-10-11 16:16:48 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# Runs all hooks and the example code.
|
|
|
|
# A captured result is returned.
|
|
|
|
private def capture_result : ResultCapture
|
|
|
|
ResultCapture.new.tap do |result|
|
|
|
|
# Get the proc that will call around-each hooks and the example.
|
2018-10-20 00:50:21 +00:00
|
|
|
wrapper = wrap_run_example(result)
|
2018-12-08 06:01:02 +00:00
|
|
|
|
|
|
|
run_before_hooks
|
2018-12-08 06:28:30 +00:00
|
|
|
run_wrapper(wrapper)
|
2018-12-08 06:01:02 +00:00
|
|
|
run_after_hooks
|
2018-10-11 16:16:48 +00:00
|
|
|
end
|
2018-10-10 22:57:43 +00:00
|
|
|
end
|
|
|
|
|
2018-12-08 06:28:30 +00:00
|
|
|
private def run_wrapper(wrapper)
|
|
|
|
wrapper.call
|
|
|
|
rescue ex
|
|
|
|
# If an error occurs calling the wrapper,
|
|
|
|
# it means it came from the `around_each` hooks.
|
|
|
|
# This is because the test code is completely wrapped with a begin/rescue block.
|
|
|
|
raise Exception.new("Error encountered while running around hooks", ex)
|
|
|
|
end
|
|
|
|
|
2018-10-10 22:57:43 +00:00
|
|
|
# Creates a proc that runs the test code
|
|
|
|
# and captures the result.
|
2018-10-20 00:50:21 +00:00
|
|
|
private def wrap_run_example(result) : ->
|
2018-10-10 22:57:43 +00:00
|
|
|
# Wrap the method that runs and captures
|
|
|
|
# the test code with the around-each hooks.
|
2018-09-15 16:45:47 +00:00
|
|
|
group.wrap_around_each_hooks do
|
2018-10-11 16:16:48 +00:00
|
|
|
run_example(result)
|
2018-09-15 04:17:16 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-10-10 22:57:43 +00:00
|
|
|
# Runs the test code and captures the result.
|
2018-10-11 16:16:48 +00:00
|
|
|
private def run_example(result)
|
2018-10-10 22:57:43 +00:00
|
|
|
# Capture how long it takes to run the test code.
|
2018-09-15 04:17:16 +00:00
|
|
|
result.elapsed = Time.measure do
|
|
|
|
begin
|
2019-01-09 23:17:33 +00:00
|
|
|
group.run_pre_conditions
|
|
|
|
run_instance # Actually run the example code.
|
|
|
|
group.run_post_conditions
|
2018-10-10 22:57:43 +00:00
|
|
|
rescue ex # Catch all errors and handle them later.
|
2018-09-15 04:17:16 +00:00
|
|
|
result.error = ex
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-10-11 16:16:48 +00:00
|
|
|
# Creates a result instance from captured result information.
|
2018-10-05 22:30:19 +00:00
|
|
|
private def translate_result(result, expectations)
|
2018-09-15 04:17:16 +00:00
|
|
|
case (error = result.error)
|
|
|
|
when Nil
|
2018-10-19 20:02:27 +00:00
|
|
|
# If no errors occurred, then the example ran successfully.
|
2018-10-05 22:30:19 +00:00
|
|
|
SuccessfulResult.new(self, result.elapsed, expectations)
|
2018-09-15 17:05:11 +00:00
|
|
|
when ExpectationFailed
|
2018-10-19 20:02:27 +00:00
|
|
|
# If a required expectation fails, then a `ExpectationRailed` exception will be raised.
|
2018-10-05 22:30:19 +00:00
|
|
|
FailedResult.new(self, result.elapsed, expectations, error)
|
2018-09-15 04:17:16 +00:00
|
|
|
else
|
2018-10-19 20:02:27 +00:00
|
|
|
# Any other exception that is raised is unexpected and is an errored result.
|
2018-10-05 22:30:19 +00:00
|
|
|
ErroredResult.new(self, result.elapsed, expectations, error)
|
2018-09-15 04:17:16 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-10-10 22:57:43 +00:00
|
|
|
# Utility class for storing parts of the result while the example is running.
|
2018-09-15 04:17:16 +00:00
|
|
|
private class ResultCapture
|
2018-10-10 22:57:43 +00:00
|
|
|
# Length of time that it took to run the test code.
|
|
|
|
# This does not include hooks.
|
2018-09-15 20:10:59 +00:00
|
|
|
property elapsed = Time::Span.zero
|
2018-10-10 22:57:43 +00:00
|
|
|
|
|
|
|
# The error that occurred while running the test code.
|
|
|
|
# If no error occurred, this will be nil.
|
2018-09-15 04:17:16 +00:00
|
|
|
property error : Exception?
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|