Some initial spec builder code

This commit is contained in:
Michael Miller 2020-09-12 18:37:14 -06:00
parent 0190cc7260
commit 67ac06e4d6
No known key found for this signature in database
GPG key ID: FB9F12F7C646A4AD
5 changed files with 84 additions and 111 deletions

View file

@ -0,0 +1,17 @@
require "../spec/builder"
module Spectator::DSL
module Builder
extend self
@@builder = Spec::Builder.new
def start_group(*args)
@@builder.start_group(*args)
end
def end_group(*args)
@@builder.end_group(*args)
end
end
end

View file

@ -7,4 +7,3 @@
require "./config"
require "./config_builder"
require "./dsl"
require "./spec_builder"

6
src/spectator/spec.cr Normal file
View file

@ -0,0 +1,6 @@
require "./spec/*"
module Spectator
class Spec
end
end

View file

@ -0,0 +1,61 @@
require "../example"
require "../example_group"
module Spectator
class Spec::Builder
# Stack tracking the current group.
# The bottom of the stack (first element) is the root group.
# The root group should never be removed.
# The top of the stack (last element) is the current group.
# New examples should be added to the current group.
@group_stack : Deque(ExampleGroup)
def initialize
root_group = ExampleGroup.new
@group_stack = Deque(ExampleGroup).new
@group_stack.push(root_group)
end
def add_example
raise NotImplementedError.new("#add_example")
end
def start_group(name, source = nil) : ExampleGroup
{% if flag?(:spectator_debug) %}
puts "Start group: #{name.inspect} @ #{source}"
{% end %}
ExampleGroup.new(name, source, current_group).tap do |group|
@group_stack << group
end
end
def end_group
{% if flag?(:spectator_debug) %}
puts "End group: #{current_group}"
{% end %}
raise "Can't pop root group" if root?
@group_stack.pop
end
def build
raise NotImplementedError.new("#build")
end
# Checks if the current group is the root group.
private def root?
@group_stack.size == 1
end
# Retrieves the root group.
private def root_group
@group_stack.first
end
# Retrieves the current group.
# This is the group that new examples should be added to.
private def current_group
@group_stack.last
end
end
end

View file

@ -1,110 +0,0 @@
require "./spec_builder/*"
module Spectator
# Global builder used to create the runtime instance of the spec.
# The DSL methods call into this module to generate parts of the spec.
# Once the DSL is done, the `#build` method can be invoked
# to create the entire spec as a runtime instance.
module SpecBuilder
extend self
@@stack = ExampleGroupStack.new
# Begins a new nested group in the spec.
# A corresponding `#end_group` call must be made
# when the group being started is finished.
# See `NestedExampleGroupBuilder#initialize` for the arguments
# as arguments to this method are passed directly to it.
def start_group(*args) : Nil
group = NestedExampleGroupBuilder.new(*args)
@@stack.push(group)
end
# Begins a new sample group in the spec -
# that is, a group defined by the `StructureDSL#sample` macro in the DSL.
# A corresponding `#end_group` call must be made
# when the group being started is finished.
# See `SampleExampleGroupBuilder#initialize` for the arguments
# as arguments to this method are passed directly to it.
def start_sample_group(*args, &block : TestValues -> Array(T)) : Nil forall T
group = SampleExampleGroupBuilder(T).new(*args, block)
@@stack.push(group)
end
# Marks the end of a group in the spec.
# This must be called for every `#start_group` and `#start_sample_group` call.
# It is also important to line up the start and end calls.
# Otherwise examples might get placed into wrong groups.
def end_group : Nil
@@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(description : String?, source : Source,
example_type : ::SpectatorTest.class, &runner : ::SpectatorTest ->) : Nil
builder = ->(values : TestValues) { example_type.new(values).as(::SpectatorTest) }
factory = RunnableExampleBuilder.new(description, source, builder, runner)
@@stack.current.add_child(factory)
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_pending_example(description : String?, source : Source,
example_type : ::SpectatorTest.class, &runner : ::SpectatorTest ->) : Nil
builder = ->(values : TestValues) { example_type.new(values).as(::SpectatorTest) }
factory = PendingExampleBuilder.new(description, source, builder, runner)
@@stack.current.add_child(factory)
end
# Adds a block of code to run before all examples in the current group.
def add_before_all_hook(&block : ->) : Nil
@@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 : TestMetaMethod) : Nil
@@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
@@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 : TestMetaMethod) : Nil
@@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 hook as an argument.
# It is expected that the block will call the hook.
def add_around_each_hook(&block : ::SpectatorTest, Proc(Nil) ->) : Nil
@@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 : TestMetaMethod) : Nil
@@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 : TestMetaMethod) : Nil
@@stack.current.add_post_condition(block)
end
def add_default_stub(*args) : Nil
@@stack.current.add_default_stub(*args)
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 = @@stack.root.build
TestSuite.new(group, filter)
end
end
end