From 95e8c48dd3348503a8c7db5d0498894a1b676395 Mon Sep 17 00:00:00 2001 From: eregon Date: Sun, 7 May 2017 12:04:49 +0000 Subject: 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 --- spec/mspec/spec/commands/fixtures/four.txt | 0 .../spec/commands/fixtures/level2/three_spec.rb | 0 spec/mspec/spec/commands/fixtures/one_spec.rb | 0 spec/mspec/spec/commands/fixtures/three.rb | 0 spec/mspec/spec/commands/fixtures/two_spec.rb | 0 spec/mspec/spec/commands/mkspec_spec.rb | 363 ++++++ spec/mspec/spec/commands/mspec_ci_spec.rb | 155 +++ spec/mspec/spec/commands/mspec_run_spec.rb | 185 +++ spec/mspec/spec/commands/mspec_spec.rb | 215 ++++ spec/mspec/spec/commands/mspec_tag_spec.rb | 419 +++++++ spec/mspec/spec/expectations/expectations_spec.rb | 29 + spec/mspec/spec/expectations/should.rb | 72 ++ spec/mspec/spec/expectations/should_spec.rb | 61 + spec/mspec/spec/fixtures/a_spec.rb | 15 + spec/mspec/spec/fixtures/b_spec.rb | 7 + spec/mspec/spec/fixtures/config.mspec | 10 + spec/mspec/spec/fixtures/my_ruby | 4 + spec/mspec/spec/fixtures/print_interpreter_spec.rb | 4 + spec/mspec/spec/fixtures/tagging_spec.rb | 16 + spec/mspec/spec/guards/block_device_spec.rb | 46 + spec/mspec/spec/guards/bug_spec.rb | 151 +++ spec/mspec/spec/guards/conflict_spec.rb | 51 + spec/mspec/spec/guards/endian_spec.rb | 55 + spec/mspec/spec/guards/feature_spec.rb | 80 ++ spec/mspec/spec/guards/guard_spec.rb | 180 +++ spec/mspec/spec/guards/platform_spec.rb | 331 +++++ spec/mspec/spec/guards/quarantine_spec.rb | 35 + spec/mspec/spec/guards/superuser_spec.rb | 35 + spec/mspec/spec/guards/support_spec.rb | 69 ++ spec/mspec/spec/guards/user_spec.rb | 20 + spec/mspec/spec/guards/version_spec.rb | 83 ++ spec/mspec/spec/helpers/argf_spec.rb | 37 + spec/mspec/spec/helpers/argv_spec.rb | 27 + spec/mspec/spec/helpers/datetime_spec.rb | 44 + spec/mspec/spec/helpers/fixture_spec.rb | 25 + spec/mspec/spec/helpers/flunk_spec.rb | 20 + spec/mspec/spec/helpers/fs_spec.rb | 182 +++ spec/mspec/spec/helpers/io_spec.rb | 174 +++ spec/mspec/spec/helpers/mock_to_path_spec.rb | 17 + spec/mspec/spec/helpers/numeric_spec.rb | 25 + spec/mspec/spec/helpers/ruby_exe_spec.rb | 220 ++++ spec/mspec/spec/helpers/scratch_spec.rb | 24 + spec/mspec/spec/helpers/tmp_spec.rb | 27 + spec/mspec/spec/integration/interpreter_spec.rb | 18 + spec/mspec/spec/integration/run_spec.rb | 52 + spec/mspec/spec/integration/tag_spec.rb | 63 + spec/mspec/spec/matchers/base_spec.rb | 225 ++++ spec/mspec/spec/matchers/be_an_instance_of_spec.rb | 50 + spec/mspec/spec/matchers/be_ancestor_of_spec.rb | 28 + spec/mspec/spec/matchers/be_close_spec.rb | 46 + spec/mspec/spec/matchers/be_computed_by_spec.rb | 42 + spec/mspec/spec/matchers/be_empty_spec.rb | 26 + spec/mspec/spec/matchers/be_false_spec.rb | 28 + spec/mspec/spec/matchers/be_kind_of_spec.rb | 31 + spec/mspec/spec/matchers/be_nan_spec.rb | 28 + spec/mspec/spec/matchers/be_nil_spec.rb | 27 + spec/mspec/spec/matchers/be_true_or_false_spec.rb | 19 + spec/mspec/spec/matchers/be_true_spec.rb | 28 + spec/mspec/spec/matchers/block_caller_spec.rb | 13 + spec/mspec/spec/matchers/complain_spec.rb | 52 + spec/mspec/spec/matchers/eql_spec.rb | 33 + spec/mspec/spec/matchers/equal_element_spec.rb | 75 ++ spec/mspec/spec/matchers/equal_spec.rb | 32 + .../spec/matchers/have_class_variable_spec.rb | 62 + spec/mspec/spec/matchers/have_constant_spec.rb | 37 + .../spec/matchers/have_instance_method_spec.rb | 53 + .../spec/matchers/have_instance_variable_spec.rb | 61 + spec/mspec/spec/matchers/have_method_spec.rb | 55 + .../matchers/have_private_instance_method_spec.rb | 57 + .../spec/matchers/have_private_method_spec.rb | 44 + .../have_protected_instance_method_spec.rb | 57 + .../matchers/have_public_instance_method_spec.rb | 53 + .../spec/matchers/have_singleton_method_spec.rb | 45 + spec/mspec/spec/matchers/include_spec.rb | 37 + spec/mspec/spec/matchers/infinity_spec.rb | 34 + spec/mspec/spec/matchers/match_yaml_spec.rb | 39 + spec/mspec/spec/matchers/output_spec.rb | 74 ++ spec/mspec/spec/matchers/output_to_fd_spec.rb | 42 + spec/mspec/spec/matchers/raise_error_spec.rb | 108 ++ spec/mspec/spec/matchers/respond_to_spec.rb | 33 + spec/mspec/spec/matchers/signed_zero_spec.rb | 32 + spec/mspec/spec/mocks/mock_spec.rb | 469 +++++++ spec/mspec/spec/mocks/proxy_spec.rb | 405 ++++++ spec/mspec/spec/runner/actions/filter_spec.rb | 84 ++ spec/mspec/spec/runner/actions/tag_spec.rb | 315 +++++ spec/mspec/spec/runner/actions/taglist_spec.rb | 152 +++ spec/mspec/spec/runner/actions/tagpurge_spec.rb | 154 +++ spec/mspec/spec/runner/actions/tally_spec.rb | 352 ++++++ spec/mspec/spec/runner/actions/timer_spec.rb | 44 + spec/mspec/spec/runner/context_spec.rb | 1041 ++++++++++++++++ spec/mspec/spec/runner/example_spec.rb | 117 ++ spec/mspec/spec/runner/exception_spec.rb | 146 +++ spec/mspec/spec/runner/filters/a.yaml | 4 + spec/mspec/spec/runner/filters/b.yaml | 11 + spec/mspec/spec/runner/filters/match_spec.rb | 34 + spec/mspec/spec/runner/filters/profile_spec.rb | 117 ++ spec/mspec/spec/runner/filters/regexp_spec.rb | 13 + spec/mspec/spec/runner/filters/tag_spec.rb | 92 ++ spec/mspec/spec/runner/formatters/describe_spec.rb | 67 + spec/mspec/spec/runner/formatters/dotted_spec.rb | 285 +++++ spec/mspec/spec/runner/formatters/file_spec.rb | 84 ++ spec/mspec/spec/runner/formatters/html_spec.rb | 217 ++++ spec/mspec/spec/runner/formatters/junit_spec.rb | 147 +++ spec/mspec/spec/runner/formatters/method_spec.rb | 178 +++ spec/mspec/spec/runner/formatters/multi_spec.rb | 68 + spec/mspec/spec/runner/formatters/specdoc_spec.rb | 106 ++ spec/mspec/spec/runner/formatters/spinner_spec.rb | 83 ++ spec/mspec/spec/runner/formatters/summary_spec.rb | 26 + spec/mspec/spec/runner/formatters/unit_spec.rb | 74 ++ spec/mspec/spec/runner/formatters/yaml_spec.rb | 125 ++ spec/mspec/spec/runner/mspec_spec.rb | 595 +++++++++ spec/mspec/spec/runner/shared_spec.rb | 88 ++ spec/mspec/spec/runner/tag_spec.rb | 123 ++ spec/mspec/spec/runner/tags.txt | 4 + spec/mspec/spec/spec_helper.rb | 54 + spec/mspec/spec/utils/deprecate_spec.rb | 17 + spec/mspec/spec/utils/name_map_spec.rb | 175 +++ spec/mspec/spec/utils/options_spec.rb | 1309 ++++++++++++++++++++ spec/mspec/spec/utils/script_spec.rb | 473 +++++++ spec/mspec/spec/utils/version_spec.rb | 45 + 120 files changed, 13245 insertions(+) create mode 100644 spec/mspec/spec/commands/fixtures/four.txt create mode 100644 spec/mspec/spec/commands/fixtures/level2/three_spec.rb create mode 100644 spec/mspec/spec/commands/fixtures/one_spec.rb create mode 100644 spec/mspec/spec/commands/fixtures/three.rb create mode 100644 spec/mspec/spec/commands/fixtures/two_spec.rb create mode 100644 spec/mspec/spec/commands/mkspec_spec.rb create mode 100644 spec/mspec/spec/commands/mspec_ci_spec.rb create mode 100644 spec/mspec/spec/commands/mspec_run_spec.rb create mode 100644 spec/mspec/spec/commands/mspec_spec.rb create mode 100644 spec/mspec/spec/commands/mspec_tag_spec.rb create mode 100644 spec/mspec/spec/expectations/expectations_spec.rb create mode 100644 spec/mspec/spec/expectations/should.rb create mode 100644 spec/mspec/spec/expectations/should_spec.rb create mode 100644 spec/mspec/spec/fixtures/a_spec.rb create mode 100644 spec/mspec/spec/fixtures/b_spec.rb create mode 100644 spec/mspec/spec/fixtures/config.mspec create mode 100755 spec/mspec/spec/fixtures/my_ruby create mode 100644 spec/mspec/spec/fixtures/print_interpreter_spec.rb create mode 100644 spec/mspec/spec/fixtures/tagging_spec.rb create mode 100644 spec/mspec/spec/guards/block_device_spec.rb create mode 100644 spec/mspec/spec/guards/bug_spec.rb create mode 100644 spec/mspec/spec/guards/conflict_spec.rb create mode 100644 spec/mspec/spec/guards/endian_spec.rb create mode 100644 spec/mspec/spec/guards/feature_spec.rb create mode 100644 spec/mspec/spec/guards/guard_spec.rb create mode 100644 spec/mspec/spec/guards/platform_spec.rb create mode 100644 spec/mspec/spec/guards/quarantine_spec.rb create mode 100644 spec/mspec/spec/guards/superuser_spec.rb create mode 100644 spec/mspec/spec/guards/support_spec.rb create mode 100644 spec/mspec/spec/guards/user_spec.rb create mode 100644 spec/mspec/spec/guards/version_spec.rb create mode 100644 spec/mspec/spec/helpers/argf_spec.rb create mode 100644 spec/mspec/spec/helpers/argv_spec.rb create mode 100644 spec/mspec/spec/helpers/datetime_spec.rb create mode 100644 spec/mspec/spec/helpers/fixture_spec.rb create mode 100644 spec/mspec/spec/helpers/flunk_spec.rb create mode 100644 spec/mspec/spec/helpers/fs_spec.rb create mode 100644 spec/mspec/spec/helpers/io_spec.rb create mode 100644 spec/mspec/spec/helpers/mock_to_path_spec.rb create mode 100644 spec/mspec/spec/helpers/numeric_spec.rb create mode 100644 spec/mspec/spec/helpers/ruby_exe_spec.rb create mode 100644 spec/mspec/spec/helpers/scratch_spec.rb create mode 100644 spec/mspec/spec/helpers/tmp_spec.rb create mode 100644 spec/mspec/spec/integration/interpreter_spec.rb create mode 100644 spec/mspec/spec/integration/run_spec.rb create mode 100644 spec/mspec/spec/integration/tag_spec.rb create mode 100644 spec/mspec/spec/matchers/base_spec.rb create mode 100644 spec/mspec/spec/matchers/be_an_instance_of_spec.rb create mode 100644 spec/mspec/spec/matchers/be_ancestor_of_spec.rb create mode 100644 spec/mspec/spec/matchers/be_close_spec.rb create mode 100644 spec/mspec/spec/matchers/be_computed_by_spec.rb create mode 100644 spec/mspec/spec/matchers/be_empty_spec.rb create mode 100644 spec/mspec/spec/matchers/be_false_spec.rb create mode 100644 spec/mspec/spec/matchers/be_kind_of_spec.rb create mode 100644 spec/mspec/spec/matchers/be_nan_spec.rb create mode 100644 spec/mspec/spec/matchers/be_nil_spec.rb create mode 100644 spec/mspec/spec/matchers/be_true_or_false_spec.rb create mode 100644 spec/mspec/spec/matchers/be_true_spec.rb create mode 100644 spec/mspec/spec/matchers/block_caller_spec.rb create mode 100644 spec/mspec/spec/matchers/complain_spec.rb create mode 100644 spec/mspec/spec/matchers/eql_spec.rb create mode 100644 spec/mspec/spec/matchers/equal_element_spec.rb create mode 100644 spec/mspec/spec/matchers/equal_spec.rb create mode 100644 spec/mspec/spec/matchers/have_class_variable_spec.rb create mode 100644 spec/mspec/spec/matchers/have_constant_spec.rb create mode 100644 spec/mspec/spec/matchers/have_instance_method_spec.rb create mode 100644 spec/mspec/spec/matchers/have_instance_variable_spec.rb create mode 100644 spec/mspec/spec/matchers/have_method_spec.rb create mode 100644 spec/mspec/spec/matchers/have_private_instance_method_spec.rb create mode 100644 spec/mspec/spec/matchers/have_private_method_spec.rb create mode 100644 spec/mspec/spec/matchers/have_protected_instance_method_spec.rb create mode 100644 spec/mspec/spec/matchers/have_public_instance_method_spec.rb create mode 100644 spec/mspec/spec/matchers/have_singleton_method_spec.rb create mode 100644 spec/mspec/spec/matchers/include_spec.rb create mode 100644 spec/mspec/spec/matchers/infinity_spec.rb create mode 100644 spec/mspec/spec/matchers/match_yaml_spec.rb create mode 100644 spec/mspec/spec/matchers/output_spec.rb create mode 100644 spec/mspec/spec/matchers/output_to_fd_spec.rb create mode 100644 spec/mspec/spec/matchers/raise_error_spec.rb create mode 100644 spec/mspec/spec/matchers/respond_to_spec.rb create mode 100644 spec/mspec/spec/matchers/signed_zero_spec.rb create mode 100644 spec/mspec/spec/mocks/mock_spec.rb create mode 100644 spec/mspec/spec/mocks/proxy_spec.rb create mode 100644 spec/mspec/spec/runner/actions/filter_spec.rb create mode 100644 spec/mspec/spec/runner/actions/tag_spec.rb create mode 100644 spec/mspec/spec/runner/actions/taglist_spec.rb create mode 100644 spec/mspec/spec/runner/actions/tagpurge_spec.rb create mode 100644 spec/mspec/spec/runner/actions/tally_spec.rb create mode 100644 spec/mspec/spec/runner/actions/timer_spec.rb create mode 100644 spec/mspec/spec/runner/context_spec.rb create mode 100644 spec/mspec/spec/runner/example_spec.rb create mode 100644 spec/mspec/spec/runner/exception_spec.rb create mode 100644 spec/mspec/spec/runner/filters/a.yaml create mode 100644 spec/mspec/spec/runner/filters/b.yaml create mode 100644 spec/mspec/spec/runner/filters/match_spec.rb create mode 100644 spec/mspec/spec/runner/filters/profile_spec.rb create mode 100644 spec/mspec/spec/runner/filters/regexp_spec.rb create mode 100644 spec/mspec/spec/runner/filters/tag_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/describe_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/dotted_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/file_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/html_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/junit_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/method_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/multi_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/specdoc_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/spinner_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/summary_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/unit_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/yaml_spec.rb create mode 100644 spec/mspec/spec/runner/mspec_spec.rb create mode 100644 spec/mspec/spec/runner/shared_spec.rb create mode 100644 spec/mspec/spec/runner/tag_spec.rb create mode 100644 spec/mspec/spec/runner/tags.txt create mode 100644 spec/mspec/spec/spec_helper.rb create mode 100644 spec/mspec/spec/utils/deprecate_spec.rb create mode 100644 spec/mspec/spec/utils/name_map_spec.rb create mode 100644 spec/mspec/spec/utils/options_spec.rb create mode 100644 spec/mspec/spec/utils/script_spec.rb create mode 100644 spec/mspec/spec/utils/version_spec.rb (limited to 'spec/mspec/spec') 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 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 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 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 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 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 == < ["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 = <' +CWD/spec/fixtures/a_spec.rb:2:in `' +CWD/bin/mspec-run:7:in `
' + +2) +Foo#bar fails ERROR +RuntimeError: failure +CWD/spec/fixtures/a_spec.rb:12:in `block (2 levels) in ' +CWD/spec/fixtures/a_spec.rb:2:in `' +CWD/bin/mspec-run:7:in `
' + +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 == <' +CWD/spec/fixtures/tagging_spec.rb:3:in `' +CWD/bin/mspec-tag:7:in `
' + +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 ' +CWD/spec/fixtures/tagging_spec.rb:3:in `' +CWD/bin/mspec-tag:7:in `
' + +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 == < 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?('').should be_true + EqualElementMatcher.new("A").matches?('').should be_true + EqualElementMatcher.new("A").matches?('').should be_true + + EqualElementMatcher.new("BASE").matches?('').should be_false + EqualElementMatcher.new("BASE").matches?('').should be_false + EqualElementMatcher.new("BASE").matches?('').should be_false + EqualElementMatcher.new("BASE").matches?('').should be_false + EqualElementMatcher.new("BASE").matches?('').should be_false + end + + it "matches if it finds an element with the passed name and the passed attributes" do + EqualElementMatcher.new("A", {}).matches?('').should be_true + EqualElementMatcher.new("A", nil).matches?('').should be_true + EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('').should be_true + + EqualElementMatcher.new("A", {}).matches?('').should be_false + EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('').should be_false + EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('').should be_false + EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('').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?('').should be_true + EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('Example').should be_true + + EqualElementMatcher.new("A", {}, "Test").matches?('').should be_false + EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('').should be_false + EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('Test').should be_false + end + + it "can match unclosed elements" do + EqualElementMatcher.new("BASE", nil, nil, :not_closed => true).matches?('').should be_true + EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, nil, :not_closed => true).matches?('').should be_true + EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "Example", :not_closed => true).matches?('Example').should be_true + + EqualElementMatcher.new("BASE", {}, nil, :not_closed => true).matches?('').should be_false + EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "", :not_closed => true).matches?('Example').should be_false + EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "Test", :not_closed => true).matches?('Example').should be_false + end + + it "provides a useful failure message" do + equal_element = EqualElementMatcher.new("A", {}, "Test") + equal_element.matches?('').should be_false + equal_element.failure_message.should == [%{Expected ""\n}, %{to be a 'A' element with no attributes and "Test" as content}] + + equal_element = EqualElementMatcher.new("A", {}, "") + equal_element.matches?('Test').should be_false + equal_element.failure_message.should == [%{Expected "Test"\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?('Test').should be_false + equal_element.failure_message.should == [%{Expected "Test"\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?('').should be_false + equal_element.negative_failure_message.should == [%{Expected ""\n}, %{not to be a 'A' element with no attributes and "Test" as content}] + + equal_element = EqualElementMatcher.new("A", {}, "") + equal_element.matches?('Test').should be_false + equal_element.negative_failure_message.should == [%{Expected "Test"\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?('Test').should be_false + equal_element.negative_failure_message.should == [%{Expected "Test"\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 if the exception message is empty" do + exc = ExceptionState.new @state, "", Exception.new("") + exc.message.should == "" + 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 == +%[ + + +Spec Output For #{ruby_name} (#{RUBY_VERSION}) + + + +] + 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 == "

describe

\n
    \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 == "
\n
\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 == +%[
  • - it (FAILED - 1)
  • +
  • - it (ERROR - 2)
  • +] + 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 == %[
  • - it
  • \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 "

    describe it ERROR

    " + 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[
    .*path/to/some/file.rb:35:in method.*
    ]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 "

    Finished in 2.0 seconds

    \n" + end + + it "prints a tally of counts" do + @tally.should_receive(:format).and_return("1 example, 0 failures") + @formatter.finish + @out.should include '

    1 example, 0 failures

    ' + 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 == +%[
  • - it (ERROR - 1)
  • +
    +
      +
    1. describe it ERROR

      +

      MSpecExampleError: broken

      +
      +path/to/some/file.rb:35:in method
      +
    2. +
    +

    Finished in 2.0 seconds

    +

    1 example, 1 failures

    + + +] + 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 -- cgit v1.2.3