Merge remote-tracking branch 'origin/release/0.9' into mocks-and-doubles

This commit is contained in:
Michael Miller 2019-09-27 14:12:29 -06:00
commit 8c180e818f
88 changed files with 838 additions and 9229 deletions

View file

@ -1,33 +0,0 @@
require "./spec_helper"
describe Spectator::CompositeExampleFilter do
describe "#includes?" do
context "with a matching filter" do
it "is true" do
example = PassingExample.create
filters = [Spectator::NullExampleFilter.new.as(Spectator::ExampleFilter)]
filter = Spectator::CompositeExampleFilter.new(filters)
filter.includes?(example).should be_true
end
end
context "with a non-matching filter" do
it "is false" do
example = PassingExample.create
source = Spectator::Source.new(__FILE__, __LINE__)
filters = [Spectator::SourceExampleFilter.new(source).as(Spectator::ExampleFilter)]
filter = Spectator::CompositeExampleFilter.new(filters)
filter.includes?(example).should be_false
end
end
context "with no filters" do
it "is false" do
example = PassingExample.create
filters = [] of Spectator::ExampleFilter
filter = Spectator::CompositeExampleFilter.new(filters)
filter.includes?(example).should be_false
end
end
end
end

View file

@ -1,27 +0,0 @@
require "../spec_helper"
describe Spectator::DSL::ExampleFactory do
describe "#build" do
it "creates an example of the correct type" do
factory = Spectator::DSL::ExampleFactory.new(SpyExample)
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
example = factory.build(group, Spectator::Internals::SampleValues.empty)
example.should be_a(SpyExample)
end
it "passes along the group" do
factory = Spectator::DSL::ExampleFactory.new(SpyExample)
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
example = factory.build(group, Spectator::Internals::SampleValues.empty)
example.group.should eq(group)
end
it "passes along the sample values" do
factory = Spectator::DSL::ExampleFactory.new(SpyExample)
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
values = Spectator::Internals::SampleValues.empty.add(:foo, "foo", 12345)
example = factory.build(group, values)
example.as(SpyExample).sample_values.should eq(values)
end
end
end

View file

@ -1,234 +0,0 @@
require "../spec_helper"
describe Spectator::DSL::NestedExampleGroupBuilder do
describe "#add_child" do
it "creates the correct number of children" do
builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
3.times do
factory = Spectator::DSL::ExampleFactory.new(PassingExample)
group_builder = Spectator::DSL::NestedExampleGroupBuilder.new("bar")
builder.add_child(factory)
builder.add_child(group_builder)
end
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.children.size.should eq(6)
end
context "with an ExampleFactory" do
it "creates the example" do
builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
factory = Spectator::DSL::ExampleFactory.new(PassingExample)
builder.add_child(factory)
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.children.first.should be_a(PassingExample)
end
end
context "with an ExampleGroupBuilder" do
it "creates the group" do
builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
group_builder = Spectator::DSL::NestedExampleGroupBuilder.new("bar")
builder.add_child(group_builder)
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.children.first.should be_a(Spectator::NestedExampleGroup)
end
end
end
describe "#add_before_all_hook" do
it "adds a hook" do
hook_called = false
builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
builder.add_before_all_hook(->{
hook_called = true
})
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.run_before_hooks
hook_called.should eq(true)
end
it "supports multiple hooks" do
call_count = 0
builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
5.times do |i|
builder.add_before_all_hook(->{
call_count += i + 1
})
end
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.run_before_hooks
call_count.should eq(15)
end
end
describe "#add_before_each_hook" do
it "adds a hook" do
hook_called = false
builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
builder.add_before_each_hook(->{
hook_called = true
})
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.run_before_hooks
hook_called.should eq(true)
end
it "supports multiple hooks" do
call_count = 0
builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
5.times do |i|
builder.add_before_each_hook(->{
call_count += i + 1
})
end
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.run_before_hooks
call_count.should eq(15)
end
end
describe "#add_after_all_hook" do
it "adds a hook" do
hook_called = false
builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
builder.add_after_all_hook(->{
hook_called = true
})
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.run_after_hooks
hook_called.should eq(true)
end
it "supports multiple hooks" do
call_count = 0
builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
5.times do |i|
builder.add_after_all_hook(->{
call_count += i + 1
})
end
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.run_after_hooks
call_count.should eq(15)
end
end
describe "#add_after_each_hook" do
it "adds a hook" do
hook_called = false
builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
builder.add_after_each_hook(->{
hook_called = true
})
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.run_after_hooks
hook_called.should eq(true)
end
it "supports multiple hooks" do
call_count = 0
builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
5.times do |i|
builder.add_after_each_hook(->{
call_count += i + 1
})
end
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.run_after_hooks
call_count.should eq(15)
end
end
describe "#add_around_each_hook" do
it "adds a hook" do
hook_called = false
builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
builder.add_around_each_hook(->(_proc : ->) {
hook_called = true
})
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
proc = group.wrap_around_each_hooks { }
proc.call
hook_called.should eq(true)
end
it "supports multiple hooks" do
call_count = 0
builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
5.times do |i|
builder.add_around_each_hook(->(proc : ->) {
call_count += i + 1
proc.call
})
end
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
proc = group.wrap_around_each_hooks { }
proc.call
call_count.should eq(15)
end
end
describe "#build" do
it "passes along the what value" do
what = "TEST"
builder = Spectator::DSL::NestedExampleGroupBuilder.new(what)
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.what.should eq(what)
end
it "passes along the parent" do
factory = Spectator::DSL::ExampleFactory.new(SpyExample)
builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
builder.add_child(factory)
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.parent.should be(root)
end
it "passes along the sample values" do
factory = Spectator::DSL::ExampleFactory.new(SpyExample)
builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
builder.add_child(factory)
values = Spectator::Internals::SampleValues.empty.add(:foo, "foo", 12345)
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, values)
group.children.first.as(SpyExample).sample_values.should eq(values)
end
it "specifies the parent of the children correctly" do
builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
3.times do
factory = Spectator::DSL::ExampleFactory.new(PassingExample)
group_builder = Spectator::DSL::NestedExampleGroupBuilder.new("bar")
builder.add_child(factory)
builder.add_child(group_builder)
end
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.children.all? do |child|
case (child)
when Spectator::Example
child.group == group
when Spectator::NestedExampleGroup
child.parent == group
else
false
end
end.should be_true
end
end
end

View file

@ -1,202 +0,0 @@
require "../spec_helper"
describe Spectator::DSL::RootExampleGroupBuilder do
describe "#add_child" do
it "creates the correct number of children" do
builder = Spectator::DSL::RootExampleGroupBuilder.new
3.times do
factory = Spectator::DSL::ExampleFactory.new(PassingExample)
group_builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
builder.add_child(factory)
builder.add_child(group_builder)
end
group = builder.build(Spectator::Internals::SampleValues.empty)
group.children.size.should eq(6)
end
context "with an ExampleFactory" do
it "creates the example" do
builder = Spectator::DSL::RootExampleGroupBuilder.new
factory = Spectator::DSL::ExampleFactory.new(PassingExample)
builder.add_child(factory)
group = builder.build(Spectator::Internals::SampleValues.empty)
group.children.first.should be_a(PassingExample)
end
end
context "with an ExampleGroupBuilder" do
it "creates the group" do
builder = Spectator::DSL::RootExampleGroupBuilder.new
group_builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
builder.add_child(group_builder)
group = builder.build(Spectator::Internals::SampleValues.empty)
group.children.first.should be_a(Spectator::NestedExampleGroup)
end
end
end
describe "#add_before_all_hook" do
it "adds a hook" do
hook_called = false
builder = Spectator::DSL::RootExampleGroupBuilder.new
builder.add_before_all_hook(->{
hook_called = true
})
group = builder.build(Spectator::Internals::SampleValues.empty)
group.run_before_hooks
hook_called.should eq(true)
end
it "supports multiple hooks" do
call_count = 0
builder = Spectator::DSL::RootExampleGroupBuilder.new
5.times do |i|
builder.add_before_all_hook(->{
call_count += i + 1
})
end
group = builder.build(Spectator::Internals::SampleValues.empty)
group.run_before_hooks
call_count.should eq(15)
end
end
describe "#add_before_each_hook" do
it "adds a hook" do
hook_called = false
builder = Spectator::DSL::RootExampleGroupBuilder.new
builder.add_before_each_hook(->{
hook_called = true
})
group = builder.build(Spectator::Internals::SampleValues.empty)
group.run_before_hooks
hook_called.should eq(true)
end
it "supports multiple hooks" do
call_count = 0
builder = Spectator::DSL::RootExampleGroupBuilder.new
5.times do |i|
builder.add_before_each_hook(->{
call_count += i + 1
})
end
group = builder.build(Spectator::Internals::SampleValues.empty)
group.run_before_hooks
call_count.should eq(15)
end
end
describe "#add_after_all_hook" do
it "adds a hook" do
hook_called = false
builder = Spectator::DSL::RootExampleGroupBuilder.new
builder.add_after_all_hook(->{
hook_called = true
})
group = builder.build(Spectator::Internals::SampleValues.empty)
group.run_after_hooks
hook_called.should eq(true)
end
it "supports multiple hooks" do
call_count = 0
builder = Spectator::DSL::RootExampleGroupBuilder.new
5.times do |i|
builder.add_after_all_hook(->{
call_count += i + 1
})
end
group = builder.build(Spectator::Internals::SampleValues.empty)
group.run_after_hooks
call_count.should eq(15)
end
end
describe "#add_after_each_hook" do
it "adds a hook" do
hook_called = false
builder = Spectator::DSL::RootExampleGroupBuilder.new
builder.add_after_each_hook(->{
hook_called = true
})
group = builder.build(Spectator::Internals::SampleValues.empty)
group.run_after_hooks
hook_called.should eq(true)
end
it "supports multiple hooks" do
call_count = 0
builder = Spectator::DSL::RootExampleGroupBuilder.new
5.times do |i|
builder.add_after_each_hook(->{
call_count += i + 1
})
end
group = builder.build(Spectator::Internals::SampleValues.empty)
group.run_after_hooks
call_count.should eq(15)
end
end
describe "#add_around_each_hook" do
it "adds a hook" do
hook_called = false
builder = Spectator::DSL::RootExampleGroupBuilder.new
builder.add_around_each_hook(->(_proc : ->) {
hook_called = true
})
group = builder.build(Spectator::Internals::SampleValues.empty)
proc = group.wrap_around_each_hooks { }
proc.call
hook_called.should eq(true)
end
it "supports multiple hooks" do
call_count = 0
builder = Spectator::DSL::RootExampleGroupBuilder.new
5.times do |i|
builder.add_around_each_hook(->(proc : ->) {
call_count += i + 1
proc.call
})
end
group = builder.build(Spectator::Internals::SampleValues.empty)
proc = group.wrap_around_each_hooks { }
proc.call
call_count.should eq(15)
end
end
describe "#build" do
it "passes along the sample values" do
factory = Spectator::DSL::ExampleFactory.new(SpyExample)
builder = Spectator::DSL::RootExampleGroupBuilder.new
builder.add_child(factory)
values = Spectator::Internals::SampleValues.empty.add(:foo, "foo", 12345)
group = builder.build(values)
group.children.first.as(SpyExample).sample_values.should eq(values)
end
it "specifies the parent of the children correctly" do
builder = Spectator::DSL::RootExampleGroupBuilder.new
3.times do
factory = Spectator::DSL::ExampleFactory.new(PassingExample)
group_builder = Spectator::DSL::NestedExampleGroupBuilder.new("foo")
builder.add_child(factory)
builder.add_child(group_builder)
end
group = builder.build(Spectator::Internals::SampleValues.empty)
group.children.all? do |child|
case (child)
when Spectator::Example
child.group == group
when Spectator::NestedExampleGroup
child.parent == group
else
false
end
end.should be_true
end
end
end

View file

@ -1,350 +0,0 @@
require "../spec_helper"
SAMPLE_VALUES_COLLECTION = %i[foo bar baz]
struct SampleValueCollection
def initialize(sample_values : ::Spectator::Internals::SampleValues)
end
def create
SAMPLE_VALUES_COLLECTION
end
end
describe Spectator::DSL::SampleExampleGroupBuilder do
describe "#add_child" do
it "creates the correct number of children" do
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
count = 4
count.times do
factory = Spectator::DSL::ExampleFactory.new(PassingExample)
group_builder = Spectator::DSL::NestedExampleGroupBuilder.new("bar")
builder.add_child(factory)
builder.add_child(group_builder)
end
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
all_children = group.map { |child| child.as(Spectator::ExampleGroup).to_a }.flatten
all_children.size.should eq(2 * count * SAMPLE_VALUES_COLLECTION.size)
end
context "with an ExampleFactory" do
it "creates an example for each item in the collection" do
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
factory = Spectator::DSL::ExampleFactory.new(PassingExample)
builder.add_child(factory)
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
all_children = group.map { |child| child.as(Spectator::ExampleGroup).to_a }.flatten
all_children.all? { |child| child.is_a?(PassingExample) }.should be_true
end
end
context "with an ExampleGroupBuilder" do
it "creates a group for each item in the collection" do
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
group_builder = Spectator::DSL::NestedExampleGroupBuilder.new("bar")
builder.add_child(group_builder)
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
all_children = group.map { |child| child.as(Spectator::ExampleGroup).to_a }.flatten
all_children.all? { |child| child.is_a?(Spectator::NestedExampleGroup) }.should be_true
end
end
end
describe "#add_before_all_hook" do
it "adds a hook" do
hook_called = false
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
builder.add_before_all_hook(->{
hook_called = true
})
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.run_before_hooks
hook_called.should eq(true)
end
it "attachs the hook to just the top-level group" do
call_count = 0
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
builder.add_before_all_hook(->{
call_count += 1
})
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.children.map(&.as(Spectator::ExampleGroup)).each(&.run_before_hooks)
call_count.should eq(1)
end
it "supports multiple hooks" do
call_count = 0
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
5.times do |i|
builder.add_before_all_hook(->{
call_count += i + 1
})
end
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.run_before_hooks
call_count.should eq(15)
end
end
describe "#add_before_each_hook" do
it "adds a hook" do
hook_called = false
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
builder.add_before_each_hook(->{
hook_called = true
})
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.run_before_hooks
hook_called.should eq(true)
end
it "attachs the hook to just the top-level group" do
call_count = 0
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
builder.add_before_each_hook(->{
call_count += 1
})
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.children.map(&.as(Spectator::ExampleGroup)).each(&.run_before_hooks)
call_count.should eq(SAMPLE_VALUES_COLLECTION.size)
end
it "supports multiple hooks" do
call_count = 0
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
5.times do |i|
builder.add_before_each_hook(->{
call_count += i + 1
})
end
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.run_before_hooks
call_count.should eq(15)
end
end
describe "#add_after_all_hook" do
it "adds a hook" do
hook_called = false
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
builder.add_after_all_hook(->{
hook_called = true
})
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.run_after_hooks
hook_called.should eq(true)
end
it "attachs the hook to just the top-level group" do
call_count = 0
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
builder.add_after_all_hook(->{
call_count += 1
})
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.children.map(&.as(Spectator::ExampleGroup)).each(&.run_after_hooks)
call_count.should eq(1)
end
it "supports multiple hooks" do
call_count = 0
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
5.times do |i|
builder.add_after_all_hook(->{
call_count += i + 1
})
end
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.run_after_hooks
call_count.should eq(15)
end
end
describe "#add_after_each_hook" do
it "adds a hook" do
hook_called = false
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
builder.add_after_each_hook(->{
hook_called = true
})
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.run_after_hooks
hook_called.should eq(true)
end
it "attachs the hook to just the top-level group" do
call_count = 0
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
builder.add_after_each_hook(->{
call_count += 1
})
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.children.map(&.as(Spectator::ExampleGroup)).each(&.run_after_hooks)
call_count.should eq(SAMPLE_VALUES_COLLECTION.size)
end
it "supports multiple hooks" do
call_count = 0
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
5.times do |i|
builder.add_after_each_hook(->{
call_count += i + 1
})
end
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.run_after_hooks
call_count.should eq(15)
end
end
describe "#build" do
it "passes along the what value" do
what = "TEST"
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new(what, SampleValueCollection, create_proc, "value", :foo)
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.what.should eq(what)
end
it "passes along the parent" do
factory = Spectator::DSL::ExampleFactory.new(SpyExample)
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
builder.add_child(factory)
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.parent.should be(root)
end
it "passes along the sample values" do
factory = Spectator::DSL::ExampleFactory.new(SpyExample)
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
builder.add_child(factory)
symbol = :test
values = Spectator::Internals::SampleValues.empty.add(symbol, "foo", 12345)
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, values)
all_children = group.map { |child| child.as(Spectator::ExampleGroup).to_a }.flatten
all_children.map(&.as(SpyExample)).all? { |child| child.sample_values.get_wrapper(symbol) }.should be_true
end
it "passes along the value name" do
symbol = :foo
name = "value"
factory = Spectator::DSL::ExampleFactory.new(SpyExample)
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, name, symbol)
builder.add_child(factory)
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
all_children = group.map { |child| child.as(Spectator::ExampleGroup).to_a }.flatten
all_children.each do |child|
entries = child.as(SpyExample).sample_values.map(&.name)
entries.should contain(name)
end
end
it "creates the correct number of sub-groups" do
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
factory = Spectator::DSL::ExampleFactory.new(PassingExample)
builder.add_child(factory)
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.children.size.should eq(SAMPLE_VALUES_COLLECTION.size)
end
it "passes the correct value to each sub-group" do
factory = Spectator::DSL::ExampleFactory.new(SpyExample)
symbol = :test
count = 3
expected = Array.new(SAMPLE_VALUES_COLLECTION.size * count) { |i| SAMPLE_VALUES_COLLECTION[i // count] }
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", symbol)
count.times { builder.add_child(factory) }
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
all_children = group.map { |child| child.as(Spectator::ExampleGroup).to_a }.flatten
all_children.map { |child| child.as(SpyExample).sample_values.get_value(symbol, typeof(SAMPLE_VALUES_COLLECTION.first)) }.should eq(expected)
end
it "specifies the parent of the children correctly" do
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
3.times do
factory = Spectator::DSL::ExampleFactory.new(PassingExample)
group_builder = Spectator::DSL::NestedExampleGroupBuilder.new("baz")
builder.add_child(factory)
builder.add_child(group_builder)
end
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.children.all? do |child|
case (child)
when Spectator::Example
child.group == group
when Spectator::NestedExampleGroup
child.parent == group
else
false
end
end.should be_true
end
it "specifies the container for the parent of the sub-groups" do
create_proc = ->(s : SampleValueCollection) { s.create }
builder = Spectator::DSL::SampleExampleGroupBuilder.new("foobar", SampleValueCollection, create_proc, "value", :foo)
3.times do
factory = Spectator::DSL::ExampleFactory.new(PassingExample)
group_builder = Spectator::DSL::NestedExampleGroupBuilder.new("baz")
builder.add_child(factory)
builder.add_child(group_builder)
end
root = Spectator::DSL::RootExampleGroupBuilder.new.build(Spectator::Internals::SampleValues.empty)
group = builder.build(root, Spectator::Internals::SampleValues.empty)
group.children.all? do |child|
case (child)
when Spectator::Example
child.group == group
when Spectator::NestedExampleGroup
child.parent == group
else
false
end
end.should be_true
end
end
end

View file

@ -1,87 +0,0 @@
require "./spec_helper"
def new_errored_result(
example : Spectator::Example? = nil,
elapsed : Time::Span? = nil,
expectations : Spectator::Expectations::ExampleExpectations? = nil,
error : Exception? = nil
)
Spectator::ErroredResult.new(
example || FailingExample.create,
elapsed || Time::Span.zero,
expectations || Spectator::Expectations::ExampleExpectations.new(generate_expectations(0, 1)[:expectations]),
error || Exception.new("foobar")
)
end
describe Spectator::ErroredResult do
describe "#call" do
context "without a block" do
it "invokes #error on an instance" do
spy = ResultCallSpy.new
new_errored_result.call(spy)
spy.error?.should be_true
end
it "returns the value of #failure" do
result = new_errored_result
returned = result.call(ResultCallSpy.new)
returned.should eq(:error)
end
end
context "with a block" do
it "invokes #error on an instance" do
spy = ResultCallSpy.new
new_errored_result.call(spy) { nil }
spy.error?.should be_true
end
it "yields itself" do
result = new_errored_result
value = nil.as(Spectator::Result?)
result.call(ResultCallSpy.new) { |r| value = r }
value.should eq(result)
end
it "returns the value of #failure" do
result = new_errored_result
value = 42
returned = result.call(ResultCallSpy.new) { value }
returned.should eq(value)
end
end
end
describe "#example" do
it "is the expected value" do
example = FailingExample.create
result = new_errored_result(example: example)
result.example.should eq(example)
end
end
describe "#elapsed" do
it "is the expected value" do
elapsed = Time::Span.new(10, 10, 10)
result = new_errored_result(elapsed: elapsed)
result.elapsed.should eq(elapsed)
end
end
describe "#expectations" do
it "is the expected value" do
expectations = Spectator::Expectations::ExampleExpectations.new(generate_expectations(5, 1)[:expectations])
result = new_errored_result(expectations: expectations)
result.expectations.should eq(expectations)
end
end
describe "#error" do
it "is the expected value" do
error = IO::Error.new("oops")
result = new_errored_result(error: error)
result.error.should eq(error)
end
end
end

View file

@ -1,36 +0,0 @@
require "./spec_helper"
describe Spectator::ExampleConditions do
{% for condition in %i[pre post] %}
describe "#run_{{condition.id}}_conditions" do
it "calls a proc" do
called = false
conditions = new_conditions({{condition.id}}: ->{ called = true; nil })
conditions.run_{{condition.id}}_conditions
called.should be_true
end
it "calls multiple procs" do
call_count = 0
conditions = new_conditions({{condition.id}}: [
->{ call_count += 1; nil },
->{ call_count += 2; nil },
->{ call_count += 3; nil },
])
conditions.run_{{condition.id}}_conditions
call_count.should eq(6)
end
it "calls procs in the correct order" do
calls = [] of Symbol
conditions = new_conditions({{condition.id}}: [
->{ calls << :a; nil },
->{ calls << :b; nil },
->{ calls << :c; nil },
])
conditions.run_{{condition.id}}_conditions
calls.should eq(\%i[a b c])
end
end
{% end %}
end

View file

@ -1,79 +0,0 @@
require "./spec_helper"
describe Spectator::ExampleHooks do
{% for hook in %i[before_all before_each after_all after_each] %}
describe "#run_{{hook.id}}" do
it "calls a proc" do
called = false
hooks = new_hooks({{hook.id}}: ->{ called = true; nil })
hooks.run_{{hook.id}}
called.should be_true
end
it "calls multiple procs" do
call_count = 0
hooks = new_hooks({{hook.id}}: [
->{ call_count += 1; nil },
->{ call_count += 2; nil },
->{ call_count += 3; nil },
])
hooks.run_{{hook.id}}
call_count.should eq(6)
end
it "calls procs in the correct order" do
calls = [] of Symbol
hooks = new_hooks({{hook.id}}: [
->{ calls << :a; nil },
->{ calls << :b; nil },
->{ calls << :c; nil },
])
hooks.run_{{hook.id}}
calls.should eq(\%i[a b c])
end
end
{% end %}
describe "#wrap_around_each" do
it "wraps the block" do
called = false
wrapper = new_hooks.wrap_around_each do
called = true
end
wrapper.call
called.should be_true
end
it "wraps a proc" do
called = false
hooks = new_hooks(around_each: ->(proc : ->) { called = true; proc.call })
wrapper = hooks.wrap_around_each { }
wrapper.call
called.should be_true
end
it "wraps multiple procs" do
call_count = 0
hooks = new_hooks(around_each: [
->(proc : ->) { call_count += 1; proc.call },
->(proc : ->) { call_count += 2; proc.call },
->(proc : ->) { call_count += 3; proc.call },
])
wrapper = hooks.wrap_around_each { }
wrapper.call
call_count.should eq(6)
end
it "wraps procs in the correct order" do
calls = [] of Symbol
hooks = new_hooks(around_each: [
->(proc : ->) { calls << :a; proc.call },
->(proc : ->) { calls << :b; proc.call },
->(proc : ->) { calls << :c; proc.call },
])
wrapper = hooks.wrap_around_each { }
wrapper.call
calls.should eq(%i[a b c])
end
end
end

View file

@ -1,205 +0,0 @@
require "./spec_helper"
describe Spectator::ExampleIterator do
describe "#next" do
context "with one example" do
it "returns the example" do
example = PassingExample.create
iterator = Spectator::ExampleIterator.new(example.group)
iterator.next.should eq(example)
end
it "returns 'stop' after the example" do
example = PassingExample.create
iterator = Spectator::ExampleIterator.new(example.group)
iterator.next # Should return example.
iterator.next.should be_a(Iterator::Stop)
end
end
context "when empty" do
it "returns 'stop'" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = [] of Spectator::ExampleComponent
iterator = Spectator::ExampleIterator.new(group)
iterator.next.should be_a(Iterator::Stop)
end
end
context "with one level of examples" do
it "iterates through all examples" do
examples = [] of Spectator::Example
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do
PassingExample.new(group, Spectator::Internals::SampleValues.empty)
end
iterator = Spectator::ExampleIterator.new(group)
5.times { examples << iterator.next.as(Spectator::Example) }
examples.should eq(group.children)
end
it "returns 'stop' at the end" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do
PassingExample.new(group, Spectator::Internals::SampleValues.empty)
end
iterator = Spectator::ExampleIterator.new(group)
5.times { iterator.next }
iterator.next.should be_a(Iterator::Stop)
end
end
context "with empty sub-groups" do
context "one level deep" do
it "returns 'stop'" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do |i|
Spectator::NestedExampleGroup.new(i.to_s, group, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group|
sub_group.children = [] of Spectator::ExampleComponent
end
end
iterator = Spectator::ExampleIterator.new(group)
iterator.next.should be_a(Iterator::Stop)
end
end
context "multiple levels deep" do
it "returns 'stop'" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do |i|
Spectator::NestedExampleGroup.new(i.to_s, group, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group|
sub_group.children = Array(Spectator::ExampleComponent).new(5) do |j|
Spectator::NestedExampleGroup.new(j.to_s, group, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_sub_group|
sub_sub_group.children = [] of Spectator::ExampleComponent
end
end
end
end
iterator = Spectator::ExampleIterator.new(group)
iterator.next.should be_a(Iterator::Stop)
end
end
end
context "with multiple levels of examples" do
it "iterates through all examples" do
actual_examples = [] of Spectator::Example
expected_examples = [] of Spectator::Example
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do |i|
if i % 2 == 0
PassingExample.new(group, Spectator::Internals::SampleValues.empty).tap do |example|
expected_examples << example
end
else
Spectator::NestedExampleGroup.new(i.to_s, group, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group|
sub_group.children = Array(Spectator::ExampleComponent).new(5) do
PassingExample.new(sub_group, Spectator::Internals::SampleValues.empty).tap do |example|
expected_examples << example
end
end
end
end
end
iterator = Spectator::ExampleIterator.new(group)
13.times { actual_examples << iterator.next.as(Spectator::Example) }
actual_examples.should eq(expected_examples)
end
it "returns 'stop' at the end" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do |i|
if i % 2 == 0
PassingExample.new(group, Spectator::Internals::SampleValues.empty)
else
Spectator::NestedExampleGroup.new(i.to_s, group, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group|
sub_group.children = Array(Spectator::ExampleComponent).new(5) do
PassingExample.new(sub_group, Spectator::Internals::SampleValues.empty)
end
end
end
end
iterator = Spectator::ExampleIterator.new(group)
13.times { iterator.next }
iterator.next.should be_a(Iterator::Stop)
end
end
context "with deep nesting" do
# Sorry for this atrocity,
# but it was fun to write.
it "iterates through all examples" do
actual_examples = [] of Spectator::Example
expected_examples = [] of Spectator::Example
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do |i|
Spectator::NestedExampleGroup.new(i.to_s, group, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group1|
sub_group1.children = Array(Spectator::ExampleComponent).new(5) do |j|
Spectator::NestedExampleGroup.new(j.to_s, sub_group1, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group2|
sub_group2.children = Array(Spectator::ExampleComponent).new(5) do |k|
Spectator::NestedExampleGroup.new(k.to_s, sub_group2, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group3|
sub_group3.children = Array(Spectator::ExampleComponent).new(5) do
PassingExample.new(sub_group3, Spectator::Internals::SampleValues.empty).tap do |example|
expected_examples << example
end
end
end
end
end
end
end
end
iterator = Spectator::ExampleIterator.new(group)
(5 ** 4).times { actual_examples << iterator.next.as(Spectator::Example) }
actual_examples.should eq(expected_examples)
end
it "returns 'stop' at the end" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do |i|
Spectator::NestedExampleGroup.new(i.to_s, group, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group1|
sub_group1.children = Array(Spectator::ExampleComponent).new(5) do |j|
Spectator::NestedExampleGroup.new(j.to_s, sub_group1, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group2|
sub_group2.children = Array(Spectator::ExampleComponent).new(5) do |k|
Spectator::NestedExampleGroup.new(k.to_s, sub_group2, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group3|
sub_group3.children = Array(Spectator::ExampleComponent).new(5) do
PassingExample.new(sub_group3, Spectator::Internals::SampleValues.empty)
end
end
end
end
end
end
end
iterator = Spectator::ExampleIterator.new(group)
(5 ** 4).times { iterator.next }
iterator.next.should be_a(Iterator::Stop)
end
end
it "returns 'stop' after the end has been reached" do
example = PassingExample.create
iterator = Spectator::ExampleIterator.new(example.group)
iterator.next # Should return example.
iterator.next # Should return "stop".
iterator.next.should be_a(Iterator::Stop) # Should still return "stop".
end
end
describe "#rewind" do
it "restarts the iterator" do
example = PassingExample.create
iterator = Spectator::ExampleIterator.new(example.group)
iterator.next
iterator.rewind
iterator.next.should eq(example)
end
it "can be called before #next" do
example = PassingExample.create
iterator = Spectator::ExampleIterator.new(example.group)
iterator.rewind
iterator.next.should eq(example)
end
end
end

View file

@ -1,19 +0,0 @@
require "./spec_helper"
describe Spectator::ExpectationFailed do
describe "#expectation" do
it "contains the expected value" do
expectation = new_unsatisfied_expectation
error = Spectator::ExpectationFailed.new(expectation)
error.expectation.should eq(expectation)
end
end
describe "#message" do
it "is the same as the expectation's #actual_message" do
expectation = new_unsatisfied_expectation
error = Spectator::ExpectationFailed.new(expectation)
error.message.should eq(expectation.failure_message)
end
end
end

View file

@ -1,207 +0,0 @@
require "../spec_helper"
describe Spectator::Expectations::ExampleExpectations do
describe "#each" do
it "yields all expectations" do
tuple = generate_expectations(5, 5)
expectations = [] of Spectator::Expectations::Expectation
tuple[:reporter].expectations.each { |expectation| expectations << expectation }
# Expectations might not be in the same order.
# Just check if if the arrays contain the same items.
expectations.size.should eq(tuple[:expectations].size)
(expectations - tuple[:expectations]).empty?.should be_true
end
end
describe "#satisfied" do
it "returns only satisfied expectations" do
tuple = generate_expectations(5, 5)
expectations = tuple[:reporter].expectations
expectations.satisfied.all?(&.satisfied?).should be_true
end
it "returns the correct expectations" do
tuple = generate_expectations(5, 5)
expectations = tuple[:reporter].expectations
satisfied = expectations.satisfied.to_a
satisfied.size.should eq(5)
(satisfied - tuple[:satisfied]).empty?.should be_true
end
context "with all satisfied expectations" do
it "returns all expectations" do
tuple = generate_expectations(5, 0)
expectations = tuple[:reporter].expectations
expectations.satisfied.size.should eq(tuple[:satisfied].size)
end
end
context "with all unsatisfied expectations" do
it "returns an empty collection" do
tuple = generate_expectations(0, 5)
expectations = tuple[:reporter].expectations
expectations.satisfied.size.should eq(0)
end
end
end
describe "#each_satisfied" do
it "yields only satisfied expectations" do
tuple = generate_expectations(5, 5)
expectations = [] of Spectator::Expectations::Expectation
tuple[:reporter].expectations.each_satisfied { |expectation| expectations << expectation }
# Expectations might not be in the same order.
# Just check if if the arrays contain the same items.
expectations.size.should eq(tuple[:satisfied].size)
(expectations - tuple[:satisfied]).empty?.should be_true
end
context "with all satisfied expectations" do
it "yields all expectations" do
tuple = generate_expectations(0, 5)
expectations = [] of Spectator::Expectations::Expectation
tuple[:reporter].expectations.each_satisfied { |expectation| expectations << expectation }
expectations.size.should eq(tuple[:satisfied].size)
end
end
context "with all unsatisfied expectations" do
it "yields nothing" do
tuple = generate_expectations(0, 5)
expectations = [] of Spectator::Expectations::Expectation
tuple[:reporter].expectations.each_satisfied { |expectation| expectations << expectation }
expectations.empty?.should be_true
end
end
end
describe "#unsatisfied" do
it "returns only unsatisfied expectations" do
tuple = generate_expectations(5, 5)
expectations = tuple[:reporter].expectations
expectations.unsatisfied.all?(&.satisfied?).should be_false
end
it "returns the correct expectations" do
tuple = generate_expectations(5, 5)
expectations = tuple[:reporter].expectations
unsatisfied = expectations.unsatisfied.to_a
unsatisfied.size.should eq(5)
(unsatisfied - tuple[:unsatisfied]).empty?.should be_true
end
context "with all satisfied expectations" do
it "returns an empty collection" do
tuple = generate_expectations(5, 0)
expectations = tuple[:reporter].expectations
expectations.unsatisfied.size.should eq(0)
end
end
context "with all unsatisfied expectations" do
it "returns all expectations" do
tuple = generate_expectations(0, 5)
expectations = tuple[:reporter].expectations
expectations.unsatisfied.size.should eq(tuple[:unsatisfied].size)
end
end
end
describe "#each_unsatisfied" do
it "yields only unsatisfied expectations" do
tuple = generate_expectations(5, 5)
expectations = [] of Spectator::Expectations::Expectation
tuple[:reporter].expectations.each_unsatisfied { |expectation| expectations << expectation }
# Expectations might not be in the same order.
# Just check if if the arrays contain the same items.
expectations.size.should eq(tuple[:unsatisfied].size)
(expectations - tuple[:unsatisfied]).empty?.should be_true
end
context "with all satisfied expectations" do
it "yields nothing" do
tuple = generate_expectations(5, 0)
expectations = [] of Spectator::Expectations::Expectation
tuple[:reporter].expectations.each_unsatisfied { |expectation| expectations << expectation }
expectations.empty?.should be_true
end
end
context "with all unsatisfied expectations" do
it "yields all expectations" do
tuple = generate_expectations(0, 5)
expectations = [] of Spectator::Expectations::Expectation
tuple[:reporter].expectations.each_unsatisfied { |expectation| expectations << expectation }
expectations.size.should eq(tuple[:unsatisfied].size)
end
end
end
describe "#successful?" do
context "with all satisfied expectations" do
it "is true" do
tuple = generate_expectations(5, 0)
expectations = tuple[:reporter].expectations
expectations.successful?.should be_true
end
end
context "with one unsatisfied expectation" do
it "is false" do
tuple = generate_expectations(5, 1)
expectations = tuple[:reporter].expectations
expectations.successful?.should be_false
end
end
context "with one satisfied expectation" do
it "is false" do
tuple = generate_expectations(1, 5)
expectations = tuple[:reporter].expectations
expectations.successful?.should be_false
end
end
context "with all unsatisfied expectations" do
it "is false" do
tuple = generate_expectations(0, 5)
expectations = tuple[:reporter].expectations
expectations.successful?.should be_false
end
end
end
describe "#failed?" do
context "with all satisfied expectations" do
it "is false" do
tuple = generate_expectations(5, 0)
expectations = tuple[:reporter].expectations
expectations.failed?.should be_false
end
end
context "with one unsatisfied expectation" do
it "is true" do
tuple = generate_expectations(5, 1)
expectations = tuple[:reporter].expectations
expectations.failed?.should be_true
end
end
context "with one satisfied expectation" do
it "is true" do
tuple = generate_expectations(1, 5)
expectations = tuple[:reporter].expectations
expectations.failed?.should be_true
end
end
context "with all unsatisfied expectations" do
it "is true" do
tuple = generate_expectations(0, 5)
expectations = tuple[:reporter].expectations
expectations.failed?.should be_true
end
end
end
end

View file

@ -1,80 +0,0 @@
require "../spec_helper"
describe Spectator::Expectations::ExpectationReporter do
describe "#report" do
context "with raise flag set" do
context "given a satisfied expectation" do
it "stores the result" do
expectation = new_satisfied_expectation
reporter = Spectator::Expectations::ExpectationReporter.new(true)
reporter.report(expectation)
reporter.expectations.should contain(expectation)
end
end
context "given a unsatisfied expectation" do
it "raises and error" do
expectation = new_unsatisfied_expectation
reporter = Spectator::Expectations::ExpectationReporter.new(true)
expect_raises(Spectator::ExpectationFailed) { reporter.report(expectation) }
end
it "stores the expectation" do
expectation = new_unsatisfied_expectation
reporter = Spectator::Expectations::ExpectationReporter.new(true)
begin
reporter.report(expectation)
rescue
# Ignore error, not testing that in this example.
end
reporter.expectations.should contain(expectation)
end
end
end
context "with raise flag not set" do
context "given a satisfied expectation" do
it "stores the expectation" do
expectation = new_satisfied_expectation
reporter = Spectator::Expectations::ExpectationReporter.new(false)
reporter.report(expectation)
reporter.expectations.should contain(expectation)
end
end
context "given a unsatisfied expectation" do
it "stores the expectation" do
expectation = new_unsatisfied_expectation
reporter = Spectator::Expectations::ExpectationReporter.new(false)
reporter.report(expectation)
reporter.expectations.should contain(expectation)
end
end
end
end
describe "#expectations" do
context "with no expectations" do
it "is empty" do
reporter = Spectator::Expectations::ExpectationReporter.new
reporter.expectations.size.should eq(0)
end
end
context "with multiple expectations" do
it "contains all expectations" do
expectation1 = new_satisfied_expectation
expectation2 = new_unsatisfied_expectation
reporter = Spectator::Expectations::ExpectationReporter.new(false)
begin
reporter.report(expectation1)
reporter.report(expectation2)
rescue
# Ignore errors for this test.
end
reporter.expectations.should contain(expectation1)
reporter.expectations.should contain(expectation2)
end
end
end
end

View file

@ -1,51 +0,0 @@
require "../spec_helper"
describe Spectator::Expectations::Expectation do
describe "#satisifed?" do
context "with a successful match" do
it "is true" do
value = 42
matcher = new_matcher(value)
partial = new_partial(value)
match_data = matcher.match(partial.actual)
match_data.matched?.should be_true # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, partial.source)
expectation.satisfied?.should be_true
end
context "when negated" do
it "is false" do
value = 42
matcher = new_matcher(value)
partial = new_partial(value)
match_data = matcher.negated_match(partial.actual)
match_data.matched?.should be_false # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, partial.source)
expectation.satisfied?.should be_false
end
end
end
context "with an unsuccessful match" do
it "is false" do
matcher = new_matcher(42)
partial = new_partial(777)
match_data = matcher.match(partial.actual)
match_data.matched?.should be_false # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, partial.source)
expectation.satisfied?.should be_false
end
context "when negated" do
it "is true" do
matcher = new_matcher(42)
partial = new_partial(777)
match_data = matcher.negated_match(partial.actual)
match_data.matched?.should be_true # Sanity check.
expectation = Spectator::Expectations::Expectation.new(match_data, partial.source)
expectation.satisfied?.should be_true
end
end
end
end
end

View file

@ -1,87 +0,0 @@
require "./spec_helper"
def new_failed_result(
example : Spectator::Example? = nil,
elapsed : Time::Span? = nil,
expectations : Spectator::Expectations::ExampleExpectations? = nil,
error : Exception? = nil
)
Spectator::FailedResult.new(
example || FailingExample.create,
elapsed || Time::Span.zero,
expectations || Spectator::Expectations::ExampleExpectations.new(generate_expectations(0, 1)[:expectations]),
error || Exception.new("foobar")
)
end
describe Spectator::FailedResult do
describe "#call" do
context "without a block" do
it "invokes #failure on an instance" do
spy = ResultCallSpy.new
new_failed_result.call(spy)
spy.failure?.should be_true
end
it "returns the value of #failure" do
result = new_failed_result
returned = result.call(ResultCallSpy.new)
returned.should eq(:failure)
end
end
context "with a block" do
it "invokes #failure on an instance" do
spy = ResultCallSpy.new
new_failed_result.call(spy) { nil }
spy.failure?.should be_true
end
it "yields itself" do
result = new_failed_result
value = nil.as(Spectator::Result?)
result.call(ResultCallSpy.new) { |r| value = r }
value.should eq(result)
end
it "returns the value of #failure" do
result = new_failed_result
value = 42
returned = result.call(ResultCallSpy.new) { value }
returned.should eq(value)
end
end
end
describe "#example" do
it "is the expected value" do
example = FailingExample.create
result = new_failed_result(example: example)
result.example.should eq(example)
end
end
describe "#elapsed" do
it "is the expected value" do
elapsed = Time::Span.new(10, 10, 10)
result = new_failed_result(elapsed: elapsed)
result.elapsed.should eq(elapsed)
end
end
describe "#expectations" do
it "is the expected value" do
expectations = Spectator::Expectations::ExampleExpectations.new(generate_expectations(5, 1)[:expectations])
result = new_failed_result(expectations: expectations)
result.expectations.should eq(expectations)
end
end
describe "#error" do
it "is the expected value" do
error = IO::Error.new("oops")
result = new_failed_result(error: error)
result.error.should eq(error)
end
end
end

View file

@ -1,93 +0,0 @@
require "../spec_helper"
describe Spectator::Formatting::Color do
describe "#success" do
it "includes the input text" do
text = "foobar"
output = Spectator::Formatting::Color.success(text)
output.to_s.should contain(text)
end
it "prefixes with green markers" do
output = Spectator::Formatting::Color.success("foobar")
output.to_s.should start_with("\e[32m")
end
it "appends color reset markers" do
output = Spectator::Formatting::Color.success("foobar")
output.to_s.should end_with("\e[0m")
end
end
describe "#failure" do
it "includes the input text" do
text = "foobar"
output = Spectator::Formatting::Color.failure(text)
output.to_s.should contain(text)
end
it "prefixes with green markers" do
output = Spectator::Formatting::Color.failure("foobar")
output.to_s.should start_with("\e[31m")
end
it "appends color reset markers" do
output = Spectator::Formatting::Color.failure("foobar")
output.to_s.should end_with("\e[0m")
end
end
describe "#error" do
it "includes the input text" do
text = "foobar"
output = Spectator::Formatting::Color.error(text)
output.to_s.should contain(text)
end
it "prefixes with green markers" do
output = Spectator::Formatting::Color.error("foobar")
output.to_s.should start_with("\e[35m")
end
it "appends color reset markers" do
output = Spectator::Formatting::Color.error("foobar")
output.to_s.should end_with("\e[0m")
end
end
describe "#pending" do
it "includes the input text" do
text = "foobar"
output = Spectator::Formatting::Color.pending(text)
output.to_s.should contain(text)
end
it "prefixes with green markers" do
output = Spectator::Formatting::Color.pending("foobar")
output.to_s.should start_with("\e[33m")
end
it "appends color reset markers" do
output = Spectator::Formatting::Color.pending("foobar")
output.to_s.should end_with("\e[0m")
end
end
describe "#comment" do
it "includes the input text" do
text = "foobar"
output = Spectator::Formatting::Color.comment(text)
output.to_s.should contain(text)
end
it "prefixes with green markers" do
output = Spectator::Formatting::Color.comment("foobar")
output.to_s.should start_with("\e[36m")
end
it "appends color reset markers" do
output = Spectator::Formatting::Color.comment("foobar")
output.to_s.should end_with("\e[0m")
end
end
end

View file

@ -1,37 +0,0 @@
# Example that always raises an exception.
class ErroredExample < Spectator::RunnableExample
# Dummy description.
def what : Symbol | String
"ERROR"
end
# Dummy source.
def source : ::Spectator::Source
::Spectator::Source.new(__FILE__, __LINE__)
end
# Dummy symbolic flag.
def symbolic? : Bool
false
end
# Dummy instance.
def instance
nil
end
# Run the example that always produces an error.
private def run_instance
raise "Oops"
end
# Creates an errored example.
def self.create
hooks = Spectator::ExampleHooks.empty
group = Spectator::RootExampleGroup.new(hooks)
values = Spectator::Internals::SampleValues.empty
new(group, values).tap do |example|
group.children = [example.as(Spectator::ExampleComponent)]
end
end
end

View file

@ -1,59 +0,0 @@
# Creates a new `Spectator::ExampleHooks` instance.
# All arguments are optional,
# only specify the sets of hooks that are needed.
# The hooks that aren't specified will be left empty.
def new_hooks(
before_all = [] of ->,
before_each = [] of ->,
after_all = [] of ->,
after_each = [] of ->,
around_each = [] of Proc(Nil) ->
)
Spectator::ExampleHooks.new(before_all, before_each,
after_all, after_each, around_each)
end
# Creates a new `Spectator::ExampleHooks` instance.
# All arguments are optional,
# only specify a hook for the types that are needed.
# The hooks that aren't specified will be left empty.
def new_hooks(
before_all : Proc(Nil)? = nil,
before_each : Proc(Nil)? = nil,
after_all : Proc(Nil)? = nil,
after_each : Proc(Nil)? = nil,
around_each : Proc(Proc(Nil), Nil)? = nil
)
new_hooks(
before_all ? [before_all] : [] of ->,
before_each ? [before_each] : [] of ->,
after_all ? [after_all] : [] of ->,
after_each ? [after_each] : [] of ->,
around_each ? [around_each] : [] of Proc(Nil) ->
)
end
# Creates a new `Spectator::ExampleConditions` instance.
# All arguments are optional,
# only specify the sets of conditions that are needed.
# The conditions that aren't specified will be left empty.
def new_conditions(
pre = [] of ->,
post = [] of ->
)
Spectator::ExampleConditions.new(pre, post)
end
# Creates a new `Spectator::ExampleConditions` instance.
# All arguments are optional,
# only specify a condition for the types that are needed.
# The conditions that aren't specified will be left empty.
def new_conditions(
pre : Proc(Nil)? = nil,
post : Proc(Nil)? = nil
)
new_conditions(
pre ? [pre] : [] of ->,
post ? [post] : [] of ->
)
end

View file

@ -1,71 +0,0 @@
# Utility methods for creating expectations, partials, and matchers.
def new_partial(actual : T, label : String) forall T
test_value = Spectator::TestValue.new(actual, label)
source = Spectator::Source.new(__FILE__, __LINE__)
Spectator::Expectations::ExpectationPartial.new(test_value, source)
end
def new_partial(actual : T = 123) forall T
test_value = Spectator::TestValue.new(actual)
source = Spectator::Source.new(__FILE__, __LINE__)
Spectator::Expectations::ExpectationPartial.new(test_value, source)
end
def new_block_partial(label = "BLOCK", &block)
test_block = Spectator::TestBlock.new(block, label)
source = Spectator::Source.new(__FILE__, __LINE__)
Spectator::Expectations::ExpectationPartial.new(test_block, source)
end
def new_matcher(expected : T, label : String) forall T
test_value = Spectator::TestValue.new(expected, label)
Spectator::Matchers::EqualityMatcher.new(test_value)
end
def new_matcher(expected : T = 123) forall T
test_value = Spectator::TestValue.new(expected)
Spectator::Matchers::EqualityMatcher.new(test_value)
end
def new_expectation(expected : ExpectedType = 123, actual : ActualType = 123) forall ExpectedType, ActualType
partial = new_partial(actual, "foo")
matcher = new_matcher(expected, "bar")
match_data = matcher.match(partial.actual)
Spectator::Expectations::Expectation.new(match_data, partial.source)
end
def new_satisfied_expectation(value : T = 123) forall T
new_expectation(value, value).tap do |expectation|
expectation.satisfied?.should be_true # Sanity check.
end
end
def new_unsatisfied_expectation(expected : ExpectedType = 123, actual : ActualType = 456) forall ExpectedType, ActualType
new_expectation(expected, actual).tap do |expectation|
expectation.satisfied?.should be_false # Sanity check.
end
end
def create_expectations(success_count = 1, failure_count = 0)
satisfied = Array.new(success_count) { new_satisfied_expectation }
unsatisfied = Array.new(failure_count) { new_unsatisfied_expectation }
(satisfied + unsatisfied).shuffle
end
def generate_expectations(success_count = 1, failure_count = 0)
satisfied = Array.new(success_count) { new_satisfied_expectation }
unsatisfied = Array.new(failure_count) { new_unsatisfied_expectation }
expectations = (satisfied + unsatisfied).shuffle
reporter = Spectator::Expectations::ExpectationReporter.new(false)
expectations.each do |expectation|
reporter.report(expectation)
end
{satisfied: satisfied, unsatisfied: unsatisfied, expectations: expectations, reporter: reporter}
end
def report_expectations(success_count = 1, failure_count = 0)
harness = Spectator::Internals::Harness.current
success_count.times { harness.report_expectation(new_satisfied_expectation) }
failure_count.times { harness.report_expectation(new_unsatisfied_expectation) }
end

View file

@ -1,44 +0,0 @@
# Example that always fails.
class FailingExample < Spectator::RunnableExample
# Dummy description.
def what : Symbol | String
"FAIL"
end
# Dummy source.
def source : ::Spectator::Source
::Spectator::Source.new(__FILE__, __LINE__)
end
# Dummy symbolic flag.
def symbolic? : Bool
false
end
# Dummy instance.
def instance
nil
end
# Run the example that always fails.
private def run_instance
report_expectations(0, 1)
end
# Creates a failing example.
def self.create(hooks = Spectator::ExampleHooks.empty, conditions = Spectator::ExampleConditions.empty)
group = Spectator::RootExampleGroup.new(hooks, conditions)
values = Spectator::Internals::SampleValues.empty
new(group, values).tap do |example|
group.children = [example.as(Spectator::ExampleComponent)]
end
end
# Creates a group of failing examples.
def self.create_group(count = 5, hooks = Spectator::ExampleHooks.empty, conditions = Spectator::ExampleConditions.empty)
values = Spectator::Internals::SampleValues.empty
Spectator::RootExampleGroup.new(hooks, conditions).tap do |group|
group.children = Array.new(count) { new(group, values).as(Spectator::ExampleComponent) }
end
end
end

View file

@ -1,20 +0,0 @@
# Retrieves a value from match data given its key/label.
def match_data_value_with_key(match_data_values, key)
labeled_value = match_data_values.find { |v| v.label == key }
raise "#{key} is missing" unless labeled_value
labeled_value.value
end
# Retrieves the string representation and base value
# from a `Spectator::Matchers::PrefixedMatchDataValue` (or similar)
# in the values returned by `Spectator::Matchers::MatchData#values`.
def match_data_value_sans_prefix(match_data_values, key)
prefix = match_data_value_with_key(match_data_values, key)
{to_s: prefix.to_s, value: prefix.value}
end
# Check whether match data has a value with a specified key/label.
def match_data_has_key?(match_data_values, key)
found = match_data_values.find { |v| v.label == key }
!!found
end

View file

@ -1,42 +0,0 @@
# Example that always succeeds.
class PassingExample < Spectator::RunnableExample
# Creates the example.
def initialize(group, values, @symbolic = false)
super(group, values)
end
# Dummy description.
def what : Symbol | String
"PASS"
end
# Dummy source.
def source : ::Spectator::Source
::Spectator::Source.new(__FILE__, __LINE__)
end
# Dummy symbolic flag.
def symbolic? : Bool
@symbolic
end
# Dummy instance.
def instance
nil
end
# Run the example that always passes.
# If this doesn't something broke.
private def run_instance
report_expectations(1, 0)
end
# Creates a passing example.
def self.create(hooks = Spectator::ExampleHooks.empty, conditions = Spectator::ExampleConditions.empty)
group = Spectator::RootExampleGroup.new(hooks, conditions)
values = Spectator::Internals::SampleValues.empty
new(group, values).tap do |example|
group.children = [example.as(Spectator::ExampleComponent)]
end
end
end

View file

@ -1,16 +0,0 @@
# Spy class for testing `Spectator::Result#call`.
class ResultCallSpy
{% for name in %i[success failure error pending] %}
getter? {{name.id}} = false
def {{name.id}}
@{{name.id}} = true
{{name}}
end
def {{name.id}}(arg)
@{{name.id}} = true
arg
end
{% end %}
end

View file

@ -1,69 +0,0 @@
# Example that invokes a closure when it is run.
# This is useful for capturing what's going on when an event is running.
class SpyExample < Spectator::RunnableExample
# Dummy description.
def what : Symbol | String
"SPY"
end
# Dummy source.
def source : ::Spectator::Source
::Spectator::Source.new(__FILE__, __LINE__)
end
# Dummy symbolic flag.
def symbolic? : Bool
false
end
# Dummy instance.
def instance
nil
end
# Captures the sample values when the example is created.
def initialize(group, @sample_values)
super(group, @sample_values)
end
# Sample values given to the example.
getter sample_values : Spectator::Internals::SampleValues
# Harness that was used while running the example.
getter! harness : Spectator::Internals::Harness
setter block : Proc(Nil)? = nil
# Method called by the framework to run the example code.
private def run_instance
@harness = Spectator::Internals::Harness.current
if block = @block
block.call
end
end
# Creates a spy example.
# The block passed to this method will be executed when the example runs.
def self.create(hooks = Spectator::ExampleHooks.empty, conditions = Spectator::ExampleConditions.empty, &block)
group = Spectator::RootExampleGroup.new(hooks, conditions)
values = Spectator::Internals::SampleValues.empty
new(group, values).tap do |example|
group.children = [example.as(Spectator::ExampleComponent)]
example.block = block
end
end
# Creates a group of spy examples.
# Specify the number of examplese to create and a block to invoke when the example is run.
# The block is given the index of the example in the group.
def self.create_group(count, &block : Int32 -> Nil)
values = Spectator::Internals::SampleValues.empty
Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |group|
group.children = Array.new(count) do |index|
new(group, values).tap do |example|
example.block = block.partial(index)
end.as(Spectator::ExampleComponent)
end
end
end
end

View file

@ -1,47 +0,0 @@
# Formatter that doubles as a spy.
# This class tracks calls made to it.
class SpyFormatter < Spectator::Formatting::Formatter
{% for item in [
{"start_suite", "Spectator::TestSuite"},
{"start_example", "Spectator::Example"},
{"end_example", "Spectator::Result"},
] %}
{% method_name = item[0].id %}
{% argument_type = item[1].id %}
# Stores all invocations made to `#{{method_name}}`.
# Each element is an invocation and the value is the argument passed to the method.
getter {{method_name}}_calls = [] of {{argument_type}}
# Number of times the `#{{method_name}}` method was called.
def {{method_name}}_call_count
@{{method_name}}_calls.size
end
# Increments `#{{method_name}}_call_count` and stores the argument.
def {{method_name}}(arg : {{argument_type}})
@all_calls << {{method_name.symbolize}}
@{{method_name}}_calls << arg
end
{% end %}
# Stores all invocatiosn made to `#end_suite`.
# Each element is an invocation and the value is the arguments passed to the method.
getter end_suite_calls = [] of NamedTuple(report: Spectator::Report, profile: Spectator::Profile?)
# Number of times the `#end_suite` method was called.
def end_suite_call_count
@end_suite_calls.size
end
# Increments `#end_suite_call_count` and stores the arguments.
def end_suite(report, profile)
@all_calls << :end_suite
@end_suite_calls << {report: report, profile: profile}
end
# Stores the methods that were called and in which order.
# The symbols will be the method name (i.e. `:start_suite`).
getter all_calls = [] of Symbol
end

View file

@ -1,28 +0,0 @@
# Example system to test that doubles as a spy.
# This class tracks calls made to it.
class SpySUT
{% for item in [
{"==", "eq"},
{"!=", "ne"},
{"<", "lt"},
{"<=", "le"},
{">", "gt"},
{">=", "ge"},
{"===", "case_eq"},
{"=~", "match"},
{"includes?", "includes"},
] %}
{% operator = item[0].id %}
{% name = item[1].id %}
# Number of times the `#{{operator}}` method was called.
getter {{name}}_call_count = 0
# Returns true and increments `#{{name}}_call_count`.
def {{operator}}(other : T) forall T
@{{name}}_call_count += 1
true
end
{% end %}
end

View file

@ -1,109 +0,0 @@
require "../spec_helper"
describe Spectator::Internals::Harness do
describe "#run" do
it "runs an example" do
run_count = 0
spy = SpyExample.create do
run_count += 1
end
Spectator::Internals::Harness.run(spy)
run_count.should eq(1)
end
context "with a passing exmaple" do
it "returns a passing result" do
example = PassingExample.create
result = Spectator::Internals::Harness.run(example)
result.should be_a(Spectator::SuccessfulResult)
end
end
context "with a failing example" do
it "returns a failing result" do
example = FailingExample.create
result = Spectator::Internals::Harness.run(example)
result.should be_a(Spectator::FailedResult)
end
end
end
describe "#current" do
it "references the current harness" do
harness = nil.as(Spectator::Internals::Harness?)
spy = SpyExample.create do
harness = Spectator::Internals::Harness.current
end
Spectator::Internals::Harness.run(spy)
harness.should be_a(Spectator::Internals::Harness)
end
end
describe "#example" do
it "references the current example" do
example = nil.as(Spectator::Example?)
spy = SpyExample.create do
example = Spectator::Internals::Harness.current.example
end
Spectator::Internals::Harness.run(spy)
example.should be(spy)
end
end
describe "#report_expectation" do
context "with a successful result" do
it "stores the result" do
expectation = new_satisfied_expectation
spy = SpyExample.create do
harness = Spectator::Internals::Harness.current
harness.report_expectation(expectation)
end
result = Spectator::Internals::Harness.run(spy)
result.should be_a(Spectator::SuccessfulResult)
result.as(Spectator::SuccessfulResult).expectations.should contain(expectation)
end
end
context "with a failed result" do
it "raises an error" do
error = nil.as(Exception?)
spy = SpyExample.create do
harness = Spectator::Internals::Harness.current
begin
harness.report_expectation(new_unsatisfied_expectation)
rescue ex
error = ex
end
end
Spectator::Internals::Harness.run(spy)
error.should be_a(Spectator::ExampleFailed)
end
it "stores the result" do
expectation = new_unsatisfied_expectation
spy = SpyExample.create do
harness = Spectator::Internals::Harness.current
harness.report_expectation(expectation)
end
result = Spectator::Internals::Harness.run(spy)
result.should be_a(Spectator::FailedResult)
result.as(Spectator::FailedResult).expectations.should contain(expectation)
end
end
end
describe "#expectation_results" do
it "contains the reported results" do
expectations = [new_satisfied_expectation, new_unsatisfied_expectation]
spy = SpyExample.create do
harness = Spectator::Internals::Harness.current
expectations.each do |expectation|
harness.report_expectation(expectation)
end
end
result = Spectator::Internals::Harness.run(spy)
reported_results = result.as(Spectator::FailedResult).expectations.to_a
(expectations - reported_results).size.should eq(0)
end
end
end

View file

@ -1,117 +0,0 @@
require "../spec_helper"
private def add_sample_value(values : Spectator::Internals::SampleValues,
symbol : Symbol, name : String, value : T) forall T
values.add(symbol, name, value)
end
private def add_sample_value(symbol, name, value : T) forall T
add_sample_value(Spectator::Internals::SampleValues.empty, symbol, name, value)
end
private def add_sample_value(symbol, value : T) forall T
add_sample_value(symbol, symbol.to_s, value)
end
describe Spectator::Internals::SampleValues do
describe "#add" do
it "creates a new set" do
original = Spectator::Internals::SampleValues.empty
new_set = original.add(:new, "new", 123)
new_set.should_not eq(original)
end
it "adds a new value" do
symbol = :new
value = 123
values = add_sample_value(symbol, value)
values.get_value(symbol, typeof(value)).should eq(value)
end
end
describe "#get_wrapper" do
it "returns a wrapper for a value" do
symbol = :new
value = 123
values = add_sample_value(symbol, value)
wrapper = values.get_wrapper(symbol)
wrapper.should be_a(Spectator::Internals::ValueWrapper)
end
it "returns the correct wrapper" do
symbol = :new
value = 123
values = add_sample_value(symbol, value)
wrapper = values.get_wrapper(symbol)
wrapper.should be_a(Spectator::Internals::TypedValueWrapper(typeof(value)))
wrapper.as(Spectator::Internals::TypedValueWrapper(typeof(value))).value.should eq(value)
end
context "with multiple values" do
it "returns the expected value" do
symbols = {
one: 123,
two: 456,
three: 789,
}
values = Spectator::Internals::SampleValues.empty
symbols.each do |symbol, number|
values = add_sample_value(values, symbol, symbol.to_s, number)
end
selected_symbol = :one
selected_number = symbols[selected_symbol]
wrapper = values.get_wrapper(selected_symbol)
wrapper.should be_a(Spectator::Internals::TypedValueWrapper(typeof(selected_number)))
wrapper.as(Spectator::Internals::TypedValueWrapper(typeof(selected_number))).value.should eq(selected_number)
end
end
end
describe "#get_value" do
it "returns a value" do
symbol = :new
value = 123
values = add_sample_value(symbol, value)
values.get_value(symbol, typeof(value)).should eq(value)
end
context "with multiple values" do
it "returns the expected value" do
symbols = {
one: 123,
two: 456,
three: 789,
}
values = Spectator::Internals::SampleValues.empty
symbols.each do |symbol, number|
values = add_sample_value(values, symbol, symbol.to_s, number)
end
selected_symbol = :one
selected_number = symbols[selected_symbol]
value = values.get_value(selected_symbol, typeof(selected_number))
value.should eq(selected_number)
end
end
end
describe "#each" do
it "yields each entry" do
symbols = {
one: 123,
two: 456,
three: 789,
}
values = Spectator::Internals::SampleValues.empty
symbols.each do |symbol, number|
values = add_sample_value(values, symbol, symbol.to_s, number)
end
size = 0
values.each do |entry|
size += 1
symbols.keys.map(&.to_s).should contain(entry.name)
end
size.should eq(symbols.size)
end
end
end

View file

@ -1,18 +0,0 @@
require "../spec_helper"
describe Spectator::Internals::TypedValueWrapper do
describe "#value" do
it "returns the expected value" do
value = 12345
wrapper = Spectator::Internals::TypedValueWrapper.new(value)
wrapper.value.should eq(value)
end
end
it "can be cast for storage" do
value = 12345
wrapper = Spectator::Internals::TypedValueWrapper.new(value).as(Spectator::Internals::ValueWrapper)
typed = wrapper.as(Spectator::Internals::TypedValueWrapper(typeof(value)))
typed.value.should eq(value)
end
end

View file

@ -1,21 +0,0 @@
require "./spec_helper"
describe Spectator::LineExampleFilter do
describe "#includes?" do
context "with a matching example" do
it "is true" do
example = PassingExample.create
filter = Spectator::LineExampleFilter.new(example.source.line)
filter.includes?(example).should be_true
end
end
context "with a non-matching example" do
it "is false" do
example = PassingExample.create
filter = Spectator::LineExampleFilter.new(example.source.line + 5)
filter.includes?(example).should be_false
end
end
end
end

View file

@ -1,21 +0,0 @@
require "./spec_helper"
describe Spectator::NameExampleFilter do
describe "#includes?" do
context "with a matching example" do
it "is true" do
example = PassingExample.create
filter = Spectator::NameExampleFilter.new(example.to_s)
filter.includes?(example).should be_true
end
end
context "with a non-matching example" do
it "is false" do
example = PassingExample.create
filter = Spectator::NameExampleFilter.new("BOGUS")
filter.includes?(example).should be_false
end
end
end
end

File diff suppressed because it is too large Load diff

View file

@ -1,11 +0,0 @@
require "./spec_helper"
describe Spectator::NullExampleFilter do
describe "#includes?" do
it "returns true" do
example = PassingExample.create
filter = Spectator::NullExampleFilter.new
filter.includes?(example).should be_true
end
end
end

View file

@ -1,122 +0,0 @@
require "./spec_helper"
class ConcretePendingExample < Spectator::PendingExample
def what : Symbol | String
"PENDING_TEST_EXAMPLE"
end
def source : ::Spectator::Source
::Spectator::Source.new(__FILE__, __LINE__)
end
def symbolic? : Bool
false
end
def instance
nil
end
end
def new_pending_example(group : Spectator::ExampleGroup? = nil)
ConcretePendingExample.new(group || new_root_group, Spectator::Internals::SampleValues.empty)
end
def new_pending_example_with_hooks(hooks)
group = new_root_group(hooks)
new_pending_example(group)
end
describe Spectator::PendingExample do
describe "#run" do
it "returns a pending result" do
new_pending_example.run.should be_a(Spectator::PendingResult)
end
it "doesn't run before_all hooks" do
called = false
hooks = new_hooks(before_all: ->{ called = true; nil })
example = new_pending_example_with_hooks(hooks)
Spectator::Internals::Harness.run(example)
called.should be_false
end
it "doesn't run before_each hooks" do
called = false
hooks = new_hooks(before_each: ->{ called = true; nil })
example = new_pending_example_with_hooks(hooks)
Spectator::Internals::Harness.run(example)
called.should be_false
end
it "doesn't run after_all hooks" do
called = false
hooks = new_hooks(after_all: ->{ called = true; nil })
example = new_pending_example_with_hooks(hooks)
Spectator::Internals::Harness.run(example)
called.should be_false
end
it "doesn't run after_each hooks" do
called = false
hooks = new_hooks(after_each: ->{ called = true; nil })
example = new_pending_example_with_hooks(hooks)
Spectator::Internals::Harness.run(example)
called.should be_false
end
it "doesn't run around_each hooks" do
called = false
hooks = new_hooks(around_each: ->(proc : ->) { called = true; proc.call })
example = new_pending_example_with_hooks(hooks)
Spectator::Internals::Harness.run(example)
called.should be_false
end
end
describe "#finished?" do
it "is initially false" do
new_pending_example.finished?.should be_false
end
it "is true after #run is called" do
example = new_pending_example
Spectator::Internals::Harness.run(example)
example.finished?.should be_true
end
end
describe "#group" do
it "is the expected value" do
group = new_root_group
example = new_pending_example(group)
example.group.should eq(group)
end
end
describe "#example_count" do
it "is one" do
new_pending_example.example_count.should eq(1)
end
end
describe "#[]" do
it "returns self" do
example = new_pending_example
example[0].should eq(example)
end
end
describe "#to_s" do
it "contains #what" do
example = new_pending_example
example.to_s.should contain(example.what)
end
it "contains the group's #what" do
group = new_nested_group
example = new_pending_example(group)
example.to_s.should contain(group.what.to_s)
end
end
end

View file

@ -1,53 +0,0 @@
require "./spec_helper"
def new_pending_result(example : Spectator::Example? = nil)
Spectator::PendingResult.new(example || FailingExample.create)
end
describe Spectator::PendingResult do
describe "#call" do
context "without a block" do
it "invokes #pending on an instance" do
spy = ResultCallSpy.new
new_pending_result.call(spy)
spy.pending?.should be_true
end
it "returns the value of #pending" do
result = new_pending_result
returned = result.call(ResultCallSpy.new)
returned.should eq(:pending)
end
end
context "with a block" do
it "invokes #pending on an instance" do
spy = ResultCallSpy.new
new_pending_result.call(spy) { nil }
spy.pending?.should be_true
end
it "yields itself" do
result = new_pending_result
value = nil.as(Spectator::Result?)
result.call(ResultCallSpy.new) { |r| value = r }
value.should eq(result)
end
it "returns the value of #pending" do
result = new_pending_result
value = 42
returned = result.call(ResultCallSpy.new) { value }
returned.should eq(value)
end
end
end
describe "#example" do
it "is the expected value" do
example = PassingExample.create
result = new_pending_result(example: example)
result.example.should eq(example)
end
end
end

View file

@ -1,237 +0,0 @@
require "./spec_helper"
def new_passing_result
example = PassingExample.create
elapsed = Time::Span.new(nanoseconds: 1_000_000)
expectations = Spectator::Expectations::ExampleExpectations.new([] of Spectator::Expectations::Expectation)
Spectator::SuccessfulResult.new(example, elapsed, expectations)
end
def new_failure_result(result_type : Spectator::Result.class = Spectator::FailedResult)
example = FailingExample.create # Doesn't matter what type of example is used here.
elapsed = Time::Span.new(nanoseconds: 1_000_000)
expectations = Spectator::Expectations::ExampleExpectations.new([] of Spectator::Expectations::Expectation)
error = Exception.new("foobar")
result_type.new(example, elapsed, expectations, error)
end
def new_results(successful_count = 5, failed_count = 5, error_count = 5, pending_count = 5)
total = successful_count + failed_count + error_count + pending_count
results = Array(Spectator::Result).new(total)
successful_count.times { results << new_passing_result }
failed_count.times { results << new_failure_result }
error_count.times { results << new_failure_result(Spectator::ErroredResult) }
pending_count.times { results << new_pending_result }
results
end
def new_report(successful_count = 5, failed_count = 5, error_count = 5, pending_count = 5, overhead_time = 1_000_000i64, fail_blank = false)
results = new_results(successful_count, failed_count, error_count, pending_count)
example_runtime = results.compact_map(&.as?(Spectator::FinishedResult)).sum(&.elapsed)
total_runtime = example_runtime + Time::Span.new(nanoseconds: overhead_time)
Spectator::Report.new(results, total_runtime, fail_blank: fail_blank)
end
describe Spectator::Report do
describe "#initialize(results)" do
describe "#runtime" do
it "is the sum of all results' runtimes" do
results = new_results
runtime = results.compact_map(&.as?(Spectator::FinishedResult)).sum(&.elapsed)
report = Spectator::Report.new(results)
report.runtime.should eq(runtime)
end
end
end
describe "#each" do
it "yields all results" do
results = new_results
report = Spectator::Report.new(results)
# The `#each` method is tested through `Enumerable#to_a`.
report.to_a.should eq(results)
end
end
describe "#runtime" do
it "is the expected value" do
span = Time::Span.new(10, 10, 10)
report = Spectator::Report.new([] of Spectator::Result, span)
report.runtime.should eq(span)
end
end
describe "#example_count" do
it "is the expected value" do
report = new_report(5, 4, 3, 2)
report.example_count.should eq(14)
end
end
describe "#examples_ran" do
it "is the number of non-skipped examples" do
report = new_report(5, 4, 3, 2)
report.examples_ran.should eq(12)
end
end
describe "#successful_count" do
it "is the expected value" do
report = new_report(5, 4, 3, 2)
report.successful_count.should eq(5)
end
end
describe "#failed_count" do
it "is the expected value" do
report = new_report(5, 4, 3, 2)
report.failed_count.should eq(7)
end
end
describe "#error_count" do
it "is the expected value" do
report = new_report(5, 4, 3, 2)
report.error_count.should eq(3)
end
end
describe "#pending_count" do
it "is the expected value" do
report = new_report(5, 4, 3, 2)
report.pending_count.should eq(2)
end
end
describe "#remaining_count" do
it "is the expected value" do
results = [] of Spectator::Result
remaining = 5
report = Spectator::Report.new(results, Time::Span.zero, remaining)
report.remaining_count.should eq(remaining)
end
end
describe "#failed?" do
context "with a failed test suite" do
it "is true" do
report = new_report(5, 4, 3, 2)
report.failed?.should be_true
end
end
context "with a passing test suite" do
it "is false" do
report = new_report(5, 0, 0, 0)
report.failed?.should be_false
end
end
context "with fail-blank enabled" do
context "when no tests run" do
it "is true" do
report = new_report(0, 0, 0, 5, fail_blank: true)
report.failed?.should be_true
end
end
context "when tests run" do
context "and there are failures" do
it "is true" do
report = new_report(5, 4, 3, 2, fail_blank: true)
report.failed?.should be_true
end
end
context "and there are no failures" do
it "is false" do
report = new_report(5, 0, 0, 2, fail_blank: true)
report.failed?.should be_false
end
end
end
end
end
describe "#remaining?" do
context "with remaining tests" do
it "is true" do
results = [] of Spectator::Result
report = Spectator::Report.new(results, Time::Span.zero, 5)
report.remaining?.should be_true
end
end
context "without remaining tests" do
it "is false" do
results = [] of Spectator::Result
report = Spectator::Report.new(results, Time::Span.zero, 0)
report.remaining?.should be_false
end
end
end
describe "#failures" do
it "returns the expected results" do
results = Array.new(5) { new_failure_result.as(Spectator::Result) }
report = Spectator::Report.new(results, Time::Span.zero)
report.failures.to_a.should eq(results)
end
it "includes errors" do
results = Array(Spectator::Result).new(5) do |index|
if index.odd?
new_failure_result
else
new_failure_result(Spectator::ErroredResult)
end
end
report = Spectator::Report.new(results, Time::Span.zero)
report.failures.to_a.should eq(results)
end
end
describe "#errors" do
it "returns the expected results" do
results = Array.new(5) { new_failure_result(Spectator::ErroredResult).as(Spectator::Result) }
report = Spectator::Report.new(results, Time::Span.zero)
report.errors.to_a.should eq(results)
end
it "does not include failures" do
results = Array(Spectator::Result).new(5) do |index|
if index.odd?
new_failure_result
else
new_failure_result(Spectator::ErroredResult)
end
end
report = Spectator::Report.new(results, Time::Span.zero)
errors_only = results.select(&.is_a?(Spectator::ErroredResult))
report.errors.to_a.should eq(errors_only)
end
end
describe "#example_runtime" do
it "is the sum of all example run-times" do
passing_results = Array.new(5) { new_passing_result }
runtime = passing_results.sum(&.elapsed)
results = passing_results.map(&.as(Spectator::Result))
total_runtime = runtime + Time::Span.new(nanoseconds: 1_234_567)
report = Spectator::Report.new(results, total_runtime)
report.example_runtime.should eq(runtime)
end
end
describe "#overhead_time" do
it "is the difference between total runtime and the sum of all example run-times" do
passing_results = Array.new(5) { new_passing_result }
runtime = passing_results.sum(&.elapsed)
results = passing_results.map(&.as(Spectator::Result))
overhead = Time::Span.new(nanoseconds: 1_234_567)
total_runtime = runtime + overhead
report = Spectator::Report.new(results, total_runtime)
report.overhead_time.should eq(overhead)
end
end
end

View file

@ -1,810 +0,0 @@
require "./spec_helper"
def new_root_group(hooks = Spectator::ExampleHooks.empty, conditions = Spectator::ExampleConditions.empty)
Spectator::RootExampleGroup.new(hooks, conditions).tap do |group|
group.children = [] of Spectator::ExampleComponent
end
end
def root_group_with_examples(example_count = 5)
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
examples = [] of Spectator::Example
group.children = Array(Spectator::ExampleComponent).new(example_count) do
PassingExample.new(group, Spectator::Internals::SampleValues.empty).tap do |example|
examples << example
end
end
{group, examples}
end
def root_group_with_sub_groups(sub_group_count = 5, example_count = 5)
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
examples = [] of Spectator::Example
group.children = Array(Spectator::ExampleComponent).new(sub_group_count) do |i|
Spectator::NestedExampleGroup.new(i.to_s, group, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group|
sub_group.children = Array(Spectator::ExampleComponent).new(example_count) do
PassingExample.new(group, Spectator::Internals::SampleValues.empty).tap do |example|
examples << example
end
end
end
end
{group, examples}
end
def complex_root_group
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
examples = [] of Spectator::Example
group.children = Array(Spectator::ExampleComponent).new(10) do |i|
if i % 2 == 0
PassingExample.new(group, Spectator::Internals::SampleValues.empty).tap do |example|
examples << example
end
else
Spectator::NestedExampleGroup.new(i.to_s, group, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group1|
sub_group1.children = Array(Spectator::ExampleComponent).new(10) do |j|
if i % 2 == 0
PassingExample.new(sub_group1, Spectator::Internals::SampleValues.empty).tap do |example|
examples << example
end
else
Spectator::NestedExampleGroup.new(j.to_s, sub_group1, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group2|
sub_group2.children = Array(Spectator::ExampleComponent).new(5) do
PassingExample.new(sub_group2, Spectator::Internals::SampleValues.empty).tap do |example|
examples << example
end
end
end
end
end
end
end
end
{group, examples}
end
describe Spectator::RootExampleGroup do
describe "#run_before_hooks" do
it "runs a before_all hook" do
called = false
hooks = new_hooks(before_all: ->{ called = true; nil })
group = new_root_group(hooks)
group.run_before_hooks
called.should be_true
end
it "runs a before_each hook" do
called = false
hooks = new_hooks(before_each: ->{ called = true; nil })
group = new_root_group(hooks)
group.run_before_hooks
called.should be_true
end
it "runs multiple before_all hooks" do
call_count = 0
hooks = new_hooks(before_all: [
->{ call_count += 1; nil },
->{ call_count += 2; nil },
->{ call_count += 3; nil },
])
group = new_root_group(hooks)
group.run_before_hooks
call_count.should eq(6)
end
it "runs multiple before_each hooks" do
call_count = 0
hooks = new_hooks(before_each: [
->{ call_count += 1; nil },
->{ call_count += 2; nil },
->{ call_count += 3; nil },
])
group = new_root_group(hooks)
group.run_before_hooks
call_count.should eq(6)
end
it "runs hooks in the correct order" do
calls = [] of Symbol
hooks = new_hooks(before_all: [
->{ calls << :a; nil },
->{ calls << :b; nil },
->{ calls << :c; nil },
],
before_each: [
->{ calls << :d; nil },
->{ calls << :e; nil },
->{ calls << :f; nil },
])
group = new_root_group(hooks)
group.run_before_hooks
calls.should eq(%i[a b c d e f])
end
it "runs the before_all hooks once" do
call_count = 0
hooks = new_hooks(before_all: ->{ call_count += 1; nil })
group = new_root_group(hooks)
2.times { group.run_before_hooks }
call_count.should eq(1)
end
it "runs the before_each hooks multiple times" do
call_count = 0
hooks = new_hooks(before_each: ->{ call_count += 1; nil })
group = new_root_group(hooks)
2.times { group.run_before_hooks }
call_count.should eq(2)
end
end
describe "#run_after_hooks" do
# No children are used for most of these examples.
# That's because `[].all?` is always true.
# Which means that all examples are considered finished, since there are none.
it "runs a single after_all hook" do
called = false
hooks = new_hooks(after_all: ->{ called = true; nil })
group = new_root_group(hooks)
group.run_after_hooks
called.should be_true
end
it "runs a single after_each hook" do
called = false
hooks = new_hooks(after_each: ->{ called = true; nil })
group = new_root_group(hooks)
group.run_after_hooks
called.should be_true
end
it "runs multiple after_all hooks" do
call_count = 0
hooks = new_hooks(after_all: [
->{ call_count += 1; nil },
->{ call_count += 2; nil },
->{ call_count += 3; nil },
])
group = new_root_group(hooks)
group.run_after_hooks
call_count.should eq(6)
end
it "runs multiple after_each hooks" do
call_count = 0
hooks = new_hooks(after_all: [
->{ call_count += 1; nil },
->{ call_count += 2; nil },
->{ call_count += 3; nil },
])
group = new_root_group(hooks)
group.run_after_hooks
call_count.should eq(6)
end
it "runs hooks in the correct order" do
calls = [] of Symbol
hooks = new_hooks(after_each: [
->{ calls << :a; nil },
->{ calls << :b; nil },
->{ calls << :c; nil },
],
after_all: [
->{ calls << :d; nil },
->{ calls << :e; nil },
->{ calls << :f; nil },
])
group = new_root_group(hooks)
group.run_after_hooks
calls.should eq(%i[a b c d e f])
end
it "runs the after_all hooks once" do
call_count = 0
hooks = new_hooks(after_all: ->{ call_count += 1; nil })
group = new_root_group(hooks)
2.times { group.run_after_hooks }
call_count.should eq(1)
end
it "runs the after_each hooks multiple times" do
call_count = 0
hooks = new_hooks(after_each: ->{ call_count += 1; nil })
group = new_root_group(hooks)
2.times { group.run_after_hooks }
call_count.should eq(2)
end
context "with no examples finished" do
it "doesn't run the after_all hooks" do
called = false
hooks = new_hooks(after_all: ->{ called = true; nil })
group = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do
PassingExample.new(group, Spectator::Internals::SampleValues.empty)
end
group.run_after_hooks
called.should be_false
end
it "runs the after_each hooks" do
called = false
hooks = new_hooks(after_each: ->{ called = true; nil })
group = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do
PassingExample.new(group, Spectator::Internals::SampleValues.empty)
end
group.run_after_hooks
called.should be_true
end
end
context "with some examples finished" do
it "doesn't run the after_all hooks" do
called = false
examples = [] of Spectator::Example
hooks = new_hooks(after_all: ->{ called = true; nil })
group = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do
PassingExample.new(group, Spectator::Internals::SampleValues.empty).tap do |example|
examples << example
end
end
examples.each_with_index do |example, index|
Spectator::Internals::Harness.run(example) if index % 2 == 0
end
group.run_after_hooks
called.should be_false
end
it "runs the after_each hooks" do
called = false
examples = [] of Spectator::Example
hooks = new_hooks(after_each: ->{ called = true; nil })
group = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do
PassingExample.new(group, Spectator::Internals::SampleValues.empty).tap do |example|
examples << example
end
end
examples.each_with_index do |example, index|
Spectator::Internals::Harness.run(example) if index % 2 == 0
end
group.run_after_hooks
called.should be_true
end
end
end
describe "#wrap_around_each_hooks" do
it "wraps the block" do
called = false
wrapper = new_root_group.wrap_around_each_hooks do
called = true
end
wrapper.call
called.should be_true
end
it "wraps a proc" do
called = false
hooks = new_hooks(around_each: ->(proc : ->) { called = true; proc.call })
wrapper = new_root_group(hooks).wrap_around_each_hooks { }
wrapper.call
called.should be_true
end
it "wraps multiple procs" do
call_count = 0
hooks = new_hooks(around_each: [
->(proc : ->) { call_count += 1; proc.call },
->(proc : ->) { call_count += 2; proc.call },
->(proc : ->) { call_count += 3; proc.call },
])
wrapper = new_root_group(hooks).wrap_around_each_hooks { }
wrapper.call
call_count.should eq(6)
end
it "wraps procs in the correct order" do
calls = [] of Symbol
hooks = new_hooks(around_each: [
->(proc : ->) { calls << :a; proc.call },
->(proc : ->) { calls << :b; proc.call },
->(proc : ->) { calls << :c; proc.call },
])
wrapper = new_root_group(hooks).wrap_around_each_hooks { }
wrapper.call
calls.should eq(%i[a b c])
end
end
describe "#run_pre_conditions" do
it "runs a condition" do
called = false
conditions = new_conditions(pre: ->{ called = true; nil })
group = new_root_group(conditions: conditions)
group.run_pre_conditions
called.should be_true
end
it "runs multiple conditions" do
call_count = 0
conditions = new_conditions(pre: [
->{ call_count += 1; nil },
->{ call_count += 2; nil },
->{ call_count += 3; nil },
])
group = new_root_group(conditions: conditions)
group.run_pre_conditions
call_count.should eq(6)
end
it "runs conditions in the correct order" do
calls = [] of Symbol
conditions = new_conditions(pre: [
->{ calls << :a; nil },
->{ calls << :b; nil },
->{ calls << :c; nil },
])
group = new_root_group(conditions: conditions)
group.run_pre_conditions
calls.should eq(%i[a b c])
end
it "runs the conditions multiple times" do
call_count = 0
conditions = new_conditions(pre: ->{ call_count += 1; nil })
group = new_root_group(conditions: conditions)
2.times { group.run_pre_conditions }
call_count.should eq(2)
end
end
describe "#run_post_conditions" do
it "runs a single condition" do
called = false
conditions = new_conditions(post: ->{ called = true; nil })
group = new_root_group(conditions: conditions)
group.run_post_conditions
called.should be_true
end
it "runs multiple conditions" do
call_count = 0
conditions = new_conditions(post: [
->{ call_count += 1; nil },
->{ call_count += 2; nil },
->{ call_count += 3; nil },
])
group = new_root_group(conditions: conditions)
group.run_post_conditions
call_count.should eq(6)
end
it "runs conditions in the correct order" do
calls = [] of Symbol
conditions = new_conditions(post: [
->{ calls << :a; nil },
->{ calls << :b; nil },
->{ calls << :c; nil },
])
group = new_root_group(conditions: conditions)
group.run_post_conditions
calls.should eq(%i[a b c])
end
it "runs the conditions multiple times" do
call_count = 0
conditions = new_conditions(post: ->{ call_count += 1; nil })
group = new_root_group(conditions: conditions)
2.times { group.run_post_conditions }
call_count.should eq(2)
end
end
describe "#to_s" do
it "is empty" do
new_root_group.to_s.should be_empty
end
end
describe "#children" do
it "raises an error when not set" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
expect_raises(Exception) { group.children }
end
it "returns the expected set" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
children = Array(Spectator::ExampleComponent).new(5) do
PassingExample.new(group, Spectator::Internals::SampleValues.empty)
end
group.children = children
group.children.should eq(children)
end
end
describe "#children=" do
it "raises an error trying to reset" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
children = Array(Spectator::ExampleComponent).new(5) do
PassingExample.new(group, Spectator::Internals::SampleValues.empty)
end
group.children = children
expect_raises(Exception) { group.children = children }
end
end
describe "#each" do
it "yields each child" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do |i|
if i % 2 == 0
PassingExample.new(group, Spectator::Internals::SampleValues.empty)
else
Spectator::NestedExampleGroup.new(i.to_s, group, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group|
sub_group.children = [] of Spectator::ExampleComponent
end
end
end
group.to_a.should eq(group.children)
end
it "doesn't yield children of children" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do |i|
if i % 2 == 0
PassingExample.new(group, Spectator::Internals::SampleValues.empty)
else
Spectator::NestedExampleGroup.new(i.to_s, group, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group|
sub_group.children = Array(Spectator::ExampleComponent).new(5) do
PassingExample.new(sub_group, Spectator::Internals::SampleValues.empty)
end
end
end
end
group.to_a.should eq(group.children)
end
end
describe "#each : Iterator" do
it "iterates over each child" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do |i|
if i % 2 == 0
PassingExample.new(group, Spectator::Internals::SampleValues.empty)
else
Spectator::NestedExampleGroup.new(i.to_s, group, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group|
sub_group.children = [] of Spectator::ExampleComponent
end
end
end
group.each.to_a.should eq(group.children)
end
it "doesn't iterate over children of children" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do |i|
if i % 2 == 0
PassingExample.new(group, Spectator::Internals::SampleValues.empty)
else
Spectator::NestedExampleGroup.new(i.to_s, group, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group|
sub_group.children = Array(Spectator::ExampleComponent).new(5) do
PassingExample.new(sub_group, Spectator::Internals::SampleValues.empty)
end
end
end
end
group.each.to_a.should eq(group.children)
end
end
describe "#example_count" do
context "with no examples" do
it "is zero" do
new_root_group.example_count.should eq(0)
end
end
context "with empty sub-groups" do
it "is zero" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do |i|
Spectator::NestedExampleGroup.new(i.to_s, group, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
end
group.example_count.should eq(0)
end
end
context "with direct descendant examples" do
it "equals the number of examples" do
example_count = 10
group, _ = root_group_with_examples(example_count)
group.example_count.should eq(example_count)
end
end
context "with examples sub-groups" do
it "equals the total number of examples" do
sub_group_count = 3
example_count = 10
group, _ = root_group_with_sub_groups(sub_group_count, example_count)
group.example_count.should eq(sub_group_count * example_count)
end
end
context "with examples at all levels" do
it "equals the total number of examples" do
group, examples = complex_root_group
group.example_count.should eq(examples.size)
end
end
end
describe "#[]" do
context "when empty" do
it "raises an error" do
group = new_root_group
expect_raises(IndexError) { group[0] }
end
end
context "with direct descendant examples" do
context "given 0" do
it "returns the first example" do
group, examples = root_group_with_examples
group[0].should eq(examples.first)
end
end
context "given -1" do
it "returns the last example" do
group, examples = root_group_with_examples
group[-1].should eq(examples.last)
end
end
context "given an in-bounds positive index" do
it "returns the expected example" do
group, examples = root_group_with_examples(10)
group[3].should eq(examples[3])
end
end
context "given an in-bounds negative index" do
it "returns the expected example" do
group, examples = root_group_with_examples(10)
group[-3].should eq(examples[-3])
end
end
context "an out-of-bounds positive index" do
it "raises an index error" do
group, _ = root_group_with_examples(10)
expect_raises(IndexError) { group[15] }
end
it "handles off-by-one" do
group, _ = root_group_with_examples(10)
expect_raises(IndexError) { group[10] }
end
end
context "an out-of-bounds negative index" do
it "raises an index error" do
group, _ = root_group_with_examples(10)
expect_raises(IndexError) { group[-15] }
end
it "handles off-by-one" do
group, _ = root_group_with_examples(10)
expect_raises(IndexError) { group[-11] }
end
end
end
context "with examples only in sub-groups" do
context "given 0" do
it "returns the first example" do
group, examples = root_group_with_sub_groups
group[0].should eq(examples.first)
end
end
context "given -1" do
it "returns the last example" do
group, examples = root_group_with_sub_groups
group[-1].should eq(examples.last)
end
end
context "given an in-bounds positive index" do
it "returns the expected example" do
group, examples = root_group_with_sub_groups(10, 2)
group[6].should eq(examples[6])
end
end
context "given an in-bounds negative index" do
it "returns the expected example" do
group, examples = root_group_with_sub_groups(10, 2)
group[-6].should eq(examples[-6])
end
end
context "an out-of-bounds positive index" do
it "raises an index error" do
group, _ = root_group_with_sub_groups(10, 2)
expect_raises(IndexError) { group[25] }
end
it "handles off-by-one" do
group, _ = root_group_with_sub_groups(10, 2)
expect_raises(IndexError) { group[20] }
end
end
context "an out-of-bounds negative index" do
it "raises an index error" do
group, _ = root_group_with_sub_groups(10, 2)
expect_raises(IndexError) { group[-25] }
end
it "handles off-by-one" do
group, _ = root_group_with_sub_groups(10, 2)
expect_raises(IndexError) { group[-21] }
end
end
end
context "with examples at all levels" do
context "given 0" do
it "returns the first example" do
group, examples = complex_root_group
group[0].should eq(examples.first)
end
end
context "given -1" do
it "returns the last example" do
group, examples = complex_root_group
group[-1].should eq(examples.last)
end
end
context "given an in-bounds positive index" do
it "returns the expected example" do
group, examples = complex_root_group
group[42].should eq(examples[42])
end
end
context "given an in-bounds negative index" do
it "returns the expected example" do
group, examples = complex_root_group
group[-42].should eq(examples[-42])
end
end
context "an out-of-bounds positive index" do
it "raises an index error" do
group, examples = complex_root_group
expect_raises(IndexError) { group[examples.size + 5] }
end
it "handles off-by-one" do
group, examples = complex_root_group
expect_raises(IndexError) { group[examples.size] }
end
end
context "an out-of-bounds negative index" do
it "raises an index error" do
group, examples = complex_root_group
expect_raises(IndexError) { group[-examples.size - 5] }
end
it "handles off-by-one" do
group, examples = complex_root_group
expect_raises(IndexError) { group[-examples.size - 1] }
end
end
end
context "with only sub-groups and no examples" do
it "raises an index error" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do |i|
Spectator::NestedExampleGroup.new(i.to_s, group, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group|
sub_group.children = [] of Spectator::ExampleComponent
end
end
expect_raises(IndexError) { group[0] }
end
end
end
describe "#finished?" do
context "with no children" do
it "is true" do
new_root_group.finished?.should be_true
end
end
context "with all unfinished children" do
it "is false" do
group, _ = root_group_with_examples
group.finished?.should be_false
end
end
context "with some finished children" do
it "is false" do
group, examples = root_group_with_examples
examples.each_with_index do |example, index|
Spectator::Internals::Harness.run(example) if index % 2 == 0
end
group.finished?.should be_false
end
end
context "with all finished children" do
it "is true" do
group, examples = root_group_with_examples
examples.each do |example|
Spectator::Internals::Harness.run(example)
end
group.finished?.should be_true
end
end
context "with a sub-group" do
context "with no children" do
it "is true" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array(Spectator::ExampleComponent).new(5) do |i|
Spectator::NestedExampleGroup.new(i.to_s, group, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty).tap do |sub_group|
sub_group.children = [] of Spectator::ExampleComponent
end
end
group.finished?.should be_true
end
end
context "with all unfinished children" do
it "is false" do
group, _ = root_group_with_sub_groups
group.finished?.should be_false
end
end
context "with some finished children" do
it "is false" do
group, examples = root_group_with_sub_groups
examples.each_with_index do |example, index|
Spectator::Internals::Harness.run(example) if index % 2 == 0
end
group.finished?.should be_false
end
end
context "with all finished children" do
it "is true" do
group, examples = root_group_with_sub_groups
examples.each do |example|
Spectator::Internals::Harness.run(example)
end
group.finished?.should be_true
end
end
end
end
describe "#symbolic?" do
it "is true" do
new_root_group.symbolic?.should be_true
end
end
end

File diff suppressed because it is too large Load diff

View file

@ -1,270 +0,0 @@
require "./spec_helper"
# Creates a `Config` for Spectator that is suited for testing it.
def spectator_test_config(formatter : Spectator::Formatting::Formatter? = nil, fail_fast = false)
builder = Spectator::ConfigBuilder.new
builder.formatter = formatter || Spectator::Formatting::SilentFormatter.new
builder.fail_fast = fail_fast
builder.build
end
def new_test_suite(group : Spectator::ExampleGroup? = nil)
filter = Spectator::NullExampleFilter.new
Spectator::TestSuite.new(group || PassingExample.create.group, filter)
end
def suite_with_nested_failures(hooks)
conditions = Spectator::ExampleConditions.empty
values = Spectator::Internals::SampleValues.empty
root = Spectator::RootExampleGroup.new(hooks, conditions)
root.children = Array(Spectator::ExampleComponent).new(5) do |index|
Spectator::NestedExampleGroup.new(index.to_s, root, hooks, conditions).tap do |group|
group.children = Array(Spectator::ExampleComponent).new(5) { FailingExample.new(group, values) }
end
end
new_test_suite(root)
end
describe Spectator::Runner do
describe "#run" do
it "runs all examples in the suite" do
called = [] of Int32
group = SpyExample.create_group(5) do |index|
called << index
nil
end
suite = new_test_suite(group)
runner = Spectator::Runner.new(suite, spectator_test_config)
runner.run
called.should eq([0, 1, 2, 3, 4])
end
context "with fail-fast enabled" do
it "stops on the first failure" do
called = [] of Int32
group = SpyExample.create_group(10) do |index|
called << index
raise "Failure" if index > 5
end
suite = new_test_suite(group)
runner = Spectator::Runner.new(suite, spectator_test_config(fail_fast: true))
runner.run
called.should eq([0, 1, 2, 3, 4, 5, 6])
end
it "runs after_each hooks" do
called = false
hooks = new_hooks(after_each: ->{ called = true; nil })
group = FailingExample.create_group(hooks: hooks)
suite = new_test_suite(group)
runner = Spectator::Runner.new(suite, spectator_test_config(fail_fast: true))
runner.run
called.should be_true
end
it "runs after_all hooks" do
called = false
hooks = new_hooks(after_all: ->{ called = true; nil })
group = FailingExample.create_group(hooks: hooks)
suite = new_test_suite(group)
runner = Spectator::Runner.new(suite, spectator_test_config(fail_fast: true))
runner.run
called.should be_true
end
it "runs the remaining around_each hook code" do
called = false
hooks = new_hooks(around_each: ->(proc : ->) {
proc.call
called = true
nil
})
group = FailingExample.create_group(hooks: hooks)
suite = new_test_suite(group)
runner = Spectator::Runner.new(suite, spectator_test_config(fail_fast: true))
runner.run
called.should be_true
end
context "with nested groups" do
it "runs after_each hooks" do
call_count = 0
hooks = new_hooks(after_each: ->{ call_count += 1; nil })
suite = suite_with_nested_failures(hooks)
runner = Spectator::Runner.new(suite, spectator_test_config(fail_fast: true))
runner.run
call_count.should eq(2)
end
it "runs after_all hooks" do
call_count = 0
hooks = new_hooks(after_all: ->{ call_count += 1; nil })
suite = suite_with_nested_failures(hooks)
runner = Spectator::Runner.new(suite, spectator_test_config(fail_fast: true))
runner.run
call_count.should eq(2)
end
it "runs the remaining around_each hook code" do
call_count = 0
hooks = new_hooks(around_each: ->(proc : ->) {
proc.call
call_count += 1
nil
})
suite = suite_with_nested_failures(hooks)
runner = Spectator::Runner.new(suite, spectator_test_config(fail_fast: true))
runner.run
call_count.should eq(2)
end
end
context "the report" do
it "has the remaining tests" do
spy = SpyFormatter.new
group = SpyExample.create_group(10) do |index|
raise "Failure" if index > 5
end
suite = new_test_suite(group)
runner = Spectator::Runner.new(suite, spectator_test_config(spy, true))
runner.run
args = spy.end_suite_calls.first
args[:report].remaining_count.should eq(3)
end
end
end
context "the formatter" do
it "#start_suite is called once" do
spy = SpyFormatter.new
runner = Spectator::Runner.new(new_test_suite, spectator_test_config(spy))
runner.run
spy.start_suite_call_count.should eq(1)
end
it "#start_suite is called at the beginning" do
spy = SpyFormatter.new
runner = Spectator::Runner.new(new_test_suite, spectator_test_config(spy))
runner.run
spy.all_calls.first.should eq(:start_suite)
end
it "passes the test suite to #start_suite" do
test_suite = new_test_suite
spy = SpyFormatter.new
runner = Spectator::Runner.new(test_suite, spectator_test_config(spy))
runner.run
spy.start_suite_calls.first.should eq(test_suite)
end
it "#end_suite is called once" do
spy = SpyFormatter.new
runner = Spectator::Runner.new(new_test_suite, spectator_test_config(spy))
runner.run
spy.end_suite_call_count.should eq(1)
end
it "#end_suite is called at the end" do
spy = SpyFormatter.new
runner = Spectator::Runner.new(new_test_suite, spectator_test_config(spy))
runner.run
spy.all_calls.last.should eq(:end_suite)
end
it "#start_example is called" do
spy = SpyFormatter.new
runner = Spectator::Runner.new(new_test_suite, spectator_test_config(spy))
runner.run
spy.start_example_call_count.should be > 0
end
it "#start_example is called for each example" do
group = SpyExample.create_group(5) { nil }
suite = new_test_suite(group)
spy = SpyFormatter.new
runner = Spectator::Runner.new(suite, spectator_test_config(spy))
runner.run
spy.start_example_call_count.should eq(5)
end
it "passes the correct example to #start_example" do
group = SpyExample.create_group(5) { nil }
suite = new_test_suite(group)
spy = SpyFormatter.new
runner = Spectator::Runner.new(suite, spectator_test_config(spy))
runner.run
spy.start_example_calls.should eq(group.children)
end
it "calls #end_example" do
spy = SpyFormatter.new
runner = Spectator::Runner.new(new_test_suite, spectator_test_config(spy))
runner.run
spy.end_example_call_count.should be > 0
end
it "calls #end_example for each example" do
group = SpyExample.create_group(5) { nil }
suite = new_test_suite(group)
spy = SpyFormatter.new
runner = Spectator::Runner.new(suite, spectator_test_config(spy))
runner.run
spy.end_example_call_count.should eq(5)
end
it "passes the correct result to #end_example" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array.new(5) do |index|
if index.odd?
PassingExample.new(group, Spectator::Internals::SampleValues.empty)
else
FailingExample.new(group, Spectator::Internals::SampleValues.empty)
end.as(Spectator::ExampleComponent)
end
suite = new_test_suite(group)
spy = SpyFormatter.new
runner = Spectator::Runner.new(suite, spectator_test_config(spy))
runner.run
spy.end_example_calls.map(&.example).should eq(group.children)
end
end
context "the report" do
it "contains the expected results" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array.new(5) do |index|
if index.odd?
PassingExample.new(group, Spectator::Internals::SampleValues.empty)
else
FailingExample.new(group, Spectator::Internals::SampleValues.empty)
end.as(Spectator::ExampleComponent)
end
suite = new_test_suite(group)
spy = SpyFormatter.new
runner = Spectator::Runner.new(suite, spectator_test_config(spy))
runner.run
args = spy.end_suite_calls.first
args[:report].each_with_index do |result, index|
if index.odd?
result.should be_a(Spectator::SuccessfulResult)
else
result.should be_a(Spectator::FailedResult)
end
end
end
it "contains the expected time span" do
group = SpyExample.create_group(5) { nil }
suite = new_test_suite(group)
spy = SpyFormatter.new
runner = Spectator::Runner.new(suite, spectator_test_config(spy))
max_time = Time.measure { runner.run }
min_time = spy.end_example_calls.each.map(&.as(Spectator::FinishedResult)).sum(&.elapsed)
args = spy.end_suite_calls.first
report = args[:report]
report.runtime.should be <= max_time
report.runtime.should be >= min_time
end
end
end
end

View file

@ -1,22 +0,0 @@
require "./spec_helper"
describe Spectator::SourceExampleFilter do
describe "#includes?" do
context "with a matching example" do
it "is true" do
example = PassingExample.create
filter = Spectator::SourceExampleFilter.new(example.source)
filter.includes?(example).should be_true
end
end
context "with a non-matching example" do
it "is false" do
example = PassingExample.create
source = Spectator::Source.new(__FILE__, __LINE__)
filter = Spectator::SourceExampleFilter.new(source)
filter.includes?(example).should be_false
end
end
end
end

View file

@ -1,76 +0,0 @@
require "./spec_helper"
describe Spectator::Source do
describe "#file" do
it "is the expected value" do
file = __FILE__
source = Spectator::Source.new(file, __LINE__)
source.file.should eq(file)
end
end
describe "#line" do
it "is the expected value" do
line = __LINE__
source = Spectator::Source.new(__FILE__, line)
source.line.should eq(line)
end
end
describe "#path" do
context "with a relative file" do
it "is shortened" do
file = "test.cr"
absolute = File.join(Dir.current, file)
source = Spectator::Source.new(absolute, __LINE__)
source.path.should eq(file)
end
end
context "with a different directory" do
it "is the absolute path" do
file = "/foo/bar/baz.cr"
source = Spectator::Source.new(file, __LINE__)
source.path.should eq(file)
end
end
end
describe "#to_s" do
it "contains #path" do
file = __FILE__
source = Spectator::Source.new(file, __LINE__)
source.to_s.should contain(source.path)
end
it "contains #line" do
line = __LINE__
source = Spectator::Source.new(__FILE__, line)
source.to_s.should contain(line.to_s)
end
it "is formatted correctly" do
source = Spectator::Source.new(__FILE__, __LINE__)
source.to_s.should match(/^(.+?)\:(\d+)$/)
end
end
describe "#parse" do
it "gets the absolute path" do
file = "foo.cr"
path = File.expand_path(file)
source = Spectator::Source.parse("#{file}:42")
source.file.should eq(path)
end
it "gets the relative path" do
source = Spectator::Source.parse("foo.cr:42")
source.path.should eq("foo.cr")
end
it "gets the line number" do
source = Spectator::Source.parse("foo.cr:42")
source.line.should eq(42)
end
end
end

View file

@ -1,6 +1,4 @@
require "spec"
require "../src/spectator"
require "./helpers/*"
# Prevent Spectator from trying to run tests.
# Prevent Spectator from trying to run tests on its own.
Spectator.autorun = false

View file

@ -1,77 +0,0 @@
require "./spec_helper"
def new_successful_result(
example : Spectator::Example? = nil,
elapsed : Time::Span? = nil,
expectations : Spectator::Expectations::ExampleExpectations? = nil
)
Spectator::SuccessfulResult.new(
example || PassingExample.create,
elapsed || Time::Span.zero,
expectations || Spectator::Expectations::ExampleExpectations.new([new_satisfied_expectation])
)
end
describe Spectator::SuccessfulResult do
describe "#call" do
context "without a block" do
it "invokes #success on an instance" do
spy = ResultCallSpy.new
new_successful_result.call(spy)
spy.success?.should be_true
end
it "returns the value of #success" do
result = new_successful_result
returned = result.call(ResultCallSpy.new)
returned.should eq(:success)
end
end
context "with a block" do
it "invokes #success on an instance" do
spy = ResultCallSpy.new
new_successful_result.call(spy) { nil }
spy.success?.should be_true
end
it "yields itself" do
result = new_successful_result
value = nil.as(Spectator::Result?)
result.call(ResultCallSpy.new) { |r| value = r }
value.should eq(result)
end
it "returns the value of #success" do
result = new_successful_result
value = 42
returned = result.call(ResultCallSpy.new) { value }
returned.should eq(value)
end
end
end
describe "#example" do
it "is the expected value" do
example = PassingExample.create
result = new_successful_result(example: example)
result.example.should eq(example)
end
end
describe "#elapsed" do
it "is the expected value" do
elapsed = Time::Span.new(10, 10, 10)
result = new_successful_result(elapsed: elapsed)
result.elapsed.should eq(elapsed)
end
end
describe "#expectations" do
it "is the expected value" do
expectations = Spectator::Expectations::ExampleExpectations.new(create_expectations(5, 0))
result = new_successful_result(expectations: expectations)
result.expectations.should eq(expectations)
end
end
end

View file

@ -1,31 +0,0 @@
require "./spec_helper"
describe Spectator::TestSuite do
describe "#each" do
it "yields each example" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array.new(5) do
PassingExample.new(group, Spectator::Internals::SampleValues.empty).as(Spectator::ExampleComponent)
end
test_suite = Spectator::TestSuite.new(group, Spectator::NullExampleFilter.new)
examples = [] of Spectator::Example
test_suite.each do |example|
examples << example
end
examples.should eq(group.children)
end
it "skips examples not in the filter" do
group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty)
group.children = Array.new(5) do
PassingExample.new(group, Spectator::Internals::SampleValues.empty).as(Spectator::ExampleComponent)
end
test_suite = Spectator::TestSuite.new(group, Spectator::CompositeExampleFilter.new([] of Spectator::ExampleFilter))
examples = [] of Spectator::Example
test_suite.each do |example|
examples << example
end
examples.should be_empty
end
end
end

View file

@ -1,11 +1,12 @@
require "./spectator/includes"
require "./spectator_test"
# Module that contains all functionality related to Spectator.
module Spectator
extend self
# Current version of the Spectator library.
VERSION = "0.8.2"
VERSION = "0.9.0"
# Top-level describe method.
# All specs in a file must be wrapped in this call.
@ -23,7 +24,7 @@ module Spectator
# NOTE: Inside the block, the `Spectator` prefix is no longer needed.
# Actually, prefixing methods and macros with `Spectator`
# most likely won't work and can cause compiler errors.
macro describe(what, &block)
macro describe(description, &block)
# This macro creates the foundation for all specs.
# Every group of examples is defined a separate module - `SpectatorExamples`.
# There's multiple reasons for this.
@ -32,30 +33,21 @@ module Spectator
# We don't want the spec code to accidentally pickup types and values from the `Spectator` module.
# Another reason is that we need a root module to put all examples and groups in.
# And lastly, the spec DSL needs to be given to the block of code somehow.
# The DSL is included in the `SpectatorExamples` module.
# The DSL is included in the `SpectatorTest` class.
#
# For more information on how the DSL works, see the `DSL` module.
# Root-level module that contains all examples and example groups.
module SpectatorExamples
# Include the DSL for creating groups, example, and more.
include ::Spectator::DSL::StructureDSL
# Placeholder initializer.
# This is needed because examples and groups call super in their initializer.
# Those initializers pass the sample values upward through their hierarchy.
def initialize(_sample_values : ::Spectator::Internals::SampleValues)
end
# Pass off the "what" argument and block to `DSL::StructureDSL.describe`.
# Root-level class that contains all examples and example groups.
class SpectatorTest
# Pass off the description argument and block to `DSL::StructureDSL.describe`.
# That method will handle creating a new group for this spec.
describe({{what}}) {{block}}
describe({{description}}) {{block}}
end
end
# ditto
macro context(what, &block)
describe({{what}}) {{block}}
macro context(description, &block)
describe({{description}}) {{block}}
end
# Flag indicating whether Spectator should automatically run tests.
@ -109,7 +101,7 @@ module Spectator
# Builds the tests and runs the framework.
private def run
# Build the test suite and run it.
suite = ::Spectator::DSL::Builder.build(config.example_filter)
suite = ::Spectator::SpecBuilder.build(config.example_filter)
Runner.new(suite, config).run
rescue ex
# Catch all unhandled exceptions here.

View file

@ -2,7 +2,6 @@ require "./dsl/*"
module Spectator
# Namespace containing methods representing the spec domain specific language.
# Also contains builders to generate classes and instances to later run the spec.
module DSL
end
end

View file

@ -2,14 +2,9 @@ require "../expectations/expectation_partial"
require "../source"
require "../test_block"
require "../test_value"
require "./matcher_dsl"
module Spectator::DSL
# Methods that are available inside test code.
# Basically, inside an `StructureDSL#it` block.
module ExampleDSL
include MatcherDSL
module Spectator
module DSL
# Starts an expectation.
# This should be followed up with `Spectator::Expectations::ExpectationPartial#to`
# or `Spectator::Expectations::ExpectationPartial#to_not`.
@ -23,9 +18,9 @@ module Spectator::DSL
# Where the actual value is returned by the system-under-test,
# and the expected value is what the actual value should be to satisfy the condition.
macro expect(actual, _source_file = __FILE__, _source_line = __LINE__)
test_value = ::Spectator::TestValue.new({{actual}}, {{actual.stringify}})
source = ::Spectator::Source.new({{_source_file}}, {{_source_line}})
::Spectator::Expectations::ExpectationPartial.new(test_value, source)
%test_value = ::Spectator::TestValue.new({{actual}}, {{actual.stringify}})
%source = ::Spectator::Source.new({{_source_file}}, {{_source_line}})
::Spectator::Expectations::ExpectationPartial.new(%test_value, %source)
end
# Starts an expectation on a block of code.

View file

@ -1,15 +0,0 @@
module Spectator::DSL
# Creates instances of examples from a specified class.
class ExampleFactory
# Creates the factory.
# The type passed to this constructor must be a sub-type of `Example`.
def initialize(@example_type : Example.class)
end
# Constructs a new example instance and returns it.
# The *group* and *sample_values* are passed to `Example#initialize`.
def build(group : ExampleGroup, sample_values : Internals::SampleValues) : Example
@example_type.new(group, sample_values)
end
end
end

View file

@ -1,93 +0,0 @@
module Spectator::DSL
# Base class for building all example groups.
abstract class ExampleGroupBuilder
# Type alias for valid children of example groups.
# NOTE: `NestedExampleGroupBuilder` is used instead of `ExampleGroupBuilder`.
# That is because `RootExampleGroupBuilder` also inherits from this class,
# and the root example group can't be a child.
alias Child = ExampleFactory | NestedExampleGroupBuilder
private getter doubles = {} of Symbol => DoubleFactory
# Factories and builders for all examples and groups.
@children = [] of Child
# Hooks added to the group so far.
@before_all_hooks = [] of ->
@before_each_hooks = [] of ->
@after_all_hooks = [] of ->
@after_each_hooks = [] of ->
@around_each_hooks = [] of Proc(Nil) ->
# Pre and post conditions so far.
@pre_conditions = [] of ->
@post_conditions = [] of ->
# Adds a new example factory or group builder to this group.
def add_child(child : Child)
@children << child
end
# Adds a hook to run before all examples (and nested examples) in this group.
def add_before_all_hook(block : ->) : Nil
@before_all_hooks << block
end
# Adds a hook to run before each example (and nested example) in this group.
def add_before_each_hook(block : ->) : Nil
@before_each_hooks << block
end
# Adds a hook to run after all examples (and nested examples) in this group.
def add_after_all_hook(block : ->) : Nil
@after_all_hooks << block
end
# Adds a hook to run after each example (and nested example) in this group.
def add_after_each_hook(block : ->) : Nil
@after_each_hooks << block
end
# Adds a hook to run around each example (and nested example) in this 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
@around_each_hooks << block
end
# Adds a pre-condition to run at the start of every example in this group.
def add_pre_condition(block : ->) : Nil
@pre_conditions << block
end
# Adds a post-condition to run at the end of every example in this group.
def add_post_condition(block : ->) : Nil
@post_conditions << block
end
def add_double(id : Symbol, double_factory : DoubleFactory) : Nil
@doubles[id] = double_factory
end
# Constructs an `ExampleHooks` instance with all the hooks defined for this group.
# This method should be called only when the group is being built,
# otherwise some hooks may be missing.
private def hooks
ExampleHooks.new(
@before_all_hooks,
@before_each_hooks,
@after_all_hooks,
@after_each_hooks,
@around_each_hooks
)
end
# Constructs an `ExampleConditions` instance
# with all the pre- and post-conditions defined for this group.
# This method should be called only when the group is being built,
# otherwise some conditions may be missing.
private def conditions
ExampleConditions.new(@pre_conditions, @post_conditions)
end
end
end

View file

@ -0,0 +1,64 @@
require "../source"
require "../spec_builder"
module Spectator
module DSL
macro it(description, _source_file = __FILE__, _source_line = __LINE__, &block)
{% if block.is_a?(Nop) %}
{% if description.is_a?(Call) %}
def %run
{{description}}
end
{% else %}
{% raise "Unrecognized syntax: `it #{description}` at #{_source_file}:#{_source_line}" %}
{% end %}
{% else %}
def %run
{{block.body}}
end
{% end %}
%source = ::Spectator::Source.new({{_source_file}}, {{_source_line}})
::Spectator::SpecBuilder.add_example(
{{description.is_a?(StringLiteral) ? description : description.stringify}},
%source,
{{@type.name}}
) { |test| test.as({{@type.name}}).%run }
end
macro specify(description, &block)
it({{description}}) {{block}}
end
macro pending(description, _source_file = __FILE__, _source_line = __LINE__, &block)
{% if block.is_a?(Nop) %}
{% if description.is_a?(Call) %}
def %run
{{description}}
end
{% else %}
{% raise "Unrecognized syntax: `pending #{description}` at #{_source_file}:#{_source_line}" %}
{% end %}
{% else %}
def %run
{{block.body}}
end
{% end %}
%source = ::Spectator::Source.new({{_source_file}}, {{_source_line}})
::Spectator::SpecBuilder.add_pending_example(
{{description.is_a?(StringLiteral) ? description : description.stringify}},
%source,
{{@type.name}}
) { |test| test.as({{@type.name}}).%run }
end
macro skip(description, &block)
pending({{description}}) {{block}}
end
macro xit(description, &block)
pending({{description}}) {{block}}
end
end
end

144
src/spectator/dsl/groups.cr Normal file
View file

@ -0,0 +1,144 @@
require "../spec_builder"
module Spectator
module DSL
macro context(what, _source_file = __FILE__, _source_line = __LINE__, &block)
class Context%context < {{@type.id}}
{%
description = if what.is_a?(StringLiteral)
if what.starts_with?("#") || what.starts_with?(".")
what.id.symbolize
else
what
end
else
what.symbolize
end
%}
%source = ::Spectator::Source.new({{_source_file}}, {{_source_line}})
::Spectator::SpecBuilder.start_group({{description}}, %source)
{% if what.is_a?(Path) || what.is_a?(Generic) %}
macro described_class
{{what}}
end
def subject(*args)
described_class.new(*args)
end
{% end %}
{{block.body}}
::Spectator::SpecBuilder.end_group
end
end
macro describe(what, &block)
context({{what}}) {{block}}
end
macro sample(collection, count = nil, _source_file = __FILE__, _source_line = __LINE__, &block)
{% name = block.args.empty? ? :value.id : block.args.first.id %}
def %collection
{{collection}}
end
def %to_a
{% if count %}
%collection.first({{count}})
{% else %}
%collection.to_a
{% end %}
end
class Context%sample < {{@type.id}}
%source = ::Spectator::Source.new({{_source_file}}, {{_source_line}})
::Spectator::SpecBuilder.start_sample_group({{collection.stringify}}, %source, :%sample, {{name.stringify}}) do |values|
sample = {{@type.id}}.new(values)
sample.%to_a
end
def {{name}}
@spectator_test_values.get_value(:%sample, typeof(%to_a.first))
end
{{block.body}}
::Spectator::SpecBuilder.end_group
end
end
macro random_sample(collection, count = nil, _source_file = __FILE__, _source_line = __LINE__, &block)
{% name = block.args.empty? ? :value.id : block.args.first.id %}
def %collection
{{collection}}
end
def %to_a
{% if count %}
%collection.first({{count}})
{% else %}
%collection.to_a
{% end %}
end
class Context%sample < {{@type.id}}
%source = ::Spectator::Source.new({{_source_file}}, {{_source_line}})
::Spectator::SpecBuilder.start_sample_group({{collection.stringify}}, %source, :%sample, {{name.stringify}}) do |values|
sample = {{@type.id}}.new(values)
collection = sample.%to_a
{% if count %}
collection.sample({{count}}, ::Spectator.random)
{% else %}
collection.shuffle(::Spectator.random)
{% end %}
end
def {{name}}
@spectator_test_values.get_value(:%sample, typeof(%to_a.first))
end
{{block.body}}
::Spectator::SpecBuilder.end_group
end
end
macro given(*assignments, &block)
context({{assignments.splat.stringify}}) do
{% for assignment in assignments %}
let({{assignment.target}}) { {{assignment.value}} }
{% end %}
{% # Trick to get the contents of the block as an array of nodes.
# If there are multiple expressions/statements in the block,
# then the body will be a `Expressions` type.
# If there's only one expression, then the body is just that.
body = if block.is_a?(Nop)
raise "Missing block for 'given'"
elsif block.body.is_a?(Expressions)
# Get the expressions, which is already an array.
block.body.expressions
else
# Wrap the expression in an array.
[block.body]
end %}
{% for item in body %}
# If the item starts with "it", then leave it as-is.
# Otherwise, prefix it with "it"
# and treat it as the one-liner "it" syntax.
{% if item.is_a?(Call) && item.name == :it.id %}
{{item}}
{% else %}
it {{item}}
{% end %}
{% end %}
end
end
end
end

View file

@ -0,0 +1,79 @@
module Spectator
module DSL
macro before_each(&block)
def %hook({{block.args.splat}}) : Nil
{{block.body}}
end
::Spectator::SpecBuilder.add_before_each_hook do |test, example|
cast_test = test.as({{@type.id}})
{% if block.args.empty? %}
cast_test.%hook
{% else %}
cast_test.%hook(example)
{% end %}
end
end
macro after_each(&block)
def %hook({{block.args.splat}}) : Nil
{{block.body}}
end
::Spectator::SpecBuilder.add_after_each_hook do |test, example|
cast_test = test.as({{@type.id}})
{% if block.args.empty? %}
cast_test.%hook
{% else %}
cast_test.%hook(example)
{% end %}
end
end
macro before_all(&block)
::Spectator::SpecBuilder.add_before_all_hook {{block}}
end
macro after_all(&block)
::Spectator::SpecBuilder.add_after_all_hook {{block}}
end
macro around_each(&block)
def %hook({{block.args.splat}}) : Nil
{{block.body}}
end
::Spectator::SpecBuilder.add_around_each_hook { |test, proc| test.as({{@type.id}}).%hook(proc) }
end
macro pre_condition(&block)
def %hook({{block.args.splat}}) : Nil
{{block.body}}
end
::Spectator::SpecBuilder.add_pre_condition do |test, example|
cast_test = test.as({{@type.id}})
{% if block.args.empty? %}
cast_test.%hook
{% else %}
cast_test.%hook(example)
{% end %}
end
end
macro post_condition(&block)
def %hook({{block.args.splat}}) : Nil
{{block.body}}
end
::Spectator::SpecBuilder.add_post_condition do |test, example|
cast_test = test.as({{@type.id}})
{% if block.args.empty? %}
cast_test.%hook
{% else %}
cast_test.%hook(example)
{% end %}
end
end
end
end

View file

@ -2,9 +2,8 @@ require "../matchers"
require "../test_block"
require "../test_value"
module Spectator::DSL
# Methods for defining matchers for expectations.
module MatcherDSL
module Spectator
module DSL
# Indicates that some value should equal another.
# The == operator is used for this check.
# The value passed to this method is the expected value.

View file

@ -1,38 +0,0 @@
module Spectator::DSL
# Standard example group builder.
# Creates groups of examples and nested groups.
class NestedExampleGroupBuilder < ExampleGroupBuilder
# Creates a new group builder.
# The value for *what* should be the context for the group.
#
# For example, in these samples:
# ```
# describe String do
# # ...
# context "with an empty string" do
# # ...
# end
# end
# ```
# The value would be "String" for the describe block
# and "with an empty string" for the context block.
# Use a `Symbol` when referencing a type name.
def initialize(@what : Symbol | String)
end
# Builds the example group.
# A new `NestedExampleGroup` will be returned
# which can have instances of `Example` and `ExampleGroup` nested in it.
# The *parent* should be the group that contains this group.
# The *sample_values* will be given to all of the examples (and groups) nested in this group.
def build(parent : ExampleGroup, sample_values : Internals::SampleValues) : NestedExampleGroup
NestedExampleGroup.new(@what, parent, hooks, conditions, doubles).tap do |group|
# Set the group's children to built versions of the children from this instance.
group.children = @children.map do |child|
# Build the child and up-cast to prevent type errors.
child.build(group, sample_values).as(ExampleComponent)
end
end
end
end
end

View file

@ -1,18 +0,0 @@
module Spectator::DSL
# Top-level example group builder.
# There should only be one instance of this class,
# and it should be at the top of the spec "tree".
class RootExampleGroupBuilder < ExampleGroupBuilder
# Creates a `RootExampleGroup` which can have instances of `Example` and `ExampleGroup` nested in it.
# The *sample_values* will be given to all of the examples (and groups) nested in this group.
def build(sample_values : Internals::SampleValues) : RootExampleGroup
RootExampleGroup.new(hooks, conditions, doubles).tap do |group|
# Set the group's children to built versions of the children from this instance.
group.children = @children.map do |child|
# Build the child and up-cast to prevent type errors.
child.build(group, sample_values).as(ExampleComponent)
end
end
end
end
end

View file

@ -1,73 +0,0 @@
require "./nested_example_group_builder"
module Spectator::DSL
# Specialized example group builder for "sample" groups.
# The type parameter `C` is the type to instantiate to create the collection.
# The type parameter `T` should be the type of each element in the sample collection.
# This builder creates a container group with groups inside for each item in the collection.
# The hooks are only defined for the container group.
# By doing so, the hooks are defined once, are inherited, and use less memory.
class SampleExampleGroupBuilder(C, T) < NestedExampleGroupBuilder
# Creates a new group builder.
# The value for *what* should be the text the user specified for the collection.
# The *collection_type* is the type to create that will produce the items.
# The *collection_builder* is a proc that takes an instance of *collection_type*
# and returns an actual array of items to create examples for.
# The *name* is the variable name that the user accesses the current collection item with.
#
# In this code:
# ```
# sample random_integers do |integer|
# # ...
# end
# ```
# The *what* would be "random_integers"
# and the collection would contain the items returned by calling *random_integers*.
# The *name* would be "integer".
#
# The *symbol* is passed along to the sample values
# so that the example code can retrieve the current item from the collection.
# The symbol should be unique.
def initialize(what : String, @collection_type : C.class, @collection_builder : C -> Array(T),
@name : String, @symbol : Symbol)
super(what)
end
# Builds the example group.
# A new `NestedExampleGroup` will be returned
# which can have instances of `Example` and `ExampleGroup` nested in it.
# The *parent* should be the group that contains this group.
# The *sample_values* will be given to all of the examples (and groups) nested in this group.
def build(parent : ExampleGroup, sample_values : Internals::SampleValues) : NestedExampleGroup
collection = @collection_builder.call(@collection_type.new(sample_values))
# This creates the container for the sub-groups.
# The hooks are defined here, instead of repeating for each sub-group.
NestedExampleGroup.new(@what, parent, hooks, conditions, doubles).tap do |group|
# Set the container group's children to be sub-groups for each item in the collection.
group.children = collection.map do |value|
# Create a sub-group for each item in the collection.
build_sub_group(group, sample_values, value).as(ExampleComponent)
end
end
end
# Builds a sub-group for one item in the collection.
# The *parent* should be the container group currently being built by the `#build` call.
# The *sample_values* should be the same as what was passed to the `#build` call.
# The *value* is the current item in the collection.
# The value will be added to the sample values for the sub-group,
# so it shouldn't be added prior to calling this method.
private def build_sub_group(parent : ExampleGroup, sample_values : Internals::SampleValues, value : T) : NestedExampleGroup
# Add the value to sample values for this sub-group.
sub_values = sample_values.add(@symbol, @name, value)
NestedExampleGroup.new(value.to_s, parent, ExampleHooks.empty, ExampleConditions.empty, {} of Symbol => DoubleFactory).tap do |group|
# Set the sub-group's children to built versions of the children from this instance.
group.children = @children.map do |child|
# Build the child and up-cast to prevent type errors.
child.build(group, sub_values).as(ExampleComponent)
end
end
end
end
end

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,58 @@
module Spectator
module DSL
macro let(name, &block)
def %value
{{block.body}}
end
@%wrapper : ::Spectator::ValueWrapper?
def {{name.id}}
if (wrapper = @%wrapper)
wrapper.as(::Spectator::TypedValueWrapper(typeof(%value))).value
else
%value.tap do |value|
@%wrapper = ::Spectator::TypedValueWrapper.new(value)
end
end
end
end
macro let!(name, &block)
# TODO: Doesn't work with late-defined values (let).
@%value = {{yield}}
def {{name.id}}
@%value
end
end
macro subject(&block)
{% if block.is_a?(Nop) %}
self.subject
{% else %}
let(:subject) {{block}}
{% end %}
end
macro subject(name, &block)
let({{name.id}}) {{block}}
def subject
{{name.id}}
end
end
macro subject!(&block)
let!(:subject) {{block}}
end
macro subject!(name, &block)
let!({{name.id}}) {{block}}
def subject
{{name.id}}
end
end
end
end

View file

@ -1,37 +0,0 @@
require "./runnable_example"
module Spectator
# Example that does nothing.
# This is to workaround a Crystal compiler bug.
# See: [Issue 4225](https://github.com/crystal-lang/crystal/issues/4225)
# If there are no concrete implementations of an abstract class,
# the compiler gives an error.
# The error indicates an abstract method is undefined.
# This class shouldn't be used, it's just to trick the compiler.
private class DummyExample < RunnableExample
# Dummy description.
def what : Symbol | String
"DUMMY"
end
# Dummy symbolic flag.
def symbolic? : Bool
false
end
# Dummy source.
def source : Source
Source.new(__FILE__, __LINE__)
end
# Dummy instance.
def instance
nil
end
# Dummy run that does nothing.
def run_instance
raise "You shouldn't be running this."
end
end
end

View file

@ -1,8 +1,9 @@
require "./example_component"
require "./test_wrapper"
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
@finished = false
@ -15,10 +16,23 @@ module Spectator
getter group : ExampleGroup
# Retrieves the internal wrapped instance.
abstract def instance
protected getter test_wrapper : TestWrapper
# Source where the example originated from.
abstract def source : Source
def source : Source
@test_wrapper.source
end
def description : String | Symbol
@test_wrapper.description
end
def symbolic? : Bool
description = @test_wrapper.description
description.starts_with?('#') || description.starts_with?('.')
end
abstract def run_impl
protected getter sample_values : Internals::SampleValues
@ -28,17 +42,14 @@ module Spectator
# An exception is raised if an attempt is made to run it more than once.
def run : Result
raise "Attempted to run example more than once (#{self})" if finished?
@finished = true
run_impl
ensure
@finished = true
end
# Implementation-specific for running the example code.
private abstract def run_impl : Result
# Creates the base of the example.
# The group should be the example group the example belongs to.
# The *sample_values* are passed to the example code.
def initialize(@group, @sample_values)
def initialize(@group, @test_wrapper)
end
# Indicates there is only one example to run.
@ -57,7 +68,7 @@ module Spectator
def to_s(io)
@group.to_s(io)
io << ' ' unless symbolic? && @group.symbolic?
io << what
io << description
end
# Creates the JSON representation of the example,

View file

@ -3,7 +3,13 @@ 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.
abstract def what : Symbol | String
abstract def description : Symbol | String
def full_description
to_s
end
abstract def source : Source
# Indicates whether the example (or group) has been completely run.
abstract def finished? : Bool

View file

@ -10,28 +10,32 @@ module Spectator
# This will effectively run nothing extra while running a test.
def self.empty
new(
[] of ->,
[] of ->
[] of TestMetaMethod,
[] of TestMetaMethod
)
end
# Creates a new set of conditions.
def initialize(
@pre_conditions : Array(->),
@post_conditions : Array(->)
@pre_conditions : Array(TestMetaMethod),
@post_conditions : Array(TestMetaMethod)
)
end
# Runs all pre-condition checks.
# These should be run before every test.
def run_pre_conditions
@pre_conditions.each &.call
def run_pre_conditions(wrapper : TestWrapper, example : Example)
@pre_conditions.each do |hook|
wrapper.call(hook, example)
end
end
# Runs all post-condition checks.
# These should be run after every test.
def run_post_conditions
@post_conditions.each &.call
def run_post_conditions(wrapper : TestWrapper, example : Example)
@post_conditions.each do |hook|
wrapper.call(hook, example)
end
end
end
end

View file

@ -15,13 +15,7 @@ module Spectator
include Enumerable(ExampleComponent)
include Iterable(ExampleComponent)
# Creates the example group.
# The hooks are stored to be triggered later.
def initialize(@hooks : ExampleHooks, @conditions : ExampleConditions, @doubles : Hash(Symbol, DSL::DoubleFactory))
@example_count = 0
@before_all_hooks_run = false
@after_all_hooks_run = false
end
@example_count = 0
# Retrieves the children in the group.
# This only returns the direct descends (non-recursive).
@ -45,6 +39,11 @@ module Spectator
@doubles[id].build(sample_values)
end
getter context
def initialize(@context : TestContext)
end
# Yields each direct descendant.
def each
children.each do |child|
@ -128,72 +127,5 @@ module Spectator
def finished? : Bool
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

View file

@ -1,4 +1,6 @@
module Spectator
alias TestMetaMethod = ::SpectatorTest, Example ->
# Collection of hooks that run at various times throughout testing.
# A hook is just a `Proc` (code block) that runs at a specified time.
class ExampleHooks
@ -7,20 +9,20 @@ module Spectator
def self.empty
new(
[] of ->,
[] of TestMetaMethod,
[] of ->,
[] of ->,
[] of ->,
[] of Proc(Nil) ->
[] of TestMetaMethod,
[] of ::SpectatorTest, Proc(Nil) ->
)
end
# Creates a new set of hooks.
def initialize(
@before_all : Array(->),
@before_each : Array(->),
@before_each : Array(TestMetaMethod),
@after_all : Array(->),
@after_each : Array(->),
@around_each : Array(Proc(Nil) ->)
@after_each : Array(TestMetaMethod),
@around_each : Array(::SpectatorTest, Proc(Nil) ->)
)
end
@ -32,8 +34,10 @@ module Spectator
# Runs all "before-each" hooks.
# These hooks should be run every time before each example in a group.
def run_before_each
@before_each.each &.call
def run_before_each(wrapper : TestWrapper, example : Example)
@before_each.each do |hook|
wrapper.call(hook, example)
end
end
# Runs all "after-all" hooks.
@ -44,27 +48,28 @@ module Spectator
# Runs all "after-all" hooks.
# These hooks should be run every time after each example in a group.
def run_after_each
@after_each.each &.call
def run_after_each(wrapper : TestWrapper, example : Example)
@after_each.each do |hook|
wrapper.call(hook, example)
end
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(&block : ->) : ->
def wrap_around_each(test, block : ->)
wrapper = block
# Must wrap in reverse order,
# otherwise hooks will run in the wrong order.
@around_each.reverse_each do |hook|
wrapper = wrap_proc(hook, wrapper)
wrapper = wrap_foo(test, hook, wrapper)
end
wrapper
end
# Utility method for wrapping one proc with another.
private def wrap_proc(inner : Proc(Nil) ->, wrapper : ->)
->{ inner.call(wrapper) }
private def wrap_foo(test, hook, wrapper)
->{ hook.call(test, wrapper) }
end
end
end

View file

@ -40,7 +40,7 @@ module Spectator::Expectations
# Reports an expectation to the current harness.
private def report(match_data : Matchers::MatchData)
expectation = Expectation.new(match_data, @source)
Internals::Harness.current.report_expectation(expectation)
Harness.current.report_expectation(expectation)
end
end
end

View file

@ -28,7 +28,7 @@ module Spectator::Formatting
# Produces a single character output based on a result.
def end_example(result)
@previous_hierarchy.size.times { @io.print INDENT }
@io.puts result.call(Color) { result.example.what }
@io.puts result.call(Color) { result.example.description }
end
# Produces a list of groups making up the hierarchy for an example.
@ -56,7 +56,7 @@ module Spectator::Formatting
private def print_sub_hierarchy(index, sub_hierarchy)
sub_hierarchy.each do |group|
index.times { @io.print INDENT }
@io.puts group.what
@io.puts group.description
index += 1
end
end

View file

@ -1,4 +1,4 @@
module Spectator::Internals
module Spectator
# Helper class that acts as a gateway between example code and the test framework.
# Every example must be invoked by passing it to `#run`.
# This sets up the harness so that the example code can use it.
@ -9,7 +9,7 @@ module Spectator::Internals
# ```
# Then from the example code, the harness can be accessed via `#current` like so:
# ```
# harness = ::Spectator::Internals::Harness.current
# harness = ::Spectator::Harness.current
# # Do something with the harness.
# ```
# Of course, the end-user shouldn't see this or work directly with the harness.
@ -34,6 +34,11 @@ module Spectator::Internals
# Retrieves the current running example.
getter example : Example
# Retrieves the group for the current running example.
def group
example.group
end
# Reports the outcome of an expectation.
# An exception will be raised when a failing result is given.
def report_expectation(expectation : Expectations::Expectation) : Nil

View file

@ -11,18 +11,17 @@
require "openssl"
# First the sub-modules.
require "./internals"
require "./dsl"
require "./expectations"
require "./matchers"
require "./formatting"
# Then all of the top-level types.
require "./spec_builder"
require "./example_component"
require "./example"
require "./runnable_example"
require "./pending_example"
require "./dummy_example"
require "./example_conditions"
require "./example_hooks"

View file

@ -1,7 +0,0 @@
require "./internals/*"
module Spectator
# Utilities and black magic (hacks) employed by the testing framework.
module Internals
end
end

View file

@ -6,92 +6,31 @@ module Spectator
class NestedExampleGroup < ExampleGroup
# Description from the user of the group's contents.
# This is a symbol when referencing a type.
getter what : Symbol | String
getter description : Symbol | String
getter source : Source
# Group that this is nested in.
getter parent : ExampleGroup
# Creates a new example group.
# The *what* argument is a description from the user.
# The *description* argument is a description from the user.
# The *parent* should contain this group.
# After creating this group, the parent's children should be updated.
# 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, doubles : Hash(Symbol, DSL::DoubleFactory))
super(hooks, conditions, doubles)
def initialize(@description, @source, @parent, context)
super(context)
end
# Indicates wheter the group references a type.
def symbolic? : Bool
@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
@description.is_a?(Symbol)
end
# Creates a string representation of the group.
# The string consists of `#what` appended to the parent.
# The string consists of `#description` appended to the parent.
# This results in a string like:
# ```text
# Foo#bar does something
@ -109,7 +48,7 @@ module Spectator
def to_s(io)
parent.to_s(io)
io << ' ' unless (symbolic? || parent.is_a?(RootExampleGroup)) && parent.symbolic?
io << what
io << description
end
end
end

View file

@ -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 : Result
PendingResult.new(self)

View file

@ -5,8 +5,12 @@ module Spectator
# The root has no parent.
class RootExampleGroup < ExampleGroup
# Dummy value - this should never be used.
def what : Symbol | String
"ROOT"
def description : Symbol | String
:root
end
def source : Source
Source.new(__FILE__, __LINE__)
end
# Indicates that the group is symbolic.

View file

@ -1,78 +1,39 @@
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 : Result
result = capture_result
expectations = Internals::Harness.current.expectations
expectations = Harness.current.expectations
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
context = group.context
ResultCapture.new.tap do |result|
# 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
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
# Creates a proc that runs the test code
# and captures the result.
private def wrap_run_example(result)
# Wrap the method that runs and captures
# the test code with the around-each hooks.
group.wrap_around_each_hooks do
context.run_before_hooks(self)
run_example(result)
context.run_after_hooks(self)
end
end
# Runs the test code and captures the result.
private def run_example(result)
context = group.context
wrapper = test_wrapper.around_hook(context)
# 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
context.run_pre_conditions(self)
wrapper.call
context.run_post_conditions(self)
rescue ex # Catch all errors and handle them later.
result.error = ex
end

View file

@ -1,3 +1,5 @@
require "./harness"
module Spectator
# Main driver for executing tests and feeding results to formatters.
class Runner
@ -35,7 +37,7 @@ module Spectator
result = run_example(example).as(Result)
results << result
if @config.fail_fast? && result.is_a?(FailedResult)
example.group.run_after_all_hooks(ignore_unfinished: true)
example.group.context.run_after_all_hooks(example.group, ignore_unfinished: true)
break
end
end
@ -57,7 +59,7 @@ module Spectator
result = if @config.dry_run? && example.is_a?(RunnableExample)
dry_run_result(example)
else
Internals::Harness.run(example)
Harness.run(example)
end
@config.each_formatter(&.end_example(result))
result

View file

@ -1,31 +1,14 @@
module Spectator::DSL
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 Builder
module SpecBuilder
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 = ExampleGroupStack.new
# Begins a new nested group in the spec.
# A corresponding `#end_group` call must be made
@ -34,7 +17,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 -
@ -43,9 +26,9 @@ module Spectator::DSL
# 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) : Nil
group = SampleExampleGroupBuilder.new(*args)
push_group(group)
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.
@ -53,52 +36,64 @@ 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 = ->(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
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)
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
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)
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 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)
# 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 : ->) : Nil
current_group.add_pre_condition(block)
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 : ->) : Nil
current_group.add_post_condition(block)
def add_post_condition(&block : TestMetaMethod) : Nil
@@stack.current.add_post_condition(block)
end
def add_double(id : Symbol, double_type : Double.class) : Nil
@ -109,7 +104,7 @@ module Spectator::DSL
# 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
TestSuite.new(group, filter)
end
end

View file

@ -0,0 +1,19 @@
require "../../spectator_test"
require "../test_values"
require "../test_wrapper"
module Spectator::SpecBuilder
abstract class ExampleBuilder
alias FactoryMethod = TestValues -> ::SpectatorTest
def initialize(@description : String, @source : Source, @builder : FactoryMethod, @runner : TestMethod)
end
abstract def build(group) : ExampleComponent
private def build_test_wrapper(group)
test = @builder.call(group.context.values)
TestWrapper.new(@description, @source, test, @runner)
end
end
end

View file

@ -0,0 +1,67 @@
require "../test_context"
require "./example_builder"
module Spectator::SpecBuilder
abstract class ExampleGroupBuilder
alias Child = NestedExampleGroupBuilder | ExampleBuilder
private getter children = Deque(Child).new
@before_each_hooks = Deque(TestMetaMethod).new
@after_each_hooks = Deque(TestMetaMethod).new
@before_all_hooks = Deque(->).new
@after_all_hooks = Deque(->).new
@around_each_hooks = Deque(::SpectatorTest, Proc(Nil) ->).new
@pre_conditions = Deque(TestMetaMethod).new
@post_conditions = Deque(TestMetaMethod).new
def add_child(child : Child)
@children << child
end
def add_before_each_hook(hook : TestMetaMethod)
@before_each_hooks << hook
end
def add_after_each_hook(hook : TestMetaMethod)
@after_each_hooks << hook
end
def add_before_all_hook(hook : ->)
@before_all_hooks << hook
end
def add_after_all_hook(hook : ->)
@after_all_hooks << hook
end
def add_around_each_hook(hook : ::SpectatorTest, Proc(Nil) ->)
@around_each_hooks << hook
end
def add_pre_condition(hook : TestMetaMethod)
@pre_conditions << hook
end
def add_post_condition(hook : TestMetaMethod)
@post_conditions << hook
end
private def build_hooks
ExampleHooks.new(
@before_all_hooks.to_a,
@before_each_hooks.to_a,
@after_all_hooks.to_a,
@after_each_hooks.to_a,
@around_each_hooks.to_a
)
end
private def build_conditions
ExampleConditions.new(
@pre_conditions.to_a,
@post_conditions.to_a
)
end
end
end

View file

@ -0,0 +1,28 @@
require "./root_example_group_builder"
require "./nested_example_group_builder"
module Spectator::SpecBuilder
struct ExampleGroupStack
getter root
def initialize
@root = RootExampleGroupBuilder.new
@stack = Deque(ExampleGroupBuilder).new(1, @root)
end
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

View file

@ -0,0 +1,18 @@
require "../test_context"
require "./example_group_builder"
module Spectator::SpecBuilder
class NestedExampleGroupBuilder < ExampleGroupBuilder
def initialize(@description : String | Symbol, @source : Source)
end
def build(parent_group)
context = TestContext.new(parent_group.context, build_hooks, build_conditions, parent_group.context.values)
NestedExampleGroup.new(@description, @source, parent_group, context).tap do |group|
group.children = children.map do |child|
child.build(group).as(ExampleComponent)
end
end
end
end
end

View file

@ -0,0 +1,10 @@
require "./example_builder"
module Spectator::SpecBuilder
class PendingExampleBuilder < ExampleBuilder
def build(group) : ExampleComponent
wrapper = build_test_wrapper(group)
PendingExample.new(group, wrapper).as(ExampleComponent)
end
end
end

View file

@ -0,0 +1,15 @@
require "../test_values"
require "./example_group_builder"
module Spectator::SpecBuilder
class RootExampleGroupBuilder < ExampleGroupBuilder
def build
context = TestContext.new(nil, build_hooks, build_conditions, TestValues.empty)
RootExampleGroup.new(context).tap do |group|
group.children = children.map do |child|
child.build(group).as(ExampleComponent)
end
end
end
end
end

View file

@ -0,0 +1,10 @@
require "./example_builder"
module Spectator::SpecBuilder
class RunnableExampleBuilder < ExampleBuilder
def build(group) : ExampleComponent
wrapper = build_test_wrapper(group)
RunnableExample.new(group, wrapper).as(ExampleComponent)
end
end
end

View file

@ -0,0 +1,30 @@
require "./nested_example_group_builder"
module Spectator::SpecBuilder
class SampleExampleGroupBuilder(T) < NestedExampleGroupBuilder
def initialize(description : String | Symbol, source : Source, @id : Symbol, @label : String, @collection_builder : TestValues -> Array(T))
super(description, source)
end
def build(parent_group)
values = parent_group.context.values
collection = @collection_builder.call(values)
context = TestContext.new(parent_group.context, build_hooks, build_conditions, values)
NestedExampleGroup.new(@description, @source, parent_group, context).tap do |group|
group.children = collection.map do |element|
build_sub_group(group, element).as(ExampleComponent)
end
end
end
private def build_sub_group(parent_group, element)
values = parent_group.context.values.add(@id, @description.to_s, element)
context = TestContext.new(parent_group.context, ExampleHooks.empty, ExampleConditions.empty, values)
NestedExampleGroup.new("#{@label} = #{element.inspect}", @source, parent_group, context).tap do |group|
group.children = children.map do |child|
child.build(group).as(ExampleComponent)
end
end
end
end
end

View file

@ -0,0 +1,74 @@
require "./example_hooks"
require "./test_values"
module Spectator
class TestContext
getter values
def initialize(@parent : TestContext?, @hooks : ExampleHooks, @conditions : ExampleConditions, @values : TestValues)
@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

View file

@ -1,10 +1,11 @@
require "./typed_value_wrapper"
require "./value_wrapper"
module Spectator::Internals
module Spectator
# Collection of test values supplied to examples.
# Each value is labeled by a symbol that the example knows.
# The values also come with a name that can be given to humans.
struct SampleValues
struct TestValues
# Creates an empty set of sample values.
def self.empty
new({} of Symbol => Entry)
@ -17,9 +18,9 @@ module Spectator::Internals
# Adds a new value by duplicating the current set and adding to it.
# The new sample values with the additional value is returned.
# The original set of sample values is not modified.
def add(id : Symbol, name : String, value : T) : SampleValues forall T
def add(id : Symbol, name : String, value : T) : TestValues forall T
wrapper = TypedValueWrapper(T).new(value)
SampleValues.new(@values.merge({
TestValues.new(@values.merge({
id => Entry.new(name, wrapper),
}))
end
@ -58,7 +59,6 @@ module Spectator::Internals
end
# This must be after `Entry` is defined.
# Could be a Cyrstal compiler bug?
include Enumerable(Entry)
end
end

View file

@ -0,0 +1,36 @@
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.
getter description
# 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 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

View file

@ -1,6 +1,6 @@
require "./value_wrapper"
module Spectator::Internals
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`

View file

@ -1,9 +1,7 @@
module Spectator::Internals
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
# Retrieves the underlying value.
abstract def value
end
end

11
src/spectator_test.cr Normal file
View file

@ -0,0 +1,11 @@
require "./spectator/dsl"
# Root-level class that all tests inherit from and are contained in.
# This class is intentionally outside of the scope of Spectator,
# so that the namespace isn't leaked into tests unexpectedly.
class SpectatorTest
include ::Spectator::DSL
def initialize(@spectator_test_values : ::Spectator::TestValues)
end
end