summaryrefslogtreecommitdiff
path: root/spec/mspec/spec
diff options
context:
space:
mode:
authoreregon <eregon@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-05-07 12:04:49 +0000
committereregon <eregon@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-05-07 12:04:49 +0000
commit95e8c48dd3348503a8c7db5d0498894a1b676395 (patch)
tree9eef7f720314ebaff56845a74e203770e62284e4 /spec/mspec/spec
parented7d803500de38186c74bce94d233e85ef51e503 (diff)
Add in-tree mspec and ruby/spec
* For easier modifications of ruby/spec by MRI developers. * .gitignore: track changes under spec. * spec/mspec, spec/rubyspec: add in-tree mspec and ruby/spec. These files can therefore be updated like any other file in MRI. Instructions are provided in spec/README. [Feature #13156] [ruby-core:79246] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58595 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'spec/mspec/spec')
-rw-r--r--spec/mspec/spec/commands/fixtures/four.txt0
-rw-r--r--spec/mspec/spec/commands/fixtures/level2/three_spec.rb0
-rw-r--r--spec/mspec/spec/commands/fixtures/one_spec.rb0
-rw-r--r--spec/mspec/spec/commands/fixtures/three.rb0
-rw-r--r--spec/mspec/spec/commands/fixtures/two_spec.rb0
-rw-r--r--spec/mspec/spec/commands/mkspec_spec.rb363
-rw-r--r--spec/mspec/spec/commands/mspec_ci_spec.rb155
-rw-r--r--spec/mspec/spec/commands/mspec_run_spec.rb185
-rw-r--r--spec/mspec/spec/commands/mspec_spec.rb215
-rw-r--r--spec/mspec/spec/commands/mspec_tag_spec.rb419
-rw-r--r--spec/mspec/spec/expectations/expectations_spec.rb29
-rw-r--r--spec/mspec/spec/expectations/should.rb72
-rw-r--r--spec/mspec/spec/expectations/should_spec.rb61
-rw-r--r--spec/mspec/spec/fixtures/a_spec.rb15
-rw-r--r--spec/mspec/spec/fixtures/b_spec.rb7
-rw-r--r--spec/mspec/spec/fixtures/config.mspec10
-rwxr-xr-xspec/mspec/spec/fixtures/my_ruby4
-rw-r--r--spec/mspec/spec/fixtures/print_interpreter_spec.rb4
-rw-r--r--spec/mspec/spec/fixtures/tagging_spec.rb16
-rw-r--r--spec/mspec/spec/guards/block_device_spec.rb46
-rw-r--r--spec/mspec/spec/guards/bug_spec.rb151
-rw-r--r--spec/mspec/spec/guards/conflict_spec.rb51
-rw-r--r--spec/mspec/spec/guards/endian_spec.rb55
-rw-r--r--spec/mspec/spec/guards/feature_spec.rb80
-rw-r--r--spec/mspec/spec/guards/guard_spec.rb180
-rw-r--r--spec/mspec/spec/guards/platform_spec.rb331
-rw-r--r--spec/mspec/spec/guards/quarantine_spec.rb35
-rw-r--r--spec/mspec/spec/guards/superuser_spec.rb35
-rw-r--r--spec/mspec/spec/guards/support_spec.rb69
-rw-r--r--spec/mspec/spec/guards/user_spec.rb20
-rw-r--r--spec/mspec/spec/guards/version_spec.rb83
-rw-r--r--spec/mspec/spec/helpers/argf_spec.rb37
-rw-r--r--spec/mspec/spec/helpers/argv_spec.rb27
-rw-r--r--spec/mspec/spec/helpers/datetime_spec.rb44
-rw-r--r--spec/mspec/spec/helpers/fixture_spec.rb25
-rw-r--r--spec/mspec/spec/helpers/flunk_spec.rb20
-rw-r--r--spec/mspec/spec/helpers/fs_spec.rb182
-rw-r--r--spec/mspec/spec/helpers/io_spec.rb174
-rw-r--r--spec/mspec/spec/helpers/mock_to_path_spec.rb17
-rw-r--r--spec/mspec/spec/helpers/numeric_spec.rb25
-rw-r--r--spec/mspec/spec/helpers/ruby_exe_spec.rb220
-rw-r--r--spec/mspec/spec/helpers/scratch_spec.rb24
-rw-r--r--spec/mspec/spec/helpers/tmp_spec.rb27
-rw-r--r--spec/mspec/spec/integration/interpreter_spec.rb18
-rw-r--r--spec/mspec/spec/integration/run_spec.rb52
-rw-r--r--spec/mspec/spec/integration/tag_spec.rb63
-rw-r--r--spec/mspec/spec/matchers/base_spec.rb225
-rw-r--r--spec/mspec/spec/matchers/be_an_instance_of_spec.rb50
-rw-r--r--spec/mspec/spec/matchers/be_ancestor_of_spec.rb28
-rw-r--r--spec/mspec/spec/matchers/be_close_spec.rb46
-rw-r--r--spec/mspec/spec/matchers/be_computed_by_spec.rb42
-rw-r--r--spec/mspec/spec/matchers/be_empty_spec.rb26
-rw-r--r--spec/mspec/spec/matchers/be_false_spec.rb28
-rw-r--r--spec/mspec/spec/matchers/be_kind_of_spec.rb31
-rw-r--r--spec/mspec/spec/matchers/be_nan_spec.rb28
-rw-r--r--spec/mspec/spec/matchers/be_nil_spec.rb27
-rw-r--r--spec/mspec/spec/matchers/be_true_or_false_spec.rb19
-rw-r--r--spec/mspec/spec/matchers/be_true_spec.rb28
-rw-r--r--spec/mspec/spec/matchers/block_caller_spec.rb13
-rw-r--r--spec/mspec/spec/matchers/complain_spec.rb52
-rw-r--r--spec/mspec/spec/matchers/eql_spec.rb33
-rw-r--r--spec/mspec/spec/matchers/equal_element_spec.rb75
-rw-r--r--spec/mspec/spec/matchers/equal_spec.rb32
-rw-r--r--spec/mspec/spec/matchers/have_class_variable_spec.rb62
-rw-r--r--spec/mspec/spec/matchers/have_constant_spec.rb37
-rw-r--r--spec/mspec/spec/matchers/have_instance_method_spec.rb53
-rw-r--r--spec/mspec/spec/matchers/have_instance_variable_spec.rb61
-rw-r--r--spec/mspec/spec/matchers/have_method_spec.rb55
-rw-r--r--spec/mspec/spec/matchers/have_private_instance_method_spec.rb57
-rw-r--r--spec/mspec/spec/matchers/have_private_method_spec.rb44
-rw-r--r--spec/mspec/spec/matchers/have_protected_instance_method_spec.rb57
-rw-r--r--spec/mspec/spec/matchers/have_public_instance_method_spec.rb53
-rw-r--r--spec/mspec/spec/matchers/have_singleton_method_spec.rb45
-rw-r--r--spec/mspec/spec/matchers/include_spec.rb37
-rw-r--r--spec/mspec/spec/matchers/infinity_spec.rb34
-rw-r--r--spec/mspec/spec/matchers/match_yaml_spec.rb39
-rw-r--r--spec/mspec/spec/matchers/output_spec.rb74
-rw-r--r--spec/mspec/spec/matchers/output_to_fd_spec.rb42
-rw-r--r--spec/mspec/spec/matchers/raise_error_spec.rb108
-rw-r--r--spec/mspec/spec/matchers/respond_to_spec.rb33
-rw-r--r--spec/mspec/spec/matchers/signed_zero_spec.rb32
-rw-r--r--spec/mspec/spec/mocks/mock_spec.rb469
-rw-r--r--spec/mspec/spec/mocks/proxy_spec.rb405
-rw-r--r--spec/mspec/spec/runner/actions/filter_spec.rb84
-rw-r--r--spec/mspec/spec/runner/actions/tag_spec.rb315
-rw-r--r--spec/mspec/spec/runner/actions/taglist_spec.rb152
-rw-r--r--spec/mspec/spec/runner/actions/tagpurge_spec.rb154
-rw-r--r--spec/mspec/spec/runner/actions/tally_spec.rb352
-rw-r--r--spec/mspec/spec/runner/actions/timer_spec.rb44
-rw-r--r--spec/mspec/spec/runner/context_spec.rb1041
-rw-r--r--spec/mspec/spec/runner/example_spec.rb117
-rw-r--r--spec/mspec/spec/runner/exception_spec.rb146
-rw-r--r--spec/mspec/spec/runner/filters/a.yaml4
-rw-r--r--spec/mspec/spec/runner/filters/b.yaml11
-rw-r--r--spec/mspec/spec/runner/filters/match_spec.rb34
-rw-r--r--spec/mspec/spec/runner/filters/profile_spec.rb117
-rw-r--r--spec/mspec/spec/runner/filters/regexp_spec.rb13
-rw-r--r--spec/mspec/spec/runner/filters/tag_spec.rb92
-rw-r--r--spec/mspec/spec/runner/formatters/describe_spec.rb67
-rw-r--r--spec/mspec/spec/runner/formatters/dotted_spec.rb285
-rw-r--r--spec/mspec/spec/runner/formatters/file_spec.rb84
-rw-r--r--spec/mspec/spec/runner/formatters/html_spec.rb217
-rw-r--r--spec/mspec/spec/runner/formatters/junit_spec.rb147
-rw-r--r--spec/mspec/spec/runner/formatters/method_spec.rb178
-rw-r--r--spec/mspec/spec/runner/formatters/multi_spec.rb68
-rw-r--r--spec/mspec/spec/runner/formatters/specdoc_spec.rb106
-rw-r--r--spec/mspec/spec/runner/formatters/spinner_spec.rb83
-rw-r--r--spec/mspec/spec/runner/formatters/summary_spec.rb26
-rw-r--r--spec/mspec/spec/runner/formatters/unit_spec.rb74
-rw-r--r--spec/mspec/spec/runner/formatters/yaml_spec.rb125
-rw-r--r--spec/mspec/spec/runner/mspec_spec.rb595
-rw-r--r--spec/mspec/spec/runner/shared_spec.rb88
-rw-r--r--spec/mspec/spec/runner/tag_spec.rb123
-rw-r--r--spec/mspec/spec/runner/tags.txt4
-rw-r--r--spec/mspec/spec/spec_helper.rb54
-rw-r--r--spec/mspec/spec/utils/deprecate_spec.rb17
-rw-r--r--spec/mspec/spec/utils/name_map_spec.rb175
-rw-r--r--spec/mspec/spec/utils/options_spec.rb1309
-rw-r--r--spec/mspec/spec/utils/script_spec.rb473
-rw-r--r--spec/mspec/spec/utils/version_spec.rb45
120 files changed, 13245 insertions, 0 deletions
diff --git a/spec/mspec/spec/commands/fixtures/four.txt b/spec/mspec/spec/commands/fixtures/four.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/spec/mspec/spec/commands/fixtures/four.txt
diff --git a/spec/mspec/spec/commands/fixtures/level2/three_spec.rb b/spec/mspec/spec/commands/fixtures/level2/three_spec.rb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/spec/mspec/spec/commands/fixtures/level2/three_spec.rb
diff --git a/spec/mspec/spec/commands/fixtures/one_spec.rb b/spec/mspec/spec/commands/fixtures/one_spec.rb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/spec/mspec/spec/commands/fixtures/one_spec.rb
diff --git a/spec/mspec/spec/commands/fixtures/three.rb b/spec/mspec/spec/commands/fixtures/three.rb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/spec/mspec/spec/commands/fixtures/three.rb
diff --git a/spec/mspec/spec/commands/fixtures/two_spec.rb b/spec/mspec/spec/commands/fixtures/two_spec.rb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/spec/mspec/spec/commands/fixtures/two_spec.rb
diff --git a/spec/mspec/spec/commands/mkspec_spec.rb b/spec/mspec/spec/commands/mkspec_spec.rb
new file mode 100644
index 0000000000..ab3410af50
--- /dev/null
+++ b/spec/mspec/spec/commands/mkspec_spec.rb
@@ -0,0 +1,363 @@
+require 'spec_helper'
+require 'mspec/commands/mkspec'
+
+
+describe "The -c, --constant CONSTANT option" do
+ before :each do
+ @options = MSpecOptions.new
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MkSpec.new
+ @config = @script.config
+ end
+
+ it "is enabled by #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-c", "--constant", "CONSTANT",
+ an_instance_of(String))
+ @script.options []
+ end
+
+ it "adds CONSTANT to the list of constants" do
+ ["-c", "--constant"].each do |opt|
+ @config[:constants] = []
+ @script.options [opt, "Object"]
+ @config[:constants].should include("Object")
+ end
+ end
+end
+
+describe "The -b, --base DIR option" do
+ before :each do
+ @options = MSpecOptions.new
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MkSpec.new
+ @config = @script.config
+ end
+
+ it "is enabled by #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-b", "--base", "DIR",
+ an_instance_of(String))
+ @script.options
+ end
+
+ it "sets the base directory relative to which the spec directories are created" do
+ ["-b", "--base"].each do |opt|
+ @config[:base] = nil
+ @script.options [opt, "superspec"]
+ @config[:base].should == File.expand_path("superspec")
+ end
+ end
+end
+
+describe "The -r, --require LIBRARY option" do
+ before :each do
+ @options = MSpecOptions.new
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MkSpec.new
+ @config = @script.config
+ end
+
+ it "is enabled by #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-r", "--require", "LIBRARY",
+ an_instance_of(String))
+ @script.options
+ end
+
+ it "adds CONSTANT to the list of constants" do
+ ["-r", "--require"].each do |opt|
+ @config[:requires] = []
+ @script.options [opt, "libspec"]
+ @config[:requires].should include("libspec")
+ end
+ end
+end
+
+describe "The -V, --version-guard VERSION option" do
+ before :each do
+ @options = MSpecOptions.new
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MkSpec.new
+ @config = @script.config
+ end
+
+ it "is enabled by #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-V", "--version-guard", "VERSION",
+ an_instance_of(String))
+ @script.options
+ end
+
+ it "sets the version for the ruby_version_is guards to VERSION" do
+ ["-r", "--require"].each do |opt|
+ @config[:requires] = []
+ @script.options [opt, "libspec"]
+ @config[:requires].should include("libspec")
+ end
+ end
+end
+
+describe MkSpec, "#options" do
+ before :each do
+ @options = MSpecOptions.new
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MkSpec.new
+ end
+
+ it "parses the command line options" do
+ @options.should_receive(:parse).with(["--this", "and", "--that"])
+ @script.options ["--this", "and", "--that"]
+ end
+
+ it "parses ARGV unless passed other options" do
+ @options.should_receive(:parse).with(ARGV)
+ @script.options
+ end
+
+ it "prints help and exits if passed an unrecognized option" do
+ @options.should_receive(:raise).with(MSpecOptions::ParseError, an_instance_of(String))
+ @options.stub(:puts)
+ @options.stub(:exit)
+ @script.options "--iunknown"
+ end
+end
+
+describe MkSpec, "#create_directory" do
+ before :each do
+ @script = MkSpec.new
+ @script.config[:base] = "spec"
+ end
+
+ it "prints a warning if a file with the directory name exists" do
+ File.should_receive(:exist?).and_return(true)
+ File.should_receive(:directory?).and_return(false)
+ FileUtils.should_not_receive(:mkdir_p)
+ @script.should_receive(:puts).with("spec/class already exists and is not a directory.")
+ @script.create_directory("Class").should == nil
+ end
+
+ it "does nothing if the directory already exists" do
+ File.should_receive(:exist?).and_return(true)
+ File.should_receive(:directory?).and_return(true)
+ FileUtils.should_not_receive(:mkdir_p)
+ @script.create_directory("Class").should == "spec/class"
+ end
+
+ it "creates the directory if it does not exist" do
+ File.should_receive(:exist?).and_return(false)
+ @script.should_receive(:mkdir_p).with("spec/class")
+ @script.create_directory("Class").should == "spec/class"
+ end
+
+ it "creates the directory for a namespaced module if it does not exist" do
+ File.should_receive(:exist?).and_return(false)
+ @script.should_receive(:mkdir_p).with("spec/struct/tms")
+ @script.create_directory("Struct::Tms").should == "spec/struct/tms"
+ end
+end
+
+describe MkSpec, "#write_requires" do
+ before :each do
+ @script = MkSpec.new
+ @script.config[:base] = "spec"
+
+ @file = double("file")
+ File.stub(:open).and_yield(@file)
+ end
+
+ it "writes the spec_helper require line" do
+ @file.should_receive(:puts).with("require File.expand_path('../../../../spec_helper', __FILE__)")
+ @script.write_requires("spec/core/tcejbo", "spec/core/tcejbo/inspect_spec.rb")
+ end
+
+ it "writes require lines for each library specified on the command line" do
+ @file.stub(:puts)
+ @file.should_receive(:puts).with("require File.expand_path('../../../../spec_helper', __FILE__)")
+ @file.should_receive(:puts).with("require 'complex'")
+ @script.config[:requires] << 'complex'
+ @script.write_requires("spec/core/tcejbo", "spec/core/tcejbo/inspect_spec.rb")
+ end
+end
+
+describe MkSpec, "#write_spec" do
+ before :each do
+ @file = IOStub.new
+ File.stub(:open).and_yield(@file)
+
+ @script = MkSpec.new
+ @script.stub(:puts)
+
+ @response = double("system command response")
+ @response.stub(:include?).and_return(false)
+ @script.stub(:`).and_return(@response)
+ end
+
+ it "checks if specs exist for the method if the spec file exists" do
+ name = Regexp.escape(@script.ruby)
+ @script.should_receive(:`).with(
+ %r"#{name} #{MSPEC_HOME}/bin/mspec-run --dry-run --unguarded -fs -e 'Object#inspect' spec/core/tcejbo/inspect_spec.rb")
+ @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
+ end
+
+ it "checks for the method name in the spec file output" do
+ @response.should_receive(:include?).with("Array#[]=")
+ @script.write_spec("spec/core/yarra/element_set_spec.rb", "Array#[]=", true)
+ end
+
+ it "returns nil if the spec file exists and contains a spec for the method" do
+ @response.stub(:include?).and_return(true)
+ @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true).should == nil
+ end
+
+ it "does not print the spec file name if it exists and contains a spec for the method" do
+ @response.stub(:include?).and_return(true)
+ @script.should_not_receive(:puts)
+ @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
+ end
+
+ it "prints the spec file name if a template spec is written" do
+ @script.should_receive(:puts).with("spec/core/tcejbo/inspect_spec.rb")
+ @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
+ end
+
+ it "writes a template spec to the file if the spec file does not exist" do
+ @file.should_receive(:puts).twice
+ @script.should_receive(:puts).with("spec/core/tcejbo/inspect_spec.rb")
+ @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", false)
+ end
+
+ it "writes a template spec to the file if it exists but contains no spec for the method" do
+ @response.should_receive(:include?).and_return(false)
+ @file.should_receive(:puts).twice
+ @script.should_receive(:puts).with("spec/core/tcejbo/inspect_spec.rb")
+ @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
+ end
+
+ it "writes a template spec" do
+ @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
+ @file.should == <<EOS
+
+describe "Object#inspect" do
+ it "needs to be reviewed for spec completeness"
+end
+EOS
+ end
+
+ it "writes a template spec with version guard" do
+ @script.config[:version] = '""..."1.9"'
+ @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
+ @file.should == <<EOS
+
+ruby_version_is ""..."1.9" do
+ describe "Object#inspect" do
+ it "needs to be reviewed for spec completeness"
+ end
+end
+EOS
+
+ end
+end
+
+describe MkSpec, "#create_file" do
+ before :each do
+ @script = MkSpec.new
+ @script.stub(:write_requires)
+ @script.stub(:write_spec)
+
+ File.stub(:exist?).and_return(false)
+ end
+
+ it "generates a file name based on the directory, class/module, and method" do
+ File.should_receive(:join).with("spec/tcejbo", "inspect_spec.rb"
+ ).and_return("spec/tcejbo/inspect_spec.rb")
+ @script.create_file("spec/tcejbo", "Object", "inspect", "Object#inspect")
+ end
+
+ it "does not call #write_requires if the spec file already exists" do
+ File.should_receive(:exist?).and_return(true)
+ @script.should_not_receive(:write_requires)
+ @script.create_file("spec/tcejbo", "Object", "inspect", "Object#inspect")
+ end
+
+ it "calls #write_requires if the spec file does not exist" do
+ File.should_receive(:exist?).and_return(false)
+ @script.should_receive(:write_requires).with(
+ "spec/tcejbo", "spec/tcejbo/inspect_spec.rb")
+ @script.create_file("spec/tcejbo", "Object", "inspect", "Object#inspect")
+ end
+
+ it "calls #write_spec with the file, method name" do
+ @script.should_receive(:write_spec).with(
+ "spec/tcejbo/inspect_spec.rb", "Object#inspect", false)
+ @script.create_file("spec/tcejbo", "Object", "inspect", "Object#inspect")
+ end
+end
+
+describe MkSpec, "#run" do
+ before :each do
+ @options = MSpecOptions.new
+ MSpecOptions.stub(:new).and_return(@options)
+
+ @map = NameMap.new
+ NameMap.stub(:new).and_return(@map)
+
+ @script = MkSpec.new
+ @script.stub(:create_directory).and_return("spec/mkspec")
+ @script.stub(:create_file)
+ @script.config[:constants] = [MkSpec]
+ end
+
+ it "loads files in the requires list" do
+ @script.stub(:require)
+ @script.should_receive(:require).with("alib")
+ @script.should_receive(:require).with("blib")
+ @script.config[:requires] = ["alib", "blib"]
+ @script.run
+ end
+
+ it "creates a map of constants to methods" do
+ @map.should_receive(:map).with({}, @script.config[:constants]).and_return({})
+ @script.run
+ end
+
+ it "calls #create_directory for each class/module in the map" do
+ @script.should_receive(:create_directory).with("MkSpec").twice
+ @script.run
+ end
+
+ it "calls #create_file for each method on each class/module in the map" do
+ @map.should_receive(:map).with({}, @script.config[:constants]
+ ).and_return({"MkSpec#" => ["run"]})
+ @script.should_receive(:create_file).with("spec/mkspec", "MkSpec", "run", "MkSpec#run")
+ @script.run
+ end
+end
+
+describe MkSpec, ".main" do
+ before :each do
+ @script = double("MkSpec").as_null_object
+ MkSpec.stub(:new).and_return(@script)
+ end
+
+ it "sets MSPEC_RUNNER = '1' in the environment" do
+ ENV["MSPEC_RUNNER"] = "0"
+ MkSpec.main
+ ENV["MSPEC_RUNNER"].should == "1"
+ end
+
+ it "creates an instance of MSpecScript" do
+ MkSpec.should_receive(:new).and_return(@script)
+ MkSpec.main
+ end
+
+ it "calls the #options method on the script" do
+ @script.should_receive(:options)
+ MkSpec.main
+ end
+
+ it "calls the #run method on the script" do
+ @script.should_receive(:run)
+ MkSpec.main
+ end
+end
diff --git a/spec/mspec/spec/commands/mspec_ci_spec.rb b/spec/mspec/spec/commands/mspec_ci_spec.rb
new file mode 100644
index 0000000000..1e8949b0d3
--- /dev/null
+++ b/spec/mspec/spec/commands/mspec_ci_spec.rb
@@ -0,0 +1,155 @@
+require 'spec_helper'
+require 'mspec/runner/mspec'
+require 'mspec/runner/filters/tag'
+require 'mspec/commands/mspec-ci'
+
+describe MSpecCI, "#options" do
+ before :each do
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+
+ @script = MSpecCI.new
+ @script.stub(:config).and_return(@config)
+ @script.stub(:files).and_return([])
+ end
+
+ it "enables the chdir option" do
+ @options.should_receive(:chdir)
+ @script.options
+ end
+
+ it "enables the prefix option" do
+ @options.should_receive(:prefix)
+ @script.options
+ end
+
+ it "enables the config option" do
+ @options.should_receive(:configure)
+ @script.options
+ end
+
+ it "provides a custom action (block) to the config option" do
+ @script.should_receive(:load).with("cfg.mspec")
+ @script.options ["-B", "cfg.mspec"]
+ end
+
+ it "enables the name option" do
+ @options.should_receive(:name)
+ @script.options
+ end
+
+ it "enables the dry run option" do
+ @options.should_receive(:pretend)
+ @script.options
+ end
+
+ it "enables the unguarded option" do
+ @options.should_receive(:unguarded)
+ @script.options
+ end
+
+ it "enables the interrupt single specs option" do
+ @options.should_receive(:interrupt)
+ @script.options
+ end
+
+ it "enables the formatter options" do
+ @options.should_receive(:formatters)
+ @script.options
+ end
+
+ it "enables the verbose option" do
+ @options.should_receive(:verbose)
+ @script.options
+ end
+
+ it "enables the action options" do
+ @options.should_receive(:actions)
+ @script.options
+ end
+
+ it "enables the action filter options" do
+ @options.should_receive(:action_filters)
+ @script.options
+ end
+
+ it "enables the version option" do
+ @options.should_receive(:version)
+ @script.options
+ end
+
+ it "enables the help option" do
+ @options.should_receive(:help)
+ @script.options
+ end
+
+ it "calls #custom_options" do
+ @script.should_receive(:custom_options).with(@options)
+ @script.options
+ end
+end
+
+describe MSpecCI, "#run" do
+ before :each do
+ MSpec.stub(:process)
+
+ @filter = double("TagFilter")
+ TagFilter.stub(:new).and_return(@filter)
+ @filter.stub(:register)
+
+ @tags = ["fails", "critical", "unstable", "incomplete", "unsupported"]
+
+ @config = { :ci_files => ["one", "two"] }
+ @script = MSpecCI.new
+ @script.stub(:exit)
+ @script.stub(:config).and_return(@config)
+ @script.stub(:files).and_return(["one", "two"])
+ @script.options
+ end
+
+ it "registers the tags patterns" do
+ @config[:tags_patterns] = [/spec/, "tags"]
+ MSpec.should_receive(:register_tags_patterns).with([/spec/, "tags"])
+ @script.run
+ end
+
+ it "registers the files to process" do
+ MSpec.should_receive(:register_files).with(["one", "two"])
+ @script.run
+ end
+
+ it "registers a tag filter for 'fails', 'unstable', 'incomplete', 'critical', 'unsupported'" do
+ filter = double("fails filter")
+ TagFilter.should_receive(:new).with(:exclude, *@tags).and_return(filter)
+ filter.should_receive(:register)
+ @script.run
+ end
+
+ it "registers an additional exclude tag specified by :ci_xtags" do
+ @config[:ci_xtags] = "windows"
+ filter = double("fails filter")
+ TagFilter.should_receive(:new).with(:exclude, *(@tags + ["windows"])).and_return(filter)
+ filter.should_receive(:register)
+ @script.run
+ end
+
+ it "registers additional exclude tags specified by a :ci_xtags array" do
+ @config[:ci_xtags] = ["windows", "windoze"]
+ filter = double("fails filter")
+ TagFilter.should_receive(:new).with(:exclude,
+ *(@tags + ["windows", "windoze"])).and_return(filter)
+ filter.should_receive(:register)
+ @script.run
+ end
+
+ it "processes the files" do
+ MSpec.should_receive(:process)
+ @script.run
+ end
+
+ it "exits with the exit code registered with MSpec" do
+ MSpec.stub(:exit_code).and_return(7)
+ @script.should_receive(:exit).with(7)
+ @script.run
+ end
+end
diff --git a/spec/mspec/spec/commands/mspec_run_spec.rb b/spec/mspec/spec/commands/mspec_run_spec.rb
new file mode 100644
index 0000000000..4d350cdc02
--- /dev/null
+++ b/spec/mspec/spec/commands/mspec_run_spec.rb
@@ -0,0 +1,185 @@
+require 'spec_helper'
+require 'mspec/runner/mspec'
+require 'mspec/commands/mspec-run'
+
+one_spec = File.expand_path(File.dirname(__FILE__)) + '/fixtures/one_spec.rb'
+two_spec = File.expand_path(File.dirname(__FILE__)) + '/fixtures/two_spec.rb'
+
+describe MSpecRun, ".new" do
+ before :each do
+ @script = MSpecRun.new
+ end
+
+ it "sets config[:files] to an empty list" do
+ @script.config[:files].should == []
+ end
+end
+
+describe MSpecRun, "#options" do
+ before :each do
+ @stdout, $stdout = $stdout, IOStub.new
+
+ @argv = [one_spec, two_spec]
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+
+ @script = MSpecRun.new
+ @script.stub(:config).and_return(@config)
+ end
+
+ after :each do
+ $stdout = @stdout
+ end
+
+ it "enables the filter options" do
+ @options.should_receive(:filters)
+ @script.options @argv
+ end
+
+ it "enables the chdir option" do
+ @options.should_receive(:chdir)
+ @script.options @argv
+ end
+
+ it "enables the prefix option" do
+ @options.should_receive(:prefix)
+ @script.options @argv
+ end
+
+ it "enables the configure option" do
+ @options.should_receive(:configure)
+ @script.options @argv
+ end
+
+ it "provides a custom action (block) to the config option" do
+ @script.should_receive(:load).with("cfg.mspec")
+ @script.options ["-B", "cfg.mspec", one_spec]
+ end
+
+ it "enables the name option" do
+ @options.should_receive(:name)
+ @script.options @argv
+ end
+
+ it "enables the randomize option to runs specs in random order" do
+ @options.should_receive(:randomize)
+ @script.options @argv
+ end
+
+ it "enables the dry run option" do
+ @options.should_receive(:pretend)
+ @script.options @argv
+ end
+
+ it "enables the unguarded option" do
+ @options.should_receive(:unguarded)
+ @script.options @argv
+ end
+
+ it "enables the interrupt single specs option" do
+ @options.should_receive(:interrupt)
+ @script.options @argv
+ end
+
+ it "enables the formatter options" do
+ @options.should_receive(:formatters)
+ @script.options @argv
+ end
+
+ it "enables the verbose option" do
+ @options.should_receive(:verbose)
+ @script.options @argv
+ end
+
+ it "enables the verify options" do
+ @options.should_receive(:verify)
+ @script.options @argv
+ end
+
+ it "enables the action options" do
+ @options.should_receive(:actions)
+ @script.options @argv
+ end
+
+ it "enables the action filter options" do
+ @options.should_receive(:action_filters)
+ @script.options @argv
+ end
+
+ it "enables the version option" do
+ @options.should_receive(:version)
+ @script.options @argv
+ end
+
+ it "enables the help option" do
+ @options.should_receive(:help)
+ @script.options @argv
+ end
+
+ it "exits if there are no files to process and './spec' is not a directory" do
+ File.should_receive(:directory?).with("./spec").and_return(false)
+ @options.should_receive(:parse).and_return([])
+ @script.should_receive(:exit)
+ @script.options
+ $stdout.should include "No files specified"
+ end
+
+ it "process 'spec/' if it is a directory and no files were specified" do
+ File.should_receive(:directory?).with("./spec").and_return(true)
+ @options.should_receive(:parse).and_return([])
+ @script.should_receive(:files).with(["spec/"])
+ @script.options
+ end
+
+ it "calls #custom_options" do
+ @script.should_receive(:custom_options).with(@options)
+ @script.options @argv
+ end
+end
+
+describe MSpecRun, "#run" do
+ before :each do
+ @script = MSpecRun.new
+ @script.stub(:exit)
+ @spec_dir = File.expand_path(File.dirname(__FILE__)+"/fixtures")
+ @file_patterns = [
+ @spec_dir+"/level2",
+ @spec_dir+"/one_spec.rb",
+ @spec_dir+"/two_spec.rb"]
+ @files = [
+ @spec_dir+"/level2/three_spec.rb",
+ @spec_dir+"/one_spec.rb",
+ @spec_dir+"/two_spec.rb"]
+ @script.options @file_patterns
+ MSpec.stub :process
+ end
+
+ it "registers the tags patterns" do
+ @script.config[:tags_patterns] = [/spec/, "tags"]
+ MSpec.should_receive(:register_tags_patterns).with([/spec/, "tags"])
+ @script.run
+ end
+
+ it "registers the files to process" do
+ MSpec.should_receive(:register_files).with(@files)
+ @script.run
+ end
+
+ it "uses config[:files] if no files are given on the command line" do
+ @script.config[:files] = @file_patterns
+ MSpec.should_receive(:register_files).with(@files)
+ @script.options []
+ @script.run
+ end
+
+ it "processes the files" do
+ MSpec.should_receive(:process)
+ @script.run
+ end
+
+ it "exits with the exit code registered with MSpec" do
+ MSpec.stub(:exit_code).and_return(7)
+ @script.should_receive(:exit).with(7)
+ @script.run
+ end
+end
diff --git a/spec/mspec/spec/commands/mspec_spec.rb b/spec/mspec/spec/commands/mspec_spec.rb
new file mode 100644
index 0000000000..8b8b8fcc41
--- /dev/null
+++ b/spec/mspec/spec/commands/mspec_spec.rb
@@ -0,0 +1,215 @@
+require 'spec_helper'
+require 'yaml'
+require 'mspec/commands/mspec'
+
+describe MSpecMain, "#options" do
+ before :each do
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+
+ @script = MSpecMain.new
+ @script.stub(:config).and_return(@config)
+ @script.stub(:load)
+ end
+
+ it "enables the configure option" do
+ @options.should_receive(:configure)
+ @script.options
+ end
+
+ it "provides a custom action (block) to the config option" do
+ @script.options ["-B", "config"]
+ @config[:options].should include("-B", "config")
+ end
+
+ it "loads the file specified by the config option" do
+ @script.should_receive(:load).with("config")
+ @script.options ["-B", "config"]
+ end
+
+ it "enables the target options" do
+ @options.should_receive(:targets)
+ @script.options
+ end
+
+ it "sets config[:options] to all argv entries that are not registered options" do
+ @options.on "-X", "--exclude", "ARG", "description"
+ @script.options [".", "-G", "fail", "-X", "ARG", "--list", "unstable", "some/file.rb"]
+ @config[:options].should == [".", "-G", "fail", "--list", "unstable", "some/file.rb"]
+ end
+
+ it "calls #custom_options" do
+ @script.should_receive(:custom_options).with(@options)
+ @script.options
+ end
+end
+
+describe MSpecMain, "#run" do
+ before :each do
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MSpecMain.new
+ @script.stub(:config).and_return(@config)
+ @script.stub(:exec)
+ @err = $stderr
+ $stderr = IOStub.new
+ end
+
+ after :each do
+ $stderr = @err
+ end
+
+ it "uses exec to invoke the runner script" do
+ @script.should_receive(:exec).with("ruby", "#{MSPEC_HOME}/bin/mspec-run")
+ @script.options []
+ @script.run
+ end
+
+ it "shows the command line on stderr" do
+ @script.should_receive(:exec).with("ruby", "#{MSPEC_HOME}/bin/mspec-run")
+ @script.options []
+ @script.run
+ $stderr.to_s.should == "$ ruby #{Dir.pwd}/bin/mspec-run\n"
+ end
+
+ it "adds config[:launch] to the exec options" do
+ @script.should_receive(:exec).with("ruby",
+ "-Xlaunch.option", "#{MSPEC_HOME}/bin/mspec-run")
+ @config[:launch] << "-Xlaunch.option"
+ @script.options []
+ @script.run
+ $stderr.to_s.should == "$ ruby -Xlaunch.option #{Dir.pwd}/bin/mspec-run\n"
+ end
+
+ it "calls #multi_exec if the command is 'ci' and the multi option is passed" do
+ @script.should_receive(:multi_exec).and_return do |argv|
+ argv.should == ["ruby", "#{MSPEC_HOME}/bin/mspec-ci", "-fy"]
+ end
+ @script.options ["ci", "-j"]
+ lambda do
+ @script.run
+ end.should raise_error(SystemExit)
+ end
+end
+
+describe "The --warnings option" do
+ before :each do
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MSpecMain.new
+ @script.stub(:config).and_return(@config)
+ end
+
+ it "is enabled by #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("--warnings", an_instance_of(String))
+ @script.options
+ end
+
+ it "sets flags to -w" do
+ @config[:flags] = []
+ @script.options ["--warnings"]
+ @config[:flags].should include("-w")
+ end
+
+ it "set OUTPUT_WARNINGS = '1' in the environment" do
+ ENV['OUTPUT_WARNINGS'] = '0'
+ @script.options ["--warnings"]
+ ENV['OUTPUT_WARNINGS'].should == '1'
+ end
+end
+
+describe "The -j, --multi option" do
+ before :each do
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MSpecMain.new
+ @script.stub(:config).and_return(@config)
+ end
+
+ it "is enabled by #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-j", "--multi", an_instance_of(String))
+ @script.options
+ end
+
+ it "sets the multiple process option" do
+ ["-j", "--multi"].each do |opt|
+ @config[:multi] = nil
+ @script.options [opt]
+ @config[:multi].should == true
+ end
+ end
+
+ it "sets the formatter to YamlFormatter" do
+ ["-j", "--multi"].each do |opt|
+ @config[:options] = []
+ @script.options [opt]
+ @config[:options].should include("-fy")
+ end
+ end
+end
+
+describe "The -h, --help option" do
+ before :each do
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MSpecMain.new
+ @script.stub(:config).and_return(@config)
+ end
+
+ it "is enabled by #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-h", "--help", an_instance_of(String))
+ @script.options
+ end
+
+ it "passes the option to the subscript" do
+ ["-h", "--help"].each do |opt|
+ @config[:options] = []
+ @script.options ["ci", opt]
+ @config[:options].sort.should == ["-h"]
+ end
+ end
+
+ it "prints help and exits" do
+ @script.should_receive(:puts).twice
+ @script.should_receive(:exit).twice
+ ["-h", "--help"].each do |opt|
+ @script.options [opt]
+ end
+ end
+end
+
+describe "The -v, --version option" do
+ before :each do
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MSpecMain.new
+ @script.stub(:config).and_return(@config)
+ end
+
+ it "is enabled by #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-v", "--version", an_instance_of(String))
+ @script.options
+ end
+
+ it "passes the option to the subscripts" do
+ ["-v", "--version"].each do |opt|
+ @config[:options] = []
+ @script.options ["ci", opt]
+ @config[:options].sort.should == ["-v"]
+ end
+ end
+
+ it "prints the version and exits if no subscript is invoked" do
+ @config[:command] = nil
+ File.stub(:basename).and_return("mspec")
+ @script.should_receive(:puts).twice.with("mspec #{MSpec::VERSION}")
+ @script.should_receive(:exit).twice
+ ["-v", "--version"].each do |opt|
+ @script.options [opt]
+ end
+ end
+end
diff --git a/spec/mspec/spec/commands/mspec_tag_spec.rb b/spec/mspec/spec/commands/mspec_tag_spec.rb
new file mode 100644
index 0000000000..3c2e94db52
--- /dev/null
+++ b/spec/mspec/spec/commands/mspec_tag_spec.rb
@@ -0,0 +1,419 @@
+require 'spec_helper'
+require 'mspec/runner/mspec'
+require 'mspec/commands/mspec-tag'
+require 'mspec/runner/actions/tag'
+require 'mspec/runner/actions/taglist'
+require 'mspec/runner/actions/tagpurge'
+
+one_spec = File.expand_path(File.dirname(__FILE__)) + '/fixtures/one_spec.rb'
+two_spec = File.expand_path(File.dirname(__FILE__)) + '/fixtures/two_spec.rb'
+
+describe MSpecTag, ".new" do
+ before :each do
+ @script = MSpecTag.new
+ end
+
+ it "sets config[:ltags] to an empty list" do
+ @script.config[:ltags].should == []
+ end
+
+ it "sets config[:tagger] to :add" do
+ @script.config[:tagger] = :add
+ end
+
+ it "sets config[:tag] to 'fails:'" do
+ @script.config[:tag] = 'fails:'
+ end
+
+ it "sets config[:outcome] to :fail" do
+ @script.config[:outcome] = :fail
+ end
+end
+
+describe MSpecTag, "#options" do
+ before :each do
+ @stdout, $stdout = $stdout, IOStub.new
+
+ @argv = [one_spec, two_spec]
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+
+ @script = MSpecTag.new
+ @script.stub(:config).and_return(@config)
+ end
+
+ after :each do
+ $stdout = @stdout
+ end
+
+ it "enables the filter options" do
+ @options.should_receive(:filters)
+ @script.options @argv
+ end
+
+ it "enables the configure option" do
+ @options.should_receive(:configure)
+ @script.options @argv
+ end
+
+ it "provides a custom action (block) to the config option" do
+ @script.should_receive(:load).with("cfg.mspec")
+ @script.options ["-B", "cfg.mspec", one_spec]
+ end
+
+ it "enables the name option" do
+ @options.should_receive(:name)
+ @script.options @argv
+ end
+
+ it "enables the dry run option" do
+ @options.should_receive(:pretend)
+ @script.options @argv
+ end
+
+ it "enables the unguarded option" do
+ @options.should_receive(:unguarded)
+ @script.options @argv
+ end
+
+ it "enables the interrupt single specs option" do
+ @options.should_receive(:interrupt)
+ @script.options @argv
+ end
+
+ it "enables the formatter options" do
+ @options.should_receive(:formatters)
+ @script.options @argv
+ end
+
+ it "enables the verbose option" do
+ @options.should_receive(:verbose)
+ @script.options @argv
+ end
+
+ it "enables the version option" do
+ @options.should_receive(:version)
+ @script.options @argv
+ end
+
+ it "enables the help option" do
+ @options.should_receive(:help)
+ @script.options @argv
+ end
+
+ it "calls #custom_options" do
+ @script.should_receive(:custom_options).with(@options)
+ @script.options @argv
+ end
+
+ it "exits if there are no files to process" do
+ @options.should_receive(:parse).and_return([])
+ @script.should_receive(:exit)
+ @script.options
+ $stdout.should include "No files specified"
+ end
+end
+
+describe MSpecTag, "options" do
+ before :each do
+ @options, @config = new_option
+ MSpecOptions.stub(:new).and_return(@options)
+ @script = MSpecTag.new
+ @script.stub(:config).and_return(@config)
+ end
+
+ describe "-N, --add TAG" do
+ it "is enabled with #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-N", "--add", "TAG", an_instance_of(String))
+ @script.options [one_spec]
+ end
+
+ it "sets the mode to :add and sets the tag to TAG" do
+ ["-N", "--add"].each do |opt|
+ @config[:tagger] = nil
+ @config[:tag] = nil
+ @script.options [opt, "taggit", one_spec]
+ @config[:tagger].should == :add
+ @config[:tag].should == "taggit:"
+ end
+ end
+ end
+
+ describe "-R, --del TAG" do
+ it "is enabled with #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-R", "--del", "TAG",
+ an_instance_of(String))
+ @script.options [one_spec]
+ end
+
+ it "it sets the mode to :del, the tag to TAG, and the outcome to :pass" do
+ ["-R", "--del"].each do |opt|
+ @config[:tagger] = nil
+ @config[:tag] = nil
+ @config[:outcome] = nil
+ @script.options [opt, "taggit", one_spec]
+ @config[:tagger].should == :del
+ @config[:tag].should == "taggit:"
+ @config[:outcome].should == :pass
+ end
+ end
+ end
+
+ describe "-Q, --pass" do
+ it "is enabled with #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-Q", "--pass", an_instance_of(String))
+ @script.options [one_spec]
+ end
+
+ it "sets the outcome to :pass" do
+ ["-Q", "--pass"].each do |opt|
+ @config[:outcome] = nil
+ @script.options [opt, one_spec]
+ @config[:outcome].should == :pass
+ end
+ end
+ end
+
+ describe "-F, --fail" do
+ it "is enabled with #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-F", "--fail", an_instance_of(String))
+ @script.options [one_spec]
+ end
+
+ it "sets the outcome to :fail" do
+ ["-F", "--fail"].each do |opt|
+ @config[:outcome] = nil
+ @script.options [opt, one_spec]
+ @config[:outcome].should == :fail
+ end
+ end
+ end
+
+ describe "-L, --all" do
+ it "is enabled with #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-L", "--all", an_instance_of(String))
+ @script.options [one_spec]
+ end
+
+ it "sets the outcome to :all" do
+ ["-L", "--all"].each do |opt|
+ @config[:outcome] = nil
+ @script.options [opt, one_spec]
+ @config[:outcome].should == :all
+ end
+ end
+ end
+
+ describe "--list TAG" do
+ it "is enabled with #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("--list", "TAG", an_instance_of(String))
+ @script.options [one_spec]
+ end
+
+ it "sets the mode to :list" do
+ @config[:tagger] = nil
+ @script.options ["--list", "TAG", one_spec]
+ @config[:tagger].should == :list
+ end
+
+ it "sets ltags to include TAG" do
+ @config[:tag] = nil
+ @script.options ["--list", "TAG", one_spec]
+ @config[:ltags].should == ["TAG"]
+ end
+ end
+
+ describe "--list-all" do
+ it "is enabled with #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("--list-all", an_instance_of(String))
+ @script.options [one_spec]
+ end
+
+ it "sets the mode to :list_all" do
+ @config[:tagger] = nil
+ @script.options ["--list-all", one_spec]
+ @config[:tagger].should == :list_all
+ end
+ end
+
+ describe "--purge" do
+ it "is enabled with #options" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("--purge", an_instance_of(String))
+ @script.options [one_spec]
+ end
+
+ it "sets the mode to :purge" do
+ @config[:tagger] = nil
+ @script.options ["--purge", one_spec]
+ @config[:tagger].should == :purge
+ end
+ end
+end
+
+describe MSpecTag, "#run" do
+ before :each do
+ MSpec.stub(:process)
+
+ options = double("MSpecOptions").as_null_object
+ options.stub(:parse).and_return(["one", "two"])
+ MSpecOptions.stub(:new).and_return(options)
+
+ @config = { }
+ @script = MSpecTag.new
+ @script.stub(:exit)
+ @script.stub(:config).and_return(@config)
+ @script.stub(:files).and_return(["one", "two"])
+ @script.options
+ end
+
+ it "registers the tags patterns" do
+ @config[:tags_patterns] = [/spec/, "tags"]
+ MSpec.should_receive(:register_tags_patterns).with([/spec/, "tags"])
+ @script.run
+ end
+
+ it "registers the files to process" do
+ MSpec.should_receive(:register_files).with(["one", "two"])
+ @script.run
+ end
+
+ it "processes the files" do
+ MSpec.should_receive(:process)
+ @script.run
+ end
+
+ it "exits with the exit code registered with MSpec" do
+ MSpec.stub(:exit_code).and_return(7)
+ @script.should_receive(:exit).with(7)
+ @script.run
+ end
+end
+
+describe MSpecTag, "#register" do
+ before :each do
+ @script = MSpecTag.new
+ @config = @script.config
+ @config[:tag] = "fake:"
+ @config[:atags] = []
+ @config[:astrings] = []
+ @config[:ltags] = ["fails", "unstable"]
+
+ @script.stub(:files).and_return([])
+ @script.options "fake"
+
+ @t = double("TagAction")
+ @t.stub(:register)
+
+ @tl = double("TagListAction")
+ @tl.stub(:register)
+ end
+
+ it "raises an ArgumentError if no recognized action is given" do
+ @config[:tagger] = :totally_whack
+ lambda { @script.register }.should raise_error(ArgumentError)
+ end
+
+ describe "when config[:tagger] is the default (:add)" do
+ before :each do
+ @config[:formatter] = false
+ end
+
+ it "creates a TagAction" do
+ TagAction.should_receive(:new).and_return(@t)
+ @script.register
+ end
+
+ it "creates a TagAction if config[:tagger] is :del" do
+ @config[:tagger] = :del
+ @config[:outcome] = :pass
+ TagAction.should_receive(:new).with(:del, :pass, "fake", nil, [], []).and_return(@t)
+ @script.register
+ end
+
+ it "calls #register on the TagAction instance" do
+ TagAction.should_receive(:new).and_return(@t)
+ @t.should_receive(:register)
+ @script.register
+ end
+ end
+
+ describe "when config[:tagger] is :list" do
+ before :each do
+ TagListAction.should_receive(:new).with(@config[:ltags]).and_return(@tl)
+ @config[:tagger] = :list
+ end
+
+ it "creates a TagListAction" do
+ @tl.should_receive(:register)
+ @script.register
+ end
+
+ it "registers MSpec pretend mode" do
+ MSpec.should_receive(:register_mode).with(:pretend)
+ @script.register
+ end
+
+ it "sets config[:formatter] to false" do
+ @script.register
+ @config[:formatter].should be_false
+ end
+ end
+
+ describe "when config[:tagger] is :list_all" do
+ before :each do
+ TagListAction.should_receive(:new).with(nil).and_return(@tl)
+ @config[:tagger] = :list_all
+ end
+
+ it "creates a TagListAction" do
+ @tl.should_receive(:register)
+ @script.register
+ end
+
+ it "registers MSpec pretend mode" do
+ MSpec.should_receive(:register_mode).with(:pretend)
+ @script.register
+ end
+
+ it "sets config[:formatter] to false" do
+ @script.register
+ @config[:formatter].should be_false
+ end
+ end
+
+ describe "when config[:tagger] is :purge" do
+ before :each do
+ TagPurgeAction.should_receive(:new).and_return(@tl)
+ MSpec.stub(:register_mode)
+ @config[:tagger] = :purge
+ end
+
+ it "creates a TagPurgeAction" do
+ @tl.should_receive(:register)
+ @script.register
+ end
+
+ it "registers MSpec in pretend mode" do
+ MSpec.should_receive(:register_mode).with(:pretend)
+ @script.register
+ end
+
+ it "registers MSpec in unguarded mode" do
+ MSpec.should_receive(:register_mode).with(:unguarded)
+ @script.register
+ end
+
+ it "sets config[:formatter] to false" do
+ @script.register
+ @config[:formatter].should be_false
+ end
+ end
+end
diff --git a/spec/mspec/spec/expectations/expectations_spec.rb b/spec/mspec/spec/expectations/expectations_spec.rb
new file mode 100644
index 0000000000..fea692f3e3
--- /dev/null
+++ b/spec/mspec/spec/expectations/expectations_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+
+describe SpecExpectationNotMetError do
+ it "is a subclass of StandardError" do
+ SpecExpectationNotMetError.ancestors.should include(StandardError)
+ end
+end
+
+describe SpecExpectationNotFoundError do
+ it "is a subclass of StandardError" do
+ SpecExpectationNotFoundError.ancestors.should include(StandardError)
+ end
+end
+
+describe SpecExpectationNotFoundError, "#message" do
+ it "returns 'No behavior expectation was found in the example'" do
+ m = SpecExpectationNotFoundError.new.message
+ m.should == "No behavior expectation was found in the example"
+ end
+end
+
+describe SpecExpectation, "#fail_with" do
+ it "raises an SpecExpectationNotMetError" do
+ lambda {
+ SpecExpectation.fail_with "expected this", "to equal that"
+ }.should raise_error(SpecExpectationNotMetError, "expected this to equal that")
+ end
+end
diff --git a/spec/mspec/spec/expectations/should.rb b/spec/mspec/spec/expectations/should.rb
new file mode 100644
index 0000000000..8404ff044e
--- /dev/null
+++ b/spec/mspec/spec/expectations/should.rb
@@ -0,0 +1,72 @@
+$: << File.dirname(__FILE__) + '/../../lib'
+require 'mspec'
+require 'mspec/utils/script'
+
+# The purpose of these specs is to confirm that the #should
+# and #should_not methods are functioning appropriately. We
+# use a separate spec file that is invoked from the MSpec
+# specs but is run by MSpec. This avoids conflicting with
+# RSpec's #should and #should_not methods.
+
+class ShouldSpecsMonitor
+ def initialize
+ @called = 0
+ end
+
+ def expectation(state)
+ @called += 1
+ end
+
+ def finish
+ puts "I was called #{@called} times"
+ end
+end
+
+# Simplistic runner
+formatter = DottedFormatter.new
+formatter.register
+
+monitor = ShouldSpecsMonitor.new
+MSpec.register :expectation, monitor
+MSpec.register :finish, monitor
+
+at_exit { MSpec.actions :finish }
+
+MSpec.actions :start
+
+# Specs
+describe "MSpec expectation method #should" do
+ it "accepts a matcher" do
+ :sym.should be_kind_of(Symbol)
+ end
+
+ it "causes a failue to be recorded" do
+ 1.should == 2
+ end
+
+ it "registers that an expectation has been encountered" do
+ # an empty example block causes an exception because
+ # no expectation was encountered
+ end
+
+ it "invokes the MSpec :expectation actions" do
+ 1.should == 1
+ end
+end
+
+describe "MSpec expectation method #should_not" do
+ it "accepts a matcher" do
+ "sym".should_not be_kind_of(Symbol)
+ end
+
+ it "causes a failure to be recorded" do
+ 1.should_not == 1
+ end
+
+ it "registers that an expectation has been encountered" do
+ end
+
+ it "invokes the MSpec :expectation actions" do
+ 1.should_not == 2
+ end
+end
diff --git a/spec/mspec/spec/expectations/should_spec.rb b/spec/mspec/spec/expectations/should_spec.rb
new file mode 100644
index 0000000000..3258caf13c
--- /dev/null
+++ b/spec/mspec/spec/expectations/should_spec.rb
@@ -0,0 +1,61 @@
+require 'spec_helper'
+require 'rbconfig'
+
+describe "MSpec" do
+ before :all do
+ path = RbConfig::CONFIG['bindir']
+ exe = RbConfig::CONFIG['ruby_install_name']
+ file = File.dirname(__FILE__) + '/should.rb'
+ @out = `#{path}/#{exe} #{file}`
+ end
+
+ describe "#should" do
+ it "records failures" do
+ @out.should include <<-EOS
+1)
+MSpec expectation method #should causes a failue to be recorded FAILED
+Expected 1
+ to equal 2
+EOS
+ end
+
+ it "raises exceptions for examples with no expectations" do
+ @out.should include <<-EOS
+2)
+MSpec expectation method #should registers that an expectation has been encountered FAILED
+No behavior expectation was found in the example
+EOS
+ end
+ end
+
+ describe "#should_not" do
+ it "records failures" do
+ @out.should include <<-EOS
+3)
+MSpec expectation method #should_not causes a failure to be recorded FAILED
+Expected 1
+ not to equal 1
+EOS
+ end
+
+ it "raises exceptions for examples with no expectations" do
+ @out.should include <<-EOS
+4)
+MSpec expectation method #should_not registers that an expectation has been encountered FAILED
+No behavior expectation was found in the example
+EOS
+ end
+ end
+
+ it "prints status information" do
+ @out.should include ".FF..FF."
+ end
+
+ it "prints out a summary" do
+ @out.should include "0 files, 8 examples, 6 expectations, 4 failures, 0 errors"
+ end
+
+ it "records expectations" do
+ @out.should include "I was called 6 times"
+ end
+end
diff --git a/spec/mspec/spec/fixtures/a_spec.rb b/spec/mspec/spec/fixtures/a_spec.rb
new file mode 100644
index 0000000000..17a7e8b664
--- /dev/null
+++ b/spec/mspec/spec/fixtures/a_spec.rb
@@ -0,0 +1,15 @@
+unless defined?(RSpec)
+ describe "Foo#bar" do
+ it "passes" do
+ 1.should == 1
+ end
+
+ it "errors" do
+ 1.should == 2
+ end
+
+ it "fails" do
+ raise "failure"
+ end
+ end
+end
diff --git a/spec/mspec/spec/fixtures/b_spec.rb b/spec/mspec/spec/fixtures/b_spec.rb
new file mode 100644
index 0000000000..f1f63317cb
--- /dev/null
+++ b/spec/mspec/spec/fixtures/b_spec.rb
@@ -0,0 +1,7 @@
+unless defined?(RSpec)
+ describe "Bar#baz" do
+ it "works" do
+ 1.should == 1
+ end
+ end
+end
diff --git a/spec/mspec/spec/fixtures/config.mspec b/spec/mspec/spec/fixtures/config.mspec
new file mode 100644
index 0000000000..4a069e2eb0
--- /dev/null
+++ b/spec/mspec/spec/fixtures/config.mspec
@@ -0,0 +1,10 @@
+class MSpecScript
+ set :target, 'ruby'
+
+ set :backtrace_filter, /lib\/mspec\//
+
+ set :tags_patterns, [
+ [%r(spec/fixtures/), 'spec/fixtures/tags/'],
+ [/_spec.rb$/, '_tags.txt']
+ ]
+end
diff --git a/spec/mspec/spec/fixtures/my_ruby b/spec/mspec/spec/fixtures/my_ruby
new file mode 100755
index 0000000000..4d552f27fb
--- /dev/null
+++ b/spec/mspec/spec/fixtures/my_ruby
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+echo $RUBY_EXE
+ruby "$@"
diff --git a/spec/mspec/spec/fixtures/print_interpreter_spec.rb b/spec/mspec/spec/fixtures/print_interpreter_spec.rb
new file mode 100644
index 0000000000..e1c514dc87
--- /dev/null
+++ b/spec/mspec/spec/fixtures/print_interpreter_spec.rb
@@ -0,0 +1,4 @@
+unless defined?(RSpec)
+ puts ENV["RUBY_EXE"]
+ puts ruby_cmd("nil").split.first
+end
diff --git a/spec/mspec/spec/fixtures/tagging_spec.rb b/spec/mspec/spec/fixtures/tagging_spec.rb
new file mode 100644
index 0000000000..0097fd1808
--- /dev/null
+++ b/spec/mspec/spec/fixtures/tagging_spec.rb
@@ -0,0 +1,16 @@
+# encoding: utf-8
+unless defined?(RSpec)
+ describe "Tag#me" do
+ it "passes" do
+ 1.should == 1
+ end
+
+ it "errors" do
+ 1.should == 2
+ end
+
+ it "érròrs in unicode" do
+ 1.should == 2
+ end
+ end
+end
diff --git a/spec/mspec/spec/guards/block_device_spec.rb b/spec/mspec/spec/guards/block_device_spec.rb
new file mode 100644
index 0000000000..3b437b6d74
--- /dev/null
+++ b/spec/mspec/spec/guards/block_device_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe Object, "#with_block_device" do
+ before :each do
+ ScratchPad.clear
+
+ @guard = BlockDeviceGuard.new
+ BlockDeviceGuard.stub(:new).and_return(@guard)
+ end
+
+ platform_is_not :freebsd, :windows do
+ it "yields if block device is available" do
+ @guard.should_receive(:`).and_return("block devices")
+ with_block_device { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield if block device is not available" do
+ @guard.should_receive(:`).and_return(nil)
+ with_block_device { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+ end
+
+ platform_is :freebsd, :windows do
+ it "does not yield, since platform does not support block devices" do
+ @guard.should_not_receive(:`)
+ with_block_device { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+ end
+
+ it "sets the name of the guard to :with_block_device" do
+ with_block_device { }
+ @guard.name.should == :with_block_device
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:match?).and_return(true)
+ @guard.should_receive(:unregister)
+ lambda do
+ with_block_device { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
diff --git a/spec/mspec/spec/guards/bug_spec.rb b/spec/mspec/spec/guards/bug_spec.rb
new file mode 100644
index 0000000000..93c549041a
--- /dev/null
+++ b/spec/mspec/spec/guards/bug_spec.rb
@@ -0,0 +1,151 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe BugGuard, "#match? when #implementation? is 'ruby'" do
+ before :all do
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ after :all do
+ $VERBOSE = @verbose
+ end
+
+ before :each do
+ hide_deprecation_warnings
+ stub_const "VersionGuard::FULL_RUBY_VERSION", SpecVersion.new('1.8.6')
+ @ruby_name = Object.const_get :RUBY_NAME
+ Object.const_set :RUBY_NAME, 'ruby'
+ end
+
+ after :each do
+ Object.const_set :RUBY_NAME, @ruby_name
+ end
+
+ it "returns false when version argument is less than RUBY_VERSION" do
+ BugGuard.new("#1", "1.8.5").match?.should == false
+ end
+
+ it "returns true when version argument is equal to RUBY_VERSION" do
+ BugGuard.new("#1", "1.8.6").match?.should == true
+ end
+
+ it "returns true when version argument is greater than RUBY_VERSION" do
+ BugGuard.new("#1", "1.8.7").match?.should == true
+ end
+
+ it "returns true when version argument implicitly includes RUBY_VERSION" do
+ BugGuard.new("#1", "1.8").match?.should == true
+ BugGuard.new("#1", "1.8.6").match?.should == true
+ end
+
+ it "returns true when the argument range includes RUBY_VERSION" do
+ BugGuard.new("#1", '1.8.5'..'1.8.7').match?.should == true
+ BugGuard.new("#1", '1.8'..'1.9').match?.should == true
+ BugGuard.new("#1", '1.8'...'1.9').match?.should == true
+ BugGuard.new("#1", '1.8'..'1.8.6').match?.should == true
+ BugGuard.new("#1", '1.8.5'..'1.8.6').match?.should == true
+ BugGuard.new("#1", ''...'1.8.7').match?.should == true
+ end
+
+ it "returns false when the argument range does not include RUBY_VERSION" do
+ BugGuard.new("#1", '1.8.7'..'1.8.9').match?.should == false
+ BugGuard.new("#1", '1.8.4'..'1.8.5').match?.should == false
+ BugGuard.new("#1", '1.8.4'...'1.8.6').match?.should == false
+ BugGuard.new("#1", '1.8.5'...'1.8.6').match?.should == false
+ BugGuard.new("#1", ''...'1.8.6').match?.should == false
+ end
+
+ it "returns false when MSpec.mode?(:no_ruby_bug) is true" do
+ MSpec.should_receive(:mode?).with(:no_ruby_bug).twice.and_return(:true)
+ BugGuard.new("#1", "1.8.5").match?.should == false
+ BugGuard.new("#1", "1.8").match?.should == false
+ end
+end
+
+describe BugGuard, "#match? when #implementation? is not 'ruby'" do
+ before :all do
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ after :all do
+ $VERBOSE = @verbose
+ end
+
+ before :each do
+ hide_deprecation_warnings
+ @ruby_version = Object.const_get :RUBY_VERSION
+ @ruby_name = Object.const_get :RUBY_NAME
+
+ Object.const_set :RUBY_VERSION, '1.8.6'
+ Object.const_set :RUBY_NAME, 'jruby'
+ end
+
+ after :each do
+ Object.const_set :RUBY_VERSION, @ruby_version
+ Object.const_set :RUBY_NAME, @ruby_name
+ end
+
+ it "returns false when version argument is less than RUBY_VERSION" do
+ BugGuard.new("#1", "1.8").match?.should == false
+ BugGuard.new("#1", "1.8.6").match?.should == false
+ end
+
+ it "returns false when version argument is equal to RUBY_VERSION" do
+ BugGuard.new("#1", "1.8.6").match?.should == false
+ end
+
+ it "returns false when version argument is greater than RUBY_VERSION" do
+ BugGuard.new("#1", "1.8.7").match?.should == false
+ end
+
+ it "returns false no matter if the argument range includes RUBY_VERSION" do
+ BugGuard.new("#1", '1.8'...'1.9').match?.should == false
+ BugGuard.new("#1", '1.8.5'...'1.8.7').match?.should == false
+ BugGuard.new("#1", '1.8.4'...'1.8.6').match?.should == false
+ end
+
+ it "returns false when MSpec.mode?(:no_ruby_bug) is true" do
+ MSpec.stub(:mode?).and_return(:true)
+ BugGuard.new("#1", "1.8.6").match?.should == false
+ end
+end
+
+describe Object, "#ruby_bug" do
+ before :each do
+ hide_deprecation_warnings
+ @guard = BugGuard.new "#1234", "x.x.x"
+ BugGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "yields when #match? returns false" do
+ @guard.stub(:match?).and_return(false)
+ ruby_bug("#1234", "1.8.6") { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield when #match? returns true" do
+ @guard.stub(:match?).and_return(true)
+ ruby_bug("#1234", "1.8.6") { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "requires a bug tracker number and a version number" do
+ lambda { ruby_bug { } }.should raise_error(ArgumentError)
+ lambda { ruby_bug("#1234") { } }.should raise_error(ArgumentError)
+ end
+
+ it "sets the name of the guard to :ruby_bug" do
+ ruby_bug("#1234", "1.8.6") { }
+ @guard.name.should == :ruby_bug
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:unregister)
+ lambda do
+ ruby_bug("", "") { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
diff --git a/spec/mspec/spec/guards/conflict_spec.rb b/spec/mspec/spec/guards/conflict_spec.rb
new file mode 100644
index 0000000000..e06a2809ee
--- /dev/null
+++ b/spec/mspec/spec/guards/conflict_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe Object, "#conflicts_with" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "does not yield if Object.constants includes any of the arguments" do
+ Object.stub(:constants).and_return(["SomeClass", "OtherClass"])
+ conflicts_with(:SomeClass, :AClass, :BClass) { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "does not yield if Object.constants (as Symbols) includes any of the arguments" do
+ Object.stub(:constants).and_return([:SomeClass, :OtherClass])
+ conflicts_with(:SomeClass, :AClass, :BClass) { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "yields if Object.constants does not include any of the arguments" do
+ Object.stub(:constants).and_return(["SomeClass", "OtherClass"])
+ conflicts_with(:AClass, :BClass) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "yields if Object.constants (as Symbols) does not include any of the arguments" do
+ Object.stub(:constants).and_return([:SomeClass, :OtherClass])
+ conflicts_with(:AClass, :BClass) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+end
+
+describe Object, "#conflicts_with" do
+ before :each do
+ @guard = ConflictsGuard.new
+ ConflictsGuard.stub(:new).and_return(@guard)
+ end
+
+ it "sets the name of the guard to :conflicts_with" do
+ conflicts_with(:AClass, :BClass) { }
+ @guard.name.should == :conflicts_with
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:unregister)
+ lambda do
+ conflicts_with(:AClass, :BClass) { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
diff --git a/spec/mspec/spec/guards/endian_spec.rb b/spec/mspec/spec/guards/endian_spec.rb
new file mode 100644
index 0000000000..5b40c203ab
--- /dev/null
+++ b/spec/mspec/spec/guards/endian_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe Object, "#big_endian" do
+ before :each do
+ @guard = BigEndianGuard.new
+ BigEndianGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "yields on big-endian platforms" do
+ @guard.stub(:pattern).and_return([?\001])
+ big_endian { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield on little-endian platforms" do
+ @guard.stub(:pattern).and_return([?\000])
+ big_endian { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "sets the name of the guard to :big_endian" do
+ big_endian { }
+ @guard.name.should == :big_endian
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.stub(:pattern).and_return([?\001])
+ @guard.should_receive(:unregister)
+ lambda do
+ big_endian { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
+
+describe Object, "#little_endian" do
+ before :each do
+ @guard = BigEndianGuard.new
+ BigEndianGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "yields on little-endian platforms" do
+ @guard.stub(:pattern).and_return([?\000])
+ little_endian { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield on big-endian platforms" do
+ @guard.stub(:pattern).and_return([?\001])
+ little_endian { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+end
diff --git a/spec/mspec/spec/guards/feature_spec.rb b/spec/mspec/spec/guards/feature_spec.rb
new file mode 100644
index 0000000000..d14e5f8e67
--- /dev/null
+++ b/spec/mspec/spec/guards/feature_spec.rb
@@ -0,0 +1,80 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe FeatureGuard, ".enabled?" do
+ it "returns true if the feature is enabled" do
+ MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(true)
+ FeatureGuard.enabled?(:encoding).should be_true
+ end
+
+ it "returns false if the feature is not enabled" do
+ MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(false)
+ FeatureGuard.enabled?(:encoding).should be_false
+ end
+
+ it "returns true if all the features are enabled" do
+ MSpec.should_receive(:feature_enabled?).with(:one).and_return(true)
+ MSpec.should_receive(:feature_enabled?).with(:two).and_return(true)
+ FeatureGuard.enabled?(:one, :two).should be_true
+ end
+
+ it "returns false if any of the features are not enabled" do
+ MSpec.should_receive(:feature_enabled?).with(:one).and_return(true)
+ MSpec.should_receive(:feature_enabled?).with(:two).and_return(false)
+ FeatureGuard.enabled?(:one, :two).should be_false
+ end
+end
+
+describe Object, "#with_feature" do
+ before :each do
+ ScratchPad.clear
+
+ @guard = FeatureGuard.new :encoding
+ FeatureGuard.stub(:new).and_return(@guard)
+ end
+
+ it "sets the name of the guard to :with_feature" do
+ with_feature(:encoding) { }
+ @guard.name.should == :with_feature
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:match?).and_return(true)
+ @guard.should_receive(:unregister)
+ lambda do
+ with_feature { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
+
+describe Object, "#with_feature" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "yields if the feature is enabled" do
+ MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(true)
+ with_feature(:encoding) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "yields if all the features are enabled" do
+ MSpec.should_receive(:feature_enabled?).with(:one).and_return(true)
+ MSpec.should_receive(:feature_enabled?).with(:two).and_return(true)
+ with_feature(:one, :two) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield if the feature is not enabled" do
+ MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(false)
+ with_feature(:encoding) { ScratchPad.record :yield }
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "does not yield if any of the features are not enabled" do
+ MSpec.should_receive(:feature_enabled?).with(:one).and_return(true)
+ MSpec.should_receive(:feature_enabled?).with(:two).and_return(false)
+ with_feature(:one, :two) { ScratchPad.record :yield }
+ ScratchPad.recorded.should be_nil
+ end
+end
diff --git a/spec/mspec/spec/guards/guard_spec.rb b/spec/mspec/spec/guards/guard_spec.rb
new file mode 100644
index 0000000000..ca7dba6455
--- /dev/null
+++ b/spec/mspec/spec/guards/guard_spec.rb
@@ -0,0 +1,180 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'rbconfig'
+
+describe SpecGuard, ".ruby_version" do
+ before :each do
+ @ruby_version = Object.const_get :RUBY_VERSION
+ Object.const_set :RUBY_VERSION, "8.2.3"
+ end
+
+ after :each do
+ Object.const_set :RUBY_VERSION, @ruby_version
+ end
+
+ it "returns the full version for :full" do
+ SpecGuard.ruby_version(:full).should == "8.2.3"
+ end
+
+ it "returns major.minor.tiny for :tiny" do
+ SpecGuard.ruby_version(:tiny).should == "8.2.3"
+ end
+
+ it "returns major.minor.tiny for :teeny" do
+ SpecGuard.ruby_version(:tiny).should == "8.2.3"
+ end
+
+ it "returns major.minor for :minor" do
+ SpecGuard.ruby_version(:minor).should == "8.2"
+ end
+
+ it "defaults to :minor" do
+ SpecGuard.ruby_version.should == "8.2"
+ end
+
+ it "returns major for :major" do
+ SpecGuard.ruby_version(:major).should == "8"
+ end
+end
+
+describe SpecGuard, "#yield?" do
+ before :each do
+ MSpec.clear_modes
+ @guard = SpecGuard.new
+ @guard.stub(:match?).and_return(false)
+ end
+
+ after :each do
+ MSpec.unregister :add, @guard
+ MSpec.clear_modes
+ SpecGuard.clear_guards
+ end
+
+ it "returns true if MSpec.mode?(:unguarded) is true" do
+ MSpec.register_mode :unguarded
+ @guard.yield?.should == true
+ end
+
+ it "returns true if MSpec.mode?(:verify) is true" do
+ MSpec.register_mode :verify
+ @guard.yield?.should == true
+ end
+
+ it "returns true if MSpec.mode?(:verify) is true regardless of invert being true" do
+ MSpec.register_mode :verify
+ @guard.yield?(true).should == true
+ end
+
+ it "returns true if MSpec.mode?(:report) is true" do
+ MSpec.register_mode :report
+ @guard.yield?.should == true
+ end
+
+ it "returns true if MSpec.mode?(:report) is true regardless of invert being true" do
+ MSpec.register_mode :report
+ @guard.yield?(true).should == true
+ end
+
+ it "returns true if MSpec.mode?(:report_on) is true and SpecGuards.guards contains the named guard" do
+ MSpec.register_mode :report_on
+ SpecGuard.guards << :guard_name
+ @guard.yield?.should == false
+ @guard.name = :guard_name
+ @guard.yield?.should == true
+ end
+
+ it "returns #match? if neither report nor verify mode are true" do
+ @guard.stub(:match?).and_return(false)
+ @guard.yield?.should == false
+ @guard.stub(:match?).and_return(true)
+ @guard.yield?.should == true
+ end
+
+ it "returns #match? if invert is true and neither report nor verify mode are true" do
+ @guard.stub(:match?).and_return(false)
+ @guard.yield?(true).should == true
+ @guard.stub(:match?).and_return(true)
+ @guard.yield?(true).should == false
+ end
+end
+
+describe SpecGuard, "#match?" do
+ before :each do
+ @guard = SpecGuard.new
+ end
+
+ it "must be implemented in subclasses" do
+ lambda {
+ @guard.match?
+ }.should raise_error("must be implemented by the subclass")
+ end
+end
+
+describe SpecGuard, "#unregister" do
+ before :each do
+ MSpec.stub(:unregister)
+ @guard = SpecGuard.new
+ end
+
+ it "unregisters from MSpec :add actions" do
+ MSpec.should_receive(:unregister).with(:add, @guard)
+ @guard.unregister
+ end
+end
+
+describe SpecGuard, "#record" do
+ after :each do
+ SpecGuard.clear
+ end
+
+ it "saves the name of the guarded spec under the name of the guard" do
+ guard = SpecGuard.new "a", "1.8"..."1.9"
+ guard.name = :named_guard
+ guard.record "SomeClass#action returns true"
+ SpecGuard.report.should == {
+ 'named_guard a, 1.8...1.9' => ["SomeClass#action returns true"]
+ }
+ end
+end
+
+describe SpecGuard, ".guards" do
+ it "returns an Array" do
+ SpecGuard.guards.should be_kind_of(Array)
+ end
+end
+
+describe SpecGuard, ".clear_guards" do
+ it "resets the array to empty" do
+ SpecGuard.guards << :guard
+ SpecGuard.guards.should == [:guard]
+ SpecGuard.clear_guards
+ SpecGuard.guards.should == []
+ end
+end
+
+describe SpecGuard, ".finish" do
+ before :each do
+ $stdout = @out = IOStub.new
+ end
+
+ after :each do
+ $stdout = STDOUT
+ SpecGuard.clear
+ end
+
+ it "prints the descriptions of the guarded specs" do
+ guard = SpecGuard.new "a", "1.8"..."1.9"
+ guard.name = :named_guard
+ guard.record "SomeClass#action returns true"
+ guard.record "SomeClass#reverse returns false"
+ SpecGuard.finish
+ $stdout.should == %[
+
+2 specs omitted by guard: named_guard a, 1.8...1.9:
+
+SomeClass#action returns true
+SomeClass#reverse returns false
+
+]
+ end
+end
diff --git a/spec/mspec/spec/guards/platform_spec.rb b/spec/mspec/spec/guards/platform_spec.rb
new file mode 100644
index 0000000000..578773e476
--- /dev/null
+++ b/spec/mspec/spec/guards/platform_spec.rb
@@ -0,0 +1,331 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe Object, "#platform_is" do
+ before :each do
+ @guard = PlatformGuard.new :dummy
+ PlatformGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "does not yield when #os? returns false" do
+ PlatformGuard.stub(:os?).and_return(false)
+ platform_is(:ruby) { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "yields when #os? returns true" do
+ PlatformGuard.stub(:os?).and_return(true)
+ platform_is(:solarce) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "sets the name of the guard to :platform_is" do
+ platform_is(:solarce) { }
+ @guard.name.should == :platform_is
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:match?).and_return(true)
+ @guard.should_receive(:unregister)
+ lambda do
+ platform_is(:solarce) { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
+
+describe Object, "#platform_is_not" do
+ before :each do
+ @guard = PlatformGuard.new :dummy
+ PlatformGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "does not yield when #os? returns true" do
+ PlatformGuard.stub(:os?).and_return(true)
+ platform_is_not(:ruby) { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "yields when #os? returns false" do
+ PlatformGuard.stub(:os?).and_return(false)
+ platform_is_not(:solarce) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "sets the name of the guard to :platform_is_not" do
+ platform_is_not(:solarce) { }
+ @guard.name.should == :platform_is_not
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:match?).and_return(false)
+ @guard.should_receive(:unregister)
+ lambda do
+ platform_is_not(:solarce) { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
+
+describe Object, "#platform_is :wordsize => SIZE_SPEC" do
+ before :each do
+ @guard = PlatformGuard.new :darwin, :wordsize => 32
+ PlatformGuard.stub(:os?).and_return(true)
+ PlatformGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "yields when #wordsize? returns true" do
+ PlatformGuard.stub(:wordsize?).and_return(true)
+ platform_is(:wordsize => 32) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "doesn not yield when #wordsize? returns false" do
+ PlatformGuard.stub(:wordsize?).and_return(false)
+ platform_is(:wordsize => 32) { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+end
+
+describe Object, "#platform_is_not :wordsize => SIZE_SPEC" do
+ before :each do
+ @guard = PlatformGuard.new :darwin, :wordsize => 32
+ PlatformGuard.stub(:os?).and_return(true)
+ PlatformGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "yields when #wordsize? returns false" do
+ PlatformGuard.stub(:wordsize?).and_return(false)
+ platform_is_not(:wordsize => 32) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "doesn not yield when #wordsize? returns true" do
+ PlatformGuard.stub(:wordsize?).and_return(true)
+ platform_is_not(:wordsize => 32) { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+end
+
+describe PlatformGuard, ".implementation?" do
+ before :all do
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ after :all do
+ $VERBOSE = @verbose
+ end
+
+ before :each do
+ @ruby_name = Object.const_get :RUBY_NAME
+ end
+
+ after :each do
+ Object.const_set :RUBY_NAME, @ruby_name
+ end
+
+ it "returns true if passed :ruby and RUBY_NAME == 'ruby'" do
+ Object.const_set :RUBY_NAME, 'ruby'
+ PlatformGuard.implementation?(:ruby).should == true
+ end
+
+ it "returns true if passed :rubinius and RUBY_NAME == 'rbx'" do
+ Object.const_set :RUBY_NAME, 'rbx'
+ PlatformGuard.implementation?(:rubinius).should == true
+ end
+
+ it "returns true if passed :jruby and RUBY_NAME == 'jruby'" do
+ Object.const_set :RUBY_NAME, 'jruby'
+ PlatformGuard.implementation?(:jruby).should == true
+ end
+
+ it "returns true if passed :ironruby and RUBY_NAME == 'ironruby'" do
+ Object.const_set :RUBY_NAME, 'ironruby'
+ PlatformGuard.implementation?(:ironruby).should == true
+ end
+
+ it "returns true if passed :maglev and RUBY_NAME == 'maglev'" do
+ Object.const_set :RUBY_NAME, 'maglev'
+ PlatformGuard.implementation?(:maglev).should == true
+ end
+
+ it "returns true if passed :topaz and RUBY_NAME == 'topaz'" do
+ Object.const_set :RUBY_NAME, 'topaz'
+ PlatformGuard.implementation?(:topaz).should == true
+ end
+
+ it "returns true if passed :ruby and RUBY_NAME matches /^ruby/" do
+ Object.const_set :RUBY_NAME, 'ruby'
+ PlatformGuard.implementation?(:ruby).should == true
+
+ Object.const_set :RUBY_NAME, 'ruby1.8'
+ PlatformGuard.implementation?(:ruby).should == true
+
+ Object.const_set :RUBY_NAME, 'ruby1.9'
+ PlatformGuard.implementation?(:ruby).should == true
+ end
+
+ it "raises an error when passed an unrecognized name" do
+ Object.const_set :RUBY_NAME, 'ruby'
+ lambda {
+ PlatformGuard.implementation?(:python)
+ }.should raise_error(/unknown implementation/)
+ end
+end
+
+describe PlatformGuard, ".standard?" do
+ it "returns true if implementation? returns true" do
+ PlatformGuard.should_receive(:implementation?).with(:ruby).and_return(true)
+ PlatformGuard.standard?.should be_true
+ end
+
+ it "returns false if implementation? returns false" do
+ PlatformGuard.should_receive(:implementation?).with(:ruby).and_return(false)
+ PlatformGuard.standard?.should be_false
+ end
+end
+
+describe PlatformGuard, ".wordsize?" do
+ it "returns true when arg is 32 and 1.size is 4" do
+ PlatformGuard.wordsize?(32).should == (1.size == 4)
+ end
+
+ it "returns true when arg is 64 and 1.size is 8" do
+ PlatformGuard.wordsize?(64).should == (1.size == 8)
+ end
+end
+
+describe PlatformGuard, ".os?" do
+ before :each do
+ stub_const 'PlatformGuard::HOST_OS', 'solarce'
+ end
+
+ it "returns false when arg does not match the platform" do
+ PlatformGuard.os?(:ruby).should == false
+ end
+
+ it "returns false when no arg matches the platform" do
+ PlatformGuard.os?(:ruby, :jruby, :rubinius, :maglev).should == false
+ end
+
+ it "returns true when arg matches the platform" do
+ PlatformGuard.os?(:solarce).should == true
+ end
+
+ it "returns true when any arg matches the platform" do
+ PlatformGuard.os?(:ruby, :jruby, :solarce, :rubinius, :maglev).should == true
+ end
+
+ it "returns true when arg is :windows and the platform contains 'mswin'" do
+ stub_const 'PlatformGuard::HOST_OS', 'mswin32'
+ PlatformGuard.os?(:windows).should == true
+ end
+
+ it "returns true when arg is :windows and the platform contains 'mingw'" do
+ stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32'
+ PlatformGuard.os?(:windows).should == true
+ end
+
+ it "returns false when arg is not :windows and RbConfig::CONFIG['host_os'] contains 'mswin'" do
+ stub_const 'PlatformGuard::HOST_OS', 'i386-mswin32'
+ PlatformGuard.os?(:linux).should == false
+ end
+
+ it "returns false when arg is not :windows and RbConfig::CONFIG['host_os'] contains 'mingw'" do
+ stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32'
+ PlatformGuard.os?(:linux).should == false
+ end
+end
+
+describe PlatformGuard, ".os? on JRuby" do
+ before :all do
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ after :all do
+ $VERBOSE = @verbose
+ end
+
+ before :each do
+ @ruby_platform = Object.const_get :RUBY_PLATFORM
+ Object.const_set :RUBY_PLATFORM, 'java'
+ end
+
+ after :each do
+ Object.const_set :RUBY_PLATFORM, @ruby_platform
+ end
+
+ it "raises an error when testing for a :java platform" do
+ lambda {
+ PlatformGuard.os?(:java)
+ }.should raise_error(":java is not a valid OS")
+ end
+
+ it "returns true when arg is :windows and RUBY_PLATFORM contains 'java' and os?(:windows) is true" do
+ stub_const 'PlatformGuard::HOST_OS', 'mswin32'
+ PlatformGuard.os?(:windows).should == true
+ end
+
+ it "returns true when RUBY_PLATFORM contains 'java' and os?(argument) is true" do
+ stub_const 'PlatformGuard::HOST_OS', 'amiga'
+ PlatformGuard.os?(:amiga).should == true
+ end
+end
+
+describe PlatformGuard, ".os?" do
+ before :each do
+ stub_const 'PlatformGuard::HOST_OS', 'unreal'
+ end
+
+ it "returns true if argument matches RbConfig::CONFIG['host_os']" do
+ PlatformGuard.os?(:unreal).should == true
+ end
+
+ it "returns true if any argument matches RbConfig::CONFIG['host_os']" do
+ PlatformGuard.os?(:bsd, :unreal, :amiga).should == true
+ end
+
+ it "returns false if no argument matches RbConfig::CONFIG['host_os']" do
+ PlatformGuard.os?(:bsd, :netbsd, :amiga, :msdos).should == false
+ end
+
+ it "returns false if argument does not match RbConfig::CONFIG['host_os']" do
+ PlatformGuard.os?(:amiga).should == false
+ end
+
+ it "returns true when arg is :windows and RbConfig::CONFIG['host_os'] contains 'mswin'" do
+ stub_const 'PlatformGuard::HOST_OS', 'i386-mswin32'
+ PlatformGuard.os?(:windows).should == true
+ end
+
+ it "returns true when arg is :windows and RbConfig::CONFIG['host_os'] contains 'mingw'" do
+ stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32'
+ PlatformGuard.os?(:windows).should == true
+ end
+
+ it "returns false when arg is not :windows and RbConfig::CONFIG['host_os'] contains 'mswin'" do
+ stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32'
+ PlatformGuard.os?(:linux).should == false
+ end
+
+ it "returns false when arg is not :windows and RbConfig::CONFIG['host_os'] contains 'mingw'" do
+ stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32'
+ PlatformGuard.os?(:linux).should == false
+ end
+end
+
+describe PlatformGuard, ".windows?" do
+ it "returns true on windows" do
+ stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32'
+ PlatformGuard.windows?.should == true
+ end
+
+ it "returns false on non-windows" do
+ stub_const 'PlatformGuard::HOST_OS', 'i586-linux'
+ PlatformGuard.windows?.should == false
+ end
+end
diff --git a/spec/mspec/spec/guards/quarantine_spec.rb b/spec/mspec/spec/guards/quarantine_spec.rb
new file mode 100644
index 0000000000..e5c7da7939
--- /dev/null
+++ b/spec/mspec/spec/guards/quarantine_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe QuarantineGuard, "#match?" do
+ it "returns true" do
+ QuarantineGuard.new.match?.should == true
+ end
+end
+
+describe Object, "#quarantine!" do
+ before :each do
+ ScratchPad.clear
+
+ @guard = QuarantineGuard.new
+ QuarantineGuard.stub(:new).and_return(@guard)
+ end
+
+ it "does not yield" do
+ quarantine! { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "sets the name of the guard to :quarantine!" do
+ quarantine! { }
+ @guard.name.should == :quarantine!
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:match?).and_return(false)
+ @guard.should_receive(:unregister)
+ lambda do
+ quarantine! { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
diff --git a/spec/mspec/spec/guards/superuser_spec.rb b/spec/mspec/spec/guards/superuser_spec.rb
new file mode 100644
index 0000000000..f8815057e1
--- /dev/null
+++ b/spec/mspec/spec/guards/superuser_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe Object, "#as_superuser" do
+ before :each do
+ @guard = SuperUserGuard.new
+ SuperUserGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "does not yield when Process.euid is not 0" do
+ Process.stub(:euid).and_return(501)
+ as_superuser { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "yields when Process.euid is 0" do
+ Process.stub(:euid).and_return(0)
+ as_superuser { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "sets the name of the guard to :as_superuser" do
+ as_superuser { }
+ @guard.name.should == :as_superuser
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:match?).and_return(true)
+ @guard.should_receive(:unregister)
+ lambda do
+ as_superuser { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
diff --git a/spec/mspec/spec/guards/support_spec.rb b/spec/mspec/spec/guards/support_spec.rb
new file mode 100644
index 0000000000..43a7e76f06
--- /dev/null
+++ b/spec/mspec/spec/guards/support_spec.rb
@@ -0,0 +1,69 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe Object, "#not_supported_on" do
+ before :all do
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ @ruby_name = Object.const_get :RUBY_NAME if Object.const_defined? :RUBY_NAME
+ end
+
+ after :all do
+ $VERBOSE = @verbose
+ if @ruby_name
+ Object.const_set :RUBY_NAME, @ruby_name
+ else
+ Object.send :remove_const, :RUBY_NAME
+ end
+ end
+
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "raises an Exception when passed :ruby" do
+ Object.const_set :RUBY_NAME, "jruby"
+ lambda {
+ not_supported_on(:ruby) { ScratchPad.record :yield }
+ }.should raise_error(Exception)
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "does not yield when #implementation? returns true" do
+ Object.const_set :RUBY_NAME, "jruby"
+ not_supported_on(:jruby) { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "yields when #standard? returns true" do
+ Object.const_set :RUBY_NAME, "ruby"
+ not_supported_on(:rubinius) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "yields when #implementation? returns false" do
+ Object.const_set :RUBY_NAME, "jruby"
+ not_supported_on(:rubinius) { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+end
+
+describe Object, "#not_supported_on" do
+ before :each do
+ @guard = SupportedGuard.new
+ SupportedGuard.stub(:new).and_return(@guard)
+ end
+
+ it "sets the name of the guard to :not_supported_on" do
+ not_supported_on(:rubinius) { }
+ @guard.name.should == :not_supported_on
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:match?).and_return(false)
+ @guard.should_receive(:unregister)
+ lambda do
+ not_supported_on(:rubinius) { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
diff --git a/spec/mspec/spec/guards/user_spec.rb b/spec/mspec/spec/guards/user_spec.rb
new file mode 100644
index 0000000000..2de4db7390
--- /dev/null
+++ b/spec/mspec/spec/guards/user_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+describe Object, "#as_user" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "yields when the Process.euid is not 0" do
+ Process.stub(:euid).and_return(501)
+ as_user { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield when the Process.euid is 0" do
+ Process.stub(:euid).and_return(0)
+ as_user { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+end
diff --git a/spec/mspec/spec/guards/version_spec.rb b/spec/mspec/spec/guards/version_spec.rb
new file mode 100644
index 0000000000..f11e3dbd94
--- /dev/null
+++ b/spec/mspec/spec/guards/version_spec.rb
@@ -0,0 +1,83 @@
+require 'spec_helper'
+require 'mspec/guards'
+
+# The VersionGuard specifies a version of Ruby with a String of
+# the form: v = 'major.minor.tiny'.
+#
+# A VersionGuard instance can be created with a single String,
+# which means any version >= each component of v.
+# Or, the guard can be created with a Range, a..b, or a...b,
+# where a, b are of the same form as v. The meaning of the Range
+# is as typically understood: a..b means v >= a and v <= b;
+# a...b means v >= a and v < b.
+
+describe VersionGuard, "#match?" do
+ before :each do
+ hide_deprecation_warnings
+ stub_const "VersionGuard::FULL_RUBY_VERSION", SpecVersion.new('1.8.6')
+ end
+
+ it "returns true when the argument is equal to RUBY_VERSION" do
+ VersionGuard.new('1.8.6').match?.should == true
+ end
+
+ it "returns true when the argument is less than RUBY_VERSION" do
+ VersionGuard.new('1.8').match?.should == true
+ VersionGuard.new('1.8.5').match?.should == true
+ end
+
+ it "returns false when the argument is greater than RUBY_VERSION" do
+ VersionGuard.new('1.8.7').match?.should == false
+ VersionGuard.new('1.9.2').match?.should == false
+ end
+
+ it "returns true when the argument range includes RUBY_VERSION" do
+ VersionGuard.new('1.8.5'..'1.8.7').match?.should == true
+ VersionGuard.new('1.8'..'1.9').match?.should == true
+ VersionGuard.new('1.8'...'1.9').match?.should == true
+ VersionGuard.new('1.8'..'1.8.6').match?.should == true
+ VersionGuard.new('1.8.5'..'1.8.6').match?.should == true
+ VersionGuard.new(''...'1.8.7').match?.should == true
+ end
+
+ it "returns false when the argument range does not include RUBY_VERSION" do
+ VersionGuard.new('1.8.7'..'1.8.9').match?.should == false
+ VersionGuard.new('1.8.4'..'1.8.5').match?.should == false
+ VersionGuard.new('1.8.4'...'1.8.6').match?.should == false
+ VersionGuard.new('1.8.5'...'1.8.6').match?.should == false
+ VersionGuard.new(''...'1.8.6').match?.should == false
+ end
+end
+
+describe Object, "#ruby_version_is" do
+ before :each do
+ @guard = VersionGuard.new 'x.x.x'
+ VersionGuard.stub(:new).and_return(@guard)
+ ScratchPad.clear
+ end
+
+ it "yields when #match? returns true" do
+ @guard.stub(:match?).and_return(true)
+ ruby_version_is('x.x.x') { ScratchPad.record :yield }
+ ScratchPad.recorded.should == :yield
+ end
+
+ it "does not yield when #match? returns false" do
+ @guard.stub(:match?).and_return(false)
+ ruby_version_is('x.x.x') { ScratchPad.record :yield }
+ ScratchPad.recorded.should_not == :yield
+ end
+
+ it "sets the name of the guard to :ruby_version_is" do
+ ruby_version_is("") { }
+ @guard.name.should == :ruby_version_is
+ end
+
+ it "calls #unregister even when an exception is raised in the guard block" do
+ @guard.should_receive(:match?).and_return(true)
+ @guard.should_receive(:unregister)
+ lambda do
+ ruby_version_is("") { raise Exception }
+ end.should raise_error(Exception)
+ end
+end
diff --git a/spec/mspec/spec/helpers/argf_spec.rb b/spec/mspec/spec/helpers/argf_spec.rb
new file mode 100644
index 0000000000..cf5eb0fe88
--- /dev/null
+++ b/spec/mspec/spec/helpers/argf_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#argf" do
+ before :each do
+ @saved_argv = ARGV.dup
+ @argv = [__FILE__]
+ end
+
+ it "sets @argf to an instance of ARGF.class with the given argv" do
+ argf @argv do
+ @argf.should be_an_instance_of ARGF.class
+ @argf.filename.should == @argv.first
+ end
+ @argf.should be_nil
+ end
+
+ it "does not alter ARGV nor ARGF" do
+ argf @argv do
+ end
+ ARGV.should == @saved_argv
+ ARGF.argv.should == @saved_argv
+ end
+
+ it "does not close STDIN" do
+ argf ['-'] do
+ end
+ STDIN.should_not be_closed
+ end
+
+ it "disallows nested calls" do
+ argf @argv do
+ lambda { argf @argv }.should raise_error
+ end
+ end
+end
diff --git a/spec/mspec/spec/helpers/argv_spec.rb b/spec/mspec/spec/helpers/argv_spec.rb
new file mode 100644
index 0000000000..c3b21c7639
--- /dev/null
+++ b/spec/mspec/spec/helpers/argv_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#argv" do
+ before :each do
+ ScratchPad.clear
+
+ @saved_argv = ARGV.dup
+ @argv = ["a", "b"]
+ end
+
+ it "replaces and restores the value of ARGV" do
+ argv @argv
+ ARGV.should == @argv
+ argv :restore
+ ARGV.should == @saved_argv
+ end
+
+ it "yields to the block after setting ARGV" do
+ argv @argv do
+ ScratchPad.record ARGV.dup
+ end
+ ScratchPad.recorded.should == @argv
+ ARGV.should == @saved_argv
+ end
+end
diff --git a/spec/mspec/spec/helpers/datetime_spec.rb b/spec/mspec/spec/helpers/datetime_spec.rb
new file mode 100644
index 0000000000..8696c0c9c7
--- /dev/null
+++ b/spec/mspec/spec/helpers/datetime_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#new_datetime" do
+ it "returns a default DateTime instance" do
+ new_datetime.should == DateTime.new
+ end
+
+ it "returns a DateTime instance with the specified year value" do
+ d = new_datetime :year => 1970
+ d.year.should == 1970
+ end
+
+ it "returns a DateTime instance with the specified month value" do
+ d = new_datetime :month => 11
+ d.mon.should == 11
+ end
+
+ it "returns a DateTime instance with the specified day value" do
+ d = new_datetime :day => 23
+ d.day.should == 23
+ end
+
+ it "returns a DateTime instance with the specified hour value" do
+ d = new_datetime :hour => 10
+ d.hour.should == 10
+ end
+
+ it "returns a DateTime instance with the specified minute value" do
+ d = new_datetime :minute => 10
+ d.min.should == 10
+ end
+
+ it "returns a DateTime instance with the specified second value" do
+ d = new_datetime :second => 2
+ d.sec.should == 2
+ end
+
+ it "returns a DateTime instance with the specified offset value" do
+ d = new_datetime :offset => Rational(3,24)
+ d.offset.should == Rational(3,24)
+ end
+end
diff --git a/spec/mspec/spec/helpers/fixture_spec.rb b/spec/mspec/spec/helpers/fixture_spec.rb
new file mode 100644
index 0000000000..4dbdd092f1
--- /dev/null
+++ b/spec/mspec/spec/helpers/fixture_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#fixture" do
+ before :each do
+ @dir = File.realpath("..", __FILE__)
+ end
+
+ it "returns the expanded path to a fixture file" do
+ name = fixture(__FILE__, "subdir", "file.txt")
+ name.should == "#{@dir}/fixtures/subdir/file.txt"
+ end
+
+ it "omits '/shared' if it is the suffix of the directory string" do
+ name = fixture("#{@dir}/shared/file.rb", "subdir", "file.txt")
+ name.should == "#{@dir}/fixtures/subdir/file.txt"
+ end
+
+ it "does not append '/fixtures' if it is the suffix of the directory string" do
+ commands_dir = "#{File.dirname(@dir)}/commands"
+ name = fixture("#{commands_dir}/fixtures/file.rb", "subdir", "file.txt")
+ name.should == "#{commands_dir}/fixtures/subdir/file.txt"
+ end
+end
diff --git a/spec/mspec/spec/helpers/flunk_spec.rb b/spec/mspec/spec/helpers/flunk_spec.rb
new file mode 100644
index 0000000000..7b1216d3f7
--- /dev/null
+++ b/spec/mspec/spec/helpers/flunk_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/runner/mspec'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#flunk" do
+ before :each do
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ end
+
+ it "raises an SpecExpectationNotMetError unconditionally" do
+ lambda { flunk }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "accepts on argument for an optional message" do
+ lambda {flunk "test"}.should raise_error(SpecExpectationNotMetError)
+ end
+end
diff --git a/spec/mspec/spec/helpers/fs_spec.rb b/spec/mspec/spec/helpers/fs_spec.rb
new file mode 100644
index 0000000000..5afa91ff58
--- /dev/null
+++ b/spec/mspec/spec/helpers/fs_spec.rb
@@ -0,0 +1,182 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#cp" do
+ before :each do
+ @source = tmp("source.txt")
+ @copy = tmp("copied.txt")
+
+ @contents = "This is a copy."
+ File.open(@source, "w") { |f| f.write @contents }
+ end
+
+ after :each do
+ File.delete @source if File.exist? @source
+ File.delete @copy if File.exist? @copy
+ end
+
+ it "copies a file" do
+ cp @source, @copy
+ data = IO.read(@copy)
+ data.should == @contents
+ data.should == IO.read(@source)
+ end
+end
+
+describe Object, "#touch" do
+ before :all do
+ @name = tmp("touched.txt")
+ end
+
+ after :each do
+ File.delete @name if File.exist? @name
+ end
+
+ it "creates a file" do
+ touch @name
+ File.exist?(@name).should be_true
+ end
+
+ it "accepts an optional mode argument" do
+ touch @name, "wb"
+ File.exist?(@name).should be_true
+ end
+
+ it "overwrites an existing file" do
+ File.open(@name, "w") { |f| f.puts "used" }
+ File.size(@name).should > 0
+
+ touch @name
+ File.size(@name).should == 0
+ end
+
+ it "yields the open file if passed a block" do
+ touch(@name) { |f| f.write "touching" }
+ IO.read(@name).should == "touching"
+ end
+end
+
+describe Object, "#touch" do
+ before :all do
+ @name = tmp("subdir/touched.txt")
+ end
+
+ after :each do
+ rm_r File.dirname(@name)
+ end
+
+ it "creates all the directories in the path to the file" do
+ touch @name
+ File.exist?(@name).should be_true
+ end
+end
+
+describe Object, "#mkdir_p" do
+ before :all do
+ @dir1 = tmp("/nested")
+ @dir2 = @dir1 + "/directory"
+ @paths = [ @dir2, @dir1 ]
+ end
+
+ after :each do
+ File.delete @dir1 if File.file? @dir1
+ @paths.each { |path| Dir.rmdir path if File.directory? path }
+ end
+
+ it "creates all the directories in a path" do
+ mkdir_p @dir2
+ File.directory?(@dir2).should be_true
+ end
+
+ it "raises an ArgumentError if a path component is a file" do
+ File.open(@dir1, "w") { |f| }
+ lambda { mkdir_p @dir2 }.should raise_error(ArgumentError)
+ end
+end
+
+describe Object, "#rm_r" do
+ before :all do
+ @topdir = tmp("rm_r_tree")
+ @topfile = @topdir + "/file.txt"
+ @link = @topdir + "/file.lnk"
+ @socket = @topdir + "/socket.sck"
+ @subdir1 = @topdir + "/subdir1"
+ @subdir2 = @subdir1 + "/subdir2"
+ @subfile = @subdir1 + "/subfile.txt"
+ end
+
+ before :each do
+ mkdir_p @subdir2
+ touch @topfile
+ touch @subfile
+ end
+
+ after :each do
+ File.delete @link if File.exist? @link or File.symlink? @link
+ File.delete @socket if File.exist? @socket
+ File.delete @subfile if File.exist? @subfile
+ File.delete @topfile if File.exist? @topfile
+
+ Dir.rmdir @subdir2 if File.directory? @subdir2
+ Dir.rmdir @subdir1 if File.directory? @subdir1
+ Dir.rmdir @topdir if File.directory? @topdir
+ end
+
+ it "raises an ArgumentError if the path is not prefixed by MSPEC_RM_PREFIX" do
+ lambda { rm_r "some_file.txt" }.should raise_error(ArgumentError)
+ end
+
+ it "removes a single file" do
+ rm_r @subfile
+ File.exist?(@subfile).should be_false
+ end
+
+ it "removes multiple files" do
+ rm_r @topfile, @subfile
+ File.exist?(@topfile).should be_false
+ File.exist?(@subfile).should be_false
+ end
+
+ platform_is_not :windows do
+ it "removes a symlink to a file" do
+ File.symlink @topfile, @link
+ rm_r @link
+ File.exist?(@link).should be_false
+ end
+
+ it "removes a symlink to a directory" do
+ File.symlink @subdir1, @link
+ rm_r @link
+ lambda do
+ File.lstat(@link)
+ end.should raise_error(Errno::ENOENT)
+ File.exist?(@subdir1).should be_true
+ end
+
+ it "removes a dangling symlink" do
+ File.symlink "non_existent_file", @link
+ rm_r @link
+ lambda do
+ File.lstat(@link)
+ end.should raise_error(Errno::ENOENT)
+ end
+
+ it "removes a socket" do
+ require 'socket'
+ UNIXServer.new(@socket).close
+ rm_r @socket
+ File.exist?(@socket).should be_false
+ end
+ end
+
+ it "removes a single directory" do
+ rm_r @subdir2
+ File.directory?(@subdir2).should be_false
+ end
+
+ it "recursively removes a directory tree" do
+ rm_r @topdir
+ File.directory?(@topdir).should be_false
+ end
+end
diff --git a/spec/mspec/spec/helpers/io_spec.rb b/spec/mspec/spec/helpers/io_spec.rb
new file mode 100644
index 0000000000..6dfd81ee56
--- /dev/null
+++ b/spec/mspec/spec/helpers/io_spec.rb
@@ -0,0 +1,174 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe IOStub do
+ before :each do
+ @out = IOStub.new
+ @sep = $\
+ end
+
+ after :each do
+ $\ = @sep
+ end
+
+ it "provides a write method" do
+ @out.write "this"
+ @out.should == "this"
+ end
+
+ it "concatenates the arguments sent to write" do
+ @out.write "flim ", "flam"
+ @out.should == "flim flam"
+ end
+
+ it "provides a print method that appends the default separator" do
+ $\ = " [newline] "
+ @out.print "hello"
+ @out.print "world"
+ @out.should == "hello [newline] world [newline] "
+ end
+
+ it "provides a puts method that appends the default separator" do
+ @out.puts "hello", 1, 2, 3
+ @out.should == "hello\n1\n2\n3\n"
+ end
+
+ it "provides a puts method that appends separator if argument not given" do
+ @out.puts
+ @out.should == "\n"
+ end
+
+ it "provides a printf method" do
+ @out.printf "%-10s, %03d, %2.1f", "test", 42, 4.2
+ @out.should == "test , 042, 4.2"
+ end
+
+ it "provides a flush method that does nothing and returns self" do
+ @out.flush.should == @out
+ end
+end
+
+describe Object, "#new_fd" do
+ before :each do
+ @name = tmp("io_specs")
+ @io = nil
+ end
+
+ after :each do
+ @io.close if @io and not @io.closed?
+ rm_r @name
+ end
+
+ it "returns a Fixnum that can be used to create an IO instance" do
+ fd = new_fd @name
+ fd.should be_an_instance_of(Fixnum)
+
+ @io = IO.new fd, fmode('w:utf-8')
+ @io.sync = true
+ @io.print "io data"
+
+ IO.read(@name).should == "io data"
+ end
+
+ it "accepts an options Hash" do
+ FeatureGuard.stub(:enabled?).and_return(true)
+ fd = new_fd @name, { :mode => 'w:utf-8' }
+ fd.should be_an_instance_of(Fixnum)
+
+ @io = IO.new fd, fmode('w:utf-8')
+ @io.sync = true
+ @io.print "io data"
+
+ IO.read(@name).should == "io data"
+ end
+
+ it "raises an ArgumentError if the options Hash does not include :mode" do
+ FeatureGuard.stub(:enabled?).and_return(true)
+ lambda { new_fd @name, { :encoding => "utf-8" } }.should raise_error(ArgumentError)
+ end
+end
+
+describe Object, "#new_io" do
+ before :each do
+ @name = tmp("io_specs.txt")
+ end
+
+ after :each do
+ @io.close if @io and !@io.closed?
+ rm_r @name
+ end
+
+ it "returns an IO instance" do
+ @io = new_io @name
+ @io.should be_an_instance_of(IO)
+ end
+
+ it "opens the IO for reading if passed 'r'" do
+ touch(@name) { |f| f.print "io data" }
+ @io = new_io @name, "r"
+ @io.read.should == "io data"
+ lambda { @io.puts "more data" }.should raise_error(IOError)
+ end
+
+ it "opens the IO for writing if passed 'w'" do
+ @io = new_io @name, "w"
+ @io.sync = true
+
+ @io.print "io data"
+ IO.read(@name).should == "io data"
+ end
+
+ it "opens the IO for reading if passed { :mode => 'r' }" do
+ touch(@name) { |f| f.print "io data" }
+ @io = new_io @name, { :mode => "r" }
+ @io.read.should == "io data"
+ lambda { @io.puts "more data" }.should raise_error(IOError)
+ end
+
+ it "opens the IO for writing if passed { :mode => 'w' }" do
+ @io = new_io @name, { :mode => "w" }
+ @io.sync = true
+
+ @io.print "io data"
+ IO.read(@name).should == "io data"
+ end
+end
+
+describe Object, "#fmode" do
+ it "returns the argument unmodified if :encoding feature is enabled" do
+ FeatureGuard.should_receive(:enabled?).with(:encoding).and_return(true)
+ fmode("rb:binary:utf-8").should == "rb:binary:utf-8"
+ end
+
+ it "returns only the file access mode if :encoding feature is not enabled" do
+ FeatureGuard.should_receive(:enabled?).with(:encoding).and_return(false)
+ fmode("rb:binary:utf-8").should == "rb"
+ end
+end
+
+describe Object, "#options_or_mode" do
+ describe "if passed a Hash" do
+ it "returns a mode string if :encoding feature is not enabled" do
+ FeatureGuard.should_receive(:enabled?).with(:encoding).twice.and_return(false)
+ options_or_mode(:mode => "rb:binary").should == "rb"
+ end
+
+ it "returns a Hash if :encoding feature is enabled" do
+ FeatureGuard.should_receive(:enabled?).with(:encoding).and_return(true)
+ options_or_mode(:mode => "rb:utf-8").should == { :mode => "rb:utf-8" }
+ end
+ end
+
+ describe "if passed a String" do
+ it "returns only the file access mode if :encoding feature is not enabled" do
+ FeatureGuard.should_receive(:enabled?).with(:encoding).and_return(false)
+ options_or_mode("rb:binary:utf-8").should == "rb"
+ end
+
+ it "returns the argument unmodified if :encoding feature is enabled" do
+ FeatureGuard.should_receive(:enabled?).with(:encoding).and_return(true)
+ options_or_mode("rb:binary:utf-8").should == "rb:binary:utf-8"
+ end
+ end
+end
diff --git a/spec/mspec/spec/helpers/mock_to_path_spec.rb b/spec/mspec/spec/helpers/mock_to_path_spec.rb
new file mode 100644
index 0000000000..464e7e5440
--- /dev/null
+++ b/spec/mspec/spec/helpers/mock_to_path_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#mock_to_path" do
+ it "returns an object that responds to #to_path" do
+ obj = mock_to_path("foo")
+ obj.should be_a(MockObject)
+ obj.should respond_to(:to_path)
+ obj.to_path
+ end
+
+ it "returns the provided path when #to_path is called" do
+ obj = mock_to_path("/tmp/foo")
+ obj.to_path.should == "/tmp/foo"
+ end
+end
diff --git a/spec/mspec/spec/helpers/numeric_spec.rb b/spec/mspec/spec/helpers/numeric_spec.rb
new file mode 100644
index 0000000000..2ea56e5961
--- /dev/null
+++ b/spec/mspec/spec/helpers/numeric_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#bignum_value" do
+ it "returns a value that is an instance of Bignum on any platform" do
+ bignum_value.should == 0x8000_0000_0000_0000
+ end
+
+ it "returns the default value incremented by the argument" do
+ bignum_value(42).should == 0x8000_0000_0000_002a
+ end
+end
+
+describe Object, "#nan_value" do
+ it "returns NaN" do
+ nan_value.nan?.should be_true
+ end
+end
+
+describe Object, "#infinity_value" do
+ it "returns Infinity" do
+ infinity_value.infinite?.should == 1
+ end
+end
diff --git a/spec/mspec/spec/helpers/ruby_exe_spec.rb b/spec/mspec/spec/helpers/ruby_exe_spec.rb
new file mode 100644
index 0000000000..debfc3b1ca
--- /dev/null
+++ b/spec/mspec/spec/helpers/ruby_exe_spec.rb
@@ -0,0 +1,220 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+require 'rbconfig'
+
+class RubyExeSpecs
+end
+
+describe "#ruby_exe_options" do
+ before :all do
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+
+ @ruby_name = Object.const_get :RUBY_NAME
+ @ruby_exe_env = ENV['RUBY_EXE']
+
+ @script = RubyExeSpecs.new
+ end
+
+ after :all do
+ Object.const_set :RUBY_NAME, @ruby_name
+ ENV['RUBY_EXE'] = @ruby_exe_env
+ $VERBOSE = @verbose
+ end
+
+ before :each do
+ @script = RubyExeSpecs.new
+ end
+
+ it "returns ENV['RUBY_EXE'] when passed :env" do
+ ENV['RUBY_EXE'] = "kowabunga"
+ @script.ruby_exe_options(:env).should == "kowabunga"
+ end
+
+ it "returns 'bin/jruby' when passed :engine and RUBY_NAME is 'jruby'" do
+ Object.const_set :RUBY_NAME, 'jruby'
+ @script.ruby_exe_options(:engine).should == 'bin/jruby'
+ end
+
+ it "returns 'bin/rbx' when passed :engine, RUBY_NAME is 'rbx'" do
+ Object.const_set :RUBY_NAME, 'rbx'
+ @script.ruby_exe_options(:engine).should == 'bin/rbx'
+ end
+
+ it "returns 'ir' when passed :engine and RUBY_NAME is 'ironruby'" do
+ Object.const_set :RUBY_NAME, 'ironruby'
+ @script.ruby_exe_options(:engine).should == 'ir'
+ end
+
+ it "returns 'maglev-ruby' when passed :engine and RUBY_NAME is 'maglev'" do
+ Object.const_set :RUBY_NAME, 'maglev'
+ @script.ruby_exe_options(:engine).should == 'maglev-ruby'
+ end
+
+ it "returns 'topaz' when passed :engine and RUBY_NAME is 'topaz'" do
+ Object.const_set :RUBY_NAME, 'topaz'
+ @script.ruby_exe_options(:engine).should == 'topaz'
+ end
+
+ it "returns RUBY_NAME + $(EXEEXT) when passed :name" do
+ bin = RUBY_NAME + (RbConfig::CONFIG['EXEEXT'] || RbConfig::CONFIG['exeext'] || '')
+ name = File.join ".", bin
+ @script.ruby_exe_options(:name).should == name
+ end
+
+ it "returns $(bindir)/$(RUBY_INSTALL_NAME) + $(EXEEXT) when passed :install_name" do
+ bin = RbConfig::CONFIG['RUBY_INSTALL_NAME'] + (RbConfig::CONFIG['EXEEXT'] || RbConfig::CONFIG['exeext'] || '')
+ name = File.join RbConfig::CONFIG['bindir'], bin
+ @script.ruby_exe_options(:install_name).should == name
+ end
+end
+
+describe "#resolve_ruby_exe" do
+ before :all do
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+
+ @name = "ruby_spec_exe"
+ end
+
+ before :each do
+ @script = RubyExeSpecs.new
+ end
+
+ after :all do
+ $VERBOSE = @verbose
+ end
+
+ it "returns the value returned by #ruby_exe_options if it exists and is executable" do
+ @script.should_receive(:ruby_exe_options).and_return(@name)
+ File.should_receive(:file?).with(@name).and_return(true)
+ File.should_receive(:executable?).with(@name).and_return(true)
+ File.should_receive(:expand_path).with(@name).and_return(@name)
+ @script.resolve_ruby_exe.should == @name
+ end
+
+ it "expands the path portion of the result of #ruby_exe_options" do
+ @script.should_receive(:ruby_exe_options).and_return("#{@name}")
+ File.should_receive(:file?).with(@name).and_return(true)
+ File.should_receive(:executable?).with(@name).and_return(true)
+ File.should_receive(:expand_path).with(@name).and_return("/usr/bin/#{@name}")
+ @script.resolve_ruby_exe.should == "/usr/bin/#{@name}"
+ end
+
+ it "adds the flags after the executable" do
+ @name = 'bin/rbx'
+ @script.should_receive(:ruby_exe_options).and_return(@name)
+ File.should_receive(:file?).with(@name).and_return(true)
+ File.should_receive(:executable?).with(@name).and_return(true)
+ File.should_receive(:expand_path).with(@name).and_return(@name)
+
+ ENV.should_receive(:[]).with("RUBY_FLAGS").and_return('-X19')
+ @script.resolve_ruby_exe.should == 'bin/rbx -X19'
+ end
+
+ it "raises an exception if no exe is found" do
+ File.should_receive(:file?).at_least(:once).and_return(false)
+ lambda {
+ @script.resolve_ruby_exe
+ }.should raise_error(Exception)
+ end
+end
+
+describe Object, "#ruby_cmd" do
+ before :all do
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+
+ @ruby_exe = Object.const_get :RUBY_EXE
+ Object.const_set :RUBY_EXE, 'ruby_spec_exe -w -Q'
+
+ @file = "some/ruby/file.rb"
+ @code = %(some "real" 'ruby' code)
+
+ @script = RubyExeSpecs.new
+ end
+
+ after :all do
+ Object.const_set :RUBY_EXE, @ruby_exe
+ $VERBOSE = @verbose
+ end
+
+ it "returns a command that runs the given file if it is a file that exists" do
+ File.should_receive(:exist?).with(@file).and_return(true)
+ @script.ruby_cmd(@file).should == "ruby_spec_exe -w -Q some/ruby/file.rb"
+ end
+
+ it "includes the given options and arguments with a file" do
+ File.should_receive(:exist?).with(@file).and_return(true)
+ @script.ruby_cmd(@file, :options => "-w -Cdir", :args => "< file.txt").should ==
+ "ruby_spec_exe -w -Q -w -Cdir some/ruby/file.rb < file.txt"
+ end
+
+ it "includes the given options and arguments with -e" do
+ File.should_receive(:exist?).with(@code).and_return(false)
+ @script.ruby_cmd(@code, :options => "-W0 -Cdir", :args => "< file.txt").should ==
+ %(ruby_spec_exe -w -Q -W0 -Cdir -e "some \\"real\\" 'ruby' code" < file.txt)
+ end
+
+ it "returns a command with options and arguments but without code or file" do
+ @script.ruby_cmd(nil, :options => "-c", :args => "> file.txt").should ==
+ "ruby_spec_exe -w -Q -c > file.txt"
+ end
+end
+
+describe Object, "#ruby_exe" do
+ before :all do
+ @script = RubyExeSpecs.new
+ end
+
+ before :each do
+ @script.stub(:`)
+ end
+
+ it "executes (using `) the result of calling #ruby_cmd with the given arguments" do
+ code = "code"
+ options = {}
+ @script.should_receive(:ruby_cmd).and_return("ruby_cmd")
+ @script.should_receive(:`).with("ruby_cmd")
+ @script.ruby_exe(code, options)
+ end
+
+ describe "with :dir option" do
+ it "is deprecated" do
+ lambda {
+ @script.ruby_exe nil, :dir => "tmp"
+ }.should raise_error(/no longer supported, use Dir\.chdir/)
+ end
+ end
+
+ describe "with :env option" do
+ it "preserves the values of existing ENV keys" do
+ ENV["ABC"] = "123"
+ ENV.stub(:[])
+ ENV.should_receive(:[]).with("ABC")
+ @script.ruby_exe nil, :env => { :ABC => "xyz" }
+ end
+
+ it "adds the :env entries to ENV" do
+ ENV.should_receive(:[]=).with("ABC", "xyz")
+ @script.ruby_exe nil, :env => { :ABC => "xyz" }
+ end
+
+ it "deletes the :env entries in ENV when an exception is raised" do
+ ENV.should_receive(:delete).with("XYZ")
+ @script.ruby_exe nil, :env => { :XYZ => "xyz" }
+ end
+
+ it "resets the values of existing ENV keys when an exception is raised" do
+ ENV["ABC"] = "123"
+ ENV.should_receive(:[]=).with("ABC", "xyz")
+ ENV.should_receive(:[]=).with("ABC", "123")
+
+ @script.should_receive(:`).and_raise(Exception)
+ lambda do
+ @script.ruby_exe nil, :env => { :ABC => "xyz" }
+ end.should raise_error(Exception)
+ end
+ end
+end
diff --git a/spec/mspec/spec/helpers/scratch_spec.rb b/spec/mspec/spec/helpers/scratch_spec.rb
new file mode 100644
index 0000000000..6a9eb2cf73
--- /dev/null
+++ b/spec/mspec/spec/helpers/scratch_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe ScratchPad do
+ it "records an object and returns a previously recorded object" do
+ ScratchPad.record :this
+ ScratchPad.recorded.should == :this
+ end
+
+ it "clears the recorded object" do
+ ScratchPad.record :that
+ ScratchPad.recorded.should == :that
+ ScratchPad.clear
+ ScratchPad.recorded.should == nil
+ end
+
+ it "provides a convenience shortcut to append to a previously recorded object" do
+ ScratchPad.record []
+ ScratchPad << :new
+ ScratchPad << :another
+ ScratchPad.recorded.should == [:new, :another]
+ end
+end
diff --git a/spec/mspec/spec/helpers/tmp_spec.rb b/spec/mspec/spec/helpers/tmp_spec.rb
new file mode 100644
index 0000000000..afadc7f51c
--- /dev/null
+++ b/spec/mspec/spec/helpers/tmp_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+require 'mspec/guards'
+require 'mspec/helpers'
+
+describe Object, "#tmp" do
+ before :all do
+ @dir = "#{File.expand_path(Dir.pwd)}/rubyspec_temp"
+ end
+
+ it "returns a name relative to the current working directory" do
+ tmp("test.txt").should == "#{@dir}/#{SPEC_TEMP_UNIQUIFIER}-test.txt"
+ end
+
+ it "returns a 'unique' name on repeated calls" do
+ a = tmp("text.txt")
+ b = tmp("text.txt")
+ a.should_not == b
+ end
+
+ it "does not 'uniquify' the name if requested not to" do
+ tmp("test.txt", false).should == "#{@dir}/test.txt"
+ end
+
+ it "returns the name of the temporary directory when passed an empty string" do
+ tmp("").should == "#{@dir}/"
+ end
+end
diff --git a/spec/mspec/spec/integration/interpreter_spec.rb b/spec/mspec/spec/integration/interpreter_spec.rb
new file mode 100644
index 0000000000..b6fa6859d1
--- /dev/null
+++ b/spec/mspec/spec/integration/interpreter_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+describe "The interpreter passed with -t" do
+ it "is used in subprocess" do
+ fixtures = "spec/fixtures"
+ interpreter = "#{fixtures}/my_ruby"
+ out, ret = run_mspec("run", "#{fixtures}/print_interpreter_spec.rb -t #{interpreter}")
+ out = out.lines.map(&:chomp).reject { |line|
+ line == 'RUBY_DESCRIPTION'
+ }.take(3)
+ out.should == [
+ interpreter,
+ interpreter,
+ "CWD/#{interpreter}"
+ ]
+ ret.success?.should == true
+ end
+end
diff --git a/spec/mspec/spec/integration/run_spec.rb b/spec/mspec/spec/integration/run_spec.rb
new file mode 100644
index 0000000000..93d2ef8b68
--- /dev/null
+++ b/spec/mspec/spec/integration/run_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+describe "Running mspec" do
+ a_spec_output = <<EOS
+
+1)
+Foo#bar errors FAILED
+Expected 1
+ to equal 2
+
+CWD/spec/fixtures/a_spec.rb:8:in `block (2 levels) in <top (required)>'
+CWD/spec/fixtures/a_spec.rb:2:in `<top (required)>'
+CWD/bin/mspec-run:7:in `<main>'
+
+2)
+Foo#bar fails ERROR
+RuntimeError: failure
+CWD/spec/fixtures/a_spec.rb:12:in `block (2 levels) in <top (required)>'
+CWD/spec/fixtures/a_spec.rb:2:in `<top (required)>'
+CWD/bin/mspec-run:7:in `<main>'
+
+Finished in D.DDDDDD seconds
+EOS
+
+ a_stats = "1 file, 3 examples, 2 expectations, 1 failure, 1 error, 0 tagged\n"
+ ab_stats = "2 files, 4 examples, 3 expectations, 1 failure, 1 error, 0 tagged\n"
+
+ it "runs the specs" do
+ fixtures = "spec/fixtures"
+ out, ret = run_mspec("run", "#{fixtures}/a_spec.rb")
+ out.should == "RUBY_DESCRIPTION\n.FE\n#{a_spec_output}\n#{a_stats}"
+ ret.success?.should == false
+ end
+
+ it "directly with mspec-run runs the specs" do
+ fixtures = "spec/fixtures"
+ out, ret = run_mspec("-run", "#{fixtures}/a_spec.rb")
+ out.should == "RUBY_DESCRIPTION\n.FE\n#{a_spec_output}\n#{a_stats}"
+ ret.success?.should == false
+ end
+
+ it "runs the specs in parallel with -j" do
+ fixtures = "spec/fixtures"
+ out, ret = run_mspec("run", "-j #{fixtures}/a_spec.rb #{fixtures}/b_spec.rb")
+ progress_bar =
+ "\r[/ | 0% | 00:00:00] \e[0;32m 0F \e[0;32m 0E\e[0m " +
+ "\r[- | ==================50% | 00:00:00] \e[0;32m 0F \e[0;32m 0E\e[0m " +
+ "\r[\\ | ==================100%================== | 00:00:00] \e[0;32m 0F \e[0;32m 0E\e[0m "
+ out.should == "RUBY_DESCRIPTION\n#{progress_bar}\n#{a_spec_output}\n#{ab_stats}"
+ ret.success?.should == false
+ end
+end
diff --git a/spec/mspec/spec/integration/tag_spec.rb b/spec/mspec/spec/integration/tag_spec.rb
new file mode 100644
index 0000000000..d980769043
--- /dev/null
+++ b/spec/mspec/spec/integration/tag_spec.rb
@@ -0,0 +1,63 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe "Running mspec tag" do
+ before :all do
+ FileUtils.rm_rf 'spec/fixtures/tags'
+ end
+
+ after :all do
+ FileUtils.rm_rf 'spec/fixtures/tags'
+ end
+
+ it "tags the failing specs" do
+ fixtures = "spec/fixtures"
+ out, ret = run_mspec("tag", "--add fails --fail #{fixtures}/tagging_spec.rb")
+ out.should == <<EOS
+RUBY_DESCRIPTION
+.FF
+TagAction: specs tagged with 'fails':
+
+Tag#me errors
+Tag#me érròrs in unicode
+
+
+1)
+Tag#me errors FAILED
+Expected 1
+ to equal 2
+
+CWD/spec/fixtures/tagging_spec.rb:9:in `block (2 levels) in <top (required)>'
+CWD/spec/fixtures/tagging_spec.rb:3:in `<top (required)>'
+CWD/bin/mspec-tag:7:in `<main>'
+
+2)
+Tag#me érròrs in unicode FAILED
+Expected 1
+ to equal 2
+
+CWD/spec/fixtures/tagging_spec.rb:13:in `block (2 levels) in <top (required)>'
+CWD/spec/fixtures/tagging_spec.rb:3:in `<top (required)>'
+CWD/bin/mspec-tag:7:in `<main>'
+
+Finished in D.DDDDDD seconds
+
+1 file, 3 examples, 3 expectations, 2 failures, 0 errors, 0 tagged
+EOS
+ ret.success?.should == false
+ end
+
+ it "does not run already tagged specs" do
+ fixtures = "spec/fixtures"
+ out, ret = run_mspec("run", "--excl-tag fails #{fixtures}/tagging_spec.rb")
+ out.should == <<EOS
+RUBY_DESCRIPTION
+.
+
+Finished in D.DDDDDD seconds
+
+1 file, 3 examples, 1 expectation, 0 failures, 0 errors, 2 tagged
+EOS
+ ret.success?.should == true
+ end
+end
diff --git a/spec/mspec/spec/matchers/base_spec.rb b/spec/mspec/spec/matchers/base_spec.rb
new file mode 100644
index 0000000000..cc13c29d1d
--- /dev/null
+++ b/spec/mspec/spec/matchers/base_spec.rb
@@ -0,0 +1,225 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+require 'time'
+
+describe SpecPositiveOperatorMatcher, "== operator" do
+ it "raises an SpecExpectationNotMetError when expected == actual returns false" do
+ lambda {
+ SpecPositiveOperatorMatcher.new(1) == 2
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x to equal y'" do
+ SpecExpectation.should_receive(:fail_with).with("Expected 1\n", "to equal 2\n")
+ SpecPositiveOperatorMatcher.new(1) == 2
+ end
+
+ it "does not raise an exception when expected == actual returns true" do
+ SpecPositiveOperatorMatcher.new(1) == 1
+ end
+end
+
+describe SpecPositiveOperatorMatcher, "=~ operator" do
+ it "raises an SpecExpectationNotMetError when expected =~ actual returns false" do
+ lambda {
+ SpecPositiveOperatorMatcher.new('real') =~ /fake/
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected \"x\" to match y'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected \"real\"\n", "to match /fake/\n")
+ SpecPositiveOperatorMatcher.new('real') =~ /fake/
+ end
+
+ it "does not raise an exception when expected =~ actual returns true" do
+ SpecPositiveOperatorMatcher.new('real') =~ /real/
+ end
+end
+
+describe SpecPositiveOperatorMatcher, "> operator" do
+ it "raises an SpecExpectationNotMetError when expected > actual returns false" do
+ lambda {
+ SpecPositiveOperatorMatcher.new(4) > 5
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x to be greater than y'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected 4\n", "to be greater than 5\n")
+ SpecPositiveOperatorMatcher.new(4) > 5
+ end
+
+ it "does not raise an exception when expected > actual returns true" do
+ SpecPositiveOperatorMatcher.new(5) > 4
+ end
+end
+
+describe SpecPositiveOperatorMatcher, ">= operator" do
+ it "raises an SpecExpectationNotMetError when expected >= actual returns false" do
+ lambda {
+ SpecPositiveOperatorMatcher.new(4) >= 5
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x to be greater than or equal to y'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected 4\n", "to be greater than or equal to 5\n")
+ SpecPositiveOperatorMatcher.new(4) >= 5
+ end
+
+ it "does not raise an exception when expected > actual returns true" do
+ SpecPositiveOperatorMatcher.new(5) >= 4
+ SpecPositiveOperatorMatcher.new(5) >= 5
+ end
+end
+
+describe SpecPositiveOperatorMatcher, "< operater" do
+ it "raises an SpecExpectationNotMetError when expected < actual returns false" do
+ lambda {
+ SpecPositiveOperatorMatcher.new(5) < 4
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x to be less than y'" do
+ SpecExpectation.should_receive(:fail_with).with("Expected 5\n", "to be less than 4\n")
+ SpecPositiveOperatorMatcher.new(5) < 4
+ end
+
+ it "does not raise an exception when expected < actual returns true" do
+ SpecPositiveOperatorMatcher.new(4) < 5
+ end
+end
+
+describe SpecPositiveOperatorMatcher, "<= operater" do
+ it "raises an SpecExpectationNotMetError when expected < actual returns false" do
+ lambda {
+ SpecPositiveOperatorMatcher.new(5) <= 4
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x to be less than or equal to y'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected 5\n", "to be less than or equal to 4\n")
+ SpecPositiveOperatorMatcher.new(5) <= 4
+ end
+
+ it "does not raise an exception when expected < actual returns true" do
+ SpecPositiveOperatorMatcher.new(4) <= 5
+ SpecPositiveOperatorMatcher.new(4) <= 4
+ end
+end
+
+describe SpecNegativeOperatorMatcher, "== operator" do
+ it "raises an SpecExpectationNotMetError when expected == actual returns true" do
+ lambda {
+ SpecNegativeOperatorMatcher.new(1) == 1
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x not to equal y'" do
+ SpecExpectation.should_receive(:fail_with).with("Expected 1\n", "not to equal 1\n")
+ SpecNegativeOperatorMatcher.new(1) == 1
+ end
+
+ it "does not raise an exception when expected == actual returns false" do
+ SpecNegativeOperatorMatcher.new(1) == 2
+ end
+end
+
+describe SpecNegativeOperatorMatcher, "=~ operator" do
+ it "raises an SpecExpectationNotMetError when expected =~ actual returns true" do
+ lambda {
+ SpecNegativeOperatorMatcher.new('real') =~ /real/
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected \"x\" not to match /y/'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected \"real\"\n", "not to match /real/\n")
+ SpecNegativeOperatorMatcher.new('real') =~ /real/
+ end
+
+ it "does not raise an exception when expected =~ actual returns false" do
+ SpecNegativeOperatorMatcher.new('real') =~ /fake/
+ end
+end
+
+describe SpecNegativeOperatorMatcher, "< operator" do
+ it "raises an SpecExpectationNotMetError when expected < actual returns true" do
+ lambda {
+ SpecNegativeOperatorMatcher.new(4) < 5
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x not to be less than y'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected 4\n", "not to be less than 5\n")
+ SpecNegativeOperatorMatcher.new(4) < 5
+ end
+
+ it "does not raise an exception when expected < actual returns false" do
+ SpecNegativeOperatorMatcher.new(5) < 4
+ end
+end
+
+describe SpecNegativeOperatorMatcher, "<= operator" do
+ it "raises an SpecExpectationNotMetError when expected <= actual returns true" do
+ lambda {
+ SpecNegativeOperatorMatcher.new(4) <= 5
+ }.should raise_error(SpecExpectationNotMetError)
+ lambda {
+ SpecNegativeOperatorMatcher.new(5) <= 5
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x not to be less than or equal to y'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected 4\n", "not to be less than or equal to 5\n")
+ SpecNegativeOperatorMatcher.new(4) <= 5
+ end
+
+ it "does not raise an exception when expected <= actual returns false" do
+ SpecNegativeOperatorMatcher.new(5) <= 4
+ end
+end
+
+describe SpecNegativeOperatorMatcher, "> operator" do
+ it "raises an SpecExpectationNotMetError when expected > actual returns true" do
+ lambda {
+ SpecNegativeOperatorMatcher.new(5) > 4
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x not to be greater than y'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected 5\n", "not to be greater than 4\n")
+ SpecNegativeOperatorMatcher.new(5) > 4
+ end
+
+ it "does not raise an exception when expected > actual returns false" do
+ SpecNegativeOperatorMatcher.new(4) > 5
+ end
+end
+
+describe SpecNegativeOperatorMatcher, ">= operator" do
+ it "raises an SpecExpectationNotMetError when expected >= actual returns true" do
+ lambda {
+ SpecNegativeOperatorMatcher.new(5) >= 4
+ }.should raise_error(SpecExpectationNotMetError)
+ lambda {
+ SpecNegativeOperatorMatcher.new(5) >= 5
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "provides a failure message that 'Expected x not to be greater than or equal to y'" do
+ SpecExpectation.should_receive(:fail_with).with(
+ "Expected 5\n", "not to be greater than or equal to 4\n")
+ SpecNegativeOperatorMatcher.new(5) >= 4
+ end
+
+ it "does not raise an exception when expected >= actual returns false" do
+ SpecNegativeOperatorMatcher.new(4) >= 5
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_an_instance_of_spec.rb b/spec/mspec/spec/matchers/be_an_instance_of_spec.rb
new file mode 100644
index 0000000000..7f2126df7d
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_an_instance_of_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+module BeAnInOfSpecs
+ class A
+ end
+
+ class B < A
+ end
+
+ class C < B
+ end
+end
+
+describe BeAnInstanceOfMatcher do
+ it "matches when actual is an instance_of? expected" do
+ a = BeAnInOfSpecs::A.new
+ BeAnInstanceOfMatcher.new(BeAnInOfSpecs::A).matches?(a).should be_true
+
+ b = BeAnInOfSpecs::B.new
+ BeAnInstanceOfMatcher.new(BeAnInOfSpecs::B).matches?(b).should be_true
+ end
+
+ it "does not match when actual is not an instance_of? expected" do
+ a = BeAnInOfSpecs::A.new
+ BeAnInstanceOfMatcher.new(BeAnInOfSpecs::B).matches?(a).should be_false
+
+ b = BeAnInOfSpecs::B.new
+ BeAnInstanceOfMatcher.new(BeAnInOfSpecs::A).matches?(b).should be_false
+
+ c = BeAnInOfSpecs::C.new
+ BeAnInstanceOfMatcher.new(BeAnInOfSpecs::A).matches?(c).should be_false
+ BeAnInstanceOfMatcher.new(BeAnInOfSpecs::B).matches?(c).should be_false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeAnInstanceOfMatcher.new(Numeric)
+ matcher.matches?("string")
+ matcher.failure_message.should == [
+ "Expected \"string\" (String)", "to be an instance of Numeric"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeAnInstanceOfMatcher.new(Numeric)
+ matcher.matches?(4.0)
+ matcher.negative_failure_message.should == [
+ "Expected 4.0 (Float)", "not to be an instance of Numeric"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_ancestor_of_spec.rb b/spec/mspec/spec/matchers/be_ancestor_of_spec.rb
new file mode 100644
index 0000000000..c6bd1a26c7
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_ancestor_of_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class Parent; end
+class Child < Parent; end
+
+describe BeAncestorOfMatcher do
+ it "matches when actual is an ancestor of expected" do
+ BeAncestorOfMatcher.new(Child).matches?(Parent).should == true
+ end
+
+ it "does not match when actual is not an ancestor of expected" do
+ BeAncestorOfMatcher.new(Parent).matches?(Child).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeAncestorOfMatcher.new(Parent)
+ matcher.matches?(Child)
+ matcher.failure_message.should == ["Expected Child", "to be an ancestor of Parent"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeAncestorOfMatcher.new(Child)
+ matcher.matches?(Parent)
+ matcher.negative_failure_message.should == ["Expected Parent", "not to be an ancestor of Child"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_close_spec.rb b/spec/mspec/spec/matchers/be_close_spec.rb
new file mode 100644
index 0000000000..3ced61dc7a
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_close_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+# Adapted from RSpec 1.0.8
+describe BeCloseMatcher do
+ it "matches when actual == expected" do
+ BeCloseMatcher.new(5.0, 0.5).matches?(5.0).should == true
+ end
+
+ it "matches when actual < (expected + tolerance)" do
+ BeCloseMatcher.new(5.0, 0.5).matches?(5.49).should == true
+ end
+
+ it "matches when actual > (expected - tolerance)" do
+ BeCloseMatcher.new(5.0, 0.5).matches?(4.51).should == true
+ end
+
+ it "does not match when actual == (expected + tolerance)" do
+ BeCloseMatcher.new(5.0, 0.5).matches?(5.5).should == false
+ end
+
+ it "does not match when actual == (expected - tolerance)" do
+ BeCloseMatcher.new(5.0, 0.5).matches?(4.5).should == false
+ end
+
+ it "does not match when actual < (expected - tolerance)" do
+ BeCloseMatcher.new(5.0, 0.5).matches?(4.49).should == false
+ end
+
+ it "does not match when actual > (expected + tolerance)" do
+ BeCloseMatcher.new(5.0, 0.5).matches?(5.51).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeCloseMatcher.new(5.0, 0.5)
+ matcher.matches?(5.5)
+ matcher.failure_message.should == ["Expected 5.0", "to be within +/- 0.5 of 5.5"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeCloseMatcher.new(5.0, 0.5)
+ matcher.matches?(5.0)
+ matcher.negative_failure_message.should == ["Expected 5.0", "not to be within +/- 0.5 of 5.0"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_computed_by_spec.rb b/spec/mspec/spec/matchers/be_computed_by_spec.rb
new file mode 100644
index 0000000000..9833e211a4
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_computed_by_spec.rb
@@ -0,0 +1,42 @@
+require 'spec_helper'
+require 'mspec/matchers'
+
+describe BeComputedByMatcher do
+ it "matches when all entries in the Array compute" do
+ array = [ [65, "A"],
+ [90, "Z"] ]
+ BeComputedByMatcher.new(:chr).matches?(array).should be_true
+ end
+
+ it "matches when all entries in the Array with arguments compute" do
+ array = [ [1, 2, 3],
+ [2, 4, 6] ]
+ BeComputedByMatcher.new(:+).matches?(array).should be_true
+ end
+
+ it "does not match when any entry in the Array does not compute" do
+ array = [ [65, "A" ],
+ [91, "Z" ] ]
+ BeComputedByMatcher.new(:chr).matches?(array).should be_false
+ end
+
+ it "accepts an argument list to apply to each method call" do
+ array = [ [65, "1000001" ],
+ [90, "1011010" ] ]
+ BeComputedByMatcher.new(:to_s, 2).matches?(array).should be_true
+ end
+
+ it "does not match when any entry in the Array with arguments does not compute" do
+ array = [ [1, 2, 3],
+ [2, 4, 7] ]
+ BeComputedByMatcher.new(:+).matches?(array).should be_false
+ end
+
+ it "provides a useful failure message" do
+ array = [ [65, "A" ],
+ [91, "Z" ] ]
+ matcher = BeComputedByMatcher.new(:chr)
+ matcher.matches?(array)
+ matcher.failure_message.should == ["Expected \"Z\"", "to be computed by 91.chr (computed \"[\" instead)"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_empty_spec.rb b/spec/mspec/spec/matchers/be_empty_spec.rb
new file mode 100644
index 0000000000..cb8663f5ee
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_empty_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe BeEmptyMatcher do
+ it "matches when actual is empty" do
+ BeEmptyMatcher.new.matches?("").should == true
+ end
+
+ it "does not match when actual is not empty" do
+ BeEmptyMatcher.new.matches?([10]).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeEmptyMatcher.new
+ matcher.matches?("not empty string")
+ matcher.failure_message.should == ["Expected \"not empty string\"", "to be empty"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeEmptyMatcher.new
+ matcher.matches?("")
+ matcher.negative_failure_message.should == ["Expected \"\"", "not to be empty"]
+ end
+end
+
diff --git a/spec/mspec/spec/matchers/be_false_spec.rb b/spec/mspec/spec/matchers/be_false_spec.rb
new file mode 100644
index 0000000000..31afd24ebc
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_false_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe BeFalseMatcher do
+ it "matches when actual is false" do
+ BeFalseMatcher.new.matches?(false).should == true
+ end
+
+ it "does not match when actual is not false" do
+ BeFalseMatcher.new.matches?("").should == false
+ BeFalseMatcher.new.matches?(true).should == false
+ BeFalseMatcher.new.matches?(nil).should == false
+ BeFalseMatcher.new.matches?(0).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeFalseMatcher.new
+ matcher.matches?("some string")
+ matcher.failure_message.should == ["Expected \"some string\"", "to be false"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeFalseMatcher.new
+ matcher.matches?(false)
+ matcher.negative_failure_message.should == ["Expected false", "not to be false"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_kind_of_spec.rb b/spec/mspec/spec/matchers/be_kind_of_spec.rb
new file mode 100644
index 0000000000..554ae6aa82
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_kind_of_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe BeKindOfMatcher do
+ it "matches when actual is a kind_of? expected" do
+ BeKindOfMatcher.new(Integer).matches?(1).should == true
+ BeKindOfMatcher.new(Fixnum).matches?(2).should == true
+ BeKindOfMatcher.new(Regexp).matches?(/m/).should == true
+ end
+
+ it "does not match when actual is not a kind_of? expected" do
+ BeKindOfMatcher.new(Integer).matches?(1.5).should == false
+ BeKindOfMatcher.new(String).matches?(:a).should == false
+ BeKindOfMatcher.new(Hash).matches?([]).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeKindOfMatcher.new(Numeric)
+ matcher.matches?('string')
+ matcher.failure_message.should == [
+ "Expected \"string\" (String)", "to be kind of Numeric"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeKindOfMatcher.new(Numeric)
+ matcher.matches?(4.0)
+ matcher.negative_failure_message.should == [
+ "Expected 4.0 (Float)", "not to be kind of Numeric"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_nan_spec.rb b/spec/mspec/spec/matchers/be_nan_spec.rb
new file mode 100644
index 0000000000..2062763a92
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_nan_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/guards'
+require 'mspec/helpers'
+require 'mspec/matchers'
+
+describe BeNaNMatcher do
+ it "matches when actual is NaN" do
+ BeNaNMatcher.new.matches?(nan_value).should == true
+ end
+
+ it "does not match when actual is not NaN" do
+ BeNaNMatcher.new.matches?(1.0).should == false
+ BeNaNMatcher.new.matches?(0).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeNaNMatcher.new
+ matcher.matches?(0)
+ matcher.failure_message.should == ["Expected 0", "to be NaN"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeNaNMatcher.new
+ matcher.matches?(nan_value)
+ matcher.negative_failure_message.should == ["Expected NaN", "not to be NaN"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_nil_spec.rb b/spec/mspec/spec/matchers/be_nil_spec.rb
new file mode 100644
index 0000000000..6551feb5de
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_nil_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe BeNilMatcher do
+ it "matches when actual is nil" do
+ BeNilMatcher.new.matches?(nil).should == true
+ end
+
+ it "does not match when actual is not nil" do
+ BeNilMatcher.new.matches?("").should == false
+ BeNilMatcher.new.matches?(false).should == false
+ BeNilMatcher.new.matches?(0).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeNilMatcher.new
+ matcher.matches?("some string")
+ matcher.failure_message.should == ["Expected \"some string\"", "to be nil"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeNilMatcher.new
+ matcher.matches?(nil)
+ matcher.negative_failure_message.should == ["Expected nil", "not to be nil"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_true_or_false_spec.rb b/spec/mspec/spec/matchers/be_true_or_false_spec.rb
new file mode 100644
index 0000000000..3edffcb1b1
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_true_or_false_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe BeTrueOrFalseMatcher do
+ it "matches when actual is true" do
+ BeTrueOrFalseMatcher.new.matches?(true).should == true
+ end
+
+ it "matches when actual is false" do
+ BeTrueOrFalseMatcher.new.matches?(false).should == true
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeTrueOrFalseMatcher.new
+ matcher.matches?("some string")
+ matcher.failure_message.should == ["Expected \"some string\"", "to be true or false"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/be_true_spec.rb b/spec/mspec/spec/matchers/be_true_spec.rb
new file mode 100644
index 0000000000..90c89b3911
--- /dev/null
+++ b/spec/mspec/spec/matchers/be_true_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe BeTrueMatcher do
+ it "matches when actual is true" do
+ BeTrueMatcher.new.matches?(true).should == true
+ end
+
+ it "does not match when actual is not true" do
+ BeTrueMatcher.new.matches?("").should == false
+ BeTrueMatcher.new.matches?(false).should == false
+ BeTrueMatcher.new.matches?(nil).should == false
+ BeTrueMatcher.new.matches?(0).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = BeTrueMatcher.new
+ matcher.matches?("some string")
+ matcher.failure_message.should == ["Expected \"some string\"", "to be true"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = BeTrueMatcher.new
+ matcher.matches?(true)
+ matcher.negative_failure_message.should == ["Expected true", "not to be true"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/block_caller_spec.rb b/spec/mspec/spec/matchers/block_caller_spec.rb
new file mode 100644
index 0000000000..d6793b9779
--- /dev/null
+++ b/spec/mspec/spec/matchers/block_caller_spec.rb
@@ -0,0 +1,13 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe BlockingMatcher do
+ it 'matches when a Proc blocks the caller' do
+ BlockingMatcher.new.matches?(proc { sleep }).should == true
+ end
+
+ it 'does not match when a Proc does not block the caller' do
+ BlockingMatcher.new.matches?(proc { 1 }).should == false
+ end
+end
diff --git a/spec/mspec/spec/matchers/complain_spec.rb b/spec/mspec/spec/matchers/complain_spec.rb
new file mode 100644
index 0000000000..709b57be6c
--- /dev/null
+++ b/spec/mspec/spec/matchers/complain_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe ComplainMatcher do
+ it "matches when executing the proc results in output to $stderr" do
+ proc = lambda { warn "I'm gonna tell yo mama" }
+ ComplainMatcher.new(nil).matches?(proc).should == true
+ end
+
+ it "maches when executing the proc results in the expected output to $stderr" do
+ proc = lambda { warn "Que haces?" }
+ ComplainMatcher.new("Que haces?\n").matches?(proc).should == true
+ ComplainMatcher.new("Que pasa?\n").matches?(proc).should == false
+ ComplainMatcher.new(/Que/).matches?(proc).should == true
+ ComplainMatcher.new(/Quoi/).matches?(proc).should == false
+ end
+
+ it "does not match when there is no output to $stderr" do
+ ComplainMatcher.new(nil).matches?(lambda {}).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = ComplainMatcher.new(nil)
+ matcher.matches?(lambda { })
+ matcher.failure_message.should == ["Expected a warning", "but received none"]
+ matcher = ComplainMatcher.new("listen here")
+ matcher.matches?(lambda { warn "look out" })
+ matcher.failure_message.should ==
+ ["Expected warning: \"listen here\"", "but got: \"look out\""]
+ matcher = ComplainMatcher.new(/talk/)
+ matcher.matches?(lambda { warn "listen up" })
+ matcher.failure_message.should ==
+ ["Expected warning to match: /talk/", "but got: \"listen up\""]
+ end
+
+ it "provides a useful negative failure message" do
+ proc = lambda { warn "ouch" }
+ matcher = ComplainMatcher.new(nil)
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Unexpected warning: ", "\"ouch\""]
+ matcher = ComplainMatcher.new("ouchy")
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected warning: \"ouchy\"", "but got: \"ouch\""]
+ matcher = ComplainMatcher.new(/ou/)
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected warning not to match: /ou/", "but got: \"ouch\""]
+ end
+end
diff --git a/spec/mspec/spec/matchers/eql_spec.rb b/spec/mspec/spec/matchers/eql_spec.rb
new file mode 100644
index 0000000000..711ebdb679
--- /dev/null
+++ b/spec/mspec/spec/matchers/eql_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe EqlMatcher do
+ it "matches when actual is eql? to expected" do
+ EqlMatcher.new(1).matches?(1).should == true
+ EqlMatcher.new(1.5).matches?(1.5).should == true
+ EqlMatcher.new("red").matches?("red").should == true
+ EqlMatcher.new(:blue).matches?(:blue).should == true
+ EqlMatcher.new(Object).matches?(Object).should == true
+
+ o = Object.new
+ EqlMatcher.new(o).matches?(o).should == true
+ end
+
+ it "does not match when actual is not eql? to expected" do
+ EqlMatcher.new(1).matches?(1.0).should == false
+ EqlMatcher.new(Hash).matches?(Object).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = EqlMatcher.new("red")
+ matcher.matches?("red")
+ matcher.failure_message.should == ["Expected \"red\"\n", "to have same value and type as \"red\"\n"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = EqlMatcher.new(1)
+ matcher.matches?(1.0)
+ matcher.negative_failure_message.should == ["Expected 1.0\n", "not to have same value or type as 1\n"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/equal_element_spec.rb b/spec/mspec/spec/matchers/equal_element_spec.rb
new file mode 100644
index 0000000000..45b8390364
--- /dev/null
+++ b/spec/mspec/spec/matchers/equal_element_spec.rb
@@ -0,0 +1,75 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe EqualElementMatcher do
+ it "matches if it finds an element with the passed name, no matter what attributes/content" do
+ EqualElementMatcher.new("A").matches?('<A></A>').should be_true
+ EqualElementMatcher.new("A").matches?('<A HREF="http://example.com"></A>').should be_true
+ EqualElementMatcher.new("A").matches?('<A HREF="http://example.com"></A>').should be_true
+
+ EqualElementMatcher.new("BASE").matches?('<BASE></A>').should be_false
+ EqualElementMatcher.new("BASE").matches?('<A></BASE>').should be_false
+ EqualElementMatcher.new("BASE").matches?('<A></A>').should be_false
+ EqualElementMatcher.new("BASE").matches?('<A HREF="http://example.com"></A>').should be_false
+ EqualElementMatcher.new("BASE").matches?('<A HREF="http://example.com"></A>').should be_false
+ end
+
+ it "matches if it finds an element with the passed name and the passed attributes" do
+ EqualElementMatcher.new("A", {}).matches?('<A></A>').should be_true
+ EqualElementMatcher.new("A", nil).matches?('<A HREF="http://example.com"></A>').should be_true
+ EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('<A HREF="http://example.com"></A>').should be_true
+
+ EqualElementMatcher.new("A", {}).matches?('<A HREF="http://example.com"></A>').should be_false
+ EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('<A></A>').should be_false
+ EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('<A HREF="http://test.com"></A>').should be_false
+ EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('<A HREF="http://example.com" HREF="http://example.com"></A>').should be_false
+ end
+
+ it "matches if it finds an element with the passed name, the passed attributes and the passed content" do
+ EqualElementMatcher.new("A", {}, "").matches?('<A></A>').should be_true
+ EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('<A HREF="http://example.com">Example</A>').should be_true
+
+ EqualElementMatcher.new("A", {}, "Test").matches?('<A></A>').should be_false
+ EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('<A HREF="http://example.com"></A>').should be_false
+ EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('<A HREF="http://example.com">Test</A>').should be_false
+ end
+
+ it "can match unclosed elements" do
+ EqualElementMatcher.new("BASE", nil, nil, :not_closed => true).matches?('<BASE>').should be_true
+ EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, nil, :not_closed => true).matches?('<BASE HREF="http://example.com">').should be_true
+ EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "Example", :not_closed => true).matches?('<BASE HREF="http://example.com">Example').should be_true
+
+ EqualElementMatcher.new("BASE", {}, nil, :not_closed => true).matches?('<BASE HREF="http://example.com">').should be_false
+ EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "", :not_closed => true).matches?('<BASE HREF="http://example.com">Example').should be_false
+ EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "Test", :not_closed => true).matches?('<BASE HREF="http://example.com">Example').should be_false
+ end
+
+ it "provides a useful failure message" do
+ equal_element = EqualElementMatcher.new("A", {}, "Test")
+ equal_element.matches?('<A></A>').should be_false
+ equal_element.failure_message.should == [%{Expected "<A></A>"\n}, %{to be a 'A' element with no attributes and "Test" as content}]
+
+ equal_element = EqualElementMatcher.new("A", {}, "")
+ equal_element.matches?('<A>Test</A>').should be_false
+ equal_element.failure_message.should == [%{Expected "<A>Test</A>"\n}, %{to be a 'A' element with no attributes and no content}]
+
+ equal_element = EqualElementMatcher.new("A", "HREF" => "http://www.example.com")
+ equal_element.matches?('<A>Test</A>').should be_false
+ equal_element.failure_message.should == [%{Expected "<A>Test</A>"\n}, %{to be a 'A' element with HREF="http://www.example.com" and any content}]
+ end
+
+ it "provides a useful negative failure message" do
+ equal_element = EqualElementMatcher.new("A", {}, "Test")
+ equal_element.matches?('<A></A>').should be_false
+ equal_element.negative_failure_message.should == [%{Expected "<A></A>"\n}, %{not to be a 'A' element with no attributes and "Test" as content}]
+
+ equal_element = EqualElementMatcher.new("A", {}, "")
+ equal_element.matches?('<A>Test</A>').should be_false
+ equal_element.negative_failure_message.should == [%{Expected "<A>Test</A>"\n}, %{not to be a 'A' element with no attributes and no content}]
+
+ equal_element = EqualElementMatcher.new("A", "HREF" => "http://www.example.com")
+ equal_element.matches?('<A>Test</A>').should be_false
+ equal_element.negative_failure_message.should == [%{Expected "<A>Test</A>"\n}, %{not to be a 'A' element with HREF="http://www.example.com" and any content}]
+ end
+end
diff --git a/spec/mspec/spec/matchers/equal_spec.rb b/spec/mspec/spec/matchers/equal_spec.rb
new file mode 100644
index 0000000000..ca7bf83fdd
--- /dev/null
+++ b/spec/mspec/spec/matchers/equal_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe EqualMatcher do
+ it "matches when actual is equal? to expected" do
+ EqualMatcher.new(1).matches?(1).should == true
+ EqualMatcher.new(:blue).matches?(:blue).should == true
+ EqualMatcher.new(Object).matches?(Object).should == true
+
+ o = Object.new
+ EqualMatcher.new(o).matches?(o).should == true
+ end
+
+ it "does not match when actual is not a equal? to expected" do
+ EqualMatcher.new(1).matches?(1.0).should == false
+ EqualMatcher.new("blue").matches?("blue").should == false
+ EqualMatcher.new(Hash).matches?(Object).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = EqualMatcher.new("red")
+ matcher.matches?("red")
+ matcher.failure_message.should == ["Expected \"red\"\n", "to be identical to \"red\"\n"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = EqualMatcher.new(1)
+ matcher.matches?(1)
+ matcher.negative_failure_message.should == ["Expected 1\n", "not to be identical to 1\n"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_class_variable_spec.rb b/spec/mspec/spec/matchers/have_class_variable_spec.rb
new file mode 100644
index 0000000000..e440050056
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_class_variable_spec.rb
@@ -0,0 +1,62 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class IVarModMock; end
+
+shared_examples_for "have_class_variable, on all Ruby versions" do
+ after :all do
+ Object.const_set :RUBY_VERSION, @ruby_version
+ end
+
+ it "matches when mod has the class variable, given as string" do
+ matcher = HaveClassVariableMatcher.new('@foo')
+ matcher.matches?(IVarModMock).should be_true
+ end
+
+ it "matches when mod has the class variable, given as symbol" do
+ matcher = HaveClassVariableMatcher.new(:@foo)
+ matcher.matches?(IVarModMock).should be_true
+ end
+
+ it "does not match when mod hasn't got the class variable, given as string" do
+ matcher = HaveClassVariableMatcher.new('@bar')
+ matcher.matches?(IVarModMock).should be_false
+ end
+
+ it "does not match when mod hasn't got the class variable, given as symbol" do
+ matcher = HaveClassVariableMatcher.new(:@bar)
+ matcher.matches?(IVarModMock).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HaveClassVariableMatcher.new(:@bar)
+ matcher.matches?(IVarModMock)
+ matcher.failure_message.should == [
+ "Expected IVarModMock to have class variable '@bar'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure messoge for #should_not" do
+ matcher = HaveClassVariableMatcher.new(:@bar)
+ matcher.matches?(IVarModMock)
+ matcher.negative_failure_message.should == [
+ "Expected IVarModMock NOT to have class variable '@bar'",
+ "but it does"
+ ]
+ end
+end
+
+describe HaveClassVariableMatcher, "on RUBY_VERSION >= 1.9" do
+ before :all do
+ @ruby_version = Object.const_get :RUBY_VERSION
+ Object.const_set :RUBY_VERSION, '1.9.0'
+
+ def IVarModMock.class_variables
+ [:@foo]
+ end
+ end
+
+ it_should_behave_like "have_class_variable, on all Ruby versions"
+end
diff --git a/spec/mspec/spec/matchers/have_constant_spec.rb b/spec/mspec/spec/matchers/have_constant_spec.rb
new file mode 100644
index 0000000000..20c5f161d4
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_constant_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class HCMSpecs
+ X = :x
+end
+
+describe HaveConstantMatcher do
+ it "matches when mod has the constant" do
+ matcher = HaveConstantMatcher.new :X
+ matcher.matches?(HCMSpecs).should be_true
+ end
+
+ it "does not match when mod does not have the constant" do
+ matcher = HaveConstantMatcher.new :A
+ matcher.matches?(HCMSpecs).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HaveConstantMatcher.new :A
+ matcher.matches?(HCMSpecs)
+ matcher.failure_message.should == [
+ "Expected HCMSpecs to have constant 'A'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure messoge for #should_not" do
+ matcher = HaveConstantMatcher.new :X
+ matcher.matches?(HCMSpecs)
+ matcher.negative_failure_message.should == [
+ "Expected HCMSpecs NOT to have constant 'X'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_instance_method_spec.rb b/spec/mspec/spec/matchers/have_instance_method_spec.rb
new file mode 100644
index 0000000000..738f5f875d
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_instance_method_spec.rb
@@ -0,0 +1,53 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class HIMMSpecs
+ def instance_method
+ end
+
+ class Subclass < HIMMSpecs
+ def instance_sub_method
+ end
+ end
+end
+
+describe HaveInstanceMethodMatcher do
+ it "inherits from MethodMatcher" do
+ HaveInstanceMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ end
+
+ it "matches when mod has the instance method" do
+ matcher = HaveInstanceMethodMatcher.new :instance_method
+ matcher.matches?(HIMMSpecs).should be_true
+ matcher.matches?(HIMMSpecs::Subclass).should be_true
+ end
+
+ it "does not match when mod does not have the instance method" do
+ matcher = HaveInstanceMethodMatcher.new :another_method
+ matcher.matches?(HIMMSpecs).should be_false
+ end
+
+ it "does not match if the method is in a superclass and include_super is false" do
+ matcher = HaveInstanceMethodMatcher.new :instance_method, false
+ matcher.matches?(HIMMSpecs::Subclass).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HaveInstanceMethodMatcher.new :some_method
+ matcher.matches?(HIMMSpecs)
+ matcher.failure_message.should == [
+ "Expected HIMMSpecs to have instance method 'some_method'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure messoge for #should_not" do
+ matcher = HaveInstanceMethodMatcher.new :some_method
+ matcher.matches?(HIMMSpecs)
+ matcher.negative_failure_message.should == [
+ "Expected HIMMSpecs NOT to have instance method 'some_method'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_instance_variable_spec.rb b/spec/mspec/spec/matchers/have_instance_variable_spec.rb
new file mode 100644
index 0000000000..ababb38bc7
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_instance_variable_spec.rb
@@ -0,0 +1,61 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+shared_examples_for "have_instance_variable, on all Ruby versions" do
+ after :all do
+ Object.const_set :RUBY_VERSION, @ruby_version
+ end
+
+ it "matches when object has the instance variable, given as string" do
+ matcher = HaveInstanceVariableMatcher.new('@foo')
+ matcher.matches?(@object).should be_true
+ end
+
+ it "matches when object has the instance variable, given as symbol" do
+ matcher = HaveInstanceVariableMatcher.new(:@foo)
+ matcher.matches?(@object).should be_true
+ end
+
+ it "does not match when object hasn't got the instance variable, given as string" do
+ matcher = HaveInstanceVariableMatcher.new('@bar')
+ matcher.matches?(@object).should be_false
+ end
+
+ it "does not match when object hasn't got the instance variable, given as symbol" do
+ matcher = HaveInstanceVariableMatcher.new(:@bar)
+ matcher.matches?(@object).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HaveInstanceVariableMatcher.new(:@bar)
+ matcher.matches?(@object)
+ matcher.failure_message.should == [
+ "Expected #{@object.inspect} to have instance variable '@bar'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure messoge for #should_not" do
+ matcher = HaveInstanceVariableMatcher.new(:@bar)
+ matcher.matches?(@object)
+ matcher.negative_failure_message.should == [
+ "Expected #{@object.inspect} NOT to have instance variable '@bar'",
+ "but it does"
+ ]
+ end
+end
+
+describe HaveInstanceVariableMatcher, "on RUBY_VERSION >= 1.9" do
+ before :all do
+ @ruby_version = Object.const_get :RUBY_VERSION
+ Object.const_set :RUBY_VERSION, '1.9.0'
+
+ @object = Object.new
+ def @object.instance_variables
+ [:@foo]
+ end
+ end
+
+ it_should_behave_like "have_instance_variable, on all Ruby versions"
+end
diff --git a/spec/mspec/spec/matchers/have_method_spec.rb b/spec/mspec/spec/matchers/have_method_spec.rb
new file mode 100644
index 0000000000..41bd485119
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_method_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class HMMSpecs
+ def instance_method
+ end
+
+ class Subclass < HMMSpecs
+ def instance_sub_method
+ end
+ end
+end
+
+describe HaveMethodMatcher do
+ it "inherits from MethodMatcher" do
+ HaveMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ end
+
+ it "matches when mod has the method" do
+ matcher = HaveMethodMatcher.new :instance_method
+ matcher.matches?(HMMSpecs).should be_true
+ matcher.matches?(HMMSpecs.new).should be_true
+ matcher.matches?(HMMSpecs::Subclass).should be_true
+ matcher.matches?(HMMSpecs::Subclass.new).should be_true
+ end
+
+ it "does not match when mod does not have the method" do
+ matcher = HaveMethodMatcher.new :another_method
+ matcher.matches?(HMMSpecs).should be_false
+ end
+
+ it "does not match if the method is in a superclass and include_super is false" do
+ matcher = HaveMethodMatcher.new :instance_method, false
+ matcher.matches?(HMMSpecs::Subclass).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HaveMethodMatcher.new :some_method
+ matcher.matches?(HMMSpecs)
+ matcher.failure_message.should == [
+ "Expected HMMSpecs to have method 'some_method'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure messoge for #should_not" do
+ matcher = HaveMethodMatcher.new :some_method
+ matcher.matches?(HMMSpecs)
+ matcher.negative_failure_message.should == [
+ "Expected HMMSpecs NOT to have method 'some_method'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_private_instance_method_spec.rb b/spec/mspec/spec/matchers/have_private_instance_method_spec.rb
new file mode 100644
index 0000000000..827c6b6034
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_private_instance_method_spec.rb
@@ -0,0 +1,57 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class HPIMMSpecs
+ private
+
+ def private_method
+ end
+
+ class Subclass < HPIMMSpecs
+ private
+
+ def private_sub_method
+ end
+ end
+end
+
+describe HavePrivateInstanceMethodMatcher do
+ it "inherits from MethodMatcher" do
+ HavePrivateInstanceMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ end
+
+ it "matches when mod has the private instance method" do
+ matcher = HavePrivateInstanceMethodMatcher.new :private_method
+ matcher.matches?(HPIMMSpecs).should be_true
+ matcher.matches?(HPIMMSpecs::Subclass).should be_true
+ end
+
+ it "does not match when mod does not have the private instance method" do
+ matcher = HavePrivateInstanceMethodMatcher.new :another_method
+ matcher.matches?(HPIMMSpecs).should be_false
+ end
+
+ it "does not match if the method is in a superclass and include_super is false" do
+ matcher = HavePrivateInstanceMethodMatcher.new :private_method, false
+ matcher.matches?(HPIMMSpecs::Subclass).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HavePrivateInstanceMethodMatcher.new :some_method
+ matcher.matches?(HPIMMSpecs)
+ matcher.failure_message.should == [
+ "Expected HPIMMSpecs to have private instance method 'some_method'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure message for #should_not" do
+ matcher = HavePrivateInstanceMethodMatcher.new :some_method
+ matcher.matches?(HPIMMSpecs)
+ matcher.negative_failure_message.should == [
+ "Expected HPIMMSpecs NOT to have private instance method 'some_method'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_private_method_spec.rb b/spec/mspec/spec/matchers/have_private_method_spec.rb
new file mode 100644
index 0000000000..e63a9a3c2f
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_private_method_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class HPMMSpecs
+ def self.private_method
+ end
+
+ private_class_method :private_method
+end
+
+describe HavePrivateMethodMatcher do
+ it "inherits from MethodMatcher" do
+ HavePrivateMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ end
+
+ it "matches when mod has the private method" do
+ matcher = HavePrivateMethodMatcher.new :private_method
+ matcher.matches?(HPMMSpecs).should be_true
+ end
+
+ it "does not match when mod does not have the private method" do
+ matcher = HavePrivateMethodMatcher.new :another_method
+ matcher.matches?(HPMMSpecs).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HavePrivateMethodMatcher.new :some_method
+ matcher.matches?(HPMMSpecs)
+ matcher.failure_message.should == [
+ "Expected HPMMSpecs to have private method 'some_method'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure message for #should_not" do
+ matcher = HavePrivateMethodMatcher.new :private_method
+ matcher.matches?(HPMMSpecs)
+ matcher.negative_failure_message.should == [
+ "Expected HPMMSpecs NOT to have private method 'private_method'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_protected_instance_method_spec.rb b/spec/mspec/spec/matchers/have_protected_instance_method_spec.rb
new file mode 100644
index 0000000000..460d0368fb
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_protected_instance_method_spec.rb
@@ -0,0 +1,57 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class HPIMMSpecs
+ protected
+
+ def protected_method
+ end
+
+ class Subclass < HPIMMSpecs
+ protected
+
+ def protected_sub_method
+ end
+ end
+end
+
+describe HaveProtectedInstanceMethodMatcher do
+ it "inherits from MethodMatcher" do
+ HaveProtectedInstanceMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ end
+
+ it "matches when mod has the protected instance method" do
+ matcher = HaveProtectedInstanceMethodMatcher.new :protected_method
+ matcher.matches?(HPIMMSpecs).should be_true
+ matcher.matches?(HPIMMSpecs::Subclass).should be_true
+ end
+
+ it "does not match when mod does not have the protected instance method" do
+ matcher = HaveProtectedInstanceMethodMatcher.new :another_method
+ matcher.matches?(HPIMMSpecs).should be_false
+ end
+
+ it "does not match if the method is in a superclass and include_super is false" do
+ matcher = HaveProtectedInstanceMethodMatcher.new :protected_method, false
+ matcher.matches?(HPIMMSpecs::Subclass).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HaveProtectedInstanceMethodMatcher.new :some_method
+ matcher.matches?(HPIMMSpecs)
+ matcher.failure_message.should == [
+ "Expected HPIMMSpecs to have protected instance method 'some_method'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure messoge for #should_not" do
+ matcher = HaveProtectedInstanceMethodMatcher.new :some_method
+ matcher.matches?(HPIMMSpecs)
+ matcher.negative_failure_message.should == [
+ "Expected HPIMMSpecs NOT to have protected instance method 'some_method'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_public_instance_method_spec.rb b/spec/mspec/spec/matchers/have_public_instance_method_spec.rb
new file mode 100644
index 0000000000..bff1046f04
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_public_instance_method_spec.rb
@@ -0,0 +1,53 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class HPIMMSpecs
+ def public_method
+ end
+
+ class Subclass < HPIMMSpecs
+ def public_sub_method
+ end
+ end
+end
+
+describe HavePublicInstanceMethodMatcher do
+ it "inherits from MethodMatcher" do
+ HavePublicInstanceMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ end
+
+ it "matches when mod has the public instance method" do
+ matcher = HavePublicInstanceMethodMatcher.new :public_method
+ matcher.matches?(HPIMMSpecs).should be_true
+ matcher.matches?(HPIMMSpecs::Subclass).should be_true
+ end
+
+ it "does not match when mod does not have the public instance method" do
+ matcher = HavePublicInstanceMethodMatcher.new :another_method
+ matcher.matches?(HPIMMSpecs).should be_false
+ end
+
+ it "does not match if the method is in a superclass and include_super is false" do
+ matcher = HavePublicInstanceMethodMatcher.new :public_method, false
+ matcher.matches?(HPIMMSpecs::Subclass).should be_false
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HavePublicInstanceMethodMatcher.new :some_method
+ matcher.matches?(HPIMMSpecs)
+ matcher.failure_message.should == [
+ "Expected HPIMMSpecs to have public instance method 'some_method'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure messoge for #should_not" do
+ matcher = HavePublicInstanceMethodMatcher.new :some_method
+ matcher.matches?(HPIMMSpecs)
+ matcher.negative_failure_message.should == [
+ "Expected HPIMMSpecs NOT to have public instance method 'some_method'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/have_singleton_method_spec.rb b/spec/mspec/spec/matchers/have_singleton_method_spec.rb
new file mode 100644
index 0000000000..57c37e01d9
--- /dev/null
+++ b/spec/mspec/spec/matchers/have_singleton_method_spec.rb
@@ -0,0 +1,45 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class HSMMSpecs
+ def self.singleton_method
+ end
+end
+
+describe HaveSingletonMethodMatcher do
+ it "inherits from MethodMatcher" do
+ HaveSingletonMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ end
+
+ it "matches when the class has a singleton method" do
+ matcher = HaveSingletonMethodMatcher.new :singleton_method
+ matcher.matches?(HSMMSpecs).should be_true
+ end
+
+ it "matches when the object has a singleton method" do
+ obj = double("HSMMSpecs")
+ def obj.singleton_method; end
+
+ matcher = HaveSingletonMethodMatcher.new :singleton_method
+ matcher.matches?(obj).should be_true
+ end
+
+ it "provides a failure message for #should" do
+ matcher = HaveSingletonMethodMatcher.new :some_method
+ matcher.matches?(HSMMSpecs)
+ matcher.failure_message.should == [
+ "Expected HSMMSpecs to have singleton method 'some_method'",
+ "but it does not"
+ ]
+ end
+
+ it "provides a failure message for #should_not" do
+ matcher = HaveSingletonMethodMatcher.new :singleton_method
+ matcher.matches?(HSMMSpecs)
+ matcher.negative_failure_message.should == [
+ "Expected HSMMSpecs NOT to have singleton method 'singleton_method'",
+ "but it does"
+ ]
+ end
+end
diff --git a/spec/mspec/spec/matchers/include_spec.rb b/spec/mspec/spec/matchers/include_spec.rb
new file mode 100644
index 0000000000..f045c5e0cb
--- /dev/null
+++ b/spec/mspec/spec/matchers/include_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe IncludeMatcher do
+ it "matches when actual includes expected" do
+ IncludeMatcher.new(2).matches?([1,2,3]).should == true
+ IncludeMatcher.new("b").matches?("abc").should == true
+ end
+
+ it "does not match when actual does not include expected" do
+ IncludeMatcher.new(4).matches?([1,2,3]).should == false
+ IncludeMatcher.new("d").matches?("abc").should == false
+ end
+
+ it "matches when actual includes all expected" do
+ IncludeMatcher.new(3, 2, 1).matches?([1,2,3]).should == true
+ IncludeMatcher.new("a", "b", "c").matches?("abc").should == true
+ end
+
+ it "does not match when actual does not include all expected" do
+ IncludeMatcher.new(3, 2, 4).matches?([1,2,3]).should == false
+ IncludeMatcher.new("a", "b", "c", "d").matches?("abc").should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = IncludeMatcher.new(5, 2)
+ matcher.matches?([1,2,3])
+ matcher.failure_message.should == ["Expected [1, 2, 3]", "to include 5"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = IncludeMatcher.new(1, 2, 3)
+ matcher.matches?([1,2,3])
+ matcher.negative_failure_message.should == ["Expected [1, 2, 3]", "not to include 3"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/infinity_spec.rb b/spec/mspec/spec/matchers/infinity_spec.rb
new file mode 100644
index 0000000000..6eb8ac2940
--- /dev/null
+++ b/spec/mspec/spec/matchers/infinity_spec.rb
@@ -0,0 +1,34 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/guards'
+require 'mspec/helpers'
+require 'mspec/matchers'
+
+describe InfinityMatcher do
+ it "matches when actual is infinite and has the correct sign" do
+ InfinityMatcher.new(1).matches?(infinity_value).should == true
+ InfinityMatcher.new(-1).matches?(-infinity_value).should == true
+ end
+
+ it "does not match when actual is not infinite" do
+ InfinityMatcher.new(1).matches?(1.0).should == false
+ InfinityMatcher.new(-1).matches?(-1.0).should == false
+ end
+
+ it "does not match when actual is infinite but has the incorrect sign" do
+ InfinityMatcher.new(1).matches?(-infinity_value).should == false
+ InfinityMatcher.new(-1).matches?(infinity_value).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = InfinityMatcher.new(-1)
+ matcher.matches?(0)
+ matcher.failure_message.should == ["Expected 0", "to be -Infinity"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = InfinityMatcher.new(1)
+ matcher.matches?(infinity_value)
+ matcher.negative_failure_message.should == ["Expected Infinity", "not to be Infinity"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/match_yaml_spec.rb b/spec/mspec/spec/matchers/match_yaml_spec.rb
new file mode 100644
index 0000000000..4f16aee0ec
--- /dev/null
+++ b/spec/mspec/spec/matchers/match_yaml_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe MatchYAMLMatcher do
+ before :each do
+ @matcher = MatchYAMLMatcher.new("--- \nfoo: bar\n")
+ end
+
+ it "compares YAML documents and matches if they're equivalent" do
+ @matcher.matches?("--- \nfoo: bar\n").should == true
+ end
+
+ it "compares YAML documents and does not match if they're not equivalent" do
+ @matcher.matches?("--- \nbar: foo\n").should == false
+ @matcher.matches?("--- \nfoo: \nbar\n").should == false
+ end
+
+ it "also receives objects that respond_to to_yaml" do
+ matcher = MatchYAMLMatcher.new("some string")
+ matcher.matches?("some string").should == true
+
+ matcher = MatchYAMLMatcher.new(['a', 'b'])
+ matcher.matches?("--- \n- a\n- b\n").should == true
+
+ matcher = MatchYAMLMatcher.new("foo" => "bar")
+ matcher.matches?("--- \nfoo: bar\n").should == true
+ end
+
+ it "matches documents with trailing whitespace" do
+ @matcher.matches?("--- \nfoo: bar \n").should == true
+ @matcher.matches?("--- \nfoo: bar \n").should == true
+ end
+
+ it "fails with a descriptive error message" do
+ @matcher.matches?("foo").should == false
+ @matcher.failure_message.should == ["Expected \"foo\"", " to match \"--- \\nfoo: bar\\n\""]
+ end
+end
diff --git a/spec/mspec/spec/matchers/output_spec.rb b/spec/mspec/spec/matchers/output_spec.rb
new file mode 100644
index 0000000000..264da3b569
--- /dev/null
+++ b/spec/mspec/spec/matchers/output_spec.rb
@@ -0,0 +1,74 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe OutputMatcher do
+ it "matches when executing the proc results in the expected output to $stdout" do
+ proc = Proc.new { puts "bang!" }
+ OutputMatcher.new("bang!\n", nil).matches?(proc).should == true
+ OutputMatcher.new("pop", nil).matches?(proc).should == false
+ OutputMatcher.new(/bang/, nil).matches?(proc).should == true
+ OutputMatcher.new(/po/, nil).matches?(proc).should == false
+ end
+
+ it "matches when executing the proc results in the expected output to $stderr" do
+ proc = Proc.new { $stderr.write "boom!" }
+ OutputMatcher.new(nil, "boom!").matches?(proc).should == true
+ OutputMatcher.new(nil, "fizzle").matches?(proc).should == false
+ OutputMatcher.new(nil, /boom/).matches?(proc).should == true
+ OutputMatcher.new(nil, /fizzl/).matches?(proc).should == false
+ end
+
+ it "provides a useful failure message" do
+ proc = Proc.new { print "unexpected"; $stderr.print "unerror" }
+ matcher = OutputMatcher.new("expected", "error")
+ matcher.matches?(proc)
+ matcher.failure_message.should ==
+ ["Expected:\n $stdout: \"expected\"\n $stderr: \"error\"\n",
+ " got:\n $stdout: \"unexpected\"\n $stderr: \"unerror\"\n"]
+ matcher = OutputMatcher.new("expected", nil)
+ matcher.matches?(proc)
+ matcher.failure_message.should ==
+ ["Expected:\n $stdout: \"expected\"\n",
+ " got:\n $stdout: \"unexpected\"\n"]
+ matcher = OutputMatcher.new(nil, "error")
+ matcher.matches?(proc)
+ matcher.failure_message.should ==
+ ["Expected:\n $stderr: \"error\"\n",
+ " got:\n $stderr: \"unerror\"\n"]
+ matcher = OutputMatcher.new(/base/, nil)
+ matcher.matches?(proc)
+ matcher.failure_message.should ==
+ ["Expected:\n $stdout: /base/\n",
+ " got:\n $stdout: \"unexpected\"\n"]
+ matcher = OutputMatcher.new(nil, /octave/)
+ matcher.matches?(proc)
+ matcher.failure_message.should ==
+ ["Expected:\n $stderr: /octave/\n",
+ " got:\n $stderr: \"unerror\"\n"]
+ end
+
+ it "provides a useful negative failure message" do
+ proc = Proc.new { puts "expected"; $stderr.puts "error" }
+ matcher = OutputMatcher.new("expected", "error")
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected output not to be:\n", " $stdout: \"expected\"\n $stderr: \"error\"\n"]
+ matcher = OutputMatcher.new("expected", nil)
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected output not to be:\n", " $stdout: \"expected\"\n"]
+ matcher = OutputMatcher.new(nil, "error")
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected output not to be:\n", " $stderr: \"error\"\n"]
+ matcher = OutputMatcher.new(/expect/, nil)
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected output not to be:\n", " $stdout: \"expected\"\n"]
+ matcher = OutputMatcher.new(nil, /err/)
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected output not to be:\n", " $stderr: \"error\"\n"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/output_to_fd_spec.rb b/spec/mspec/spec/matchers/output_to_fd_spec.rb
new file mode 100644
index 0000000000..d35da58829
--- /dev/null
+++ b/spec/mspec/spec/matchers/output_to_fd_spec.rb
@@ -0,0 +1,42 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe OutputToFDMatcher do
+ # Figure out how in the hell to achieve this
+ it "matches when running the block produces the expected output to the given FD" do
+ output_to_fd("Hi\n", STDERR).matches?(lambda { $stderr.print "Hi\n" }).should == true
+ end
+
+ it "does not match if running the block does not produce the expected output to the FD" do
+ output_to_fd("Hi\n", STDERR).matches?(lambda { $stderr.puts("Hello\n") }).should == false
+ end
+
+ it "propagate the exception if one is thrown while matching" do
+ exc = RuntimeError.new("propagates")
+ lambda {
+ output_to_fd("Hi\n", STDERR).matches?(lambda {
+ raise exc
+ }).should == false
+ }.should raise_error(exc)
+ end
+
+ it "defaults to matching against STDOUT" do
+ output_to_fd("Hi\n").matches?(lambda { $stdout.print "Hi\n" }).should == true
+ end
+
+ it "accepts any IO instance" do
+ io = IO.new STDOUT.fileno
+ output_to_fd("Hi\n", io).matches?(lambda { io.print "Hi\n" }).should == true
+ end
+
+ it "allows matching with a Regexp" do
+ s = "Hi there\n"
+ output_to_fd(/Hi/, STDERR).matches?(lambda { $stderr.print s }).should == true
+ output_to_fd(/Hi?/, STDERR).matches?(lambda { $stderr.print s }).should == true
+ output_to_fd(/[hH]i?/, STDERR).matches?(lambda { $stderr.print s }).should == true
+ output_to_fd(/.*/, STDERR).matches?(lambda { $stderr.print s }).should == true
+ output_to_fd(/H.*?here/, STDERR).matches?(lambda { $stderr.print s }).should == true
+ output_to_fd(/Ahoy/, STDERR).matches?(lambda { $stderr.print s }).should == false
+ end
+end
diff --git a/spec/mspec/spec/matchers/raise_error_spec.rb b/spec/mspec/spec/matchers/raise_error_spec.rb
new file mode 100644
index 0000000000..88aab34d53
--- /dev/null
+++ b/spec/mspec/spec/matchers/raise_error_spec.rb
@@ -0,0 +1,108 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+class ExpectedException < Exception; end
+class UnexpectedException < Exception; end
+
+describe RaiseErrorMatcher do
+ it "matches when the proc raises the expected exception" do
+ proc = Proc.new { raise ExpectedException }
+ matcher = RaiseErrorMatcher.new(ExpectedException, nil)
+ matcher.matches?(proc).should == true
+ end
+
+ it "executes it's optional block if matched" do
+ run = false
+ proc = Proc.new { raise ExpectedException }
+ matcher = RaiseErrorMatcher.new(ExpectedException, nil) { |error|
+ run = true
+ error.class.should == ExpectedException
+ }
+
+ matcher.matches?(proc).should == true
+ run.should == true
+ end
+
+ it "matches when the proc raises the expected exception with the expected message" do
+ proc = Proc.new { raise ExpectedException, "message" }
+ matcher = RaiseErrorMatcher.new(ExpectedException, "message")
+ matcher.matches?(proc).should == true
+ end
+
+ it "matches when the proc raises the expected exception with a matching message" do
+ proc = Proc.new { raise ExpectedException, "some message" }
+ matcher = RaiseErrorMatcher.new(ExpectedException, /some/)
+ matcher.matches?(proc).should == true
+ end
+
+ it "does not match when the proc does not raise the expected exception" do
+ exc = UnexpectedException.new
+ matcher = RaiseErrorMatcher.new(ExpectedException, nil)
+
+ matcher.matching_exception?(exc).should == false
+ lambda {
+ matcher.matches?(Proc.new { raise exc })
+ }.should raise_error(UnexpectedException)
+ end
+
+ it "does not match when the proc raises the expected exception with an unexpected message" do
+ exc = ExpectedException.new("unexpected")
+ matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
+
+ matcher.matching_exception?(exc).should == false
+ lambda {
+ matcher.matches?(Proc.new { raise exc })
+ }.should raise_error(ExpectedException)
+ end
+
+ it "does not match when the proc does not raise an exception" do
+ proc = Proc.new {}
+ matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
+ matcher.matches?(proc).should == false
+ end
+
+ it "provides a useful failure message" do
+ exc = UnexpectedException.new("unexpected")
+ matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
+
+ matcher.matching_exception?(exc).should == false
+ lambda {
+ matcher.matches?(Proc.new { raise exc })
+ }.should raise_error(UnexpectedException)
+ matcher.failure_message.should ==
+ ["Expected ExpectedException (expected)", "but got UnexpectedException (unexpected)"]
+ end
+
+ it "provides a useful failure message when no exception is raised" do
+ proc = Proc.new { 120 }
+ matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
+ matcher.matches?(proc)
+ matcher.failure_message.should ==
+ ["Expected ExpectedException (expected)", "but no exception was raised (120 was returned)"]
+ end
+
+ it "provides a useful failure message when no exception is raised and nil is returned" do
+ proc = Proc.new { nil }
+ matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
+ matcher.matches?(proc)
+ matcher.failure_message.should ==
+ ["Expected ExpectedException (expected)", "but no exception was raised (nil was returned)"]
+ end
+
+ it "provides a useful negative failure message" do
+ proc = Proc.new { raise ExpectedException, "expected" }
+ matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected to not get ExpectedException (expected)", ""]
+ end
+
+ it "provides a useful negative failure message for strict subclasses of the matched exception class" do
+ proc = Proc.new { raise UnexpectedException, "unexpected" }
+ matcher = RaiseErrorMatcher.new(Exception, nil)
+ matcher.matches?(proc)
+ matcher.negative_failure_message.should ==
+ ["Expected to not get Exception", "but got UnexpectedException (unexpected)"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/respond_to_spec.rb b/spec/mspec/spec/matchers/respond_to_spec.rb
new file mode 100644
index 0000000000..988caf4dff
--- /dev/null
+++ b/spec/mspec/spec/matchers/respond_to_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe RespondToMatcher do
+ it "matches when actual does respond_to? expected" do
+ RespondToMatcher.new(:to_s).matches?(Object.new).should == true
+ RespondToMatcher.new(:inject).matches?([]).should == true
+ RespondToMatcher.new(:[]).matches?(1).should == true
+ RespondToMatcher.new(:[]=).matches?("string").should == true
+ end
+
+ it "does not match when actual does not respond_to? expected" do
+ RespondToMatcher.new(:to_i).matches?(Object.new).should == false
+ RespondToMatcher.new(:inject).matches?(1).should == false
+ RespondToMatcher.new(:non_existent_method).matches?([]).should == false
+ RespondToMatcher.new(:[]=).matches?(1).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = RespondToMatcher.new(:non_existent_method)
+ matcher.matches?('string')
+ matcher.failure_message.should == [
+ "Expected \"string\" (String)", "to respond to non_existent_method"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = RespondToMatcher.new(:to_i)
+ matcher.matches?(4.0)
+ matcher.negative_failure_message.should == [
+ "Expected 4.0 (Float)", "not to respond to to_i"]
+ end
+end
diff --git a/spec/mspec/spec/matchers/signed_zero_spec.rb b/spec/mspec/spec/matchers/signed_zero_spec.rb
new file mode 100644
index 0000000000..9c5c50c602
--- /dev/null
+++ b/spec/mspec/spec/matchers/signed_zero_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+require 'mspec/expectations/expectations'
+require 'mspec/matchers'
+
+describe SignedZeroMatcher do
+ it "matches when actual is zero and has the correct sign" do
+ SignedZeroMatcher.new(1).matches?(0.0).should == true
+ SignedZeroMatcher.new(-1).matches?(-0.0).should == true
+ end
+
+ it "does not match when actual is non-zero" do
+ SignedZeroMatcher.new(1).matches?(1.0).should == false
+ SignedZeroMatcher.new(-1).matches?(-1.0).should == false
+ end
+
+ it "does not match when actual is zero but has the incorrect sign" do
+ SignedZeroMatcher.new(1).matches?(-0.0).should == false
+ SignedZeroMatcher.new(-1).matches?(0.0).should == false
+ end
+
+ it "provides a useful failure message" do
+ matcher = SignedZeroMatcher.new(-1)
+ matcher.matches?(0.0)
+ matcher.failure_message.should == ["Expected 0.0", "to be -0.0"]
+ end
+
+ it "provides a useful negative failure message" do
+ matcher = SignedZeroMatcher.new(-1)
+ matcher.matches?(-0.0)
+ matcher.negative_failure_message.should == ["Expected -0.0", "not to be -0.0"]
+ end
+end
diff --git a/spec/mspec/spec/mocks/mock_spec.rb b/spec/mspec/spec/mocks/mock_spec.rb
new file mode 100644
index 0000000000..1a7fb2d567
--- /dev/null
+++ b/spec/mspec/spec/mocks/mock_spec.rb
@@ -0,0 +1,469 @@
+# This is a bit awkward. Currently the way to verify that the
+# opposites are true (for example a failure when the specified
+# arguments are NOT provided) is to simply alter the particular
+# spec to a failure condition.
+require 'spec_helper'
+require 'mspec/runner/mspec'
+require 'mspec/mocks/mock'
+require 'mspec/mocks/proxy'
+
+describe Mock, ".mocks" do
+ it "returns a Hash" do
+ Mock.mocks.should be_kind_of(Hash)
+ end
+end
+
+describe Mock, ".stubs" do
+ it "returns a Hash" do
+ Mock.stubs.should be_kind_of(Hash)
+ end
+end
+
+describe Mock, ".replaced_name" do
+ it "returns the name for a method that is being replaced by a mock method" do
+ m = double('a fake id')
+ m.stub(:__mspec_object_id__).and_return(42)
+ Mock.replaced_name(m, :method_call).should == :__mspec_42_method_call__
+ end
+end
+
+describe Mock, ".replaced_key" do
+ it "returns a key used internally by Mock" do
+ m = double('a fake id')
+ m.stub(:__mspec_object_id__).and_return(42)
+ Mock.replaced_key(m, :method_call).should == [:__mspec_42_method_call__, :method_call]
+ end
+end
+
+describe Mock, ".replaced?" do
+ before :each do
+ @mock = double('install_method')
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ end
+
+ it "returns true if a method has been stubbed on an object" do
+ Mock.install_method @mock, :method_call
+ Mock.replaced?(Mock.replaced_name(@mock, :method_call)).should be_true
+ end
+
+ it "returns true if a method has been mocked on an object" do
+ Mock.install_method @mock, :method_call, :stub
+ Mock.replaced?(Mock.replaced_name(@mock, :method_call)).should be_true
+ end
+
+ it "returns false if a method has not been stubbed or mocked" do
+ Mock.replaced?(Mock.replaced_name(@mock, :method_call)).should be_false
+ end
+end
+
+describe Mock, ".name_or_inspect" do
+ before :each do
+ @mock = double("I have a #name")
+ end
+
+ it "returns the value of @name if set" do
+ @mock.instance_variable_set(:@name, "Myself")
+ Mock.name_or_inspect(@mock).should == "Myself"
+ end
+end
+
+describe Mock, ".install_method for mocks" do
+ before :each do
+ @mock = double('install_method')
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ end
+
+ after :each do
+ Mock.cleanup
+ end
+
+ it "returns a MockProxy instance" do
+ Mock.install_method(@mock, :method_call).should be_an_instance_of(MockProxy)
+ end
+
+ it "does not override a previously mocked method with the same name" do
+ Mock.install_method(@mock, :method_call).with(:a, :b).and_return(1)
+ Mock.install_method(@mock, :method_call).with(:c).and_return(2)
+ @mock.method_call(:a, :b)
+ @mock.method_call(:c)
+ lambda { @mock.method_call(:d) }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ # This illustrates RSpec's behavior. This spec fails in mock call count verification
+ # on RSpec (i.e. Mock 'foo' expected :foo with (any args) once, but received it 0 times)
+ # and we mimic the behavior of RSpec.
+ #
+ # describe "A mock receiving multiple calls to #should_receive" do
+ # it "returns the first value mocked" do
+ # m = mock 'multiple #should_receive'
+ # m.should_receive(:foo).and_return(true)
+ # m.foo.should == true
+ # m.should_receive(:foo).and_return(false)
+ # m.foo.should == true
+ # end
+ # end
+ #
+ it "does not override a previously mocked method having the same arguments" do
+ Mock.install_method(@mock, :method_call).with(:a).and_return(true)
+ @mock.method_call(:a).should == true
+ Mock.install_method(@mock, :method_call).with(:a).and_return(false)
+ @mock.method_call(:a).should == true
+ lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "properly sends #respond_to? calls to the aliased respond_to? method when not matching mock expectations" do
+ Mock.install_method(@mock, :respond_to?).with(:to_str).and_return('mock to_str')
+ Mock.install_method(@mock, :respond_to?).with(:to_int).and_return('mock to_int')
+ @mock.respond_to?(:to_str).should == 'mock to_str'
+ @mock.respond_to?(:to_int).should == 'mock to_int'
+ @mock.respond_to?(:to_s).should == true
+ @mock.respond_to?(:not_really_a_real_method_seriously).should == false
+ end
+
+ it "adds to the expectation tally" do
+ state = double("run state").as_null_object
+ state.stub(:state).and_return(double("spec state"))
+ MSpec.should_receive(:current).and_return(state)
+ MSpec.should_receive(:actions).with(:expectation, state.state)
+ Mock.install_method(@mock, :method_call).and_return(1)
+ @mock.method_call.should == 1
+ end
+
+ it "registers that an expectation has been encountered" do
+ state = double("run state").as_null_object
+ state.stub(:state).and_return(double("spec state"))
+ MSpec.should_receive(:expectation)
+ Mock.install_method(@mock, :method_call).and_return(1)
+ @mock.method_call.should == 1
+ end
+end
+
+describe Mock, ".install_method for stubs" do
+ before :each do
+ @mock = double('install_method')
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ end
+
+ after :each do
+ Mock.cleanup
+ end
+
+ it "returns a MockProxy instance" do
+ Mock.install_method(@mock, :method_call, :stub).should be_an_instance_of(MockProxy)
+ end
+
+ # This illustrates RSpec's behavior. This spec passes on RSpec and we mimic it
+ #
+ # describe "A mock receiving multiple calls to #stub" do
+ # it "returns the last value stubbed" do
+ # m = mock 'multiple #stub'
+ # m.stub(:foo).and_return(true)
+ # m.foo.should == true
+ # m.stub(:foo).and_return(false)
+ # m.foo.should == false
+ # end
+ # end
+ it "inserts new stubs before old stubs" do
+ Mock.install_method(@mock, :method_call, :stub).with(:a).and_return(true)
+ @mock.method_call(:a).should == true
+ Mock.install_method(@mock, :method_call, :stub).with(:a).and_return(false)
+ @mock.method_call(:a).should == false
+ Mock.verify_count
+ end
+
+ it "does not add to the expectation tally" do
+ state = double("run state").as_null_object
+ state.stub(:state).and_return(double("spec state"))
+ MSpec.should_not_receive(:actions)
+ Mock.install_method(@mock, :method_call, :stub).and_return(1)
+ @mock.method_call.should == 1
+ end
+end
+
+describe Mock, ".install_method" do
+ before :each do
+ @mock = double('install_method')
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ end
+
+ after :each do
+ Mock.cleanup
+ end
+
+ it "does not alias a mocked or stubbed method when installing a new mock or stub" do
+ @mock.should_not respond_to(:method_call)
+
+ Mock.install_method @mock, :method_call
+ @mock.should respond_to(:method_call)
+ @mock.should_not respond_to(Mock.replaced_name(@mock, :method_call))
+
+ Mock.install_method @mock, :method_call, :stub
+ @mock.should respond_to(:method_call)
+ @mock.should_not respond_to(Mock.replaced_name(@mock, :method_call))
+ end
+end
+
+class MockAndRaiseError < Exception; end
+
+describe Mock, ".verify_call" do
+ before :each do
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+
+ @mock = double('verify_call')
+ @proxy = Mock.install_method @mock, :method_call
+ end
+
+ after :each do
+ ScratchPad.clear
+ Mock.cleanup
+ end
+
+ it "does not raise an exception when the mock method receives the expected arguments" do
+ @proxy.with(1, 'two', :three)
+ Mock.verify_call @mock, :method_call, 1, 'two', :three
+ end
+
+ it "raises an SpecExpectationNotMetError when the mock method does not receive the expected arguments" do
+ @proxy.with(4, 2)
+ lambda {
+ Mock.verify_call @mock, :method_call, 42
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "raises an SpecExpectationNotMetError when the mock method is called with arguments but expects none" do
+ lambda {
+ @proxy.with(:no_args)
+ Mock.verify_call @mock, :method_call, "hello"
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "raises an SpecExpectationNotMetError when the mock method is called with no arguments but expects some" do
+ @proxy.with("hello", "beautiful", "world")
+ lambda {
+ Mock.verify_call @mock, :method_call
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "does not raise an exception when the mock method is called with arguments and is expecting :any_args" do
+ @proxy.with(:any_args)
+ Mock.verify_call @mock, :method_call, 1, 2, 3
+ end
+
+ it "yields a passed block when it is expected to" do
+ @proxy.and_yield()
+ Mock.verify_call @mock, :method_call do
+ ScratchPad.record true
+ end
+ ScratchPad.recorded.should == true
+ end
+
+ it "does not yield a passed block when it is not expected to" do
+ Mock.verify_call @mock, :method_call do
+ ScratchPad.record true
+ end
+ ScratchPad.recorded.should == nil
+ end
+
+ it "can yield subsequently" do
+ @proxy.and_yield(1).and_yield(2).and_yield(3)
+
+ ScratchPad.record []
+ Mock.verify_call @mock, :method_call do |arg|
+ ScratchPad << arg
+ end
+ ScratchPad.recorded.should == [1, 2, 3]
+ end
+
+ it "can yield and return an expected value" do
+ @proxy.and_yield(1).and_return(3)
+
+ Mock.verify_call(@mock, :method_call) { |arg| ScratchPad.record arg }.should == 3
+ ScratchPad.recorded.should == 1
+ end
+
+ it "raises an expection when it is expected to yield but no block is given" do
+ @proxy.and_yield(1, 2, 3)
+ lambda {
+ Mock.verify_call(@mock, :method_call)
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "raises an expection when it is expected to yield more arguments than the block can take" do
+ @proxy.and_yield(1, 2, 3)
+ lambda {
+ Mock.verify_call(@mock, :method_call) {|a, b|}
+ }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "does not raise an expection when it is expected to yield to a block that can take any number of arguments" do
+ @proxy.and_yield(1, 2, 3)
+ expect {
+ Mock.verify_call(@mock, :method_call) {|*a|}
+ }.not_to raise_error
+ end
+
+ it "raises an exception when expected to" do
+ @proxy.and_raise(MockAndRaiseError)
+ lambda {
+ Mock.verify_call @mock, :method_call
+ }.should raise_error(MockAndRaiseError)
+ end
+end
+
+describe Mock, ".verify_count" do
+ before :each do
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+
+ @mock = double('verify_count')
+ @proxy = Mock.install_method @mock, :method_call
+ end
+
+ after :each do
+ Mock.cleanup
+ end
+
+ it "does not raise an exception when the mock receives at least the expected number of calls" do
+ @proxy.at_least(2)
+ @mock.method_call
+ @mock.method_call
+ Mock.verify_count
+ end
+
+ it "raises an SpecExpectationNotMetError when the mock receives less than at least the expected number of calls" do
+ @proxy.at_least(2)
+ @mock.method_call
+ lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "does not raise an exception when the mock receives at most the expected number of calls" do
+ @proxy.at_most(2)
+ @mock.method_call
+ @mock.method_call
+ Mock.verify_count
+ end
+
+ it "raises an SpecExpectationNotMetError when the mock receives more than at most the expected number of calls" do
+ @proxy.at_most(2)
+ @mock.method_call
+ @mock.method_call
+ @mock.method_call
+ lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "does not raise an exception when the mock receives exactly the expected number of calls" do
+ @proxy.exactly(2)
+ @mock.method_call
+ @mock.method_call
+ Mock.verify_count
+ end
+
+ it "raises an SpecExpectationNotMetError when the mock receives less than exactly the expected number of calls" do
+ @proxy.exactly(2)
+ @mock.method_call
+ lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError)
+ end
+
+ it "raises an SpecExpectationNotMetError when the mock receives more than exactly the expected number of calls" do
+ @proxy.exactly(2)
+ @mock.method_call
+ @mock.method_call
+ @mock.method_call
+ lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError)
+ end
+end
+
+describe Mock, ".verify_count mixing mocks and stubs" do
+ before :each do
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+
+ @mock = double('verify_count')
+ end
+
+ after :each do
+ Mock.cleanup
+ end
+
+ it "does not raise an exception for a stubbed method that is never called" do
+ Mock.install_method @mock, :method_call, :stub
+ Mock.verify_count
+ end
+
+ it "verifies the calls to the mocked method when a mock is defined after a stub" do
+ Mock.install_method @mock, :method_call, :stub
+ Mock.install_method @mock, :method_call, :mock
+ @mock.method_call
+ Mock.verify_count
+ end
+
+ it "verifies the calls to the mocked method when a mock is defined before a stub" do
+ Mock.install_method @mock, :method_call, :mock
+ Mock.install_method @mock, :method_call, :stub
+ @mock.method_call
+ Mock.verify_count
+ end
+end
+
+describe Mock, ".cleanup" do
+ before :each do
+ MSpec.stub(:actions)
+ MSpec.stub(:current).and_return(double("spec state").as_null_object)
+
+ @mock = double('cleanup')
+ @proxy = Mock.install_method @mock, :method_call
+ @stub = Mock.install_method @mock, :method_call, :stub
+ end
+
+ after :each do
+ Mock.cleanup
+ end
+
+ it "removes the mock method call if it did not override an existing method" do
+ @mock.should respond_to(:method_call)
+
+ Mock.cleanup
+ @mock.should_not respond_to(:method_call)
+ end
+
+ it "removes the replaced method if the mock method overrides an existing method" do
+ def @mock.already_here() :hey end
+ @mock.should respond_to(:already_here)
+ replaced_name = Mock.replaced_name(@mock, :already_here)
+ Mock.install_method @mock, :already_here
+ @mock.should respond_to(replaced_name)
+
+ Mock.cleanup
+ @mock.should_not respond_to(replaced_name)
+ @mock.should respond_to(:already_here)
+ @mock.already_here.should == :hey
+ end
+
+ it "removes all mock expectations" do
+ Mock.mocks.should == { Mock.replaced_key(@mock, :method_call) => [@proxy] }
+ Mock.cleanup
+ Mock.mocks.should == {}
+ end
+
+ it "removes all stubs" do
+ Mock.stubs.should == { Mock.replaced_key(@mock, :method_call) => [@stub] }
+ Mock.cleanup
+ Mock.stubs.should == {}
+ end
+
+ it "removes the replaced name for mocks" do
+ replaced_key = Mock.replaced_key(@mock, :method_call)
+ Mock.should_receive(:clear_replaced).with(replaced_key)
+
+ replaced_name = Mock.replaced_name(@mock, :method_call)
+ Mock.replaced?(replaced_name).should be_true
+
+ Mock.cleanup
+ Mock.replaced?(replaced_name).should be_false
+ end
+end
diff --git a/spec/mspec/spec/mocks/proxy_spec.rb b/spec/mspec/spec/mocks/proxy_spec.rb
new file mode 100644
index 0000000000..d9e754b972
--- /dev/null
+++ b/spec/mspec/spec/mocks/proxy_spec.rb
@@ -0,0 +1,405 @@
+require 'spec_helper'
+require 'mspec/mocks/proxy'
+
+describe MockObject, ".new" do
+ it "creates a new mock object" do
+ m = MockObject.new('not a null object')
+ lambda { m.not_a_method }.should raise_error(NoMethodError)
+ end
+
+ it "creates a new mock object that follows the NullObject pattern" do
+ m = MockObject.new('null object', :null_object => true)
+ m.not_really_a_method.should equal(m)
+ end
+end
+
+describe MockProxy, ".new" do
+ it "creates a mock proxy by default" do
+ MockProxy.new.mock?.should be_true
+ end
+
+ it "creates a stub proxy by request" do
+ MockProxy.new(:stub).stub?.should be_true
+ end
+
+ it "sets the call expectation to 1 call for a mock" do
+ MockProxy.new.count.should == [:exactly, 1]
+ end
+
+ it "sets the call expectation to any number of times for a stub" do
+ MockProxy.new(:stub).count.should == [:any_number_of_times, 0]
+ end
+end
+
+describe MockProxy, "#count" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns the expected number of calls the mock should receive" do
+ @proxy.count.should == [:exactly, 1]
+ @proxy.at_least(3).count.should == [:at_least, 3]
+ end
+end
+
+describe MockProxy, "#arguments" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns the expected arguments" do
+ @proxy.arguments.should == :any_args
+ end
+end
+
+describe MockProxy, "#with" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.with(:a).should be_equal(@proxy)
+ end
+
+ it "raises an ArgumentError if no arguments are given" do
+ lambda { @proxy.with }.should raise_error(ArgumentError)
+ end
+
+ it "accepts any number of arguments" do
+ @proxy.with(1, 2, 3).should be_an_instance_of(MockProxy)
+ @proxy.arguments.should == [1,2,3]
+ end
+end
+
+describe MockProxy, "#once" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.once.should be_equal(@proxy)
+ end
+
+ it "sets the expected calls to 1" do
+ @proxy.once
+ @proxy.count.should == [:exactly, 1]
+ end
+
+ it "accepts no arguments" do
+ lambda { @proxy.once(:a) }.should raise_error
+ end
+end
+
+describe MockProxy, "#twice" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.twice.should be_equal(@proxy)
+ end
+
+ it "sets the expected calls to 2" do
+ @proxy.twice
+ @proxy.count.should == [:exactly, 2]
+ end
+
+ it "accepts no arguments" do
+ lambda { @proxy.twice(:b) }.should raise_error
+ end
+end
+
+describe MockProxy, "#exactly" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.exactly(2).should be_equal(@proxy)
+ end
+
+ it "sets the expected calls to exactly n" do
+ @proxy.exactly(5)
+ @proxy.count.should == [:exactly, 5]
+ end
+
+ it "does not accept an argument that Integer() cannot convert" do
+ lambda { @proxy.exactly('x') }.should raise_error
+ end
+end
+
+describe MockProxy, "#at_least" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.at_least(3).should be_equal(@proxy)
+ end
+
+ it "sets the expected calls to at least n" do
+ @proxy.at_least(3)
+ @proxy.count.should == [:at_least, 3]
+ end
+
+ it "accepts :once :twice" do
+ @proxy.at_least(:once)
+ @proxy.count.should == [:at_least, 1]
+ @proxy.at_least(:twice)
+ @proxy.count.should == [:at_least, 2]
+ end
+
+ it "does not accept an argument that Integer() cannot convert" do
+ lambda { @proxy.at_least('x') }.should raise_error
+ end
+end
+
+describe MockProxy, "#at_most" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.at_most(2).should be_equal(@proxy)
+ end
+
+ it "sets the expected calls to at most n" do
+ @proxy.at_most(2)
+ @proxy.count.should == [:at_most, 2]
+ end
+
+ it "accepts :once, :twice" do
+ @proxy.at_most(:once)
+ @proxy.count.should == [:at_most, 1]
+ @proxy.at_most(:twice)
+ @proxy.count.should == [:at_most, 2]
+ end
+
+ it "does not accept an argument that Integer() cannot convert" do
+ lambda { @proxy.at_most('x') }.should raise_error
+ end
+end
+
+describe MockProxy, "#any_number_of_times" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.any_number_of_times.should be_equal(@proxy)
+ end
+
+ it "sets the expected calls to any number of times" do
+ @proxy.any_number_of_times
+ @proxy.count.should == [:any_number_of_times, 0]
+ end
+
+ it "does not accept an argument" do
+ lambda { @proxy.any_number_of_times(2) }.should raise_error
+ end
+end
+
+describe MockProxy, "#and_return" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.and_return(false).should equal(@proxy)
+ end
+
+ it "sets the expected return value" do
+ @proxy.and_return(false)
+ @proxy.returning.should == false
+ end
+
+ it "accepts any number of return values" do
+ @proxy.and_return(1, 2, 3)
+ @proxy.returning.should == 1
+ @proxy.returning.should == 2
+ @proxy.returning.should == 3
+ end
+
+ it "implicitly sets the expected number of calls" do
+ @proxy.and_return(1, 2, 3)
+ @proxy.count.should == [:exactly, 3]
+ end
+
+ it "only sets the expected number of calls if it is higher than what is already set" do
+ @proxy.at_least(5).times.and_return(1, 2, 3)
+ @proxy.count.should == [:at_least, 5]
+
+ @proxy.at_least(2).times.and_return(1, 2, 3)
+ @proxy.count.should == [:at_least, 3]
+ end
+end
+
+describe MockProxy, "#returning" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns nil by default" do
+ @proxy.returning.should be_nil
+ end
+
+ it "returns the value set by #and_return" do
+ @proxy.and_return(2)
+ @proxy.returning.should == 2
+ @proxy.returning.should == 2
+ end
+
+ it "returns a sequence of values set by #and_return" do
+ @proxy.and_return(1,2,3,4)
+ @proxy.returning.should == 1
+ @proxy.returning.should == 2
+ @proxy.returning.should == 3
+ @proxy.returning.should == 4
+ @proxy.returning.should == 4
+ @proxy.returning.should == 4
+ end
+end
+
+describe MockProxy, "#calls" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns the number of times the proxy is called" do
+ @proxy.calls.should == 0
+ end
+end
+
+describe MockProxy, "#called" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "increments the number of times the proxy is called" do
+ @proxy.called
+ @proxy.called
+ @proxy.calls.should == 2
+ end
+end
+
+describe MockProxy, "#times" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "is a no-op" do
+ @proxy.times.should == @proxy
+ end
+end
+
+describe MockProxy, "#stub?" do
+ it "returns true if the proxy is created as a stub" do
+ MockProxy.new(:stub).stub?.should be_true
+ end
+
+ it "returns false if the proxy is created as a mock" do
+ MockProxy.new(:mock).stub?.should be_false
+ end
+end
+
+describe MockProxy, "#mock?" do
+ it "returns true if the proxy is created as a mock" do
+ MockProxy.new(:mock).mock?.should be_true
+ end
+
+ it "returns false if the proxy is created as a stub" do
+ MockProxy.new(:stub).mock?.should be_false
+ end
+end
+
+describe MockProxy, "#and_yield" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns self" do
+ @proxy.and_yield(false).should equal(@proxy)
+ end
+
+ it "sets the expected values to yield" do
+ @proxy.and_yield(1).yielding.should == [[1]]
+ end
+
+ it "accepts multiple values to yield" do
+ @proxy.and_yield(1, 2, 3).yielding.should == [[1, 2, 3]]
+ end
+end
+
+describe MockProxy, "#raising" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns nil by default" do
+ @proxy.raising.should be_nil
+ end
+
+ it "returns the exception object passed to #and_raise" do
+ exc = double("exception")
+ @proxy.and_raise(exc)
+ @proxy.raising.should equal(exc)
+ end
+
+ it "returns an instance of RuntimeError when a String is passed to #and_raise" do
+ @proxy.and_raise("an error")
+ exc = @proxy.raising
+ exc.should be_an_instance_of(RuntimeError)
+ exc.message.should == "an error"
+ end
+end
+
+describe MockProxy, "#yielding" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns an empty array by default" do
+ @proxy.yielding.should == []
+ end
+
+ it "returns an array of arrays of values the proxy should yield" do
+ @proxy.and_yield(3)
+ @proxy.yielding.should == [[3]]
+ end
+
+ it "returns an accumulation of arrays of values the proxy should yield" do
+ @proxy.and_yield(1).and_yield(2, 3)
+ @proxy.yielding.should == [[1], [2, 3]]
+ end
+end
+
+describe MockProxy, "#yielding?" do
+ before :each do
+ @proxy = MockProxy.new
+ end
+
+ it "returns false if the proxy is not yielding" do
+ @proxy.yielding?.should be_false
+ end
+
+ it "returns true if the proxy is yielding" do
+ @proxy.and_yield(1)
+ @proxy.yielding?.should be_true
+ end
+end
+
+describe MockIntObject, "#to_int" do
+ before :each do
+ @int = MockIntObject.new(10)
+ end
+
+ it "returns the number if to_int is called" do
+ @int.to_int.should == 10
+ @int.count.should == [:at_least, 1]
+ end
+
+ it "tries to convert the target to int if to_int is called" do
+ MockIntObject.new(@int).to_int.should == 10
+ @int.count.should == [:at_least, 1]
+ end
+end
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"
diff --git a/spec/mspec/spec/spec_helper.rb b/spec/mspec/spec/spec_helper.rb
new file mode 100644
index 0000000000..4fae9bd7e2
--- /dev/null
+++ b/spec/mspec/spec/spec_helper.rb
@@ -0,0 +1,54 @@
+require 'pp'
+require 'mspec/helpers/io'
+require 'mspec/helpers/scratch'
+
+# Remove this when MRI has intelligent warnings
+$VERBOSE = nil unless $VERBOSE
+
+class MOSConfig < Hash
+ def initialize
+ self[:loadpath] = []
+ self[:requires] = []
+ self[:flags] = []
+ self[:options] = []
+ self[:includes] = []
+ self[:excludes] = []
+ self[:patterns] = []
+ self[:xpatterns] = []
+ self[:tags] = []
+ self[:xtags] = []
+ self[:atags] = []
+ self[:astrings] = []
+ self[:target] = 'ruby'
+ self[:command] = nil
+ self[:ltags] = []
+ self[:files] = []
+ self[:launch] = []
+ end
+end
+
+def new_option
+ config = MOSConfig.new
+ return MSpecOptions.new("spec", 20, config), config
+end
+
+# Just to have an exception name output not be "Exception"
+class MSpecExampleError < Exception
+end
+
+def hide_deprecation_warnings
+ MSpec.stub(:deprecate)
+end
+
+def run_mspec(command, args)
+ cwd = Dir.pwd
+ command = " #{command}" unless command.start_with?('-')
+ cmd = "#{cwd}/bin/mspec#{command} -B spec/fixtures/config.mspec #{args}"
+ out = `#{cmd} 2>&1`
+ ret = $?
+ out = out.sub(/\A\$.+\n/, '') # Remove printed command line
+ out = out.sub(RUBY_DESCRIPTION, "RUBY_DESCRIPTION")
+ out = out.gsub(/\d\.\d{6}/, "D.DDDDDD")
+ out = out.gsub(cwd, "CWD")
+ return out, ret
+end
diff --git a/spec/mspec/spec/utils/deprecate_spec.rb b/spec/mspec/spec/utils/deprecate_spec.rb
new file mode 100644
index 0000000000..14e05c6f35
--- /dev/null
+++ b/spec/mspec/spec/utils/deprecate_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+require 'mspec/utils/deprecate'
+
+describe MSpec, "#deprecate" do
+ it "warns when using a deprecated method" do
+ warning = nil
+ $stderr.stub(:puts) { |str| warning = str }
+ MSpec.deprecate(:some_method, :other_method)
+ warning.should start_with(<<-EOS.chomp)
+
+some_method is deprecated, use other_method instead.
+from
+EOS
+ warning.should include(__FILE__)
+ warning.should include('8')
+ end
+end
diff --git a/spec/mspec/spec/utils/name_map_spec.rb b/spec/mspec/spec/utils/name_map_spec.rb
new file mode 100644
index 0000000000..d38230ce06
--- /dev/null
+++ b/spec/mspec/spec/utils/name_map_spec.rb
@@ -0,0 +1,175 @@
+require 'spec_helper'
+require 'mspec/utils/name_map'
+
+module NameMapSpecs
+ class A
+ A = self
+
+ def self.a; end
+ def a; end
+ def c; end
+
+ class B
+ def b; end
+ end
+ end
+
+ class Error
+ end
+
+ class Fixnum
+ def f; end
+ end
+
+ def self.n; end
+ def n; end
+end
+
+describe NameMap, "#exception?" do
+ before :each do
+ @map = NameMap.new
+ end
+
+ it "returns true if the constant is Errno" do
+ @map.exception?("Errno").should == true
+ end
+
+ it "returns true if the constant is a kind of Exception" do
+ @map.exception?("Errno::EBADF").should == true
+ @map.exception?("LoadError").should == true
+ @map.exception?("SystemExit").should == true
+ end
+
+ it "returns false if the constant is not a kind of Exception" do
+ @map.exception?("NameMapSpecs::Error").should == false
+ @map.exception?("NameMapSpecs").should == false
+ end
+
+ it "returns false if the constant does not exist" do
+ @map.exception?("Nonexistent").should == false
+ end
+end
+
+describe NameMap, "#class_or_module" do
+ before :each do
+ @map = NameMap.new true
+ end
+
+ it "returns the constant specified by the string" do
+ @map.class_or_module("NameMapSpecs").should == NameMapSpecs
+ end
+
+ it "returns the constant specified by the 'A::B' string" do
+ @map.class_or_module("NameMapSpecs::A").should == NameMapSpecs::A
+ end
+
+ it "returns nil if the constant is not a class or module" do
+ @map.class_or_module("Float::MAX").should == nil
+ end
+
+ it "returns nil if the constant is in the set of excluded constants" do
+ excluded = %w[
+ MSpecScript
+ MkSpec
+ NameMap
+ ]
+
+ excluded.each do |const|
+ @map.class_or_module(const).should == nil
+ end
+ end
+
+ it "returns nil if the constant does not exist" do
+ @map.class_or_module("Heaven").should == nil
+ @map.class_or_module("Hell").should == nil
+ @map.class_or_module("Bush::Brain").should == nil
+ end
+end
+
+describe NameMap, "#dir_name" do
+ before :each do
+ @map = NameMap.new
+ end
+
+ it "returns a directory name from the base name and constant" do
+ @map.dir_name("NameMapSpecs", 'spec/core').should == 'spec/core/namemapspecs'
+ end
+
+ it "returns a directory name from the components in the constants name" do
+ @map.dir_name("NameMapSpecs::A", 'spec').should == 'spec/namemapspecs/a'
+ @map.dir_name("NameMapSpecs::A::B", 'spec').should == 'spec/namemapspecs/a/b'
+ end
+
+ it "returns a directory name without 'class' for constants like TrueClass" do
+ @map.dir_name("TrueClass", 'spec').should == 'spec/true'
+ @map.dir_name("FalseClass", 'spec').should == 'spec/false'
+ end
+
+ it "returns 'exception' for the directory name of any Exception subclass" do
+ @map.dir_name("SystemExit", 'spec').should == 'spec/exception'
+ @map.dir_name("Errno::EBADF", 'spec').should == 'spec/exception'
+ end
+
+ it "returns 'class' for Class" do
+ @map.dir_name("Class", 'spec').should == 'spec/class'
+ end
+end
+
+# These specs do not cover all the mappings, but only describe how the
+# name is derived when the hash item maps to a single value, a hash with
+# a specific item, or a hash with a :default item.
+describe NameMap, "#file_name" do
+ before :each do
+ @map = NameMap.new
+ end
+
+ it "returns the name of the spec file based on the constant and method" do
+ @map.file_name("[]=", "Array").should == "element_set_spec.rb"
+ end
+
+ it "returns the name of the spec file based on the special entry for the method" do
+ @map.file_name("~", "Regexp").should == "match_spec.rb"
+ @map.file_name("~", "Fixnum").should == "complement_spec.rb"
+ end
+
+ it "returns the name of the spec file based on the default entry for the method" do
+ @map.file_name("<<", "NameMapSpecs").should == "append_spec.rb"
+ end
+
+ it "uses the last component of the constant to look up the method name" do
+ @map.file_name("^", "NameMapSpecs::Fixnum").should == "bit_xor_spec.rb"
+ end
+end
+
+describe NameMap, "#namespace" do
+ before :each do
+ @map = NameMap.new
+ end
+
+ it "prepends the module to the constant name" do
+ @map.namespace("SubModule", Integer).should == "SubModule::Integer"
+ end
+
+ it "does not prepend Object, Class, or Module to the constant name" do
+ @map.namespace("Object", String).should == "String"
+ @map.namespace("Module", Integer).should == "Integer"
+ @map.namespace("Class", Float).should == "Float"
+ end
+end
+
+describe NameMap, "#map" do
+ before :each do
+ @map = NameMap.new
+ end
+
+ it "flattens an object hierarchy into a single Hash" do
+ @map.map({}, [NameMapSpecs]).should == {
+ "NameMapSpecs." => ["n"],
+ "NameMapSpecs#" => ["n"],
+ "NameMapSpecs::A." => ["a"],
+ "NameMapSpecs::A#" => ["a", "c"],
+ "NameMapSpecs::A::B#" => ["b"],
+ "NameMapSpecs::Fixnum#" => ["f"]
+ }
+ end
+end
diff --git a/spec/mspec/spec/utils/options_spec.rb b/spec/mspec/spec/utils/options_spec.rb
new file mode 100644
index 0000000000..26c52bdbd0
--- /dev/null
+++ b/spec/mspec/spec/utils/options_spec.rb
@@ -0,0 +1,1309 @@
+require 'spec_helper'
+require 'mspec/utils/options'
+require 'mspec/version'
+require 'mspec/guards/guard'
+require 'mspec/runner/mspec'
+require 'mspec/runner/formatters'
+
+describe MSpecOption, ".new" do
+ before :each do
+ @opt = MSpecOption.new("-a", "--bdc", "ARG", "desc", :block)
+ end
+
+ it "sets the short attribute" do
+ @opt.short.should == "-a"
+ end
+
+ it "sets the long attribute" do
+ @opt.long.should == "--bdc"
+ end
+
+ it "sets the arg attribute" do
+ @opt.arg.should == "ARG"
+ end
+
+ it "sets the description attribute" do
+ @opt.description.should == "desc"
+ end
+
+ it "sets the block attribute" do
+ @opt.block.should == :block
+ end
+end
+
+describe MSpecOption, "#arg?" do
+ it "returns true if arg attribute is not nil" do
+ MSpecOption.new(nil, nil, "ARG", nil, nil).arg?.should be_true
+ end
+
+ it "returns false if arg attribute is nil" do
+ MSpecOption.new(nil, nil, nil, nil, nil).arg?.should be_false
+ end
+end
+
+describe MSpecOption, "#match?" do
+ before :each do
+ @opt = MSpecOption.new("-a", "--bdc", "ARG", "desc", :block)
+ end
+
+ it "returns true if the argument matches the short option" do
+ @opt.match?("-a").should be_true
+ end
+
+ it "returns true if the argument matches the long option" do
+ @opt.match?("--bdc").should be_true
+ end
+
+ it "returns false if the argument matches neither the short nor long option" do
+ @opt.match?("-b").should be_false
+ @opt.match?("-abdc").should be_false
+ end
+end
+
+describe MSpecOptions, ".new" do
+ before :each do
+ @opt = MSpecOptions.new("cmd", 20, :config)
+ end
+
+ it "sets the banner attribute" do
+ @opt.banner.should == "cmd"
+ end
+
+ it "sets the config attribute" do
+ @opt.config.should == :config
+ end
+
+ it "sets the width attribute" do
+ @opt.width.should == 20
+ end
+
+ it "sets the default width attribute" do
+ MSpecOptions.new.width.should == 30
+ end
+end
+
+describe MSpecOptions, "#on" do
+ before :each do
+ @opt = MSpecOptions.new
+ end
+
+ it "adds a short option" do
+ @opt.should_receive(:add).with("-a", nil, nil, "desc", nil)
+ @opt.on("-a", "desc")
+ end
+
+ it "adds a short option taking an argument" do
+ @opt.should_receive(:add).with("-a", nil, "ARG", "desc", nil)
+ @opt.on("-a", "ARG", "desc")
+ end
+
+ it "adds a long option" do
+ @opt.should_receive(:add).with("-a", nil, nil, "desc", nil)
+ @opt.on("-a", "desc")
+ end
+
+ it "adds a long option taking an argument" do
+ @opt.should_receive(:add).with("-a", nil, nil, "desc", nil)
+ @opt.on("-a", "desc")
+ end
+
+ it "adds a short and long option" do
+ @opt.should_receive(:add).with("-a", nil, nil, "desc", nil)
+ @opt.on("-a", "desc")
+ end
+
+ it "adds a short and long option taking an argument" do
+ @opt.should_receive(:add).with("-a", nil, nil, "desc", nil)
+ @opt.on("-a", "desc")
+ end
+
+ it "raises MSpecOptions::OptionError if pass less than 2 arguments" do
+ lambda { @opt.on }.should raise_error(MSpecOptions::OptionError)
+ lambda { @opt.on "" }.should raise_error(MSpecOptions::OptionError)
+ end
+end
+
+describe MSpecOptions, "#add" do
+ before :each do
+ @opt = MSpecOptions.new "cmd", 20
+ @prc = lambda { }
+ end
+
+ it "adds documentation for an option" do
+ @opt.should_receive(:doc).with(" -t, --typo ARG Correct typo ARG")
+ @opt.add("-t", "--typo", "ARG", "Correct typo ARG", @prc)
+ end
+
+ it "leaves spaces in the documentation for a missing short option" do
+ @opt.should_receive(:doc).with(" --typo ARG Correct typo ARG")
+ @opt.add(nil, "--typo", "ARG", "Correct typo ARG", @prc)
+ end
+
+ it "handles a short option with argument but no long argument" do
+ @opt.should_receive(:doc).with(" -t ARG Correct typo ARG")
+ @opt.add("-t", nil, "ARG", "Correct typo ARG", @prc)
+ end
+
+ it "registers an option" do
+ option = MSpecOption.new "-t", "--typo", "ARG", "Correct typo ARG", @prc
+ MSpecOption.should_receive(:new).with(
+ "-t", "--typo", "ARG", "Correct typo ARG", @prc).and_return(option)
+ @opt.add("-t", "--typo", "ARG", "Correct typo ARG", @prc)
+ @opt.options.should == [option]
+ end
+end
+
+describe MSpecOptions, "#match?" do
+ before :each do
+ @opt = MSpecOptions.new
+ end
+
+ it "returns the MSpecOption instance matching the argument" do
+ @opt.on "-a", "--abdc", "desc"
+ option = @opt.match? "-a"
+ @opt.match?("--abdc").should be(option)
+ option.should be_kind_of(MSpecOption)
+ option.short.should == "-a"
+ option.long.should == "--abdc"
+ option.description.should == "desc"
+ end
+end
+
+describe MSpecOptions, "#process" do
+ before :each do
+ @opt = MSpecOptions.new
+ ScratchPad.clear
+ end
+
+ it "calls the on_extra block if the argument does not match any option" do
+ @opt.on_extra { ScratchPad.record :extra }
+ @opt.process ["-a"], "-a", "-a", nil
+ ScratchPad.recorded.should == :extra
+ end
+
+ it "returns the matching option" do
+ @opt.on "-a", "ARG", "desc"
+ option = @opt.process [], "-a", "-a", "ARG"
+ option.should be_kind_of(MSpecOption)
+ option.short.should == "-a"
+ option.arg.should == "ARG"
+ option.description.should == "desc"
+ end
+
+ it "raises an MSpecOptions::ParseError if arg is nil and there are no more entries in argv" do
+ @opt.on "-a", "ARG", "desc"
+ lambda { @opt.process [], "-a", "-a", nil }.should raise_error(MSpecOptions::ParseError)
+ end
+
+ it "fetches the argument for the option from argv if arg is nil" do
+ @opt.on("-a", "ARG", "desc") { |o| ScratchPad.record o }
+ @opt.process ["ARG"], "-a", "-a", nil
+ ScratchPad.recorded.should == "ARG"
+ end
+
+ it "calls the option's block" do
+ @opt.on("-a", "ARG", "desc") { ScratchPad.record :option }
+ @opt.process [], "-a", "-a", "ARG"
+ ScratchPad.recorded.should == :option
+ end
+
+ it "does not call the option's block if it is nil" do
+ @opt.on "-a", "ARG", "desc"
+ lambda { @opt.process [], "-a", "-a", "ARG" }.should_not raise_error
+ end
+end
+
+describe MSpecOptions, "#split" do
+ before :each do
+ @opt = MSpecOptions.new
+ end
+
+ it "breaks a string at the nth character" do
+ opt, arg, rest = @opt.split "-bdc", 2
+ opt.should == "-b"
+ arg.should == "dc"
+ rest.should == "dc"
+ end
+
+ it "returns nil for arg if there are no characters left" do
+ opt, arg, rest = @opt.split "-b", 2
+ opt.should == "-b"
+ arg.should == nil
+ rest.should == ""
+ end
+end
+
+describe MSpecOptions, "#parse" do
+ before :each do
+ @opt = MSpecOptions.new
+ @prc = lambda { ScratchPad.record :parsed }
+ @arg_prc = lambda { |o| ScratchPad.record [:parsed, o] }
+ ScratchPad.clear
+ end
+
+ it "parses a short option" do
+ @opt.on "-a", "desc", &@prc
+ @opt.parse ["-a"]
+ ScratchPad.recorded.should == :parsed
+ end
+
+ it "parse a long option" do
+ @opt.on "--abdc", "desc", &@prc
+ @opt.parse ["--abdc"]
+ ScratchPad.recorded.should == :parsed
+ end
+
+ it "parses a short option group" do
+ @opt.on "-a", "ARG", "desc", &@arg_prc
+ @opt.parse ["-a", "ARG"]
+ ScratchPad.recorded.should == [:parsed, "ARG"]
+ end
+
+ it "parses a short option with an argument" do
+ @opt.on "-a", "ARG", "desc", &@arg_prc
+ @opt.parse ["-a", "ARG"]
+ ScratchPad.recorded.should == [:parsed, "ARG"]
+ end
+
+ it "parses a short option with connected argument" do
+ @opt.on "-a", "ARG", "desc", &@arg_prc
+ @opt.parse ["-aARG"]
+ ScratchPad.recorded.should == [:parsed, "ARG"]
+ end
+
+ it "parses a long option with an argument" do
+ @opt.on "--abdc", "ARG", "desc", &@arg_prc
+ @opt.parse ["--abdc", "ARG"]
+ ScratchPad.recorded.should == [:parsed, "ARG"]
+ end
+
+ it "parses a long option with an '=' argument" do
+ @opt.on "--abdc", "ARG", "desc", &@arg_prc
+ @opt.parse ["--abdc=ARG"]
+ ScratchPad.recorded.should == [:parsed, "ARG"]
+ end
+
+ it "parses a short option group with the final option taking an argument" do
+ ScratchPad.record []
+ @opt.on("-a", "desc") { |o| ScratchPad << :a }
+ @opt.on("-b", "ARG", "desc") { |o| ScratchPad << [:b, o] }
+ @opt.parse ["-ab", "ARG"]
+ ScratchPad.recorded.should == [:a, [:b, "ARG"]]
+ end
+
+ it "parses a short option group with a connected argument" do
+ ScratchPad.record []
+ @opt.on("-a", "desc") { |o| ScratchPad << :a }
+ @opt.on("-b", "ARG", "desc") { |o| ScratchPad << [:b, o] }
+ @opt.on("-c", "desc") { |o| ScratchPad << :c }
+ @opt.parse ["-acbARG"]
+ ScratchPad.recorded.should == [:a, :c, [:b, "ARG"]]
+ end
+
+ it "returns the unprocessed entries" do
+ @opt.on "-a", "ARG", "desc", &@arg_prc
+ @opt.parse(["abdc", "-a", "ilny"]).should == ["abdc"]
+ end
+
+ it "calls the on_extra handler with unrecognized options" do
+ ScratchPad.record []
+ @opt.on_extra { |o| ScratchPad << o }
+ @opt.on "-a", "desc"
+ @opt.parse ["-a", "-b"]
+ ScratchPad.recorded.should == ["-b"]
+ end
+
+ it "does not attempt to call the block if it is nil" do
+ @opt.on "-a", "ARG", "desc"
+ @opt.parse(["-a", "ARG"]).should == []
+ end
+
+ it "raises MSpecOptions::ParseError if passed an unrecognized option" do
+ @opt.should_receive(:raise).with(MSpecOptions::ParseError, an_instance_of(String))
+ @opt.stub(:puts)
+ @opt.stub(:exit)
+ @opt.parse "-u"
+ end
+end
+
+describe MSpecOptions, "#banner=" do
+ before :each do
+ @opt = MSpecOptions.new
+ end
+
+ it "sets the banner attribute" do
+ @opt.banner.should == ""
+ @opt.banner = "banner"
+ @opt.banner.should == "banner"
+ end
+end
+
+describe MSpecOptions, "#width=" do
+ before :each do
+ @opt = MSpecOptions.new
+ end
+
+ it "sets the width attribute" do
+ @opt.width.should == 30
+ @opt.width = 20
+ @opt.width.should == 20
+ end
+end
+
+describe MSpecOptions, "#config=" do
+ before :each do
+ @opt = MSpecOptions.new
+ end
+
+ it "sets the config attribute" do
+ @opt.config.should be_nil
+ @opt.config = :config
+ @opt.config.should == :config
+ end
+end
+
+describe MSpecOptions, "#doc" do
+ before :each do
+ @opt = MSpecOptions.new "command"
+ end
+
+ it "adds text to be displayed with #to_s" do
+ @opt.doc "Some message"
+ @opt.doc "Another message"
+ @opt.to_s.should == <<-EOD
+command
+
+Some message
+Another message
+EOD
+ end
+end
+
+describe MSpecOptions, "#version" do
+ before :each do
+ @opt = MSpecOptions.new
+ ScratchPad.clear
+ end
+
+ it "installs a basic -v, --version option" do
+ @opt.should_receive(:puts)
+ @opt.should_receive(:exit)
+ @opt.version "1.0.0"
+ @opt.parse "-v"
+ end
+
+ it "accepts a block instead of using the default block" do
+ @opt.version("1.0.0") { |o| ScratchPad.record :version }
+ @opt.parse "-v"
+ ScratchPad.recorded.should == :version
+ end
+end
+
+describe MSpecOptions, "#help" do
+ before :each do
+ @opt = MSpecOptions.new
+ ScratchPad.clear
+ end
+
+ it "installs a basic -h, --help option" do
+ @opt.should_receive(:puts)
+ @opt.should_receive(:exit).with(1)
+ @opt.help
+ @opt.parse "-h"
+ end
+
+ it "accepts a block instead of using the default block" do
+ @opt.help { |o| ScratchPad.record :help }
+ @opt.parse "-h"
+ ScratchPad.recorded.should == :help
+ end
+end
+
+describe MSpecOptions, "#on_extra" do
+ before :each do
+ @opt = MSpecOptions.new
+ ScratchPad.clear
+ end
+
+ it "registers a block to be called when an option is not recognized" do
+ @opt.on_extra { ScratchPad.record :extra }
+ @opt.parse "-g"
+ ScratchPad.recorded.should == :extra
+ end
+end
+
+describe MSpecOptions, "#to_s" do
+ before :each do
+ @opt = MSpecOptions.new "command"
+ end
+
+ it "returns the banner and descriptive strings for all registered options" do
+ @opt.on "-t", "--this ARG", "Adds this ARG to the list"
+ @opt.to_s.should == <<-EOD
+command
+
+ -t, --this ARG Adds this ARG to the list
+EOD
+ end
+end
+
+describe "The -B, --config FILE option" do
+ before :each do
+ @options, @config = new_option
+ end
+
+ it "is enabled with #configure { }" do
+ @options.should_receive(:on).with("-B", "--config", "FILE",
+ an_instance_of(String))
+ @options.configure {}
+ end
+
+ it "calls the passed block" do
+ ["-B", "--config"].each do |opt|
+ ScratchPad.clear
+
+ @options.configure { |x| ScratchPad.record x }
+ @options.parse [opt, "file"]
+ ScratchPad.recorded.should == "file"
+ end
+ end
+end
+
+describe "The -C, --chdir DIR option" do
+ before :each do
+ @options, @config = new_option
+ @options.chdir
+ end
+
+ it "is enabled with #chdir" do
+ @options.should_receive(:on).with("-C", "--chdir", "DIR",
+ an_instance_of(String))
+ @options.chdir
+ end
+
+ it "changes the working directory to DIR" do
+ Dir.should_receive(:chdir).with("dir").twice
+ ["-C", "--chdir"].each do |opt|
+ @options.parse [opt, "dir"]
+ end
+ end
+end
+
+describe "The --prefix STR option" do
+ before :each do
+ @options, @config = new_option
+ end
+
+ it "is enabled with #prefix" do
+ @options.should_receive(:on).with("--prefix", "STR",
+ an_instance_of(String))
+ @options.prefix
+ end
+
+ it "sets the prefix config value" do
+ @options.prefix
+ @options.parse ["--prefix", "some/dir"]
+ @config[:prefix].should == "some/dir"
+ end
+end
+
+describe "The -n, --name RUBY_NAME option" do
+ before :each do
+ @verbose, $VERBOSE = $VERBOSE, nil
+ @options, @config = new_option
+ end
+
+ after :each do
+ $VERBOSE = @verbose
+ end
+
+ it "is enabled with #name" do
+ @options.should_receive(:on).with("-n", "--name", "RUBY_NAME",
+ an_instance_of(String))
+ @options.name
+ end
+
+ it "sets RUBY_NAME when invoked" do
+ Object.should_receive(:const_set).with(:RUBY_NAME, "name").twice
+ @options.name
+ @options.parse ["-n", "name"]
+ @options.parse ["--name", "name"]
+ end
+end
+
+describe "The -t, --target TARGET option" do
+ before :each do
+ @options, @config = new_option
+ @options.targets
+ end
+
+ it "is enabled with #targets" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-t", "--target", "TARGET",
+ an_instance_of(String))
+ @options.targets
+ end
+
+ it "sets the target to 'ruby' and flags to verbose with TARGET 'r' or 'ruby'" do
+ ["-t", "--target"].each do |opt|
+ ["r", "ruby"].each do |t|
+ @config[:target] = nil
+ @options.parse [opt, t]
+ @config[:target].should == "ruby"
+ end
+ end
+ end
+
+ it "sets the target to 'jruby' with TARGET 'j' or 'jruby'" do
+ ["-t", "--target"].each do |opt|
+ ["j", "jruby"].each do |t|
+ @config[:target] = nil
+ @options.parse [opt, t]
+ @config[:target].should == "jruby"
+ end
+ end
+ end
+
+ it "sets the target to 'shotgun/rubinius' with TARGET 'x' or 'rubinius'" do
+ ["-t", "--target"].each do |opt|
+ ["x", "rubinius"].each do |t|
+ @config[:target] = nil
+ @options.parse [opt, t]
+ @config[:target].should == "./bin/rbx"
+ end
+ end
+ end
+
+ it "set the target to 'rbx' with TARGET 'rbx'" do
+ ["-t", "--target"].each do |opt|
+ ["X", "rbx"].each do |t|
+ @config[:target] = nil
+ @options.parse [opt, t]
+ @config[:target].should == "rbx"
+ end
+ end
+ end
+
+ it "sets the target to 'maglev' with TARGET 'm' or 'maglev'" do
+ ["-t", "--target"].each do |opt|
+ ["m", "maglev"].each do |t|
+ @config[:target] = nil
+ @options.parse [opt, t]
+ @config[:target].should == "maglev-ruby"
+ end
+ end
+ end
+
+ it "sets the target to 'topaz' with TARGET 't' or 'topaz'" do
+ ["-t", "--target"].each do |opt|
+ ["t", "topaz"].each do |t|
+ @config[:target] = nil
+ @options.parse [opt, t]
+ @config[:target].should == "topaz"
+ end
+ end
+ end
+
+ it "sets the target to TARGET" do
+ ["-t", "--target"].each do |opt|
+ @config[:target] = nil
+ @options.parse [opt, "whateva"]
+ @config[:target].should == "whateva"
+ end
+ end
+end
+
+describe "The -T, --target-opt OPT option" do
+ before :each do
+ @options, @config = new_option
+ @options.targets
+ end
+
+ it "is enabled with #targets" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-T", "--target-opt", "OPT",
+ an_instance_of(String))
+ @options.targets
+ end
+
+ it "adds OPT to flags" do
+ ["-T", "--target-opt"].each do |opt|
+ @config[:flags].delete "--whateva"
+ @options.parse [opt, "--whateva"]
+ @config[:flags].should include("--whateva")
+ end
+ end
+end
+
+describe "The -I, --include DIR option" do
+ before :each do
+ @options, @config = new_option
+ @options.targets
+ end
+
+ it "is enabled with #targets" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-I", "--include", "DIR",
+ an_instance_of(String))
+ @options.targets
+ end
+
+ it "add DIR to the load path" do
+ ["-I", "--include"].each do |opt|
+ @config[:loadpath].delete "-Ipackage"
+ @options.parse [opt, "package"]
+ @config[:loadpath].should include("-Ipackage")
+ end
+ end
+end
+
+describe "The -r, --require LIBRARY option" do
+ before :each do
+ @options, @config = new_option
+ @options.targets
+ end
+
+ it "is enabled with #targets" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-r", "--require", "LIBRARY",
+ an_instance_of(String))
+ @options.targets
+ end
+
+ it "adds LIBRARY to the requires list" do
+ ["-r", "--require"].each do |opt|
+ @config[:requires].delete "-rlibrick"
+ @options.parse [opt, "librick"]
+ @config[:requires].should include("-rlibrick")
+ end
+ end
+end
+
+describe "The -f, --format FORMAT option" do
+ before :each do
+ @options, @config = new_option
+ @options.formatters
+ end
+
+ it "is enabled with #formatters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-f", "--format", "FORMAT",
+ an_instance_of(String))
+ @options.formatters
+ end
+
+ it "sets the SpecdocFormatter with FORMAT 's' or 'specdoc'" do
+ ["-f", "--format"].each do |opt|
+ ["s", "specdoc"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == SpecdocFormatter
+ end
+ end
+ end
+
+ it "sets the HtmlFormatter with FORMAT 'h' or 'html'" do
+ ["-f", "--format"].each do |opt|
+ ["h", "html"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == HtmlFormatter
+ end
+ end
+ end
+
+ it "sets the DottedFormatter with FORMAT 'd', 'dot' or 'dotted'" do
+ ["-f", "--format"].each do |opt|
+ ["d", "dot", "dotted"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == DottedFormatter
+ end
+ end
+ end
+
+ it "sets the DescribeFormatter with FORMAT 'b' or 'describe'" do
+ ["-f", "--format"].each do |opt|
+ ["b", "describe"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == DescribeFormatter
+ end
+ end
+ end
+
+ it "sets the FileFormatter with FORMAT 'f', 'file'" do
+ ["-f", "--format"].each do |opt|
+ ["f", "file"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == FileFormatter
+ end
+ end
+ end
+
+ it "sets the UnitdiffFormatter with FORMAT 'u', 'unit', or 'unitdiff'" do
+ ["-f", "--format"].each do |opt|
+ ["u", "unit", "unitdiff"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == UnitdiffFormatter
+ end
+ end
+ end
+
+ it "sets the SummaryFormatter with FORMAT 'm' or 'summary'" do
+ ["-f", "--format"].each do |opt|
+ ["m", "summary"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == SummaryFormatter
+ end
+ end
+ end
+
+ it "sets the SpinnerFormatter with FORMAT 'a', '*', or 'spin'" do
+ ["-f", "--format"].each do |opt|
+ ["a", "*", "spin"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == SpinnerFormatter
+ end
+ end
+ end
+
+ it "sets the MethodFormatter with FORMAT 't' or 'method'" do
+ ["-f", "--format"].each do |opt|
+ ["t", "method"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == MethodFormatter
+ end
+ end
+ end
+
+ it "sets the YamlFormatter with FORMAT 'y' or 'yaml'" do
+ ["-f", "--format"].each do |opt|
+ ["y", "yaml"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == YamlFormatter
+ end
+ end
+ end
+
+ it "sets the JUnitFormatter with FORMAT 'j' or 'junit'" do
+ ["-f", "--format"].each do |opt|
+ ["j", "junit"].each do |f|
+ @config[:formatter] = nil
+ @options.parse [opt, f]
+ @config[:formatter].should == JUnitFormatter
+ end
+ end
+ end
+end
+
+describe "The -o, --output FILE option" do
+ before :each do
+ @options, @config = new_option
+ @options.formatters
+ end
+
+ it "is enabled with #formatters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-o", "--output", "FILE",
+ an_instance_of(String))
+ @options.formatters
+ end
+
+ it "sets the output to FILE" do
+ ["-o", "--output"].each do |opt|
+ @config[:output] = nil
+ @options.parse [opt, "some/file"]
+ @config[:output].should == "some/file"
+ end
+ end
+end
+
+describe "The -e, --example STR" do
+ before :each do
+ @options, @config = new_option
+ @options.filters
+ end
+
+ it "is enabled with #filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-e", "--example", "STR",
+ an_instance_of(String))
+ @options.filters
+ end
+
+ it "adds STR to the includes list" do
+ ["-e", "--example"].each do |opt|
+ @config[:includes] = []
+ @options.parse [opt, "this spec"]
+ @config[:includes].should include("this spec")
+ end
+ end
+end
+
+describe "The -E, --exclude STR" do
+ before :each do
+ @options, @config = new_option
+ @options.filters
+ end
+
+ it "is enabled with #filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-E", "--exclude", "STR",
+ an_instance_of(String))
+ @options.filters
+ end
+
+ it "adds STR to the excludes list" do
+ ["-E", "--exclude"].each do |opt|
+ @config[:excludes] = []
+ @options.parse [opt, "this spec"]
+ @config[:excludes].should include("this spec")
+ end
+ end
+end
+
+describe "The -p, --pattern PATTERN" do
+ before :each do
+ @options, @config = new_option
+ @options.filters
+ end
+
+ it "is enabled with #filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-p", "--pattern", "PATTERN",
+ an_instance_of(String))
+ @options.filters
+ end
+
+ it "adds PATTERN to the included patterns list" do
+ ["-p", "--pattern"].each do |opt|
+ @config[:patterns] = []
+ @options.parse [opt, "this spec"]
+ @config[:patterns].should include(/this spec/)
+ end
+ end
+end
+
+describe "The -P, --excl-pattern PATTERN" do
+ before :each do
+ @options, @config = new_option
+ @options.filters
+ end
+
+ it "is enabled with #filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-P", "--excl-pattern", "PATTERN",
+ an_instance_of(String))
+ @options.filters
+ end
+
+ it "adds PATTERN to the excluded patterns list" do
+ ["-P", "--excl-pattern"].each do |opt|
+ @config[:xpatterns] = []
+ @options.parse [opt, "this spec"]
+ @config[:xpatterns].should include(/this spec/)
+ end
+ end
+end
+
+describe "The -g, --tag TAG" do
+ before :each do
+ @options, @config = new_option
+ @options.filters
+ end
+
+ it "is enabled with #filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-g", "--tag", "TAG",
+ an_instance_of(String))
+ @options.filters
+ end
+
+ it "adds TAG to the included tags list" do
+ ["-g", "--tag"].each do |opt|
+ @config[:tags] = []
+ @options.parse [opt, "this spec"]
+ @config[:tags].should include("this spec")
+ end
+ end
+end
+
+describe "The -G, --excl-tag TAG" do
+ before :each do
+ @options, @config = new_option
+ @options.filters
+ end
+
+ it "is enabled with #filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-G", "--excl-tag", "TAG",
+ an_instance_of(String))
+ @options.filters
+ end
+
+ it "adds TAG to the excluded tags list" do
+ ["-G", "--excl-tag"].each do |opt|
+ @config[:xtags] = []
+ @options.parse [opt, "this spec"]
+ @config[:xtags].should include("this spec")
+ end
+ end
+end
+
+describe "The -w, --profile FILE option" do
+ before :each do
+ @options, @config = new_option
+ @options.filters
+ end
+
+ it "is enabled with #filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-w", "--profile", "FILE",
+ an_instance_of(String))
+ @options.filters
+ end
+
+ it "adds FILE to the included profiles list" do
+ ["-w", "--profile"].each do |opt|
+ @config[:profiles] = []
+ @options.parse [opt, "spec/profiles/rails.yaml"]
+ @config[:profiles].should include("spec/profiles/rails.yaml")
+ end
+ end
+end
+
+describe "The -W, --excl-profile FILE option" do
+ before :each do
+ @options, @config = new_option
+ @options.filters
+ end
+
+ it "is enabled with #filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-W", "--excl-profile", "FILE",
+ an_instance_of(String))
+ @options.filters
+ end
+
+ it "adds FILE to the excluded profiles list" do
+ ["-W", "--excl-profile"].each do |opt|
+ @config[:xprofiles] = []
+ @options.parse [opt, "spec/profiles/rails.yaml"]
+ @config[:xprofiles].should include("spec/profiles/rails.yaml")
+ end
+ end
+end
+
+describe "The -Z, --dry-run option" do
+ before :each do
+ @options, @config = new_option
+ @options.pretend
+ end
+
+ it "is enabled with #pretend" do
+ @options.should_receive(:on).with("-Z", "--dry-run", an_instance_of(String))
+ @options.pretend
+ end
+
+ it "registers the MSpec pretend mode" do
+ MSpec.should_receive(:register_mode).with(:pretend).twice
+ ["-Z", "--dry-run"].each do |opt|
+ @options.parse opt
+ end
+ end
+end
+
+describe "The --unguarded option" do
+ before :each do
+ @options, @config = new_option
+ @options.unguarded
+ end
+
+ it "is enabled with #unguarded" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("--unguarded", an_instance_of(String))
+ @options.unguarded
+ end
+
+ it "registers the MSpec unguarded mode" do
+ MSpec.should_receive(:register_mode).with(:unguarded)
+ @options.parse "--unguarded"
+ end
+end
+
+describe "The --no-ruby_guard option" do
+ before :each do
+ @options, @config = new_option
+ @options.unguarded
+ end
+
+ it "is enabled with #unguarded" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("--no-ruby_bug", an_instance_of(String))
+ @options.unguarded
+ end
+
+ it "registers the MSpec no_ruby_bug mode" do
+ MSpec.should_receive(:register_mode).with(:no_ruby_bug)
+ @options.parse "--no-ruby_bug"
+ end
+end
+
+describe "The -H, --random option" do
+ before :each do
+ @options, @config = new_option
+ @options.randomize
+ end
+
+ it "is enabled with #randomize" do
+ @options.should_receive(:on).with("-H", "--random", an_instance_of(String))
+ @options.randomize
+ end
+
+ it "registers the MSpec randomize mode" do
+ MSpec.should_receive(:randomize).twice
+ ["-H", "--random"].each do |opt|
+ @options.parse opt
+ end
+ end
+end
+
+describe "The -R, --repeat option" do
+ before :each do
+ @options, @config = new_option
+ @options.repeat
+ end
+
+ it "is enabled with #repeat" do
+ @options.should_receive(:on).with("-R", "--repeat", "NUMBER", an_instance_of(String))
+ @options.repeat
+ end
+
+ it "registers the MSpec repeat mode" do
+ ["-R", "--repeat"].each do |opt|
+ MSpec.repeat = 1
+ @options.parse [opt, "10"]
+ repeat_count = 0
+ MSpec.repeat do
+ repeat_count += 1
+ end
+ repeat_count.should == 10
+ end
+ end
+end
+
+describe "The -V, --verbose option" do
+ before :each do
+ @options, @config = new_option
+ @options.verbose
+ end
+
+ it "is enabled with #verbose" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-V", "--verbose", an_instance_of(String))
+ @options.verbose
+ end
+
+ it "registers a verbose output object with MSpec" do
+ MSpec.should_receive(:register).with(:start, anything()).twice
+ MSpec.should_receive(:register).with(:load, anything()).twice
+ ["-V", "--verbose"].each do |opt|
+ @options.parse opt
+ end
+ end
+end
+
+describe "The -m, --marker MARKER option" do
+ before :each do
+ @options, @config = new_option
+ @options.verbose
+ end
+
+ it "is enabled with #verbose" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-m", "--marker", "MARKER",
+ an_instance_of(String))
+ @options.verbose
+ end
+
+ it "registers a marker output object with MSpec" do
+ MSpec.should_receive(:register).with(:load, anything()).twice
+ ["-m", "--marker"].each do |opt|
+ @options.parse [opt, ","]
+ end
+ end
+end
+
+describe "The --int-spec option" do
+ before :each do
+ @options, @config = new_option
+ @options.interrupt
+ end
+
+ it "is enabled with #interrupt" do
+ @options.should_receive(:on).with("--int-spec", an_instance_of(String))
+ @options.interrupt
+ end
+
+ it "sets the abort config option to false to only abort the running spec with ^C" do
+ @config[:abort] = true
+ @options.parse "--int-spec"
+ @config[:abort].should == false
+ end
+end
+
+describe "The -Y, --verify option" do
+ before :each do
+ @options, @config = new_option
+ @options.verify
+ end
+
+ it "is enabled with #interrupt" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-Y", "--verify", an_instance_of(String))
+ @options.verify
+ end
+
+ it "sets the MSpec mode to :verify" do
+ MSpec.should_receive(:register_mode).with(:verify).twice
+ ["-Y", "--verify"].each do |m|
+ @options.parse m
+ end
+ end
+end
+
+describe "The -O, --report option" do
+ before :each do
+ @options, @config = new_option
+ @options.verify
+ end
+
+ it "is enabled with #interrupt" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-O", "--report", an_instance_of(String))
+ @options.verify
+ end
+
+ it "sets the MSpec mode to :report" do
+ MSpec.should_receive(:register_mode).with(:report).twice
+ ["-O", "--report"].each do |m|
+ @options.parse m
+ end
+ end
+end
+
+describe "The --report-on GUARD option" do
+ before :all do
+ MSpec.stub(:register_mode)
+ end
+
+ before :each do
+ @options, @config = new_option
+ @options.verify
+
+ SpecGuard.clear_guards
+ end
+
+ after :each do
+ SpecGuard.clear_guards
+ end
+
+ it "is enabled with #interrupt" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("--report-on", "GUARD", an_instance_of(String))
+ @options.verify
+ end
+
+ it "sets the MSpec mode to :report_on" do
+ MSpec.should_receive(:register_mode).with(:report_on)
+ @options.parse ["--report-on", "ruby_bug"]
+ end
+
+ it "converts the guard name to a symbol" do
+ name = double("ruby_bug")
+ name.should_receive(:to_sym)
+ @options.parse ["--report-on", name]
+ end
+
+ it "saves the name of the guard" do
+ @options.parse ["--report-on", "ruby_bug"]
+ SpecGuard.guards.should == [:ruby_bug]
+ end
+end
+
+describe "The -K, --action-tag TAG option" do
+ before :each do
+ @options, @config = new_option
+ @options.action_filters
+ end
+
+ it "is enabled with #action_filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-K", "--action-tag", "TAG",
+ an_instance_of(String))
+ @options.action_filters
+ end
+
+ it "adds TAG to the list of tags that trigger actions" do
+ ["-K", "--action-tag"].each do |opt|
+ @config[:atags] = []
+ @options.parse [opt, "action-tag"]
+ @config[:atags].should include("action-tag")
+ end
+ end
+end
+
+describe "The -S, --action-string STR option" do
+ before :each do
+ @options, @config = new_option
+ @options.action_filters
+ end
+
+ it "is enabled with #action_filters" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-S", "--action-string", "STR",
+ an_instance_of(String))
+ @options.action_filters
+ end
+
+ it "adds STR to the list of spec descriptions that trigger actions" do
+ ["-S", "--action-string"].each do |opt|
+ @config[:astrings] = []
+ @options.parse [opt, "action-str"]
+ @config[:astrings].should include("action-str")
+ end
+ end
+end
+
+describe "The -d, --debug option" do
+ before :each do
+ @options, @config = new_option
+ @options.debug
+ end
+
+ after :each do
+ $MSPEC_DEBUG = nil
+ end
+
+ it "is enabled with #debug" do
+ @options.stub(:on)
+ @options.should_receive(:on).with("-d", "--debug", an_instance_of(String))
+ @options.debug
+ end
+
+ it "sets $MSPEC_DEBUG to true" do
+ ["-d", "--debug"].each do |opt|
+ $MSPEC_DEBUG.should_not be_true
+ @options.parse opt
+ $MSPEC_DEBUG.should be_true
+ $MSPEC_DEBUG = nil
+ end
+ end
+end
diff --git a/spec/mspec/spec/utils/script_spec.rb b/spec/mspec/spec/utils/script_spec.rb
new file mode 100644
index 0000000000..62f6787acd
--- /dev/null
+++ b/spec/mspec/spec/utils/script_spec.rb
@@ -0,0 +1,473 @@
+require 'spec_helper'
+require 'mspec/utils/script'
+require 'mspec/runner/mspec'
+require 'mspec/runner/filters'
+require 'mspec/runner/actions/filter'
+
+describe MSpecScript, ".config" do
+ it "returns a Hash" do
+ MSpecScript.config.should be_kind_of(Hash)
+ end
+end
+
+describe MSpecScript, ".set" do
+ it "sets the config hash key, value" do
+ MSpecScript.set :a, 10
+ MSpecScript.config[:a].should == 10
+ end
+end
+
+describe MSpecScript, ".get" do
+ it "gets the config hash value for a key" do
+ MSpecScript.set :a, 10
+ MSpecScript.get(:a).should == 10
+ end
+end
+
+describe MSpecScript, "#config" do
+ it "returns the MSpecScript config hash" do
+ MSpecScript.set :b, 5
+ MSpecScript.new.config[:b].should == 5
+ end
+
+ it "returns the MSpecScript config hash from subclasses" do
+ class MSSClass < MSpecScript; end
+ MSpecScript.set :b, 5
+ MSSClass.new.config[:b].should == 5
+ end
+end
+
+describe MSpecScript, "#load_default" do
+ before :all do
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ after :all do
+ $VERBOSE = @verbose
+ end
+
+ before :each do
+ @version = RUBY_VERSION
+ if Object.const_defined? :RUBY_ENGINE
+ @engine = Object.const_get :RUBY_ENGINE
+ end
+ @script = MSpecScript.new
+ MSpecScript.stub(:new).and_return(@script)
+ end
+
+ after :each do
+ Object.const_set :RUBY_VERSION, @version
+ Object.const_set :RUBY_ENGINE, @engine if @engine
+ end
+
+ it "attempts to load 'default.mspec'" do
+ @script.stub(:try_load)
+ @script.should_receive(:try_load).with('default.mspec').and_return(true)
+ @script.load_default
+ end
+
+ it "attempts to load a config file based on RUBY_ENGINE and RUBY_VERSION" do
+ Object.const_set :RUBY_ENGINE, "ybur"
+ Object.const_set :RUBY_VERSION, "1.8.9"
+ default = "ybur.1.8.mspec"
+ @script.should_receive(:try_load).with('default.mspec').and_return(false)
+ @script.should_receive(:try_load).with(default)
+ @script.should_receive(:try_load).with('ybur.mspec')
+ @script.load_default
+ end
+end
+
+describe MSpecScript, ".main" do
+ before :each do
+ @script = double("MSpecScript").as_null_object
+ MSpecScript.stub(:new).and_return(@script)
+ # Do not require full mspec as it would conflict with RSpec
+ MSpecScript.should_receive(:require).with('mspec')
+ end
+
+ it "creates an instance of MSpecScript" do
+ MSpecScript.should_receive(:new).and_return(@script)
+ MSpecScript.main
+ end
+
+ it "attempts to load the default config" do
+ @script.should_receive(:load_default)
+ MSpecScript.main
+ end
+
+ it "attempts to load the '~/.mspecrc' script" do
+ @script.should_receive(:try_load).with('~/.mspecrc')
+ MSpecScript.main
+ end
+
+ it "calls the #options method on the script" do
+ @script.should_receive(:options)
+ MSpecScript.main
+ end
+
+ it "calls the #signals method on the script" do
+ @script.should_receive(:signals)
+ MSpecScript.main
+ end
+
+ it "calls the #register method on the script" do
+ @script.should_receive(:register)
+ MSpecScript.main
+ end
+
+ it "calls the #setup_env method on the script" do
+ @script.should_receive(:setup_env)
+ MSpecScript.main
+ end
+
+ it "calls the #run method on the script" do
+ @script.should_receive(:run)
+ MSpecScript.main
+ end
+end
+
+describe MSpecScript, "#initialize" do
+ before :each do
+ @config = MSpecScript.new.config
+ end
+
+ it "sets the default config values" do
+ @config[:formatter].should == nil
+ @config[:includes].should == []
+ @config[:excludes].should == []
+ @config[:patterns].should == []
+ @config[:xpatterns].should == []
+ @config[:tags].should == []
+ @config[:xtags].should == []
+ @config[:atags].should == []
+ @config[:astrings].should == []
+ @config[:abort].should == true
+ @config[:config_ext].should == '.mspec'
+ end
+end
+
+describe MSpecScript, "#load" do
+ before :each do
+ File.stub(:exist?).and_return(false)
+ @script = MSpecScript.new
+ @file = "default.mspec"
+ @base = "default"
+ 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)
+ Kernel.should_receive(:load).with(@file).and_return(:loaded)
+ @script.load(@file).should == :loaded
+ end
+
+ it "appends config[:config_ext] to the name and attempts to locate the file through the expanded path name" do
+ File.should_receive(:expand_path).with(@base, ".").and_return(@base)
+ File.should_receive(:expand_path).with(@base, "spec").and_return(@base)
+ File.should_receive(:expand_path).with(@file, ".").and_return(@file)
+ File.should_receive(:exist?).with(@base).and_return(false)
+ File.should_receive(:exist?).with(@file).and_return(true)
+ Kernel.should_receive(:load).with(@file).and_return(:loaded)
+ @script.load(@base).should == :loaded
+ end
+
+ it "attemps to locate the file in '.'" do
+ path = File.expand_path @file, "."
+ File.should_receive(:exist?).with(path).and_return(true)
+ Kernel.should_receive(:load).with(path).and_return(:loaded)
+ @script.load(@file).should == :loaded
+ end
+
+ it "appends config[:config_ext] to the name and attempts to locate the file in '.'" do
+ path = File.expand_path @file, "."
+ File.should_receive(:exist?).with(path).and_return(true)
+ Kernel.should_receive(:load).with(path).and_return(:loaded)
+ @script.load(@base).should == :loaded
+ end
+
+ it "attemps to locate the file in 'spec'" do
+ path = File.expand_path @file, "spec"
+ File.should_receive(:exist?).with(path).and_return(true)
+ Kernel.should_receive(:load).with(path).and_return(:loaded)
+ @script.load(@file).should == :loaded
+ end
+
+ it "appends config[:config_ext] to the name and attempts to locate the file in 'spec'" do
+ path = File.expand_path @file, "spec"
+ File.should_receive(:exist?).with(path).and_return(true)
+ Kernel.should_receive(:load).with(path).and_return(:loaded)
+ @script.load(@base).should == :loaded
+ end
+
+ it "loads a given file only once" do
+ path = File.expand_path @file, "spec"
+ File.should_receive(:exist?).with(path).and_return(true)
+ Kernel.should_receive(:load).once.with(path).and_return(:loaded)
+ @script.load(@base).should == :loaded
+ @script.load(@base).should == true
+ end
+end
+
+describe MSpecScript, "#custom_options" do
+ before :each do
+ @script = MSpecScript.new
+ end
+
+ after :each do
+ end
+
+ it "prints 'None'" do
+ options = double("options")
+ options.should_receive(:doc).with(" No custom options registered")
+ @script.custom_options options
+ end
+end
+
+describe MSpecScript, "#register" do
+ before :each do
+ @script = MSpecScript.new
+
+ @formatter = double("formatter").as_null_object
+ @script.config[:formatter] = @formatter
+ end
+
+ it "creates and registers the formatter" do
+ @formatter.should_receive(:new).and_return(@formatter)
+ @formatter.should_receive(:register)
+ @script.register
+ end
+
+ it "does not register the formatter if config[:formatter] is false" do
+ @script.config[:formatter] = false
+ @script.register
+ end
+
+ it "calls #custom_register" do
+ @script.should_receive(:custom_register)
+ @script.register
+ end
+
+ it "registers :formatter with the formatter instance" do
+ @formatter.stub(:new).and_return(@formatter)
+ MSpec.should_receive(:store).with(:formatter, @formatter)
+ @script.register
+ end
+
+ it "does not register :formatter if config[:formatter] is false" do
+ @script.config[:formatter] = false
+ MSpec.should_not_receive(:store)
+ @script.register
+ end
+end
+
+describe MSpecScript, "#register" do
+ before :each do
+ @script = MSpecScript.new
+
+ @formatter = double("formatter").as_null_object
+ @script.config[:formatter] = @formatter
+
+ @filter = double("filter")
+ @filter.should_receive(:register)
+
+ @ary = ["some", "spec"]
+ end
+
+ it "creates and registers a MatchFilter for include specs" do
+ MatchFilter.should_receive(:new).with(:include, *@ary).and_return(@filter)
+ @script.config[:includes] = @ary
+ @script.register
+ end
+
+ it "creates and registers a MatchFilter for excluded specs" do
+ MatchFilter.should_receive(:new).with(:exclude, *@ary).and_return(@filter)
+ @script.config[:excludes] = @ary
+ @script.register
+ end
+
+ it "creates and registers a RegexpFilter for include specs" do
+ RegexpFilter.should_receive(:new).with(:include, *@ary).and_return(@filter)
+ @script.config[:patterns] = @ary
+ @script.register
+ end
+
+ it "creates and registers a RegexpFilter for excluded specs" do
+ RegexpFilter.should_receive(:new).with(:exclude, *@ary).and_return(@filter)
+ @script.config[:xpatterns] = @ary
+ @script.register
+ end
+
+ it "creates and registers a TagFilter for include specs" do
+ TagFilter.should_receive(:new).with(:include, *@ary).and_return(@filter)
+ @script.config[:tags] = @ary
+ @script.register
+ end
+
+ it "creates and registers a TagFilter for excluded specs" do
+ TagFilter.should_receive(:new).with(:exclude, *@ary).and_return(@filter)
+ @script.config[:xtags] = @ary
+ @script.register
+ end
+
+ it "creates and registers a ProfileFilter for include specs" do
+ ProfileFilter.should_receive(:new).with(:include, *@ary).and_return(@filter)
+ @script.config[:profiles] = @ary
+ @script.register
+ end
+
+ it "creates and registers a ProfileFilter for excluded specs" do
+ ProfileFilter.should_receive(:new).with(:exclude, *@ary).and_return(@filter)
+ @script.config[:xprofiles] = @ary
+ @script.register
+ end
+end
+
+describe MSpecScript, "#signals" do
+ before :each do
+ @script = MSpecScript.new
+ @abort = @script.config[:abort]
+ end
+
+ after :each do
+ @script.config[:abort] = @abort
+ end
+
+ it "traps the INT signal if config[:abort] is true" do
+ Signal.should_receive(:trap).with("INT")
+ @script.config[:abort] = true
+ @script.signals
+ end
+
+ it "does not trap the INT signal if config[:abort] is not true" do
+ Signal.should_not_receive(:trap).with("INT")
+ @script.config[:abort] = false
+ @script.signals
+ end
+end
+
+describe MSpecScript, "#entries" do
+ before :each do
+ @script = MSpecScript.new
+
+ File.stub(:expand_path).and_return("name")
+ File.stub(:file?).and_return(false)
+ File.stub(:directory?).and_return(false)
+ end
+
+ it "returns the pattern in an array if it is a file" do
+ File.should_receive(:expand_path).with("file").and_return("file/expanded")
+ File.should_receive(:file?).with("file/expanded").and_return(true)
+ @script.entries("file").should == ["file/expanded"]
+ end
+
+ it "returns Dir['pattern/**/*_spec.rb'] if pattern is a directory" do
+ File.should_receive(:directory?).with("name").and_return(true)
+ File.stub(:expand_path).and_return("name","name/**/*_spec.rb")
+ Dir.should_receive(:[]).with("name/**/*_spec.rb").and_return(["dir1", "dir2"])
+ @script.entries("name").should == ["dir1", "dir2"]
+ end
+
+ it "aborts if pattern cannot be resolved to a file nor a directory" do
+ @script.should_receive(:abort)
+ @script.entries("pattern")
+ end
+
+ describe "with config[:prefix] set" do
+ before :each do
+ prefix = "prefix/dir"
+ @script.config[:prefix] = prefix
+ @name = prefix + "/name"
+ end
+
+ it "returns the pattern in an array if it is a file" do
+ File.should_receive(:expand_path).with(@name).and_return(@name)
+ File.should_receive(:file?).with(@name).and_return(true)
+ @script.entries("name").should == [@name]
+ end
+
+ it "returns Dir['pattern/**/*_spec.rb'] if pattern is a directory" do
+ File.stub(:expand_path).and_return(@name, @name+"/**/*_spec.rb")
+ File.should_receive(:directory?).with(@name).and_return(true)
+ Dir.should_receive(:[]).with(@name + "/**/*_spec.rb").and_return(["dir1", "dir2"])
+ @script.entries("name").should == ["dir1", "dir2"]
+ end
+
+ it "aborts if pattern cannot be resolved to a file nor a directory" do
+ @script.should_receive(:abort)
+ @script.entries("pattern")
+ end
+ end
+end
+
+describe MSpecScript, "#files" do
+ before :each do
+ @script = MSpecScript.new
+ end
+
+ it "accumlates the values returned by #entries" do
+ @script.should_receive(:entries).and_return(["file1"], ["file2"])
+ @script.files(["a", "b"]).should == ["file1", "file2"]
+ end
+
+ it "strips a leading '^' and removes the values returned by #entries" do
+ @script.should_receive(:entries).and_return(["file1"], ["file2"], ["file1"])
+ @script.files(["a", "b", "^a"]).should == ["file2"]
+ end
+
+ it "processes the array elements in order" do
+ @script.should_receive(:entries).and_return(["file1"], ["file1"], ["file2"])
+ @script.files(["^a", "a", "b"]).should == ["file1", "file2"]
+ end
+end
+
+describe MSpecScript, "#files" do
+ before :each do
+ MSpecScript.set :files, ["file1", "file2"]
+
+ @script = MSpecScript.new
+ end
+
+ after :each do
+ MSpecScript.config.delete :files
+ end
+
+ it "looks up items with leading ':' in the config object" do
+ @script.should_receive(:entries).and_return(["file1"], ["file2"])
+ @script.files([":files"]).should == ["file1", "file2"]
+ end
+
+ it "returns an empty list if the config key is not set" do
+ @script.files([":all_files"]).should == []
+ end
+end
+
+describe MSpecScript, "#setup_env" do
+ before :each do
+ @script = MSpecScript.new
+ @options, @config = new_option
+ @script.stub(:config).and_return(@config)
+ end
+
+ after :each do
+ end
+
+ it "sets MSPEC_RUNNER = '1' in the environment" do
+ ENV["MSPEC_RUNNER"] = "0"
+ @script.setup_env
+ ENV["MSPEC_RUNNER"].should == "1"
+ end
+
+ it "sets RUBY_EXE = config[:target] in the environment" do
+ ENV["RUBY_EXE"] = nil
+ @script.setup_env
+ ENV["RUBY_EXE"].should == @config[:target]
+ end
+
+ it "sets RUBY_FLAGS = config[:flags] in the environment" do
+ ENV["RUBY_FLAGS"] = nil
+ @config[:flags] = ["-w", "-Q"]
+ @script.setup_env
+ ENV["RUBY_FLAGS"].should == "-w -Q"
+ end
+end
diff --git a/spec/mspec/spec/utils/version_spec.rb b/spec/mspec/spec/utils/version_spec.rb
new file mode 100644
index 0000000000..0b2d383c6d
--- /dev/null
+++ b/spec/mspec/spec/utils/version_spec.rb
@@ -0,0 +1,45 @@
+require 'spec_helper'
+require 'mspec/utils/version'
+
+describe SpecVersion, "#to_s" do
+ it "returns the string with which it was initialized" do
+ SpecVersion.new("1.8").to_s.should == "1.8"
+ SpecVersion.new("2.118.9").to_s.should == "2.118.9"
+ end
+end
+
+describe SpecVersion, "#to_str" do
+ it "returns the same string as #to_s" do
+ version = SpecVersion.new("2.118.9")
+ version.to_str.should == version.to_s
+ end
+end
+
+describe SpecVersion, "#to_i with ceil = false" do
+ it "returns an integer representation of the version string" do
+ SpecVersion.new("2.23.10").to_i.should == 1022310
+ end
+
+ it "replaces missing version parts with zeros" do
+ SpecVersion.new("1.8").to_i.should == 1010800
+ SpecVersion.new("1.8.6").to_i.should == 1010806
+ end
+end
+
+describe SpecVersion, "#to_i with ceil = true" do
+ it "returns an integer representation of the version string" do
+ SpecVersion.new("1.8.6", true).to_i.should == 1010806
+ end
+
+ it "fills in 9s for missing tiny values" do
+ SpecVersion.new("1.8", true).to_i.should == 1010899
+ SpecVersion.new("1.8.6", true).to_i.should == 1010806
+ end
+end
+
+describe SpecVersion, "#to_int" do
+ it "returns the same value as #to_i" do
+ version = SpecVersion.new("4.16.87")
+ version.to_int.should == version.to_i
+ end
+end