summaryrefslogtreecommitdiff
path: root/spec/mspec/spec/runner
diff options
context:
space:
mode:
Diffstat (limited to 'spec/mspec/spec/runner')
-rw-r--r--spec/mspec/spec/runner/actions/filter_spec.rb84
-rw-r--r--spec/mspec/spec/runner/actions/tag_spec.rb315
-rw-r--r--spec/mspec/spec/runner/actions/taglist_spec.rb152
-rw-r--r--spec/mspec/spec/runner/actions/tagpurge_spec.rb154
-rw-r--r--spec/mspec/spec/runner/actions/tally_spec.rb352
-rw-r--r--spec/mspec/spec/runner/actions/timer_spec.rb44
-rw-r--r--spec/mspec/spec/runner/context_spec.rb1041
-rw-r--r--spec/mspec/spec/runner/example_spec.rb117
-rw-r--r--spec/mspec/spec/runner/exception_spec.rb146
-rw-r--r--spec/mspec/spec/runner/filters/a.yaml4
-rw-r--r--spec/mspec/spec/runner/filters/b.yaml11
-rw-r--r--spec/mspec/spec/runner/filters/match_spec.rb34
-rw-r--r--spec/mspec/spec/runner/filters/profile_spec.rb117
-rw-r--r--spec/mspec/spec/runner/filters/regexp_spec.rb13
-rw-r--r--spec/mspec/spec/runner/filters/tag_spec.rb92
-rw-r--r--spec/mspec/spec/runner/formatters/describe_spec.rb67
-rw-r--r--spec/mspec/spec/runner/formatters/dotted_spec.rb285
-rw-r--r--spec/mspec/spec/runner/formatters/file_spec.rb84
-rw-r--r--spec/mspec/spec/runner/formatters/html_spec.rb217
-rw-r--r--spec/mspec/spec/runner/formatters/junit_spec.rb147
-rw-r--r--spec/mspec/spec/runner/formatters/method_spec.rb178
-rw-r--r--spec/mspec/spec/runner/formatters/multi_spec.rb68
-rw-r--r--spec/mspec/spec/runner/formatters/specdoc_spec.rb106
-rw-r--r--spec/mspec/spec/runner/formatters/spinner_spec.rb83
-rw-r--r--spec/mspec/spec/runner/formatters/summary_spec.rb26
-rw-r--r--spec/mspec/spec/runner/formatters/unit_spec.rb74
-rw-r--r--spec/mspec/spec/runner/formatters/yaml_spec.rb125
-rw-r--r--spec/mspec/spec/runner/mspec_spec.rb595
-rw-r--r--spec/mspec/spec/runner/shared_spec.rb88
-rw-r--r--spec/mspec/spec/runner/tag_spec.rb123
-rw-r--r--spec/mspec/spec/runner/tags.txt4
31 files changed, 4946 insertions, 0 deletions
diff --git a/spec/mspec/spec/runner/actions/filter_spec.rb b/spec/mspec/spec/runner/actions/filter_spec.rb
new file mode 100644
index 0000000000..d185781757
--- /dev/null
+++ b/spec/mspec/spec/runner/actions/filter_spec.rb
@@ -0,0 +1,84 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/actions/filter'
+require 'mspec/runner/mspec'
+require 'mspec/runner/tag'
+
+describe ActionFilter do
+ it "creates a filter when not passed a description" do
+ MatchFilter.should_not_receive(:new)
+ ActionFilter.new(nil, nil)
+ end
+
+ it "creates a filter from a single description" do
+ MatchFilter.should_receive(:new).with(nil, "match me")
+ ActionFilter.new(nil, "match me")
+ end
+
+ it "creates a filter from an array of descriptions" do
+ MatchFilter.should_receive(:new).with(nil, "match me", "again")
+ ActionFilter.new(nil, ["match me", "again"])
+ end
+end
+
+describe ActionFilter, "#===" do
+ before :each do
+ MSpec.stub(:read_tags).and_return(["match"])
+ @action = ActionFilter.new(nil, ["catch", "if you"])
+ end
+
+ it "returns false if there are no filters" do
+ action = ActionFilter.new
+ action.===("anything").should == false
+ end
+
+ it "returns true if the argument matches any of the descriptions" do
+ @action.===("catch").should == true
+ @action.===("if you can").should == true
+ end
+
+ it "returns false if the argument does not match any of the descriptions" do
+ @action.===("patch me").should == false
+ @action.===("if I can").should == false
+ end
+end
+
+describe ActionFilter, "#load" do
+ before :each do
+ @tag = SpecTag.new "tag(comment):description"
+ end
+
+ it "creates a filter from a single tag" do
+ MSpec.should_receive(:read_tags).with(["tag"]).and_return([@tag])
+ MatchFilter.should_receive(:new).with(nil, "description")
+ ActionFilter.new("tag", nil).load
+ end
+
+ it "creates a filter from an array of tags" do
+ MSpec.should_receive(:read_tags).with(["tag", "key"]).and_return([@tag])
+ MatchFilter.should_receive(:new).with(nil, "description")
+ ActionFilter.new(["tag", "key"], nil).load
+ end
+
+ it "creates a filter from both tags and descriptions" do
+ MSpec.should_receive(:read_tags).and_return([@tag])
+ filter = ActionFilter.new("tag", ["match me", "again"])
+ MatchFilter.should_receive(:new).with(nil, "description")
+ filter.load
+ end
+end
+
+describe ActionFilter, "#register" do
+ it "registers itself with MSpec for the :load actions" do
+ filter = ActionFilter.new
+ MSpec.should_receive(:register).with(:load, filter)
+ filter.register
+ end
+end
+
+describe ActionFilter, "#unregister" do
+ it "unregisters itself with MSpec for the :load actions" do
+ filter = ActionFilter.new
+ MSpec.should_receive(:unregister).with(:load, filter)
+ filter.unregister
+ end
+end
diff --git a/spec/mspec/spec/runner/actions/tag_spec.rb b/spec/mspec/spec/runner/actions/tag_spec.rb
new file mode 100644
index 0000000000..92df362d02
--- /dev/null
+++ b/spec/mspec/spec/runner/actions/tag_spec.rb
@@ -0,0 +1,315 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/actions/tag'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+require 'mspec/runner/tag'
+
+describe TagAction, ".new" do
+ it "creates an MatchFilter with its tag and desc arguments" do
+ filter = double('action filter').as_null_object
+ MatchFilter.should_receive(:new).with(nil, "some", "thing").and_return(filter)
+ TagAction.new :add, :all, nil, nil, ["tag", "key"], ["some", "thing"]
+ end
+end
+
+describe TagAction, "#===" do
+ before :each do
+ MSpec.stub(:read_tags).and_return(["match"])
+ @action = TagAction.new :add, :fail, nil, nil, nil, ["catch", "if you"]
+ end
+
+ it "returns true if there are no filters" do
+ action = TagAction.new :add, :all, nil, nil
+ action.===("anything").should == true
+ end
+
+ it "returns true if the argument matches any of the descriptions" do
+ @action.===("catch").should == true
+ @action.===("if you can").should == true
+ end
+
+ it "returns false if the argument does not match any of the descriptions" do
+ @action.===("patch me").should == false
+ @action.===("if I can").should == false
+ end
+end
+
+describe TagAction, "#exception?" do
+ before :each do
+ @action = TagAction.new :add, :fail, nil, nil, nil, nil
+ end
+
+ it "returns false if no exception has been raised while evaluating an example" do
+ @action.exception?.should be_false
+ end
+
+ it "returns true if an exception was raised while evaluating an example" do
+ @action.exception ExceptionState.new nil, nil, Exception.new("failed")
+ @action.exception?.should be_true
+ end
+end
+
+describe TagAction, "#outcome?" do
+ before :each do
+ MSpec.stub(:read_tags).and_return([])
+ @exception = ExceptionState.new nil, nil, Exception.new("failed")
+ end
+
+ it "returns true if outcome is :fail and the spec fails" do
+ action = TagAction.new :add, :fail, nil, nil, nil, nil
+ action.exception @exception
+ action.outcome?.should == true
+ end
+
+ it "returns false if the outcome is :fail and the spec passes" do
+ action = TagAction.new :add, :fail, nil, nil, nil, nil
+ action.outcome?.should == false
+ end
+
+ it "returns true if the outcome is :pass and the spec passes" do
+ action = TagAction.new :del, :pass, nil, nil, nil, nil
+ action.outcome?.should == true
+ end
+
+ it "returns false if the outcome is :pass and the spec fails" do
+ action = TagAction.new :del, :pass, nil, nil, nil, nil
+ action.exception @exception
+ action.outcome?.should == false
+ end
+
+ it "returns true if the outcome is :all" do
+ action = TagAction.new :add, :all, nil, nil, nil, nil
+ action.exception @exception
+ action.outcome?.should == true
+ end
+end
+
+describe TagAction, "#before" do
+ it "resets the #exception? flag to false" do
+ action = TagAction.new :add, :fail, nil, nil, nil, nil
+ action.exception?.should be_false
+ action.exception ExceptionState.new(nil, nil, Exception.new("Fail!"))
+ action.exception?.should be_true
+ action.before(ExampleState.new(ContextState.new("describe"), "it"))
+ action.exception?.should be_false
+ end
+end
+
+describe TagAction, "#exception" do
+ it "sets the #exception? flag" do
+ action = TagAction.new :add, :fail, nil, nil, nil, nil
+ action.exception?.should be_false
+ action.exception ExceptionState.new(nil, nil, Exception.new("Fail!"))
+ action.exception?.should be_true
+ end
+end
+
+describe TagAction, "#after when action is :add" do
+ before :each do
+ MSpec.stub(:read_tags).and_return([])
+ context = ContextState.new "Catch#me"
+ @state = ExampleState.new context, "if you can"
+ @tag = SpecTag.new "tag(comment):Catch#me if you can"
+ SpecTag.stub(:new).and_return(@tag)
+ @exception = ExceptionState.new nil, nil, Exception.new("failed")
+ end
+
+ it "does not write a tag if the description does not match" do
+ MSpec.should_not_receive(:write_tag)
+ action = TagAction.new :add, :all, "tag", "comment", nil, "match"
+ action.after @state
+ end
+
+ it "does not write a tag if outcome is :fail and the spec passed" do
+ MSpec.should_not_receive(:write_tag)
+ action = TagAction.new :add, :fail, "tag", "comment", nil, "can"
+ action.after @state
+ end
+
+ it "writes a tag if the outcome is :fail and the spec failed" do
+ MSpec.should_receive(:write_tag).with(@tag)
+ action = TagAction.new :add, :fail, "tag", "comment", nil, "can"
+ action.exception @exception
+ action.after @state
+ end
+
+ it "does not write a tag if outcome is :pass and the spec failed" do
+ MSpec.should_not_receive(:write_tag)
+ action = TagAction.new :add, :pass, "tag", "comment", nil, "can"
+ action.exception @exception
+ action.after @state
+ end
+
+ it "writes a tag if the outcome is :pass and the spec passed" do
+ MSpec.should_receive(:write_tag).with(@tag)
+ action = TagAction.new :add, :pass, "tag", "comment", nil, "can"
+ action.after @state
+ end
+
+ it "writes a tag if the outcome is :all" do
+ MSpec.should_receive(:write_tag).with(@tag)
+ action = TagAction.new :add, :all, "tag", "comment", nil, "can"
+ action.after @state
+ end
+end
+
+describe TagAction, "#after when action is :del" do
+ before :each do
+ MSpec.stub(:read_tags).and_return([])
+ context = ContextState.new "Catch#me"
+ @state = ExampleState.new context, "if you can"
+ @tag = SpecTag.new "tag(comment):Catch#me if you can"
+ SpecTag.stub(:new).and_return(@tag)
+ @exception = ExceptionState.new nil, nil, Exception.new("failed")
+ end
+
+ it "does not delete a tag if the description does not match" do
+ MSpec.should_not_receive(:delete_tag)
+ action = TagAction.new :del, :all, "tag", "comment", nil, "match"
+ action.after @state
+ end
+
+ it "does not delete a tag if outcome is :fail and the spec passed" do
+ MSpec.should_not_receive(:delete_tag)
+ action = TagAction.new :del, :fail, "tag", "comment", nil, "can"
+ action.after @state
+ end
+
+ it "deletes a tag if the outcome is :fail and the spec failed" do
+ MSpec.should_receive(:delete_tag).with(@tag)
+ action = TagAction.new :del, :fail, "tag", "comment", nil, "can"
+ action.exception @exception
+ action.after @state
+ end
+
+ it "does not delete a tag if outcome is :pass and the spec failed" do
+ MSpec.should_not_receive(:delete_tag)
+ action = TagAction.new :del, :pass, "tag", "comment", nil, "can"
+ action.exception @exception
+ action.after @state
+ end
+
+ it "deletes a tag if the outcome is :pass and the spec passed" do
+ MSpec.should_receive(:delete_tag).with(@tag)
+ action = TagAction.new :del, :pass, "tag", "comment", nil, "can"
+ action.after @state
+ end
+
+ it "deletes a tag if the outcome is :all" do
+ MSpec.should_receive(:delete_tag).with(@tag)
+ action = TagAction.new :del, :all, "tag", "comment", nil, "can"
+ action.after @state
+ end
+end
+
+describe TagAction, "#finish" do
+ before :each do
+ $stdout = @out = IOStub.new
+ context = ContextState.new "Catch#me"
+ @state = ExampleState.new context, "if you can"
+ MSpec.stub(:write_tag).and_return(true)
+ MSpec.stub(:delete_tag).and_return(true)
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "reports no specs tagged if none where tagged" do
+ action = TagAction.new :add, :fail, "tag", "comment", nil, "can"
+ action.stub(:outcome?).and_return(false)
+ action.after @state
+ action.finish
+ @out.should == "\nTagAction: no specs were tagged with 'tag'\n"
+ end
+
+ it "reports no specs tagged if none where tagged" do
+ action = TagAction.new :del, :fail, "tag", "comment", nil, "can"
+ action.stub(:outcome?).and_return(false)
+ action.after @state
+ action.finish
+ @out.should == "\nTagAction: no tags 'tag' were deleted\n"
+ end
+
+ it "reports the spec descriptions that were tagged" do
+ action = TagAction.new :add, :fail, "tag", "comment", nil, "can"
+ action.stub(:outcome?).and_return(true)
+ action.after @state
+ action.finish
+ @out.should ==
+%[
+TagAction: specs tagged with 'tag':
+
+Catch#me if you can
+]
+ end
+
+ it "reports the spec descriptions for the tags that were deleted" do
+ action = TagAction.new :del, :fail, "tag", "comment", nil, "can"
+ action.stub(:outcome?).and_return(true)
+ action.after @state
+ action.finish
+ @out.should ==
+%[
+TagAction: tag 'tag' deleted for specs:
+
+Catch#me if you can
+]
+ end
+end
+
+describe TagAction, "#register" do
+ before :each do
+ MSpec.stub(:register)
+ MSpec.stub(:read_tags).and_return([])
+ @action = TagAction.new :add, :all, nil, nil, nil, nil
+ end
+
+ it "registers itself with MSpec for the :before event" do
+ MSpec.should_receive(:register).with(:before, @action)
+ @action.register
+ end
+
+ it "registers itself with MSpec for the :after event" do
+ MSpec.should_receive(:register).with(:after, @action)
+ @action.register
+ end
+
+ it "registers itself with MSpec for the :exception event" do
+ MSpec.should_receive(:register).with(:exception, @action)
+ @action.register
+ end
+
+ it "registers itself with MSpec for the :finish event" do
+ MSpec.should_receive(:register).with(:finish, @action)
+ @action.register
+ end
+end
+
+describe TagAction, "#unregister" do
+ before :each do
+ MSpec.stub(:unregister)
+ MSpec.stub(:read_tags).and_return([])
+ @action = TagAction.new :add, :all, nil, nil, nil, nil
+ end
+
+ it "unregisters itself with MSpec for the :before event" do
+ MSpec.should_receive(:unregister).with(:before, @action)
+ @action.unregister
+ end
+
+ it "unregisters itself with MSpec for the :after event" do
+ MSpec.should_receive(:unregister).with(:after, @action)
+ @action.unregister
+ end
+
+ it "unregisters itself with MSpec for the :exception event" do
+ MSpec.should_receive(:unregister).with(:exception, @action)
+ @action.unregister
+ end
+
+ it "unregisters itself with MSpec for the :finish event" do
+ MSpec.should_receive(:unregister).with(:finish, @action)
+ @action.unregister
+ end
+end
diff --git a/spec/mspec/spec/runner/actions/taglist_spec.rb b/spec/mspec/spec/runner/actions/taglist_spec.rb
new file mode 100644
index 0000000000..418c761c2d
--- /dev/null
+++ b/spec/mspec/spec/runner/actions/taglist_spec.rb
@@ -0,0 +1,152 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/actions/taglist'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+require 'mspec/runner/tag'
+
+describe TagListAction, "#include?" do
+ it "returns true" do
+ TagListAction.new.include?(:anything).should be_true
+ end
+end
+
+describe TagListAction, "#===" do
+ before :each do
+ tag = SpecTag.new "fails:description"
+ MSpec.stub(:read_tags).and_return([tag])
+ @filter = double("MatchFilter").as_null_object
+ MatchFilter.stub(:new).and_return(@filter)
+ @action = TagListAction.new
+ @action.load
+ end
+
+ it "returns true if filter === string returns true" do
+ @filter.should_receive(:===).with("str").and_return(true)
+ @action.===("str").should be_true
+ end
+
+ it "returns false if filter === string returns false" do
+ @filter.should_receive(:===).with("str").and_return(false)
+ @action.===("str").should be_false
+ end
+end
+
+describe TagListAction, "#start" do
+ before :each do
+ @stdout = $stdout
+ $stdout = IOStub.new
+ end
+
+ after :each do
+ $stdout = @stdout
+ end
+
+ it "prints a banner for specific tags" do
+ action = TagListAction.new ["fails", "unstable"]
+ action.start
+ $stdout.should == "\nListing specs tagged with 'fails', 'unstable'\n\n"
+ end
+
+ it "prints a banner for all tags" do
+ action = TagListAction.new
+ action.start
+ $stdout.should == "\nListing all tagged specs\n\n"
+ end
+end
+
+describe TagListAction, "#load" do
+ before :each do
+ @t1 = SpecTag.new "fails:I fail"
+ @t2 = SpecTag.new "unstable:I'm unstable"
+ end
+
+ it "creates a MatchFilter for matching tags" do
+ MSpec.should_receive(:read_tags).with(["fails"]).and_return([@t1])
+ MatchFilter.should_receive(:new).with(nil, "I fail")
+ TagListAction.new(["fails"]).load
+ end
+
+ it "creates a MatchFilter for all tags" do
+ MSpec.should_receive(:read_tags).and_return([@t1, @t2])
+ MatchFilter.should_receive(:new).with(nil, "I fail", "I'm unstable")
+ TagListAction.new.load
+ end
+
+ it "does not create a MatchFilter if there are no matching tags" do
+ MSpec.stub(:read_tags).and_return([])
+ MatchFilter.should_not_receive(:new)
+ TagListAction.new(["fails"]).load
+ end
+end
+
+describe TagListAction, "#after" do
+ before :each do
+ @stdout = $stdout
+ $stdout = IOStub.new
+
+ @state = double("ExampleState")
+ @state.stub(:description).and_return("str")
+
+ @action = TagListAction.new
+ end
+
+ after :each do
+ $stdout = @stdout
+ end
+
+ it "prints nothing if the filter does not match" do
+ @action.should_receive(:===).with("str").and_return(false)
+ @action.after(@state)
+ $stdout.should == ""
+ end
+
+ it "prints the example description if the filter matches" do
+ @action.should_receive(:===).with("str").and_return(true)
+ @action.after(@state)
+ $stdout.should == "str\n"
+ end
+end
+
+describe TagListAction, "#register" do
+ before :each do
+ MSpec.stub(:register)
+ @action = TagListAction.new
+ end
+
+ it "registers itself with MSpec for the :start event" do
+ MSpec.should_receive(:register).with(:start, @action)
+ @action.register
+ end
+
+ it "registers itself with MSpec for the :load event" do
+ MSpec.should_receive(:register).with(:load, @action)
+ @action.register
+ end
+
+ it "registers itself with MSpec for the :after event" do
+ MSpec.should_receive(:register).with(:after, @action)
+ @action.register
+ end
+end
+
+describe TagListAction, "#unregister" do
+ before :each do
+ MSpec.stub(:unregister)
+ @action = TagListAction.new
+ end
+
+ it "unregisters itself with MSpec for the :start event" do
+ MSpec.should_receive(:unregister).with(:start, @action)
+ @action.unregister
+ end
+
+ it "unregisters itself with MSpec for the :load event" do
+ MSpec.should_receive(:unregister).with(:load, @action)
+ @action.unregister
+ end
+
+ it "unregisters itself with MSpec for the :after event" do
+ MSpec.should_receive(:unregister).with(:after, @action)
+ @action.unregister
+ end
+end
diff --git a/spec/mspec/spec/runner/actions/tagpurge_spec.rb b/spec/mspec/spec/runner/actions/tagpurge_spec.rb
new file mode 100644
index 0000000000..27ad2a1470
--- /dev/null
+++ b/spec/mspec/spec/runner/actions/tagpurge_spec.rb
@@ -0,0 +1,154 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/actions/tagpurge'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+require 'mspec/runner/tag'
+
+describe TagPurgeAction, "#start" do
+ before :each do
+ @stdout = $stdout
+ $stdout = IOStub.new
+ end
+
+ after :each do
+ $stdout = @stdout
+ end
+
+ it "prints a banner" do
+ action = TagPurgeAction.new
+ action.start
+ $stdout.should == "\nRemoving tags not matching any specs\n\n"
+ end
+end
+
+describe TagPurgeAction, "#load" do
+ before :each do
+ @t1 = SpecTag.new "fails:I fail"
+ @t2 = SpecTag.new "unstable:I'm unstable"
+ end
+
+ it "creates a MatchFilter for all tags" do
+ MSpec.should_receive(:read_tags).and_return([@t1, @t2])
+ MatchFilter.should_receive(:new).with(nil, "I fail", "I'm unstable")
+ TagPurgeAction.new.load
+ end
+end
+
+describe TagPurgeAction, "#after" do
+ before :each do
+ @state = double("ExampleState")
+ @state.stub(:description).and_return("str")
+
+ @action = TagPurgeAction.new
+ end
+
+ it "does not save the description if the filter does not match" do
+ @action.should_receive(:===).with("str").and_return(false)
+ @action.after @state
+ @action.matching.should == []
+ end
+
+ it "saves the description if the filter matches" do
+ @action.should_receive(:===).with("str").and_return(true)
+ @action.after @state
+ @action.matching.should == ["str"]
+ end
+end
+
+describe TagPurgeAction, "#unload" do
+ before :each do
+ @stdout = $stdout
+ $stdout = IOStub.new
+
+ @t1 = SpecTag.new "fails:I fail"
+ @t2 = SpecTag.new "unstable:I'm unstable"
+ @t3 = SpecTag.new "fails:I'm unstable"
+
+ MSpec.stub(:read_tags).and_return([@t1, @t2, @t3])
+ MSpec.stub(:write_tags)
+
+ @state = double("ExampleState")
+ @state.stub(:description).and_return("I'm unstable")
+
+ @action = TagPurgeAction.new
+ @action.load
+ @action.after @state
+ end
+
+ after :each do
+ $stdout = @stdout
+ end
+
+ it "does not rewrite any tags if there were no tags for the specs" do
+ MSpec.should_receive(:read_tags).and_return([])
+ MSpec.should_receive(:delete_tags)
+ MSpec.should_not_receive(:write_tags)
+
+ @action.load
+ @action.after @state
+ @action.unload
+
+ $stdout.should == ""
+ end
+
+ it "rewrites tags that were matched" do
+ MSpec.should_receive(:write_tags).with([@t2, @t3])
+ @action.unload
+ end
+
+ it "prints tags that were not matched" do
+ @action.unload
+ $stdout.should == "I fail\n"
+ end
+end
+
+describe TagPurgeAction, "#unload" do
+ before :each do
+ @stdout = $stdout
+ $stdout = IOStub.new
+
+ MSpec.stub(:read_tags).and_return([])
+
+ @state = double("ExampleState")
+ @state.stub(:description).and_return("I'm unstable")
+
+ @action = TagPurgeAction.new
+ @action.load
+ @action.after @state
+ end
+
+ after :each do
+ $stdout = @stdout
+ end
+
+ it "deletes the tag file if no tags were found" do
+ MSpec.should_not_receive(:write_tags)
+ MSpec.should_receive(:delete_tags)
+ @action.unload
+ $stdout.should == ""
+ end
+end
+
+describe TagPurgeAction, "#register" do
+ before :each do
+ MSpec.stub(:register)
+ @action = TagPurgeAction.new
+ end
+
+ it "registers itself with MSpec for the :unload event" do
+ MSpec.should_receive(:register).with(:unload, @action)
+ @action.register
+ end
+end
+
+describe TagPurgeAction, "#unregister" do
+ before :each do
+ MSpec.stub(:unregister)
+ @action = TagPurgeAction.new
+ end
+
+ it "unregisters itself with MSpec for the :unload event" do
+ MSpec.should_receive(:unregister).with(:unload, @action)
+ @action.unregister
+ end
+end
diff --git a/spec/mspec/spec/runner/actions/tally_spec.rb b/spec/mspec/spec/runner/actions/tally_spec.rb
new file mode 100644
index 0000000000..be4635ffeb
--- /dev/null
+++ b/spec/mspec/spec/runner/actions/tally_spec.rb
@@ -0,0 +1,352 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/runner/actions/tally'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+
+describe Tally, "#files!" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "increments the count returned by #files" do
+ @tally.files! 3
+ @tally.files.should == 3
+ @tally.files!
+ @tally.files.should == 4
+ end
+end
+
+describe Tally, "#examples!" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "increments the count returned by #examples" do
+ @tally.examples! 2
+ @tally.examples.should == 2
+ @tally.examples! 2
+ @tally.examples.should == 4
+ end
+end
+
+describe Tally, "#expectations!" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "increments the count returned by #expectations" do
+ @tally.expectations!
+ @tally.expectations.should == 1
+ @tally.expectations! 3
+ @tally.expectations.should == 4
+ end
+end
+
+describe Tally, "#failures!" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "increments the count returned by #failures" do
+ @tally.failures! 1
+ @tally.failures.should == 1
+ @tally.failures!
+ @tally.failures.should == 2
+ end
+end
+
+describe Tally, "#errors!" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "increments the count returned by #errors" do
+ @tally.errors!
+ @tally.errors.should == 1
+ @tally.errors! 2
+ @tally.errors.should == 3
+ end
+end
+
+describe Tally, "#guards!" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "increments the count returned by #guards" do
+ @tally.guards!
+ @tally.guards.should == 1
+ @tally.guards! 2
+ @tally.guards.should == 3
+ end
+end
+
+describe Tally, "#file" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "returns a formatted string of the number of #files" do
+ @tally.file.should == "0 files"
+ @tally.files!
+ @tally.file.should == "1 file"
+ @tally.files!
+ @tally.file.should == "2 files"
+ end
+end
+
+describe Tally, "#example" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "returns a formatted string of the number of #examples" do
+ @tally.example.should == "0 examples"
+ @tally.examples!
+ @tally.example.should == "1 example"
+ @tally.examples!
+ @tally.example.should == "2 examples"
+ end
+end
+
+describe Tally, "#expectation" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "returns a formatted string of the number of #expectations" do
+ @tally.expectation.should == "0 expectations"
+ @tally.expectations!
+ @tally.expectation.should == "1 expectation"
+ @tally.expectations!
+ @tally.expectation.should == "2 expectations"
+ end
+end
+
+describe Tally, "#failure" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "returns a formatted string of the number of #failures" do
+ @tally.failure.should == "0 failures"
+ @tally.failures!
+ @tally.failure.should == "1 failure"
+ @tally.failures!
+ @tally.failure.should == "2 failures"
+ end
+end
+
+describe Tally, "#error" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "returns a formatted string of the number of #errors" do
+ @tally.error.should == "0 errors"
+ @tally.errors!
+ @tally.error.should == "1 error"
+ @tally.errors!
+ @tally.error.should == "2 errors"
+ end
+end
+
+describe Tally, "#guard" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ it "returns a formatted string of the number of #guards" do
+ @tally.guard.should == "0 guards"
+ @tally.guards!
+ @tally.guard.should == "1 guard"
+ @tally.guards!
+ @tally.guard.should == "2 guards"
+ end
+end
+
+describe Tally, "#format" do
+ before :each do
+ @tally = Tally.new
+ end
+
+ after :each do
+ MSpec.clear_modes
+ end
+
+ it "returns a formatted string of counts" do
+ @tally.files!
+ @tally.examples! 2
+ @tally.expectations! 4
+ @tally.errors!
+ @tally.tagged!
+ @tally.format.should == "1 file, 2 examples, 4 expectations, 0 failures, 1 error, 1 tagged"
+ end
+
+ it "includes guards if MSpec is in verify mode" do
+ MSpec.register_mode :verify
+ @tally.files!
+ @tally.examples! 2
+ @tally.expectations! 4
+ @tally.errors!
+ @tally.tagged!
+ @tally.guards!
+ @tally.format.should ==
+ "1 file, 2 examples, 4 expectations, 0 failures, 1 error, 1 tagged, 1 guard"
+ end
+
+ it "includes guards if MSpec is in report mode" do
+ MSpec.register_mode :report
+ @tally.files!
+ @tally.examples! 2
+ @tally.expectations! 4
+ @tally.errors!
+ @tally.tagged!
+ @tally.guards! 2
+ @tally.format.should ==
+ "1 file, 2 examples, 4 expectations, 0 failures, 1 error, 1 tagged, 2 guards"
+ end
+
+ it "includes guards if MSpec is in report_on mode" do
+ MSpec.register_mode :report_on
+ @tally.files!
+ @tally.examples! 2
+ @tally.expectations! 4
+ @tally.errors!
+ @tally.guards! 2
+ @tally.format.should ==
+ "1 file, 2 examples, 4 expectations, 0 failures, 1 error, 0 tagged, 2 guards"
+ end
+end
+
+describe TallyAction, "#counter" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "returns the Tally object" do
+ @tally.counter.should be_kind_of(Tally)
+ end
+end
+
+describe TallyAction, "#load" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "increments the count returned by Tally#files" do
+ @tally.load
+ @tally.counter.files.should == 1
+ end
+end
+
+describe TallyAction, "#expectation" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "increments the count returned by Tally#expectations" do
+ @tally.expectation @state
+ @tally.counter.expectations.should == 1
+ end
+end
+
+describe TallyAction, "#example" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "increments counts returned by Tally#examples" do
+ @tally.example @state, nil
+ @tally.counter.examples.should == 1
+ @tally.counter.expectations.should == 0
+ @tally.counter.failures.should == 0
+ @tally.counter.errors.should == 0
+ end
+end
+
+describe TallyAction, "#exception" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "increments counts returned by Tally#failures" do
+ exc = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("Failed!")
+ @tally.exception exc
+ @tally.counter.examples.should == 0
+ @tally.counter.expectations.should == 0
+ @tally.counter.failures.should == 1
+ @tally.counter.errors.should == 0
+ end
+end
+
+describe TallyAction, "#exception" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "increments counts returned by Tally#errors" do
+ exc = ExceptionState.new nil, nil, Exception.new("Error!")
+ @tally.exception exc
+ @tally.counter.examples.should == 0
+ @tally.counter.expectations.should == 0
+ @tally.counter.failures.should == 0
+ @tally.counter.errors.should == 1
+ end
+end
+
+describe TallyAction, "#format" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "returns a readable string of counts" do
+ @tally.load
+ @tally.example @state, nil
+ @tally.expectation @state
+ @tally.expectation @state
+ exc = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("Failed!")
+ @tally.exception exc
+ @tally.format.should == "1 file, 1 example, 2 expectations, 1 failure, 0 errors, 0 tagged"
+ end
+end
+
+describe TallyAction, "#register" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "registers itself with MSpec for appropriate actions" do
+ MSpec.should_receive(:register).with(:load, @tally)
+ MSpec.should_receive(:register).with(:exception, @tally)
+ MSpec.should_receive(:register).with(:example, @tally)
+ MSpec.should_receive(:register).with(:tagged, @tally)
+ MSpec.should_receive(:register).with(:expectation, @tally)
+ @tally.register
+ end
+end
+
+describe TallyAction, "#unregister" do
+ before :each do
+ @tally = TallyAction.new
+ @state = ExampleState.new("describe", "it")
+ end
+
+ it "unregisters itself with MSpec for appropriate actions" do
+ MSpec.should_receive(:unregister).with(:load, @tally)
+ MSpec.should_receive(:unregister).with(:exception, @tally)
+ MSpec.should_receive(:unregister).with(:example, @tally)
+ MSpec.should_receive(:unregister).with(:tagged, @tally)
+ MSpec.should_receive(:unregister).with(:expectation, @tally)
+ @tally.unregister
+ end
+end
diff --git a/spec/mspec/spec/runner/actions/timer_spec.rb b/spec/mspec/spec/runner/actions/timer_spec.rb
new file mode 100644
index 0000000000..417367d5a2
--- /dev/null
+++ b/spec/mspec/spec/runner/actions/timer_spec.rb
@@ -0,0 +1,44 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/actions/timer'
+require 'mspec/runner/mspec'
+require 'time'
+
+describe TimerAction do
+ before :each do
+ @timer = TimerAction.new
+ @start_time = Time.utc(2009, 3, 30, 14, 5, 19)
+ @stop_time = Time.utc(2009, 3, 30, 14, 5, 52)
+ end
+
+ it "responds to #start by recording the current time" do
+ Time.should_receive(:now)
+ @timer.start
+ end
+
+ it "responds to #finish by recording the current time" do
+ Time.should_receive(:now)
+ @timer.finish
+ end
+
+ it "responds to #elapsed by returning the difference between stop and start" do
+ Time.stub(:now).and_return(@start_time)
+ @timer.start
+ Time.stub(:now).and_return(@stop_time)
+ @timer.finish
+ @timer.elapsed.should == 33
+ end
+
+ it "responds to #format by returning a readable string of elapsed time" do
+ Time.stub(:now).and_return(@start_time)
+ @timer.start
+ Time.stub(:now).and_return(@stop_time)
+ @timer.finish
+ @timer.format.should == "Finished in 33.000000 seconds"
+ end
+
+ it "responds to #register by registering itself with MSpec for appropriate actions" do
+ MSpec.should_receive(:register).with(:start, @timer)
+ MSpec.should_receive(:register).with(:finish, @timer)
+ @timer.register
+ end
+end
diff --git a/spec/mspec/spec/runner/context_spec.rb b/spec/mspec/spec/runner/context_spec.rb
new file mode 100644
index 0000000000..f8759b639d
--- /dev/null
+++ b/spec/mspec/spec/runner/context_spec.rb
@@ -0,0 +1,1041 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers/base'
+require 'mspec/runner/mspec'
+require 'mspec/mocks/mock'
+require 'mspec/runner/context'
+require 'mspec/runner/example'
+
+describe ContextState, "#describe" do
+ before :each do
+ @state = ContextState.new "C#m"
+ @proc = lambda {|*| ScratchPad.record :a }
+ ScratchPad.clear
+ end
+
+ it "evaluates the passed block" do
+ @state.describe(&@proc)
+ ScratchPad.recorded.should == :a
+ end
+
+ it "evaluates the passed block via #protect" do
+ @state.should_receive(:protect).with("C#m", @proc, false)
+ @state.describe(&@proc)
+ end
+
+ it "registers #parent as the current MSpec ContextState" do
+ parent = ContextState.new ""
+ @state.parent = parent
+ MSpec.should_receive(:register_current).with(parent)
+ @state.describe { }
+ end
+
+ it "registers self with MSpec when #shared? is true" do
+ state = ContextState.new "something shared", :shared => true
+ MSpec.should_receive(:register_shared).with(state)
+ state.describe { }
+ end
+end
+
+describe ContextState, "#shared?" do
+ it "returns false when the ContextState is not shared" do
+ ContextState.new("").shared?.should be_false
+ end
+
+ it "returns true when the ContextState is shared" do
+ ContextState.new("", {:shared => true}).shared?.should be_true
+ end
+end
+
+describe ContextState, "#to_s" do
+ it "returns a description string for self when passed a Module" do
+ ContextState.new(Object).to_s.should == "Object"
+ end
+
+ it "returns a description string for self when passed a String" do
+ ContextState.new("SomeClass").to_s.should == "SomeClass"
+ end
+
+ it "returns a description string for self when passed a Module, String" do
+ ContextState.new(Object, "when empty").to_s.should == "Object when empty"
+ end
+
+ it "returns a description string for self when passed a Module and String beginning with '#'" do
+ ContextState.new(Object, "#to_s").to_s.should == "Object#to_s"
+ end
+
+ it "returns a description string for self when passed a Module and String beginning with '.'" do
+ ContextState.new(Object, ".to_s").to_s.should == "Object.to_s"
+ end
+
+ it "returns a description string for self when passed a Module and String beginning with '::'" do
+ ContextState.new(Object, "::to_s").to_s.should == "Object::to_s"
+ end
+end
+
+describe ContextState, "#description" do
+ before :each do
+ @state = ContextState.new "when empty"
+ @parent = ContextState.new "Toplevel"
+ end
+
+ it "returns a composite description string from self and all parents" do
+ @parent.description.should == "Toplevel"
+ @state.description.should == "when empty"
+ @state.parent = @parent
+ @state.description.should == "Toplevel when empty"
+ end
+end
+
+describe ContextState, "#it" do
+ before :each do
+ @state = ContextState.new ""
+ @proc = lambda {|*| }
+
+ @ex = ExampleState.new("", "", &@proc)
+ end
+
+ it "creates an ExampleState instance for the block" do
+ ExampleState.should_receive(:new).with(@state, "it", @proc).and_return(@ex)
+ @state.describe(&@proc)
+ @state.it("it", &@proc)
+ end
+
+ it "calls registered :add actions" do
+ ExampleState.should_receive(:new).with(@state, "it", @proc).and_return(@ex)
+
+ add_action = double("add")
+ add_action.should_receive(:add).with(@ex).and_return { ScratchPad.record :add }
+ MSpec.register :add, add_action
+
+ @state.it("it", &@proc)
+ ScratchPad.recorded.should == :add
+ MSpec.unregister :add, add_action
+ end
+end
+
+describe ContextState, "#examples" do
+ before :each do
+ @state = ContextState.new ""
+ end
+
+ it "returns a list of all examples in this ContextState" do
+ @state.it("first") { }
+ @state.it("second") { }
+ @state.examples.size.should == 2
+ end
+end
+
+describe ContextState, "#before" do
+ before :each do
+ @state = ContextState.new ""
+ @proc = lambda {|*| }
+ end
+
+ it "records the block for :each" do
+ @state.before(:each, &@proc)
+ @state.before(:each).should == [@proc]
+ end
+
+ it "records the block for :all" do
+ @state.before(:all, &@proc)
+ @state.before(:all).should == [@proc]
+ end
+end
+
+describe ContextState, "#after" do
+ before :each do
+ @state = ContextState.new ""
+ @proc = lambda {|*| }
+ end
+
+ it "records the block for :each" do
+ @state.after(:each, &@proc)
+ @state.after(:each).should == [@proc]
+ end
+
+ it "records the block for :all" do
+ @state.after(:all, &@proc)
+ @state.after(:all).should == [@proc]
+ end
+end
+
+describe ContextState, "#pre" do
+ before :each do
+ @a = lambda {|*| }
+ @b = lambda {|*| }
+ @c = lambda {|*| }
+
+ parent = ContextState.new ""
+ parent.before(:each, &@c)
+ parent.before(:all, &@c)
+
+ @state = ContextState.new ""
+ @state.parent = parent
+ end
+
+ it "returns before(:each) actions in the order they were defined" do
+ @state.before(:each, &@a)
+ @state.before(:each, &@b)
+ @state.pre(:each).should == [@c, @a, @b]
+ end
+
+ it "returns before(:all) actions in the order they were defined" do
+ @state.before(:all, &@a)
+ @state.before(:all, &@b)
+ @state.pre(:all).should == [@c, @a, @b]
+ end
+end
+
+describe ContextState, "#post" do
+ before :each do
+ @a = lambda {|*| }
+ @b = lambda {|*| }
+ @c = lambda {|*| }
+
+ parent = ContextState.new ""
+ parent.after(:each, &@c)
+ parent.after(:all, &@c)
+
+ @state = ContextState.new ""
+ @state.parent = parent
+ end
+
+ it "returns after(:each) actions in the reverse order they were defined" do
+ @state.after(:each, &@a)
+ @state.after(:each, &@b)
+ @state.post(:each).should == [@b, @a, @c]
+ end
+
+ it "returns after(:all) actions in the reverse order they were defined" do
+ @state.after(:all, &@a)
+ @state.after(:all, &@b)
+ @state.post(:all).should == [@b, @a, @c]
+ end
+end
+
+describe ContextState, "#protect" do
+ before :each do
+ ScratchPad.record []
+ @a = lambda {|*| ScratchPad << :a }
+ @b = lambda {|*| ScratchPad << :b }
+ @c = lambda {|*| raise Exception, "Fail!" }
+ end
+
+ it "returns true and does execute any blocks if check and MSpec.mode?(:pretend) are true" do
+ MSpec.should_receive(:mode?).with(:pretend).and_return(true)
+ ContextState.new("").protect("message", [@a, @b]).should be_true
+ ScratchPad.recorded.should == []
+ end
+
+ it "executes the blocks if MSpec.mode?(:pretend) is false" do
+ MSpec.should_receive(:mode?).with(:pretend).and_return(false)
+ ContextState.new("").protect("message", [@a, @b])
+ ScratchPad.recorded.should == [:a, :b]
+ end
+
+ it "executes the blocks if check is false" do
+ ContextState.new("").protect("message", [@a, @b], false)
+ ScratchPad.recorded.should == [:a, :b]
+ end
+
+ it "returns true if none of the blocks raise an exception" do
+ ContextState.new("").protect("message", [@a, @b]).should be_true
+ end
+
+ it "returns false if any of the blocks raise an exception" do
+ ContextState.new("").protect("message", [@a, @c, @b]).should be_false
+ end
+end
+
+describe ContextState, "#parent=" do
+ before :each do
+ @state = ContextState.new ""
+ @parent = double("describe")
+ @parent.stub(:parent).and_return(nil)
+ @parent.stub(:child)
+ end
+
+ it "does not set self as a child of parent if shared" do
+ @parent.should_not_receive(:child)
+ state = ContextState.new "", :shared => true
+ state.parent = @parent
+ end
+
+ it "does not set parents if shared" do
+ state = ContextState.new "", :shared => true
+ state.parent = @parent
+ state.parents.should == [state]
+ end
+
+ it "sets self as a child of parent" do
+ @parent.should_receive(:child).with(@state)
+ @state.parent = @parent
+ end
+
+ it "creates the list of parents" do
+ @state.parent = @parent
+ @state.parents.should == [@parent, @state]
+ end
+end
+
+describe ContextState, "#parent" do
+ before :each do
+ @state = ContextState.new ""
+ @parent = double("describe")
+ @parent.stub(:parent).and_return(nil)
+ @parent.stub(:child)
+ end
+
+ it "returns nil if parent has not been set" do
+ @state.parent.should be_nil
+ end
+
+ it "returns the parent" do
+ @state.parent = @parent
+ @state.parent.should == @parent
+ end
+end
+
+describe ContextState, "#parents" do
+ before :each do
+ @first = ContextState.new ""
+ @second = ContextState.new ""
+ @parent = double("describe")
+ @parent.stub(:parent).and_return(nil)
+ @parent.stub(:child)
+ end
+
+ it "returns a list of all enclosing ContextState instances" do
+ @first.parent = @parent
+ @second.parent = @first
+ @second.parents.should == [@parent, @first, @second]
+ end
+end
+
+describe ContextState, "#child" do
+ before :each do
+ @first = ContextState.new ""
+ @second = ContextState.new ""
+ @parent = double("describe")
+ @parent.stub(:parent).and_return(nil)
+ @parent.stub(:child)
+ end
+
+ it "adds the ContextState to the list of contained ContextStates" do
+ @first.child @second
+ @first.children.should == [@second]
+ end
+end
+
+describe ContextState, "#children" do
+ before :each do
+ @parent = ContextState.new ""
+ @first = ContextState.new ""
+ @second = ContextState.new ""
+ end
+
+ it "returns the list of directly contained ContextStates" do
+ @first.parent = @parent
+ @second.parent = @first
+ @parent.children.should == [@first]
+ @first.children.should == [@second]
+ end
+end
+
+describe ContextState, "#state" do
+ before :each do
+ MSpec.store :before, []
+ MSpec.store :after, []
+
+ @state = ContextState.new ""
+ end
+
+ it "returns nil if no spec is being executed" do
+ @state.state.should == nil
+ end
+
+ it "returns a ExampleState instance if an example is being executed" do
+ ScratchPad.record @state
+ @state.describe { }
+ @state.it("") { ScratchPad.record ScratchPad.recorded.state }
+ @state.process
+ @state.state.should == nil
+ ScratchPad.recorded.should be_kind_of(ExampleState)
+ end
+end
+
+describe ContextState, "#process" do
+ before :each do
+ MSpec.store :before, []
+ MSpec.store :after, []
+ MSpec.stub(:register_current)
+
+ @state = ContextState.new ""
+ @state.describe { }
+
+ @a = lambda {|*| ScratchPad << :a }
+ @b = lambda {|*| ScratchPad << :b }
+ ScratchPad.record []
+ end
+
+ it "calls each before(:all) block" do
+ @state.before(:all, &@a)
+ @state.before(:all, &@b)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == [:a, :b]
+ end
+
+ it "calls each after(:all) block" do
+ @state.after(:all, &@a)
+ @state.after(:all, &@b)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == [:b, :a]
+ end
+
+ it "calls each it block" do
+ @state.it("one", &@a)
+ @state.it("two", &@b)
+ @state.process
+ ScratchPad.recorded.should == [:a, :b]
+ end
+
+ it "does not call the #it block if #filtered? returns true" do
+ @state.it("one", &@a)
+ @state.it("two", &@b)
+ @state.examples.first.stub(:filtered?).and_return(true)
+ @state.process
+ ScratchPad.recorded.should == [:b]
+ end
+
+ it "calls each before(:each) block" do
+ @state.before(:each, &@a)
+ @state.before(:each, &@b)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == [:a, :b]
+ end
+
+ it "calls each after(:each) block" do
+ @state.after(:each, &@a)
+ @state.after(:each, &@b)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == [:b, :a]
+ end
+
+ it "calls Mock.cleanup for each it block" do
+ @state.it("") { }
+ @state.it("") { }
+ Mock.should_receive(:cleanup).twice
+ @state.process
+ end
+
+ it "calls Mock.verify_count for each it block" do
+ @state.it("") { }
+ @state.it("") { }
+ Mock.should_receive(:verify_count).twice
+ @state.process
+ end
+
+ it "calls the describe block" do
+ ScratchPad.record []
+ @state.describe { ScratchPad << :a }
+ @state.process
+ ScratchPad.recorded.should == [:a]
+ end
+
+ it "creates a new ExampleState instance for each example" do
+ ScratchPad.record @state
+ @state.describe { }
+ @state.it("it") { ScratchPad.record ScratchPad.recorded.state }
+ @state.process
+ ScratchPad.recorded.should be_kind_of(ExampleState)
+ end
+
+ it "clears the expectations flag before evaluating the #it block" do
+ MSpec.clear_expectations
+ MSpec.should_receive(:clear_expectations)
+ @state.it("it") { ScratchPad.record MSpec.expectation? }
+ @state.process
+ ScratchPad.recorded.should be_false
+ end
+
+ it "shuffles the spec list if MSpec.randomize? is true" do
+ MSpec.randomize
+ MSpec.should_receive(:shuffle)
+ @state.it("") { }
+ @state.process
+ MSpec.randomize false
+ end
+
+ it "sets the current MSpec ContextState" do
+ MSpec.should_receive(:register_current).with(@state)
+ @state.process
+ end
+
+ it "resets the current MSpec ContextState to nil when there are examples" do
+ MSpec.should_receive(:register_current).with(nil)
+ @state.it("") { }
+ @state.process
+ end
+
+ it "resets the current MSpec ContextState to nil when there are no examples" do
+ MSpec.should_receive(:register_current).with(nil)
+ @state.process
+ end
+
+ it "call #process on children when there are examples" do
+ child = ContextState.new ""
+ child.should_receive(:process)
+ @state.child child
+ @state.it("") { }
+ @state.process
+ end
+
+ it "call #process on children when there are no examples" do
+ child = ContextState.new ""
+ child.should_receive(:process)
+ @state.child child
+ @state.process
+ end
+end
+
+describe ContextState, "#process" do
+ before :each do
+ MSpec.store :exception, []
+
+ @state = ContextState.new ""
+ @state.describe { }
+
+ action = double("action")
+ def action.exception(exc)
+ ScratchPad.record :exception if exc.exception.is_a? SpecExpectationNotFoundError
+ end
+ MSpec.register :exception, action
+
+ MSpec.clear_expectations
+ ScratchPad.clear
+ end
+
+ after :each do
+ MSpec.store :exception, nil
+ end
+
+ it "raises an SpecExpectationNotFoundError if an #it block does not contain an expectation" do
+ @state.it("it") { }
+ @state.process
+ ScratchPad.recorded.should == :exception
+ end
+
+ it "does not raise an SpecExpectationNotFoundError if an #it block does contain an expectation" do
+ @state.it("it") { MSpec.expectation }
+ @state.process
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "does not raise an SpecExpectationNotFoundError if the #it block causes a failure" do
+ @state.it("it") { raise Exception, "Failed!" }
+ @state.process
+ ScratchPad.recorded.should be_nil
+ end
+end
+
+describe ContextState, "#process" do
+ before :each do
+ MSpec.store :example, []
+
+ @state = ContextState.new ""
+ @state.describe { }
+
+ example = double("example")
+ def example.example(state, spec)
+ ScratchPad << state << spec
+ end
+ MSpec.register :example, example
+
+ ScratchPad.record []
+ end
+
+ after :each do
+ MSpec.store :example, nil
+ end
+
+ it "calls registered :example actions with the current ExampleState and block" do
+ @state.it("") { MSpec.expectation }
+ @state.process
+
+ ScratchPad.recorded.first.should be_kind_of(ExampleState)
+ ScratchPad.recorded.last.should be_kind_of(Proc)
+ end
+
+ it "does not call registered example actions if the example has no block" do
+ @state.it("empty example")
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+end
+
+describe ContextState, "#process" do
+ before :each do
+ MSpec.store :before, []
+ MSpec.store :after, []
+
+ @state = ContextState.new ""
+ @state.describe { }
+ @state.it("") { MSpec.expectation }
+ end
+
+ after :each do
+ MSpec.store :before, nil
+ MSpec.store :after, nil
+ end
+
+ it "calls registered :before actions with the current ExampleState instance" do
+ before = double("before")
+ before.should_receive(:before).and_return {
+ ScratchPad.record :before
+ @spec_state = @state.state
+ }
+ MSpec.register :before, before
+ @state.process
+ ScratchPad.recorded.should == :before
+ @spec_state.should be_kind_of(ExampleState)
+ end
+
+ it "calls registered :after actions with the current ExampleState instance" do
+ after = double("after")
+ after.should_receive(:after).and_return {
+ ScratchPad.record :after
+ @spec_state = @state.state
+ }
+ MSpec.register :after, after
+ @state.process
+ ScratchPad.recorded.should == :after
+ @spec_state.should be_kind_of(ExampleState)
+ end
+end
+
+describe ContextState, "#process" do
+ before :each do
+ MSpec.store :enter, []
+ MSpec.store :leave, []
+
+ @state = ContextState.new "C#m"
+ @state.describe { }
+ @state.it("") { MSpec.expectation }
+ end
+
+ after :each do
+ MSpec.store :enter, nil
+ MSpec.store :leave, nil
+ end
+
+ it "calls registered :enter actions with the current #describe string" do
+ enter = double("enter")
+ enter.should_receive(:enter).with("C#m").and_return { ScratchPad.record :enter }
+ MSpec.register :enter, enter
+ @state.process
+ ScratchPad.recorded.should == :enter
+ end
+
+ it "calls registered :leave actions" do
+ leave = double("leave")
+ leave.should_receive(:leave).and_return { ScratchPad.record :leave }
+ MSpec.register :leave, leave
+ @state.process
+ ScratchPad.recorded.should == :leave
+ end
+end
+
+describe ContextState, "#process when an exception is raised in before(:all)" do
+ before :each do
+ MSpec.store :before, []
+ MSpec.store :after, []
+
+ @state = ContextState.new ""
+ @state.describe { }
+
+ @a = lambda {|*| ScratchPad << :a }
+ @b = lambda {|*| ScratchPad << :b }
+ ScratchPad.record []
+
+ @state.before(:all) { raise Exception, "Fail!" }
+ end
+
+ after :each do
+ MSpec.store :before, nil
+ MSpec.store :after, nil
+ end
+
+ it "does not call before(:each)" do
+ @state.before(:each, &@a)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call the it block" do
+ @state.it("one", &@a)
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call after(:each)" do
+ @state.after(:each, &@a)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call after(:each)" do
+ @state.after(:all, &@a)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call Mock.verify_count" do
+ @state.it("") { }
+ Mock.should_not_receive(:verify_count)
+ @state.process
+ end
+
+ it "calls Mock.cleanup" do
+ @state.it("") { }
+ Mock.should_receive(:cleanup)
+ @state.process
+ end
+end
+
+describe ContextState, "#process when an exception is raised in before(:each)" do
+ before :each do
+ MSpec.store :before, []
+ MSpec.store :after, []
+
+ @state = ContextState.new ""
+ @state.describe { }
+
+ @a = lambda {|*| ScratchPad << :a }
+ @b = lambda {|*| ScratchPad << :b }
+ ScratchPad.record []
+
+ @state.before(:each) { raise Exception, "Fail!" }
+ end
+
+ after :each do
+ MSpec.store :before, nil
+ MSpec.store :after, nil
+ end
+
+ it "does not call the it block" do
+ @state.it("one", &@a)
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does call after(:each)" do
+ @state.after(:each, &@a)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == [:a]
+ end
+
+ it "does not call Mock.verify_count" do
+ @state.it("") { }
+ Mock.should_not_receive(:verify_count)
+ @state.process
+ end
+end
+
+describe ContextState, "#process in pretend mode" do
+ before :all do
+ MSpec.register_mode :pretend
+ end
+
+ after :all do
+ MSpec.clear_modes
+ end
+
+ before :each do
+ ScratchPad.clear
+ MSpec.store :before, []
+ MSpec.store :after, []
+
+ @state = ContextState.new ""
+ @state.describe { }
+ @state.it("") { }
+ end
+
+ after :each do
+ MSpec.store :before, nil
+ MSpec.store :after, nil
+ end
+
+ it "calls registered :before actions with the current ExampleState instance" do
+ before = double("before")
+ before.should_receive(:before).and_return {
+ ScratchPad.record :before
+ @spec_state = @state.state
+ }
+ MSpec.register :before, before
+ @state.process
+ ScratchPad.recorded.should == :before
+ @spec_state.should be_kind_of(ExampleState)
+ end
+
+ it "calls registered :after actions with the current ExampleState instance" do
+ after = double("after")
+ after.should_receive(:after).and_return {
+ ScratchPad.record :after
+ @spec_state = @state.state
+ }
+ MSpec.register :after, after
+ @state.process
+ ScratchPad.recorded.should == :after
+ @spec_state.should be_kind_of(ExampleState)
+ end
+end
+
+describe ContextState, "#process in pretend mode" do
+ before :all do
+ MSpec.register_mode :pretend
+ end
+
+ after :all do
+ MSpec.clear_modes
+ end
+
+ before :each do
+ MSpec.store :before, []
+ MSpec.store :after, []
+
+ @state = ContextState.new ""
+ @state.describe { }
+
+ @a = lambda {|*| ScratchPad << :a }
+ @b = lambda {|*| ScratchPad << :b }
+ ScratchPad.record []
+ end
+
+ it "calls the describe block" do
+ ScratchPad.record []
+ @state.describe { ScratchPad << :a }
+ @state.process
+ ScratchPad.recorded.should == [:a]
+ end
+
+ it "does not call any before(:all) block" do
+ @state.before(:all, &@a)
+ @state.before(:all, &@b)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call any after(:all) block" do
+ @state.after(:all, &@a)
+ @state.after(:all, &@b)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call any it block" do
+ @state.it("one", &@a)
+ @state.it("two", &@b)
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call any before(:each) block" do
+ @state.before(:each, &@a)
+ @state.before(:each, &@b)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call any after(:each) block" do
+ @state.after(:each, &@a)
+ @state.after(:each, &@b)
+ @state.it("") { }
+ @state.process
+ ScratchPad.recorded.should == []
+ end
+
+ it "does not call Mock.cleanup" do
+ @state.it("") { }
+ @state.it("") { }
+ Mock.should_not_receive(:cleanup)
+ @state.process
+ end
+end
+
+describe ContextState, "#process in pretend mode" do
+ before :all do
+ MSpec.register_mode :pretend
+ end
+
+ after :all do
+ MSpec.clear_modes
+ end
+
+ before :each do
+ MSpec.store :enter, []
+ MSpec.store :leave, []
+
+ @state = ContextState.new ""
+ @state.describe { }
+ @state.it("") { }
+ end
+
+ after :each do
+ MSpec.store :enter, nil
+ MSpec.store :leave, nil
+ end
+
+ it "calls registered :enter actions with the current #describe string" do
+ enter = double("enter")
+ enter.should_receive(:enter).and_return { ScratchPad.record :enter }
+ MSpec.register :enter, enter
+ @state.process
+ ScratchPad.recorded.should == :enter
+ end
+
+ it "calls registered :leave actions" do
+ leave = double("leave")
+ leave.should_receive(:leave).and_return { ScratchPad.record :leave }
+ MSpec.register :leave, leave
+ @state.process
+ ScratchPad.recorded.should == :leave
+ end
+end
+
+describe ContextState, "#it_should_behave_like" do
+ before :each do
+ @shared_desc = :shared_context
+ @shared = ContextState.new(@shared_desc, :shared => true)
+ MSpec.stub(:retrieve_shared).and_return(@shared)
+
+ @state = ContextState.new "Top level"
+ @a = lambda {|*| }
+ @b = lambda {|*| }
+ end
+
+ it "raises an Exception if unable to find the shared ContextState" do
+ MSpec.should_receive(:retrieve_shared).and_return(nil)
+ lambda { @state.it_should_behave_like "this" }.should raise_error(Exception)
+ end
+
+ describe "for nested ContextState instances" do
+ before :each do
+ @nested = ContextState.new "nested context"
+ @nested.parents.unshift @shared
+
+ @shared.children << @nested
+
+ @nested_dup = @nested.dup
+ @nested.stub(:dup).and_return(@nested_dup)
+ end
+
+ it "duplicates the nested ContextState" do
+ @state.it_should_behave_like @shared_desc
+ @state.children.first.should equal(@nested_dup)
+ end
+
+ it "sets the parent of the nested ContextState to the containing ContextState" do
+ @state.it_should_behave_like @shared_desc
+ @nested_dup.parent.should equal(@state)
+ end
+
+ it "sets the context for nested examples to the nested ContextState's dup" do
+ @shared.it "an example", &@a
+ @shared.it "another example", &@b
+ @state.it_should_behave_like @shared_desc
+ @nested_dup.examples.each { |x| x.context.should equal(@nested_dup) }
+ end
+
+ it "omits the shored ContextState's description" do
+ @nested.it "an example", &@a
+ @nested.it "another example", &@b
+ @state.it_should_behave_like @shared_desc
+
+ @nested_dup.description.should == "Top level nested context"
+ @nested_dup.examples.first.description.should == "Top level nested context an example"
+ @nested_dup.examples.last.description.should == "Top level nested context another example"
+ end
+ end
+
+ it "adds duped examples from the shared ContextState" do
+ @shared.it "some method", &@a
+ ex_dup = @shared.examples.first.dup
+ @shared.examples.first.stub(:dup).and_return(ex_dup)
+
+ @state.it_should_behave_like @shared_desc
+ @state.examples.should == [ex_dup]
+ end
+
+ it "sets the context for examples to the containing ContextState" do
+ @shared.it "an example", &@a
+ @shared.it "another example", &@b
+ @state.it_should_behave_like @shared_desc
+ @state.examples.each { |x| x.context.should equal(@state) }
+ end
+
+ it "adds before(:all) blocks from the shared ContextState" do
+ @shared.before :all, &@a
+ @shared.before :all, &@b
+ @state.it_should_behave_like @shared_desc
+ @state.before(:all).should include(*@shared.before(:all))
+ end
+
+ it "adds before(:each) blocks from the shared ContextState" do
+ @shared.before :each, &@a
+ @shared.before :each, &@b
+ @state.it_should_behave_like @shared_desc
+ @state.before(:each).should include(*@shared.before(:each))
+ end
+
+ it "adds after(:each) blocks from the shared ContextState" do
+ @shared.after :each, &@a
+ @shared.after :each, &@b
+ @state.it_should_behave_like @shared_desc
+ @state.after(:each).should include(*@shared.after(:each))
+ end
+
+ it "adds after(:all) blocks from the shared ContextState" do
+ @shared.after :all, &@a
+ @shared.after :all, &@b
+ @state.it_should_behave_like @shared_desc
+ @state.after(:all).should include(*@shared.after(:all))
+ end
+end
+
+describe ContextState, "#filter_examples" do
+ before :each do
+ @state = ContextState.new ""
+ @state.it("one") { }
+ @state.it("two") { }
+ end
+
+ it "removes examples that are filtered" do
+ @state.examples.first.stub(:filtered?).and_return(true)
+ @state.examples.size.should == 2
+ @state.filter_examples
+ @state.examples.size.should == 1
+ end
+
+ it "returns true if there are remaining examples to evaluate" do
+ @state.examples.first.stub(:filtered?).and_return(true)
+ @state.filter_examples.should be_true
+ end
+
+ it "returns false if there are no remaining examples to evaluate" do
+ @state.examples.first.stub(:filtered?).and_return(true)
+ @state.examples.last.stub(:filtered?).and_return(true)
+ @state.filter_examples.should be_false
+ end
+end
diff --git a/spec/mspec/spec/runner/example_spec.rb b/spec/mspec/spec/runner/example_spec.rb
new file mode 100644
index 0000000000..b4391f802d
--- /dev/null
+++ b/spec/mspec/spec/runner/example_spec.rb
@@ -0,0 +1,117 @@
+require 'spec_helper'
+require 'mspec/matchers/base'
+require 'mspec/runner/mspec'
+require 'mspec/mocks/mock'
+require 'mspec/runner/example'
+
+describe ExampleState do
+ it "is initialized with the ContextState, #it string, and #it block" do
+ prc = lambda { }
+ context = ContextState.new ""
+ ExampleState.new(context, "does", prc).should be_kind_of(ExampleState)
+ end
+end
+
+describe ExampleState, "#describe" do
+ before :each do
+ @context = ContextState.new Object, "#to_s"
+ @state = ExampleState.new @context, "it"
+ end
+
+ it "returns the ContextState#description" do
+ @state.describe.should == @context.description
+ end
+end
+
+describe ExampleState, "#it" do
+ before :each do
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ end
+
+ it "returns the argument to the #it block" do
+ @state.it.should == "it"
+ end
+end
+
+describe ExampleState, "#context=" do
+ before :each do
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ @context = ContextState.new "New#context"
+ end
+
+ it "sets the containing ContextState" do
+ @state.context = @context
+ @state.context.should == @context
+ end
+
+ it "resets the description" do
+ @state.description.should == "describe it"
+ @state.context = @context
+ @state.description.should == "New#context it"
+ end
+end
+
+describe ExampleState, "#example" do
+ before :each do
+ @proc = lambda { }
+ @state = ExampleState.new ContextState.new("describe"), "it", @proc
+ end
+
+ it "returns the #it block" do
+ @state.example.should == @proc
+ end
+end
+
+describe ExampleState, "#filtered?" do
+ before :each do
+ MSpec.store :include, nil
+ MSpec.store :exclude, nil
+
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ @filter = double("filter")
+ end
+
+ after :each do
+ MSpec.store :include, nil
+ MSpec.store :exclude, nil
+ end
+
+ it "returns false if MSpec include filters list is empty" do
+ @state.filtered?.should == false
+ end
+
+ it "returns false if MSpec include filters match this spec" do
+ @filter.should_receive(:===).and_return(true)
+ MSpec.register :include, @filter
+ @state.filtered?.should == false
+ end
+
+ it "returns true if MSpec include filters do not match this spec" do
+ @filter.should_receive(:===).and_return(false)
+ MSpec.register :include, @filter
+ @state.filtered?.should == true
+ end
+
+ it "returns false if MSpec exclude filters list is empty" do
+ @state.filtered?.should == false
+ end
+
+ it "returns false if MSpec exclude filters do not match this spec" do
+ @filter.should_receive(:===).and_return(false)
+ MSpec.register :exclude, @filter
+ @state.filtered?.should == false
+ end
+
+ it "returns true if MSpec exclude filters match this spec" do
+ @filter.should_receive(:===).and_return(true)
+ MSpec.register :exclude, @filter
+ @state.filtered?.should == true
+ end
+
+ it "returns true if MSpec include and exclude filters match this spec" do
+ @filter.should_receive(:===).twice.and_return(true)
+ MSpec.register :include, @filter
+ MSpec.register :exclude, @filter
+ @state.filtered?.should == true
+ end
+end
diff --git a/spec/mspec/spec/runner/exception_spec.rb b/spec/mspec/spec/runner/exception_spec.rb
new file mode 100644
index 0000000000..309442435c
--- /dev/null
+++ b/spec/mspec/spec/runner/exception_spec.rb
@@ -0,0 +1,146 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/runner/example'
+require 'mspec/runner/exception'
+require 'mspec/utils/script'
+
+describe ExceptionState, "#initialize" do
+ it "takes a state, location (e.g. before :each), and exception" do
+ context = ContextState.new "Class#method"
+ state = ExampleState.new context, "does something"
+ exc = Exception.new "Fail!"
+ ExceptionState.new(state, "location", exc).should be_kind_of(ExceptionState)
+ end
+end
+
+describe ExceptionState, "#description" do
+ before :each do
+ context = ContextState.new "Class#method"
+ @state = ExampleState.new context, "does something"
+ end
+
+ it "returns the state description if state was not nil" do
+ exc = ExceptionState.new(@state, nil, nil)
+ exc.description.should == "Class#method does something"
+ end
+
+ it "returns the location if it is not nil and description is nil" do
+ exc = ExceptionState.new(nil, "location", nil)
+ exc.description.should == "An exception occurred during: location"
+ end
+
+ it "returns both description and location if neither are nil" do
+ exc = ExceptionState.new(@state, "location", nil)
+ exc.description.should == "An exception occurred during: location\nClass#method does something"
+ end
+end
+
+describe ExceptionState, "#describe" do
+ before :each do
+ context = ContextState.new "Class#method"
+ @state = ExampleState.new context, "does something"
+ end
+
+ it "returns the ExampleState#describe string if created with a non-nil state" do
+ ExceptionState.new(@state, nil, nil).describe.should == @state.describe
+ end
+
+ it "returns an empty string if created with a nil state" do
+ ExceptionState.new(nil, nil, nil).describe.should == ""
+ end
+end
+
+describe ExceptionState, "#it" do
+ before :each do
+ context = ContextState.new "Class#method"
+ @state = ExampleState.new context, "does something"
+ end
+
+ it "returns the ExampleState#it string if created with a non-nil state" do
+ ExceptionState.new(@state, nil, nil).it.should == @state.it
+ end
+
+ it "returns an empty string if created with a nil state" do
+ ExceptionState.new(nil, nil, nil).it.should == ""
+ end
+end
+
+describe ExceptionState, "#failure?" do
+ before :each do
+ @state = ExampleState.new ContextState.new("C#m"), "works"
+ end
+
+ it "returns true if the exception is an SpecExpectationNotMetError" do
+ exc = ExceptionState.new @state, "", SpecExpectationNotMetError.new("Fail!")
+ exc.failure?.should be_true
+ end
+
+ it "returns true if the exception is an SpecExpectationNotFoundError" do
+ exc = ExceptionState.new @state, "", SpecExpectationNotFoundError.new("Fail!")
+ exc.failure?.should be_true
+ end
+
+ it "returns false if the exception is not an SpecExpectationNotMetError or an SpecExpectationNotFoundError" do
+ exc = ExceptionState.new @state, "", Exception.new("Fail!")
+ exc.failure?.should be_false
+ end
+end
+
+describe ExceptionState, "#message" do
+ before :each do
+ @state = ExampleState.new ContextState.new("C#m"), "works"
+ end
+
+ it "returns <No message> if the exception message is empty" do
+ exc = ExceptionState.new @state, "", Exception.new("")
+ exc.message.should == "<No message>"
+ end
+
+ it "returns the message without exception class when the exception is an SpecExpectationNotMetError" do
+ exc = ExceptionState.new @state, "", SpecExpectationNotMetError.new("Fail!")
+ exc.message.should == "Fail!"
+ end
+
+ it "returns SpecExpectationNotFoundError#message when the exception is an SpecExpectationNotFoundError" do
+ e = SpecExpectationNotFoundError.new
+ exc = ExceptionState.new @state, "", e
+ exc.message.should == e.message
+ end
+
+ it "returns the message with exception class when the exception is not an SpecExpectationNotMetError or an SpecExpectationNotFoundError" do
+ exc = ExceptionState.new @state, "", Exception.new("Fail!")
+ exc.message.should == "Exception: Fail!"
+ end
+end
+
+describe ExceptionState, "#backtrace" do
+ before :each do
+ @state = ExampleState.new ContextState.new("C#m"), "works"
+ begin
+ raise Exception
+ rescue Exception => @exception
+ @exc = ExceptionState.new @state, "", @exception
+ end
+ end
+
+ after :each do
+ $MSPEC_DEBUG = nil
+ end
+
+ it "returns a string representation of the exception backtrace" do
+ @exc.backtrace.should be_kind_of(String)
+ end
+
+ it "does not filter files from the backtrace if $MSPEC_DEBUG is true" do
+ $MSPEC_DEBUG = true
+ @exc.backtrace.should == @exception.backtrace.join("\n")
+ end
+
+ it "filters files matching config[:backtrace_filter]" do
+ MSpecScript.set :backtrace_filter, %r[mspec/lib]
+ $MSPEC_DEBUG = nil
+ @exc.backtrace.split("\n").each do |line|
+ line.should_not =~ %r[mspec/lib]
+ end
+ end
+end
diff --git a/spec/mspec/spec/runner/filters/a.yaml b/spec/mspec/spec/runner/filters/a.yaml
new file mode 100644
index 0000000000..1940e3cba6
--- /dev/null
+++ b/spec/mspec/spec/runner/filters/a.yaml
@@ -0,0 +1,4 @@
+---
+A#:
+- a
+- aa
diff --git a/spec/mspec/spec/runner/filters/b.yaml b/spec/mspec/spec/runner/filters/b.yaml
new file mode 100644
index 0000000000..a24bdb2f19
--- /dev/null
+++ b/spec/mspec/spec/runner/filters/b.yaml
@@ -0,0 +1,11 @@
+---
+B.:
+- b
+- bb
+B::C#:
+- b!
+- b=
+- b?
+- "-"
+- "[]"
+- "[]="
diff --git a/spec/mspec/spec/runner/filters/match_spec.rb b/spec/mspec/spec/runner/filters/match_spec.rb
new file mode 100644
index 0000000000..f2c665c495
--- /dev/null
+++ b/spec/mspec/spec/runner/filters/match_spec.rb
@@ -0,0 +1,34 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/mspec'
+require 'mspec/runner/filters/match'
+
+describe MatchFilter, "#===" do
+ before :each do
+ @filter = MatchFilter.new nil, 'a', 'b', 'c'
+ end
+
+ it "returns true if the argument matches any of the #initialize strings" do
+ @filter.===('aaa').should == true
+ @filter.===('bccb').should == true
+ end
+
+ it "returns false if the argument matches none of the #initialize strings" do
+ @filter.===('d').should == false
+ end
+end
+
+describe MatchFilter, "#register" do
+ it "registers itself with MSpec for the designated action list" do
+ filter = MatchFilter.new :include
+ MSpec.should_receive(:register).with(:include, filter)
+ filter.register
+ end
+end
+
+describe MatchFilter, "#unregister" do
+ it "unregisters itself with MSpec for the designated action list" do
+ filter = MatchFilter.new :exclude
+ MSpec.should_receive(:unregister).with(:exclude, filter)
+ filter.unregister
+ end
+end
diff --git a/spec/mspec/spec/runner/filters/profile_spec.rb b/spec/mspec/spec/runner/filters/profile_spec.rb
new file mode 100644
index 0000000000..78807bca5c
--- /dev/null
+++ b/spec/mspec/spec/runner/filters/profile_spec.rb
@@ -0,0 +1,117 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/mspec'
+require 'mspec/runner/filters/profile'
+
+describe ProfileFilter, "#find" do
+ before :each do
+ @filter = ProfileFilter.new nil
+ File.stub(:exist?).and_return(false)
+ @file = "rails.yaml"
+ end
+
+ it "attempts to locate the file through the expanded path name" do
+ File.should_receive(:expand_path).with(@file).and_return(@file)
+ File.should_receive(:exist?).with(@file).and_return(true)
+ @filter.find(@file).should == @file
+ end
+
+ it "attemps to locate the file in 'spec/profiles'" do
+ path = File.join "spec/profiles", @file
+ File.should_receive(:exist?).with(path).and_return(true)
+ @filter.find(@file).should == path
+ end
+
+ it "attemps to locate the file in 'spec'" do
+ path = File.join "spec", @file
+ File.should_receive(:exist?).with(path).and_return(true)
+ @filter.find(@file).should == path
+ end
+
+ it "attemps to locate the file in 'profiles'" do
+ path = File.join "profiles", @file
+ File.should_receive(:exist?).with(path).and_return(true)
+ @filter.find(@file).should == path
+ end
+
+ it "attemps to locate the file in '.'" do
+ path = File.join ".", @file
+ File.should_receive(:exist?).with(path).and_return(true)
+ @filter.find(@file).should == path
+ end
+end
+
+describe ProfileFilter, "#parse" do
+ before :each do
+ @filter = ProfileFilter.new nil
+ @file = File.open(File.dirname(__FILE__) + "/b.yaml", "r")
+ end
+
+ after :each do
+ @file.close
+ end
+
+ it "creates a Hash of the contents of the YAML file" do
+ @filter.parse(@file).should == {
+ "B." => ["b", "bb"],
+ "B::C#" => ["b!", "b=", "b?", "-", "[]", "[]="]
+ }
+ end
+end
+
+describe ProfileFilter, "#load" do
+ before :each do
+ @filter = ProfileFilter.new nil
+ @files = [
+ File.dirname(__FILE__) + "/a.yaml",
+ File.dirname(__FILE__) + "/b.yaml"
+ ]
+ end
+
+ it "generates a composite hash from multiple YAML files" do
+ @filter.load(*@files).should == {
+ "A#" => ["a", "aa"],
+ "B." => ["b", "bb"],
+ "B::C#" => ["b!", "b=", "b?", "-", "[]", "[]="]
+ }
+ end
+end
+
+describe ProfileFilter, "#===" do
+ before :each do
+ @filter = ProfileFilter.new nil
+ @filter.stub(:load).and_return({ "A#" => ["[]=", "a", "a!", "a?", "aa="]})
+ @filter.send :initialize, nil
+ end
+
+ it "returns true if the spec description is for a method in the profile" do
+ @filter.===("The A#[]= method").should == true
+ @filter.===("A#a returns").should == true
+ @filter.===("A#a! replaces").should == true
+ @filter.===("A#a? returns").should == true
+ @filter.===("A#aa= raises").should == true
+ end
+
+ it "returns false if the spec description is for a method not in the profile" do
+ @filter.===("The A#[] method").should == false
+ @filter.===("B#a returns").should == false
+ @filter.===("A.a! replaces").should == false
+ @filter.===("AA#a? returns").should == false
+ @filter.===("A#aa raises").should == false
+ end
+end
+
+describe ProfileFilter, "#register" do
+ it "registers itself with MSpec for the designated action list" do
+ filter = ProfileFilter.new :include
+ MSpec.should_receive(:register).with(:include, filter)
+ filter.register
+ end
+end
+
+describe ProfileFilter, "#unregister" do
+ it "unregisters itself with MSpec for the designated action list" do
+ filter = ProfileFilter.new :exclude
+ MSpec.should_receive(:unregister).with(:exclude, filter)
+ filter.unregister
+ end
+end
diff --git a/spec/mspec/spec/runner/filters/regexp_spec.rb b/spec/mspec/spec/runner/filters/regexp_spec.rb
new file mode 100644
index 0000000000..6c05b0f42f
--- /dev/null
+++ b/spec/mspec/spec/runner/filters/regexp_spec.rb
@@ -0,0 +1,13 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/mspec'
+require 'mspec/runner/filters/regexp'
+
+describe RegexpFilter, "#to_regexp" do
+ before :each do
+ @filter = RegexpFilter.new nil
+ end
+
+ it "converts its arguments to Regexp instances" do
+ @filter.to_regexp('a(b|c)', 'b[^ab]', 'cc?').should == [/a(b|c)/, /b[^ab]/, /cc?/]
+ end
+end
diff --git a/spec/mspec/spec/runner/filters/tag_spec.rb b/spec/mspec/spec/runner/filters/tag_spec.rb
new file mode 100644
index 0000000000..fe1f3df039
--- /dev/null
+++ b/spec/mspec/spec/runner/filters/tag_spec.rb
@@ -0,0 +1,92 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/mspec'
+require 'mspec/runner/filters/match'
+require 'mspec/runner/filters/tag'
+
+describe TagFilter, "#load" do
+ before :each do
+ @match = double("match filter").as_null_object
+ @filter = TagFilter.new :include, "tag", "key"
+ @tag = SpecTag.new "tag(comment):description"
+ MSpec.stub(:read_tags).and_return([@tag])
+ MSpec.stub(:register)
+ end
+
+ it "loads tags from the tag file" do
+ MSpec.should_receive(:read_tags).with(["tag", "key"]).and_return([])
+ @filter.load
+ end
+
+
+ it "registers itself with MSpec for the :include action" do
+ filter = TagFilter.new(:include)
+ MSpec.should_receive(:register).with(:include, filter)
+ filter.load
+ end
+
+ it "registers itself with MSpec for the :exclude action" do
+ filter = TagFilter.new(:exclude)
+ MSpec.should_receive(:register).with(:exclude, filter)
+ filter.load
+ end
+end
+
+describe TagFilter, "#unload" do
+ before :each do
+ @filter = TagFilter.new :include, "tag", "key"
+ @tag = SpecTag.new "tag(comment):description"
+ MSpec.stub(:read_tags).and_return([@tag])
+ MSpec.stub(:register)
+ end
+
+ it "unregisters itself" do
+ @filter.load
+ MSpec.should_receive(:unregister).with(:include, @filter)
+ @filter.unload
+ end
+end
+
+describe TagFilter, "#register" do
+ before :each do
+ MSpec.stub(:register)
+ end
+
+ it "registers itself with MSpec for the :load, :unload actions" do
+ filter = TagFilter.new(nil)
+ MSpec.should_receive(:register).with(:load, filter)
+ MSpec.should_receive(:register).with(:unload, filter)
+ filter.register
+ end
+end
+
+describe TagFilter, "#unregister" do
+ before :each do
+ MSpec.stub(:unregister)
+ end
+
+ it "unregisters itself with MSpec for the :load, :unload actions" do
+ filter = TagFilter.new(nil)
+ MSpec.should_receive(:unregister).with(:load, filter)
+ MSpec.should_receive(:unregister).with(:unload, filter)
+ filter.unregister
+ end
+end
+
+describe TagFilter, "#===" do
+ before :each do
+ @filter = TagFilter.new nil, "tag", "key"
+ @tag = SpecTag.new "tag(comment):description"
+ MSpec.stub(:read_tags).and_return([@tag])
+ MSpec.stub(:register)
+ @filter.load
+ end
+
+ it "returns true if the argument matches any of the descriptions" do
+ @filter.===('description').should == true
+ end
+
+ it "returns false if the argument matches none of the descriptions" do
+ @filter.===('descriptionA').should == false
+ @filter.===('adescription').should == false
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/describe_spec.rb b/spec/mspec/spec/runner/formatters/describe_spec.rb
new file mode 100644
index 0000000000..415ced71fb
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/describe_spec.rb
@@ -0,0 +1,67 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/describe'
+require 'mspec/runner/example'
+
+describe DescribeFormatter, "#finish" do
+ before :each do
+ MSpec.stub(:register)
+ MSpec.stub(:unregister)
+
+ @timer = double("timer").as_null_object
+ TimerAction.stub(:new).and_return(@timer)
+ @timer.stub(:format).and_return("Finished in 2.0 seconds")
+
+ $stdout = @out = IOStub.new
+ context = ContextState.new "Class#method"
+ @state = ExampleState.new(context, "runs")
+
+ @formatter = DescribeFormatter.new
+ @formatter.register
+
+ @tally = @formatter.tally
+ @counter = @tally.counter
+
+ @counter.files!
+ @counter.examples!
+ @counter.expectations! 2
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints a summary of elapsed time" do
+ @formatter.finish
+ @out.should =~ /^Finished in 2.0 seconds$/
+ end
+
+ it "prints a tally of counts" do
+ @formatter.finish
+ @out.should =~ /^1 file, 1 example, 2 expectations, 0 failures, 0 errors, 0 tagged$/
+ end
+
+ it "does not print exceptions" do
+ @formatter.finish
+ @out.should == %[
+
+Finished in 2.0 seconds
+
+1 file, 1 example, 2 expectations, 0 failures, 0 errors, 0 tagged
+]
+ end
+
+ it "prints a summary of failures and errors for each describe block" do
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+ @formatter.finish
+ @out.should == %[
+
+Class#method 0 failures, 1 error
+
+Finished in 2.0 seconds
+
+1 file, 1 example, 2 expectations, 0 failures, 0 errors, 0 tagged
+]
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/dotted_spec.rb b/spec/mspec/spec/runner/formatters/dotted_spec.rb
new file mode 100644
index 0000000000..1e9b06f6e1
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/dotted_spec.rb
@@ -0,0 +1,285 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/dotted'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+require 'mspec/utils/script'
+
+describe DottedFormatter, "#initialize" do
+ it "permits zero arguments" do
+ DottedFormatter.new
+ end
+
+ it "accepts one argument" do
+ DottedFormatter.new nil
+ end
+end
+
+describe DottedFormatter, "#register" do
+ before :each do
+ @formatter = DottedFormatter.new
+ MSpec.stub(:register)
+ end
+
+ it "registers self with MSpec for appropriate actions" do
+ MSpec.should_receive(:register).with(:exception, @formatter)
+ MSpec.should_receive(:register).with(:before, @formatter)
+ MSpec.should_receive(:register).with(:after, @formatter)
+ MSpec.should_receive(:register).with(:finish, @formatter)
+ @formatter.register
+ end
+
+ it "creates TimerAction and TallyAction" do
+ timer = double("timer")
+ tally = double("tally")
+ timer.should_receive(:register)
+ tally.should_receive(:register)
+ tally.should_receive(:counter)
+ TimerAction.should_receive(:new).and_return(timer)
+ TallyAction.should_receive(:new).and_return(tally)
+ @formatter.register
+ end
+end
+
+describe DottedFormatter, "#print" do
+ before :each do
+ $stdout = IOStub.new
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "writes to $stdout by default" do
+ formatter = DottedFormatter.new
+ formatter.print "begonias"
+ $stdout.should == "begonias"
+ end
+
+ it "writes to the file specified when the formatter was created" do
+ out = IOStub.new
+ File.should_receive(:open).with("some/file", "w").and_return(out)
+ formatter = DottedFormatter.new "some/file"
+ formatter.print "begonias"
+ out.should == "begonias"
+ end
+
+ it "flushes the IO output" do
+ $stdout.should_receive(:flush)
+ formatter = DottedFormatter.new
+ formatter.print "begonias"
+ end
+end
+
+describe DottedFormatter, "#exception" do
+ before :each do
+ @formatter = DottedFormatter.new
+ @failure = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("failed")
+ @error = ExceptionState.new nil, nil, MSpecExampleError.new("boom!")
+ end
+
+ it "sets the #failure? flag" do
+ @formatter.exception @failure
+ @formatter.failure?.should be_true
+ @formatter.exception @error
+ @formatter.failure?.should be_false
+ end
+
+ it "sets the #exception? flag" do
+ @formatter.exception @error
+ @formatter.exception?.should be_true
+ @formatter.exception @failure
+ @formatter.exception?.should be_true
+ end
+
+ it "addes the exception to the list of exceptions" do
+ @formatter.exceptions.should == []
+ @formatter.exception @error
+ @formatter.exception @failure
+ @formatter.exceptions.should == [@error, @failure]
+ end
+end
+
+describe DottedFormatter, "#exception?" do
+ before :each do
+ @formatter = DottedFormatter.new
+ @failure = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("failed")
+ @error = ExceptionState.new nil, nil, MSpecExampleError.new("boom!")
+ end
+
+ it "returns false if there have been no exceptions" do
+ @formatter.exception?.should be_false
+ end
+
+ it "returns true if any exceptions are errors" do
+ @formatter.exception @failure
+ @formatter.exception @error
+ @formatter.exception?.should be_true
+ end
+
+ it "returns true if all exceptions are failures" do
+ @formatter.exception @failure
+ @formatter.exception @failure
+ @formatter.exception?.should be_true
+ end
+
+ it "returns true if all exceptions are errors" do
+ @formatter.exception @error
+ @formatter.exception @error
+ @formatter.exception?.should be_true
+ end
+end
+
+describe DottedFormatter, "#failure?" do
+ before :each do
+ @formatter = DottedFormatter.new
+ @failure = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("failed")
+ @error = ExceptionState.new nil, nil, MSpecExampleError.new("boom!")
+ end
+
+ it "returns false if there have been no exceptions" do
+ @formatter.failure?.should be_false
+ end
+
+ it "returns false if any exceptions are errors" do
+ @formatter.exception @failure
+ @formatter.exception @error
+ @formatter.failure?.should be_false
+ end
+
+ it "returns true if all exceptions are failures" do
+ @formatter.exception @failure
+ @formatter.exception @failure
+ @formatter.failure?.should be_true
+ end
+end
+
+describe DottedFormatter, "#before" do
+ before :each do
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ @formatter = DottedFormatter.new
+ @formatter.exception ExceptionState.new(nil, nil, SpecExpectationNotMetError.new("Failed!"))
+ end
+
+ it "resets the #failure? flag to false" do
+ @formatter.failure?.should be_true
+ @formatter.before @state
+ @formatter.failure?.should be_false
+ end
+
+ it "resets the #exception? flag to false" do
+ @formatter.exception?.should be_true
+ @formatter.before @state
+ @formatter.exception?.should be_false
+ end
+end
+
+describe DottedFormatter, "#after" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = DottedFormatter.new
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints a '.' if there was no exception raised" do
+ @formatter.after(@state)
+ @out.should == "."
+ end
+
+ it "prints an 'F' if there was an expectation failure" do
+ exc = SpecExpectationNotMetError.new "failed"
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ @formatter.after(@state)
+ @out.should == "F"
+ end
+
+ it "prints an 'E' if there was an exception other than expectation failure" do
+ exc = MSpecExampleError.new("boom!")
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ @formatter.after(@state)
+ @out.should == "E"
+ end
+
+ it "prints an 'E' if there are mixed exceptions and exepctation failures" do
+ exc = SpecExpectationNotMetError.new "failed"
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ exc = MSpecExampleError.new("boom!")
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ @formatter.after(@state)
+ @out.should == "E"
+ end
+end
+
+describe DottedFormatter, "#finish" do
+ before :each do
+ @tally = double("tally").as_null_object
+ TallyAction.stub(:new).and_return(@tally)
+ @timer = double("timer").as_null_object
+ TimerAction.stub(:new).and_return(@timer)
+
+ $stdout = @out = IOStub.new
+ context = ContextState.new "Class#method"
+ @state = ExampleState.new(context, "runs")
+ MSpec.stub(:register)
+ @formatter = DottedFormatter.new
+ @formatter.register
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints a failure message for an exception" do
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
+ @formatter.exception exc
+ @formatter.after @state
+ @formatter.finish
+ @out.should =~ /^1\)\nClass#method runs ERROR$/
+ end
+
+ it "prints a backtrace for an exception" do
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+ @formatter.after @state
+ @formatter.finish
+ @out.should =~ %r[path/to/some/file.rb:35:in method$]
+ end
+
+ it "prints a summary of elapsed time" do
+ @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
+ @formatter.finish
+ @out.should =~ /^Finished in 2.0 seconds$/
+ end
+
+ it "prints a tally of counts" do
+ @tally.should_receive(:format).and_return("1 example, 0 failures")
+ @formatter.finish
+ @out.should =~ /^1 example, 0 failures$/
+ end
+
+ it "prints errors, backtraces, elapsed time, and tallies" do
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+ @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
+ @tally.should_receive(:format).and_return("1 example, 1 failure")
+ @formatter.after @state
+ @formatter.finish
+ @out.should ==
+%[E
+
+1)
+Class#method runs ERROR
+MSpecExampleError: broken
+path/to/some/file.rb:35:in method
+
+Finished in 2.0 seconds
+
+1 example, 1 failure
+]
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/file_spec.rb b/spec/mspec/spec/runner/formatters/file_spec.rb
new file mode 100644
index 0000000000..946683ad58
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/file_spec.rb
@@ -0,0 +1,84 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/file'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+
+describe FileFormatter, "#register" do
+ before :each do
+ @formatter = FileFormatter.new
+ MSpec.stub(:register)
+ MSpec.stub(:unregister)
+ end
+
+ it "registers self with MSpec for :load, :unload actions" do
+ MSpec.should_receive(:register).with(:load, @formatter)
+ MSpec.should_receive(:register).with(:unload, @formatter)
+ @formatter.register
+ end
+
+ it "unregisters self with MSpec for :before, :after actions" do
+ MSpec.should_receive(:unregister).with(:before, @formatter)
+ MSpec.should_receive(:unregister).with(:after, @formatter)
+ @formatter.register
+ end
+end
+
+describe FileFormatter, "#load" do
+ before :each do
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ @formatter = FileFormatter.new
+ @formatter.exception ExceptionState.new(nil, nil, SpecExpectationNotMetError.new("Failed!"))
+ end
+
+ it "resets the #failure? flag to false" do
+ @formatter.failure?.should be_true
+ @formatter.load @state
+ @formatter.failure?.should be_false
+ end
+
+ it "resets the #exception? flag to false" do
+ @formatter.exception?.should be_true
+ @formatter.load @state
+ @formatter.exception?.should be_false
+ end
+end
+
+describe FileFormatter, "#unload" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = FileFormatter.new
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints a '.' if there was no exception raised" do
+ @formatter.unload(@state)
+ @out.should == "."
+ end
+
+ it "prints an 'F' if there was an expectation failure" do
+ exc = SpecExpectationNotMetError.new "failed"
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ @formatter.unload(@state)
+ @out.should == "F"
+ end
+
+ it "prints an 'E' if there was an exception other than expectation failure" do
+ exc = MSpecExampleError.new("boom!")
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ @formatter.unload(@state)
+ @out.should == "E"
+ end
+
+ it "prints an 'E' if there are mixed exceptions and exepctation failures" do
+ exc = SpecExpectationNotMetError.new "failed"
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ exc = MSpecExampleError.new("boom!")
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ @formatter.unload(@state)
+ @out.should == "E"
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/html_spec.rb b/spec/mspec/spec/runner/formatters/html_spec.rb
new file mode 100644
index 0000000000..d2aff1b2a7
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/html_spec.rb
@@ -0,0 +1,217 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/utils/ruby_name'
+require 'mspec/guards/guard'
+require 'mspec/runner/formatters/html'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+require 'mspec/utils/script'
+
+describe HtmlFormatter do
+ before :each do
+ @formatter = HtmlFormatter.new
+ end
+
+ it "responds to #register by registering itself with MSpec for appropriate actions" do
+ MSpec.stub(:register)
+ MSpec.should_receive(:register).with(:start, @formatter)
+ MSpec.should_receive(:register).with(:enter, @formatter)
+ MSpec.should_receive(:register).with(:leave, @formatter)
+ @formatter.register
+ end
+end
+
+describe HtmlFormatter, "#start" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = HtmlFormatter.new
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints the HTML head" do
+ @formatter.start
+ ruby_name = RUBY_NAME
+ ruby_name.should =~ /^#{ruby_name}/
+ @out.should ==
+%[<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>Spec Output For #{ruby_name} (#{RUBY_VERSION})</title>
+<style type="text/css">
+ul {
+ list-style: none;
+}
+.fail {
+ color: red;
+}
+.pass {
+ color: green;
+}
+#details :target {
+ background-color: #ffffe0;
+}
+</style>
+</head>
+<body>
+]
+ end
+end
+
+describe HtmlFormatter, "#enter" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = HtmlFormatter.new
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints the #describe string" do
+ @formatter.enter "describe"
+ @out.should == "<div><p>describe</p>\n<ul>\n"
+ end
+end
+
+describe HtmlFormatter, "#leave" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = HtmlFormatter.new
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints the closing tags for the #describe string" do
+ @formatter.leave
+ @out.should == "</ul>\n</div>\n"
+ end
+end
+
+describe HtmlFormatter, "#exception" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = HtmlFormatter.new
+ @formatter.register
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints the #it string once for each exception raised" do
+ exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing")
+ @formatter.exception exc
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("painful")
+ @formatter.exception exc
+ @out.should ==
+%[<li class="fail">- it (<a href="#details-1">FAILED - 1</a>)</li>
+<li class="fail">- it (<a href="#details-2">ERROR - 2</a>)</li>
+]
+ end
+end
+
+describe HtmlFormatter, "#after" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = HtmlFormatter.new
+ @formatter.register
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints the #it once when there are no exceptions raised" do
+ @formatter.after @state
+ @out.should == %[<li class="pass">- it</li>\n]
+ end
+
+ it "does not print any output if an exception is raised" do
+ exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing")
+ @formatter.exception exc
+ out = @out.dup
+ @formatter.after @state
+ @out.should == out
+ end
+end
+
+describe HtmlFormatter, "#finish" do
+ before :each do
+ @tally = double("tally").as_null_object
+ TallyAction.stub(:new).and_return(@tally)
+ @timer = double("timer").as_null_object
+ TimerAction.stub(:new).and_return(@timer)
+
+ $stdout = @out = IOStub.new
+ context = ContextState.new "describe"
+ @state = ExampleState.new(context, "it")
+ MSpec.stub(:register)
+ @formatter = HtmlFormatter.new
+ @formatter.register
+ @exception = MSpecExampleError.new("broken")
+ @exception.stub(:backtrace).and_return(["file.rb:1", "file.rb:2"])
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints a failure message for an exception" do
+ exc = ExceptionState.new @state, nil, @exception
+ @formatter.exception exc
+ @formatter.finish
+ @out.should include "<p>describe it ERROR</p>"
+ end
+
+ it "prints a backtrace for an exception" do
+ exc = ExceptionState.new @state, nil, @exception
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+ @formatter.finish
+ @out.should =~ %r[<pre>.*path/to/some/file.rb:35:in method.*</pre>]m
+ end
+
+ it "prints a summary of elapsed time" do
+ @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
+ @formatter.finish
+ @out.should include "<p>Finished in 2.0 seconds</p>\n"
+ end
+
+ it "prints a tally of counts" do
+ @tally.should_receive(:format).and_return("1 example, 0 failures")
+ @formatter.finish
+ @out.should include '<p class="pass">1 example, 0 failures</p>'
+ end
+
+ it "prints errors, backtraces, elapsed time, and tallies" do
+ exc = ExceptionState.new @state, nil, @exception
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+
+ @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
+ @tally.should_receive(:format).and_return("1 example, 1 failures")
+ @formatter.finish
+ @out.should ==
+%[<li class=\"fail\">- it (<a href=\"#details-1\">ERROR - 1</a>)</li>
+<hr>
+<ol id="details">
+<li id="details-1"><p>describe it ERROR</p>
+<p>MSpecExampleError: broken</p>
+<pre>
+path/to/some/file.rb:35:in method</pre>
+</li>
+</ol>
+<p>Finished in 2.0 seconds</p>
+<p class="fail">1 example, 1 failures</p>
+</body>
+</html>
+]
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/junit_spec.rb b/spec/mspec/spec/runner/formatters/junit_spec.rb
new file mode 100644
index 0000000000..66e7d70e92
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/junit_spec.rb
@@ -0,0 +1,147 @@
+# -*- coding: utf-8 -*-
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/junit'
+require 'mspec/runner/example'
+
+describe JUnitFormatter, "#initialize" do
+ it "permits zero arguments" do
+ lambda { JUnitFormatter.new }.should_not raise_error
+ end
+
+ it "accepts one argument" do
+ lambda { JUnitFormatter.new nil }.should_not raise_error
+ end
+end
+
+describe JUnitFormatter, "#print" do
+ before :each do
+ $stdout = IOStub.new
+ @out = IOStub.new
+ File.stub(:open).and_return(@out)
+ @formatter = JUnitFormatter.new "some/file"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "writes to $stdout if #switch has not been called" do
+ @formatter.print "begonias"
+ $stdout.should == "begonias"
+ @out.should == ""
+ end
+
+ it "writes to the file passed to #initialize once #switch has been called" do
+ @formatter.switch
+ @formatter.print "begonias"
+ $stdout.should == ""
+ @out.should == "begonias"
+ end
+
+ it "writes to $stdout once #switch is called if no file was passed to #initialize" do
+ formatter = JUnitFormatter.new
+ formatter.switch
+ formatter.print "begonias"
+ $stdout.should == "begonias"
+ @out.should == ""
+ end
+end
+
+describe JUnitFormatter, "#finish" do
+ before :each do
+ @tally = double("tally").as_null_object
+ @counter = double("counter").as_null_object
+ @tally.stub(:counter).and_return(@counter)
+ TallyAction.stub(:new).and_return(@tally)
+
+ @timer = double("timer").as_null_object
+ TimerAction.stub(:new).and_return(@timer)
+
+ $stdout = IOStub.new
+ context = ContextState.new "describe"
+ @state = ExampleState.new(context, "it")
+
+ @formatter = JUnitFormatter.new
+ @formatter.stub(:backtrace).and_return("")
+ MSpec.stub(:register)
+ @formatter.register
+
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+ @formatter.after @state
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "calls #switch" do
+ @formatter.should_receive(:switch)
+ @formatter.finish
+ end
+
+ it "outputs a failure message and backtrace" do
+ @formatter.finish
+ $stdout.should include 'message="error in describe it" type="error"'
+ $stdout.should include "MSpecExampleError: broken\n"
+ $stdout.should include "path/to/some/file.rb:35:in method"
+ end
+
+ it "encodes message and backtrace in latin1 for jenkins" do
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken…")
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in methød")
+ @formatter.exception exc
+ @formatter.finish
+ $stdout.should =~ /MSpecExampleError: broken((\.\.\.)|\?)\n/
+ $stdout.should =~ /path\/to\/some\/file\.rb:35:in meth(\?|o)d/
+ end
+
+ it "outputs an elapsed time" do
+ @timer.should_receive(:elapsed).and_return(4.2)
+ @formatter.finish
+ $stdout.should include 'time="4.2"'
+ end
+
+ it "outputs overall elapsed time" do
+ @timer.should_receive(:elapsed).and_return(4.2)
+ @formatter.finish
+ $stdout.should include 'timeCount="4.2"'
+ end
+
+ it "outputs the number of examples as test count" do
+ @counter.should_receive(:examples).and_return(9)
+ @formatter.finish
+ $stdout.should include 'tests="9"'
+ end
+
+ it "outputs overall number of examples as test count" do
+ @counter.should_receive(:examples).and_return(9)
+ @formatter.finish
+ $stdout.should include 'testCount="9"'
+ end
+
+ it "outputs a failure count" do
+ @counter.should_receive(:failures).and_return(2)
+ @formatter.finish
+ $stdout.should include 'failureCount="2"'
+ end
+
+ it "outputs overall failure count" do
+ @counter.should_receive(:failures).and_return(2)
+ @formatter.finish
+ $stdout.should include 'failures="2"'
+ end
+
+ it "outputs an error count" do
+ @counter.should_receive(:errors).and_return(1)
+ @formatter.finish
+ $stdout.should include 'errors="1"'
+ end
+
+ it "outputs overall error count" do
+ @counter.should_receive(:errors).and_return(1)
+ @formatter.finish
+ $stdout.should include 'errorCount="1"'
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/method_spec.rb b/spec/mspec/spec/runner/formatters/method_spec.rb
new file mode 100644
index 0000000000..77204f74c5
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/method_spec.rb
@@ -0,0 +1,178 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/method'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+require 'mspec/utils/script'
+
+describe MethodFormatter, "#method_type" do
+ before :each do
+ @formatter = MethodFormatter.new
+ end
+
+ it "returns 'class' if the separator is '.' or '::'" do
+ @formatter.method_type('.').should == "class"
+ @formatter.method_type('::').should == "class"
+ end
+
+ it "returns 'instance' if the separator is '#'" do
+ @formatter.method_type('#').should == "instance"
+ end
+
+ it "returns 'unknown' for all other cases" do
+ @formatter.method_type(nil).should == "unknown"
+ end
+end
+
+describe MethodFormatter, "#before" do
+ before :each do
+ @formatter = MethodFormatter.new
+ MSpec.stub(:register)
+ @formatter.register
+ end
+
+ it "resets the tally counters to 0" do
+ @formatter.tally.counter.examples = 3
+ @formatter.tally.counter.expectations = 4
+ @formatter.tally.counter.failures = 2
+ @formatter.tally.counter.errors = 1
+
+ state = ExampleState.new ContextState.new("describe"), "it"
+ @formatter.before state
+ @formatter.tally.counter.examples.should == 0
+ @formatter.tally.counter.expectations.should == 0
+ @formatter.tally.counter.failures.should == 0
+ @formatter.tally.counter.errors.should == 0
+ end
+
+ it "records the class, method if available" do
+ state = ExampleState.new ContextState.new("Some#method"), "it"
+ @formatter.before state
+ key = "Some#method"
+ @formatter.methods.keys.should include(key)
+ h = @formatter.methods[key]
+ h[:class].should == "Some"
+ h[:method].should == "method"
+ h[:description].should == "Some#method it"
+ end
+
+ it "does not record class, method unless both are available" do
+ state = ExampleState.new ContextState.new("Some method"), "it"
+ @formatter.before state
+ key = "Some method"
+ @formatter.methods.keys.should include(key)
+ h = @formatter.methods[key]
+ h[:class].should == ""
+ h[:method].should == ""
+ h[:description].should == "Some method it"
+ end
+
+ it "sets the method type to unknown if class and method are not available" do
+ state = ExampleState.new ContextState.new("Some method"), "it"
+ @formatter.before state
+ key = "Some method"
+ h = @formatter.methods[key]
+ h[:type].should == "unknown"
+ end
+
+ it "sets the method type based on the class, method separator" do
+ [["C#m", "instance"], ["C.m", "class"], ["C::m", "class"]].each do |k, t|
+ state = ExampleState.new ContextState.new(k), "it"
+ @formatter.before state
+ h = @formatter.methods[k]
+ h[:type].should == t
+ end
+ end
+
+ it "clears the list of exceptions" do
+ state = ExampleState.new ContextState.new("describe"), "it"
+ @formatter.exceptions << "stuff"
+ @formatter.before state
+ @formatter.exceptions.should be_empty
+ end
+end
+
+describe MethodFormatter, "#after" do
+ before :each do
+ @formatter = MethodFormatter.new
+ MSpec.stub(:register)
+ @formatter.register
+ end
+
+ it "sets the tally counts" do
+ state = ExampleState.new ContextState.new("Some#method"), "it"
+ @formatter.before state
+
+ @formatter.tally.counter.examples = 3
+ @formatter.tally.counter.expectations = 4
+ @formatter.tally.counter.failures = 2
+ @formatter.tally.counter.errors = 1
+
+ @formatter.after state
+ h = @formatter.methods["Some#method"]
+ h[:examples].should == 3
+ h[:expectations].should == 4
+ h[:failures].should == 2
+ h[:errors].should == 1
+ end
+
+ it "renders the list of exceptions" do
+ state = ExampleState.new ContextState.new("Some#method"), "it"
+ @formatter.before state
+
+ exc = SpecExpectationNotMetError.new "failed"
+ @formatter.exception ExceptionState.new(state, nil, exc)
+ @formatter.exception ExceptionState.new(state, nil, exc)
+
+ @formatter.after state
+ h = @formatter.methods["Some#method"]
+ h[:exceptions].should == [
+ %[failed\n\n],
+ %[failed\n\n]
+ ]
+ end
+end
+
+describe MethodFormatter, "#after" do
+ before :each do
+ $stdout = IOStub.new
+ context = ContextState.new "Class#method"
+ @state = ExampleState.new(context, "runs")
+ @formatter = MethodFormatter.new
+ MSpec.stub(:register)
+ @formatter.register
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints a summary of the results of an example in YAML format" do
+ @formatter.before @state
+ @formatter.tally.counter.examples = 3
+ @formatter.tally.counter.expectations = 4
+ @formatter.tally.counter.failures = 2
+ @formatter.tally.counter.errors = 1
+
+ exc = SpecExpectationNotMetError.new "failed"
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+ @formatter.exception ExceptionState.new(@state, nil, exc)
+
+ @formatter.after @state
+ @formatter.finish
+ $stdout.should ==
+%[---
+"Class#method":
+ class: "Class"
+ method: "method"
+ type: instance
+ description: "Class#method runs"
+ examples: 3
+ expectations: 4
+ failures: 2
+ errors: 1
+ exceptions:
+ - "failed\\n\\n"
+ - "failed\\n\\n"
+]
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/multi_spec.rb b/spec/mspec/spec/runner/formatters/multi_spec.rb
new file mode 100644
index 0000000000..afcc7e9cea
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/multi_spec.rb
@@ -0,0 +1,68 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/multi'
+require 'mspec/runner/example'
+
+describe MultiFormatter, "#aggregate_results" do
+ before :each do
+ @stdout, $stdout = $stdout, IOStub.new
+
+ @file = double("file").as_null_object
+
+ File.stub(:delete)
+ YAML.stub(:load)
+
+ @hash = { "files"=>1, "examples"=>1, "expectations"=>2, "failures"=>0, "errors"=>0 }
+ File.stub(:open).and_yield(@file).and_return(@hash)
+
+ @formatter = MultiFormatter.new
+ @formatter.timer.stub(:format).and_return("Finished in 42 seconds")
+ end
+
+ after :each do
+ $stdout = @stdout
+ end
+
+ it "outputs a summary without errors" do
+ @formatter.aggregate_results(["a", "b"])
+ @formatter.finish
+ $stdout.should ==
+%[
+
+Finished in 42 seconds
+
+2 files, 2 examples, 4 expectations, 0 failures, 0 errors, 0 tagged
+]
+ end
+
+ it "outputs a summary with errors" do
+ @hash["exceptions"] = [
+ "Some#method works real good FAILED\nExpected real good\n to equal fail\n\nfoo.rb:1\nfoo.rb:2",
+ "Some#method never fails ERROR\nExpected 5\n to equal 3\n\nfoo.rb:1\nfoo.rb:2"
+ ]
+ @formatter.aggregate_results(["a"])
+ @formatter.finish
+ $stdout.should ==
+%[
+
+1)
+Some#method works real good FAILED
+Expected real good
+ to equal fail
+
+foo.rb:1
+foo.rb:2
+
+2)
+Some#method never fails ERROR
+Expected 5
+ to equal 3
+
+foo.rb:1
+foo.rb:2
+
+Finished in 42 seconds
+
+1 file, 1 example, 2 expectations, 0 failures, 0 errors, 0 tagged
+]
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/specdoc_spec.rb b/spec/mspec/spec/runner/formatters/specdoc_spec.rb
new file mode 100644
index 0000000000..edb439fc11
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/specdoc_spec.rb
@@ -0,0 +1,106 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/specdoc'
+require 'mspec/runner/example'
+
+describe SpecdocFormatter do
+ before :each do
+ @formatter = SpecdocFormatter.new
+ end
+
+ it "responds to #register by registering itself with MSpec for appropriate actions" do
+ MSpec.stub(:register)
+ MSpec.should_receive(:register).with(:enter, @formatter)
+ @formatter.register
+ end
+end
+
+describe SpecdocFormatter, "#enter" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = SpecdocFormatter.new
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints the #describe string" do
+ @formatter.enter("describe")
+ @out.should == "\ndescribe\n"
+ end
+end
+
+describe SpecdocFormatter, "#before" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = SpecdocFormatter.new
+ @state = ExampleState.new ContextState.new("describe"), "it"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints the #it string" do
+ @formatter.before @state
+ @out.should == "- it"
+ end
+
+ it "resets the #exception? flag" do
+ exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing")
+ @formatter.exception exc
+ @formatter.exception?.should be_true
+ @formatter.before @state
+ @formatter.exception?.should be_false
+ end
+end
+
+describe SpecdocFormatter, "#exception" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = SpecdocFormatter.new
+ context = ContextState.new "describe"
+ @state = ExampleState.new context, "it"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints 'ERROR' if an exception is not an SpecExpectationNotMetError" do
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("painful")
+ @formatter.exception exc
+ @out.should == " (ERROR - 1)"
+ end
+
+ it "prints 'FAILED' if an exception is an SpecExpectationNotMetError" do
+ exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing")
+ @formatter.exception exc
+ @out.should == " (FAILED - 1)"
+ end
+
+ it "prints the #it string if an exception has already been raised" do
+ exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing")
+ @formatter.exception exc
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("painful")
+ @formatter.exception exc
+ @out.should == " (FAILED - 1)\n- it (ERROR - 2)"
+ end
+end
+
+describe SpecdocFormatter, "#after" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = SpecdocFormatter.new
+ @state = ExampleState.new "describe", "it"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints a newline character" do
+ @formatter.after @state
+ @out.should == "\n"
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/spinner_spec.rb b/spec/mspec/spec/runner/formatters/spinner_spec.rb
new file mode 100644
index 0000000000..a122620e39
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/spinner_spec.rb
@@ -0,0 +1,83 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/spinner'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+
+describe SpinnerFormatter, "#initialize" do
+ it "permits zero arguments" do
+ SpinnerFormatter.new
+ end
+
+ it "accepts one argument" do
+ SpinnerFormatter.new nil
+ end
+end
+
+describe SpinnerFormatter, "#register" do
+ before :each do
+ @formatter = SpinnerFormatter.new
+ MSpec.stub(:register)
+ end
+
+ it "registers self with MSpec for appropriate actions" do
+ MSpec.should_receive(:register).with(:start, @formatter)
+ MSpec.should_receive(:register).with(:unload, @formatter)
+ MSpec.should_receive(:register).with(:after, @formatter)
+ MSpec.should_receive(:register).with(:finish, @formatter)
+ @formatter.register
+ end
+
+ it "creates TimerAction and TallyAction" do
+ timer = double("timer")
+ tally = double("tally")
+ timer.should_receive(:register)
+ tally.should_receive(:register)
+ tally.should_receive(:counter)
+ TimerAction.should_receive(:new).and_return(timer)
+ TallyAction.should_receive(:new).and_return(tally)
+ @formatter.register
+ end
+end
+
+describe SpinnerFormatter, "#print" do
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "ignores the argument to #initialize and writes to $stdout" do
+ $stdout = IOStub.new
+ formatter = SpinnerFormatter.new "some/file"
+ formatter.print "begonias"
+ $stdout.should == "begonias"
+ end
+end
+
+describe SpinnerFormatter, "#after" do
+ before :each do
+ $stdout = IOStub.new
+ MSpec.store(:files, ["a", "b", "c", "d"])
+ @formatter = SpinnerFormatter.new
+ @formatter.register
+ @state = ExampleState.new("describe", "it")
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "updates the spinner" do
+ @formatter.start
+ @formatter.after @state
+ @formatter.unload
+
+ if ENV["TERM"] != "dumb"
+ green = "\e[0;32m"
+ reset = "\e[0m"
+ end
+
+ output = "\r[/ | 0% | 00:00:00] #{green} 0F #{green} 0E#{reset} " \
+ "\r[- | 0% | 00:00:00] #{green} 0F #{green} 0E#{reset} " \
+ "\r[\\ | ========== 25% | 00:00:00] #{green} 0F #{green} 0E#{reset} "
+ $stdout.should == output
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/summary_spec.rb b/spec/mspec/spec/runner/formatters/summary_spec.rb
new file mode 100644
index 0000000000..16a156b695
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/summary_spec.rb
@@ -0,0 +1,26 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/summary'
+require 'mspec/runner/example'
+
+describe SummaryFormatter, "#after" do
+ before :each do
+ $stdout = @out = IOStub.new
+ @formatter = SummaryFormatter.new
+ @formatter.register
+ context = ContextState.new "describe"
+ @state = ExampleState.new(context, "it")
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "does not print anything" do
+ exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing")
+ @formatter.exception exc
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("painful")
+ @formatter.exception exc
+ @formatter.after(@state)
+ @out.should == ""
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/unit_spec.rb b/spec/mspec/spec/runner/formatters/unit_spec.rb
new file mode 100644
index 0000000000..c8ba406f51
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/unit_spec.rb
@@ -0,0 +1,74 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/unit'
+require 'mspec/runner/example'
+require 'mspec/utils/script'
+
+describe UnitdiffFormatter, "#finish" do
+ before :each do
+ @tally = double("tally").as_null_object
+ TallyAction.stub(:new).and_return(@tally)
+ @timer = double("timer").as_null_object
+ TimerAction.stub(:new).and_return(@timer)
+
+ $stdout = @out = IOStub.new
+ context = ContextState.new "describe"
+ @state = ExampleState.new(context, "it")
+ MSpec.stub(:register)
+ @formatter = UnitdiffFormatter.new
+ @formatter.register
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "prints a failure message for an exception" do
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
+ @formatter.exception exc
+ @formatter.after @state
+ @formatter.finish
+ @out.should =~ /^1\)\ndescribe it ERROR$/
+ end
+
+ it "prints a backtrace for an exception" do
+ exc = ExceptionState.new @state, nil, Exception.new("broken")
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+ @formatter.finish
+ @out.should =~ %r[path/to/some/file.rb:35:in method$]
+ end
+
+ it "prints a summary of elapsed time" do
+ @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
+ @formatter.finish
+ @out.should =~ /^Finished in 2.0 seconds$/
+ end
+
+ it "prints a tally of counts" do
+ @tally.should_receive(:format).and_return("1 example, 0 failures")
+ @formatter.finish
+ @out.should =~ /^1 example, 0 failures$/
+ end
+
+ it "prints errors, backtraces, elapsed time, and tallies" do
+ exc = ExceptionState.new @state, nil, Exception.new("broken")
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+ @formatter.after @state
+ @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
+ @tally.should_receive(:format).and_return("1 example, 0 failures")
+ @formatter.finish
+ @out.should ==
+%[E
+
+Finished in 2.0 seconds
+
+1)
+describe it ERROR
+Exception: broken:
+path/to/some/file.rb:35:in method
+
+1 example, 0 failures
+]
+ end
+end
diff --git a/spec/mspec/spec/runner/formatters/yaml_spec.rb b/spec/mspec/spec/runner/formatters/yaml_spec.rb
new file mode 100644
index 0000000000..eb4d99f74c
--- /dev/null
+++ b/spec/mspec/spec/runner/formatters/yaml_spec.rb
@@ -0,0 +1,125 @@
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'mspec/runner/formatters/yaml'
+require 'mspec/runner/example'
+
+describe YamlFormatter, "#initialize" do
+ it "permits zero arguments" do
+ YamlFormatter.new
+ end
+
+ it "accepts one argument" do
+ YamlFormatter.new nil
+ end
+end
+
+describe YamlFormatter, "#print" do
+ before :each do
+ $stdout = IOStub.new
+ @out = IOStub.new
+ File.stub(:open).and_return(@out)
+ @formatter = YamlFormatter.new "some/file"
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "writes to $stdout if #switch has not been called" do
+ @formatter.print "begonias"
+ $stdout.should == "begonias"
+ @out.should == ""
+ end
+
+ it "writes to the file passed to #initialize once #switch has been called" do
+ @formatter.switch
+ @formatter.print "begonias"
+ $stdout.should == ""
+ @out.should == "begonias"
+ end
+
+ it "writes to $stdout once #switch is called if no file was passed to #initialize" do
+ formatter = YamlFormatter.new
+ formatter.switch
+ formatter.print "begonias"
+ $stdout.should == "begonias"
+ @out.should == ""
+ end
+end
+
+describe YamlFormatter, "#finish" do
+ before :each do
+ @tally = double("tally").as_null_object
+ @counter = double("counter").as_null_object
+ @tally.stub(:counter).and_return(@counter)
+ TallyAction.stub(:new).and_return(@tally)
+
+ @timer = double("timer").as_null_object
+ TimerAction.stub(:new).and_return(@timer)
+
+ $stdout = IOStub.new
+ context = ContextState.new "describe"
+ @state = ExampleState.new(context, "it")
+
+ @formatter = YamlFormatter.new
+ @formatter.stub(:backtrace).and_return("")
+ MSpec.stub(:register)
+ @formatter.register
+
+ exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
+ exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ @formatter.exception exc
+ @formatter.after @state
+ end
+
+ after :each do
+ $stdout = STDOUT
+ end
+
+ it "calls #switch" do
+ @formatter.should_receive(:switch)
+ @formatter.finish
+ end
+
+ it "outputs a failure message and backtrace" do
+ @formatter.finish
+ $stdout.should include "describe it ERROR"
+ $stdout.should include "MSpecExampleError: broken\\n"
+ $stdout.should include "path/to/some/file.rb:35:in method"
+ end
+
+ it "outputs an elapsed time" do
+ @timer.should_receive(:elapsed).and_return(4.2)
+ @formatter.finish
+ $stdout.should include "time: 4.2"
+ end
+
+ it "outputs a file count" do
+ @counter.should_receive(:files).and_return(3)
+ @formatter.finish
+ $stdout.should include "files: 3"
+ end
+
+ it "outputs an example count" do
+ @counter.should_receive(:examples).and_return(3)
+ @formatter.finish
+ $stdout.should include "examples: 3"
+ end
+
+ it "outputs an expectation count" do
+ @counter.should_receive(:expectations).and_return(9)
+ @formatter.finish
+ $stdout.should include "expectations: 9"
+ end
+
+ it "outputs a failure count" do
+ @counter.should_receive(:failures).and_return(2)
+ @formatter.finish
+ $stdout.should include "failures: 2"
+ end
+
+ it "outputs an error count" do
+ @counter.should_receive(:errors).and_return(1)
+ @formatter.finish
+ $stdout.should include "errors: 1"
+ end
+end
diff --git a/spec/mspec/spec/runner/mspec_spec.rb b/spec/mspec/spec/runner/mspec_spec.rb
new file mode 100644
index 0000000000..9b8142414e
--- /dev/null
+++ b/spec/mspec/spec/runner/mspec_spec.rb
@@ -0,0 +1,595 @@
+require 'spec_helper'
+require 'mspec/helpers/tmp'
+require 'mspec/helpers/fs'
+require 'mspec/matchers/base'
+require 'mspec/runner/mspec'
+require 'mspec/runner/example'
+
+describe MSpec, ".register_files" do
+ it "records which spec files to run" do
+ MSpec.register_files [:one, :two, :three]
+ MSpec.retrieve(:files).should == [:one, :two, :three]
+ end
+end
+
+describe MSpec, ".register_mode" do
+ before :each do
+ MSpec.clear_modes
+ end
+
+ it "sets execution mode flags" do
+ MSpec.register_mode :verify
+ MSpec.retrieve(:modes).should == [:verify]
+ end
+end
+
+describe MSpec, ".register_tags_patterns" do
+ it "records the patterns for generating a tag file from a spec file" do
+ MSpec.register_tags_patterns [[/spec\/ruby/, "spec/tags"], [/frozen/, "ruby"]]
+ MSpec.retrieve(:tags_patterns).should == [[/spec\/ruby/, "spec/tags"], [/frozen/, "ruby"]]
+ end
+end
+
+describe MSpec, ".register_exit" do
+ before :each do
+ MSpec.store :exit, 0
+ end
+
+ it "records the exit code" do
+ MSpec.exit_code.should == 0
+ MSpec.register_exit 1
+ MSpec.exit_code.should == 1
+ end
+end
+
+describe MSpec, ".exit_code" do
+ it "retrieves the code set with .register_exit" do
+ MSpec.register_exit 99
+ MSpec.exit_code.should == 99
+ end
+end
+
+describe MSpec, ".store" do
+ it "records data for MSpec settings" do
+ MSpec.store :anything, :value
+ MSpec.retrieve(:anything).should == :value
+ end
+end
+
+describe MSpec, ".retrieve" do
+ it "accesses .store'd data" do
+ MSpec.register :action, :first
+ MSpec.retrieve(:action).should == [:first]
+ end
+end
+
+describe MSpec, ".randomize" do
+ it "sets the flag to randomize spec execution order" do
+ MSpec.randomize?.should == false
+ MSpec.randomize
+ MSpec.randomize?.should == true
+ MSpec.randomize false
+ MSpec.randomize?.should == false
+ end
+end
+
+describe MSpec, ".register" do
+ it "is the gateway behind the register(symbol, action) facility" do
+ MSpec.register :bonus, :first
+ MSpec.register :bonus, :second
+ MSpec.register :bonus, :second
+ MSpec.retrieve(:bonus).should == [:first, :second]
+ end
+end
+
+describe MSpec, ".unregister" do
+ it "is the gateway behind the unregister(symbol, actions) facility" do
+ MSpec.register :unregister, :first
+ MSpec.register :unregister, :second
+ MSpec.unregister :unregister, :second
+ MSpec.retrieve(:unregister).should == [:first]
+ end
+end
+
+describe MSpec, ".protect" do
+ before :each do
+ MSpec.clear_current
+ @cs = ContextState.new "C#m"
+ @cs.parent = MSpec.current
+
+ @es = ExampleState.new @cs, "runs"
+ ScratchPad.record Exception.new("Sharp!")
+ end
+
+ it "returns true if no exception is raised" do
+ MSpec.protect("passed") { 1 }.should be_true
+ end
+
+ it "returns false if an exception is raised" do
+ MSpec.protect("testing") { raise ScratchPad.recorded }.should be_false
+ end
+
+ it "rescues any exceptions raised when evaluating the block argument" do
+ MSpec.protect("") { raise Exception, "Now you see me..." }
+ end
+
+ it "does not rescue SystemExit" do
+ begin
+ MSpec.protect("") { exit 1 }
+ rescue SystemExit
+ ScratchPad.record :system_exit
+ end
+ ScratchPad.recorded.should == :system_exit
+ end
+
+ it "calls all the exception actions" do
+ exc = ExceptionState.new @es, "testing", ScratchPad.recorded
+ ExceptionState.stub(:new).and_return(exc)
+ action = double("exception")
+ action.should_receive(:exception).with(exc)
+ MSpec.register :exception, action
+ MSpec.protect("testing") { raise ScratchPad.recorded }
+ MSpec.unregister :exception, action
+ end
+
+ it "registers a non-zero exit code when an exception is raised" do
+ MSpec.should_receive(:register_exit).with(1)
+ MSpec.protect("testing") { raise ScratchPad.recorded }
+ end
+end
+
+describe MSpec, ".register_current" do
+ before :each do
+ MSpec.clear_current
+ end
+
+ it "sets the value returned by MSpec.current" do
+ MSpec.current.should be_nil
+ MSpec.register_current :a
+ MSpec.current.should == :a
+ end
+end
+
+describe MSpec, ".clear_current" do
+ it "sets the value returned by MSpec.current to nil" do
+ MSpec.register_current :a
+ MSpec.current.should_not be_nil
+ MSpec.clear_current
+ MSpec.current.should be_nil
+ end
+end
+
+describe MSpec, ".current" do
+ before :each do
+ MSpec.clear_current
+ end
+
+ it "returns nil if no ContextState has been registered" do
+ MSpec.current.should be_nil
+ end
+
+ it "returns the most recently registered ContextState" do
+ first = ContextState.new ""
+ second = ContextState.new ""
+ MSpec.register_current first
+ MSpec.current.should == first
+ MSpec.register_current second
+ MSpec.current.should == second
+ end
+end
+
+describe MSpec, ".actions" do
+ before :each do
+ MSpec.store :start, []
+ ScratchPad.record []
+ start_one = double("one")
+ start_one.stub(:start).and_return { ScratchPad << :one }
+ start_two = double("two")
+ start_two.stub(:start).and_return { ScratchPad << :two }
+ MSpec.register :start, start_one
+ MSpec.register :start, start_two
+ end
+
+ it "does not attempt to run any actions if none have been registered" do
+ MSpec.store :finish, nil
+ lambda { MSpec.actions :finish }.should_not raise_error
+ end
+
+ it "runs each action registered as a start action" do
+ MSpec.actions :start
+ ScratchPad.recorded.should == [:one, :two]
+ end
+end
+
+describe MSpec, ".mode?" do
+ before :each do
+ MSpec.clear_modes
+ end
+
+ it "returns true if the mode has been set" do
+ MSpec.mode?(:verify).should == false
+ MSpec.register_mode :verify
+ MSpec.mode?(:verify).should == true
+ end
+end
+
+describe MSpec, ".clear_modes" do
+ it "clears all registered modes" do
+ MSpec.register_mode(:pretend)
+ MSpec.register_mode(:verify)
+
+ MSpec.mode?(:pretend).should == true
+ MSpec.mode?(:verify).should == true
+
+ MSpec.clear_modes
+
+ MSpec.mode?(:pretend).should == false
+ MSpec.mode?(:verify).should == false
+ end
+end
+
+describe MSpec, ".guarded?" do
+ before :each do
+ MSpec.instance_variable_set :@guarded, []
+ end
+
+ it "returns false if no guard has run" do
+ MSpec.guarded?.should == false
+ end
+
+ it "returns true if a single guard has run" do
+ MSpec.guard
+ MSpec.guarded?.should == true
+ end
+
+ it "returns true if more than one guard has run" do
+ MSpec.guard
+ MSpec.guard
+ MSpec.guarded?.should == true
+ end
+
+ it "returns true until all guards have finished" do
+ MSpec.guard
+ MSpec.guard
+ MSpec.guarded?.should == true
+ MSpec.unguard
+ MSpec.guarded?.should == true
+ MSpec.unguard
+ MSpec.guarded?.should == false
+ end
+end
+
+describe MSpec, ".describe" do
+ before :each do
+ MSpec.clear_current
+ @cs = ContextState.new ""
+ ContextState.stub(:new).and_return(@cs)
+ MSpec.stub(:current).and_return(nil)
+ MSpec.stub(:register_current)
+ end
+
+ it "creates a new ContextState for the block" do
+ ContextState.should_receive(:new).and_return(@cs)
+ MSpec.describe(Object) { }
+ end
+
+ it "accepts an optional second argument" do
+ ContextState.should_receive(:new).and_return(@cs)
+ MSpec.describe(Object, "msg") { }
+ end
+
+ it "registers the newly created ContextState" do
+ MSpec.should_receive(:register_current).with(@cs).twice
+ MSpec.describe(Object) { }
+ end
+
+ it "invokes the ContextState#describe method" do
+ prc = lambda { }
+ @cs.should_receive(:describe).with(&prc)
+ MSpec.describe(Object, "msg", &prc)
+ end
+end
+
+describe MSpec, ".process" do
+ before :each do
+ MSpec.stub(:files)
+ MSpec.store :start, []
+ MSpec.store :finish, []
+ STDOUT.stub(:puts)
+ end
+
+ it "prints the RUBY_DESCRIPTION" do
+ STDOUT.should_receive(:puts).with(RUBY_DESCRIPTION)
+ MSpec.process
+ end
+
+ it "calls all start actions" do
+ start = double("start")
+ start.stub(:start).and_return { ScratchPad.record :start }
+ MSpec.register :start, start
+ MSpec.process
+ ScratchPad.recorded.should == :start
+ end
+
+ it "calls all finish actions" do
+ finish = double("finish")
+ finish.stub(:finish).and_return { ScratchPad.record :finish }
+ MSpec.register :finish, finish
+ MSpec.process
+ ScratchPad.recorded.should == :finish
+ end
+
+ it "calls the files method" do
+ MSpec.should_receive(:files)
+ MSpec.process
+ end
+end
+
+describe MSpec, ".files" do
+ before :each do
+ MSpec.store :load, []
+ MSpec.store :unload, []
+ MSpec.register_files [:one, :two, :three]
+ Kernel.stub(:load)
+ end
+
+ it "calls load actions before each file" do
+ load = double("load")
+ load.stub(:load).and_return { ScratchPad.record :load }
+ MSpec.register :load, load
+ MSpec.files
+ ScratchPad.recorded.should == :load
+ end
+
+ it "shuffles the file list if .randomize? is true" do
+ MSpec.randomize
+ MSpec.should_receive(:shuffle)
+ MSpec.files
+ MSpec.randomize false
+ end
+
+ it "registers the current file" do
+ MSpec.should_receive(:store).with(:file, :one)
+ MSpec.should_receive(:store).with(:file, :two)
+ MSpec.should_receive(:store).with(:file, :three)
+ MSpec.files
+ end
+end
+
+describe MSpec, ".shuffle" do
+ before :each do
+ @base = (0..100).to_a
+ @list = @base.clone
+ MSpec.shuffle @list
+ end
+
+ it "does not alter the elements in the list" do
+ @base.each do |elt|
+ @list.should include(elt)
+ end
+ end
+
+ it "changes the order of the list" do
+ # obviously, this spec has a certain probability
+ # of failing. If it fails, run it again.
+ @list.should_not == @base
+ end
+end
+
+describe MSpec, ".tags_file" do
+ before :each do
+ MSpec.store :file, "path/to/spec/something/some_spec.rb"
+ MSpec.store :tags_patterns, nil
+ end
+
+ it "returns the default tags file for the current spec file" do
+ MSpec.tags_file.should == "path/to/spec/tags/something/some_tags.txt"
+ end
+
+ it "returns the tags file for the current spec file with custom tags_patterns" do
+ MSpec.register_tags_patterns [[/^(.*)\/spec/, '\1/tags'], [/_spec.rb/, "_tags.txt"]]
+ MSpec.tags_file.should == "path/to/tags/something/some_tags.txt"
+ end
+
+ it "performs multiple substitutions" do
+ MSpec.register_tags_patterns [
+ [%r(/spec/something/), "/spec/other/"],
+ [%r(/spec/), "/spec/tags/"],
+ [/_spec.rb/, "_tags.txt"]
+ ]
+ MSpec.tags_file.should == "path/to/spec/tags/other/some_tags.txt"
+ end
+
+ it "handles cases where no substitution is performed" do
+ MSpec.register_tags_patterns [[/nothing/, "something"]]
+ MSpec.tags_file.should == "path/to/spec/something/some_spec.rb"
+ end
+end
+
+describe MSpec, ".read_tags" do
+ before :each do
+ MSpec.stub(:tags_file).and_return(File.dirname(__FILE__) + '/tags.txt')
+ end
+
+ it "returns a list of tag instances for matching tag names found" do
+ one = SpecTag.new "fail(broken):Some#method? works"
+ MSpec.read_tags(["fail", "pass"]).should == [one]
+ end
+
+ it "returns [] if no tags names match" do
+ MSpec.read_tags("super").should == []
+ end
+end
+
+describe MSpec, ".read_tags" do
+ before :each do
+ @tag = SpecTag.new "fails:Some#method"
+ File.open(tmp("tags.txt", false), "w") do |f|
+ f.puts ""
+ f.puts @tag
+ f.puts ""
+ end
+ MSpec.stub(:tags_file).and_return(tmp("tags.txt", false))
+ end
+
+ it "does not return a tag object for empty lines" do
+ MSpec.read_tags(["fails"]).should == [@tag]
+ end
+end
+
+describe MSpec, ".write_tags" do
+ before :each do
+ FileUtils.cp File.dirname(__FILE__) + "/tags.txt", tmp("tags.txt", false)
+ MSpec.stub(:tags_file).and_return(tmp("tags.txt", false))
+ @tag1 = SpecTag.new "check(broken):Tag#rewrite works"
+ @tag2 = SpecTag.new "broken:Tag#write_tags fails"
+ end
+
+ after :all do
+ rm_r tmp("tags.txt", false)
+ end
+
+ it "overwrites the tags in the tag file" do
+ IO.read(tmp("tags.txt", false)).should == %[fail(broken):Some#method? works
+incomplete(20%):The#best method ever
+benchmark(0.01825):The#fastest method today
+extended():\"Multi-line\\ntext\\ntag\"
+]
+ MSpec.write_tags [@tag1, @tag2]
+ IO.read(tmp("tags.txt", false)).should == %[check(broken):Tag#rewrite works
+broken:Tag#write_tags fails
+]
+ end
+end
+
+describe MSpec, ".write_tag" do
+ before :each do
+ FileUtils.stub(:mkdir_p)
+ MSpec.stub(:tags_file).and_return(tmp("tags.txt", false))
+ @tag = SpecTag.new "fail(broken):Some#method works"
+ end
+
+ after :all do
+ rm_r tmp("tags.txt", false)
+ end
+
+ it "writes a tag to the tags file for the current spec file" do
+ MSpec.write_tag @tag
+ IO.read(tmp("tags.txt", false)).should == "fail(broken):Some#method works\n"
+ end
+
+ it "does not write a duplicate tag" do
+ File.open(tmp("tags.txt", false), "w") { |f| f.puts @tag }
+ MSpec.write_tag @tag
+ IO.read(tmp("tags.txt", false)).should == "fail(broken):Some#method works\n"
+ end
+end
+
+describe MSpec, ".delete_tag" do
+ before :each do
+ FileUtils.cp File.dirname(__FILE__) + "/tags.txt", tmp("tags.txt", false)
+ MSpec.stub(:tags_file).and_return(tmp("tags.txt", false))
+ @tag = SpecTag.new "fail(Comments don't matter):Some#method? works"
+ end
+
+ after :each do
+ rm_r tmp("tags.txt", false)
+ end
+
+ it "deletes the tag if it exists" do
+ MSpec.delete_tag(@tag).should == true
+ IO.read(tmp("tags.txt", false)).should == %[incomplete(20%):The#best method ever
+benchmark(0.01825):The#fastest method today
+extended():\"Multi-line\\ntext\\ntag\"
+]
+ end
+
+ it "deletes a tag with escaped newlines" do
+ MSpec.delete_tag(SpecTag.new('extended:"Multi-line\ntext\ntag"')).should == true
+ IO.read(tmp("tags.txt", false)).should == %[fail(broken):Some#method? works
+incomplete(20%):The#best method ever
+benchmark(0.01825):The#fastest method today
+]
+ end
+
+ it "does not change the tags file contents if the tag doesn't exist" do
+ @tag.tag = "failed"
+ MSpec.delete_tag(@tag).should == false
+ IO.read(tmp("tags.txt", false)).should == %[fail(broken):Some#method? works
+incomplete(20%):The#best method ever
+benchmark(0.01825):The#fastest method today
+extended():\"Multi-line\\ntext\\ntag\"
+]
+ end
+
+ it "deletes the tag file if it is empty" do
+ MSpec.delete_tag(@tag).should == true
+ MSpec.delete_tag(SpecTag.new("incomplete:The#best method ever")).should == true
+ MSpec.delete_tag(SpecTag.new("benchmark:The#fastest method today")).should == true
+ MSpec.delete_tag(SpecTag.new('extended:"Multi-line\ntext\ntag"')).should == true
+ File.exist?(tmp("tags.txt", false)).should == false
+ end
+end
+
+describe MSpec, ".delete_tags" do
+ before :each do
+ @tags = tmp("tags.txt", false)
+ FileUtils.cp File.dirname(__FILE__) + "/tags.txt", @tags
+ MSpec.stub(:tags_file).and_return(@tags)
+ end
+
+ it "deletes the tag file" do
+ MSpec.delete_tags
+ File.exist?(@tags).should be_false
+ end
+end
+
+describe MSpec, ".expectation" do
+ it "sets the flag that an expectation has been reported" do
+ MSpec.clear_expectations
+ MSpec.expectation?.should be_false
+ MSpec.expectation
+ MSpec.expectation?.should be_true
+ end
+end
+
+describe MSpec, ".expectation?" do
+ it "returns true if an expectation has been reported" do
+ MSpec.expectation
+ MSpec.expectation?.should be_true
+ end
+
+ it "returns false if an expectation has not been reported" do
+ MSpec.clear_expectations
+ MSpec.expectation?.should be_false
+ end
+end
+
+describe MSpec, ".clear_expectations" do
+ it "clears the flag that an expectation has been reported" do
+ MSpec.expectation
+ MSpec.expectation?.should be_true
+ MSpec.clear_expectations
+ MSpec.expectation?.should be_false
+ end
+end
+
+describe MSpec, ".register_shared" do
+ it "stores a shared ContextState by description" do
+ parent = ContextState.new "container"
+ state = ContextState.new "shared"
+ state.parent = parent
+ prc = lambda { }
+ state.describe(&prc)
+ MSpec.register_shared(state)
+ MSpec.retrieve(:shared)["shared"].should == state
+ end
+end
+
+describe MSpec, ".retrieve_shared" do
+ it "retrieves the shared ContextState matching description" do
+ state = ContextState.new ""
+ MSpec.retrieve(:shared)["shared"] = state
+ MSpec.retrieve_shared(:shared).should == state
+ end
+end
diff --git a/spec/mspec/spec/runner/shared_spec.rb b/spec/mspec/spec/runner/shared_spec.rb
new file mode 100644
index 0000000000..791588fdca
--- /dev/null
+++ b/spec/mspec/spec/runner/shared_spec.rb
@@ -0,0 +1,88 @@
+require 'spec_helper'
+require 'mspec/runner/shared'
+require 'mspec/runner/context'
+require 'mspec/runner/example'
+
+describe Object, "#it_behaves_like" do
+ before :each do
+ ScratchPad.clear
+
+ MSpec.setup_env
+
+ @state = ContextState.new "Top level"
+ @state.instance_variable_set :@parsed, true
+
+ @shared = ContextState.new :shared_spec, :shared => true
+ MSpec.stub(:retrieve_shared).and_return(@shared)
+ end
+
+ it "creates @method set to the name of the aliased method" do
+ @shared.it("an example") { ScratchPad.record @method }
+ @state.it_behaves_like :shared_spec, :some_method
+ @state.process
+ ScratchPad.recorded.should == :some_method
+ end
+
+ it "creates @object if the passed object" do
+ object = Object.new
+ @shared.it("an example") { ScratchPad.record @object }
+ @state.it_behaves_like :shared_spec, :some_method, object
+ @state.process
+ ScratchPad.recorded.should == object
+ end
+
+ it "creates @object if the passed false" do
+ object = false
+ @shared.it("an example") { ScratchPad.record @object }
+ @state.it_behaves_like :shared_spec, :some_method, object
+ @state.process
+ ScratchPad.recorded.should == object
+ end
+
+ it "sends :it_should_behave_like" do
+ @state.should_receive(:it_should_behave_like)
+ @state.it_behaves_like :shared_spec, :some_method
+ end
+
+ describe "with multiple shared contexts" do
+ before :each do
+ @obj = Object.new
+ @obj2 = Object.new
+
+ @state2 = ContextState.new "Second top level"
+ @state2.instance_variable_set :@parsed, true
+ end
+
+ it "ensures the shared spec state is distinct" do
+ @shared.it("an example") { ScratchPad.record [@method, @object] }
+
+ @state.it_behaves_like :shared_spec, :some_method, @obj
+
+ @state.process
+ ScratchPad.recorded.should == [:some_method, @obj]
+
+ @state2.it_behaves_like :shared_spec, :another_method, @obj2
+
+ @state2.process
+ ScratchPad.recorded.should == [:another_method, @obj2]
+ end
+
+ it "ensures the shared spec state is distinct for nested shared specs" do
+ nested = ContextState.new "nested context"
+ nested.instance_variable_set :@parsed, true
+ nested.parent = @shared
+
+ nested.it("another example") { ScratchPad.record [:shared, @method, @object] }
+
+ @state.it_behaves_like :shared_spec, :some_method, @obj
+
+ @state.process
+ ScratchPad.recorded.should == [:shared, :some_method, @obj]
+
+ @state2.it_behaves_like :shared_spec, :another_method, @obj2
+
+ @state2.process
+ ScratchPad.recorded.should == [:shared, :another_method, @obj2]
+ end
+ end
+end
diff --git a/spec/mspec/spec/runner/tag_spec.rb b/spec/mspec/spec/runner/tag_spec.rb
new file mode 100644
index 0000000000..db55a1b186
--- /dev/null
+++ b/spec/mspec/spec/runner/tag_spec.rb
@@ -0,0 +1,123 @@
+require 'spec_helper'
+require 'mspec/runner/tag'
+
+describe SpecTag do
+ it "accepts an optional string to parse into fields" do
+ tag = SpecTag.new "tag(comment):description"
+ tag.tag.should == "tag"
+ tag.comment.should == "comment"
+ tag.description.should == "description"
+ end
+end
+
+describe SpecTag, "#parse" do
+ before :each do
+ @tag = SpecTag.new
+ end
+
+ it "accepts 'tag(comment):description'" do
+ @tag.parse "tag(I'm real):Some#method returns a value"
+ @tag.tag.should == "tag"
+ @tag.comment.should == "I'm real"
+ @tag.description.should == "Some#method returns a value"
+ end
+
+ it "accepts 'tag:description'" do
+ @tag.parse "tag:Another#method"
+ @tag.tag.should == "tag"
+ @tag.comment.should == nil
+ @tag.description.should == "Another#method"
+ end
+
+ it "accepts 'tag():description'" do
+ @tag.parse "tag():Another#method"
+ @tag.tag.should == "tag"
+ @tag.comment.should == nil
+ @tag.description.should == "Another#method"
+ end
+
+ it "accepts 'tag:'" do
+ @tag.parse "tag:"
+ @tag.tag.should == "tag"
+ @tag.comment.should == nil
+ @tag.description.should == ""
+ end
+
+ it "accepts 'tag(bug:555):Another#method'" do
+ @tag.parse "tag(bug:555):Another#method"
+ @tag.tag.should == "tag"
+ @tag.comment.should == "bug:555"
+ @tag.description.should == "Another#method"
+ end
+
+ it "accepts 'tag(http://someplace.com/neato):Another#method'" do
+ @tag.parse "tag(http://someplace.com/neato):Another#method"
+ @tag.tag.should == "tag"
+ @tag.comment.should == "http://someplace.com/neato"
+ @tag.description.should == "Another#method"
+ end
+
+ it "accepts 'tag(comment):\"Multi-line\\ntext\"'" do
+ @tag.parse 'tag(comment):"Multi-line\ntext"'
+ @tag.tag.should == "tag"
+ @tag.comment.should == "comment"
+ @tag.description.should == "Multi-line\ntext"
+ end
+
+ it "ignores '#anything'" do
+ @tag.parse "# this could be a comment"
+ @tag.tag.should == nil
+ @tag.comment.should == nil
+ @tag.description.should == nil
+ end
+end
+
+describe SpecTag, "#to_s" do
+ it "formats itself as 'tag(comment):description'" do
+ txt = "tag(comment):description"
+ tag = SpecTag.new txt
+ tag.tag.should == "tag"
+ tag.comment.should == "comment"
+ tag.description.should == "description"
+ tag.to_s.should == txt
+ end
+
+ it "formats itself as 'tag:description" do
+ txt = "tag:description"
+ tag = SpecTag.new txt
+ tag.tag.should == "tag"
+ tag.comment.should == nil
+ tag.description.should == "description"
+ tag.to_s.should == txt
+ end
+
+ it "formats itself as 'tag(comment):\"multi-line\\ntext\\ntag\"'" do
+ txt = 'tag(comment):"multi-line\ntext\ntag"'
+ tag = SpecTag.new txt
+ tag.tag.should == "tag"
+ tag.comment.should == "comment"
+ tag.description.should == "multi-line\ntext\ntag"
+ tag.to_s.should == txt
+ end
+end
+
+describe SpecTag, "#==" do
+ it "returns true if the tags have the same fields" do
+ one = SpecTag.new "tag(this):unicorn"
+ two = SpecTag.new "tag(this):unicorn"
+ one.==(two).should == true
+ [one].==([two]).should == true
+ end
+end
+
+describe SpecTag, "#unescape" do
+ it "replaces \\n by LF when the description is quoted" do
+ tag = SpecTag.new 'tag:"desc with\nnew line"'
+ tag.description.should == "desc with\nnew line"
+ end
+
+ it "does not replaces \\n by LF when the description is not quoted " do
+ tag = SpecTag.new 'tag:desc with\nnew line'
+ tag.description.should == "desc with\\nnew line"
+ end
+end
diff --git a/spec/mspec/spec/runner/tags.txt b/spec/mspec/spec/runner/tags.txt
new file mode 100644
index 0000000000..f4eb6ad034
--- /dev/null
+++ b/spec/mspec/spec/runner/tags.txt
@@ -0,0 +1,4 @@
+fail(broken):Some#method? works
+incomplete(20%):The#best method ever
+benchmark(0.01825):The#fastest method today
+extended():"Multi-line\ntext\ntag"