diff --git a/spec/composite_example_filter_spec.cr b/spec/composite_example_filter_spec.cr deleted file mode 100644 index 0319712..0000000 --- a/spec/composite_example_filter_spec.cr +++ /dev/null @@ -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 diff --git a/spec/dsl/example_factory_spec.cr b/spec/dsl/example_factory_spec.cr deleted file mode 100644 index b3041d2..0000000 --- a/spec/dsl/example_factory_spec.cr +++ /dev/null @@ -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 diff --git a/spec/dsl/nested_example_group_builder_spec.cr b/spec/dsl/nested_example_group_builder_spec.cr deleted file mode 100644 index cf41fb6..0000000 --- a/spec/dsl/nested_example_group_builder_spec.cr +++ /dev/null @@ -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 diff --git a/spec/dsl/root_example_group_builder_spec.cr b/spec/dsl/root_example_group_builder_spec.cr deleted file mode 100644 index b4aeb9d..0000000 --- a/spec/dsl/root_example_group_builder_spec.cr +++ /dev/null @@ -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 diff --git a/spec/dsl/sample_example_group_builder_spec.cr b/spec/dsl/sample_example_group_builder_spec.cr deleted file mode 100644 index a5a9d88..0000000 --- a/spec/dsl/sample_example_group_builder_spec.cr +++ /dev/null @@ -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 diff --git a/spec/errored_result_spec.cr b/spec/errored_result_spec.cr deleted file mode 100644 index 4b5f524..0000000 --- a/spec/errored_result_spec.cr +++ /dev/null @@ -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 diff --git a/spec/example_conditions_spec.cr b/spec/example_conditions_spec.cr deleted file mode 100644 index 3398f02..0000000 --- a/spec/example_conditions_spec.cr +++ /dev/null @@ -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 diff --git a/spec/example_hooks_spec.cr b/spec/example_hooks_spec.cr deleted file mode 100644 index 9fc8798..0000000 --- a/spec/example_hooks_spec.cr +++ /dev/null @@ -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 diff --git a/spec/example_iterator_spec.cr b/spec/example_iterator_spec.cr deleted file mode 100644 index e7829f4..0000000 --- a/spec/example_iterator_spec.cr +++ /dev/null @@ -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 diff --git a/spec/expectation_failed_spec.cr b/spec/expectation_failed_spec.cr deleted file mode 100644 index 54c5d1d..0000000 --- a/spec/expectation_failed_spec.cr +++ /dev/null @@ -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 diff --git a/spec/expectations/example_expectations_spec.cr b/spec/expectations/example_expectations_spec.cr deleted file mode 100644 index 0b55ab9..0000000 --- a/spec/expectations/example_expectations_spec.cr +++ /dev/null @@ -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 diff --git a/spec/expectations/expectation_reporter_spec.cr b/spec/expectations/expectation_reporter_spec.cr deleted file mode 100644 index 0f32281..0000000 --- a/spec/expectations/expectation_reporter_spec.cr +++ /dev/null @@ -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 diff --git a/spec/expectations/expectation_spec.cr b/spec/expectations/expectation_spec.cr deleted file mode 100644 index 06540fc..0000000 --- a/spec/expectations/expectation_spec.cr +++ /dev/null @@ -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 diff --git a/spec/failed_result_spec.cr b/spec/failed_result_spec.cr deleted file mode 100644 index 34b557d..0000000 --- a/spec/failed_result_spec.cr +++ /dev/null @@ -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 diff --git a/spec/fomatting/color_spec.cr b/spec/fomatting/color_spec.cr deleted file mode 100644 index 406b5db..0000000 --- a/spec/fomatting/color_spec.cr +++ /dev/null @@ -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 diff --git a/spec/helpers/errored_example.cr b/spec/helpers/errored_example.cr deleted file mode 100644 index aa82617..0000000 --- a/spec/helpers/errored_example.cr +++ /dev/null @@ -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 diff --git a/spec/helpers/example_hooks_helper.cr b/spec/helpers/example_hooks_helper.cr deleted file mode 100644 index afdf537..0000000 --- a/spec/helpers/example_hooks_helper.cr +++ /dev/null @@ -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 diff --git a/spec/helpers/expectations_helper.cr b/spec/helpers/expectations_helper.cr deleted file mode 100644 index 0096f84..0000000 --- a/spec/helpers/expectations_helper.cr +++ /dev/null @@ -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 diff --git a/spec/helpers/failing_example.cr b/spec/helpers/failing_example.cr deleted file mode 100644 index a0e972d..0000000 --- a/spec/helpers/failing_example.cr +++ /dev/null @@ -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 diff --git a/spec/helpers/matchers_helper.cr b/spec/helpers/matchers_helper.cr deleted file mode 100644 index 2f123df..0000000 --- a/spec/helpers/matchers_helper.cr +++ /dev/null @@ -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 diff --git a/spec/helpers/passing_example.cr b/spec/helpers/passing_example.cr deleted file mode 100644 index 6d03aae..0000000 --- a/spec/helpers/passing_example.cr +++ /dev/null @@ -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 diff --git a/spec/helpers/result_call_spy.cr b/spec/helpers/result_call_spy.cr deleted file mode 100644 index 30830fb..0000000 --- a/spec/helpers/result_call_spy.cr +++ /dev/null @@ -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 diff --git a/spec/helpers/spy_example.cr b/spec/helpers/spy_example.cr deleted file mode 100644 index 32b6474..0000000 --- a/spec/helpers/spy_example.cr +++ /dev/null @@ -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 diff --git a/spec/helpers/spy_formatter.cr b/spec/helpers/spy_formatter.cr deleted file mode 100644 index 394200e..0000000 --- a/spec/helpers/spy_formatter.cr +++ /dev/null @@ -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 diff --git a/spec/helpers/spy_sut.cr b/spec/helpers/spy_sut.cr deleted file mode 100644 index 2c3feff..0000000 --- a/spec/helpers/spy_sut.cr +++ /dev/null @@ -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 diff --git a/spec/internals/harness_spec.cr b/spec/internals/harness_spec.cr deleted file mode 100644 index 9a360c0..0000000 --- a/spec/internals/harness_spec.cr +++ /dev/null @@ -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 diff --git a/spec/internals/sample_values_spec.cr b/spec/internals/sample_values_spec.cr deleted file mode 100644 index 0e0991a..0000000 --- a/spec/internals/sample_values_spec.cr +++ /dev/null @@ -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 diff --git a/spec/internals/typed_value_wrapper_spec.cr b/spec/internals/typed_value_wrapper_spec.cr deleted file mode 100644 index ec997b1..0000000 --- a/spec/internals/typed_value_wrapper_spec.cr +++ /dev/null @@ -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 diff --git a/spec/line_example_filter_spec.cr b/spec/line_example_filter_spec.cr deleted file mode 100644 index ca2b1df..0000000 --- a/spec/line_example_filter_spec.cr +++ /dev/null @@ -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 diff --git a/spec/name_example_filter_spec.cr b/spec/name_example_filter_spec.cr deleted file mode 100644 index 0fdeefb..0000000 --- a/spec/name_example_filter_spec.cr +++ /dev/null @@ -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 diff --git a/spec/nested_example_group_spec.cr b/spec/nested_example_group_spec.cr deleted file mode 100644 index 7d102b0..0000000 --- a/spec/nested_example_group_spec.cr +++ /dev/null @@ -1,1130 +0,0 @@ -require "./spec_helper" - -def new_nested_group(what : Symbol | String = "what", hooks = Spectator::ExampleHooks.empty, conditions = Spectator::ExampleConditions.empty, parent : Spectator::ExampleGroup? = nil) - parent ||= Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - Spectator::NestedExampleGroup.new(what, parent, hooks, conditions).tap do |group| - parent.children = [group.as(Spectator::ExampleComponent)] - group.children = [] of Spectator::ExampleComponent - end -end - -def nested_group_with_examples(example_count = 5) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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 nested_group_with_sub_groups(sub_group_count = 5, example_count = 5) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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_nested_group - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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::NestedExampleGroup do - describe "#what" do - it "is the expected value" do - what = "foobar" - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new(what, root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group.what.should eq(what) - end - end - - describe "#parent" do - it "is the expected value" do - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group.parent.should eq(root) - end - end - - describe "#run_before_hooks" do - it "runs a single before_all hook" do - called = false - hooks = new_hooks(before_all: ->{ called = true; nil }) - group = new_nested_group(hooks: hooks) - group.run_before_hooks - called.should be_true - end - - it "runs a single before_each hook" do - called = false - hooks = new_hooks(before_each: ->{ called = true; nil }) - group = new_nested_group(hooks: 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_nested_group(hooks: 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_nested_group(hooks: 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_nested_group(hooks: hooks) - group.run_before_hooks - calls.should eq(%i[a b c d e f]) - end - - it "runs the parent before_all hooks" do - called = false - hooks = new_hooks(before_all: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - group = new_nested_group(parent: root) - group.run_before_hooks - called.should be_true - end - - it "runs the parent before_each hooks" do - called = false - hooks = new_hooks(before_each: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - group = new_nested_group(parent: root) - group.run_before_hooks - called.should be_true - end - - it "runs the parent before_all hooks first" do - calls = [] of Symbol - root_hooks = new_hooks(before_all: ->{ calls << :a; nil }) - group_hooks = new_hooks(before_all: ->{ calls << :b; nil }) - root = Spectator::RootExampleGroup.new(root_hooks, Spectator::ExampleConditions.empty) - group = new_nested_group(hooks: group_hooks, parent: root) - group.run_before_hooks - calls.should eq(%i[a b]) - end - - it "runs the parent before_each hooks first" do - calls = [] of Symbol - root_hooks = new_hooks(before_each: ->{ calls << :a; nil }) - group_hooks = new_hooks(before_each: ->{ calls << :b; nil }) - root = Spectator::RootExampleGroup.new(root_hooks, Spectator::ExampleConditions.empty) - group = new_nested_group(hooks: group_hooks, parent: root) - group.run_before_hooks - calls.should eq(%i[a b]) - end - - it "runs the before_all hooks once" do - call_count = 0 - hooks = new_hooks(before_all: ->{ call_count += 1; nil }) - group = new_nested_group(hooks: 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_nested_group(hooks: 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_nested_group(hooks: 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_nested_group(hooks: 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_nested_group(hooks: hooks) - group.run_after_hooks - calls.should eq(%i[a b c d e f]) - end - - it "runs the parent after_all hooks" do - called = false - hooks = new_hooks(after_all: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - group = new_nested_group(parent: root) - group.run_after_hooks - called.should be_true - end - - it "runs the parent after_each hooks" do - called = false - hooks = new_hooks(after_each: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - group = new_nested_group(parent: root) - group.run_after_hooks - called.should be_true - end - - it "runs the parent after_all hooks last" do - calls = [] of Symbol - root_hooks = new_hooks(after_all: ->{ calls << :a; nil }) - group_hooks = new_hooks(after_all: ->{ calls << :b; nil }) - root = Spectator::RootExampleGroup.new(root_hooks, Spectator::ExampleConditions.empty) - group = new_nested_group(hooks: group_hooks, parent: root) - group.run_after_hooks - calls.should eq(%i[b a]) - end - - it "runs the parent after_each hooks last" do - calls = [] of Symbol - root_hooks = new_hooks(after_each: ->{ calls << :a; nil }) - group_hooks = new_hooks(after_each: ->{ calls << :b; nil }) - root = Spectator::RootExampleGroup.new(root_hooks, Spectator::ExampleConditions.empty) - group = new_nested_group(hooks: group_hooks, parent: root) - group.run_after_hooks - calls.should eq(%i[b a]) - end - - it "runs the after_all hooks once" do - call_count = 0 - hooks = new_hooks(after_all: ->{ call_count += 1; nil }) - group = new_nested_group(hooks: 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_nested_group(hooks: 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 }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, hooks, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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 }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, hooks, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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 - - it "doesn't run the parent after_all hooks" do - called = false - hooks = new_hooks(after_all: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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 parent after_each hooks" do - called = false - hooks = new_hooks(after_each: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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 }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, hooks, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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 }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, hooks, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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 - - it "doesn't run the parent after_all hooks" do - called = false - examples = [] of Spectator::Example - hooks = new_hooks(after_all: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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 parent after_each hooks" do - called = false - examples = [] of Spectator::Example - hooks = new_hooks(after_each: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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_nested_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_nested_group(hooks: 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_nested_group(hooks: 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_nested_group(hooks: hooks).wrap_around_each_hooks { } - wrapper.call - calls.should eq(%i[a b c]) - end - - it "wraps the parent hooks" do - called = false - hooks = new_hooks(around_each: ->(proc : ->) { called = true; proc.call }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - wrapper = new_nested_group(parent: root).wrap_around_each_hooks { } - wrapper.call - called.should be_true - end - - it "wraps the parent hooks so they are outermost" do - calls = [] of Symbol - root_hooks = new_hooks(around_each: ->(proc : ->) { calls << :a; proc.call }) - group_hooks = new_hooks(around_each: ->(proc : ->) { calls << :b; proc.call }) - root = Spectator::RootExampleGroup.new(root_hooks, Spectator::ExampleConditions.empty) - group = new_nested_group(hooks: group_hooks, parent: root) - wrapper = group.wrap_around_each_hooks { } - wrapper.call - calls.should eq(%i[a b]) - end - end - - describe "#run_pre_conditions" do - it "runs a single pre-condition" do - called = false - conditions = new_conditions(pre: ->{ called = true; nil }) - group = new_nested_group(conditions: conditions) - group.run_pre_conditions - called.should be_true - end - - it "runs multiple pre-conditions" do - call_count = 0 - conditions = new_conditions(pre: [ - ->{ call_count += 1; nil }, - ->{ call_count += 2; nil }, - ->{ call_count += 3; nil }, - ]) - group = new_nested_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_nested_group(conditions: conditions) - group.run_pre_conditions - calls.should eq(%i[a b c]) - end - - it "runs the parent conditions" do - called = false - conditions = new_conditions(pre: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, conditions) - group = new_nested_group(parent: root) - group.run_pre_conditions - called.should be_true - end - - it "runs the parent conditions first" do - calls = [] of Symbol - root_conditions = new_conditions(pre: ->{ calls << :a; nil }) - group_conditions = new_conditions(pre: ->{ calls << :b; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, root_conditions) - group = new_nested_group(conditions: group_conditions, parent: root) - group.run_pre_conditions - calls.should eq(%i[a b]) - end - - it "runs the conditions multiple times" do - call_count = 0 - conditions = new_conditions(pre: ->{ call_count += 1; nil }) - group = new_nested_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_nested_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_nested_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_nested_group(conditions: conditions) - group.run_post_conditions - calls.should eq(%i[a b c]) - end - - it "runs the parent conditions" do - called = false - conditions = new_conditions(post: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, conditions) - group = new_nested_group(parent: root) - group.run_post_conditions - called.should be_true - end - - it "runs the parent conditions last" do - calls = [] of Symbol - root_conditions = new_conditions(post: ->{ calls << :a; nil }) - group_conditions = new_conditions(post: ->{ calls << :b; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, root_conditions) - group = new_nested_group(conditions: group_conditions, parent: root) - group.run_post_conditions - calls.should eq(%i[b a]) - end - - it "runs the conditions multiple times" do - call_count = 0 - conditions = new_conditions(post: ->{ call_count += 1; nil }) - group = new_nested_group(conditions: conditions) - 2.times { group.run_post_conditions } - call_count.should eq(2) - end - end - - describe "#to_s" do - it "contains #what" do - group = new_nested_group - group.to_s.should contain(group.what.to_s) - end - - it "contains the parent's #to_s" do - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - parent = Spectator::NestedExampleGroup.new("PARENT", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("GROUP", parent, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [parent.as(Spectator::ExampleComponent)] - parent.children = [group.as(Spectator::ExampleComponent)] - group.children = [] of Spectator::ExampleComponent - group.to_s.should contain(parent.to_s) - end - - context "when #symbolic? is true" do - context "and the parent group is symbolic" do - it "omits the space" do - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - parent = Spectator::NestedExampleGroup.new(:Parent, root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new(:"#foo", parent, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [parent.as(Spectator::ExampleComponent)] - parent.children = [group.as(Spectator::ExampleComponent)] - group.children = [] of Spectator::ExampleComponent - parent.symbolic?.should be_true - group.symbolic?.should be_true - group.to_s.should_not contain(' ') - end - end - - context "and the parent group isn't symbolic" do - it "inserts a space" do - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - parent = Spectator::NestedExampleGroup.new("PARENT", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new(:"#foo", parent, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [parent.as(Spectator::ExampleComponent)] - parent.children = [group.as(Spectator::ExampleComponent)] - group.children = [] of Spectator::ExampleComponent - parent.symbolic?.should be_false - group.symbolic?.should be_true - group.to_s.should contain(' ') - end - end - end - - context "when #symbolic? is false" do - context "and the parent group is symbolic" do - it "inserts a space" do - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - parent = Spectator::NestedExampleGroup.new(:Parent, root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("GROUP", parent, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [parent.as(Spectator::ExampleComponent)] - parent.children = [group.as(Spectator::ExampleComponent)] - group.children = [] of Spectator::ExampleComponent - parent.symbolic?.should be_true - group.symbolic?.should be_false - group.to_s.should contain(' ') - end - end - - context "and the parent group isn't symbolic" do - it "inserts a space" do - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - parent = Spectator::NestedExampleGroup.new("PARENT", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("GROUP", parent, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [parent.as(Spectator::ExampleComponent)] - parent.children = [group.as(Spectator::ExampleComponent)] - group.children = [] of Spectator::ExampleComponent - parent.symbolic?.should be_false - group.symbolic?.should be_false - group.to_s.should contain(' ') - end - end - end - - context "when the parent group is root" do - it "omits the space" do - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("GROUP", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - group.children = [] of Spectator::ExampleComponent - group.to_s.should_not contain(' ') - end - end - end - - describe "#children" do - it "raises an error when not set" do - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - expect_raises(Exception) { group.children } - end - - it "returns the expected set" do - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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 - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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 - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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 - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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 - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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 - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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_nested_group.example_count.should eq(0) - end - end - - context "with empty sub-groups" do - it "is zero" do - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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, _ = nested_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, _ = nested_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_nested_group - group.example_count.should eq(examples.size) - end - end - end - - describe "#[]" do - context "when empty" do - it "raises an error" do - group = new_nested_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 = nested_group_with_examples - group[0].should eq(examples.first) - end - end - - context "given -1" do - it "returns the last example" do - group, examples = nested_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 = nested_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 = nested_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, _ = nested_group_with_examples(10) - expect_raises(IndexError) { group[15] } - end - - it "handles off-by-one" do - group, _ = nested_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, _ = nested_group_with_examples(10) - expect_raises(IndexError) { group[-15] } - end - - it "handles off-by-one" do - group, _ = nested_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 = nested_group_with_sub_groups - group[0].should eq(examples.first) - end - end - - context "given -1" do - it "returns the last example" do - group, examples = nested_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 = nested_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 = nested_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, _ = nested_group_with_sub_groups(10, 2) - expect_raises(IndexError) { group[25] } - end - - it "handles off-by-one" do - group, _ = nested_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, _ = nested_group_with_sub_groups(10, 2) - expect_raises(IndexError) { group[-25] } - end - - it "handles off-by-one" do - group, _ = nested_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_nested_group - group[0].should eq(examples.first) - end - end - - context "given -1" do - it "returns the last example" do - group, examples = complex_nested_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_nested_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_nested_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_nested_group - expect_raises(IndexError) { group[examples.size + 5] } - end - - it "handles off-by-one" do - group, examples = complex_nested_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_nested_group - expect_raises(IndexError) { group[-examples.size - 5] } - end - - it "handles off-by-one" do - group, examples = complex_nested_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 - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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_nested_group.finished?.should be_true - end - end - - context "with all unfinished children" do - it "is false" do - group, _ = nested_group_with_examples - group.finished?.should be_false - end - end - - context "with some finished children" do - it "is false" do - group, examples = nested_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 = nested_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 - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - 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, _ = nested_group_with_sub_groups - group.finished?.should be_false - end - end - - context "with some finished children" do - it "is false" do - group, examples = nested_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 = nested_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 - context "when 'what' is a Symbol" do - it "is true" do - new_nested_group(:What).symbolic?.should be_true - end - end - - context "when 'what' is a String" do - it "is false" do - new_nested_group("what").symbolic?.should be_false - end - end - end -end diff --git a/spec/null_example_filter_spec.cr b/spec/null_example_filter_spec.cr deleted file mode 100644 index 42722a6..0000000 --- a/spec/null_example_filter_spec.cr +++ /dev/null @@ -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 diff --git a/spec/pending_example_spec.cr b/spec/pending_example_spec.cr deleted file mode 100644 index 8ed23df..0000000 --- a/spec/pending_example_spec.cr +++ /dev/null @@ -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 diff --git a/spec/pending_result_spec.cr b/spec/pending_result_spec.cr deleted file mode 100644 index c83b1fb..0000000 --- a/spec/pending_result_spec.cr +++ /dev/null @@ -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 diff --git a/spec/report_spec.cr b/spec/report_spec.cr deleted file mode 100644 index 5566674..0000000 --- a/spec/report_spec.cr +++ /dev/null @@ -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 diff --git a/spec/root_example_group_spec.cr b/spec/root_example_group_spec.cr deleted file mode 100644 index d69c80b..0000000 --- a/spec/root_example_group_spec.cr +++ /dev/null @@ -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 diff --git a/spec/runnable_example_spec.cr b/spec/runnable_example_spec.cr deleted file mode 100644 index 5907454..0000000 --- a/spec/runnable_example_spec.cr +++ /dev/null @@ -1,1568 +0,0 @@ -require "./spec_helper" - -def new_runnable_example(group : Spectator::ExampleGroup? = nil, symbolic = false) - actual_group = group || Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - PassingExample.new(actual_group, Spectator::Internals::SampleValues.empty, symbolic).tap do |example| - actual_group.children = [example.as(Spectator::ExampleComponent)] - end -end - -def run_example(example_type : Spectator::Example.class, hooks : Spectator::ExampleHooks? = nil, conditions : Spectator::ExampleConditions? = nil) - group = Spectator::RootExampleGroup.new(hooks || Spectator::ExampleHooks.empty, conditions || Spectator::ExampleConditions.empty) - run_example(example_type, group) -end - -def run_example(example_type : Spectator::Example.class, group : Spectator::ExampleGroup? = nil) - actual_group = group || Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - example = example_type.new(actual_group, Spectator::Internals::SampleValues.empty) - actual_group.children = [example.as(Spectator::ExampleComponent)] - Spectator::Internals::Harness.run(example) -end - -def run_example(hooks : Spectator::ExampleHooks? = nil, conditions : Spectator::ExampleConditions? = nil, &block) - example = SpyExample.create(hooks || Spectator::ExampleHooks.empty, conditions || Spectator::ExampleConditions.empty, &block) - Spectator::Internals::Harness.run(example) -end - -describe Spectator::RunnableExample do - describe "#run" do - context "with a passing test" do - it "returns a successful result" do - run_example(PassingExample).should be_a(Spectator::SuccessfulResult) - end - - {% for hook_type in %i[before_all before_each] %} - context "{{hook_type.id}} hooks" do - it "runs a hook" do - called = false - hooks = new_hooks({{hook_type.id}}: ->{ called = true; nil }) - run_example(PassingExample, hooks) - called.should be_true - end - - it "runs multiple hooks" do - call_count = 0 - hooks = new_hooks({{hook_type.id}}: [ - ->{ call_count += 1; nil }, - ->{ call_count += 2; nil }, - ->{ call_count += 3; nil }, - ]) - run_example(PassingExample, hooks) - call_count.should eq(6) - end - - it "runs them in the correct order" do - calls = [] of Symbol - hooks = new_hooks({{hook_type.id}}: [ - ->{ calls << :a; nil }, - ->{ calls << :b; nil }, - ->{ calls << :c; nil }, - ]) - run_example(PassingExample, hooks) - calls.should eq(\%i[a b c]) - end - - it "runs parent group hooks" do - called = false - hooks = new_hooks({{hook_type.id}}: -> { called = true; nil }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(PassingExample, group) - called.should be_true - end - - it "runs parent group hooks first" do - calls = [] of Symbol - root_hooks = new_hooks({{hook_type.id}}: -> { calls << :a; nil }) - group_hooks = new_hooks({{hook_type.id}}: -> { calls << :b; nil }) - root = Spectator::RootExampleGroup.new(root_hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, group_hooks, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(PassingExample, group) - calls.should eq(\%i[a b]) - end - - it "runs the hook before the test code" do - calls = [] of Symbol - hooks = new_hooks({{hook_type.id}}: ->{ calls << :a; nil }) - run_example(hooks) do - calls << :b - end - calls.should eq(\%i[a b]) - end - end - {% end %} - - it "runs before_all hooks prior to before_each hooks" do - calls = [] of Symbol - hooks = new_hooks( - before_all: ->{ calls << :a; nil }, - before_each: ->{ calls << :b; nil } - ) - run_example(PassingExample, hooks) - calls.should eq(%i[a b]) - end - - {% for hook_type in %i[after_all after_each] %} - context "{{hook_type.id}} hooks" do - it "runs a hook" do - called = false - hooks = new_hooks({{hook_type.id}}: ->{ called = true; nil }) - run_example(PassingExample, hooks) - called.should be_true - end - - it "runs multiple hooks" do - call_count = 0 - hooks = new_hooks({{hook_type.id}}: [ - ->{ call_count += 1; nil }, - ->{ call_count += 2; nil }, - ->{ call_count += 3; nil }, - ]) - run_example(PassingExample, hooks) - call_count.should eq(6) - end - - it "runs them in the correct order" do - calls = [] of Symbol - hooks = new_hooks({{hook_type.id}}: [ - ->{ calls << :a; nil }, - ->{ calls << :b; nil }, - ->{ calls << :c; nil }, - ]) - run_example(PassingExample, hooks) - calls.should eq(\%i[a b c]) - end - - it "runs parent group hooks" do - called = false - hooks = new_hooks({{hook_type.id}}: -> { called = true; nil }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(PassingExample, group) - called.should be_true - end - - it "runs parent group hooks last" do - calls = [] of Symbol - root_hooks = new_hooks({{hook_type.id}}: -> { calls << :a; nil }) - group_hooks = new_hooks({{hook_type.id}}: -> { calls << :b; nil }) - root = Spectator::RootExampleGroup.new(root_hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, group_hooks, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(PassingExample, group) - calls.should eq(\%i[b a]) - end - - it "runs the hook after the test code" do - calls = [] of Symbol - hooks = new_hooks({{hook_type.id}}: ->{ calls << :a; nil }) - run_example(hooks) do - calls << :b - end - calls.should eq(\%i[b a]) - end - end - {% end %} - - it "runs after_each hooks prior to after_all hooks" do - calls = [] of Symbol - hooks = new_hooks( - after_each: ->{ calls << :a; nil }, - after_all: ->{ calls << :b; nil } - ) - run_example(PassingExample, hooks) - calls.should eq(%i[a b]) - end - - context "around_each hooks" do - it "runs a hook" do - called = false - hooks = new_hooks(around_each: ->(proc : ->) { called = true; proc.call }) - run_example(PassingExample, hooks) - called.should be_true - end - - it "runs multiple hooks" 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 }, - ]) - run_example(PassingExample, hooks) - call_count.should eq(6) - end - - it "runs them 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 }, - ]) - run_example(PassingExample, hooks) - calls.should eq(%i[a b c]) - end - - it "runs parent group hooks" do - called = false - hooks = new_hooks(around_each: ->(proc : ->) { called = true; proc.call }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(PassingExample, group) - called.should be_true - end - - it "runs parent group hooks first" do - calls = [] of Symbol - root_hooks = new_hooks(around_each: ->(proc : ->) { calls << :a; proc.call }) - group_hooks = new_hooks(around_each: ->(proc : ->) { calls << :b; proc.call }) - root = Spectator::RootExampleGroup.new(root_hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, group_hooks, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(PassingExample, group) - calls.should eq(%i[a b]) - end - - it "runs around_each hooks after the before hooks" do - calls = [] of Symbol - hooks = new_hooks( - before_all: ->{ calls << :b; nil }, - before_each: ->{ calls << :b; nil }, - around_each: ->(proc : ->) { calls << :c; proc.call } - ) - run_example(PassingExample, hooks) - calls.should eq(%i[b b c]) - end - - it "runs around_each hooks prior to after hooks" do - calls = [] of Symbol - hooks = new_hooks( - around_each: ->(proc : ->) { calls << :c; proc.call }, - after_each: ->{ calls << :a; nil }, - after_all: ->{ calls << :a; nil }, - ) - run_example(PassingExample, hooks) - calls.should eq(%i[c a a]) - end - end - - {% for condition in %i[pre post] %} - context "{{condition.id}}-conditions" do - it "checks a single condition" do - called = false - conditions = new_conditions({{condition.id}}: -> { called = true; nil }) - run_example(PassingExample, conditions: conditions) - called.should be_true - end - - it "checks multiple conditions" do - call_count = 0 - conditions = new_conditions({{condition.id}}: [ - -> { call_count += 1; nil }, - -> { call_count += 2; nil }, - -> { call_count += 3; nil }, - ]) - run_example(PassingExample, conditions: conditions) - call_count.should eq(6) - end - - it "checks them in the correct order" do - calls = [] of Symbol - conditions = new_conditions({{condition.id}}: [ - -> { calls << :a; nil }, - -> { calls << :b; nil }, - -> { calls << :c; nil }, - ]) - run_example(PassingExample, conditions: conditions) - calls.should eq(\%i[a b c]) - end - - it "checks parent group conditions" do - called = false - conditions = new_conditions({{condition.id}}: -> { called = true; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(PassingExample, group) - called.should be_true - end - - {% if condition == :pre %} - it "runs prior to the test code" do - calls = [] of Symbol - conditions = new_conditions({{condition.id}}: ->{ calls << :a; nil }) - run_example(conditions: conditions) do - calls << :b - end - calls.should eq(\%i[a b]) - end - - it "checks parent group conditions first" do - calls = [] of Symbol - root_conditions = new_conditions({{condition.id}}: -> { calls << :a; nil }) - group_conditions = new_conditions({{condition.id}}: -> { calls << :b; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, root_conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, group_conditions) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(PassingExample, group) - calls.should eq(\%i[a b]) - end - {% else %} - it "runs after the test code" do - calls = [] of Symbol - conditions = new_conditions({{condition.id}}: ->{ calls << :b; nil }) - run_example(conditions: conditions) do - calls << :a - end - calls.should eq(\%i[a b]) - end - - it "checks parent group conditions last" do - calls = [] of Symbol - root_conditions = new_conditions({{condition.id}}: -> { calls << :a; nil }) - group_conditions = new_conditions({{condition.id}}: -> { calls << :b; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, root_conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, group_conditions) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(PassingExample, group) - calls.should eq(\%i[b a]) - end - {% end %} - end - {% end %} - - it "runs before hooks prior to pre-conditions" do - calls = [] of Symbol - hooks = new_hooks( - before_all: ->{ calls << :b; nil }, - before_each: ->{ calls << :b; nil } - ) - conditions = new_conditions(pre: ->{ calls << :p; nil }) - run_example(PassingExample, hooks, conditions) - calls.should eq(%i[b b p]) - end - - it "runs around_each hooks prior to pre-conditions" do - calls = [] of Symbol - hooks = new_hooks( - around_each: ->(proc : ->) { calls << :c; proc.call } - ) - conditions = new_conditions(pre: ->{ calls << :p; nil }) - run_example(PassingExample, hooks, conditions) - calls.should eq(%i[c p]) - end - - it "runs post-conditions prior to after hooks" do - calls = [] of Symbol - hooks = new_hooks( - after_all: ->{ calls << :a; nil }, - after_each: ->{ calls << :a; nil } - ) - conditions = new_conditions(pre: ->{ calls << :p; nil }) - run_example(PassingExample, hooks, conditions) - calls.should eq(%i[p a a]) - end - - context "failing pre-condition" do - it "fails the test" do - conditions = new_conditions(pre: ->{ report_expectations(0, 1) }) - result = run_example(PassingExample, conditions: conditions) - result.should be_a(Spectator::FailedResult) - end - - it "prevents the test code from running" do - called = false - conditions = new_conditions(pre: ->{ report_expectations(0, 1) }) - run_example(conditions: conditions) do - called = true - end - called.should be_false - end - - it "prevents additional pre-conditions from running" do - called = false - conditions = new_conditions(pre: [ - ->{ report_expectations(0, 1) }, - ->{ called = true; nil }, - ]) - run_example(PassingExample, conditions: conditions) - called.should be_false - end - - it "prevents additional post-conditions from running" do - called = false - conditions = new_conditions( - pre: ->{ report_expectations(0, 1) }, - post: ->{ called = true; nil } - ) - run_example(PassingExample, conditions: conditions) - called.should be_false - end - - context "in a parent group" do - it "fails the test" do - conditions = new_conditions(pre: ->{ report_expectations(0, 1) }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - result = run_example(PassingExample, group) - result.should be_a(Spectator::FailedResult) - end - - it "prevents the test code from running" do - called = false - conditions = new_conditions(pre: ->{ report_expectations(0, 1) }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - example = SpyExample.new(group, Spectator::Internals::SampleValues.empty) - group.children = [example.as(Spectator::ExampleComponent)] - example.block = ->{ called = true; nil } - Spectator::Internals::Harness.run(example) - called.should be_false - end - - it "doesn't run child pre-conditions" do - called = false - root_conditions = new_conditions(pre: ->{ report_expectations(0, 1) }) - group_conditions = new_conditions(pre: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, root_conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, group_conditions) - root.children = [group.as(Spectator::ExampleComponent)] - example = PassingExample.new(group, Spectator::Internals::SampleValues.empty) - group.children = [example.as(Spectator::ExampleComponent)] - Spectator::Internals::Harness.run(example) - called.should be_false - end - - it "doesn't run child post-conditions" do - called = false - root_conditions = new_conditions(pre: ->{ report_expectations(0, 1) }) - group_conditions = new_conditions(post: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, root_conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, group_conditions) - root.children = [group.as(Spectator::ExampleComponent)] - example = PassingExample.new(group, Spectator::Internals::SampleValues.empty) - group.children = [example.as(Spectator::ExampleComponent)] - Spectator::Internals::Harness.run(example) - called.should be_false - end - end - end - - context "failing post-condition" do - it "fails the test" do - conditions = new_conditions(post: ->{ report_expectations(0, 1) }) - result = run_example(PassingExample, conditions: conditions) - result.should be_a(Spectator::FailedResult) - end - - it "prevents additional post-conditions from running" do - called = false - conditions = new_conditions(post: [ - ->{ report_expectations(0, 1) }, - ->{ called = true; nil }, - ]) - run_example(PassingExample, conditions: conditions) - called.should be_false - end - - context "in a parent group" do - it "fails the test" do - conditions = new_conditions(post: ->{ report_expectations(0, 1) }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - result = run_example(PassingExample, group) - result.should be_a(Spectator::FailedResult) - end - - it "doesn't run parent post-conditions" do - called = false - root_conditions = new_conditions(post: ->{ called = true; nil }) - group_conditions = new_conditions(post: ->{ report_expectations(0, 1) }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, root_conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, group_conditions) - root.children = [group.as(Spectator::ExampleComponent)] - example = PassingExample.new(group, Spectator::Internals::SampleValues.empty) - group.children = [example.as(Spectator::ExampleComponent)] - Spectator::Internals::Harness.run(example) - called.should be_false - end - end - end - end - - context "with a failing test" do - it "returns a failed result" do - run_example(FailingExample).should be_a(Spectator::FailedResult) - end - - {% for hook_type in %i[before_all before_each] %} - context "{{hook_type.id}} hooks" do - it "runs a hook" do - called = false - hooks = new_hooks({{hook_type.id}}: ->{ called = true; nil }) - run_example(FailingExample, hooks) - called.should be_true - end - - it "runs multiple hooks" do - call_count = 0 - hooks = new_hooks({{hook_type.id}}: [ - ->{ call_count += 1; nil }, - ->{ call_count += 2; nil }, - ->{ call_count += 3; nil }, - ]) - run_example(FailingExample, hooks) - call_count.should eq(6) - end - - it "runs them in the correct order" do - calls = [] of Symbol - hooks = new_hooks({{hook_type.id}}: [ - ->{ calls << :a; nil }, - ->{ calls << :b; nil }, - ->{ calls << :c; nil }, - ]) - run_example(FailingExample, hooks) - calls.should eq(\%i[a b c]) - end - - it "runs parent group hooks" do - called = false - hooks = new_hooks({{hook_type.id}}: -> { called = true; nil }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(FailingExample, group) - called.should be_true - end - - it "runs parent group hooks first" do - calls = [] of Symbol - root_hooks = new_hooks({{hook_type.id}}: -> { calls << :a; nil }) - group_hooks = new_hooks({{hook_type.id}}: -> { calls << :b; nil }) - root = Spectator::RootExampleGroup.new(root_hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, group_hooks, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(FailingExample, group) - calls.should eq(\%i[a b]) - end - end - {% end %} - - it "runs before_all hooks prior to before_each hooks" do - calls = [] of Symbol - hooks = new_hooks( - before_all: ->{ calls << :a; nil }, - before_each: ->{ calls << :b; nil } - ) - run_example(FailingExample, hooks) - calls.should eq(%i[a b]) - end - - {% for hook_type in %i[after_all after_each] %} - context "{{hook_type.id}} hooks" do - it "runs a hook" do - called = false - hooks = new_hooks({{hook_type.id}}: ->{ called = true; nil }) - run_example(FailingExample, hooks) - called.should be_true - end - - it "runs multiple hooks" do - call_count = 0 - hooks = new_hooks({{hook_type.id}}: [ - ->{ call_count += 1; nil }, - ->{ call_count += 2; nil }, - ->{ call_count += 3; nil }, - ]) - run_example(FailingExample, hooks) - call_count.should eq(6) - end - - it "runs them in the correct order" do - calls = [] of Symbol - hooks = new_hooks({{hook_type.id}}: [ - ->{ calls << :a; nil }, - ->{ calls << :b; nil }, - ->{ calls << :c; nil }, - ]) - run_example(FailingExample, hooks) - calls.should eq(\%i[a b c]) - end - - it "runs parent group hooks" do - called = false - hooks = new_hooks({{hook_type.id}}: -> { called = true; nil }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(FailingExample, group) - called.should be_true - end - - it "runs parent group hooks last" do - calls = [] of Symbol - root_hooks = new_hooks({{hook_type.id}}: -> { calls << :a; nil }) - group_hooks = new_hooks({{hook_type.id}}: -> { calls << :b; nil }) - root = Spectator::RootExampleGroup.new(root_hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, group_hooks, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(FailingExample, group) - calls.should eq(\%i[b a]) - end - end - {% end %} - - it "runs after_each hooks prior to after_all hooks" do - calls = [] of Symbol - hooks = new_hooks( - after_each: ->{ calls << :a; nil }, - after_all: ->{ calls << :b; nil } - ) - run_example(FailingExample, hooks) - calls.should eq(%i[a b]) - end - - context "around_each hooks" do - it "runs a hook" do - called = false - hooks = new_hooks(around_each: ->(proc : ->) { called = true; proc.call }) - run_example(FailingExample, hooks) - called.should be_true - end - - it "runs multiple hooks" 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 }, - ]) - run_example(FailingExample, hooks) - call_count.should eq(6) - end - - it "runs them 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 }, - ]) - run_example(FailingExample, hooks) - calls.should eq(%i[a b c]) - end - - it "runs parent group hooks" do - called = false - hooks = new_hooks(around_each: ->(proc : ->) { called = true; proc.call }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(FailingExample, group) - called.should be_true - end - - it "runs parent group hooks first" do - calls = [] of Symbol - root_hooks = new_hooks(around_each: ->(proc : ->) { calls << :a; proc.call }) - group_hooks = new_hooks(around_each: ->(proc : ->) { calls << :b; proc.call }) - root = Spectator::RootExampleGroup.new(root_hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, group_hooks, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(FailingExample, group) - calls.should eq(%i[a b]) - end - - it "runs around_each hooks after the before hooks" do - calls = [] of Symbol - hooks = new_hooks( - before_all: ->{ calls << :b; nil }, - before_each: ->{ calls << :b; nil }, - around_each: ->(proc : ->) { calls << :c; proc.call } - ) - run_example(FailingExample, hooks) - calls.should eq(%i[b b c]) - end - - it "runs around_each hooks prior to after hooks" do - calls = [] of Symbol - hooks = new_hooks( - around_each: ->(proc : ->) { calls << :c; proc.call }, - after_each: ->{ calls << :a; nil }, - after_all: ->{ calls << :a; nil }, - ) - run_example(FailingExample, hooks) - calls.should eq(%i[c a a]) - end - end - - context "pre-conditions" do - it "checks a single condition" do - called = false - conditions = new_conditions(pre: ->{ called = true; nil }) - run_example(FailingExample, conditions: conditions) - called.should be_true - end - - it "checks multiple conditions" do - call_count = 0 - conditions = new_conditions(pre: [ - ->{ call_count += 1; nil }, - ->{ call_count += 2; nil }, - ->{ call_count += 3; nil }, - ]) - run_example(FailingExample, conditions: conditions) - call_count.should eq(6) - end - - it "checks them in the correct order" do - calls = [] of Symbol - conditions = new_conditions(pre: [ - ->{ calls << :a; nil }, - ->{ calls << :b; nil }, - ->{ calls << :c; nil }, - ]) - run_example(FailingExample, conditions: conditions) - calls.should eq(%i[a b c]) - end - - it "checks parent group conditions" do - called = false - conditions = new_conditions(pre: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(FailingExample, group) - called.should be_true - end - - it "checks parent group conditions first" do - calls = [] of Symbol - root_conditions = new_conditions(pre: ->{ calls << :a; nil }) - group_conditions = new_conditions(pre: ->{ calls << :b; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, root_conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, group_conditions) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(FailingExample, group) - calls.should eq(%i[a b]) - end - end - - it "runs before hooks prior to pre-conditions" do - calls = [] of Symbol - hooks = new_hooks( - before_all: ->{ calls << :b; nil }, - before_each: ->{ calls << :b; nil } - ) - conditions = new_conditions(pre: ->{ calls << :p; nil }) - run_example(FailingExample, hooks, conditions) - calls.should eq(%i[b b p]) - end - - it "runs around_each hooks prior to pre-conditions" do - calls = [] of Symbol - hooks = new_hooks( - around_each: ->(proc : ->) { calls << :c; proc.call } - ) - conditions = new_conditions(pre: ->{ calls << :p; nil }) - run_example(FailingExample, hooks, conditions) - calls.should eq(%i[c p]) - end - - context "failing pre-condition" do - it "fails the test" do - conditions = new_conditions(pre: ->{ report_expectations(0, 1) }) - result = run_example(FailingExample, conditions: conditions) - result.should be_a(Spectator::FailedResult) - end - - it "prevents additional pre-conditions from running" do - called = false - conditions = new_conditions(pre: [ - ->{ report_expectations(0, 1) }, - ->{ called = true; nil }, - ]) - run_example(FailingExample, conditions: conditions) - called.should be_false - end - - it "prevents additional post-conditions from running" do - called = false - conditions = new_conditions( - pre: ->{ report_expectations(0, 1) }, - post: ->{ called = true; nil } - ) - run_example(FailingExample, conditions: conditions) - called.should be_false - end - - context "in a parent group" do - it "fails the test" do - conditions = new_conditions(pre: ->{ report_expectations(0, 1) }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - result = run_example(FailingExample, group) - result.should be_a(Spectator::FailedResult) - end - - it "doesn't run child pre-conditions" do - called = false - root_conditions = new_conditions(pre: ->{ report_expectations(0, 1) }) - group_conditions = new_conditions(pre: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, root_conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, group_conditions) - root.children = [group.as(Spectator::ExampleComponent)] - example = FailingExample.new(group, Spectator::Internals::SampleValues.empty) - group.children = [example.as(Spectator::ExampleComponent)] - Spectator::Internals::Harness.run(example) - called.should be_false - end - - it "doesn't run child post-conditions" do - called = false - root_conditions = new_conditions(pre: ->{ report_expectations(0, 1) }) - group_conditions = new_conditions(post: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, root_conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, group_conditions) - root.children = [group.as(Spectator::ExampleComponent)] - example = FailingExample.new(group, Spectator::Internals::SampleValues.empty) - group.children = [example.as(Spectator::ExampleComponent)] - Spectator::Internals::Harness.run(example) - called.should be_false - end - end - end - - it "doesn't run post-conditions" do - called = false - conditions = new_conditions(post: ->{ called = true; nil }) - run_example(FailingExample, conditions: conditions) - called.should be_false - end - - it "doesn't run parent group post-conditions" do - called = false - conditions = new_conditions(post: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(FailingExample, group) - called.should be_false - end - end - - context "with an errored test" do - it "returns an errored result" do - run_example(ErroredExample).should be_a(Spectator::ErroredResult) - end - - {% for hook_type in %i[before_all before_each] %} - context "{{hook_type.id}} hooks" do - it "runs a hook" do - called = false - hooks = new_hooks({{hook_type.id}}: ->{ called = true; nil }) - run_example(ErroredExample, hooks) - called.should be_true - end - - it "runs multiple hooks" do - call_count = 0 - hooks = new_hooks({{hook_type.id}}: [ - ->{ call_count += 1; nil }, - ->{ call_count += 2; nil }, - ->{ call_count += 3; nil }, - ]) - run_example(ErroredExample, hooks) - call_count.should eq(6) - end - - it "runs them in the correct order" do - calls = [] of Symbol - hooks = new_hooks({{hook_type.id}}: [ - ->{ calls << :a; nil }, - ->{ calls << :b; nil }, - ->{ calls << :c; nil }, - ]) - run_example(ErroredExample, hooks) - calls.should eq(\%i[a b c]) - end - - it "runs parent group hooks" do - called = false - hooks = new_hooks({{hook_type.id}}: -> { called = true; nil }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(ErroredExample, group) - called.should be_true - end - - it "runs parent group hooks first" do - calls = [] of Symbol - root_hooks = new_hooks({{hook_type.id}}: -> { calls << :a; nil }) - group_hooks = new_hooks({{hook_type.id}}: -> { calls << :b; nil }) - root = Spectator::RootExampleGroup.new(root_hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, group_hooks, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(ErroredExample, group) - calls.should eq(\%i[a b]) - end - end - {% end %} - - it "runs before_all hooks prior to before_each hooks" do - calls = [] of Symbol - hooks = new_hooks( - before_all: ->{ calls << :a; nil }, - before_each: ->{ calls << :b; nil } - ) - run_example(ErroredExample, hooks) - calls.should eq(%i[a b]) - end - - {% for hook_type in %i[after_all after_each] %} - context "{{hook_type.id}} hooks" do - it "runs a hook" do - called = false - hooks = new_hooks({{hook_type.id}}: ->{ called = true; nil }) - run_example(ErroredExample, hooks) - called.should be_true - end - - it "runs multiple hooks" do - call_count = 0 - hooks = new_hooks({{hook_type.id}}: [ - ->{ call_count += 1; nil }, - ->{ call_count += 2; nil }, - ->{ call_count += 3; nil }, - ]) - run_example(ErroredExample, hooks) - call_count.should eq(6) - end - - it "runs them in the correct order" do - calls = [] of Symbol - hooks = new_hooks({{hook_type.id}}: [ - ->{ calls << :a; nil }, - ->{ calls << :b; nil }, - ->{ calls << :c; nil }, - ]) - run_example(ErroredExample, hooks) - calls.should eq(\%i[a b c]) - end - - it "runs parent group hooks" do - called = false - hooks = new_hooks({{hook_type.id}}: -> { called = true; nil }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(ErroredExample, group) - called.should be_true - end - - it "runs parent group hooks last" do - calls = [] of Symbol - root_hooks = new_hooks({{hook_type.id}}: -> { calls << :a; nil }) - group_hooks = new_hooks({{hook_type.id}}: -> { calls << :b; nil }) - root = Spectator::RootExampleGroup.new(root_hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, group_hooks, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(ErroredExample, group) - calls.should eq(\%i[b a]) - end - end - {% end %} - - it "runs after_each hooks prior to after_all hooks" do - calls = [] of Symbol - hooks = new_hooks( - after_each: ->{ calls << :a; nil }, - after_all: ->{ calls << :b; nil } - ) - run_example(ErroredExample, hooks) - calls.should eq(%i[a b]) - end - - context "around_each hooks" do - it "runs a hook" do - called = false - hooks = new_hooks(around_each: ->(proc : ->) { called = true; proc.call }) - run_example(ErroredExample, hooks) - called.should be_true - end - - it "runs multiple hooks" 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 }, - ]) - run_example(ErroredExample, hooks) - call_count.should eq(6) - end - - it "runs them 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 }, - ]) - run_example(ErroredExample, hooks) - calls.should eq(%i[a b c]) - end - - it "runs parent group hooks" do - called = false - hooks = new_hooks(around_each: ->(proc : ->) { called = true; proc.call }) - root = Spectator::RootExampleGroup.new(hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(ErroredExample, group) - called.should be_true - end - - it "runs parent group hooks first" do - calls = [] of Symbol - root_hooks = new_hooks(around_each: ->(proc : ->) { calls << :a; proc.call }) - group_hooks = new_hooks(around_each: ->(proc : ->) { calls << :b; proc.call }) - root = Spectator::RootExampleGroup.new(root_hooks, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("what", root, group_hooks, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(ErroredExample, group) - calls.should eq(%i[a b]) - end - - it "runs around_each hooks after the before hooks" do - calls = [] of Symbol - hooks = new_hooks( - before_all: ->{ calls << :b; nil }, - before_each: ->{ calls << :b; nil }, - around_each: ->(proc : ->) { calls << :c; proc.call } - ) - run_example(ErroredExample, hooks) - calls.should eq(%i[b b c]) - end - - it "runs around_each hooks prior to after hooks" do - calls = [] of Symbol - hooks = new_hooks( - around_each: ->(proc : ->) { calls << :c; proc.call }, - after_each: ->{ calls << :a; nil }, - after_all: ->{ calls << :a; nil }, - ) - run_example(ErroredExample, hooks) - calls.should eq(%i[c a a]) - end - end - - context "pre-conditions" do - it "checks a single condition" do - called = false - conditions = new_conditions(pre: ->{ called = true; nil }) - run_example(ErroredExample, conditions: conditions) - called.should be_true - end - - it "checks multiple conditions" do - call_count = 0 - conditions = new_conditions(pre: [ - ->{ call_count += 1; nil }, - ->{ call_count += 2; nil }, - ->{ call_count += 3; nil }, - ]) - run_example(ErroredExample, conditions: conditions) - call_count.should eq(6) - end - - it "checks them in the correct order" do - calls = [] of Symbol - conditions = new_conditions(pre: [ - ->{ calls << :a; nil }, - ->{ calls << :b; nil }, - ->{ calls << :c; nil }, - ]) - run_example(ErroredExample, conditions: conditions) - calls.should eq(%i[a b c]) - end - - it "checks parent group conditions" do - called = false - conditions = new_conditions(pre: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(ErroredExample, group) - called.should be_true - end - - it "checks parent group conditions first" do - calls = [] of Symbol - root_conditions = new_conditions(pre: ->{ calls << :a; nil }) - group_conditions = new_conditions(pre: ->{ calls << :b; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, root_conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, group_conditions) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(ErroredExample, group) - calls.should eq(%i[a b]) - end - end - - it "runs before hooks prior to pre-conditions" do - calls = [] of Symbol - hooks = new_hooks( - before_all: ->{ calls << :b; nil }, - before_each: ->{ calls << :b; nil } - ) - conditions = new_conditions(pre: ->{ calls << :p; nil }) - run_example(ErroredExample, hooks, conditions) - calls.should eq(%i[b b p]) - end - - it "runs around_each hooks prior to pre-conditions" do - calls = [] of Symbol - hooks = new_hooks( - around_each: ->(proc : ->) { calls << :c; proc.call } - ) - conditions = new_conditions(pre: ->{ calls << :p; nil }) - run_example(ErroredExample, hooks, conditions) - calls.should eq(%i[c p]) - end - - context "failing pre-condition" do - it "fails the test" do - conditions = new_conditions(pre: ->{ report_expectations(0, 1) }) - result = run_example(ErroredExample, conditions: conditions) - result.should be_a(Spectator::FailedResult) - end - - it "prevents additional pre-conditions from running" do - called = false - conditions = new_conditions(pre: [ - ->{ report_expectations(0, 1) }, - ->{ called = true; nil }, - ]) - run_example(ErroredExample, conditions: conditions) - called.should be_false - end - - it "prevents additional post-conditions from running" do - called = false - conditions = new_conditions( - pre: ->{ report_expectations(0, 1) }, - post: ->{ called = true; nil } - ) - run_example(ErroredExample, conditions: conditions) - called.should be_false - end - - context "in a parent group" do - it "fails the test" do - conditions = new_conditions(pre: ->{ report_expectations(0, 1) }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - result = run_example(ErroredExample, group) - result.should be_a(Spectator::FailedResult) - end - - it "doesn't run child pre-conditions" do - called = false - root_conditions = new_conditions(pre: ->{ report_expectations(0, 1) }) - group_conditions = new_conditions(pre: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, root_conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, group_conditions) - root.children = [group.as(Spectator::ExampleComponent)] - example = ErroredExample.new(group, Spectator::Internals::SampleValues.empty) - group.children = [example.as(Spectator::ExampleComponent)] - Spectator::Internals::Harness.run(example) - called.should be_false - end - - it "doesn't run child post-conditions" do - called = false - root_conditions = new_conditions(pre: ->{ report_expectations(0, 1) }) - group_conditions = new_conditions(post: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, root_conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, group_conditions) - root.children = [group.as(Spectator::ExampleComponent)] - example = ErroredExample.new(group, Spectator::Internals::SampleValues.empty) - group.children = [example.as(Spectator::ExampleComponent)] - Spectator::Internals::Harness.run(example) - called.should be_false - end - end - end - - it "doesn't run post-conditions" do - called = false - conditions = new_conditions(post: ->{ called = true; nil }) - run_example(ErroredExample, conditions: conditions) - called.should be_false - end - - it "doesn't run parent group post-conditions" do - called = false - conditions = new_conditions(post: ->{ called = true; nil }) - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, conditions) - group = Spectator::NestedExampleGroup.new("what", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - run_example(ErroredExample, group) - called.should be_false - end - end - - context "when an error is raised in a before_all hook" do - it "raises the exception" do - hooks = new_hooks(before_all: ->{ raise "oops"; nil }) - expect_raises(Exception) do - run_example(PassingExample, hooks) - end - end - - it "passes along the original exception" do - error = Exception.new("oops") - hooks = new_hooks(before_all: ->{ raise error; nil }) - begin - run_example(PassingExample, hooks) - rescue ex - ex.cause.should eq(error) - end - end - - it "doesn't run the test code" do - called = false - hooks = new_hooks(before_all: ->{ raise "oops"; nil }) - expect_raises(Exception) do - run_example(hooks) do - called = true - end - end - called.should be_false - end - - it "doesn't run any additional before_all hooks" do - called = false - hooks = new_hooks(before_all: [ - ->{ raise "oops"; nil }, - ->{ called = true; nil }, - ]) - expect_raises(Exception) do - run_example(PassingExample, hooks) - end - called.should be_false - end - - it "doesn't run any additional hooks" do - called = :none - hooks = new_hooks( - before_all: ->{ raise "oops"; nil }, - before_each: ->{ called = :before_each; nil }, - after_all: ->{ called = :after_all; nil }, - after_each: ->{ called = :after_each; nil }, - around_each: ->(proc : ->) { called = :around_each; proc.call }) - expect_raises(Exception) do - run_example(PassingExample, hooks) - end - called.should eq(:none) - end - end - - context "when an error is raised in a before_each hook" do - it "raises the exception" do - hooks = new_hooks(before_each: ->{ raise "oops"; nil }) - expect_raises(Exception) do - run_example(PassingExample, hooks) - end - end - - it "passes along the original exception" do - error = Exception.new("oops") - hooks = new_hooks(before_each: ->{ raise error; nil }) - begin - run_example(PassingExample, hooks) - rescue ex - ex.cause.should eq(error) - end - end - - it "doesn't run the test code" do - called = false - hooks = new_hooks(before_each: ->{ raise "oops"; nil }) - expect_raises(Exception) do - run_example(hooks) do - called = true - end - end - called.should be_false - end - - it "doesn't run any additional before_each hooks" do - called = false - hooks = new_hooks(before_each: [ - ->{ raise "oops"; nil }, - ->{ called = true; nil }, - ]) - expect_raises(Exception) do - run_example(PassingExample, hooks) - end - called.should be_false - end - - it "doesn't run any additional hooks" do - called = :none - hooks = new_hooks( - before_each: ->{ raise "oops"; nil }, - after_all: ->{ called = :after_all; nil }, - after_each: ->{ called = :after_each; nil }, - around_each: ->(proc : ->) { called = :around_each; proc.call }) - expect_raises(Exception) do - run_example(PassingExample, hooks) - end - called.should eq(:none) - end - end - - context "when an error is raised in an after_all hook" do - it "raises the exception" do - hooks = new_hooks(after_all: ->{ raise "oops"; nil }) - expect_raises(Exception) do - run_example(PassingExample, hooks) - end - end - - it "passes along the original exception" do - error = Exception.new("oops") - hooks = new_hooks(after_all: ->{ raise error; nil }) - begin - run_example(PassingExample, hooks) - rescue ex - ex.cause.should eq(error) - end - end - - it "doesn't run any additional after_all hooks" do - called = false - hooks = new_hooks(after_all: [ - ->{ raise "oops"; nil }, - ->{ called = true; nil }, - ]) - expect_raises(Exception) do - run_example(PassingExample, hooks) - end - called.should be_false - end - end - - context "when an error is raised in an after_each hook" do - it "raises the exception" do - hooks = new_hooks(after_each: ->{ raise "oops"; nil }) - expect_raises(Exception) do - run_example(PassingExample, hooks) - end - end - - it "passes along the original exception" do - error = Exception.new("oops") - hooks = new_hooks(after_each: ->{ raise error; nil }) - begin - run_example(PassingExample, hooks) - rescue ex - ex.cause.should eq(error) - end - end - - it "doesn't run any additional after_each hooks" do - called = false - hooks = new_hooks(after_each: [ - ->{ raise "oops"; nil }, - ->{ called = true; nil }, - ]) - expect_raises(Exception) do - run_example(PassingExample, hooks) - end - called.should be_false - end - - it "doesn't run any additional hooks" do - called = :none - hooks = new_hooks( - before_all: ->{ nil }, - before_each: ->{ nil }, - after_all: ->{ called = :after_all; nil }, - after_each: ->{ raise "oops"; nil }, - around_each: ->(proc : ->) { proc.call }) - expect_raises(Exception) do - run_example(PassingExample, hooks) - end - called.should eq(:none) - end - end - - context "when an error is raised in an around_each hook" do - it "raises the exception" do - hooks = new_hooks(around_each: ->(proc : ->) { raise "oops"; proc.call }) - expect_raises(Exception) do - run_example(PassingExample, hooks) - end - end - - it "passes along the original exception" do - error = Exception.new("oops") - hooks = new_hooks(around_each: ->(proc : ->) { raise error; proc.call }) - begin - run_example(PassingExample, hooks) - rescue ex - ex.cause.should eq(error) - end - end - - it "doesn't run the test code" do - called = false - hooks = new_hooks(around_each: ->(proc : ->) { raise "oops"; proc.call }) - expect_raises(Exception) do - run_example(hooks) do - called = true - end - end - called.should be_false - end - - it "doesn't run any additional around_each hooks" do - called = false - hooks = new_hooks(around_each: [ - ->(proc : ->) { raise "oops"; proc.call }, - ->(proc : ->) { called = true; proc.call }, - ]) - expect_raises(Exception) do - run_example(PassingExample, hooks) - end - called.should be_false - end - - it "doesn't run any additional hooks" do - called = :none - hooks = new_hooks( - after_all: ->{ called = :after_all; nil }, - after_each: ->{ called = :after_each; nil }, - around_each: ->(proc : ->) { raise "oops"; proc.call }) - expect_raises(Exception) do - run_example(PassingExample, hooks) - end - called.should eq(:none) - end - end - end - - describe "#finished?" do - it "is initially false" do - new_runnable_example.finished?.should be_false - end - - it "is true after #run is called" do - example = new_runnable_example - Spectator::Internals::Harness.run(example) - example.finished?.should be_true - end - end - - describe "#group" do - it "is the expected value" do - group = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - example = new_runnable_example(group) - example.group.should eq(group) - end - end - - describe "#example_count" do - it "is one" do - new_runnable_example.example_count.should eq(1) - end - end - - describe "#[]" do - it "returns self" do - example = new_runnable_example - example[0].should eq(example) - end - end - - describe "#to_s" do - it "contains #what" do - example = new_runnable_example - example.to_s.should contain(example.what) - end - - it "contains the group's #what" do - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("GROUP", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - example = new_runnable_example(group) - example.to_s.should contain(group.what.to_s) - end - - context "when #symbolic? is true" do - context "and the group is symbolic" do - it "omits the space" do - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new(:Group, root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - example = new_runnable_example(group, true) - group.symbolic?.should be_true - example.symbolic?.should be_true - example.to_s.should_not contain(' ') - end - end - - context "and the group isn't symbolic" do - it "inserts a space" do - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("GROUP", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - example = new_runnable_example(group, true) - group.symbolic?.should be_false - example.symbolic?.should be_true - example.to_s.should contain(' ') - end - end - end - - context "when #symbolic? is false" do - context "and the group is symbolic" do - it "inserts a space" do - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new(:Group, root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - example = new_runnable_example(group, false) - group.symbolic?.should be_true - example.symbolic?.should be_false - example.to_s.should contain(' ') - end - end - - context "and the group isn't symbolic" do - it "inserts a space" do - root = Spectator::RootExampleGroup.new(Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - group = Spectator::NestedExampleGroup.new("GROUP", root, Spectator::ExampleHooks.empty, Spectator::ExampleConditions.empty) - root.children = [group.as(Spectator::ExampleComponent)] - example = new_runnable_example(group, false) - group.symbolic?.should be_false - example.symbolic?.should be_false - example.to_s.should contain(' ') - end - end - end - end -end diff --git a/spec/runner_spec.cr b/spec/runner_spec.cr deleted file mode 100644 index c2c4dfb..0000000 --- a/spec/runner_spec.cr +++ /dev/null @@ -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 diff --git a/spec/source_example_filter_spec.cr b/spec/source_example_filter_spec.cr deleted file mode 100644 index f8e5e0a..0000000 --- a/spec/source_example_filter_spec.cr +++ /dev/null @@ -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 diff --git a/spec/source_spec.cr b/spec/source_spec.cr deleted file mode 100644 index 1ad6950..0000000 --- a/spec/source_spec.cr +++ /dev/null @@ -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 diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr index 32978f5..30b0ee7 100644 --- a/spec/spec_helper.cr +++ b/spec/spec_helper.cr @@ -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 diff --git a/spec/successful_result_spec.cr b/spec/successful_result_spec.cr deleted file mode 100644 index 2f6be49..0000000 --- a/spec/successful_result_spec.cr +++ /dev/null @@ -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 diff --git a/spec/test_suite_spec.cr b/spec/test_suite_spec.cr deleted file mode 100644 index 5e41278..0000000 --- a/spec/test_suite_spec.cr +++ /dev/null @@ -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 diff --git a/src/spectator.cr b/src/spectator.cr index 5f17921..a752c60 100644 --- a/src/spectator.cr +++ b/src/spectator.cr @@ -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. diff --git a/src/spectator/dsl.cr b/src/spectator/dsl.cr index 5689a6f..0d13112 100644 --- a/src/spectator/dsl.cr +++ b/src/spectator/dsl.cr @@ -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 diff --git a/src/spectator/dsl/example_dsl.cr b/src/spectator/dsl/assertions.cr similarity index 93% rename from src/spectator/dsl/example_dsl.cr rename to src/spectator/dsl/assertions.cr index 5fea5a7..f315a63 100644 --- a/src/spectator/dsl/example_dsl.cr +++ b/src/spectator/dsl/assertions.cr @@ -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. diff --git a/src/spectator/dsl/example_factory.cr b/src/spectator/dsl/example_factory.cr deleted file mode 100644 index 91ee4b6..0000000 --- a/src/spectator/dsl/example_factory.cr +++ /dev/null @@ -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 diff --git a/src/spectator/dsl/example_group_builder.cr b/src/spectator/dsl/example_group_builder.cr deleted file mode 100644 index f8cf8d0..0000000 --- a/src/spectator/dsl/example_group_builder.cr +++ /dev/null @@ -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 diff --git a/src/spectator/dsl/examples.cr b/src/spectator/dsl/examples.cr new file mode 100644 index 0000000..041b1a8 --- /dev/null +++ b/src/spectator/dsl/examples.cr @@ -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 diff --git a/src/spectator/dsl/groups.cr b/src/spectator/dsl/groups.cr new file mode 100644 index 0000000..8153b0e --- /dev/null +++ b/src/spectator/dsl/groups.cr @@ -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 diff --git a/src/spectator/dsl/hooks.cr b/src/spectator/dsl/hooks.cr new file mode 100644 index 0000000..5254068 --- /dev/null +++ b/src/spectator/dsl/hooks.cr @@ -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 diff --git a/src/spectator/dsl/matcher_dsl.cr b/src/spectator/dsl/matchers.cr similarity index 99% rename from src/spectator/dsl/matcher_dsl.cr rename to src/spectator/dsl/matchers.cr index bcfbec1..5c55a06 100644 --- a/src/spectator/dsl/matcher_dsl.cr +++ b/src/spectator/dsl/matchers.cr @@ -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. diff --git a/src/spectator/dsl/nested_example_group_builder.cr b/src/spectator/dsl/nested_example_group_builder.cr deleted file mode 100644 index 31aef42..0000000 --- a/src/spectator/dsl/nested_example_group_builder.cr +++ /dev/null @@ -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 diff --git a/src/spectator/dsl/root_example_group_builder.cr b/src/spectator/dsl/root_example_group_builder.cr deleted file mode 100644 index 8345000..0000000 --- a/src/spectator/dsl/root_example_group_builder.cr +++ /dev/null @@ -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 diff --git a/src/spectator/dsl/sample_example_group_builder.cr b/src/spectator/dsl/sample_example_group_builder.cr deleted file mode 100644 index 5a03798..0000000 --- a/src/spectator/dsl/sample_example_group_builder.cr +++ /dev/null @@ -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 diff --git a/src/spectator/dsl/structure_dsl.cr b/src/spectator/dsl/structure_dsl.cr deleted file mode 100644 index af2c0ec..0000000 --- a/src/spectator/dsl/structure_dsl.cr +++ /dev/null @@ -1,1701 +0,0 @@ -require "../example_group" - -module Spectator::DSL - # Domain specific language for the main structure of a spec. - # The primary components of this are `#describe`, `#context`, and `#it`. - # - # These macros define modules and classes. - # Those modules and classes are used to create the test cases. - # - # A class is created for every block of code that contains test code. - # An `#it` block creates a class derived from `RunnableExample`. - # A `#pending` block creates a class derived from `PendingExample`. - # The classes are built so that they run the example's code when invoked. - # However, the actual example code is placed into a separate "wrapper" class. - # This is done to avoid overlap with the Spectator namespace. - # The example code ends up having visibility only into itself and the DSL. - # - # Here's some skeleton code to demonstrate this: - # ``` - # it "does something" do - # # Test code goes here... - # end - # ``` - # - # becomes... - # - # ``` - # # Class describing the example - # # and provides a means of running the test. - # # Typically every class, module, and method - # # that the user might see or be able to reference is obscured. - # # Fresh variables from Crystal's macros are used to achive this. - # # It makes debugging Spectator more difficult, - # # but prevents name collision with user code. - # class Example123 < RunnableExample - # def initialize(group, sample_values) - # # group and sample_values are covered later. - # super - # @instance = Test123.new(sample_values) - # end - # - # # Returns the text provided by the user. - # # This isn't stored as a member - # # so that it can be referenced directly in compiled code. - # def what - # "does something" - # end - # - # # This method is called by `RunnableExample` - # # when the example code should be ran. - # def run_instance - # @instance._run123 - # end - # end - # - # # Wrapper class for the example code. - # # This isolates it from Spectator's internals. - # class Test123 - # include Context123 # More on this in a bit. - # include ExampleDSL # Include DSL for the example code. - # - # # Generated method name to avoid conflicts. - # def _run123 - # # Test code goes here... - # end - # end - # ``` - # - # Modules are used to provide context and share methods across examples. - # They are used as mix-ins for the example code. - # The example code wrapper class includes its parent module. - # This allows the example to access anything that was defined in the same context. - # Contexts can be nested, and this is achieved by including the parent module. - # Whenever a module or class is defined, - # it includes its parent so that functionality can be inherited. - # - # For example: - # ``` - # describe "#foo" do - # subject { described_class.foo(value) } - # - # context "when given :bar" do - # let(value) { :bar } - # - # it "does something" do - # # ... - # end - # end - # end - # ``` - # - # becomes... - # - # ``` - # # describe "#foo" - # module Context123 - # # Start a new group. - # # More on this in a bit. - # Builder.start_group("#foo") - # - # def subject - # described_class.foo(value) - # end - # - # # context "when given :bar" - # module Context456 - # include Context123 # Inherit parent module. - # - # # Start a nested group. - # Builder.start_group("when given :bar") - # - # def value - # :bar - # end - # - # # Wrapper class for the test case. - # class Test456 - # include Context456 # Include context. - # - # # Rest of test code... - # end - # - # # Example class for the test case. - # class Example456 < RunnableExample - # # Rest of example code... - # end - # - # # Add example to group. - # Builder.add_example(Example456) - # - # # End "when given :bar" group. - # Builder.end_group - # end - # - # # End "#foo" group. - # Builder.end_group - # end - # ``` - # - # In addition to providing modules as mix-ins, - # example groups are defined with `#describe` and `#context`. - # The DSL makes use of `Builder` to construct the run-time portion of the spec. - # As groups are defined, they are pushed on a stack - # and popped off after everything nested in them is defined. - # `Builder` tracks the current group (top of the stack). - # This way, examples, hooks, nested groups, and other items can be added to it. - # Groups and examples are nested in a parent group. - # The only group that isn't nested is the root group - `RootExampleGroup`. - # - # Some example groups make use of sample values. - # Sample values are a collection of test values that can be used in examples. - # For more information, see `Internals::SampleValues`. - module StructureDSL - # Creates a new example group to describe a component. - # The *what* argument describes "what" is being tested. - # Additional example groups and DSL may be nested in the block. - # - # Typically when testing a method, - # the spec is written like so: - # ``` - # describe "#foo" do - # it "does something" do - # # ... - # end - # end - # ``` - # - # When describing a class (or any other type), - # the *what* parameter doesn't need to be quoted. - # ``` - # describe String do - # it "does something" do - # # ... - # end - # end - # ``` - # - # And when combining the two together: - # ``` - # describe String do - # describe "#size" do - # it "returns the length" do - # # ... - # end - # end - # end - # ``` - # - # The `#describe` and `#context` are identical in terms of functionality. - # However, `#describe` is typically used on classes and methods, - # while `#context` is used for use cases and scenarios. - macro describe(what, &block) - context({{what}}) {{block}} - end - - # Creates a new example group to describe a situation. - # The *what* argument describes the scenario or case being tested. - # Additional example groups and DSL may be nested in the block. - # - # The `#describe` and `#context` are identical in terms of functionality. - # However, `#describe` is typically used on classes and methods, - # while `#context` is used for use cases and scenarios. - # - # Using context blocks in conjunction with hooks, `#let`, and other methods - # provide an easy way to define the scenario in code. - # This also gives each example in the context an identical situation to run in. - # - # For instance: - # ``` - # describe String do - # context "when empty" do - # subject { "" } - # - # it "has a size of zero" do - # expect(subject.size).to eq(0) - # end - # - # it "is blank" do - # expect(subject.blank?).to be_true - # end - # end - # - # context "when not empty" do - # subject { "foobar" } - # - # it "has a non-zero size" do - # expect(subject.size).to_not eq(0) - # end - # - # it "is not blank" do - # expect(subject.blank?).to be_false - # end - # end - # end - # ``` - # - # While this is a somewhat contrived example, - # it demonstrates how contexts can reuse code. - # Contexts also make it clearer how a scenario is setup. - macro context(what, &block) - # Module for the context. - # The module uses a generated unique name. - module Context%context - # Include the parent module. - # Since `@type` resolves immediately, - # this will reference the parent type. - include {{@type.id}} - - # Check if `what` looks like a type. - # If it is, add the `#described_class` and `subject` methods. - # At the time of writing this code, - # this is the way (at least that I know of) - # to check if an AST node is a type name. - # - # NOTE: In Crystal 0.27, it looks like `#resolve` can be used. - # Need to investigate, but would also increase minimum version. - {% if what.is_a?(Path) || what.is_a?(Generic) %} - # Returns the type currently being described. - macro described_class - {{what}} - end - - # Implicit subject definition. - # Simply creates a new instance of the described type. - def subject(*args) - described_class.new(*args) - end - {% end %} - - # Start a new group. - ::Spectator::DSL::Builder.start_group( - {% if what.is_a?(StringLiteral) %} - {% if what.starts_with?("#") || what.starts_with?(".") %} - {{what.id.symbolize}} - {% else %} - {{what}} - {% end %} - {% else %} - {{what.symbolize}} - {% end %} - ) - - # Nest the block's content in the module. - {{block.body}} - - # End the current group. - ::Spectator::DSL::Builder.end_group - end - end - - # Creates an example group with a very concise syntax. - # This can be used in scenarios where one or more input values - # change the result of various methods. - # The normal DSL can be used within this context, - # but a shorter syntax provides an easier way to read and write multiple tests. - # - # Here's an example of where this is useful: - # ``` - # describe Int32 do - # subject { described_class.new(value) } - # - # context "when given 5" do - # describe "#odd?" do - # subject { value.odd? } - # - # it "is true" do - # is_expected.to be_true - # end - # - # # NOTE: These could also be the one-liner syntax, - # # but that is still very verbose. - # end - # - # describe "#even?" do - # subject { value.even? } - # - # it "is false" do - # is_expected.to be_false - # end - # end - # end - # - # context "when given 42" do - # describe "#odd?" do - # subject { value.odd? } - # - # it "is false" do - # is_expected.to be_false - # end - # end - # - # describe "#even?" do - # subject { value.even? } - # - # it "is true" do - # is_expected.to be_true - # end - # end - # end - # end - # ``` - # - # There's a lot of repetition and nested groups - # to test a very simple scenario. - # - # Using a `#given` block, this type of scenario becomes much more compact. - # ``` - # describe Int32 do - # subject { described_class.new(value) } - # - # given value = 5 do - # expect(&.odd?).to be_true - # expect(&.even?).to be_false - # end - # - # given value = 42 do - # expect(&.odd?).to be_false - # expect(&.even?).to be_true - # end - # end - # ``` - # - # One or more assignments can be used. - # Each assignment is passed to its own `#let`. - # For example: - # ``` - # given x = 1, y = 2 do - # expect(x + y).to eq(3) - # end - # ``` - # - # Each statement in the block is converted to the one-liner syntax of `#it`. - # For instance: - # ``` - # given x = 1 do - # expect(x).to eq(1) - # end - # ``` - # is converted to: - # ``` - # context "x = 1" do - # let(x) { 1 } - # - # it expect(x).to eq(1) - # end - # ``` - # - # Additionally, the "it" syntax can be used and mixed in. - # This allows for flexibility and a more readable format when needed. - # ``` - # given x = 1 do - # it "is odd" do - # expect(x.odd?).to be_true - # end - # - # it is(&.odd?) - # end - # ``` - macro given(*assignments, &block) - context({{assignments.splat.stringify}}) do - # Create a `let` entry for each assignment. - {% 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 body for given block" - 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 - %} - - # Transform every item in the block to a test case. - {% 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 - - # Creates a new example group to test multiple values with. - # This method takes a collection of values - # and repeats the contents of the block with each value. - # The *collection* argument should be a literal collection, - # such as an array, or a function that returns an enumerable. - # Additionally, a count may be specified to limit the number of values tested. - # - # NOTE: If an infinite enumerable is provided for the collection, - # then a count must be specified. - # Only the first *count* items will be used. - # - # The block can accept an argument. - # If it does, then the argument's name is used to reference - # the current item in the collection. - # If an argument isn't provided, then *value* can be used instead. - # - # Example with a block argument: - # ``` - # sample some_integers do |integer| - # it "sets the value" do - # subject.value = integer - # expect(subject.value).to eq(integer) - # end - # end - # ``` - # - # Same spec, but without a block argument: - # ``` - # sample some_integers do - # it "sets the value" do - # subject.value = value - # expect(subject.value).to eq(value) - # end - # end - # ``` - # - # In the examples above, the test case (`#it` block) - # is repeated for each element in *some_integers*. - # *some_integers* is a ficticous collection. - # The collection will be iterated once. - # `#sample` and `#random_sample` blocks can be nested, - # and work similarly to loops. - # - # A limit can be specified as well. - # After the collection, a count can be added to limit - # the number of items taken from the collection. - # For instance: - # ``` - # sample some_integers, 5 do |integer| - # it "sets the value" do - # subject.value = integer - # expect(subject.value).to eq(integer) - # end - # end - # ``` - # - # See also: `#random_sample` - macro sample(collection, count = nil, &block) - # Figure out the name to use for the current collection element. - # If a block argument is provided, use it, otherwise use "value". - {% name = block.args.empty? ? "value".id : block.args.first %} - - # Method for retrieving the entire collection. - # This simplifies getting the element type. - # The name is uniquely generated to prevent namespace collision. - # This method should be called only once. - def %sample - {{collection}} - end - - # Class for generating an array with the collection's contents. - # This has to be a class that includes the parent module. - # The collection could reference a helper method - # or anything else in the parent scope. - class Sample%sample - # Include the parent module. - include {{@type.id}} - - # 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) - super - end - - # Method that returns an array containing the collection. - # This method should be called only once. - # The framework stores the collection as an array for a couple of reasons. - # 1. The collection may not support multiple iterations. - # 2. The collection might contain randomly generated values. - # Iterating multiple times would generate inconsistent values at runtime. - def %to_a - # If a count was provided, - # only select the first *count* items from the collection. - # Otherwise, select all of them. - {% if count %} - %sample.first({{count}}) - {% else %} - %sample.to_a - {% end %} - end - end - - # Module for the context. - # The module uses a generated unique name. - module Context%sample - # Include the parent module. - # Since @type resolves immediately, - # this will reference the parent type. - include {{@type.id}} - - # Value wrapper for the current element. - @%wrapper : ::Spectator::Internals::ValueWrapper - - # Retrieves the current element from the collection. - def {{name}} - # Unwrap the value and return it. - # The `Enumerable#first` method has a return type that matches the element type. - # So it is used on the collection method proxy to resolve the type at compile-time. - @%wrapper.as(::Spectator::Internals::TypedValueWrapper(typeof(%sample.first))).value - end - - # Initializer to extract current element of the collection from sample values. - def initialize(sample_values : ::Spectator::Internals::SampleValues) - super - @%wrapper = sample_values.get_wrapper(:%sample) - end - - # Start a new example group. - # Sample groups require additional configuration. - ::Spectator::DSL::Builder.start_sample_group( - {{collection.stringify}}, # String representation of the collection. - Sample%sample, # Type that can construct the elements. - ->(s : Sample%sample) { s.%to_a }, # Proc to build the array of elements in the collection. - {{name.stringify}}, # Name for the current element. - :%sample # Unique identifier for retrieving elements for the associated collection. - ) - - # Nest the block's content in the module. - {{block.body}} - - # End the current group. - ::Spectator::DSL::Builder.end_group - end - end - - # Creates a new example group to test multiple random values with. - # This method takes a collection of values and count - # and repeats the contents of the block with each value. - # This method randomly selects *count* items from the collection. - # The *collection* argument should be a literal collection, - # such as an array, or a function that returns an enumerable. - # - # NOTE: If an enumerable is used, it must be finite. - # - # The block can accept an argument. - # If it does, then the argument's name is used to reference - # the current item in the collection. - # If an argument isn't provided, then *value* can be used instead. - # - # Example with a block argument: - # ``` - # random_sample some_integers, 5 do |integer| - # it "sets the value" do - # subject.value = integer - # expect(subject.value).to eq(integer) - # end - # end - # ``` - # - # Same spec, but without a block argument: - # ``` - # random_sample some_integers, 5 do - # it "sets the value" do - # subject.value = value - # expect(subject.value).to eq(value) - # end - # end - # ``` - # - # In the examples above, the test case (`#it` block) - # is repeated for 5 random elements in *some_integers*. - # *some_integers* is a ficticous collection. - # The collection will be iterated once. - # `#sample` and `#random_sample` blocks can be nested, - # and work similarly to loops. - # - # NOTE: If the count is the same or higher - # than the number of elements in the collection, - # then this method if functionaly equivalent to `#sample`. - # - # See also: `#sample` - macro random_sample(collection, count, &block) - # Figure out the name to use for the current collection element. - # If a block argument is provided, use it, otherwise use "value". - {% name = block.args.empty? ? "value".id : block.args.first %} - - # Method for retrieving the entire collection. - # This simplifies getting the element type. - # The name is uniquely generated to prevent namespace collision. - # This method should be called only once. - def %sample - {{collection}} - end - - # Class for generating an array with the collection's contents. - # This has to be a class that includes the parent module. - # The collection could reference a helper method - # or anything else in the parent scope. - class Sample%sample - # Include the parent module. - include {{@type.id}} - - # Method that returns an array containing the collection. - # This method should be called only once. - # The framework stores the collection as an array for a couple of reasons. - # 1. The collection may not support multiple iterations. - # 2. The collection might contain randomly generated values. - # Iterating multiple times would generate inconsistent values at runtime. - def %to_a - %sample.to_a.sample({{count}}, ::Spectator.random) - end - end - - # Module for the context. - # The module uses a generated unique name. - module Context%sample - # Include the parent module. - # Since @type resolves immediately, - # this will reference the parent type. - include {{@type.id}} - - # Value wrapper for the current element. - @%wrapper : ::Spectator::Internals::ValueWrapper - - # Retrieves the current element from the collection. - def {{name}} - # Unwrap the value and return it. - # The `Enumerable#first` method has a return type that matches the element type. - # So it is used on the collection method proxy to resolve the type at compile-time. - @%wrapper.as(::Spectator::Internals::TypedValueWrapper(typeof(%sample.first))).value - end - - # Initializer to extract current element of the collection from sample values. - def initialize(sample_values : ::Spectator::Internals::SampleValues) - super - @%wrapper = sample_values.get_wrapper(:%sample) - end - - # Start a new example group. - # Sample groups require additional configuration. - ::Spectator::DSL::Builder.start_sample_group( - {{collection.stringify}}, # String representation of the collection. - Sample%sample, # All elements in the collection. - {{name.stringify}}, # Name for the current element. - :%sample # Unique identifier for retrieving elements for the associated collection. - ) - - # Nest the block's content in the module. - {{block.body}} - - # End the current group. - ::Spectator::DSL::Builder.end_group - end - end - - # Explicitly defines the subject being tested. - # The `#subject` method can be used in examples to retrieve the value (basically a method). - # - # This macro expects a block. - # The block should return the value of the subject. - # This can be used to define a value once and reuse it in multiple examples. - # - # For instance: - # ``` - # subject { "foobar" } - # - # it "isn't empty" do - # expect(subject.empty?).to be_false - # end - # - # it "is six characters" do - # expect(subject.size).to eq(6) - # end - # ``` - # - # By using a subject, some of the DSL becomes simpler. - # For example, `ExampleDSL#is_expected` can be used. - # ``` - # subject { "foobar" } - # - # it "isn't empty" do - # is_expected.to_not be_empty # is the same as: - # expect(subject).to_not be_empty - # end - # ``` - # - # This macro is functionaly equivalent to: - # ``` - # let(:subject) { "foo" } - # ``` - # - # The subject is created the first time it is referenced (lazy initialization). - # It is cached so that the same instance is used throughout the test. - # The subject will be recreated for each test it is used in. - # - # ``` - # subject { [0, 1, 2] } - # - # it "modifies the array" do - # subject[0] = 42 - # is_expected.to eq([42, 1, 2]) - # end - # - # it "doesn't carry across tests" do - # subject[1] = 777 - # is_expected.to eq([0, 777, 2]) - # end - # ``` - macro subject(&block) - let(:subject) {{block}} - end - - # Explicitly defines the subject being tested. - # The `#subject` method can be used in examples to retrieve the value (basically a method). - # This also names the subject so that it can be referenced by the name instead. - # - # This macro expects a block. - # The block should return the value of the subject. - # This can be used to define a value once and reuse it in multiple examples. - # - # For instance: - # ``` - # subject(string) { "foobar" } - # - # it "isn't empty" do - # # Refer to it with `subject`. - # expect(subject.empty?).to be_false - # end - # - # it "is six characters" do - # # Refer to it by its name `string`. - # expect(string.size).to eq(6) - # end - # ``` - # - # The subject is created the first time it is referenced (lazy initialization). - # It is cached so that the same instance is used throughout the test. - # The subject will be recreated for each test it is used in. - macro subject(name, &block) - let({{name.id}}) {{block}} - subject { {{name.id}} } - end - - # Defines an expression by name. - # The name can be used in examples to retrieve the value (basically a method). - # This can be used to define a value once and reuse it in multiple examples. - # - # There are two variants - assignment and block. - # Both must be given a name. - # - # For the assignment variant: - # ``` - # let string = "foobar" - # - # it "isn't empty" do - # expect(string.empty?).to be_false - # end - # - # it "is six characters" do - # expect(string.size).to eq(6) - # end - # ``` - # - # The value is evaluated and stored immediately. - # This is different from other `#let` variants that lazily-evaluate. - # - # ``` - # let current_time = Time.utc - # let(lazy_time) { Time.utc } - # - # it "lazy evaluates" do - # sleep 5 - # expect(lazy_time).to_not eq(now) - # end - # ``` - # - # However, the value is not reused across tests. - # Each test will have its own copy. - # - # ``` - # let array = [0, 1, 2] - # - # it "modifies the array" do - # array[0] = 42 - # expect(array).to eq([42, 1, 2]) - # end - # - # it "doesn't carry across tests" do - # array[1] = 777 - # expect(array).to eq([0, 777, 2]) - # end - # ``` - # - # The block variant expects a name and a block. - # The name can be a symbol or a literal - same as `Object#getter`. - # The block should return the value. - # - # For instance: - # ``` - # let(string) { "foobar" } - # - # it "isn't empty" do - # expect(string.empty?).to be_false - # end - # - # it "is six characters" do - # expect(string.size).to eq(6) - # end - # ``` - # - # The value is lazy-evaluated - - # meaning that it is only created on the first reference to it. - # Afterwards, the value is cached, - # so the same value is returned with consecutive calls. - # - # ``` - # let(current_time) { Time.utc } - # - # it "lazy evaluates" do - # now = current_time - # sleep 5 - # expect(current_time).to eq(now) - # end - # ``` - # - # However, the value is not reused across tests. - # It will be reconstructed the first time it is referenced in the next test. - # - # ``` - # let(array) { [0, 1, 2] } - # - # it "modifies the array" do - # array[0] = 42 - # expect(array).to eq([42, 1, 2]) - # end - # - # it "doesn't carry across tests" do - # array[1] = 777 - # expect(array).to eq([0, 777, 2]) - # end - # ``` - macro let(name, &block) - {% if block.is_a?(Nop) %} - # Assignment variant. - @%value = {{name.value}} - - def {{name.target}} - @%value - end - {% else %} - # Block variant. - - # Create a block that returns the value. - let!(%value) {{block}} - - # Wrapper to hold the value. - # This will be nil if the value hasn't been referenced yet. - # After being referenced, the cached value will be stored in a wrapper. - @%wrapper : ::Spectator::Internals::ValueWrapper? - - # Method for returning the value. - def {{name.id}} - # Check if the value is cached. - # The wrapper will be nil if it isn't. - if (wrapper = @%wrapper) - # It is cached, return that value. - # Unwrap it from the wrapper variable. - # Here we use typeof to get around the issue - # that the macro has no idea what type the value is. - wrapper.unsafe_as(::Spectator::Internals::TypedValueWrapper(typeof(%value))).value - else - # The value isn't cached, - # Construct it and store it in the wrapper. - %value.tap do |value| - @%wrapper = ::Spectator::Internals::TypedValueWrapper(typeof(%value)).new(value) - end - end - end - {% end %} - end - - # The noisier sibling to `#let`. - # Defines an expression by giving it a name. - # The name can be used in examples to retrieve the value (basically a method). - # This can be used to define a value once and reuse it in multiple examples. - # - # This macro expects a name and a block. - # The name can be a symbol or a literal - same as `Object#getter`. - # The block should return the value. - # - # For instance: - # ``` - # let!(string) { "foobar" } - # - # it "isn't empty" do - # expect(string.empty?).to be_false - # end - # - # it "is six characters" do - # expect(string.size).to eq(6) - # end - # ``` - # - # The value is lazy-evaluated - - # meaning that it is only created when it is referenced. - # Unlike `#let`, the value is not cached and is recreated on each call. - # - # ``` - # let!(current_time) { Time.utc } - # - # it "lazy evaluates" do - # now = current_time - # sleep 5 - # expect(current_time).to_not eq(now) - # end - # ``` - macro let!(name) - def {{name.id}} - {{yield}} - end - end - - # Creates a hook that will run prior to any example in the group. - # The block of code provided to this macro is used for the hook. - # The hook is executed only once. - # If the hook raises an exception, - # the current example will be skipped and marked as an error. - # - # NOTE: Inside a `#sample` block, the hook is run once, not once per iteration. - # - # This can be useful to initialize something before testing: - # ``` - # before_all { Thing.start } # 1 - # - # it "does something" do - # # 2 - # end - # ``` - # - # The hook cannot use values and methods in the group like examples can. - # This is because the hook is not associated with one example, but many. - # ``` - # let(array) { [1, 2, 3] } - # before_all { array << 4 } # *ERROR!* - # ``` - # - # If multiple `#before_all` blocks are specified, - # then they are run in the order they were defined. - # ``` - # before_all { Thing.first } # 1 - # before_all { Thing.second } # 2 - # ``` - # - # With nested groups, the outer blocks will run first. - # ``` - # describe Something do - # before_all { Something.start } # 1 - # - # describe "#foo" do - # before_all { Something.foo } # 2 - # - # it "does a cool thing" do - # # 3 - # end - # end - # end - # ``` - # - # NOTE: Pre-conditions should not be checked in a `#before_all` or related block. - # Errors that occur in a `#before_all` block will halt testing and abort with an error. - # Use `#pre_condition` instead for pre-test checks. - # - # See also: `#before_each`, `#after_all`, `#after_each`, and `#around_each`. - macro before_all(&block) - ::Spectator::DSL::Builder.add_before_all_hook {{block}} - end - - # Creates a hook that will run prior to every example in the group. - # The block of code provided to this macro is used for the hook. - # The hook is executed once per example in the group (and sub-groups). - # If the hook raises an exception, - # the current example will be skipped and marked as an error. - # - # NOTE: Inside a `#sample` block, the hook is run before every example of every iteration. - # - # This can be useful for setting up environments for tests: - # ``` - # before_each { Thing.start } # 1 - # - # it "does something" do - # # 2 - # end - # ``` - # - # The hook can use values and methods in the group like examples can. - # It is called in the same scope as the example code. - # ``` - # let(array) { [1, 2, 3] } - # before_each { array << 4 } - # ``` - # - # If multiple `#before_each` blocks are specified, - # then they are run in the order they were defined. - # ``` - # before_each { Thing.first } # 1 - # before_each { Thing.second } # 2 - # ``` - # - # With nested groups, the outer blocks will run first. - # ``` - # describe Something do - # before_each { Something.start } # 1 - # - # describe "#foo" do - # before_each { Something.foo } # 2 - # - # it "does a cool thing" do - # # 3 - # end - # end - # end - # ``` - # - # NOTE: Pre-conditions should not be checked in a `#before_each` or related block. - # Errors that occur in a `#before_each` block will halt testing and abort with an error. - # Use `#pre_condition` instead for pre-test checks. - # - # See also: `#before_all`, `#after_all`, `#after_each`, and `#around_each`. - macro before_each - # Before each hook. - # Defined as a method so that it can access the same scope as the example code. - def %hook : Nil - {{yield}} - end - - ::Spectator::DSL::Builder.add_before_each_hook do - # Get the wrapper instance and cast to current group type. - example = ::Spectator::Internals::Harness.current.example - instance = example.instance.as({{@type.id}}) - instance.%hook - end - end - - # Creates a hook that will run following all examples in the group. - # The block of code provided to this macro is used for the hook. - # The hook is executed only once. - # Even if an example fails or raises an error, the hook will run. - # - # NOTE: Inside a `#sample` block, the hook is run once, not once per iteration. - # - # This can be useful to cleanup after testing: - # ``` - # after_all { Thing.stop } # 2 - # - # it "does something" do - # # 1 - # end - # ``` - # - # The hook cannot use values and methods in the group like examples can. - # This is because the hook is not associated with one example, but many. - # ``` - # let(array) { [1, 2, 3] } - # after_all { array << 4 } # *ERROR!* - # ``` - # - # If multiple `#after_all` blocks are specified, - # then they are run in the order they were defined. - # ``` - # after_all { Thing.first } # 1 - # after_all { Thing.second } # 2 - # ``` - # - # With nested groups, the inner blocks will run first. - # ``` - # describe Something do - # after_all { Something.cleanup } # 3 - # - # describe "#foo" do - # after_all { Something.stop } # 2 - # - # it "does a cool thing" do - # # 1 - # end - # end - # end - # ``` - # - # NOTE: Post-conditions should not be checked in an `#after_all` or related block. - # Errors that occur in an `#after_all` block will halt testing and abort with an error. - # Use `#post_condition` instead for post-test checks. - # - # See also: `#before_all`, `#before_each`, `#after_each`, and `#around_each`. - macro after_all(&block) - ::Spectator::DSL::Builder.add_after_all_hook {{block}} - end - - # Creates a hook that will run following every example in the group. - # The block of code provided to this macro is used for the hook. - # The hook is executed once per example in the group (and sub-groups). - # Even if an example fails or raises an error, the hook will run. - # - # NOTE: Inside a `#sample` block, the hook is run after every example of every iteration. - # - # This can be useful for cleaning up environments after tests: - # ``` - # after_each { Thing.stop } # 2 - # - # it "does something" do - # # 1 - # end - # ``` - # - # The hook can use values and methods in the group like examples can. - # It is called in the same scope as the example code. - # ``` - # let(array) { [1, 2, 3] } - # after_each { array << 4 } - # ``` - # - # If multiple `#after_each` blocks are specified, - # then they are run in the order they were defined. - # ``` - # after_each { Thing.first } # 1 - # after_each { Thing.second } # 2 - # ``` - # - # With nested groups, the inner blocks will run first. - # ``` - # describe Something do - # after_each { Something.cleanup } # 3 - # - # describe "#foo" do - # after_each { Something.stop } # 2 - # - # it "does a cool thing" do - # # 1 - # end - # end - # end - # ``` - # - # NOTE: Post-conditions should not be checked in an `#after_each` or related block. - # Errors that occur in an `#after_each` block will halt testing and abort with an error. - # Use `#post_condition` instead for post-test checks. - # - # See also: `#before_all`, `#before_each`, `#after_all`, and `#around_each`. - macro after_each - # After each hook. - # Defined as a method so that it can access the same scope as the example code. - def %hook : Nil - {{yield}} - end - - ::Spectator::DSL::Builder.add_after_each_hook do - # Get the wrapper instance and cast to current group type. - example = ::Spectator::Internals::Harness.current.example - instance = example.instance.as({{@type.id}}) - instance.%hook - end - end - - # Creates a hook that will run for every example in the group. - # This can be used as an alternative to `#before_each` and `#after_each`. - # The block of code provided to this macro is used for the hook. - # The hook is executed once per example in the group (and sub-groups). - # If the hook raises an exception, - # the current example will be skipped and marked as an error. - # - # Sometimes the test code must run in a block: - # ``` - # around_each do |proc| - # Thing.run do - # proc.call - # end - # end - # - # it "does something" do - # # ... - # end - # ``` - # - # The block argument is provided a `Proc`. - # To run the example, that proc must be called. - # Make sure to call it! - # ``` - # around_each do |proc| - # Thing.run - # # Missing proc.call - # end - # - # it "does something" do - # # Whoops! This is never run. - # end - # ``` - # - # The hook can use values and methods in the group like examples can. - # It is called in the same scope as the example code. - # ``` - # let(array) { [1, 2, 3] } - # around_each do |proc| - # array << 4 - # proc.call - # array.pop - # end - # ``` - # - # If multiple `#around_each` blocks are specified, - # then they are run in the order they were defined. - # ``` - # around_each { |p| p.call } # 1 - # around_each { |p| p.call } # 2 - # ``` - # - # With nested groups, the outer blocks will run first. - # But the return from calling the proc will be in the oposite order. - # ``` - # describe Something do - # around_each do |proc| - # Thing.foo # 1 - # proc.call - # Thing.bar # 5 - # end - # - # describe "#foo" do - # around_each do |proc| - # Thing.foo # 2 - # proc.call - # Thing.bar # 4 - # end - # - # it "does a cool thing" do - # # 3 - # end - # end - # end - # ``` - # - # NOTE: Pre- and post-conditions should not be checked in an `#around_each` or similar block. - # Errors that occur in an `#around_each` block will halt testing and abort with an error. - # Use `#pre_condition` and `#post_condition` instead for pre- and post-test checks. - # - # See also: `#before_all`, `#before_each`, `#after_all`, and `#after_each`. - macro around_each(&block) - # Around each hook. - # Defined as a method so that it can access the same scope as the example code. - def %hook({{block.args.splat}}) : Nil - {{yield}} - end - - ::Spectator::DSL::Builder.add_around_each_hook do |proc| - # Get the wrapper instance and cast to current group type. - example = ::Spectator::Internals::Harness.current.example - instance = example.instance.as({{@type.id}}) - instance.%hook(proc) - end - end - - # Defines a block of code to run prior to every example in the group. - # The condition is executed once per example in the group (and sub-groups). - # If the condition fails, then the example fails. - # - # NOTE: Inside a `#sample` block, the condition is checked before every example of every iteration. - # - # This can be useful for ensuring the state before a test. - # ``` - # pre_condition { expect(array).to_not be_nil } - # - # it "is the correct length" do - # expect(array.size).to eq(3) - # end - # ``` - # - # The condition can use values and methods in the group like examples can. - # It is called in the same scope as the example code. - # ``` - # let(array) { [1, 2, 3] } - # pre_condition { expect(array.size).to eq(3) } - # ``` - # - # If multiple `#pre_condition` blocks are specified, - # then they are run in the order they were defined. - # ``` - # pre_condition { expect(array).to_not be_nil } # 1 - # pre_condition { expect(array.size).to eq(3) } # 2 - # ``` - # - # With nested groups, the outer blocks will run first. - # ``` - # describe Something do - # pre_condition { is_expected.to_not be_nil } # 1 - # - # describe "#foo" do - # pre_condition { expect(subject.foo).to_not be_nil } # 2 - # - # it "does a cool thing" do - # # 3 - # end - # end - # end - # ``` - # - # See also: `#post_condition`. - macro pre_condition - # Pre-condition check. - def %condition : Nil - {{yield}} - end - - ::Spectator::DSL::Builder.add_pre_condition do - example = ::Spectator::Internals::Harness.current.example - instance = example.instance.as({{@type.id}}) - instance.%condition - end - end - - # Defines a block of code to run after every example in the group. - # The condition is executed once per example in the group (and sub-groups). - # If the condition fails, then the example fails. - # - # NOTE: Inside a `#sample` block, the condition is checked before every example of every iteration. - # - # This can be useful for ensuring the state after a test. - # ``` - # # The variable x shouldn't be modified if an error is raised. - # post_condition { expect(x).to eq(original_x) } - # - # it "raises on divide by zero" do - # expect_raises { x /= 0 } - # end - # ``` - # - # The condition can use values and methods in the group like examples can. - # It is called in the same scope as the example code. - # ``` - # let(array) { [1, 2, 3] } - # post_condition { expect(array.size).to eq(3) } - # ``` - # - # If multiple `#post_condition` blocks are specified, - # then they are run in the order they were defined. - # ``` - # post_condition { expect(array).to_not be_nil } # 1 - # post_condition { expect(array.size).to eq(3) } # 2 - # ``` - # - # With nested groups, the inner blocks will run first. - # ``` - # describe Something do - # post_condition { is_expected.to_not be_nil } # 3 - # - # describe "#foo" do - # post_condition { expect(subject.foo).to_not be_nil } # 2 - # - # it "does a cool thing" do - # # 1 - # end - # end - # end - # ``` - # - # See also: `#pre_condition`. - macro post_condition - # Post-condition check. - def %condition : Nil - {{yield}} - end - - ::Spectator::DSL::Builder.add_post_condition do - example = ::Spectator::Internals::Harness.current.example - instance = example.instance.as({{@type.id}}) - instance.%condition - end - end - - macro mock(type) - {% real_type = type.resolve %} - {% if real_type < ::Reference %} - class ::{{real_type.name.id}} - include ::Spectator::Mock - end - {% elsif real_type < ::Value %} - struct ::{{real_type.name.id}} - include ::Spectator::Mock - end - {% else %} - module ::{{real_type.name.id}} - include ::Spectator::Mock - end - {% end %} - {% debug %} - end - - macro double(name, &block) - {% if block.is_a?(Nop) %} - # Create an instance of the double. - ::Spectator::Internals::Harness.current.double({{name.id.symbolize}}).as(Double{{name.id}}) - {% else %} - # Define a double. - struct Double{{name.id}} < ::Spectator::Double # TODO: Use fresh variable %double - #include {{@type.id}} # Include parent type to allow contextual methods in the double. - - {{block.body}} - end - ::Spectator::DSL::Builder.add_double({{name.id.symbolize}}, Double{{name.id}}) - {% end %} - {% debug %} - end - - # Creates an example, or a test case. - # The *what* argument describes "what" is being tested or asserted. - # The block contains the code to run the test. - # One or more expectations should be in the block. - # - # ``` - # it "can do math" do - # expect(1 + 2).to eq(3) - # end - # ``` - # - # See `ExampleDSL` and `MatcherDSL` for additional macros and methods - # that can be used in example code blocks. - # - # A short-hand, one-liner syntax can also be used. - # Typically, this is combined with `#subject`. - # For instance: - # ``` - # subject { 1 + 2 } - # it is_expected.to eq(3) - # ``` - macro it(what, _source_file = __FILE__, _source_line = __LINE__, &block) - # Create the wrapper class for the test code. - {% if block.is_a?(Nop) %} - {% if what.is_a?(Call) %} - _spectator_test(Test%example, %run) do - {{what}} - end - {% else %} - {% raise "Unrecognized syntax: `it #{what}`" %} - {% end %} - {% else %} - _spectator_test(Test%example, %run) {{block}} - {% end %} - - # Create a class derived from `RunnableExample` to run the test code. - _spectator_example(Example%example, Test%example, ::Spectator::RunnableExample, {{what}}) do - # Source where the example originated from. - def source : ::Spectator::Source - ::Spectator::Source.new({{_source_file}}, {{_source_line}}) - end - - # Implement abstract method to run the wrapped example block. - protected def run_instance - @instance.%run - end - end - - # Add the example to the current group. - ::Spectator::DSL::Builder.add_example(Example%example) - end - - # Creates an example, or a test case. - # The block contains the code to run the test. - # One or more expectations should be in the block. - # - # ``` - # it { expect(1 + 2).to eq(3) } - # ``` - # - # See `ExampleDSL` and `MatcherDSL` for additional macros and methods - # that can be used in example code blocks. - # - # A short-hand, one-liner syntax can also be used. - # Typically, this is combined with `#subject`. - # For instance: - # ``` - # subject { 1 + 2 } - # it is_expected.to eq(3) - # ``` - macro it(&block) - it({{block.body.stringify}}) {{block}} - end - - # An alternative way to write an example. - # This is identical to `#it`. - macro specify(what, &block) - it({{what}}) {{block}} - end - - # An alternative way to write an example. - # This is identical to `#it`, - # except that it doesn't take a "what" argument. - macro specify(&block) - it({{block.body.stringify}}) {{block}} - end - - # Creates an example, or a test case, that does not run. - # This can be used to prototype functionality that isn't ready. - # The *what* argument describes "what" is being tested or asserted. - # The block contains the code to run the test. - # One or more expectations should be in the block. - # - # ``` - # pending "something that isn't implemented yet" do - # # ... - # end - # ``` - # - # See `ExampleDSL` and `MatcherDSL` for additional macros and methods - # that can be used in example code blocks. - # - # NOTE: Crystal appears to "lazily" compile code. - # Any code that isn't referenced seems to be ignored. - # Sometimes syntax, type, and other compile-time errors - # can occur in unreferenced code and won't be caught by the compiler. - # By creating a `#pending` test, the code will be referenced. - # Thus, forcing the compiler to at least process the code, even if it isn't run. - macro pending(what, _source_file = __FILE__, _source_line = __LINE__, &block) - # Create the wrapper class for the test code. - _spectator_test(Test%example, %run) {{block}} - - # Create a class derived from `PendingExample` to skip the test code. - _spectator_example(Example%example, Test%example, ::Spectator::PendingExample, {{what}}) do - # Source where the example originated from. - def source : ::Spectator::Source - ::Spectator::Source.new({{_source_file}}, {{_source_line}}) - end - end - - # Add the example to the current group. - ::Spectator::DSL::Builder.add_example(Example%example) - end - - # Creates an example, or a test case, that does not run. - # This can be used to prototype functionality that isn't ready. - # The *what* argument describes "what" is being tested or asserted. - # The block contains the code to run the test. - # One or more expectations should be in the block. - # - # ``` - # pending do - # # Something that isn't implemented yet. - # end - # ``` - macro pending(&block) - peding({{block.body.stringify}}) {{block}} - end - - # Same as `#pending`. - # Included for compatibility with RSpec. - macro skip(what, &block) - pending({{what}}) {{block}} - end - - # Same as `#pending`. - # Included for compatibility with RSpec. - macro skip(&block) - pending({{block.body.stringify}}) {{block}} - end - - # Same as `#pending`. - # Included for compatibility with RSpec. - macro xit(what, &block) - pending({{what}}) {{block}} - end - - # Same as `#pending`. - # Included for compatibility with RSpec. - macro xit(&block) - pending({{block.body.stringify}}) {{block}} - end - - # Creates a wrapper class for test code. - # The class serves multiple purposes, mostly dealing with scope. - # 1. Include the parent modules as mix-ins. - # 2. Enable DSL specific to examples. - # 3. Isolate methods in `Example` from the test code. - # - # Since the names are generated, and macros can't return values, - # the names for everything must be passed in as arguments. - # The *class_name* argument is the name of the class to define. - # The *run_method_name* argument is the name of the method in the wrapper class - # that will actually run the test code. - # The block passed to this macro is the actual test code. - private macro _spectator_test(class_name, run_method_name) - # Wrapper class for isolating the test code. - class {{class_name.id}} - # Mix in methods and macros specifically for example DSL. - include ::Spectator::DSL::ExampleDSL - - # Include the parent (example group) context. - include {{@type.id}} - - # Initializer that accepts sample values. - # The sample values are passed upward to the group modules. - # Any module that adds sample values can pull their values from this instance. - def initialize(sample_values : ::Spectator::Internals::SampleValues) - super - end - - # Generated method for actually running the test code. - def {{run_method_name.id}} - {{yield}} - end - end - end - - # Creates an example class. - # Since the names are generated, and macros can't return values, - # the names for everything must be passed in as arguments. - # The *example_class_name* argument is the name of the class to define. - # The *test_class_name* argument is the name of the wrapper class to reference. - # This must be the same as `class_name` for `#_spectator_example_wrapper`. - # The *base_class* argument specifies which type of example class the new class should derive from. - # This should typically be `RunnableExample` or `PendingExample`. - # The *what* argument is the description passed to the `#it` or `#pending` block. - # And lastly, the block specified is additional content to put in the class. - # For instance, to define a method in the class, do it in the block. - # ``` - # _spectator_example(Example123, Test123, RunnableExample, "does something") do - # def something - # # This method is defined in the Example123 class. - # end - # end - # ``` - private macro _spectator_example(example_class_name, test_class_name, base_class, what, &block) - # Example class containing meta information and instructions for running the test. - class {{example_class_name.id}} < {{base_class.id}} - # Stores the group the example belongs to - # and sample values specific to this instance of the test. - # This method's signature must match the one used in `ExampleFactory#build`. - def initialize(group : ::Spectator::ExampleGroup, sample_values : ::Spectator::Internals::SampleValues) - super - @instance = {{test_class_name.id}}.new(sample_values) - end - - # Retrieves the underlying, wrapped test code. - getter instance - - # Indicates whether the example references a method. - def symbolic? : Bool - {{what.is_a?(StringLiteral) && what.starts_with?('#') ? true : false}} - end - - # Add the block's content. - {{block.body}} - - # Description for the test. - def what : String | Symbol - {{what.is_a?(StringLiteral) ? what : what.stringify}} - end - end - end - end -end diff --git a/src/spectator/dsl/values.cr b/src/spectator/dsl/values.cr new file mode 100644 index 0000000..194e853 --- /dev/null +++ b/src/spectator/dsl/values.cr @@ -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 diff --git a/src/spectator/dummy_example.cr b/src/spectator/dummy_example.cr deleted file mode 100644 index 62e11c9..0000000 --- a/src/spectator/dummy_example.cr +++ /dev/null @@ -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 diff --git a/src/spectator/example.cr b/src/spectator/example.cr index dddd6ff..8399a3b 100644 --- a/src/spectator/example.cr +++ b/src/spectator/example.cr @@ -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, diff --git a/src/spectator/example_component.cr b/src/spectator/example_component.cr index 6ac628f..9260f95 100644 --- a/src/spectator/example_component.cr +++ b/src/spectator/example_component.cr @@ -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 diff --git a/src/spectator/example_conditions.cr b/src/spectator/example_conditions.cr index d1fc95a..0fbd03d 100644 --- a/src/spectator/example_conditions.cr +++ b/src/spectator/example_conditions.cr @@ -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 diff --git a/src/spectator/example_group.cr b/src/spectator/example_group.cr index 0470a0e..ae8a5a8 100644 --- a/src/spectator/example_group.cr +++ b/src/spectator/example_group.cr @@ -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 diff --git a/src/spectator/example_hooks.cr b/src/spectator/example_hooks.cr index db6eef9..d345bf5 100644 --- a/src/spectator/example_hooks.cr +++ b/src/spectator/example_hooks.cr @@ -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 diff --git a/src/spectator/expectations/expectation_partial.cr b/src/spectator/expectations/expectation_partial.cr index 1cc8831..ed2e33c 100644 --- a/src/spectator/expectations/expectation_partial.cr +++ b/src/spectator/expectations/expectation_partial.cr @@ -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 diff --git a/src/spectator/formatting/document_formatter.cr b/src/spectator/formatting/document_formatter.cr index 45c0a3b..c100e55 100644 --- a/src/spectator/formatting/document_formatter.cr +++ b/src/spectator/formatting/document_formatter.cr @@ -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 diff --git a/src/spectator/internals/harness.cr b/src/spectator/harness.cr similarity index 92% rename from src/spectator/internals/harness.cr rename to src/spectator/harness.cr index 5207b37..ebbf5db 100644 --- a/src/spectator/internals/harness.cr +++ b/src/spectator/harness.cr @@ -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 diff --git a/src/spectator/includes.cr b/src/spectator/includes.cr index 65e97c8..d103cfa 100644 --- a/src/spectator/includes.cr +++ b/src/spectator/includes.cr @@ -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" diff --git a/src/spectator/internals.cr b/src/spectator/internals.cr deleted file mode 100644 index c29ac5d..0000000 --- a/src/spectator/internals.cr +++ /dev/null @@ -1,7 +0,0 @@ -require "./internals/*" - -module Spectator - # Utilities and black magic (hacks) employed by the testing framework. - module Internals - end -end diff --git a/src/spectator/nested_example_group.cr b/src/spectator/nested_example_group.cr index 1b6d4d6..c21345e 100644 --- a/src/spectator/nested_example_group.cr +++ b/src/spectator/nested_example_group.cr @@ -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 diff --git a/src/spectator/pending_example.cr b/src/spectator/pending_example.cr index 897b134..743b778 100644 --- a/src/spectator/pending_example.cr +++ b/src/spectator/pending_example.cr @@ -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) diff --git a/src/spectator/root_example_group.cr b/src/spectator/root_example_group.cr index 65836c3..16380e0 100644 --- a/src/spectator/root_example_group.cr +++ b/src/spectator/root_example_group.cr @@ -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. diff --git a/src/spectator/runnable_example.cr b/src/spectator/runnable_example.cr index 95b1c41..d981689 100644 --- a/src/spectator/runnable_example.cr +++ b/src/spectator/runnable_example.cr @@ -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 diff --git a/src/spectator/runner.cr b/src/spectator/runner.cr index 306f837..8bde725 100644 --- a/src/spectator/runner.cr +++ b/src/spectator/runner.cr @@ -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 diff --git a/src/spectator/dsl/builder.cr b/src/spectator/spec_builder.cr similarity index 56% rename from src/spectator/dsl/builder.cr rename to src/spectator/spec_builder.cr index beb56ff..07ddf8c 100644 --- a/src/spectator/dsl/builder.cr +++ b/src/spectator/spec_builder.cr @@ -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 diff --git a/src/spectator/spec_builder/example_builder.cr b/src/spectator/spec_builder/example_builder.cr new file mode 100644 index 0000000..44f23fe --- /dev/null +++ b/src/spectator/spec_builder/example_builder.cr @@ -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 diff --git a/src/spectator/spec_builder/example_group_builder.cr b/src/spectator/spec_builder/example_group_builder.cr new file mode 100644 index 0000000..f9ed5bf --- /dev/null +++ b/src/spectator/spec_builder/example_group_builder.cr @@ -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 diff --git a/src/spectator/spec_builder/example_group_stack.cr b/src/spectator/spec_builder/example_group_stack.cr new file mode 100644 index 0000000..50385bb --- /dev/null +++ b/src/spectator/spec_builder/example_group_stack.cr @@ -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 diff --git a/src/spectator/spec_builder/nested_example_group_builder.cr b/src/spectator/spec_builder/nested_example_group_builder.cr new file mode 100644 index 0000000..dc08cff --- /dev/null +++ b/src/spectator/spec_builder/nested_example_group_builder.cr @@ -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 diff --git a/src/spectator/spec_builder/pending_example_builder.cr b/src/spectator/spec_builder/pending_example_builder.cr new file mode 100644 index 0000000..731b9ab --- /dev/null +++ b/src/spectator/spec_builder/pending_example_builder.cr @@ -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 diff --git a/src/spectator/spec_builder/root_example_group_builder.cr b/src/spectator/spec_builder/root_example_group_builder.cr new file mode 100644 index 0000000..02f4914 --- /dev/null +++ b/src/spectator/spec_builder/root_example_group_builder.cr @@ -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 diff --git a/src/spectator/spec_builder/runnable_example_builder.cr b/src/spectator/spec_builder/runnable_example_builder.cr new file mode 100644 index 0000000..8c22a15 --- /dev/null +++ b/src/spectator/spec_builder/runnable_example_builder.cr @@ -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 diff --git a/src/spectator/spec_builder/sample_example_group_builder.cr b/src/spectator/spec_builder/sample_example_group_builder.cr new file mode 100644 index 0000000..eacd596 --- /dev/null +++ b/src/spectator/spec_builder/sample_example_group_builder.cr @@ -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 diff --git a/src/spectator/test_context.cr b/src/spectator/test_context.cr new file mode 100644 index 0000000..8d16fee --- /dev/null +++ b/src/spectator/test_context.cr @@ -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 diff --git a/src/spectator/internals/sample_values.cr b/src/spectator/test_values.cr similarity index 89% rename from src/spectator/internals/sample_values.cr rename to src/spectator/test_values.cr index 8cab714..d07da71 100644 --- a/src/spectator/internals/sample_values.cr +++ b/src/spectator/test_values.cr @@ -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 diff --git a/src/spectator/test_wrapper.cr b/src/spectator/test_wrapper.cr new file mode 100644 index 0000000..756f287 --- /dev/null +++ b/src/spectator/test_wrapper.cr @@ -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 diff --git a/src/spectator/internals/typed_value_wrapper.cr b/src/spectator/typed_value_wrapper.cr similarity index 94% rename from src/spectator/internals/typed_value_wrapper.cr rename to src/spectator/typed_value_wrapper.cr index 39e06cf..8131d82 100644 --- a/src/spectator/internals/typed_value_wrapper.cr +++ b/src/spectator/typed_value_wrapper.cr @@ -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` diff --git a/src/spectator/internals/value_wrapper.cr b/src/spectator/value_wrapper.cr similarity index 69% rename from src/spectator/internals/value_wrapper.cr rename to src/spectator/value_wrapper.cr index 4c0183e..170951d 100644 --- a/src/spectator/internals/value_wrapper.cr +++ b/src/spectator/value_wrapper.cr @@ -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 diff --git a/src/spectator_test.cr b/src/spectator_test.cr new file mode 100644 index 0000000..364d79e --- /dev/null +++ b/src/spectator_test.cr @@ -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