diff options
Diffstat (limited to 'spec/mspec/spec/runner')
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" |