From 60036a8901f9ee32a9325ddde922d1c5f2d656fa Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 4 Sep 2014 19:28:32 -0700 Subject: [PATCH 001/275] Bump version to 3.2.0.pre. --- lib/rspec/core/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/core/version.rb b/lib/rspec/core/version.rb index 30d470a5c9..f0cbac101f 100644 --- a/lib/rspec/core/version.rb +++ b/lib/rspec/core/version.rb @@ -3,7 +3,7 @@ module Core # Version information for RSpec Core. module Version # Current version of RSpec Core, in semantic versioning format. - STRING = '3.1.0' + STRING = '3.2.0.pre' end end end From a2379d04f5a962ec1cf4cb807158780e070464bb Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 5 Sep 2014 09:09:11 -0700 Subject: [PATCH 002/275] Fix another regression in rake task pattern handling. This was reported by @aprescott: https://github.com/rspec/rspec-core/pull/1671#issuecomment-54623310. The regression was originally introduced in #1653. See also #1691 and #1692. --- Changelog.md | 10 +++++ lib/rspec/core/rake_task.rb | 24 ++++++++---- spec/rspec/core/rake_task_spec.rb | 64 ++++++++++++++++++++++++------- 3 files changed, 77 insertions(+), 21 deletions(-) diff --git a/Changelog.md b/Changelog.md index e64fd88cf6..53888cf15c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,13 @@ +### 3.1.1 Development +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.0...3-1-maintenance) + +Bug Fixes: + +* Fix a regression in rake task pattern handling, so that `rake_task.pattern = array` + works again. While we never intended to support array values (or even knew that worked!), + the implementation from 3.0 and earlier used `FileList` internally, which allows arrays. + The fix restores the old behavior. (Myron Marston, #1694) + ### 3.1.0 / 2014-09-04 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.0.4...v3.1.0) diff --git a/lib/rspec/core/rake_task.rb b/lib/rspec/core/rake_task.rb index 349a809071..c7e6697684 100644 --- a/lib/rspec/core/rake_task.rb +++ b/lib/rspec/core/rake_task.rb @@ -114,15 +114,25 @@ def define(args, &task_block) def file_inclusion_specification if ENV['SPEC'] FileList[ ENV['SPEC']].sort - elsif File.exist?(pattern) - # The provided pattern is a directory or a file, not a file glob. Historically, this - # worked because `FileList[some_dir]` would return `[some_dir]` which would - # get passed to `rspec` and cause it to load files under that dir that match - # the default pattern. To continue working, we need to pass it on to `rspec` - # directly rather than treating it as a `--pattern` option. + elsif Array === pattern || File.exist?(pattern) + # Before RSpec 3.1, we used `FileList` to get the list of matched files, and + # then pass that along to the `rspec` command. Starting with 3.1, we prefer to + # pass along the pattern as-is to the `rspec` command, for 3 reasons: + # + # * It's *much* less verbose to pass one `--pattern` option than a long list of files. + # * It ensures `task.pattern` and `--pattern` have the same behavior. + # * It fixes a bug, where `task.pattern = pattern_that_matches_no_files` would run + # *all* files because it would cause no pattern or file args to get passed to `rspec`, + # which causes all files to get run. + # + # However, `FileList` is *far* more flexible than the `--pattern` option. Specifically, it + # supports individual files and directories, as well as arrays of files, directories and globs. + # + # For backwards compatibility, we have to fall back to using FileList if the user has passed + # a `pattern` option that will not work with `--pattern`. # # TODO: consider deprecating support for this and removing it in RSpec 4. - pattern.shellescape + FileList[pattern].sort.map(&:shellescape) else "--pattern #{pattern.shellescape}" end diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index 96673da51f..17e7caaaeb 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -166,24 +166,60 @@ def self.it_configures_rspec_load_path(description, path_template) specify_consistent_ordering_of_files_to_run('a/*.rb', Dir) end - context "with a pattern that matches no files" do - it "runs nothing" do - task.pattern = 'a/*.no_match' - expect(loaded_files).to eq([]) + context "with a pattern value" do + context "that matches no files" do + it "runs nothing" do + task.pattern = 'a/*.no_match' + expect(loaded_files).to eq([]) + end end - end - context "with a pattern value that is an existing directory, not a file glob" do - it "loads the spec files in that directory" do - task.pattern = "./spec/rspec/core/resources/acceptance" - expect(loaded_files).to eq(["./spec/rspec/core/resources/acceptance/foo_spec.rb"]) + context "that is an existing directory, not a file glob" do + it "loads the spec files in that directory" do + task.pattern = "./spec/rspec/core/resources/acceptance" + expect(loaded_files).to eq(["./spec/rspec/core/resources/acceptance/foo_spec.rb"]) + end end - end - context "with a pattern value that is an existing file, not a file glob" do - it "loads the spec file" do - task.pattern = "./spec/rspec/core/resources/acceptance/foo_spec.rb" - expect(loaded_files).to eq(["./spec/rspec/core/resources/acceptance/foo_spec.rb"]) + context "that is an existing file, not a file glob" do + it "loads the spec file" do + task.pattern = "./spec/rspec/core/resources/acceptance/foo_spec.rb" + expect(loaded_files).to eq(["./spec/rspec/core/resources/acceptance/foo_spec.rb"]) + end + end + + context "that is an array of existing files or directories, not a file glob" do + it "loads the specified spec files, and spec files from the specified directories" do + task.pattern = ["./spec/rspec/core/resources/acceptance", "./spec/rspec/core/resources/a_bar.rb"] + + expect(loaded_files).to contain_exactly( + "./spec/rspec/core/resources/acceptance/foo_spec.rb", + "./spec/rspec/core/resources/a_bar.rb" + ) + end + end + + context "that is an array of globs relative to the current working dir" do + it "loads spec files that match any of the globs" do + task.pattern = ["./spec/rspec/core/resources/acceptance/*_spec.rb", + "./spec/rspec/core/resources/*_bar.rb"] + + expect(loaded_files).to contain_exactly( + "./spec/rspec/core/resources/acceptance/foo_spec.rb", + "./spec/rspec/core/resources/a_bar.rb" + ) + end + end + + context "that is a mixture of file globs and individual files or dirs" do + it "loads all specified or matching files" do + task.pattern = ["./spec/rspec/core/resources/acceptance/*_spec.rb", "./spec/rspec/core/resources/a_bar.rb"] + + expect(loaded_files).to contain_exactly( + "./spec/rspec/core/resources/acceptance/foo_spec.rb", + "./spec/rspec/core/resources/a_bar.rb" + ) + end end end From ec0c5695788e38533c2f39448be4b8db20d4256b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 5 Sep 2014 12:33:33 -0700 Subject: [PATCH 003/275] 3.1.1 has been released. --- Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 53888cf15c..90e69ff40f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### 3.1.1 Development -[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.0...3-1-maintenance) +### 3.1.1 / 2014-09-05 +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.0...v3.1.1) Bug Fixes: From 436a209cc635a7c8e9cbfdf96ad209e8ab614606 Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Mon, 8 Sep 2014 09:13:27 -0700 Subject: [PATCH 004/275] Single glob patterns that start with ./ don't match any files. --- lib/rspec/core/configuration.rb | 2 +- spec/rspec/core/configuration_spec.rb | 6 ++++++ spec/rspec/core/rake_task_spec.rb | 8 ++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index 6704fd66b5..20f7ce5109 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1337,7 +1337,7 @@ def gather_directories(path) def get_matching_files(path, pattern) stripped = "{#{pattern.gsub(/\s*,\s*/, ',')}}" - pattern =~ /^#{Regexp.escape path}/ ? Dir[stripped] : Dir["#{path}/#{stripped}"] + pattern =~ /^(\.\/)?#{Regexp.escape path}/ ? Dir[stripped] : Dir["#{path}/#{stripped}"] end def extract_location(path) diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index f73c18e45c..051a92b3a5 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -441,6 +441,12 @@ def stub_expectation_adapters expect(config.files_to_run).to eq(["spec/rspec/core/resources/a_spec.rb"]) end + it "supports patterns starting with ./" do + config.pattern = "./spec/**/a_spec.rb" + assign_files_or_directories_to_run "spec" + expect(config.files_to_run).to eq(["./spec/rspec/core/resources/a_spec.rb"]) + end + it 'reloads when `files_or_directories_to_run` is reassigned' do config.pattern = "spec/**/a_spec.rb" config.files_or_directories_to_run = "empty_dir" diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index 17e7caaaeb..005f49fe2e 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -199,6 +199,14 @@ def self.it_configures_rspec_load_path(description, path_template) end end + # https://github.com/rspec/rspec-core/issues/1695 + context "that is a single glob that starts with ./" do + it "loads the spec files that match the glob" do + task.pattern = "./spec/rspec/core/resources/acceptance/**/*_spec.rb" + expect(loaded_files).to eq(["./spec/rspec/core/resources/acceptance/foo_spec.rb"]) + end + end + context "that is an array of globs relative to the current working dir" do it "loads spec files that match any of the globs" do task.pattern = ["./spec/rspec/core/resources/acceptance/*_spec.rb", From fe5ca27362c3e995b393553e2888d09c0ef3b3d0 Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Mon, 8 Sep 2014 09:14:31 -0700 Subject: [PATCH 005/275] Tweak for consistent whitespace. --- spec/rspec/core/configuration_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index 051a92b3a5..639106a514 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -426,7 +426,7 @@ def stub_expectation_adapters describe "#files_to_run" do it "loads files not following pattern if named explicitly" do assign_files_or_directories_to_run "spec/rspec/core/resources/a_bar.rb" - expect(config.files_to_run).to eq([ "spec/rspec/core/resources/a_bar.rb"]) + expect(config.files_to_run).to eq(["spec/rspec/core/resources/a_bar.rb"]) end it "prevents repetition of dir when start of the pattern" do From 6128281a2343ece0c080339428f6e50658766c45 Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Mon, 8 Sep 2014 09:14:54 -0700 Subject: [PATCH 006/275] Tweak for consistent handling of multi-element array formatting. --- spec/rspec/core/rake_task_spec.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index 005f49fe2e..3dc852dfc0 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -190,7 +190,8 @@ def self.it_configures_rspec_load_path(description, path_template) context "that is an array of existing files or directories, not a file glob" do it "loads the specified spec files, and spec files from the specified directories" do - task.pattern = ["./spec/rspec/core/resources/acceptance", "./spec/rspec/core/resources/a_bar.rb"] + task.pattern = ["./spec/rspec/core/resources/acceptance", + "./spec/rspec/core/resources/a_bar.rb"] expect(loaded_files).to contain_exactly( "./spec/rspec/core/resources/acceptance/foo_spec.rb", @@ -221,7 +222,8 @@ def self.it_configures_rspec_load_path(description, path_template) context "that is a mixture of file globs and individual files or dirs" do it "loads all specified or matching files" do - task.pattern = ["./spec/rspec/core/resources/acceptance/*_spec.rb", "./spec/rspec/core/resources/a_bar.rb"] + task.pattern = ["./spec/rspec/core/resources/acceptance/*_spec.rb", + "./spec/rspec/core/resources/a_bar.rb"] expect(loaded_files).to contain_exactly( "./spec/rspec/core/resources/acceptance/foo_spec.rb", From 70ca4e48c9b18b801faa2a748e1ec66c3d8b2e3d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 8 Sep 2014 14:44:12 -0700 Subject: [PATCH 007/275] Forward port 3.1.2 release notes. [ci skip] --- Changelog.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Changelog.md b/Changelog.md index 90e69ff40f..6322983fbd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,11 @@ +### 3.1.2 / 2014-09-08 +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.1...v3.1.2) + +Bug Fixes: + +* Fix another regression in rake task pattern handling, so that patterns + that start with `./` still work. (Christian Nelson, #1696) + ### 3.1.1 / 2014-09-05 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.0...v3.1.1) From ffd7492569a4a6688be512befc2007dc13b3685a Mon Sep 17 00:00:00 2001 From: Mike Dalton Date: Fri, 29 Aug 2014 10:17:21 -0400 Subject: [PATCH 008/275] Add better `inspect` output for ExampleGroup Changing from defining a singleton method to storing the inspect output on the ExampleGroup Add documentation for instance_variables and inspect Handling string element in instance_variables for ruby 1.8 Removing un-needed expct calls in spec Handling ExampleGroup without inspect output, handling ExampleGroup without a description, refactoring logic to ignore `@__inspect_output` when determining before context ivars Improve readability of no inspect output spec and does not copy inspect output from before context to examples spec --- lib/rspec/core/example.rb | 11 +++- lib/rspec/core/example_group.rb | 33 ++++++++-- spec/rspec/core/example_group_spec.rb | 92 +++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 7 deletions(-) diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 0304da46f8..8de2abdafa 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -82,6 +82,15 @@ def description RSpec.configuration.format_docstrings_block.call(description) end + # Returns a description of the example that always includes the location. + def inspect_output + inspect_output = "\"#{description}\"" + unless metadata[:description].to_s.empty? + inspect_output << " (#{location})" + end + inspect_output + end + # @attr_reader # # Returns the first exception raised in the context of running this @@ -170,7 +179,7 @@ def run(example_group_instance, reporter) rescue Exception => e set_exception(e) ensure - @example_group_instance.instance_variables.each do |ivar| + ExampleGroup.instance_variables_for_example(@example_group_instance).each do |ivar| @example_group_instance.instance_variable_set(ivar, nil) end @example_group_instance = nil diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index f12fedfc58..dcab883c38 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -419,9 +419,7 @@ def self.before_context_ivars # @private def self.store_before_context_ivars(example_group_instance) - return if example_group_instance.instance_variables.empty? - - example_group_instance.instance_variables.each do |ivar| + instance_variables_for_example(example_group_instance).each do |ivar| before_context_ivars[ivar] = example_group_instance.instance_variable_get(ivar) end end @@ -459,7 +457,8 @@ def self.run(reporter) reporter.example_group_started(self) begin - run_before_context_hooks(new) + instance = new('before(:context) hook') + run_before_context_hooks(instance) result_for_this_group = run_examples(reporter) results_for_descendants = ordering_strategy.order(children).map { |child| child.run(reporter) }.all? result_for_this_group && results_for_descendants @@ -469,7 +468,8 @@ def self.run(reporter) RSpec.world.wants_to_quit = true if fail_fast? for_filtered_examples(reporter) { |example| example.fail_with_exception(reporter, ex) } ensure - run_after_context_hooks(new) + instance = new('after(:context) hook') + run_after_context_hooks(instance) before_context_ivars.clear reporter.example_group_finished(self) end @@ -495,7 +495,7 @@ def self.ordering_strategy def self.run_examples(reporter) ordering_strategy.order(filtered_examples).map do |example| next if RSpec.world.wants_to_quit - instance = new + instance = new(example.inspect_output) set_ivars(instance, before_context_ivars) succeeded = example.run(instance, reporter) RSpec.world.wants_to_quit = true if fail_fast? && !succeeded @@ -570,6 +570,27 @@ def self.pending_metadata_and_block_for(options, block) return options, callback end + + if RUBY_VERSION.to_f < 1.9 + # @private + def self.instance_variables_for_example(group) + group.instance_variables - ['@__inspect_output'] + end + else + # @private + def self.instance_variables_for_example(group) + group.instance_variables - [:@__inspect_output] + end + end + + def initialize(inspect_output=nil) + @__inspect_output = inspect_output || '(no description provided)' + end + + # @private + def inspect + "#<#{self.class} #{@__inspect_output}>" + end end # @private diff --git a/spec/rspec/core/example_group_spec.rb b/spec/rspec/core/example_group_spec.rb index 8b560499c1..81edc3a51e 100644 --- a/spec/rspec/core/example_group_spec.rb +++ b/spec/rspec/core/example_group_spec.rb @@ -1604,5 +1604,97 @@ def foo; end end }.to raise_error(/not allowed/) end + + describe 'inspect output' do + context 'when there is no inspect output provided' do + it "uses '(no description provided)' instead" do + expect(ExampleGroup.new.inspect).to eq('#') + end + end + + context 'when an example has a description' do + it 'includes description and location' do + an_example = nil + + line = __LINE__ + 2 + group = ExampleGroup.describe 'SomeClass1' do + example 'an example' do + an_example = self + end + end + + group.run + + path = RSpec::Core::Metadata.relative_path(__FILE__) + expect(an_example.inspect).to eq("#") + end + end + + context 'when an example does not have a description' do + it 'includes fallback description' do + an_example = nil + + line = __LINE__ + 2 + group = ExampleGroup.describe 'SomeClass2' do + example do + an_example = self + end + end + + group.run + + path = RSpec::Core::Metadata.relative_path(__FILE__) + expect(an_example.inspect).to eq("#") + end + end + + it 'handles before context hooks' do + a_before_hook = nil + + group = ExampleGroup.describe 'SomeClass3' do + before(:context) do + a_before_hook = self + end + + example {} + end + + group.run + expect(a_before_hook.inspect).to eq("#") + end + + it 'handles after context hooks' do + an_after_hook = nil + + group = ExampleGroup.describe 'SomeClass4' do + after(:context) do + an_after_hook = self + end + + example {} + end + + group.run + expect(an_after_hook.inspect).to eq("#") + end + + it "does not pollute an example's `inspect` output with the inspect ivar from `before(:context)`" do + inspect_output = nil + + line = __LINE__ + 2 + group = ExampleGroup.describe do + example do + inspect_output = inspect + end + + before(:context) {} + end + + group.run + + path = RSpec::Core::Metadata.relative_path(__FILE__) + expect(inspect_output).to end_with("\"example at #{path}:#{line}\">") + end + end end end From 117e67a19c2fbba436f656df4f0483fa93393314 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 12 Sep 2014 08:32:40 -0700 Subject: [PATCH 009/275] Changelog for #1687. [ci skip] --- Changelog.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Changelog.md b/Changelog.md index 6322983fbd..fdacb9c8b1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,10 @@ +### 3.2.0 Development +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.2...master) + +Enhancements: + +* Improve the `inspect` output of example groups. (Mike Dalton, #1687) + ### 3.1.2 / 2014-09-08 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.1...v3.1.2) From 6f881c041e57554380cffcd7f6d5823e0f63dfc8 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 9 Sep 2014 23:17:59 -0700 Subject: [PATCH 010/275] Fix another regression in rake task pattern handling. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently `FileList[file_list_object]` works, and some users used `task.pattern = FileList[…]`. See https://github.com/rspec/rspec-core/commit/667a4ca00f50fddb3fca7cdfcaf97633ba267b1f#commitcomment-7725799 --- lib/rspec/core/rake_task.rb | 9 +++++---- spec/rspec/core/rake_task_spec.rb | 11 +++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/rspec/core/rake_task.rb b/lib/rspec/core/rake_task.rb index c7e6697684..a9fc3fe241 100644 --- a/lib/rspec/core/rake_task.rb +++ b/lib/rspec/core/rake_task.rb @@ -114,7 +114,9 @@ def define(args, &task_block) def file_inclusion_specification if ENV['SPEC'] FileList[ ENV['SPEC']].sort - elsif Array === pattern || File.exist?(pattern) + elsif String === pattern && !File.exist?(pattern) + "--pattern #{pattern.shellescape}" + else # Before RSpec 3.1, we used `FileList` to get the list of matched files, and # then pass that along to the `rspec` command. Starting with 3.1, we prefer to # pass along the pattern as-is to the `rspec` command, for 3 reasons: @@ -126,15 +128,14 @@ def file_inclusion_specification # which causes all files to get run. # # However, `FileList` is *far* more flexible than the `--pattern` option. Specifically, it - # supports individual files and directories, as well as arrays of files, directories and globs. + # supports individual files and directories, as well as arrays of files, directories and globs, + # as well as other `FileList` objects. # # For backwards compatibility, we have to fall back to using FileList if the user has passed # a `pattern` option that will not work with `--pattern`. # # TODO: consider deprecating support for this and removing it in RSpec 4. FileList[pattern].sort.map(&:shellescape) - else - "--pattern #{pattern.shellescape}" end end diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index 3dc852dfc0..be326a65a0 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -231,6 +231,17 @@ def self.it_configures_rspec_load_path(description, path_template) ) end end + + context "that is a FileList" do + it "loads the files from the FileList" do + task.pattern = FileList["spec/rspec/core/resources/**/*_spec.rb"] + + expect(loaded_files).to contain_exactly( + "spec/rspec/core/resources/a_spec.rb", + "spec/rspec/core/resources/acceptance/foo_spec.rb" + ) + end + end end context "without an exclude_pattern" do From 01c942a56a74dc78fdc1d75a1e9d75b866fe4be1 Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Mon, 8 Sep 2014 16:39:39 -0700 Subject: [PATCH 011/275] Normalize to absolute paths so that patterns work as expected. This solves a problem where configuration treats dirname and ./dirname (and ./dirname and ././dirname, etc) as non-equivilant values, leading to unexpected bahvior when patterns are not consistently expressed. For example, given the pattern "spec/models/*_spec.rb" and an exclude_pattern "./spec/models/*", nothing would be excluded because of the spec ./spec mismatch. --- lib/rspec/core/configuration.rb | 3 ++- spec/rspec/core/configuration_spec.rb | 16 ++++++++-------- spec/rspec/core/rake_task_spec.rb | 24 ++++++++++++++++-------- spec/support/matchers.rb | 14 ++++++++++++++ spec/support/shared_example_groups.rb | 4 ++-- 5 files changed, 42 insertions(+), 19 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index 20f7ce5109..a9e76ff776 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1337,7 +1337,8 @@ def gather_directories(path) def get_matching_files(path, pattern) stripped = "{#{pattern.gsub(/\s*,\s*/, ',')}}" - pattern =~ /^(\.\/)?#{Regexp.escape path}/ ? Dir[stripped] : Dir["#{path}/#{stripped}"] + files = (pattern =~ /^(\.\/)?#{Regexp.escape path}/) ? Dir[stripped] : Dir["#{path}/#{stripped}"] + files.map { |file| File.expand_path(file) } end def extract_location(path) diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index 639106a514..41d58ddc8c 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -426,25 +426,25 @@ def stub_expectation_adapters describe "#files_to_run" do it "loads files not following pattern if named explicitly" do assign_files_or_directories_to_run "spec/rspec/core/resources/a_bar.rb" - expect(config.files_to_run).to eq(["spec/rspec/core/resources/a_bar.rb"]) + expect(config.files_to_run).to contain_files(["spec/rspec/core/resources/a_bar.rb"]) end it "prevents repetition of dir when start of the pattern" do config.pattern = "spec/**/a_spec.rb" assign_files_or_directories_to_run "spec" - expect(config.files_to_run).to eq(["spec/rspec/core/resources/a_spec.rb"]) + expect(config.files_to_run).to contain_files(["spec/rspec/core/resources/a_spec.rb"]) end it "does not prevent repetition of dir when later of the pattern" do config.pattern = "rspec/**/a_spec.rb" assign_files_or_directories_to_run "spec" - expect(config.files_to_run).to eq(["spec/rspec/core/resources/a_spec.rb"]) + expect(config.files_to_run).to contain_files(["spec/rspec/core/resources/a_spec.rb"]) end it "supports patterns starting with ./" do config.pattern = "./spec/**/a_spec.rb" assign_files_or_directories_to_run "spec" - expect(config.files_to_run).to eq(["./spec/rspec/core/resources/a_spec.rb"]) + expect(config.files_to_run).to contain_files(["./spec/rspec/core/resources/a_spec.rb"]) end it 'reloads when `files_or_directories_to_run` is reassigned' do @@ -454,7 +454,7 @@ def stub_expectation_adapters expect { config.files_or_directories_to_run = "spec" }.to change { config.files_to_run }. - to(["spec/rspec/core/resources/a_spec.rb"]) + to(a_file_collection("spec/rspec/core/resources/a_spec.rb")) end context "with :" do @@ -492,17 +492,17 @@ def stub_expectation_adapters context "with default pattern" do it "loads files named _spec.rb" do assign_files_or_directories_to_run "spec/rspec/core/resources" - expect(config.files_to_run).to contain_exactly("spec/rspec/core/resources/a_spec.rb", "spec/rspec/core/resources/acceptance/foo_spec.rb") + expect(config.files_to_run).to contain_files("spec/rspec/core/resources/a_spec.rb", "spec/rspec/core/resources/acceptance/foo_spec.rb") end it "loads files in Windows", :if => RSpec.world.windows_os? do assign_files_or_directories_to_run "C:\\path\\to\\project\\spec\\sub\\foo_spec.rb" - expect(config.files_to_run).to eq(["C:/path/to/project/spec/sub/foo_spec.rb"]) + expect(config.files_to_run).to contain_files(["C:/path/to/project/spec/sub/foo_spec.rb"]) end it "loads files in Windows when directory is specified", :if => RSpec.world.windows_os? do assign_files_or_directories_to_run "spec\\rspec\\core\\resources" - expect(config.files_to_run).to eq(["spec/rspec/core/resources/a_spec.rb"]) + expect(config.files_to_run).to contain_files(["spec/rspec/core/resources/a_spec.rb"]) end it_behaves_like "handling symlinked directories when loading spec files" do diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index be326a65a0..5b7613b22d 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -177,14 +177,14 @@ def self.it_configures_rspec_load_path(description, path_template) context "that is an existing directory, not a file glob" do it "loads the spec files in that directory" do task.pattern = "./spec/rspec/core/resources/acceptance" - expect(loaded_files).to eq(["./spec/rspec/core/resources/acceptance/foo_spec.rb"]) + expect(loaded_files).to contain_files(["./spec/rspec/core/resources/acceptance/foo_spec.rb"]) end end context "that is an existing file, not a file glob" do it "loads the spec file" do task.pattern = "./spec/rspec/core/resources/acceptance/foo_spec.rb" - expect(loaded_files).to eq(["./spec/rspec/core/resources/acceptance/foo_spec.rb"]) + expect(loaded_files).to contain_files(["./spec/rspec/core/resources/acceptance/foo_spec.rb"]) end end @@ -193,7 +193,7 @@ def self.it_configures_rspec_load_path(description, path_template) task.pattern = ["./spec/rspec/core/resources/acceptance", "./spec/rspec/core/resources/a_bar.rb"] - expect(loaded_files).to contain_exactly( + expect(loaded_files).to contain_files( "./spec/rspec/core/resources/acceptance/foo_spec.rb", "./spec/rspec/core/resources/a_bar.rb" ) @@ -204,7 +204,7 @@ def self.it_configures_rspec_load_path(description, path_template) context "that is a single glob that starts with ./" do it "loads the spec files that match the glob" do task.pattern = "./spec/rspec/core/resources/acceptance/**/*_spec.rb" - expect(loaded_files).to eq(["./spec/rspec/core/resources/acceptance/foo_spec.rb"]) + expect(loaded_files).to contain_files(["./spec/rspec/core/resources/acceptance/foo_spec.rb"]) end end @@ -213,7 +213,7 @@ def self.it_configures_rspec_load_path(description, path_template) task.pattern = ["./spec/rspec/core/resources/acceptance/*_spec.rb", "./spec/rspec/core/resources/*_bar.rb"] - expect(loaded_files).to contain_exactly( + expect(loaded_files).to contain_files( "./spec/rspec/core/resources/acceptance/foo_spec.rb", "./spec/rspec/core/resources/a_bar.rb" ) @@ -225,7 +225,7 @@ def self.it_configures_rspec_load_path(description, path_template) task.pattern = ["./spec/rspec/core/resources/acceptance/*_spec.rb", "./spec/rspec/core/resources/a_bar.rb"] - expect(loaded_files).to contain_exactly( + expect(loaded_files).to contain_files( "./spec/rspec/core/resources/acceptance/foo_spec.rb", "./spec/rspec/core/resources/a_bar.rb" ) @@ -282,7 +282,15 @@ def make_files_in_dir(dir) task.pattern = "spec/**/*_spec.rb" unit_files = make_files_in_dir "unit" - expect(loaded_files).to match_array(unit_files) + expect(loaded_files).to contain_files(unit_files) + end + + it "excludes files when pattern and exclusion_pattern don't consistently start with ./" do + task.exclude_pattern = "./spec/acceptance/*_spec.rb" + task.pattern = "spec/**/*_spec.rb" + unit_files = make_files_in_dir "unit" + + expect(loaded_files).to contain_files(unit_files) end end @@ -298,7 +306,7 @@ def make_files_in_dir(dir) File.join("spec", file_name).tap { |f| FileUtils.touch(f) } end - expect(loaded_files).to match_array(files) + expect(loaded_files).to contain_files(files) end end diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index 0596bca6ff..b194b7a7ae 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -87,3 +87,17 @@ def failure_reason(example) "expected: example skipped with #{message.inspect}\n got: #{example.execution_result.pending_message.inspect}" end end + +RSpec::Matchers.define :contain_files do |*expected_files| + contain_exactly_matcher = RSpec::Matchers::BuiltIn::ContainExactly.new(expected_files.flatten.map { |f| File.expand_path(f) }) + + match do |actual_files| + files = actual_files.map { |f| File.expand_path(f) } + contain_exactly_matcher.matches?(files) + end + + failure_message { contain_exactly_matcher.failure_message } + failure_message_when_negated { contain_exactly_matcher.failure_message_when_negated } +end + +RSpec::Matchers.alias_matcher :a_file_collection, :contain_files diff --git a/spec/support/shared_example_groups.rb b/spec/support/shared_example_groups.rb index 1a58242f58..5751babf60 100644 --- a/spec/support/shared_example_groups.rb +++ b/spec/support/shared_example_groups.rb @@ -26,7 +26,7 @@ FileUtils.ln_s bars_dir, File.join(project_dir, "spec/bars") - expect(loaded_files).to contain_exactly( + expect(loaded_files).to contain_files( "spec/bars/bar_spec.rb", "spec/foos/foo_spec.rb" ) @@ -38,6 +38,6 @@ FileUtils.touch("subtrees/DD/spec/dd_foo_spec.rb") FileUtils.ln_s(File.join(project_dir, "subtrees/DD/spec"), "spec/lib/DD") - expect(loaded_files).to contain_exactly("spec/lib/DD/dd_foo_spec.rb") + expect(loaded_files).to contain_files("spec/lib/DD/dd_foo_spec.rb") end end From d660b6adfbe186ee9e576e6365685cde1a90deca Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 15 Sep 2014 11:27:46 -0700 Subject: [PATCH 012/275] Forwardport 3.1.3 release notes. [ci skip] --- Changelog.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index fdacb9c8b1..8d3a8d035a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,10 +1,25 @@ ### 3.2.0 Development -[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.2...master) +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.3...master) Enhancements: * Improve the `inspect` output of example groups. (Mike Dalton, #1687) +### 3.1.3 / 2014-09-15 +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.2...v3.1.3) + +Bug Fixes: + +* Fix yet another regression in rake task pattern handling, to allow + `task.pattern = FileList["..."]` to work. That was never intended + to be supported but accidentally worked in 3.0 and earlier. + (Myron Marston, #1701) +* Fix pattern handling so that files are normalized to absolute paths + before subtracting the `--exclude-pattern` matched files from the + `--pattern` matched files so that it still works even if the patterns + are in slightly different forms (e.g. one starting with `./`). + (Christian Nelson, #1698) + ### 3.1.2 / 2014-09-08 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.1...v3.1.2) From 2cc12cefece83918b2e0737f43a72be52f195a16 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 16 Sep 2014 18:46:26 -0700 Subject: [PATCH 013/275] Allow users to filter subdirs of their current dir. This also greatly simplifies the backtrace formatter; the system_exclusion_patterns approach made it more complicated, as did always having an inclusion_pattern. We only need the current directory as an inclusion_pattern when it matches one of the built-in exclusion patterns. Note that this was the approach that we originally had in #843; it got lost in the refactoring in #1062 (af0b271c92ad2a3336eb70be7bf2c0de4325a568). While it seemed like a simpler approach to always put the current dir in the inclusion_patterns, in practice, this caused a sequence of whack-a-mole regressions: - #1328 (fixed by #1329) - #1604 (fixed by #1616) - #1705 (fixed by this commit) I'm hopeful that returning to only including the current dir in `inclusion_patterns` if it matches a built-in exclusion pattern will solve these issues once and for all. --- lib/rspec/core/backtrace_formatter.rb | 19 ++++++------- spec/rspec/core/backtrace_formatter_spec.rb | 31 +++++++++++++++++++-- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/lib/rspec/core/backtrace_formatter.rb b/lib/rspec/core/backtrace_formatter.rb index d8dde2893e..b1dff2f1c7 100644 --- a/lib/rspec/core/backtrace_formatter.rb +++ b/lib/rspec/core/backtrace_formatter.rb @@ -12,23 +12,22 @@ def initialize patterns << "org/jruby/" if RUBY_PLATFORM == 'java' patterns.map! { |s| Regexp.new(s.gsub("/", File::SEPARATOR)) } - @system_exclusion_patterns = [Regexp.union(RSpec::CallerFilter::IGNORE_REGEX, *patterns)] - @exclusion_patterns = [] + @system_exclusion_patterns - @inclusion_patterns = [Regexp.new(Dir.getwd)] + @exclusion_patterns = [Regexp.union(RSpec::CallerFilter::IGNORE_REGEX, *patterns)] + @inclusion_patterns = [] + + return unless matches?(@exclusion_patterns, File.join(Dir.getwd, "lib", "foo.rb:13")) + inclusion_patterns << Regexp.new(Dir.getwd) end attr_writer :full_backtrace def full_backtrace? - @full_backtrace || @exclusion_patterns.empty? + @full_backtrace || exclusion_patterns.empty? end def filter_gem(gem_name) sep = File::SEPARATOR - pattern = /#{sep}#{gem_name}(-[^#{sep}]+)?#{sep}/ - - @exclusion_patterns << pattern - @system_exclusion_patterns << pattern + exclusion_patterns << /#{sep}#{gem_name}(-[^#{sep}]+)?#{sep}/ end def format_backtrace(backtrace, options={}) @@ -54,9 +53,7 @@ def backtrace_line(line) def exclude?(line) return false if @full_backtrace - relative_line = Metadata.relative_path(line) - return false unless matches?(@exclusion_patterns, relative_line) - matches?(@system_exclusion_patterns, relative_line) || !matches?(@inclusion_patterns, line) + matches?(exclusion_patterns, line) && !matches?(inclusion_patterns, line) end private diff --git a/spec/rspec/core/backtrace_formatter_spec.rb b/spec/rspec/core/backtrace_formatter_spec.rb index 257d546495..c9fecbfb51 100644 --- a/spec/rspec/core/backtrace_formatter_spec.rb +++ b/spec/rspec/core/backtrace_formatter_spec.rb @@ -35,9 +35,10 @@ def make_backtrace_formatter(exclusion_patterns=nil, inclusion_patterns=nil) expect(make_backtrace_formatter.exclude?("#{Dir.getwd}/arbitrary")).to be false end - it "includes something in the current working directory even with a matching exclusion pattern" do - formatter = make_backtrace_formatter([/foo/]) - expect(formatter.exclude? "#{Dir.getwd}/foo").to be false + it 'allows users to exclude their bundler vendor directory' do + formatter = make_backtrace_formatter([%r{/vendor/bundle/}]) + vendored_gem_line = File.join(Dir.getwd, "vendor/bundle/gems/mygem-4.1.6/lib/my_gem:241") + expect(formatter.exclude? vendored_gem_line).to be true end context "when the exclusion list has been replaced" do @@ -227,6 +228,30 @@ def make_backtrace_formatter(exclusion_patterns=nil, inclusion_patterns=nil) end end + context "when the current directory matches one of the default exclusion patterns" do + include_context "isolated directory" + + around do |ex| + FileUtils.mkdir_p("bin") + Dir.chdir("./bin", &ex) + end + + let(:line) { File.join(Dir.getwd, "foo.rb:13") } + + it 'does not exclude lines from files in the current directory' do + expect(make_backtrace_formatter.exclude? line).to be false + end + + context "with inclusion_patterns cleared" do + it 'excludes lines from files in the current directory' do + formatter = make_backtrace_formatter + formatter.inclusion_patterns.clear + + expect(formatter.exclude? line).to be true + end + end + end + context "with no patterns" do it "keeps all lines" do lines = ["/tmp/a_file", "some_random_text", "hello\330\271!"] From 4cd24147ff8be116f0df4110cd1fb8cdd2eda742 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 26 Aug 2014 22:45:26 -0700 Subject: [PATCH 014/275] Extract sandboxing logic into a separate file. The formatting specs rely on a particular expected backtrace, which depends on which lines in particular bits of the sandboxing code are on. Before, changes to `spec_helper` would require updating the formatting support expected backtraces. This change will isolate it. --- spec/spec_helper.rb | 52 ------------------------------- spec/support/formatter_support.rb | 39 +++++++++++------------ spec/support/sandboxing.rb | 50 +++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 73 deletions(-) create mode 100644 spec/support/sandboxing.rb diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e13bad3efa..65fa6c3430 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -49,51 +49,6 @@ def self.new(*args, &block) Dir['./spec/support/**/*.rb'].map {|f| require f} - class NullObject - private - def method_missing(method, *args, &block) - # ignore - end - end - - module Sandboxing - def self.sandboxed(&block) - @orig_config = RSpec.configuration - @orig_world = RSpec.world - @orig_example = RSpec.current_example - new_config = RSpec::Core::Configuration.new - new_config.expose_dsl_globally = false - new_config.expecting_with_rspec = true - new_world = RSpec::Core::World.new(new_config) - RSpec.configuration = new_config - RSpec.world = new_world - object = Object.new - object.extend(RSpec::Core::SharedExampleGroup) - - (class << RSpec::Core::ExampleGroup; self; end).class_exec do - alias_method :orig_run, :run - def run(reporter=nil) - RSpec.current_example = nil - orig_run(reporter || NullObject.new) - end - end - - RSpec::Mocks.with_temporary_scope do - object.instance_exec(&block) - end - ensure - (class << RSpec::Core::ExampleGroup; self; end).class_exec do - remove_method :run - alias_method :run, :orig_run - remove_method :orig_run - end - - RSpec.configuration = @orig_config - RSpec.world = @orig_world - RSpec.current_example = @orig_example - end - end - module EnvHelpers def with_env_vars(vars) original = ENV.to_hash @@ -121,13 +76,6 @@ def without_env_vars(*vars) RSpec.configure do |c| # structural c.alias_it_behaves_like_to 'it_has_behavior' - c.around {|example| Sandboxing.sandboxed { example.run }} - c.around do |ex| - orig_load_path = $LOAD_PATH.dup - ex.run - $LOAD_PATH.replace(orig_load_path) - end - c.include(RSpecHelpers) c.include Aruba::Api, :file_path => /spec\/command_line/ diff --git a/spec/support/formatter_support.rb b/spec/support/formatter_support.rb index b49012d938..64455f165d 100644 --- a/spec/support/formatter_support.rb +++ b/spec/support/formatter_support.rb @@ -54,14 +54,13 @@ def expected_summary_output_for_example_specs | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:31 - | # ./spec/spec_helper.rb:77:in `run' + | # ./spec/support/sandboxing.rb:31:in `run' | # ./spec/support/formatter_support.rb:13:in `run_example_specs_with_formatter' - | # ./spec/spec_helper.rb:127 - | # ./spec/spec_helper.rb:124 - | # ./spec/spec_helper.rb:82:in `instance_exec' - | # ./spec/spec_helper.rb:82:in `sandboxed' - | # ./spec/spec_helper.rb:81:in `sandboxed' - | # ./spec/spec_helper.rb:124 + | # ./spec/support/sandboxing.rb:2 + | # ./spec/support/sandboxing.rb:36:in `instance_exec' + | # ./spec/support/sandboxing.rb:36:in `sandboxed' + | # ./spec/support/sandboxing.rb:35:in `sandboxed' + | # ./spec/support/sandboxing.rb:2 | | 3) a failing spec with odd backtraces fails with a backtrace that has no file | Failure/Error: Unable to find matching line from backtrace @@ -111,14 +110,13 @@ def expected_summary_output_for_example_specs | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:31:in `block (2 levels) in ' - | # ./spec/spec_helper.rb:77:in `run' + | # ./spec/support/sandboxing.rb:31:in `run' | # ./spec/support/formatter_support.rb:13:in `run_example_specs_with_formatter' - | # ./spec/spec_helper.rb:127:in `block (3 levels) in ' - | # ./spec/spec_helper.rb:124:in `block (4 levels) in ' - | # ./spec/spec_helper.rb:82:in `instance_exec' - | # ./spec/spec_helper.rb:82:in `block in sandboxed' - | # ./spec/spec_helper.rb:81:in `sandboxed' - | # ./spec/spec_helper.rb:124:in `block (3 levels) in ' + | # ./spec/support/sandboxing.rb:2:in `block (3 levels) in ' + | # ./spec/support/sandboxing.rb:36:in `instance_exec' + | # ./spec/support/sandboxing.rb:36:in `block in sandboxed' + | # ./spec/support/sandboxing.rb:35:in `sandboxed' + | # ./spec/support/sandboxing.rb:2:in `block (2 levels) in ' | | 3) a failing spec with odd backtraces fails with a backtrace that has no file | Failure/Error: ERB.new("<%= raise 'foo' %>").result @@ -126,14 +124,13 @@ def expected_summary_output_for_example_specs | foo | # (erb):1:in `
' | # ./spec/rspec/core/resources/formatter_specs.rb:39:in `block (2 levels) in ' - | # ./spec/spec_helper.rb:77:in `run' + | # ./spec/support/sandboxing.rb:31:in `run' | # ./spec/support/formatter_support.rb:13:in `run_example_specs_with_formatter' - | # ./spec/spec_helper.rb:127:in `block (3 levels) in ' - | # ./spec/spec_helper.rb:124:in `block (4 levels) in ' - | # ./spec/spec_helper.rb:82:in `instance_exec' - | # ./spec/spec_helper.rb:82:in `block in sandboxed' - | # ./spec/spec_helper.rb:81:in `sandboxed' - | # ./spec/spec_helper.rb:124:in `block (3 levels) in ' + | # ./spec/support/sandboxing.rb:2:in `block (3 levels) in ' + | # ./spec/support/sandboxing.rb:36:in `instance_exec' + | # ./spec/support/sandboxing.rb:36:in `block in sandboxed' + | # ./spec/support/sandboxing.rb:35:in `sandboxed' + | # ./spec/support/sandboxing.rb:2:in `block (2 levels) in ' | | 4) a failing spec with odd backtraces fails with a backtrace containing an erb file | Failure/Error: Unable to find matching line from backtrace diff --git a/spec/support/sandboxing.rb b/spec/support/sandboxing.rb new file mode 100644 index 0000000000..1eeb33461f --- /dev/null +++ b/spec/support/sandboxing.rb @@ -0,0 +1,50 @@ +RSpec.configure do |c| + c.around { |ex| Sandboxing.sandboxed { ex.run } } +end + +class NullObject + private + def method_missing(method, *args, &block) + # ignore + end +end + +module Sandboxing + def self.sandboxed(&block) + orig_load_path = $LOAD_PATH.dup + orig_config = RSpec.configuration + orig_world = RSpec.world + orig_example = RSpec.current_example + new_config = RSpec::Core::Configuration.new + new_config.expose_dsl_globally = false + new_config.expecting_with_rspec = true + new_world = RSpec::Core::World.new(new_config) + RSpec.configuration = new_config + RSpec.world = new_world + object = Object.new + object.extend(RSpec::Core::SharedExampleGroup) + + (class << RSpec::Core::ExampleGroup; self; end).class_exec do + alias_method :orig_run, :run + def run(reporter=nil) + RSpec.current_example = nil + orig_run(reporter || NullObject.new) + end + end + + RSpec::Mocks.with_temporary_scope do + object.instance_exec(&block) + end + ensure + (class << RSpec::Core::ExampleGroup; self; end).class_exec do + remove_method :run + alias_method :run, :orig_run + remove_method :orig_run + end + + RSpec.configuration = orig_config + RSpec.world = orig_world + RSpec.current_example = orig_example + $LOAD_PATH.replace(orig_load_path) + end +end From 745ed28738ccd78c6d9ada3aec6f3c186ac3ae1a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 26 Aug 2014 22:55:53 -0700 Subject: [PATCH 015/275] Address ruby warnings from spec files. For rspec/rspec-support#106. --- spec/rspec/core/resources/utf8_encoded.rb | 1 + spec/spec_helper.rb | 147 ++++++++++------------ 2 files changed, 65 insertions(+), 83 deletions(-) diff --git a/spec/rspec/core/resources/utf8_encoded.rb b/spec/rspec/core/resources/utf8_encoded.rb index 7cbdd6908f..b173bce3b4 100644 --- a/spec/rspec/core/resources/utf8_encoded.rb +++ b/spec/rspec/core/resources/utf8_encoded.rb @@ -3,6 +3,7 @@ module Custom class ExampleUTF8ClassNameVarietà def self.è così = :però + così end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 65fa6c3430..cebad55b16 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,108 +1,89 @@ require 'rubygems' if RUBY_VERSION.to_f < 1.9 -begin - require 'spork' -rescue LoadError - module Spork - def self.prefork - yield - end +require 'rspec/support/spec' - def self.each_run - yield - end +module ArubaLoader + extend RSpec::Support::WithIsolatedStdErr + with_isolated_stderr do + require 'aruba/api' end end -Spork.prefork do - require 'rspec/support/spec' - - module ArubaLoader - extend RSpec::Support::WithIsolatedStdErr - with_isolated_stderr do - require 'aruba/api' - end - end - - class << RSpec - attr_writer :configuration, :world - end +class << RSpec + attr_writer :configuration, :world +end - if RUBY_PLATFORM == 'java' - # Works around https://jira.codehaus.org/browse/JRUBY-5678 - require 'fileutils' - ENV['TMPDIR'] = File.expand_path('../../tmp', __FILE__) - FileUtils.mkdir_p(ENV['TMPDIR']) - end +if RUBY_PLATFORM == 'java' + # Works around https://jira.codehaus.org/browse/JRUBY-5678 + require 'fileutils' + ENV['TMPDIR'] = File.expand_path('../../tmp', __FILE__) + FileUtils.mkdir_p(ENV['TMPDIR']) +end - $rspec_core_without_stderr_monkey_patch = RSpec::Core::Configuration.new +$rspec_core_without_stderr_monkey_patch = RSpec::Core::Configuration.new - class RSpec::Core::Configuration - def self.new(*args, &block) - super.tap do |config| - # We detect ruby warnings via $stderr, - # so direct our deprecations to $stdout instead. - config.deprecation_stream = $stdout - end +class RSpec::Core::Configuration + def self.new(*args, &block) + super.tap do |config| + # We detect ruby warnings via $stderr, + # so direct our deprecations to $stdout instead. + config.deprecation_stream = $stdout end end +end - Dir['./spec/support/**/*.rb'].map {|f| require f} +Dir['./spec/support/**/*.rb'].map {|f| require f} - module EnvHelpers - def with_env_vars(vars) - original = ENV.to_hash - vars.each { |k, v| ENV[k] = v } +module EnvHelpers + def with_env_vars(vars) + original = ENV.to_hash + vars.each { |k, v| ENV[k] = v } - begin - yield - ensure - ENV.replace(original) - end + begin + yield + ensure + ENV.replace(original) end + end - def without_env_vars(*vars) - original = ENV.to_hash - vars.each { |k| ENV.delete(k) } + def without_env_vars(*vars) + original = ENV.to_hash + vars.each { |k| ENV.delete(k) } - begin - yield - ensure - ENV.replace(original) - end + begin + yield + ensure + ENV.replace(original) end end +end - RSpec.configure do |c| - # structural - c.alias_it_behaves_like_to 'it_has_behavior' - c.include(RSpecHelpers) - c.include Aruba::Api, :file_path => /spec\/command_line/ +RSpec.configure do |c| + # structural + c.alias_it_behaves_like_to 'it_has_behavior' + c.include(RSpecHelpers) + c.include Aruba::Api, :file_path => /spec\/command_line/ - c.expect_with :rspec do |expectations| - expectations.syntax = :expect - end - - c.mock_with :rspec do |mocks| - mocks.syntax = :expect - end + c.expect_with :rspec do |expectations| + expectations.syntax = :expect + end - # runtime options - c.raise_errors_for_deprecations! - c.color = true - c.include EnvHelpers - c.filter_run_excluding :ruby => lambda {|version| - case version.to_s - when "!jruby" - RUBY_ENGINE == "jruby" - when /^> (.*)/ - !(RUBY_VERSION.to_s > $1) - else - !(RUBY_VERSION.to_s =~ /^#{version.to_s}/) - end - } + c.mock_with :rspec do |mocks| + mocks.syntax = :expect end -end -Spork.each_run do + # runtime options + c.raise_errors_for_deprecations! + c.color = true + c.include EnvHelpers + c.filter_run_excluding :ruby => lambda {|version| + case version.to_s + when "!jruby" + RUBY_ENGINE == "jruby" + when /^> (.*)/ + !(RUBY_VERSION.to_s > $1) + else + !(RUBY_VERSION.to_s =~ /^#{version.to_s}/) + end + } end From ec086b0294a80d622bb33dac65c2ce79b46fbee6 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 16 Sep 2014 23:26:47 -0700 Subject: [PATCH 016/275] Make the appropriate specs pending. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running against rspec/rspec-support#106, the warnings are only triggered for particular cases that we can’t easily solve, so only pend those specific specs. --- spec/rspec/core_spec.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/spec/rspec/core_spec.rb b/spec/rspec/core_spec.rb index 424ec92b77..e7de7fbe40 100644 --- a/spec/rspec/core_spec.rb +++ b/spec/rspec/core_spec.rb @@ -7,8 +7,17 @@ # Loading minitest issues warnings, so we put our fake minitest on the load # path to prevent the real minitest from being loaded. "$LOAD_PATH.unshift '#{fake_minitest}'", 'require "rspec/core"', 'RSpec::Core::Runner.disable_autorun!' do - if RUBY_VERSION == '1.9.2' || (RUBY_PLATFORM == 'java' && RUBY_VERSION == '2.0.0') - before { pending "Not working on #{RUBY_DESCRIPTION}" } + + pending_when = { + '1.9.2' => { :description => "issues no warnings when loaded" }, + '1.8.7' => { :description => "issues no warnings when the spec files are loaded" }, + '2.0.0' => { } + } + + if RUBY_VERSION == '1.9.2' || RUBY_VERSION == '1.8.7' || (RUBY_PLATFORM == 'java' && RUBY_VERSION == '2.0.0') + before(:example, pending_when.fetch(RUBY_VERSION)) do + pending "Not working on #{RUBY_DESCRIPTION}" + end end end From 88be15888ae848eb555868d91aaa6d8e36c15d00 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 17 Sep 2014 10:26:59 -0700 Subject: [PATCH 017/275] Fix `subject` for `describe nil` and `describe false`. Before, these were returning the string forms. Fixes rspec/rspec-expectations#653. --- lib/rspec/core/memoized_helpers.rb | 2 +- spec/rspec/core/memoized_helpers_spec.rb | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/rspec/core/memoized_helpers.rb b/lib/rspec/core/memoized_helpers.rb index 11e56bf1a3..035f76cceb 100644 --- a/lib/rspec/core/memoized_helpers.rb +++ b/lib/rspec/core/memoized_helpers.rb @@ -55,7 +55,7 @@ module MemoizedHelpers def subject __memoized.fetch(:subject) do __memoized[:subject] = begin - described = described_class || self.class.description + described = described_class || self.class.metadata.fetch(:description_args).first Class === described ? described.new : described end end diff --git a/spec/rspec/core/memoized_helpers_spec.rb b/spec/rspec/core/memoized_helpers_spec.rb index 401b4f0231..e4c8cf6a76 100644 --- a/spec/rspec/core/memoized_helpers_spec.rb +++ b/spec/rspec/core/memoized_helpers_spec.rb @@ -49,6 +49,24 @@ def subject_value_for(describe_arg, &block) end end + describe "with true" do + it "returns `true`" do + expect(subject_value_for(true)).to eq(true) + end + end + + describe "with false" do + it "returns `false`" do + expect(subject_value_for(false)).to eq(false) + end + end + + describe "with nil" do + it "returns `nil`" do + expect(subject_value_for(nil)).to eq(nil) + end + end + it "can be overriden and super'd to from a nested group" do outer_subject_value = inner_subject_value = nil From b66d38dae3c7e9e063526695a8b862d94493bb6d Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 18 Sep 2014 08:22:26 +1000 Subject: [PATCH 018/275] changelog for #1710 --- Changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Changelog.md b/Changelog.md index 8d3a8d035a..d15cb711ab 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,12 @@ Enhancements: * Improve the `inspect` output of example groups. (Mike Dalton, #1687) +Bugfixes: + +* Using `true`, `false` or `nil` with `describe` generated implicit subjects + now correctly returns the primitive rather than a string representation. + (Myron Marston, #1710) + ### 3.1.3 / 2014-09-15 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.2...v3.1.3) From 288dfba8b7b24b4928c095d56d35054c6df03354 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 18 Sep 2014 09:44:33 -0700 Subject: [PATCH 019/275] Forwardport 3.1.4 release notes. [ci skip] --- Changelog.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index d15cb711ab..2e0739bdeb 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,15 +1,21 @@ ### 3.2.0 Development -[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.3...master) +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.4...master) Enhancements: * Improve the `inspect` output of example groups. (Mike Dalton, #1687) -Bugfixes: +### 3.1.4 / 2014-09-18 +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.3...v3.1.4) -* Using `true`, `false` or `nil` with `describe` generated implicit subjects - now correctly returns the primitive rather than a string representation. - (Myron Marston, #1710) +Bug Fixes: + +* Fix implicit `subject` when using `describe false` or `describe nil` + so that it returns the provided primitive rather than the string + representation. (Myron Marston, #1710) +* Fix backtrace filtering to allow code in subdirectories of your + current working directory (such as vendor/bundle/...) to be filtered + from backtraces. (Myron Marston, #1708) ### 3.1.3 / 2014-09-15 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.2...v3.1.3) From e0a1dee7fddd083fadeb1ee439f3c637f8696677 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 23 Sep 2014 08:07:33 +1000 Subject: [PATCH 020/275] switches from our private `windows_os?` method to the one in RSpec::Support --- lib/rspec/core/configuration.rb | 2 +- lib/rspec/core/world.rb | 5 ----- spec/rspec/core/backtrace_formatter_spec.rb | 6 +++--- spec/rspec/core/configuration_spec.rb | 4 ++-- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index a9e76ff776..2366bd66c7 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -629,7 +629,7 @@ def color_enabled?(output=output_stream) def color=(true_or_false) return unless true_or_false - if RSpec.world.windows_os? && !ENV['ANSICON'] + if RSpec::Support::OS.windows? && !ENV['ANSICON'] RSpec.warning "You must use ANSICON 1.31 or later (http://adoxa.3eeweb.com/ansicon/) to use colour on Windows" @color = false else diff --git a/lib/rspec/core/world.rb b/lib/rspec/core/world.rb index e36f56694c..7830b355d6 100644 --- a/lib/rspec/core/world.rb +++ b/lib/rspec/core/world.rb @@ -31,11 +31,6 @@ def clear_remaining_example_groups example_groups.clear end - # @private - def windows_os? - RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/ - end - # @api private # # Apply ordering strategy from configuration to example groups diff --git a/spec/rspec/core/backtrace_formatter_spec.rb b/spec/rspec/core/backtrace_formatter_spec.rb index c9fecbfb51..d5276be943 100644 --- a/spec/rspec/core/backtrace_formatter_spec.rb +++ b/spec/rspec/core/backtrace_formatter_spec.rb @@ -103,7 +103,7 @@ def make_backtrace_formatter(exclusion_patterns=nil, inclusion_patterns=nil) end describe "#format_backtrace" do - it "excludes lines from rspec libs by default", :unless => RSpec.world.windows_os? do + it "excludes lines from rspec libs by default", :unless => RSpec::Support::OS.windows? do backtrace = [ "/path/to/rspec-expectations/lib/rspec/expectations/foo.rb:37", "/path/to/rspec-expectations/lib/rspec/matchers/foo.rb:37", @@ -115,7 +115,7 @@ def make_backtrace_formatter(exclusion_patterns=nil, inclusion_patterns=nil) expect(BacktraceFormatter.new.format_backtrace(backtrace)).to eq(["./my_spec.rb:5"]) end - it "excludes lines from rspec libs by default", :if => RSpec.world.windows_os? do + it "excludes lines from rspec libs by default", :if => RSpec::Support::OS.windows? do backtrace = [ "\\path\\to\\rspec-expectations\\lib\\rspec\\expectations\\foo.rb:37", "\\path\\to\\rspec-expectations\\lib\\rspec\\matchers\\foo.rb:37", @@ -147,7 +147,7 @@ def make_backtrace_formatter(exclusion_patterns=nil, inclusion_patterns=nil) end context "when rspec is installed in the current working directory" do - it "excludes lines from rspec libs by default", :unless => RSpec.world.windows_os? do + it "excludes lines from rspec libs by default", :unless => RSpec::Support::OS.windows? do backtrace = [ "#{Dir.getwd}/.bundle/path/to/rspec-expectations/lib/rspec/expectations/foo.rb:37", "#{Dir.getwd}/.bundle/path/to/rspec-expectations/lib/rspec/matchers/foo.rb:37", diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index 41d58ddc8c..170db423ef 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -495,12 +495,12 @@ def stub_expectation_adapters expect(config.files_to_run).to contain_files("spec/rspec/core/resources/a_spec.rb", "spec/rspec/core/resources/acceptance/foo_spec.rb") end - it "loads files in Windows", :if => RSpec.world.windows_os? do + it "loads files in Windows", :if => RSpec::Support::OS.windows? do assign_files_or_directories_to_run "C:\\path\\to\\project\\spec\\sub\\foo_spec.rb" expect(config.files_to_run).to contain_files(["C:/path/to/project/spec/sub/foo_spec.rb"]) end - it "loads files in Windows when directory is specified", :if => RSpec.world.windows_os? do + it "loads files in Windows when directory is specified", :if => RSpec::Support::OS.windows? do assign_files_or_directories_to_run "spec\\rspec\\core\\resources" expect(config.files_to_run).to contain_files(["spec/rspec/core/resources/a_spec.rb"]) end From d9774656f627522cbe33657db3a8e0d4d05e295a Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 23 Sep 2014 08:27:08 +1000 Subject: [PATCH 021/275] JRuby 2.0.0 currently produces no warning, but this mode is still experimental, so skip instead. --- spec/rspec/core_spec.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/rspec/core_spec.rb b/spec/rspec/core_spec.rb index e7de7fbe40..677ab2d812 100644 --- a/spec/rspec/core_spec.rb +++ b/spec/rspec/core_spec.rb @@ -14,11 +14,16 @@ '2.0.0' => { } } - if RUBY_VERSION == '1.9.2' || RUBY_VERSION == '1.8.7' || (RUBY_PLATFORM == 'java' && RUBY_VERSION == '2.0.0') + if RUBY_VERSION == '1.9.2' || RUBY_VERSION == '1.8.7' before(:example, pending_when.fetch(RUBY_VERSION)) do pending "Not working on #{RUBY_DESCRIPTION}" end end + if (RUBY_PLATFORM == 'java' && RUBY_VERSION == '2.0.0') + before(:example, pending_when.fetch(RUBY_VERSION)) do + skip "Not reliably working on #{RUBY_DESCRIPTION}" + end + end end describe "::configuration" do From 3d6d04f0bea801501d53c552b822aa6d8388bc16 Mon Sep 17 00:00:00 2001 From: Ben Snape Date: Mon, 22 Sep 2014 19:00:43 +0100 Subject: [PATCH 022/275] returning command failure message only when in verbose mode --- lib/rspec/core/rake_task.rb | 2 +- spec/rspec/core/rake_task_spec.rb | 37 +++++++++++++++++++++++++++---- spec/support/matchers.rb | 2 ++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/lib/rspec/core/rake_task.rb b/lib/rspec/core/rake_task.rb index a9fc3fe241..a7cfe65781 100644 --- a/lib/rspec/core/rake_task.rb +++ b/lib/rspec/core/rake_task.rb @@ -93,7 +93,7 @@ def run_task(verbose) return unless fail_on_error && !success - $stderr.puts "#{command} failed" + $stderr.puts "#{command} failed" if verbose exit $?.exitstatus end diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index 5b7613b22d..418c329794 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -69,12 +69,41 @@ def spec_command context 'with custom exit status' do it 'returns the correct status on exit', :slow do - with_isolated_stderr do - expect($stderr).to receive(:puts) { |cmd| expect(cmd).to match(/-e "exit\(2\);".* failed/) } - expect(task).to receive(:exit).with(2) + expect(task).to receive(:exit).with(2) + + expect { task.ruby_opts = '-e "exit(2);" ;#' + task.run_task true + }.to output(/-e "exit\(2\);" ;#/).to_stdout.and output(/-e "exit\(2\);".* failed/).to_stderr + end + end + + context 'with verbose enabled' do + it 'prints the command only to stdout for passing specs', :slow do + expect { + task.ruby_opts = '-e ""' + task.run_task true + }.to output(/-e ""/).to_stdout.and avoid_outputting.to_stderr + end + + it 'prints an additional message to stderr for failures', :slow do + expect(task).to receive(:exit) + + expect { + task.ruby_opts = '-e "exit(1);" ;#' + task.run_task true + }.to output(/-e "exit\(1\);" ;#/).to_stdout.and output(/-e "exit\(1\);".* failed/).to_stderr + end + end + + context 'with verbose disabled' do + it 'does not print to stdout or stderr', :slow do + expect(task).to receive(:exit) + + expect { + task.ruby_opts = '-e "exit(1);" ;#' task.run_task false - end + }.to avoid_outputting.to_stdout.and avoid_outputting.to_stderr end end diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index b194b7a7ae..224acc91da 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -101,3 +101,5 @@ def failure_reason(example) end RSpec::Matchers.alias_matcher :a_file_collection, :contain_files + +RSpec::Matchers.define_negated_matcher :avoid_outputting, :output From d72645e31bcd24ea43b9f476318dcd7601774e16 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 23 Sep 2014 10:07:23 -0700 Subject: [PATCH 023/275] Add changelog for #1704. --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 2e0739bdeb..c3bd5951b8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,8 @@ Enhancements: * Improve the `inspect` output of example groups. (Mike Dalton, #1687) +* When rake task fails, only output the command if `verbose` flag is + set. (Ben Snape, #1704) ### 3.1.4 / 2014-09-18 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.3...v3.1.4) From acd9622d8d8645e08175309c3a1f370906707a46 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 23 Sep 2014 10:08:46 -0700 Subject: [PATCH 024/275] Prefer `allow` over `expect` for these specs. The focus of these specs is on the output, not the fact that `exit` is called. --- spec/rspec/core/rake_task_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index 418c329794..b138c0c892 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -87,7 +87,7 @@ def spec_command end it 'prints an additional message to stderr for failures', :slow do - expect(task).to receive(:exit) + allow(task).to receive(:exit) expect { task.ruby_opts = '-e "exit(1);" ;#' @@ -98,7 +98,7 @@ def spec_command context 'with verbose disabled' do it 'does not print to stdout or stderr', :slow do - expect(task).to receive(:exit) + allow(task).to receive(:exit) expect { task.ruby_opts = '-e "exit(1);" ;#' From 9a76eea127a5bae5cbd62fa0339b3bb4b5dfb23a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 23 Sep 2014 10:11:16 -0700 Subject: [PATCH 025/275] This spec is focused on the `exit` behavior, not output behavior. --- spec/rspec/core/rake_task_spec.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index b138c0c892..396daa1a1a 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -68,13 +68,17 @@ def spec_command end context 'with custom exit status' do + def silence_output(&block) + expect(&block).to output(anything).to_stdout.and output(anything).to_stderr + end + it 'returns the correct status on exit', :slow do expect(task).to receive(:exit).with(2) - expect { + silence_output do task.ruby_opts = '-e "exit(2);" ;#' task.run_task true - }.to output(/-e "exit\(2\);" ;#/).to_stdout.and output(/-e "exit\(2\);".* failed/).to_stderr + end end end From 9b8ad12e329330f96c8f171b7ab80a65227349a5 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 26 Sep 2014 16:56:14 +1000 Subject: [PATCH 026/275] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 3 ++- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/run_build | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 5a03e132a1..8de3db2762 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-08-23T21:27:12-07:00 from the rspec-dev repo. +# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 371495bd64..67cc663934 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-08-23T21:27:12-07:00 from the rspec-dev repo. +# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -18,6 +18,7 @@ rvm: - 2.1.0 - 2.1.1 - 2.1.2 + - 2.1.3 - ruby-head - ree - jruby-18mode diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index f57555608b..d03e9f6f36 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-08-23T21:27:12-07:00 from the rspec-dev repo. +# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x diff --git a/script/functions.sh b/script/functions.sh index 5c7e3d0daa..daf776e19a 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-08-23T21:27:12-07:00 from the rspec-dev repo. +# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html diff --git a/script/run_build b/script/run_build index 10c1c23091..eb356b8bf8 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-08-23T21:27:12-07:00 from the rspec-dev repo. +# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e -x From a85884c48e62b309a7857481f446ead335e843cd Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 24 Sep 2014 08:20:49 +1000 Subject: [PATCH 027/275] Shellwords is for posix path escaping, we shouldn't use it on windows --- lib/rspec/core/rake_task.rb | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/rspec/core/rake_task.rb b/lib/rspec/core/rake_task.rb index a7cfe65781..07dc67a4b4 100644 --- a/lib/rspec/core/rake_task.rb +++ b/lib/rspec/core/rake_task.rb @@ -1,6 +1,6 @@ require 'rake' require 'rake/tasklib' -require 'shellwords' +require 'rspec/support/os' module RSpec module Core @@ -115,7 +115,7 @@ def file_inclusion_specification if ENV['SPEC'] FileList[ ENV['SPEC']].sort elsif String === pattern && !File.exist?(pattern) - "--pattern #{pattern.shellescape}" + "--pattern #{escape pattern}" else # Before RSpec 3.1, we used `FileList` to get the list of matched files, and # then pass that along to the `rspec` command. Starting with 3.1, we prefer to @@ -135,12 +135,24 @@ def file_inclusion_specification # a `pattern` option that will not work with `--pattern`. # # TODO: consider deprecating support for this and removing it in RSpec 4. - FileList[pattern].sort.map(&:shellescape) + FileList[pattern].sort.map { |file| escape file } + end + end + + if RSpec::Support::OS.windows? + def escape(shell_command) + "'#{shell_command.gsub("'", "\'")}'" + end + else + require 'shellwords' + + def escape(shell_command) + shell_command.shellescape end end def file_exclusion_specification - " --exclude-pattern #{exclude_pattern.shellescape}" if exclude_pattern + " --exclude-pattern #{escape exclude_pattern}" if exclude_pattern end def spec_command @@ -165,7 +177,7 @@ def rspec_load_path /#{File::SEPARATOR}rspec-(core|support)[^#{File::SEPARATOR}]*#{File::SEPARATOR}lib/ ) - "-I#{core_and_support.map(&:shellescape).join(File::PATH_SEPARATOR)}" + "-I#{core_and_support.map { |file| escape file }.join(File::PATH_SEPARATOR)}" end end end From d1d64630af1b8beba33ca0ea05c7198f3c6c96f4 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 28 Sep 2014 10:07:28 +1000 Subject: [PATCH 028/275] changelog for #1718 [skip ci] --- Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index 2e0739bdeb..8761d6cf3c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,11 @@ Enhancements: * Improve the `inspect` output of example groups. (Mike Dalton, #1687) +Bugfixes: + +* Fix issue with the rake task incorrectly escaping strings on Windows. + (Jon Rowe #1718) + ### 3.1.4 / 2014-09-18 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.3...v3.1.4) From 97dac0477bcfd126d1396e533c0bce5a61cfb4f5 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 28 Sep 2014 19:46:19 -0700 Subject: [PATCH 029/275] Support absolute path patterns. While this wasn't officially supported previously, setting `rake_task.pattern` to an absolute path pattern in RSpec 3.0 and before worked since it delegated to `FileList` internally (but now just forwards the pattern on to the `rspec` command). Fixes #1721. fixup --- Changelog.md | 9 ++++++++- lib/rspec/core/configuration.rb | 23 +++++++++++++++++++++-- spec/rspec/core/configuration_spec.rb | 11 +++++++++++ spec/rspec/core/rake_task_spec.rb | 12 ++++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index b8ff2b95c3..eb60c5feb8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,10 +7,17 @@ Enhancements: * When rake task fails, only output the command if `verbose` flag is set. (Ben Snape, #1704) -Bugfixes: +### 3.1.5 Development + +Bug Fixes: * Fix issue with the rake task incorrectly escaping strings on Windows. (Jon Rowe #1718) +* Support absolute path patterns. While this wasn't officially supported + previously, setting `rake_task.pattern` to an absolute path pattern in + RSpec 3.0 and before worked since it delegated to `FileList` internally + (but now just forwards the pattern on to the `rspec` command). + (Myron Marston, #1726) ### 3.1.4 / 2014-09-18 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.3...v3.1.4) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index 2366bd66c7..ee7bb92ba2 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1336,9 +1336,28 @@ def gather_directories(path) end def get_matching_files(path, pattern) + Dir[file_glob_from(path, pattern)].map { |file| File.expand_path(file) } + end + + def file_glob_from(path, pattern) stripped = "{#{pattern.gsub(/\s*,\s*/, ',')}}" - files = (pattern =~ /^(\.\/)?#{Regexp.escape path}/) ? Dir[stripped] : Dir["#{path}/#{stripped}"] - files.map { |file| File.expand_path(file) } + return stripped if pattern =~ /^(\.\/)?#{Regexp.escape path}/ || absolute_pattern?(pattern) + File.join(path, stripped) + end + + if RSpec::Support::OS.windows? + def absolute_pattern?(pattern) + pattern =~ /\A[A-Z]:\\/ || windows_absolute_network_path?(pattern) + end + + def windows_absolute_network_path?(pattern) + return false unless ::File::ALT_SEPARATOR + pattern.start_with?(::File::ALT_SEPARATOR + ::File::ALT_SEPARATOR) + end + else + def absolute_pattern?(pattern) + pattern.start_with?(File::Separator) + end end def extract_location(path) diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index 170db423ef..f33b0740a7 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -447,6 +447,17 @@ def stub_expectation_adapters expect(config.files_to_run).to contain_files(["./spec/rspec/core/resources/a_spec.rb"]) end + it "supports absolute path patterns" do + dir = File.expand_path("../resources", __FILE__) + config.pattern = File.join(dir, "**/*_spec.rb") + assign_files_or_directories_to_run "spec" + + expect(config.files_to_run).to contain_files( + "./spec/rspec/core/resources/acceptance/foo_spec.rb", + "./spec/rspec/core/resources/a_spec.rb" + ) + end + it 'reloads when `files_or_directories_to_run` is reassigned' do config.pattern = "spec/**/a_spec.rb" config.files_or_directories_to_run = "empty_dir" diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index 396daa1a1a..f894e0c553 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -221,6 +221,18 @@ def self.it_configures_rspec_load_path(description, path_template) end end + context "that is an absolute path file glob" do + it "loads the matching spec files" do + dir = File.expand_path("../resources", __FILE__) + task.pattern = File.join(dir, "**/*_spec.rb") + + expect(loaded_files).to contain_files( + "./spec/rspec/core/resources/acceptance/foo_spec.rb", + "./spec/rspec/core/resources/a_spec.rb" + ) + end + end + context "that is an array of existing files or directories, not a file glob" do it "loads the specified spec files, and spec files from the specified directories" do task.pattern = ["./spec/rspec/core/resources/acceptance", From 1f3938ba8c6f57cacc839ecd30620db119753241 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 28 Sep 2014 19:54:40 -0700 Subject: [PATCH 030/275] Simplify `contain_files` matcher. No need for `flatten`. This also makes the call sites less noisy by removing the array brackets. --- spec/rspec/core/configuration_spec.rb | 12 ++++++------ spec/rspec/core/rake_task_spec.rb | 12 ++++++------ spec/support/matchers.rb | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index f33b0740a7..31a0c6177f 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -426,25 +426,25 @@ def stub_expectation_adapters describe "#files_to_run" do it "loads files not following pattern if named explicitly" do assign_files_or_directories_to_run "spec/rspec/core/resources/a_bar.rb" - expect(config.files_to_run).to contain_files(["spec/rspec/core/resources/a_bar.rb"]) + expect(config.files_to_run).to contain_files("spec/rspec/core/resources/a_bar.rb") end it "prevents repetition of dir when start of the pattern" do config.pattern = "spec/**/a_spec.rb" assign_files_or_directories_to_run "spec" - expect(config.files_to_run).to contain_files(["spec/rspec/core/resources/a_spec.rb"]) + expect(config.files_to_run).to contain_files("spec/rspec/core/resources/a_spec.rb") end it "does not prevent repetition of dir when later of the pattern" do config.pattern = "rspec/**/a_spec.rb" assign_files_or_directories_to_run "spec" - expect(config.files_to_run).to contain_files(["spec/rspec/core/resources/a_spec.rb"]) + expect(config.files_to_run).to contain_files("spec/rspec/core/resources/a_spec.rb") end it "supports patterns starting with ./" do config.pattern = "./spec/**/a_spec.rb" assign_files_or_directories_to_run "spec" - expect(config.files_to_run).to contain_files(["./spec/rspec/core/resources/a_spec.rb"]) + expect(config.files_to_run).to contain_files("./spec/rspec/core/resources/a_spec.rb") end it "supports absolute path patterns" do @@ -508,12 +508,12 @@ def stub_expectation_adapters it "loads files in Windows", :if => RSpec::Support::OS.windows? do assign_files_or_directories_to_run "C:\\path\\to\\project\\spec\\sub\\foo_spec.rb" - expect(config.files_to_run).to contain_files(["C:/path/to/project/spec/sub/foo_spec.rb"]) + expect(config.files_to_run).to contain_files("C:/path/to/project/spec/sub/foo_spec.rb") end it "loads files in Windows when directory is specified", :if => RSpec::Support::OS.windows? do assign_files_or_directories_to_run "spec\\rspec\\core\\resources" - expect(config.files_to_run).to contain_files(["spec/rspec/core/resources/a_spec.rb"]) + expect(config.files_to_run).to contain_files("spec/rspec/core/resources/a_spec.rb") end it_behaves_like "handling symlinked directories when loading spec files" do diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index f894e0c553..9ec46619f9 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -210,14 +210,14 @@ def self.it_configures_rspec_load_path(description, path_template) context "that is an existing directory, not a file glob" do it "loads the spec files in that directory" do task.pattern = "./spec/rspec/core/resources/acceptance" - expect(loaded_files).to contain_files(["./spec/rspec/core/resources/acceptance/foo_spec.rb"]) + expect(loaded_files).to contain_files("./spec/rspec/core/resources/acceptance/foo_spec.rb") end end context "that is an existing file, not a file glob" do it "loads the spec file" do task.pattern = "./spec/rspec/core/resources/acceptance/foo_spec.rb" - expect(loaded_files).to contain_files(["./spec/rspec/core/resources/acceptance/foo_spec.rb"]) + expect(loaded_files).to contain_files("./spec/rspec/core/resources/acceptance/foo_spec.rb") end end @@ -249,7 +249,7 @@ def self.it_configures_rspec_load_path(description, path_template) context "that is a single glob that starts with ./" do it "loads the spec files that match the glob" do task.pattern = "./spec/rspec/core/resources/acceptance/**/*_spec.rb" - expect(loaded_files).to contain_files(["./spec/rspec/core/resources/acceptance/foo_spec.rb"]) + expect(loaded_files).to contain_files("./spec/rspec/core/resources/acceptance/foo_spec.rb") end end @@ -327,7 +327,7 @@ def make_files_in_dir(dir) task.pattern = "spec/**/*_spec.rb" unit_files = make_files_in_dir "unit" - expect(loaded_files).to contain_files(unit_files) + expect(loaded_files).to contain_files(*unit_files) end it "excludes files when pattern and exclusion_pattern don't consistently start with ./" do @@ -335,7 +335,7 @@ def make_files_in_dir(dir) task.pattern = "spec/**/*_spec.rb" unit_files = make_files_in_dir "unit" - expect(loaded_files).to contain_files(unit_files) + expect(loaded_files).to contain_files(*unit_files) end end @@ -351,7 +351,7 @@ def make_files_in_dir(dir) File.join("spec", file_name).tap { |f| FileUtils.touch(f) } end - expect(loaded_files).to contain_files(files) + expect(loaded_files).to contain_files(*files) end end diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index 224acc91da..148d624682 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -89,7 +89,7 @@ def failure_reason(example) end RSpec::Matchers.define :contain_files do |*expected_files| - contain_exactly_matcher = RSpec::Matchers::BuiltIn::ContainExactly.new(expected_files.flatten.map { |f| File.expand_path(f) }) + contain_exactly_matcher = RSpec::Matchers::BuiltIn::ContainExactly.new(expected_files.map { |f| File.expand_path(f) }) match do |actual_files| files = actual_files.map { |f| File.expand_path(f) } From 6b7f55e4fb36226cf830983aab8da8308f641708 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 29 Sep 2014 23:37:16 -0700 Subject: [PATCH 031/275] Forwardport 3.1.5 release notes. --- Changelog.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index eb60c5feb8..70bb722f2b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ ### 3.2.0 Development -[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.4...master) +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.5...master) Enhancements: @@ -7,7 +7,8 @@ Enhancements: * When rake task fails, only output the command if `verbose` flag is set. (Ben Snape, #1704) -### 3.1.5 Development +### 3.1.5 / 2014-09-29 +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.4...v3.1.5) Bug Fixes: From 82655023a58dc1bb0790a8109a85392ae1bfb14a Mon Sep 17 00:00:00 2001 From: Alexey Fedorov Date: Sun, 5 Oct 2014 00:52:00 +0300 Subject: [PATCH 032/275] Introduce RSpec::Core.clear_examples --- lib/rspec/core.rb | 13 +++++ lib/rspec/core/configuration.rb | 16 ++++++ lib/rspec/core/metadata.rb | 10 ++++ lib/rspec/core/reporter.rb | 7 +++ spec/rspec/core_spec.rb | 88 ++++++++++++++++++++++++++++++--- 5 files changed, 128 insertions(+), 6 deletions(-) diff --git a/lib/rspec/core.rb b/lib/rspec/core.rb index 5283587401..298396777b 100644 --- a/lib/rspec/core.rb +++ b/lib/rspec/core.rb @@ -53,6 +53,19 @@ def self.reset @configuration = nil end + # Used to ensure examples get reloaded between multiple runs in the same + # process and ensures user configuration is persisted. + # + # Users must invoke this if they want to clear all examples but preserve + # current configuration when they use runner multiple times within the same + # process. + def self.clear_examples + world.reset + configuration.reporter.reset + configuration.start_time = ::RSpec::Core::Time.now + configuration.reset_filters + end + # Returns the global [Configuration](RSpec/Core/Configuration) object. While you # _can_ use this method to access the configuration, the more common # convention is to use [RSpec.configure](RSpec#configure-class_method). diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index ee7bb92ba2..2719227cdb 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -279,6 +279,8 @@ def treat_symbols_as_metadata_keys_with_true_values=(_value) # @private attr_accessor :filter_manager # @private + attr_accessor :original_filter_manager + # @private attr_reader :backtrace_formatter, :ordering_manager def initialize @@ -303,6 +305,7 @@ def initialize @reporter = nil @reporter_buffer = nil @filter_manager = FilterManager.new + @original_filter_manager = FilterManager.new @ordering_manager = Ordering::ConfigurationManager.new @preferred_options = {} @failure_color = :red @@ -332,6 +335,17 @@ def reset @formatter_loader = nil end + # @private + def reset_filters + self.filter_manager = FilterManager.new + filter_manager.include_only( + Metadata.deep_hash_dup(original_filter_manager.inclusions.rules) + ) + filter_manager.exclude_only( + Metadata.deep_hash_dup(original_filter_manager.exclusions.rules) + ) + end + # @overload add_setting(name) # @overload add_setting(name, opts) # @option opts [Symbol] :default @@ -900,6 +914,7 @@ def alias_it_behaves_like_to(new_name, report_label='') def filter_run_including(*args) meta = Metadata.build_hash_from(args, :warn_about_example_group_filtering) filter_manager.include_with_low_priority meta + original_filter_manager.include_with_low_priority Metadata.deep_hash_dup(meta) end alias_method :filter_run, :filter_run_including @@ -958,6 +973,7 @@ def inclusion_filter def filter_run_excluding(*args) meta = Metadata.build_hash_from(args, :warn_about_example_group_filtering) filter_manager.exclude_with_low_priority meta + original_filter_manager.exclude_with_low_priority Metadata.deep_hash_dup(meta) end # Clears and reassigns the `exclusion_filter`. Set to `nil` if you don't diff --git a/lib/rspec/core/metadata.rb b/lib/rspec/core/metadata.rb index 6eb72328a8..808c6fffdb 100644 --- a/lib/rspec/core/metadata.rb +++ b/lib/rspec/core/metadata.rb @@ -56,6 +56,16 @@ def self.build_hash_from(args, warn_about_example_group_filtering=false) hash end + # @private + def self.deep_hash_dup(hash) + return hash.dup if hash.is_a?(Array) + return hash unless hash.is_a?(Hash) + hash.inject(hash.dup) do |duplicate, (key, value)| + duplicate[key] = deep_hash_dup(value) + duplicate + end + end + # @private def self.backtrace_from(block) return caller unless block.respond_to?(:source_location) diff --git a/lib/rspec/core/reporter.rb b/lib/rspec/core/reporter.rb index 8cb9c9aa6d..18744ad36d 100644 --- a/lib/rspec/core/reporter.rb +++ b/lib/rspec/core/reporter.rb @@ -14,6 +14,13 @@ def initialize(configuration) # @private attr_reader :examples, :failed_examples, :pending_examples + # @private + def reset + @examples = [] + @failed_examples = [] + @pending_examples = [] + end + # Registers a listener to a list of notifications. The reporter will send notification of # events to all registered listeners # diff --git a/spec/rspec/core_spec.rb b/spec/rspec/core_spec.rb index 677ab2d812..5cc852e584 100644 --- a/spec/rspec/core_spec.rb +++ b/spec/rspec/core_spec.rb @@ -26,13 +26,13 @@ end end - describe "::configuration" do + describe ".configuration" do it "returns the same object every time" do expect(RSpec.configuration).to equal(RSpec.configuration) end end - describe "::configuration=" do + describe ".configuration=" do it "sets the configuration object" do configuration = RSpec::Core::Configuration.new @@ -42,7 +42,7 @@ end end - describe "::configure" do + describe ".configure" do it "yields the current configuration" do RSpec.configure do |config| expect(config).to equal(RSpec::configuration) @@ -50,13 +50,13 @@ end end - describe "::world" do + describe ".world" do it "returns the same object every time" do expect(RSpec.world).to equal(RSpec.world) end end - describe "::world=" do + describe ".world=" do it "sets the world object" do world = RSpec::Core::World.new @@ -76,7 +76,7 @@ end end - describe "::reset" do + describe ".reset" do it "resets the configuration and world objects" do config_before_reset = RSpec.configuration world_before_reset = RSpec.world @@ -88,6 +88,82 @@ end end + describe ".clear_examples" do + let(:listener) { double("listener") } + let(:reporter) { RSpec.configuration.reporter } + + def some_example + double("example").as_null_object + end + + it "clears example groups" do + RSpec.world.example_groups << :example_group + + RSpec.clear_examples + + expect(RSpec.world.example_groups).to be_empty + end + + it "resets start_time" do + start_time_before_clear = RSpec.configuration.start_time + + RSpec.clear_examples + + expect(RSpec.configuration.start_time).not_to eq(start_time_before_clear) + end + + it "clears examples, failed_examples and pending_examples" do + reporter.start(3) + reporter.example_started(some_example) + reporter.example_failed(some_example) + reporter.example_pending(some_example) + reporter.finish + + reporter.register_listener(listener, :dump_summary) + + expect(listener).to receive(:dump_summary) do |notification| + expect(notification.examples).to be_empty + expect(notification.failed_examples).to be_empty + expect(notification.pending_examples).to be_empty + end + + RSpec.clear_examples + reporter.start(0) + reporter.finish + end + + it "restores inclusion rules set by configuration" do + file_path = File.expand_path("foo_spec.rb") + RSpec.configure { |config| config.filter_run_including(locations: { file_path => [12] }) } + allow(RSpec.configuration).to receive(:load).with(file_path) + allow(reporter).to receive(:report) + RSpec::Core::Runner.run(["foo_spec.rb:14"]) + + expect(RSpec.configuration.filter_manager.inclusions[:locations]) + .to eq(file_path => [12, 14]) + + RSpec.clear_examples + + expect(RSpec.configuration.filter_manager.inclusions[:locations]) + .to eq(file_path => [12]) + end + + it "restores exclusion rules set by configuration" do + RSpec.configure { |config| config.filter_run_excluding(slow: true) } + allow(RSpec.configuration).to receive(:load) + allow(reporter).to receive(:report) + RSpec::Core::Runner.run(["--tag", "~fast"]) + + expect(RSpec.configuration.filter_manager.exclusions.rules) + .to eq(slow: true, fast: true) + + RSpec.clear_examples + + expect(RSpec.configuration.filter_manager.exclusions.rules) + .to eq(slow: true) + end + end + describe "::Core.path_to_executable" do it 'returns the absolute location of the exe/rspec file' do expect(File.exist? RSpec::Core.path_to_executable).to be_truthy From 8952dc5bf02bbccf67760baad2b1a704224526e7 Mon Sep 17 00:00:00 2001 From: Alexey Fedorov Date: Sun, 5 Oct 2014 02:14:10 +0300 Subject: [PATCH 033/275] Add feature spec for RSpec.clear_examples --- features/clear_examples.feature | 113 ++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 features/clear_examples.feature diff --git a/features/clear_examples.feature b/features/clear_examples.feature new file mode 100644 index 0000000000..9e27ef7b34 --- /dev/null +++ b/features/clear_examples.feature @@ -0,0 +1,113 @@ +Feature: Running specs multiple times with different runner options in the same + process + + Use `clear_examples` command to clear all example groups between different + runs in the same process. It: + + - clears all example groups + - restores inclusion and exclusion filters set by configuration + - clears inclusion and exclusion filters set by previous spec run (via + runner) + - resets all time counters (start time, load time, duration, etc.) + - resets different counts of examples (all examples, pending and failed) + + ```ruby + require "spec_helper" + + RSpec::Core::Runner.run([... some parameters ...]) + + RSpec.clear_examples + + RSpec::Core::Runner.run([... different parameters ...]) + ``` + + Background: + Given a file named "spec/spec_helper.rb" with: + """ruby + RSpec.configure do |config| + config.filter_run_including :focus => true + config.filter_run_excluding :slow => true + config.run_all_when_everything_filtered = true + end + """ + Given a file named "spec/truth_spec.rb" with: + """ruby + require 'spec_helper' + + RSpec.describe "truth" do + describe true do + it "is truthy" do + expect(true).to be_truthy + end + + it "is not falsy" do + expect(true).not_to be_falsy + end + end + + describe false do + it "is falsy" do + expect(false).to be_falsy + end + + it "is truthy" do + expect(false).not_to be_truthy + end + end + end + """ + + Scenario: Running specs multiple times in the same process + Given a file named "scripts/multiple_runs.rb" with: + """ruby + require 'rspec' + require 'rspec/core' + require './spec/spec_helper' + + RSpec::Core::Runner.run(['spec']) + RSpec.clear_examples + RSpec::Core::Runner.run(['spec']) + """ + When I run `ruby scripts/multiple_runs.rb` + Then the output should match: + """ + 4 examples, 0 failures + .* + 4 examples, 0 failures + """ + + Scenario: Running specs multiple times in the same process with different parameters + Given a file named "spec/bar_spec.rb" with: + """ruby + require 'spec_helper' + + RSpec.describe 'bar' do + subject(:bar) { :focused } + + it 'is focused', :focus do + expect(bar).to be(:focused) + end + end + """ + Given a file named "scripts/different_parameters.rb" with: + """ruby + require 'rspec' + require 'rspec/core' + require './spec/spec_helper' + + RSpec::Core::Runner.run(['spec']) + RSpec.clear_examples + RSpec::Core::Runner.run(['spec/truth_spec.rb:4']) + RSpec.clear_examples + RSpec::Core::Runner.run(['spec', '-e', 'fals']) + """ + When I run `ruby scripts/different_parameters.rb` + Then the output should match: + """ + 1 example, 0 failures + .* + 2 examples, 0 failures + .* + 3 examples, 0 failures + """ + From 37fb812bd58ae27b35db70a57b927293ae7c6091 Mon Sep 17 00:00:00 2001 From: Alexey Fedorov Date: Sun, 5 Oct 2014 02:20:47 +0300 Subject: [PATCH 034/275] fix syntax for old rubies --- spec/rspec/core_spec.rb | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/spec/rspec/core_spec.rb b/spec/rspec/core_spec.rb index 5cc852e584..2c7e619dd8 100644 --- a/spec/rspec/core_spec.rb +++ b/spec/rspec/core_spec.rb @@ -134,33 +134,39 @@ def some_example it "restores inclusion rules set by configuration" do file_path = File.expand_path("foo_spec.rb") - RSpec.configure { |config| config.filter_run_including(locations: { file_path => [12] }) } + RSpec.configure { + |config| config.filter_run_including(:locations => { file_path => [12] }) + } allow(RSpec.configuration).to receive(:load).with(file_path) allow(reporter).to receive(:report) RSpec::Core::Runner.run(["foo_spec.rb:14"]) - expect(RSpec.configuration.filter_manager.inclusions[:locations]) - .to eq(file_path => [12, 14]) + expect( + RSpec.configuration.filter_manager.inclusions[:locations] + ).to eq(file_path => [12, 14]) RSpec.clear_examples - expect(RSpec.configuration.filter_manager.inclusions[:locations]) - .to eq(file_path => [12]) + expect( + RSpec.configuration.filter_manager.inclusions[:locations] + ).to eq(file_path => [12]) end it "restores exclusion rules set by configuration" do - RSpec.configure { |config| config.filter_run_excluding(slow: true) } + RSpec.configure { |config| config.filter_run_excluding(:slow => true) } allow(RSpec.configuration).to receive(:load) allow(reporter).to receive(:report) RSpec::Core::Runner.run(["--tag", "~fast"]) - expect(RSpec.configuration.filter_manager.exclusions.rules) - .to eq(slow: true, fast: true) + expect( + RSpec.configuration.filter_manager.exclusions.rules + ).to eq(:slow => true, :fast => true) RSpec.clear_examples - expect(RSpec.configuration.filter_manager.exclusions.rules) - .to eq(slow: true) + expect( + RSpec.configuration.filter_manager.exclusions.rules + ).to eq(:slow => true) end end From 3236601755682f170f8e9fcbfb4006d59d625bfa Mon Sep 17 00:00:00 2001 From: Alexey Fedorov Date: Sun, 5 Oct 2014 03:13:02 +0300 Subject: [PATCH 035/275] fix filter_gems_from_backtrace cuke feature for jruby 1.8 mode --- features/configuration/backtrace_exclusion_patterns.feature | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/configuration/backtrace_exclusion_patterns.feature b/features/configuration/backtrace_exclusion_patterns.feature index be4b8f9f48..3945f0a28a 100644 --- a/features/configuration/backtrace_exclusion_patterns.feature +++ b/features/configuration/backtrace_exclusion_patterns.feature @@ -134,5 +134,5 @@ Feature: Excluding lines from the backtrace config.filter_gems_from_backtrace "my_gem" end """ - Then the output from `rspec` should contain "# ./vendor/my_gem-1.2.3/lib/my_gem.rb:4:in `do_amazing_things!'" - But the output from `rspec --require spec_helper` should not contain "# ./vendor/my_gem-1.2.3/lib/my_gem.rb:4:in `do_amazing_things!'" + Then the output from `rspec` should contain "vendor/my_gem-1.2.3/lib/my_gem.rb:4:in `do_amazing_things!'" + But the output from `rspec --require spec_helper` should not contain "vendor/my_gem-1.2.3/lib/my_gem.rb:4:in `do_amazing_things!'" From 04636f39069cff8d675dd2b095e859cb974e474c Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 5 Oct 2014 16:14:16 +1100 Subject: [PATCH 036/275] changelog and cleanup for #1706 [skip ci] --- Changelog.md | 2 ++ spec/rspec/core_spec.rb | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 70bb722f2b..239e3766cf 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,8 @@ Enhancements: * Improve the `inspect` output of example groups. (Mike Dalton, #1687) * When rake task fails, only output the command if `verbose` flag is set. (Ben Snape, #1704) +* Add `RSpec.clear_examples` as a clear way to reset examples in between + spec runs, whilst retaining user configuration. (Alexey Fedorov, #1706) ### 3.1.5 / 2014-09-29 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.4...v3.1.5) diff --git a/spec/rspec/core_spec.rb b/spec/rspec/core_spec.rb index 2c7e619dd8..72201817bf 100644 --- a/spec/rspec/core_spec.rb +++ b/spec/rspec/core_spec.rb @@ -134,9 +134,9 @@ def some_example it "restores inclusion rules set by configuration" do file_path = File.expand_path("foo_spec.rb") - RSpec.configure { - |config| config.filter_run_including(:locations => { file_path => [12] }) - } + RSpec.configure do |config| + config.filter_run_including(:locations => { file_path => [12] }) + end allow(RSpec.configuration).to receive(:load).with(file_path) allow(reporter).to receive(:report) RSpec::Core::Runner.run(["foo_spec.rb:14"]) From 3e577352e92607f80d942c7b637b70eeaaef87c2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 5 Oct 2014 15:55:31 -0700 Subject: [PATCH 037/275] Cleanup cuke. - Fix odd line breaks. - Remove unneeded `require 'rspec'`. The rspec gem contains no code, just dependency declarations on rspec-core, rspec-mocks and rspec-expectations. --- features/clear_examples.feature | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/features/clear_examples.feature b/features/clear_examples.feature index 9e27ef7b34..abf4428517 100644 --- a/features/clear_examples.feature +++ b/features/clear_examples.feature @@ -1,13 +1,11 @@ -Feature: Running specs multiple times with different runner options in the same - process +Feature: Running specs multiple times with different runner options in the same process Use `clear_examples` command to clear all example groups between different runs in the same process. It: - clears all example groups - restores inclusion and exclusion filters set by configuration - - clears inclusion and exclusion filters set by previous spec run (via - runner) + - clears inclusion and exclusion filters set by previous spec run (via runner) - resets all time counters (start time, load time, duration, etc.) - resets different counts of examples (all examples, pending and failed) @@ -60,7 +58,6 @@ Feature: Running specs multiple times with different runner options in the same Scenario: Running specs multiple times in the same process Given a file named "scripts/multiple_runs.rb" with: """ruby - require 'rspec' require 'rspec/core' require './spec/spec_helper' @@ -91,7 +88,6 @@ Feature: Running specs multiple times with different runner options in the same """ Given a file named "scripts/different_parameters.rb" with: """ruby - require 'rspec' require 'rspec/core' require './spec/spec_helper' From 1af2261a0343a99010be4a844c5100d5a1127edc Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 5 Oct 2014 16:11:01 -0700 Subject: [PATCH 038/275] Cleanup `deep_hash_dup` a bit. - Prefer `klass === obj` over `obj.is_a? klass`. This is preferred because with user objects we generally can't trust `is_a?` as they may have redefined it to do something else. Redefining `Array.===` or `Hash.===` is much less likely. - Rename the arg to `object` since it may not be a hash. --- lib/rspec/core/metadata.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/rspec/core/metadata.rb b/lib/rspec/core/metadata.rb index 808c6fffdb..73780919fe 100644 --- a/lib/rspec/core/metadata.rb +++ b/lib/rspec/core/metadata.rb @@ -57,10 +57,11 @@ def self.build_hash_from(args, warn_about_example_group_filtering=false) end # @private - def self.deep_hash_dup(hash) - return hash.dup if hash.is_a?(Array) - return hash unless hash.is_a?(Hash) - hash.inject(hash.dup) do |duplicate, (key, value)| + def self.deep_hash_dup(object) + return object.dup if Array === object + return object unless Hash === object + + object.inject(object.dup) do |duplicate, (key, value)| duplicate[key] = deep_hash_dup(value) duplicate end From c8e438d7b51f653f02ea3dfea9cb0035f06c8ed1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 5 Oct 2014 16:20:56 -0700 Subject: [PATCH 039/275] Prevent unwanted output to stdout/stderr. These specs were calling reporter methods to simulate examples completing, which in turn caused printed output. --- spec/rspec/core_spec.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/rspec/core_spec.rb b/spec/rspec/core_spec.rb index 72201817bf..356f11f605 100644 --- a/spec/rspec/core_spec.rb +++ b/spec/rspec/core_spec.rb @@ -92,6 +92,11 @@ let(:listener) { double("listener") } let(:reporter) { RSpec.configuration.reporter } + before do + RSpec.configuration.output_stream = StringIO.new + RSpec.configuration.error_stream = StringIO.new + end + def some_example double("example").as_null_object end From ce75cef006a190a12d6d63d3e1f35b32ba085f3f Mon Sep 17 00:00:00 2001 From: Alexey Fedorov Date: Mon, 6 Oct 2014 02:50:10 +0300 Subject: [PATCH 040/275] PR 1706 followups --- features/clear_examples.feature | 2 -- lib/rspec/core/configuration.rb | 12 ++++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/features/clear_examples.feature b/features/clear_examples.feature index abf4428517..139879041a 100644 --- a/features/clear_examples.feature +++ b/features/clear_examples.feature @@ -59,7 +59,6 @@ Feature: Running specs multiple times with different runner options in the same Given a file named "scripts/multiple_runs.rb" with: """ruby require 'rspec/core' - require './spec/spec_helper' RSpec::Core::Runner.run(['spec']) RSpec.clear_examples @@ -89,7 +88,6 @@ Feature: Running specs multiple times with different runner options in the same Given a file named "scripts/different_parameters.rb" with: """ruby require 'rspec/core' - require './spec/spec_helper' RSpec::Core::Runner.run(['spec']) RSpec.clear_examples diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index 2719227cdb..f790987866 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -279,7 +279,7 @@ def treat_symbols_as_metadata_keys_with_true_values=(_value) # @private attr_accessor :filter_manager # @private - attr_accessor :original_filter_manager + attr_accessor :static_config_filter_manager # @private attr_reader :backtrace_formatter, :ordering_manager @@ -305,7 +305,7 @@ def initialize @reporter = nil @reporter_buffer = nil @filter_manager = FilterManager.new - @original_filter_manager = FilterManager.new + @static_config_filter_manager = FilterManager.new @ordering_manager = Ordering::ConfigurationManager.new @preferred_options = {} @failure_color = :red @@ -339,10 +339,10 @@ def reset def reset_filters self.filter_manager = FilterManager.new filter_manager.include_only( - Metadata.deep_hash_dup(original_filter_manager.inclusions.rules) + Metadata.deep_hash_dup(static_config_filter_manager.inclusions.rules) ) filter_manager.exclude_only( - Metadata.deep_hash_dup(original_filter_manager.exclusions.rules) + Metadata.deep_hash_dup(static_config_filter_manager.exclusions.rules) ) end @@ -914,7 +914,7 @@ def alias_it_behaves_like_to(new_name, report_label='') def filter_run_including(*args) meta = Metadata.build_hash_from(args, :warn_about_example_group_filtering) filter_manager.include_with_low_priority meta - original_filter_manager.include_with_low_priority Metadata.deep_hash_dup(meta) + static_config_filter_manager.include_with_low_priority Metadata.deep_hash_dup(meta) end alias_method :filter_run, :filter_run_including @@ -973,7 +973,7 @@ def inclusion_filter def filter_run_excluding(*args) meta = Metadata.build_hash_from(args, :warn_about_example_group_filtering) filter_manager.exclude_with_low_priority meta - original_filter_manager.exclude_with_low_priority Metadata.deep_hash_dup(meta) + static_config_filter_manager.exclude_with_low_priority Metadata.deep_hash_dup(meta) end # Clears and reassigns the `exclusion_filter`. Set to `nil` if you don't From 0da9c2faf8cd47cbbff893a50080565c59994297 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 6 Oct 2014 21:41:39 -0700 Subject: [PATCH 041/275] Improve convention explanation for shared examples. --- features/example_groups/shared_examples.feature | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/features/example_groups/shared_examples.feature b/features/example_groups/shared_examples.feature index b021bff50a..01183b4014 100644 --- a/features/example_groups/shared_examples.feature +++ b/features/example_groups/shared_examples.feature @@ -28,18 +28,22 @@ Feature: shared examples 'shared_examples_for_widgets'` to require a file at `#{PROJECT_ROOT}/spec/shared_examples_for_widgets.rb`. - 2. Put files containing shared examples in `spec/support/` and require files - in that directory from `spec/spec_helper.rb`: + 2. One convention is to put files containing shared examples in `spec/support/` + and require files in that directory from `spec/spec_helper.rb`: ```ruby Dir["./spec/support/**/*.rb"].sort.each { |f| require f} ``` - This is included in the generated `spec/spec_helper.rb` file in - `rspec-rails` + Historically, this was included in the generated `spec/spec_helper.rb` file in + `rspec-rails`. However, in order to keep your test suite boot time down, + it's a good idea to not autorequire all files in a directory like this. + When running only one spec file, loading unneeded dependencies or performing + unneeded setup can have a significant, noticable effect on how long it takes + before the first example runs. - 3. When all of the groups that include the shared group, just declare the - shared group in the same file. + 3. When all of the groups that include the shared group reside in the same file, + just declare the shared group in that file. Scenario: Shared examples group included in two groups in one file Given a file named "collection_spec.rb" with: From 3fe7414db1030396e50f51da6df1a2907608939c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 6 Oct 2014 21:45:00 -0700 Subject: [PATCH 042/275] Add support for relish staging environment. --- Rakefile | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/Rakefile b/Rakefile index 1c9210b9d0..550f874261 100644 --- a/Rakefile +++ b/Rakefile @@ -43,17 +43,34 @@ task :rdoc do sh "yardoc" end +with_changelog_in_features = lambda do |&block| + begin + sh "cp Changelog.md features/" + block.call + ensure + sh "rm features/Changelog.md" + end +end + desc "Push docs/cukes to relishapp using the relish-client-gem" task :relish, :version do |_t, args| raise "rake relish[VERSION]" unless args[:version] - sh "cp Changelog.md features/" - if `relish versions rspec/rspec-core`.split.map(&:strip).include? args[:version] - puts "Version #{args[:version]} already exists" - else - sh "relish versions:add rspec/rspec-core:#{args[:version]}" + + with_changelog_in_features.call do + if `relish versions rspec/rspec-core`.split.map(&:strip).include? args[:version] + puts "Version #{args[:version]} already exists" + else + sh "relish versions:add rspec/rspec-core:#{args[:version]}" + end + sh "relish push rspec/rspec-core:#{args[:version]}" + end +end + +desc "Push to relish staging environment" +task :relish_staging do + with_changelog_in_features.call do + sh "relish push rspec-staging/rspec-core" end - sh "relish push rspec/rspec-core:#{args[:version]}" - sh "rm features/Changelog.md" end task :default => [:spec, :cucumber, :rubocop] From dbe1493b1bfbe41ef03d69e875139fb7cabd79ff Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 7 Oct 2014 22:48:57 -0700 Subject: [PATCH 043/275] Prevent duplicate load path entries. Fixes #1729. --- Changelog.md | 5 +++++ lib/rspec/core/rake_task.rb | 2 +- spec/rspec/core/rake_task_spec.rb | 11 +++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 239e3766cf..7a136c3434 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,11 @@ Enhancements: * Add `RSpec.clear_examples` as a clear way to reset examples in between spec runs, whilst retaining user configuration. (Alexey Fedorov, #1706) +Bug Fixes: + +* Prevent rake task from generating duplicate load path entries. + (Myron Marston, #1735) + ### 3.1.5 / 2014-09-29 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.4...v3.1.5) diff --git a/lib/rspec/core/rake_task.rb b/lib/rspec/core/rake_task.rb index 07dc67a4b4..5c46d05b11 100644 --- a/lib/rspec/core/rake_task.rb +++ b/lib/rspec/core/rake_task.rb @@ -175,7 +175,7 @@ def rspec_load_path @rspec_load_path ||= begin core_and_support = $LOAD_PATH.grep( /#{File::SEPARATOR}rspec-(core|support)[^#{File::SEPARATOR}]*#{File::SEPARATOR}lib/ - ) + ).uniq "-I#{core_and_support.map { |file| escape file }.join(File::PATH_SEPARATOR)}" end diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index 9ec46619f9..bdbbccc627 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -162,6 +162,17 @@ def self.it_configures_rspec_load_path(description, path_template) expect(spec_command).to include(" -I#{path_template % "rspec-core"}:#{path_template % "rspec-support"} ") end + + it "avoids adding the same load path entries twice" do + $LOAD_PATH.replace([ + path_template % "rspec-core", + path_template % "rspec-support", + path_template % "rspec-core", + path_template % "rspec-support" + ]) + + expect(spec_command).to include(" -I#{path_template % "rspec-core"}:#{path_template % "rspec-support"} ") + end end end From 82436c1409ba18d05c9eab73a8bdae85c563ab7f Mon Sep 17 00:00:00 2001 From: Alexey Fedorov Date: Wed, 8 Oct 2014 18:36:23 +0200 Subject: [PATCH 044/275] [#1733] clarify that RSpec.reset resets all user configuration And users are responsible for re-configuration --- lib/rspec/core.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/rspec/core.rb b/lib/rspec/core.rb index 298396777b..e8c4a810fa 100644 --- a/lib/rspec/core.rb +++ b/lib/rspec/core.rb @@ -43,11 +43,12 @@ module RSpec extend RSpec::Core::Warnings - # Used to ensure examples get reloaded between multiple runs in - # the same process. + # Used to ensure examples get reloaded and user configuration gets reset to + # defaults between multiple runs in the same process. # # Users must invoke this if they want to have the configuration reset when - # they use runner multiple times within the same process. + # they use runner multiple times within the same process. Users must deal + # themselves with re-configuration of RSpec before run. def self.reset @world = nil @configuration = nil From be959b3c3ed5b549bc8700855206b26b21d1b77e Mon Sep 17 00:00:00 2001 From: Alexey Fedorov Date: Wed, 8 Oct 2014 18:56:22 +0200 Subject: [PATCH 045/275] [#1733] fix typo "they use runner" -> "they use the runner" --- lib/rspec/core.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rspec/core.rb b/lib/rspec/core.rb index e8c4a810fa..706aa03f7a 100644 --- a/lib/rspec/core.rb +++ b/lib/rspec/core.rb @@ -47,7 +47,7 @@ module RSpec # defaults between multiple runs in the same process. # # Users must invoke this if they want to have the configuration reset when - # they use runner multiple times within the same process. Users must deal + # they use the runner multiple times within the same process. Users must deal # themselves with re-configuration of RSpec before run. def self.reset @world = nil @@ -58,8 +58,8 @@ def self.reset # process and ensures user configuration is persisted. # # Users must invoke this if they want to clear all examples but preserve - # current configuration when they use runner multiple times within the same - # process. + # current configuration when they use the runner multiple times within the + # same process. def self.clear_examples world.reset configuration.reporter.reset From 6e3cff001e1300f91ca3d5ba0f2917180ccfb48d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 3 Oct 2014 13:35:04 -0700 Subject: [PATCH 046/275] Fix regression in rake task pattern handling. Since our change to forward `task.pattern` to `rspec` in #1653, it has not handled patterns that are relative to the current directory for a side directory (e.g. not under `spec`) of specs. This restores that behavior. Fixes #1728. --- Changelog.md | 6 +++++ lib/rspec/core/configuration.rb | 14 +++++++++-- spec/rspec/core/configuration_spec.rb | 34 +++++++++++++++++++++++++++ spec/rspec/core/rake_task_spec.rb | 13 ++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 7a136c3434..dcc0998b92 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,8 +9,14 @@ Enhancements: * Add `RSpec.clear_examples` as a clear way to reset examples in between spec runs, whilst retaining user configuration. (Alexey Fedorov, #1706) +### 3.1.6 Development +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.5...3-1-maintenance) + Bug Fixes: +* Fix regression in rake task pattern handling, that prevented patterns + that were relative from the current directory rather than from `spec` + from working properly. (Myron Marston, #1734) * Prevent rake task from generating duplicate load path entries. (Myron Marston, #1735) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index f790987866..32e1db5022 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1339,10 +1339,19 @@ def apply_derived_metadata_to(metadata) private def get_files_to_run(paths) - FlatMap.flat_map(paths) do |path| + FlatMap.flat_map(paths_to_check(paths)) do |path| path = path.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR File.directory?(path) ? gather_directories(path) : extract_location(path) - end.sort + end.sort.uniq + end + + def paths_to_check(paths) + return paths if pattern_might_load_specs_from_vendored_dirs? + paths + ['.'] + end + + def pattern_might_load_specs_from_vendored_dirs? + pattern.split(File::SEPARATOR).first.include?('**') end def gather_directories(path) @@ -1385,6 +1394,7 @@ def extract_location(path) filter_manager.add_location path, lines end + return [] if path == default_path path end diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index 31a0c6177f..a5357f6276 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -458,6 +458,27 @@ def stub_expectation_adapters ) end + it "supports relative path patterns for an alternate directory from `spec`" do + Dir.chdir("./spec/rspec/core") do + config.pattern = "resources/**/*_spec.rb" + assign_files_or_directories_to_run "spec" # default dir + + expect(config.files_to_run).to contain_files( + "resources/acceptance/foo_spec.rb", + "resources/a_spec.rb" + ) + end + end + + it "does not attempt to treat the pattern relative to `.` if it uses `**` in the first path segment as that would cause it load specs from vendored gems" do + Dir.chdir("./spec/rspec/core") do + config.pattern = "**/*_spec.rb" + assign_files_or_directories_to_run "spec" # default dir + + expect(config.files_to_run).to contain_files() + end + end + it 'reloads when `files_or_directories_to_run` is reassigned' do config.pattern = "spec/**/a_spec.rb" config.files_or_directories_to_run = "empty_dir" @@ -468,6 +489,17 @@ def stub_expectation_adapters to(a_file_collection("spec/rspec/core/resources/a_spec.rb")) end + it 'attempts to load the provided file names' do + assign_files_or_directories_to_run "path/to/some/file.rb" + expect(config.files_to_run).to eq(["path/to/some/file.rb"]) + end + + it 'does not attempt to load a file at the `default_path`' do + config.default_path = "path/to/dir" + assign_files_or_directories_to_run "path/to/dir" + expect(config.files_to_run).to eq([]) + end + context "with :" do it "overrides inclusion filters set on config" do config.filter_run_including :foo => :bar @@ -547,8 +579,10 @@ def loaded_files def specify_consistent_ordering_of_files_to_run allow(File).to receive(:directory?).with('a') { true } + allow(File).to receive(:directory?).with('.') { true } globbed_files = nil allow(Dir).to receive(:[]).with(/^\{?a/) { globbed_files } + allow(Dir).to receive(:[]).with(/^\{?\./) { [] } orderings = [ %w[ a/1.rb a/2.rb a/3.rb ], diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index bdbbccc627..bfd6ee593f 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -244,6 +244,19 @@ def self.it_configures_rspec_load_path(description, path_template) end end + context "that is a relative file glob, for a path not under the default spec dir (`spec`)" do + it "loads the matching spec files" do + Dir.chdir("./spec/rspec/core") do + task.pattern = "resources/**/*_spec.rb" + + expect(loaded_files).to contain_files( + "resources/acceptance/foo_spec.rb", + "resources/a_spec.rb" + ) + end + end + end + context "that is an array of existing files or directories, not a file glob" do it "loads the specified spec files, and spec files from the specified directories" do task.pattern = ["./spec/rspec/core/resources/acceptance", From 0ad25c755ef97f23fb5fa04bb4b8a3eb97c52094 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 8 Oct 2014 23:45:41 -0700 Subject: [PATCH 047/275] Forwardport 3.1.6 release notes. [ci skip] --- Changelog.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index dcc0998b92..2b12619570 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ ### 3.2.0 Development -[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.5...master) +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.6...master) Enhancements: @@ -9,8 +9,8 @@ Enhancements: * Add `RSpec.clear_examples` as a clear way to reset examples in between spec runs, whilst retaining user configuration. (Alexey Fedorov, #1706) -### 3.1.6 Development -[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.5...3-1-maintenance) +### 3.1.6 / 2014-10-08 +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.5...v3.1.6) Bug Fixes: From 4873896e86d461f19e5391bfb746f49ac37fb65f Mon Sep 17 00:00:00 2001 From: Akos Vandra Date: Fri, 3 Oct 2014 17:35:34 +0200 Subject: [PATCH 048/275] fixing relative path for /foo/bar , /foo/bar_baz becoming ._baz --- lib/rspec/core/metadata.rb | 13 ++++++++++++- spec/rspec/core/metadata_spec.rb | 7 +++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/rspec/core/metadata.rb b/lib/rspec/core/metadata.rb index 6eb72328a8..82025e7d93 100644 --- a/lib/rspec/core/metadata.rb +++ b/lib/rspec/core/metadata.rb @@ -30,7 +30,18 @@ module Metadata # @param line [String] current code line # @return [String] relative path to line def self.relative_path(line) - line = line.sub(File.expand_path("."), ".") + # Matches strings either at the beginning of the input or prefixed with a whitespace, + # containing the current path, either postfixed with the separator, or at the end of the string. + # Match groups are the character before and the character after the string if any. + # + # http://rubular.com/r/fT0gmX6VJX + # http://rubular.com/r/duOrD4i3wb + # http://rubular.com/r/sbAMHFrOx1 + # + + regex = /(\A|\s)#{File.expand_path('.')}(#{File::SEPARATOR}|\s|\Z)/ + + line = line.sub(regex, "\\1.\\2") line = line.sub(/\A([^:]+:\d+)$/, '\\1') return nil if line == '-e:1' line diff --git a/spec/rspec/core/metadata_spec.rb b/spec/rspec/core/metadata_spec.rb index 1a29691480..39e3df6bc6 100644 --- a/spec/rspec/core/metadata_spec.rb +++ b/spec/rspec/core/metadata_spec.rb @@ -24,6 +24,13 @@ module Core end end + it 'should not transform directories beginning with the same prefix' do + #E.g. /foo/bar_baz is not relative to /foo/bar !! + + similar_directory = "#{File.expand_path(".")}_similar" + expect(Metadata.relative_path(similar_directory)).to eq similar_directory + end + end context "when created" do From 3429dec3a47d9a23f68abdbbbed5a13b91f52687 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Oct 2014 09:59:37 -0700 Subject: [PATCH 049/275] Changelog for #1730. --- Changelog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Changelog.md b/Changelog.md index 2b12619570..ed769e88f1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,15 @@ Enhancements: * Add `RSpec.clear_examples` as a clear way to reset examples in between spec runs, whilst retaining user configuration. (Alexey Fedorov, #1706) +### 3.1.7 Development +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.6...3-1-maintenance) + +Bug Fixes: + +* Fix `Metadata.relative_path` so that for a current directory of + `/foo/bar`, `/foo/bar_1` is not wrongly converted to `._1`. + (Akos Vandra, #1730) + ### 3.1.6 / 2014-10-08 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.5...v3.1.6) From e8e4541394a411a6c2575253be91095e908d070e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 9 Oct 2014 22:06:35 +1100 Subject: [PATCH 050/275] demonstate issue with wonky constants on 1.9.2 --- spec/rspec/core/example_group_constants_spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 spec/rspec/core/example_group_constants_spec.rb diff --git a/spec/rspec/core/example_group_constants_spec.rb b/spec/rspec/core/example_group_constants_spec.rb new file mode 100644 index 0000000000..0519b05733 --- /dev/null +++ b/spec/rspec/core/example_group_constants_spec.rb @@ -0,0 +1,16 @@ +# encoding: utf-8 +require 'spec_helper' + +RSpec.describe "::RSpec::Core::ExampleGroup" do + context "does not cause problems when users reference a top level constant of the same name" do + file_in_outer_group = File + example { expect(File).to eq ::File } + example { expect(file_in_outer_group).to be(::File) } + + describe "File" do + file_in_inner_group = File + example { expect(File).to eq ::File } + example { expect(file_in_inner_group).to be(::File) } + end + end +end From a0acf2efe070f020950fffec1feb5285b91a387a Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 10 Oct 2014 08:14:53 +1100 Subject: [PATCH 051/275] deal with 1.9.2 issue by appending a _ on all constant names --- lib/rspec/core/example_group.rb | 10 +++++++++ spec/rspec/core/example_group_spec.rb | 31 +++++++++++++++++++-------- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index dcab883c38..73f4dda16a 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -639,6 +639,16 @@ def self.base_name_for(group) name end + if RUBY_VERSION == '1.9.2' + class << self + alias _base_name_for base_name_for + def base_name_for(group) + _base_name_for(group) + '_' + end + end + private_class_method :_base_name_for + end + def self.disambiguate(name, const_scope) return name unless const_defined_on?(const_scope, name) diff --git a/spec/rspec/core/example_group_spec.rb b/spec/rspec/core/example_group_spec.rb index 81edc3a51e..49ce5c3722 100644 --- a/spec/rspec/core/example_group_spec.rb +++ b/spec/rspec/core/example_group_spec.rb @@ -39,11 +39,24 @@ def metadata_hash(*args) end end - RSpec::Matchers.define :have_class_const do |class_name| - match do |group| - group.name == "RSpec::ExampleGroups::#{class_name}" && - group == class_name.split('::').inject(RSpec::ExampleGroups) do |mod, name| - mod.const_get(name) + if RUBY_VERSION == "1.9.2" + RSpec::Matchers.define :have_class_const do |class_name| + match do |group| + class_name.gsub!('::','_::') + class_name << '_' + group.name == "RSpec::ExampleGroups::#{class_name}" && + group == class_name.split('::').inject(RSpec::ExampleGroups) do |mod, name| + mod.const_get(name) + end + end + end + else + RSpec::Matchers.define :have_class_const do |class_name, _| + match do |group| + group.name == "RSpec::ExampleGroups::#{class_name}" && + group == class_name.split('::').inject(RSpec::ExampleGroups) do |mod, name| + mod.const_get(name) + end end end end @@ -88,7 +101,7 @@ def metadata_hash(*args) expect(child).to have_class_const("SomeParentGroup::Hash") end - it 'disambiguates name collisions by appending a number' do + it 'disambiguates name collisions by appending a number', :unless => RUBY_VERSION == '1.9.2' do groups = 10.times.map { ExampleGroup.describe("Collision") } expect(groups[0]).to have_class_const("Collision") expect(groups[1]).to have_class_const("Collision_2") @@ -116,7 +129,7 @@ def metadata_hash(*args) }.to raise_error(/ExampleGroups::CallingAnUndefinedMethod/) end - it 'does not have problems with example groups named "Core"' do + it 'does not have problems with example groups named "Core"', :unless => RUBY_VERSION == '1.9.2' do ExampleGroup.describe("Core") expect(defined?(::RSpec::ExampleGroups::Core)).to be_truthy @@ -126,7 +139,7 @@ def metadata_hash(*args) expect(group).to have_class_const("AnotherGroup") end - it 'does not have problems with example groups named "RSpec"' do + it 'does not have problems with example groups named "RSpec"', :unless => RUBY_VERSION == '1.9.2' do ExampleGroup.describe("RSpec") expect(defined?(::RSpec::ExampleGroups::RSpec)).to be_truthy @@ -1605,7 +1618,7 @@ def foo; end }.to raise_error(/not allowed/) end - describe 'inspect output' do + describe 'inspect output', :unless => RUBY_VERSION == '1.9.2' do context 'when there is no inspect output provided' do it "uses '(no description provided)' instead" do expect(ExampleGroup.new.inspect).to eq('#') From bd154c654b8ba6d92f13c46854934d6605142609 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 10 Oct 2014 08:58:04 +1100 Subject: [PATCH 052/275] changelog for #1737 [skip ci] --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index ed769e88f1..293cd25509 100644 --- a/Changelog.md +++ b/Changelog.md @@ -17,6 +17,9 @@ Bug Fixes: * Fix `Metadata.relative_path` so that for a current directory of `/foo/bar`, `/foo/bar_1` is not wrongly converted to `._1`. (Akos Vandra, #1730) +* Prevent constant lookup mistakenly finding `RSpec::ExampleGroups` generated + constants on 1.9.2 by appending a trailing `_` to the generated names. + (Jon Rowe, #1737) ### 3.1.6 / 2014-10-08 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.5...v3.1.6) From 99cf33f7d99ff645671d5ed3cea02aed2e7a9efb Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Oct 2014 21:26:13 -0700 Subject: [PATCH 053/275] Cut string allocations when defining examples. My changes here cut string allocations when examples are defined by 70%. --- .../allocations/1_group_1000_examples.rb | 63 +++++++++++++++++++ benchmarks/allocations/helper.rb | 17 +++++ lib/rspec/core/metadata.rb | 30 +++++---- 3 files changed, 94 insertions(+), 16 deletions(-) create mode 100644 benchmarks/allocations/1_group_1000_examples.rb create mode 100644 benchmarks/allocations/helper.rb diff --git a/benchmarks/allocations/1_group_1000_examples.rb b/benchmarks/allocations/1_group_1000_examples.rb new file mode 100644 index 0000000000..c058abd86a --- /dev/null +++ b/benchmarks/allocations/1_group_1000_examples.rb @@ -0,0 +1,63 @@ +require_relative "helper" + +benchmark_allocations do + RSpec.describe "one example group" do + 1000.times do |i| + example "example #{i}" do + end + end + end +end + +__END__ + +Original stats: + + class_plus count +---------------------------------------- ----- +String 22046 +Hash 3006 +Array 3002 +Proc 2007 +RubyVM::Env 2007 +Array 1013 +Regexp 1001 +RSpec::Core::Example::ExecutionResult 1001 +Array 1001 +RSpec::Core::Example 1000 +RSpec::Core::Metadata::ExampleHash 1000 +RSpec::Core::Hooks::HookCollection 6 +MatchData 4 +Array 2 +Module 2 +RSpec::Core::Metadata::ExampleGroupHash 1 +RSpec::Core::Hooks::AroundHookCollection 1 +Class 1 +Array 1 +RSpec::Core::Hooks::HookCollections 1 +Array 1 + +After my fixes: + + class_plus count +---------------------------------------- ----- +String 6030 +Hash 3006 +Array 3002 +RubyVM::Env 2007 +Proc 2007 +Array 1013 +RSpec::Core::Example::ExecutionResult 1001 +Array 1001 +RSpec::Core::Metadata::ExampleHash 1000 +RSpec::Core::Example 1000 +RSpec::Core::Hooks::HookCollection 6 +MatchData 4 +Module 2 +Array 2 +RSpec::Core::Hooks::HookCollections 1 +Array 1 +RSpec::Core::Hooks::AroundHookCollection 1 +RSpec::Core::Metadata::ExampleGroupHash 1 +Class 1 +Array 1 diff --git a/benchmarks/allocations/helper.rb b/benchmarks/allocations/helper.rb new file mode 100644 index 0000000000..d8421ea154 --- /dev/null +++ b/benchmarks/allocations/helper.rb @@ -0,0 +1,17 @@ +$LOAD_PATH.unshift File.expand_path("../../../lib", __FILE__) +require 'rspec/core' +require 'allocation_stats' + +def benchmark_allocations + stats = AllocationStats.new(burn: 1).trace do + yield + end + + columns = if ENV['DETAIL'] + [:sourcefile, :sourceline, :class_plus] + else + [:class_plus] + end + + puts stats.allocations(alias_paths: true).group_by(*columns).from_pwd.sort_by_size.to_text +end diff --git a/lib/rspec/core/metadata.rb b/lib/rspec/core/metadata.rb index 51ca81292a..8d103c1489 100644 --- a/lib/rspec/core/metadata.rb +++ b/lib/rspec/core/metadata.rb @@ -25,25 +25,23 @@ module Core # @see Configuration#filter_run_including # @see Configuration#filter_run_excluding module Metadata + # Matches strings either at the beginning of the input or prefixed with a whitespace, + # containing the current path, either postfixed with the separator, or at the end of the string. + # Match groups are the character before and the character after the string if any. + # + # http://rubular.com/r/fT0gmX6VJX + # http://rubular.com/r/duOrD4i3wb + # http://rubular.com/r/sbAMHFrOx1 + RELATIVE_PATH_REGEX = /(\A|\s)#{File.expand_path('.')}(#{File::SEPARATOR}|\s|\Z)/ + # @api private # # @param line [String] current code line # @return [String] relative path to line def self.relative_path(line) - # Matches strings either at the beginning of the input or prefixed with a whitespace, - # containing the current path, either postfixed with the separator, or at the end of the string. - # Match groups are the character before and the character after the string if any. - # - # http://rubular.com/r/fT0gmX6VJX - # http://rubular.com/r/duOrD4i3wb - # http://rubular.com/r/sbAMHFrOx1 - # - - regex = /(\A|\s)#{File.expand_path('.')}(#{File::SEPARATOR}|\s|\Z)/ - - line = line.sub(regex, "\\1.\\2") - line = line.sub(/\A([^:]+:\d+)$/, '\\1') - return nil if line == '-e:1' + line = line.sub(RELATIVE_PATH_REGEX, "\\1.\\2".freeze) + line = line.sub(/\A([^:]+:\d+)$/, '\\1'.freeze) + return nil if line == '-e:1'.freeze line rescue SecurityError nil @@ -139,9 +137,9 @@ def file_path_and_line_number_from(backtrace) def description_separator(parent_part, child_part) if parent_part.is_a?(Module) && child_part =~ /^(#|::|\.)/ - '' + ''.freeze else - ' ' + ' '.freeze end end From 241ff4bb83e00d7d3edb950ab3458cc27f8aa09d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Oct 2014 23:04:20 -0700 Subject: [PATCH 054/275] Cut string allocations when running examples. This cuts the string allocations by ~50%. --- benchmarks/allocations/helper.rb | 4 +- .../running_1_group_1000_examples.rb | 60 +++++++++++++++++++ lib/rspec/core/configuration.rb | 22 ++++--- 3 files changed, 77 insertions(+), 9 deletions(-) create mode 100644 benchmarks/allocations/running_1_group_1000_examples.rb diff --git a/benchmarks/allocations/helper.rb b/benchmarks/allocations/helper.rb index d8421ea154..6932ae5ed2 100644 --- a/benchmarks/allocations/helper.rb +++ b/benchmarks/allocations/helper.rb @@ -2,8 +2,8 @@ require 'rspec/core' require 'allocation_stats' -def benchmark_allocations - stats = AllocationStats.new(burn: 1).trace do +def benchmark_allocations(burn: 1) + stats = AllocationStats.new(burn: burn).trace do yield end diff --git a/benchmarks/allocations/running_1_group_1000_examples.rb b/benchmarks/allocations/running_1_group_1000_examples.rb new file mode 100644 index 0000000000..506394a3be --- /dev/null +++ b/benchmarks/allocations/running_1_group_1000_examples.rb @@ -0,0 +1,60 @@ +require_relative "helper" + +RSpec.describe "one example group" do + 1000.times do |i| + example "example #{i}" do + end + end +end + +benchmark_allocations(burn: 0) do + RSpec::Core::Runner.run([]) +end + +__END__ + +Original allocations: + + class_plus count +----------------------------------------------------------------------------------------------- ----- +String 35018 +Array 14030 +Array 12075 +RSpec::Core::Hooks::HookCollection 4000 +Time 2002 +Array 2000 +RSpec::Core::Hooks::AroundHookCollection 2000 +RSpec::Core::Notifications::ExampleNotification 2000 +Proc 1065 +RubyVM::Env 1018 +Array 1006 +Array 1005 +RSpec::ExampleGroups::OneExampleGroup 1002 +Array 67 +RubyVM::InstructionSequence 41 +Hash 35 +Set 30 +File 6 + +After my change: + + class_plus count +----------------------------------------------------------------------------------------------- ----- +Array 14030 +String 12967 +Array 12075 +RSpec::Core::Hooks::HookCollection 4000 +Time 2002 +RSpec::Core::Notifications::ExampleNotification 2000 +Array 2000 +RSpec::Core::Hooks::AroundHookCollection 2000 +Proc 1065 +RubyVM::Env 1018 +Array 1006 +Array 1005 +RSpec::ExampleGroups::OneExampleGroup 1002 +Array 67 +RubyVM::InstructionSequence 41 +Hash 35 +Set 30 +File 6 diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index 32e1db5022..cc8def104c 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -32,15 +32,23 @@ module Core class Configuration include RSpec::Core::Hooks + # Module that holds `attr_reader` declarations. It's in a separate + # module to allow us to override those methods and use `super`. + # @private + Readers = Module.new + include Readers + # @private class MustBeConfiguredBeforeExampleGroupsError < StandardError; end # @private def self.define_reader(name) - define_method(name) do - variable = instance_variable_defined?("@#{name}") ? instance_variable_get("@#{name}") : nil - value_for(name, variable) + Readers.class_eval do + remove_method name if method_defined?(name) + attr_reader name end + + define_method(name) { value_for(name) { super() } } end # @private @@ -627,7 +635,7 @@ def full_backtrace=(true_or_false) # @see color_enabled? # @return [Boolean] def color - value_for(:color, @color) + value_for(:color) { @color } end # Check if color is enabled for a particular output @@ -760,7 +768,7 @@ def reporter # Defaults `profile_examples` to 10 examples when `@profile_examples` is `true`. # def profile_examples - profile = value_for(:profile_examples, @profile_examples) + profile = value_for(:profile_examples) { @profile_examples } if profile && !profile.is_a?(Integer) 10 else @@ -1402,8 +1410,8 @@ def command $0.split(File::SEPARATOR).last end - def value_for(key, default=nil) - @preferred_options.key?(key) ? @preferred_options[key] : default + def value_for(key) + @preferred_options.fetch(key) { yield } end def assert_no_example_groups_defined(config_option) From 65523aad3ff4fff2717ca2a7cce7a6336c19d8af Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 9 Oct 2014 22:04:55 -0700 Subject: [PATCH 055/275] Add allocation benchmark for many groups w/ 1 example. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I’m not sure how to address the high number of allocations here yet. --- .../allocations/1000_groups_1_example.rb | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 benchmarks/allocations/1000_groups_1_example.rb diff --git a/benchmarks/allocations/1000_groups_1_example.rb b/benchmarks/allocations/1000_groups_1_example.rb new file mode 100644 index 0000000000..f2d7cc02d0 --- /dev/null +++ b/benchmarks/allocations/1000_groups_1_example.rb @@ -0,0 +1,37 @@ +require_relative "helper" + +benchmark_allocations do + 1000.times do |i| + RSpec.describe "group #{i}" do + it "has one example" do + end + end + end +end + +__END__ + +Original allocations: + + class_plus count +---------------------------------------- ----- +String 32000 +Array 14000 +RubyVM::Env 9000 +Hash 9000 +Proc 9000 +RSpec::Core::Hooks::HookCollection 6000 +Array 5000 +MatchData 3000 +RSpec::Core::Example::ExecutionResult 2000 +Array 2000 +Array 2000 +Module 2000 +Class 1000 +Array 1000 +RSpec::Core::Metadata::ExampleGroupHash 1000 +RSpec::Core::Metadata::ExampleHash 1000 +RSpec::Core::Example 1000 +Array 1000 +RSpec::Core::Hooks::AroundHookCollection 1000 +RSpec::Core::Hooks::HookCollections 1000 From 75c54e71044fa663e4437db54e181c5f40f027d9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 10 Oct 2014 10:31:29 -0700 Subject: [PATCH 056/275] Add changelog for #1738. [ci skip] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 293cd25509..5c05fa4b47 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,8 @@ Enhancements: set. (Ben Snape, #1704) * Add `RSpec.clear_examples` as a clear way to reset examples in between spec runs, whilst retaining user configuration. (Alexey Fedorov, #1706) +* Reduce string allocations when defining and running examples by 70% + and 50% respectively. (Myron Marston, #1738) ### 3.1.7 Development [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.6...3-1-maintenance) From 55d699ab9b9143bb9ea01a78a0f15cc7fb6ec08a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 10 Oct 2014 20:05:08 -0700 Subject: [PATCH 057/275] Fix `:pending` metadata to always work. It got processed immediately by `it`, which means that if it got set later (e.g. via `define_derived_metadata`) it would not take effect. --- Changelog.md | 4 +++ lib/rspec/core/example.rb | 1 + lib/rspec/core/example_group.rb | 36 +------------------------ spec/rspec/core/pending_example_spec.rb | 34 +++++++++++++++++++++++ 4 files changed, 40 insertions(+), 35 deletions(-) diff --git a/Changelog.md b/Changelog.md index 5c05fa4b47..ae0773b104 100644 --- a/Changelog.md +++ b/Changelog.md @@ -22,6 +22,10 @@ Bug Fixes: * Prevent constant lookup mistakenly finding `RSpec::ExampleGroups` generated constants on 1.9.2 by appending a trailing `_` to the generated names. (Jon Rowe, #1737) +* Fix bug in `:pending` metadata. If it got set in any way besides passing + it as part of the metadata literal passed to `it` (such as by using + `define_derived_metadata`), it did not have the desired effect, + instead marking the example as `:passed`. (Myron Marston, #1739) ### 3.1.6 / 2014-10-08 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.5...v3.1.6) diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 8de2abdafa..ed33677d6b 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -149,6 +149,7 @@ def run(example_group_instance, reporter) RSpec.current_example = self start(reporter) + Pending.mark_pending!(self, pending) if pending? begin if skipped? diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index 73f4dda16a..e2293b961d 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -109,21 +109,11 @@ def described_class def self.define_example_method(name, extra_options={}) define_singleton_method(name) do |*all_args, &block| desc, *args = *all_args + options = Metadata.build_hash_from(args) options.update(:skip => RSpec::Core::Pending::NOT_YET_IMPLEMENTED) unless block options.update(extra_options) - # Metadata inheritance normally happens in `Example#initialize`, - # but for `:pending` specifically we need it earlier. - pending_metadata = options[:pending] || metadata[:pending] - - if pending_metadata - options, block = ExampleGroup.pending_metadata_and_block_for( - options.merge(:pending => pending_metadata), - block - ) - end - examples << RSpec::Core::Example.new(self, desc, options, block) examples.last end @@ -547,30 +537,6 @@ def self.set_ivars(instance, ivars) ivars.each { |name, value| instance.instance_variable_set(name, value) } end - # @private - def self.pending_metadata_and_block_for(options, block) - if String === options[:pending] - reason = options[:pending] - else - options[:pending] = true - reason = RSpec::Core::Pending::NO_REASON_GIVEN - end - - # Assign :caller so that the callback's source_location isn't used - # as the example location. - options[:caller] ||= Metadata.backtrace_from(block) - - # This will fail if no block is provided, which is effectively the - # same as failing the example so it will be marked correctly as - # pending. - callback = Proc.new do - pending(reason) - instance_exec(&block) - end - - return options, callback - end - if RUBY_VERSION.to_f < 1.9 # @private def self.instance_variables_for_example(group) diff --git a/spec/rspec/core/pending_example_spec.rb b/spec/rspec/core/pending_example_spec.rb index 35da981bcd..99c19e27b1 100644 --- a/spec/rspec/core/pending_example_spec.rb +++ b/spec/rspec/core/pending_example_spec.rb @@ -50,6 +50,40 @@ end end + context "made pending with `define_derived_metadata`" do + before do + RSpec.configure do |config| + config.define_derived_metadata(:not_ready) do |meta| + meta[:pending] ||= "Not ready" + end + end + end + + it 'has a pending result if there is an error' do + group = RSpec.describe "group" do + example "something", :not_ready do + boom + end + end + + group.run + example = group.examples.first + expect(example).to be_pending_with("Not ready") + end + + it 'fails if there is no error' do + group = RSpec.describe "group" do + example "something", :not_ready do + end + end + + group.run + example = group.examples.first + expect(example.execution_result.status).to be(:failed) + expect(example.execution_result.exception.message).to include("Expected example to fail") + end + end + context "with no block" do it "is listed as pending with 'Not yet implemented'" do group = RSpec::Core::ExampleGroup.describe('group') do From 0ecabf9f37eefb42782811da063d12e95bf6c345 Mon Sep 17 00:00:00 2001 From: ChaYoung You Date: Sat, 11 Oct 2014 10:57:51 +0900 Subject: [PATCH 058/275] Keep comment line length to 80 --- lib/rspec/core.rb | 4 +- lib/rspec/core/backport_random.rb | 9 +- lib/rspec/core/configuration.rb | 86 +++++++++------- lib/rspec/core/configuration_options.rb | 16 +-- lib/rspec/core/example.rb | 32 +++--- lib/rspec/core/example_group.rb | 38 ++++--- lib/rspec/core/filter_manager.rb | 4 +- lib/rspec/core/formatters.rb | 9 +- lib/rspec/core/formatters/base_formatter.rb | 9 +- .../core/formatters/base_text_formatter.rb | 13 +-- lib/rspec/core/formatters/helpers.rb | 8 +- lib/rspec/core/formatters/html_formatter.rb | 9 +- .../core/formatters/profile_formatter.rb | 4 +- lib/rspec/core/formatters/protocol.rb | 43 ++++---- .../core/formatters/snippet_extractor.rb | 21 ++-- lib/rspec/core/hooks.rb | 99 ++++++++++--------- lib/rspec/core/memoized_helpers.rb | 22 ++--- lib/rspec/core/metadata.rb | 22 +++-- lib/rspec/core/notifications.rb | 36 ++++--- lib/rspec/core/option_parser.rb | 9 +- lib/rspec/core/pending.rb | 12 ++- .../project_initializer/spec/spec_helper.rb | 12 ++- lib/rspec/core/rake_task.rb | 38 ++++--- lib/rspec/core/reporter.rb | 13 ++- lib/rspec/core/runner.rb | 3 +- lib/rspec/core/shared_example_group.rb | 16 +-- 26 files changed, 337 insertions(+), 250 deletions(-) diff --git a/lib/rspec/core.rb b/lib/rspec/core.rb index 706aa03f7a..4c226678f4 100644 --- a/lib/rspec/core.rb +++ b/lib/rspec/core.rb @@ -67,8 +67,8 @@ def self.clear_examples configuration.reset_filters end - # Returns the global [Configuration](RSpec/Core/Configuration) object. While you - # _can_ use this method to access the configuration, the more common + # Returns the global [Configuration](RSpec/Core/Configuration) object. While + # you _can_ use this method to access the configuration, the more common # convention is to use [RSpec.configure](RSpec#configure-class_method). # # @example diff --git a/lib/rspec/core/backport_random.rb b/lib/rspec/core/backport_random.rb index 90046d54e4..f9b857a3a6 100644 --- a/lib/rspec/core/backport_random.rb +++ b/lib/rspec/core/backport_random.rb @@ -92,7 +92,8 @@ def next_state end # Seed must be either an Integer (only the first 32 bits will be used) - # or an Array of Integers (of which only the first 32 bits will be used) + # or an Array of Integers (of which only the first 32 bits will be + # used) # # No conversion or type checking is done at this level def seed=(seed) @@ -202,7 +203,8 @@ def marshal_load(ary) end end - # Convert an Integer seed of arbitrary size to either a single 32 bit integer, or an Array of 32 bit integers + # Convert an Integer seed of arbitrary size to either a single 32 bit + # integer, or an Array of 32 bit integers def self.convert_seed(seed) seed = seed.abs long_values = [] @@ -211,7 +213,8 @@ def self.convert_seed(seed) seed >>= 32 end until seed == 0 - long_values.pop if long_values[-1] == 1 && long_values.size > 1 # Done to allow any kind of sequence of integers + # Done to allow any kind of sequence of integers + long_values.pop if long_values[-1] == 1 && long_values.size > 1 long_values.size > 1 ? long_values : long_values.first end diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index cc8def104c..e073d4b6fd 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -98,8 +98,8 @@ def self.add_read_only_setting(name, opts={}) # `"spec"`). Allows you to just type `rspec` instead of `rspec spec` to # run all the examples in the `spec` directory. # - # Note: Other scripts invoking `rspec` indirectly will ignore this - # setting. + # @note Other scripts invoking `rspec` indirectly will ignore this + # setting. add_setting :default_path # @macro add_setting @@ -172,7 +172,8 @@ def deprecation_stream=(value) define_reader :requires # @macro define_reader - # Returns dirs that have been prepended to the load path by the `-I` command line option + # Returns dirs that have been prepended to the load path by the `-I` + # command line option. define_reader :libs # @macro add_setting @@ -219,49 +220,50 @@ def exclude_pattern=(value) add_setting :profile_examples # @macro add_setting - # Run all examples if none match the configured filters (default: `false`). + # Run all examples if none match the configured filters + # (default: `false`). add_setting :run_all_when_everything_filtered # @macro add_setting # Color to use to indicate success. # @param color [Symbol] defaults to `:green` but can be set to one of the - # following: `[:black, :white, :red, :green, :yellow, - # :blue, :magenta, :cyan]` + # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta, + # :cyan]` add_setting :success_color # @macro add_setting # Color to use to print pending examples. # @param color [Symbol] defaults to `:yellow` but can be set to one of the - # following: `[:black, :white, :red, :green, :yellow, - # :blue, :magenta, :cyan]` + # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta, + # :cyan]` add_setting :pending_color # @macro add_setting # Color to use to indicate failure. # @param color [Symbol] defaults to `:red` but can be set to one of the - # following: `[:black, :white, :red, :green, :yellow, - # :blue, :magenta, :cyan]` + # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta, + # :cyan]` add_setting :failure_color # @macro add_setting # The default output color. # @param color [Symbol] defaults to `:white` but can be set to one of the - # following:`[:black, :white, :red, :green, :yellow, - # :blue, :magenta, :cyan]` + # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta, + # :cyan]` add_setting :default_color # @macro add_setting # Color used when a pending example is fixed. # @param color [Symbol] defaults to `:blue` but can be set to one of the - # following: `[:black, :white, :red, :green, :yellow, - # :blue, :magenta, :cyan]` + # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta, + # :cyan]` add_setting :fixed_color # @macro add_setting # Color used to print details. # @param color [Symbol] defaults to `:cyan` but can be set to one of the - # following: `[:black, :white, :red, :green, :yellow, - # :blue, :magenta, :cyan]` + # following: `[:black, :white, :red, :green, :yellow, :blue, :magenta, + # :cyan]` add_setting :detail_color # Deprecated. This config option was added in RSpec 2 to pave the way @@ -364,8 +366,8 @@ def reset_filters # # @option opts [Symbol] :alias_with # - # Use `:alias_with` to alias the setter, getter, and predicate to another - # name, or names: + # Use `:alias_with` to alias the setter, getter, and predicate to + # another name, or names: # # add_setting(:foo, :alias_with => :bar) # add_setting(:foo, :alias_with => [:bar, :baz]) @@ -507,8 +509,8 @@ def filter_gems_from_backtrace(*gem_names) # teardown_mocks_for_rspec # - called after verify_mocks_for_rspec (even if there are errors) # - # If the module responds to `configuration` and `mock_with` receives a block, - # it will yield the configuration object to the block e.g. + # If the module responds to `configuration` and `mock_with` receives a + # block, it will yield the configuration object to the block e.g. # # config.mock_with OtherMockFrameworkAdapter do |mod_config| # mod_config.custom_setting = true @@ -765,8 +767,8 @@ def reporter # @api private # - # Defaults `profile_examples` to 10 examples when `@profile_examples` is `true`. - # + # Defaults `profile_examples` to 10 examples when `@profile_examples` is + # `true`. def profile_examples profile = value_for(:profile_examples) { @profile_examples } if profile && !profile.is_a?(Integer) @@ -911,11 +913,12 @@ def alias_it_behaves_like_to(new_name, report_label='') # config.filter_run_including :foo => lambda {|v| v == 'bar'} # config.filter_run_including :foo => lambda {|v,m| m[:foo] == 'bar'} # - # # given a proc with an arity of 1, the lambda is passed the value related to the key, e.g. + # # given a proc with an arity of 1, the lambda is passed the value + # # related to the key, e.g. # config.filter_run_including :foo => lambda {|v| v == 'bar'} # - # # given a proc with an arity of 2, the lambda is passed the value related to the key, - # # and the metadata itself e.g. + # # given a proc with an arity of 2, the lambda is passed the value + # # related to the key, and the metadata itself e.g. # config.filter_run_including :foo => lambda {|v,m| m[:foo] == 'bar'} # # filter_run_including :foo # same as filter_run_including :foo => true @@ -970,11 +973,12 @@ def inclusion_filter # config.filter_run_excluding :foo => lambda {|v| v == 'bar'} # config.filter_run_excluding :foo => lambda {|v,m| m[:foo] == 'bar'} # - # # given a proc with an arity of 1, the lambda is passed the value related to the key, e.g. + # # given a proc with an arity of 1, the lambda is passed the value + # # related to the key, e.g. # config.filter_run_excluding :foo => lambda {|v| v == 'bar'} # - # # given a proc with an arity of 2, the lambda is passed the value related to the key, - # # and the metadata itself e.g. + # # given a proc with an arity of 2, the lambda is passed the value + # # related to the key, and the metadata itself e.g. # config.filter_run_excluding :foo => lambda {|v,m| m[:foo] == 'bar'} # # filter_run_excluding :foo # same as filter_run_excluding :foo => true @@ -1136,7 +1140,8 @@ def load_spec_files # Formats the docstring output using the block provided. # # @example - # # This will strip the descriptions of both examples and example groups. + # # This will strip the descriptions of both examples and example + # # groups. # RSpec.configure do |config| # config.format_docstrings { |s| s.strip } # end @@ -1181,7 +1186,8 @@ def self.delegate_to_ordering_manager(*methods) # @macro delegate_to_ordering_manager # - # Sets the default global order and, if order is `'rand:'`, also sets the seed. + # Sets the default global order and, if order is `'rand:'`, also + # sets the seed. delegate_to_ordering_manager :order= # @macro delegate_to_ordering_manager @@ -1191,8 +1197,10 @@ def self.delegate_to_ordering_manager(*methods) # # @param name [Symbol] The name of the ordering. # @yield Block that will order the given examples or example groups - # @yieldparam list [Array, Array] The examples or groups to order - # @yieldreturn [Array, Array] The re-ordered examples or groups + # @yieldparam list [Array, + # Array] The examples or groups to order + # @yieldreturn [Array, + # Array] The re-ordered examples or groups # # @example # RSpec.configure do |rspec| @@ -1319,15 +1327,17 @@ def disable_monkey_patching! # Defines a callback that can assign derived metadata values. # - # @param filters [Array, Hash] metadata filters that determine which example - # or group metadata hashes the callback will be triggered for. If none are given, - # the callback will be run against the metadata hashes of all groups and examples. - # @yieldparam metadata [Hash] original metadata hash from an example or group. Mutate this in - # your block as needed. + # @param filters [Array, Hash] metadata filters that determine + # which example or group metadata hashes the callback will be triggered + # for. If none are given, the callback will be run against the metadata + # hashes of all groups and examples. + # @yieldparam metadata [Hash] original metadata hash from an example or + # group. Mutate this in your block as needed. # # @example # RSpec.configure do |config| - # # Tag all groups and examples in the spec/unit directory with :type => :unit + # # Tag all groups and examples in the spec/unit directory with + # # :type => :unit # config.define_derived_metadata(:file_path => %r{/spec/unit/}) do |metadata| # metadata[:type] = :unit # end diff --git a/lib/rspec/core/configuration_options.rb b/lib/rspec/core/configuration_options.rb index d6c1c0ecdd..3e70bdb642 100644 --- a/lib/rspec/core/configuration_options.rb +++ b/lib/rspec/core/configuration_options.rb @@ -84,15 +84,17 @@ def order(keys) # set before it. :default_path, - # These must be set before `requires` to support checking `config.files_to_run` - # from within `spec_helper.rb` when a `-rspec_helper` option is used. + # These must be set before `requires` to support checking + # `config.files_to_run` from within `spec_helper.rb` when a + # `-rspec_helper` option is used. :files_or_directories_to_run, :pattern, :exclude_pattern, - # In general, we want to require the specified files as early as possible. - # The `--require` option is specifically intended to allow early requires. - # For later requires, they can just put the require in their spec files, but - # `--require` provides a unique opportunity for users to instruct RSpec to - # load an extension file early for maximum flexibility. + # In general, we want to require the specified files as early as + # possible. The `--require` option is specifically intended to allow + # early requires. For later requires, they can just put the require in + # their spec files, but `--require` provides a unique opportunity for + # users to instruct RSpec to load an extension file early for maximum + # flexibility. :requires ] diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 8de2abdafa..25f7300db5 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -51,7 +51,8 @@ def self.delegate_to_metadata(key) # @return [ExecutionResult] represents the result of running this example. delegate_to_metadata :execution_result - # @return [String] the relative path to the file where this example was defined. + # @return [String] the relative path to the file where this example was + # defined. delegate_to_metadata :file_path # @return [String] the full description (including the docstrings of # all parent example groups). @@ -59,19 +60,19 @@ def self.delegate_to_metadata(key) # @return [String] the exact source location of this example in a form # like `./path/to/spec.rb:17` delegate_to_metadata :location - # @return [Boolean] flag that indicates that the example is not expected to pass. - # It will be run and will either have a pending result (if a failure occurs) - # or a failed result (if no failure occurs). + # @return [Boolean] flag that indicates that the example is not expected + # to pass. It will be run and will either have a pending result (if a + # failure occurs) or a failed result (if no failure occurs). delegate_to_metadata :pending # @return [Boolean] flag that will cause the example to not run. # The {ExecutionResult} status will be `:pending`. delegate_to_metadata :skip # Returns the string submitted to `example` or its aliases (e.g. - # `specify`, `it`, etc). If no string is submitted (e.g. `it { is_expected.to - # do_something }`) it returns the message generated by the matcher if - # there is one, otherwise returns a message including the location of the - # example. + # `specify`, `it`, etc). If no string is submitted (e.g. + # `it { is_expected.to do_something }`) it returns the message generated + # by the matcher if there is one, otherwise returns a message including + # the location of the example. def description description = if metadata[:description].to_s.empty? "example at #{location}" @@ -114,10 +115,14 @@ def inspect_output attr_accessor :clock # Creates a new instance of Example. - # @param example_group_class [Class] the subclass of ExampleGroup in which this Example is declared - # @param description [String] the String passed to the `it` method (or alias) - # @param user_metadata [Hash] additional args passed to `it` to be used as metadata - # @param example_block [Proc] the block of code that represents the example + # @param example_group_class [Class] the subclass of ExampleGroup in which + # this Example is declared + # @param description [String] the String passed to the `it` method (or + # alias) + # @param user_metadata [Hash] additional args passed to `it` to be used as + # metadata + # @param example_block [Proc] the block of code that represents the + # example # @api private def initialize(example_group_class, description, user_metadata, example_block=nil) @example_group_class = example_group_class @@ -231,7 +236,8 @@ def call(*args, &block) end alias run call - # Provides a wrapped proc that will update our `executed?` state when executed. + # Provides a wrapped proc that will update our `executed?` state when + # executed. def to_proc method(:call).to_proc end diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index 73f4dda16a..bb6a3117f5 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -17,13 +17,13 @@ module Core # # Example group bodies (e.g. `describe` or `context` blocks) are evaluated # in the context of a new subclass of ExampleGroup. Individual examples are - # evaluated in the context of an instance of the specific ExampleGroup subclass - # to which they belong. + # evaluated in the context of an instance of the specific ExampleGroup + # subclass to which they belong. # # Besides the class methods defined here, there are other interesting macros - # defined in {Hooks}, {MemoizedHelpers::ClassMethods} and {SharedExampleGroup}. - # There are additional instance methods available to your examples defined in - # {MemoizedHelpers} and {Pending}. + # defined in {Hooks}, {MemoizedHelpers::ClassMethods} and + # {SharedExampleGroup}. There are additional instance methods available to + # your examples defined in {MemoizedHelpers} and {Pending}. class ExampleGroup extend Hooks @@ -180,7 +180,8 @@ def self.define_example_method(name, extra_options={}) # @macro [attach] alias_example_group_to # @!scope class # @param name [String] The example group doc string - # @param metadata [Hash] Additional metadata to attach to the example group + # @param metadata [Hash] Additional metadata to attach to the example + # group # @yield The example group definition # # Generates a subclass of this example group which inherits @@ -240,8 +241,8 @@ def self.define_example_group_method(name, metadata={}) define_example_group_method :example_group - # An alias of `example_group`. Generally used when grouping - # examples by a thing you are describing (e.g. an object, class or method). + # An alias of `example_group`. Generally used when grouping examples by a + # thing you are describing (e.g. an object, class or method). # @see example_group define_example_group_method :describe @@ -297,7 +298,8 @@ def self.define_nested_shared_group_method(new_name, report_label="it should beh # Includes shared content mapped to `name` directly in the group in which # it is declared, as opposed to `it_behaves_like`, which creates a nested - # group. If given a block, that block is also eval'd in the current context. + # group. If given a block, that block is also eval'd in the current + # context. # # @see SharedExampleGroup def self.include_context(name, *args, &block) @@ -306,7 +308,8 @@ def self.include_context(name, *args, &block) # Includes shared content mapped to `name` directly in the group in which # it is declared, as opposed to `it_behaves_like`, which creates a nested - # group. If given a block, that block is also eval'd in the current context. + # group. If given a block, that block is also eval'd in the current + # context. # # @see SharedExampleGroup def self.include_examples(name, *args, &block) @@ -348,10 +351,11 @@ def self.set_it_up(*args, &example_group_block) # Ruby 1.9 has a bug that can lead to infinite recursion and a # SystemStackError if you include a module in a superclass after # including it in a subclass: https://gist.github.com/845896 - # To prevent this, we must include any modules in RSpec::Core::ExampleGroup - # before users create example groups and have a chance to include - # the same module in a subclass of RSpec::Core::ExampleGroup. - # So we need to configure example groups here. + # To prevent this, we must include any modules in + # RSpec::Core::ExampleGroup before users create example groups and have + # a chance to include the same module in a subclass of + # RSpec::Core::ExampleGroup. So we need to configure example groups + # here. ensure_example_groups_are_configured description = args.shift @@ -604,7 +608,8 @@ def self.metadata # @private # - # Namespace for the example group subclasses generated by top-level `describe`. + # Namespace for the example group subclasses generated by top-level + # `describe`. module ExampleGroups extend Support::RecursiveConstMethods @@ -652,7 +657,8 @@ def base_name_for(group) def self.disambiguate(name, const_scope) return name unless const_defined_on?(const_scope, name) - # Add a trailing number if needed to disambiguate from an existing constant. + # Add a trailing number if needed to disambiguate from an existing + # constant. name << "_2" name.next! while const_defined_on?(const_scope, name) name diff --git a/lib/rspec/core/filter_manager.rb b/lib/rspec/core/filter_manager.rb index 406369b214..92411ca1c7 100644 --- a/lib/rspec/core/filter_manager.rb +++ b/lib/rspec/core/filter_manager.rb @@ -20,8 +20,8 @@ module Core # rspec -t awesome:true # rspec -t awesome # - # Prefixing the tag names with `~` negates the tags, thus excluding this group with - # any of: + # Prefixing the tag names with `~` negates the tags, thus excluding this + # group with any of: # # rspec --tag ~awesome:true # rspec --tag ~awesome diff --git a/lib/rspec/core/formatters.rb b/lib/rspec/core/formatters.rb index bc46a990e0..84e850c627 100644 --- a/lib/rspec/core/formatters.rb +++ b/lib/rspec/core/formatters.rb @@ -1,8 +1,10 @@ RSpec::Support.require_rspec_support "directory_maker" # ## Built-in Formatters # -# * progress (default) - prints dots for passing examples, `F` for failures, `*` for pending -# * documentation - prints the docstrings passed to `describe` and `it` methods (and their aliases) +# * progress (default) - prints dots for passing examples, `F` for failures, `*` +# for pending +# * documentation - prints the docstrings passed to `describe` and `it` methods +# (and their aliases) # * html # * json - useful for archiving data for subsequent analysis # @@ -72,7 +74,8 @@ module RSpec::Core::Formatters # Register the formatter class # @param formatter_class [Class] formatter class to register - # @param notifications [Symbol, ...] one or more notifications to be registered to the specified formatter + # @param notifications [Symbol, ...] one or more notifications to be + # registered to the specified formatter # # @see RSpec::Core::Formatters::BaseFormatter def self.register(formatter_class, *notifications) diff --git a/lib/rspec/core/formatters/base_formatter.rb b/lib/rspec/core/formatters/base_formatter.rb index 55c52b110e..2f302e01d8 100644 --- a/lib/rspec/core/formatters/base_formatter.rb +++ b/lib/rspec/core/formatters/base_formatter.rb @@ -4,13 +4,15 @@ module RSpec module Core module Formatters - # RSpec's built-in formatters are all subclasses of RSpec::Core::Formatters::BaseTextFormatter. + # RSpec's built-in formatters are all subclasses of + # RSpec::Core::Formatters::BaseTextFormatter. # # @see RSpec::Core::Formatters::BaseTextFormatter # @see RSpec::Core::Reporter # @see RSpec::Core::Formatters::Protocol class BaseFormatter - # all formatters inheriting from this formatter will receive these notifications + # all formatters inheriting from this formatter will receive these + # notifications Formatters.register self, :start, :example_group_started, :close attr_accessor :example_group attr_reader :output @@ -34,7 +36,8 @@ def start(notification) # @api public # - # @param notification [GroupNotification] containing example_group subclass of `RSpec::Core::ExampleGroup` + # @param notification [GroupNotification] containing example_group + # subclass of `RSpec::Core::ExampleGroup` # @see RSpec::Core::Formatters::Protocol#example_group_started def example_group_started(notification) @example_group = notification.group diff --git a/lib/rspec/core/formatters/base_text_formatter.rb b/lib/rspec/core/formatters/base_text_formatter.rb index 6a858b1e44..5cc0ea0201 100644 --- a/lib/rspec/core/formatters/base_text_formatter.rb +++ b/lib/rspec/core/formatters/base_text_formatter.rb @@ -4,8 +4,9 @@ module RSpec module Core module Formatters - # Base for all of RSpec's built-in formatters. See RSpec::Core::Formatters::BaseFormatter - # to learn more about all of the methods called by the reporter. + # Base for all of RSpec's built-in formatters. See + # RSpec::Core::Formatters::BaseFormatter to learn more about all of the + # methods called by the reporter. # # @see RSpec::Core::Formatters::BaseFormatter # @see RSpec::Core::Reporter @@ -37,11 +38,11 @@ def dump_failures(notification) # @method dump_summary # @api public # - # This method is invoked after the dumping of examples and failures. Each parameter - # is assigned to a corresponding attribute. + # This method is invoked after the dumping of examples and failures. + # Each parameter is assigned to a corresponding attribute. # - # @param summary [SummaryNotification] containing duration, example_count, - # failure_count and pending_count + # @param summary [SummaryNotification] containing duration, + # example_count, failure_count and pending_count def dump_summary(summary) output.puts summary.fully_formatted end diff --git a/lib/rspec/core/formatters/helpers.rb b/lib/rspec/core/formatters/helpers.rb index 9eb867bda0..c314e72792 100644 --- a/lib/rspec/core/formatters/helpers.rb +++ b/lib/rspec/core/formatters/helpers.rb @@ -39,8 +39,9 @@ def self.format_duration(duration) # @api private # - # Formats seconds to have 5 digits of precision with trailing zeros removed if the number - # is less than 1 or with 2 digits of precision if the number is greater than zero. + # Formats seconds to have 5 digits of precision with trailing zeros + # removed if the number is less than 1 or with 2 digits of precision if + # the number is greater than zero. # # @param float [Float] # @return [String] formatted float @@ -50,7 +51,8 @@ def self.format_duration(duration) # format_seconds(0.020000) #=> "0.02" # format_seconds(1.00000000001) #=> "1" # - # The precision used is set in {Helpers::SUB_SECOND_PRECISION} and {Helpers::DEFAULT_PRECISION}. + # The precision used is set in {Helpers::SUB_SECOND_PRECISION} and + # {Helpers::DEFAULT_PRECISION}. # # @see #strip_trailing_zeroes def self.format_seconds(float, precision=nil) diff --git a/lib/rspec/core/formatters/html_formatter.rb b/lib/rspec/core/formatters/html_formatter.rb index 5eeb23c128..907449c668 100644 --- a/lib/rspec/core/formatters/html_formatter.rb +++ b/lib/rspec/core/formatters/html_formatter.rb @@ -111,7 +111,8 @@ def dump_summary(summary) private - # If these methods are declared with attr_reader Ruby will issue a warning because they are private + # If these methods are declared with attr_reader Ruby will issue a + # warning because they are private # rubocop:disable Style/TrivialAccessors # The number of the currently running example_group @@ -133,9 +134,9 @@ def percent_done result end - # Override this method if you wish to output extra HTML for a failed spec. For example, you - # could output links to images or other files produced during the specs. - # + # Override this method if you wish to output extra HTML for a failed + # spec. For example, you could output links to images or other files + # produced during the specs. def extra_failure_content(failure) RSpec::Support.require_rspec_core "formatters/snippet_extractor" backtrace = failure.exception.backtrace.map { |line| RSpec.configuration.backtrace_formatter.backtrace_line(line) } diff --git a/lib/rspec/core/formatters/profile_formatter.rb b/lib/rspec/core/formatters/profile_formatter.rb index af57b44f83..a6624037e6 100644 --- a/lib/rspec/core/formatters/profile_formatter.rb +++ b/lib/rspec/core/formatters/profile_formatter.rb @@ -21,8 +21,8 @@ def initialize(output) # This method is invoked after the dumping the summary if profiling is # enabled. # - # @param profile [ProfileNotification] containing duration, slowest_examples - # and slowest_example_groups + # @param profile [ProfileNotification] containing duration, + # slowest_examples and slowest_example_groups def dump_profile(profile) dump_profile_slowest_examples(profile) dump_profile_slowest_example_groups(profile) diff --git a/lib/rspec/core/formatters/protocol.rb b/lib/rspec/core/formatters/protocol.rb index 69ac65dba6..ea4b3b3c0c 100644 --- a/lib/rspec/core/formatters/protocol.rb +++ b/lib/rspec/core/formatters/protocol.rb @@ -39,12 +39,14 @@ class Protocol # @api public # @group Group Notifications # - # This method is invoked at the beginning of the execution of each example group. + # This method is invoked at the beginning of the execution of each + # example group. # # The next method to be invoked after this is {#example_passed}, # {#example_pending}, or {#example_group_finished}. # - # @param notification [GroupNotification] containing example_group subclass of `RSpec::Core::ExampleGroup` + # @param notification [GroupNotification] containing example_group + # subclass of `RSpec::Core::ExampleGroup` # @method example_group_finished # @api public @@ -52,7 +54,8 @@ class Protocol # # Invoked at the end of the execution of each example group. # - # @param notification [GroupNotification] containing example_group subclass of `RSpec::Core::ExampleGroup` + # @param notification [GroupNotification] containing example_group + # subclass of `RSpec::Core::ExampleGroup` # @method example_started # @api public @@ -60,7 +63,8 @@ class Protocol # # Invoked at the beginning of the execution of each example. # - # @param notification [ExampleNotification] containing example subclass of `RSpec::Core::Example` + # @param notification [ExampleNotification] containing example subclass + # of `RSpec::Core::Example` # @method example_passed # @api public @@ -68,7 +72,8 @@ class Protocol # # Invoked when an example passes. # - # @param notification [ExampleNotification] containing example subclass of `RSpec::Core::Example` + # @param notification [ExampleNotification] containing example subclass + # of `RSpec::Core::Example` # @method example_pending # @api public @@ -76,7 +81,8 @@ class Protocol # # Invoked when an example is pending. # - # @param notification [ExampleNotification] containing example subclass of `RSpec::Core::Example` + # @param notification [ExampleNotification] containing example subclass + # of `RSpec::Core::Example` # @method example_failed # @api public @@ -84,7 +90,8 @@ class Protocol # # Invoked when an example fails. # - # @param notification [ExampleNotification] containing example subclass of `RSpec::Core::Example` + # @param notification [ExampleNotification] containing example subclass + # of `RSpec::Core::Example` # @method message # @api public @@ -98,7 +105,8 @@ class Protocol # @api public # @group Suite Notifications # - # Invoked after all examples have executed, before dumping post-run reports. + # Invoked after all examples have executed, before dumping post-run + # reports. # # @param notification [NullNotification] @@ -106,9 +114,10 @@ class Protocol # @api public # @group Suite Notifications # - # This method is invoked after all of the examples have executed. The next method - # to be invoked after this one is {#dump_failures} - # (BaseTextFormatter then calls {#dump_failure} once for each failed example.) + # This method is invoked after all of the examples have executed. The + # next method to be invoked after this one is {#dump_failures} + # (BaseTextFormatter then calls {#dump_failure} once for each failed + # example.) # # @param notification [NullNotification] @@ -124,11 +133,11 @@ class Protocol # @api public # @group Suite Notifications # - # This method is invoked after the dumping of examples and failures. Each parameter - # is assigned to a corresponding attribute. + # This method is invoked after the dumping of examples and failures. + # Each parameter is assigned to a corresponding attribute. # - # @param summary [SummaryNotification] containing duration, example_count, - # failure_count and pending_count + # @param summary [SummaryNotification] containing duration, + # example_count, failure_count and pending_count # @method dump_profile # @api public @@ -137,8 +146,8 @@ class Protocol # This method is invoked after the dumping the summary if profiling is # enabled. # - # @param profile [ProfileNotification] containing duration, slowest_examples - # and slowest_example_groups + # @param profile [ProfileNotification] containing duration, + # slowest_examples and slowest_example_groups # @method dump_pending # @api public diff --git a/lib/rspec/core/formatters/snippet_extractor.rb b/lib/rspec/core/formatters/snippet_extractor.rb index dea7ccaa5c..2546acae69 100644 --- a/lib/rspec/core/formatters/snippet_extractor.rb +++ b/lib/rspec/core/formatters/snippet_extractor.rb @@ -3,7 +3,8 @@ module Core module Formatters # @api private # - # Extracts code snippets by looking at the backtrace of the passed error and applies synax highlighting and line numbers using html. + # Extracts code snippets by looking at the backtrace of the passed error + # and applies synax highlighting and line numbers using html. class SnippetExtractor # @private class NullConverter @@ -33,7 +34,8 @@ def convert(code) # Extract lines of code corresponding to a backtrace. # # @param backtrace [String] the backtrace from a test failure - # @return [String] highlighted code snippet indicating where the test failure occured + # @return [String] highlighted code snippet indicating where the test + # failure occured # # @see #post_process def snippet(backtrace) @@ -46,7 +48,8 @@ def snippet(backtrace) # # Create a snippet from a line of code. # - # @param error_line [String] file name with line number (i.e. 'foo_spec.rb:12') + # @param error_line [String] file name with line number (i.e. + # 'foo_spec.rb:12') # @return [String] lines around the target line within the file # # @see #lines_around @@ -62,11 +65,13 @@ def snippet_for(error_line) # @api private # - # Extract lines of code centered around a particular line within a source file. + # Extract lines of code centered around a particular line within a + # source file. # # @param file [String] filename # @param line [Fixnum] line number - # @return [String] lines around the target line within the file (2 above and 1 below). + # @return [String] lines around the target line within the file (2 above + # and 1 below). def lines_around(file, line) if File.file?(file) lines = File.read(file).split("\n") @@ -84,9 +89,11 @@ def lines_around(file, line) # @api private # - # Adds line numbers to all lines and highlights the line where the failure occurred using html `span` tags. + # Adds line numbers to all lines and highlights the line where the + # failure occurred using html `span` tags. # - # @param highlighted [String] syntax-highlighted snippet surrounding the offending line of code + # @param highlighted [String] syntax-highlighted snippet surrounding the + # offending line of code # @param offending_line [Fixnum] line where failure occured # @return [String] completed snippet def post_process(highlighted, offending_line) diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index 226b24ff49..88f90e40cb 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -11,18 +11,20 @@ module Hooks # # @overload before(&block) # @overload before(scope, &block) - # @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to `:example`) + # @param scope [Symbol] `:example`, `:context`, or `:suite` + # (defaults to `:example`) # @overload before(scope, conditions, &block) - # @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to `:example`) + # @param scope [Symbol] `:example`, `:context`, or `:suite` + # (defaults to `:example`) # @param conditions [Hash] # constrains this hook to examples matching these conditions e.g. - # `before(:example, :ui => true) { ... }` will only run with examples or - # groups declared with `:ui => true`. + # `before(:example, :ui => true) { ... }` will only run with examples + # or groups declared with `:ui => true`. # @overload before(conditions, &block) # @param conditions [Hash] # constrains this hook to examples matching these conditions e.g. - # `before(:example, :ui => true) { ... }` will only run with examples or - # groups declared with `:ui => true`. + # `before(:example, :ui => true) { ... }` will only run with examples + # or groups declared with `:ui => true`. # # @see #after # @see #around @@ -32,22 +34,22 @@ module Hooks # @see Configuration # # Declare a block of code to be run before each example (using `:example`) - # or once before any example (using `:context`). These are usually declared - # directly in the {ExampleGroup} to which they apply, but they can also - # be shared across multiple groups. + # or once before any example (using `:context`). These are usually + # declared directly in the {ExampleGroup} to which they apply, but they + # can also be shared across multiple groups. # # You can also use `before(:suite)` to run a block of code before any # example groups are run. This should be declared in {RSpec.configure} # - # Instance variables declared in `before(:example)` or `before(:context)` are - # accessible within each example. + # Instance variables declared in `before(:example)` or `before(:context)` + # are accessible within each example. # # ### Order # # `before` hooks are stored in three scopes, which are run in order: - # `:suite`, `:context`, and `:example`. They can also be declared in several - # different places: `RSpec.configure`, a parent group, the current group. - # They are run in the following order: + # `:suite`, `:context`, and `:example`. They can also be declared in + # several different places: `RSpec.configure`, a parent group, the current + # group. They are run in the following order: # # before(:suite) # declared in RSpec.configure # before(:context) # declared in RSpec.configure @@ -62,9 +64,9 @@ module Hooks # # ### Conditions # - # When you add a conditions hash to `before(:example)` or `before(:context)`, - # RSpec will only apply that hook to groups or examples that match the - # conditions. e.g. + # When you add a conditions hash to `before(:example)` or + # `before(:context)`, RSpec will only apply that hook to groups or + # examples that match the conditions. e.g. # # RSpec.configure do |config| # config.before(:example, :authorized => true) do @@ -115,20 +117,21 @@ module Hooks # # #### context # - # `before(:context)` is run in an example that is generated to provide group - # context for the block. + # `before(:context)` is run in an example that is generated to provide + # group context for the block. # # #### instance variables # - # Instance variables declared in `before(:context)` are shared across all the - # examples in the group. This means that each example can change the + # Instance variables declared in `before(:context)` are shared across all + # the examples in the group. This means that each example can change the # state of a shared object, resulting in an ordering dependency that can # make it difficult to reason about failures. # # #### unsupported rspec constructs # # RSpec has several constructs that reset state between each example - # automatically. These are not intended for use from within `before(:context)`: + # automatically. These are not intended for use from within + # `before(:context)`: # # * `let` declarations # * `subject` declarations @@ -138,13 +141,13 @@ module Hooks # # Mock object frameworks and database transaction managers (like # ActiveRecord) are typically designed around the idea of setting up - # before an example, running that one example, and then tearing down. - # This means that mocks and stubs can (sometimes) be declared in - # `before(:context)`, but get torn down before the first real example is ever - # run. + # before an example, running that one example, and then tearing down. This + # means that mocks and stubs can (sometimes) be declared in + # `before(:context)`, but get torn down before the first real example is + # ever run. # - # You _can_ create database-backed model objects in a `before(:context)` in - # rspec-rails, but it will not be wrapped in a transaction for you, so + # You _can_ create database-backed model objects in a `before(:context)` + # in rspec-rails, but it will not be wrapped in a transaction for you, so # you are on your own to clean up in an `after(:context)` block. # # @example before(:example) declared in an {ExampleGroup} @@ -198,18 +201,20 @@ def prepend_before(*args, &block) # @api public # @overload after(&block) # @overload after(scope, &block) - # @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to `:example`) + # @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to + # `:example`) # @overload after(scope, conditions, &block) - # @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to `:example`) + # @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to + # `:example`) # @param conditions [Hash] # constrains this hook to examples matching these conditions e.g. - # `after(:example, :ui => true) { ... }` will only run with examples or - # groups declared with `:ui => true`. + # `after(:example, :ui => true) { ... }` will only run with examples + # or groups declared with `:ui => true`. # @overload after(conditions, &block) # @param conditions [Hash] # constrains this hook to examples matching these conditions e.g. - # `after(:example, :ui => true) { ... }` will only run with examples or - # groups declared with `:ui => true`. + # `after(:example, :ui => true) { ... }` will only run with examples + # or groups declared with `:ui => true`. # # @see #before # @see #around @@ -218,9 +223,9 @@ def prepend_before(*args, &block) # @see SharedExampleGroup # @see Configuration # - # Declare a block of code to be run after each example (using `:example`) or - # once after all examples n the context (using `:context`). See {#before} for - # more information about ordering. + # Declare a block of code to be run after each example (using `:example`) + # or once after all examples n the context (using `:context`). See + # {#before} for more information about ordering. # # ### Exceptions # @@ -232,9 +237,9 @@ def prepend_before(*args, &block) # ### Order # # `after` hooks are stored in three scopes, which are run in order: - # `:example`, `:context`, and `:suite`. They can also be declared in several - # different places: `RSpec.configure`, a parent group, the current group. - # They are run in the following order: + # `:example`, `:context`, and `:suite`. They can also be declared in + # several different places: `RSpec.configure`, a parent group, the current + # group. They are run in the following order: # # after(:example) # declared in the current group # after(:example) # declared in a parent group @@ -274,15 +279,13 @@ def append_after(*args, &block) # @param scope [Symbol] `:example` (defaults to `:example`) # present for syntax parity with `before` and `after`, but # `:example`/`:each` is the only supported value. - # @param conditions [Hash] - # constrains this hook to examples matching these conditions e.g. - # `around(:example, :ui => true) { ... }` will only run with examples or - # groups declared with `:ui => true`. + # @param conditions [Hash] constrains this hook to examples matching + # these conditions e.g. `around(:example, :ui => true) { ... }` will + # only run with examples or groups declared with `:ui => true`. # @overload around(conditions, &block) - # @param conditions [Hash] - # constrains this hook to examples matching these conditions e.g. - # `around(:example, :ui => true) { ... }` will only run with examples or - # groups declared with `:ui => true`. + # @param conditions [Hash] constrains this hook to examples matching + # these conditions e.g. `around(:example, :ui => true) { ... }` will + # only run with examples or groups declared with `:ui => true`. # # @yield [Example] the example to run # diff --git a/lib/rspec/core/memoized_helpers.rb b/lib/rspec/core/memoized_helpers.rb index 035f76cceb..c96a8760ad 100644 --- a/lib/rspec/core/memoized_helpers.rb +++ b/lib/rspec/core/memoized_helpers.rb @@ -44,10 +44,10 @@ module MemoizedHelpers # it { should be_eligible_to_vote } # end # - # @note Because `subject` is designed to create state that is reset between - # each example, and `before(:context)` is designed to setup state that is - # shared across _all_ examples in an example group, `subject` is _not_ - # intended to be used in a `before(:context)` hook. + # @note Because `subject` is designed to create state that is reset + # between each example, and `before(:context)` is designed to setup + # state that is shared across _all_ examples in an example group, + # `subject` is _not_ intended to be used in a `before(:context)` hook. # # @see #should # @see #should_not @@ -211,8 +211,8 @@ module ClassMethods # though we have yet to see this in practice. You've been warned. # # @note Because `let` is designed to create state that is reset between - # each example, and `before(:context)` is designed to setup state that is - # shared across _all_ examples in an example group, `let` is _not_ + # each example, and `before(:context)` is designed to setup state that + # is shared across _all_ examples in an example group, `let` is _not_ # intended to be used in a `before(:context)` hook. # # @example @@ -302,8 +302,8 @@ def let!(name, &block) end # Declares a `subject` for an example group which can then be wrapped - # with `expect` using `is_expected` to make it the target of an expectation - # in a concise, one-line example. + # with `expect` using `is_expected` to make it the target of an + # expectation in a concise, one-line example. # # Given a `name`, defines a method with that name which returns the # `subject`. This lets you declare the subject once and access it @@ -348,9 +348,9 @@ def subject(name=nil, &block) end end - # Just like `subject`, except the block is invoked by an implicit `before` - # hook. This serves a dual purpose of setting up state and providing a - # memoized reference to that state. + # Just like `subject`, except the block is invoked by an implicit + # `before` hook. This serves a dual purpose of setting up state and + # providing a memoized reference to that state. # # @example # diff --git a/lib/rspec/core/metadata.rb b/lib/rspec/core/metadata.rb index 8d103c1489..6d5e7d22dd 100644 --- a/lib/rspec/core/metadata.rb +++ b/lib/rspec/core/metadata.rb @@ -25,9 +25,10 @@ module Core # @see Configuration#filter_run_including # @see Configuration#filter_run_excluding module Metadata - # Matches strings either at the beginning of the input or prefixed with a whitespace, - # containing the current path, either postfixed with the separator, or at the end of the string. - # Match groups are the character before and the character after the string if any. + # Matches strings either at the beginning of the input or prefixed with a + # whitespace, containing the current path, either postfixed with the + # separator, or at the end of the string. Match groups are the character + # before and the character after the string if any. # # http://rubular.com/r/fT0gmX6VJX # http://rubular.com/r/duOrD4i3wb @@ -224,16 +225,18 @@ def self.backwards_compatibility_default_proc(&example_group_selector) Proc.new do |hash, key| case key when :example_group - # We commonly get here when rspec-core is applying a previously configured - # filter rule, such as when a gem configures: + # We commonly get here when rspec-core is applying a previously + # configured filter rule, such as when a gem configures: # # RSpec.configure do |c| # c.include MyGemHelpers, :example_group => { :file_path => /spec\/my_gem_specs/ } # end # - # It's confusing for a user to get a deprecation at this point in the code, so instead - # we issue a deprecation from the config APIs that take a metadata hash, and MetadataFilter - # sets this thread local to silence the warning here since it would be so confusing. + # It's confusing for a user to get a deprecation at this point in + # the code, so instead we issue a deprecation from the config APIs + # that take a metadata hash, and MetadataFilter sets this thread + # local to silence the warning here since it would be so + # confusing. unless RSpec.thread_local_metadata[:silence_metadata_example_group_deprecations] RSpec.deprecate("The `:example_group` key in an example group's metadata hash", :replacement => "the example group's hash directly for the " \ @@ -414,7 +417,8 @@ def attr_accessor(*names) # `metadata[:example_group][:described_class]` when you use # anonymous controller specs) such that changes are written # back to the top-level metadata hash. - # * Exposes the parent group metadata as `[:example_group][:example_group]`. + # * Exposes the parent group metadata as + # `[:example_group][:example_group]`. class LegacyExampleGroupHash include HashImitatable diff --git a/lib/rspec/core/notifications.rb b/lib/rspec/core/notifications.rb index c984555918..8c896c2def 100644 --- a/lib/rspec/core/notifications.rb +++ b/lib/rspec/core/notifications.rb @@ -86,8 +86,8 @@ def failure_notifications @failed_notifications ||= format_examples(failed_examples) end - # @return [String] The list of failed examples, fully formatted in the way that - # RSpec's built-in formatters emit. + # @return [String] The list of failed examples, fully formatted in the way + # that RSpec's built-in formatters emit. def fully_formatted_failed_examples(colorizer=::RSpec::Core::Formatters::ConsoleCodes) formatted = "\nFailures:\n" @@ -98,8 +98,8 @@ def fully_formatted_failed_examples(colorizer=::RSpec::Core::Formatters::Console formatted end - # @return [String] The list of pending examples, fully formatted in the way that - # RSpec's built-in formatters emit. + # @return [String] The list of pending examples, fully formatted in the + # way that RSpec's built-in formatters emit. def fully_formatted_pending_examples(colorizer=::RSpec::Core::Formatters::ConsoleCodes) formatted = "\nPending:\n" @@ -315,9 +315,10 @@ def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes) end end - # The `GroupNotification` represents notifications sent by the reporter which - # contain information about the currently running (or soon to be) example group - # It is used by formatters to access information about that group. + # The `GroupNotification` represents notifications sent by the reporter + # which contain information about the currently running (or soon to be) + # example group. It is used by formatters to access information about that + # group. # # @example # def example_group_started(notification) @@ -425,13 +426,14 @@ def colorized_rerun_commands(colorizer=::RSpec::Core::Formatters::ConsoleCodes) end.join("\n") end - # @return [String] a formatted version of the time it took to run the suite + # @return [String] a formatted version of the time it took to run the + # suite def formatted_duration Formatters::Helpers.format_duration(duration) end - # @return [String] a formatted version of the time it took to boot RSpec and - # load the spec files + # @return [String] a formatted version of the time it took to boot RSpec + # and load the spec files def formatted_load_time Formatters::Helpers.format_duration(load_time) end @@ -451,8 +453,8 @@ def fully_formatted(colorizer=::RSpec::Core::Formatters::ConsoleCodes) end end - # The `ProfileNotification` holds information about the results of running - # a test suite when profiling is enabled. It is used by formatters to provide + # The `ProfileNotification` holds information about the results of running a + # test suite when profiling is enabled. It is used by formatters to provide # information at the end of the test run for profiling information. # # @attr duration [Float] the time taken (in seconds) to run the suite @@ -517,13 +519,15 @@ def calculate_slowest_groups end # The `DeprecationNotification` is issued by the reporter when a deprecated - # part of RSpec is encountered. It represents information about the deprecated - # call site. + # part of RSpec is encountered. It represents information about the + # deprecated call site. # # @attr message [String] A custom message about the deprecation - # @attr deprecated [String] A custom message about the deprecation (alias of message) + # @attr deprecated [String] A custom message about the deprecation (alias of + # message) # @attr replacement [String] An optional replacement for the deprecation - # @attr call_site [String] An optional call site from which the deprecation was issued + # @attr call_site [String] An optional call site from which the deprecation + # was issued DeprecationNotification = Struct.new(:deprecated, :message, :replacement, :call_site) do private_class_method :new diff --git a/lib/rspec/core/option_parser.rb b/lib/rspec/core/option_parser.rb index 7e406da01e..1aa27a0f6c 100644 --- a/lib/rspec/core/option_parser.rb +++ b/lib/rspec/core/option_parser.rb @@ -198,9 +198,12 @@ def parser(options) exit end - # these options would otherwise be confusing to users, so we forcibly prevent them from executing - # --I is too similar to -I - # -d was a shorthand for --debugger, which is removed, but now would trigger --default-path + # these options would otherwise be confusing to users, so we forcibly + # prevent them from executing + # + # * --I is too similar to -I + # * -d was a shorthand for --debugger, which is removed, but now would + # trigger --default-path invalid_options = %w[-d --I] parser.on_tail('-h', '--help', "You're looking at it.") do diff --git a/lib/rspec/core/pending.rb b/lib/rspec/core/pending.rb index 41639c0da7..47b53b7c89 100644 --- a/lib/rspec/core/pending.rb +++ b/lib/rspec/core/pending.rb @@ -1,9 +1,10 @@ module RSpec module Core - # Provides methods to mark examples as pending. These methods are available to be - # called from within any example or hook. + # Provides methods to mark examples as pending. These methods are available + # to be called from within any example or hook. module Pending - # Raised in the middle of an example to indicate that it should be marked as skipped. + # Raised in the middle of an example to indicate that it should be marked + # as skipped. class SkipDeclaredInExample < StandardError attr_reader :argument @@ -12,8 +13,9 @@ def initialize(argument) end end - # If Test::Unit is loaded, we'll use its error as baseclass, so that Test::Unit - # will report unmet RSpec expectations as failures rather than errors. + # If Test::Unit is loaded, we'll use its error as baseclass, so that + # Test::Unit will report unmet RSpec expectations as failures rather than + # errors. begin class PendingExampleFixedError < Test::Unit::AssertionFailedError; end rescue diff --git a/lib/rspec/core/project_initializer/spec/spec_helper.rb b/lib/rspec/core/project_initializer/spec/spec_helper.rb index 607474b24c..ea1578dc7c 100644 --- a/lib/rspec/core/project_initializer/spec/spec_helper.rb +++ b/lib/rspec/core/project_initializer/spec/spec_helper.rb @@ -1,14 +1,16 @@ # This file was generated by the `rspec --init` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. -# The generated `.rspec` file contains `--require spec_helper` which will cause this -# file to always be loaded, without a need to explicitly require it in any files. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. # # Given that it is always loaded, you are encouraged to keep this file as # light-weight as possible. Requiring heavyweight dependencies from this file # will add to the boot time of your test suite on EVERY test run, even for an # individual file that may not need all of that loaded. Instead, consider making # a separate helper file that requires the additional dependencies and performs -# the additional setup, and require it from the spec files that actually need it. +# the additional setup, and require it from the spec files that actually need +# it. # # The `.rspec` file also contains a few flags that are not defaults but that # users commonly want. @@ -48,8 +50,8 @@ config.filter_run :focus config.run_all_when_everything_filtered = true - # Limits the available syntax to the non-monkey patched syntax that is recommended. - # For more details, see: + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching diff --git a/lib/rspec/core/rake_task.rb b/lib/rspec/core/rake_task.rb index 5c46d05b11..e088d92289 100644 --- a/lib/rspec/core/rake_task.rb +++ b/lib/rspec/core/rake_task.rb @@ -34,7 +34,8 @@ class RakeTask < ::Rake::TaskLib # 'spec/**/*_spec.rb' attr_accessor :exclude_pattern - # Whether or not to fail Rake when an error occurs (typically when examples fail). + # Whether or not to fail Rake when an error occurs (typically when + # examples fail). # # default: # true @@ -117,24 +118,31 @@ def file_inclusion_specification elsif String === pattern && !File.exist?(pattern) "--pattern #{escape pattern}" else - # Before RSpec 3.1, we used `FileList` to get the list of matched files, and - # then pass that along to the `rspec` command. Starting with 3.1, we prefer to - # pass along the pattern as-is to the `rspec` command, for 3 reasons: + # Before RSpec 3.1, we used `FileList` to get the list of matched + # files, and then pass that along to the `rspec` command. Starting + # with 3.1, we prefer to pass along the pattern as-is to the `rspec` + # command, for 3 reasons: # - # * It's *much* less verbose to pass one `--pattern` option than a long list of files. - # * It ensures `task.pattern` and `--pattern` have the same behavior. - # * It fixes a bug, where `task.pattern = pattern_that_matches_no_files` would run - # *all* files because it would cause no pattern or file args to get passed to `rspec`, - # which causes all files to get run. + # * It's *much* less verbose to pass one `--pattern` option than a + # long list of files. + # * It ensures `task.pattern` and `--pattern` have the same + # behavior. + # * It fixes a bug, where + # `task.pattern = pattern_that_matches_no_files` would run *all* + # files because it would cause no pattern or file args to get + # passed to `rspec`, which causes all files to get run. # - # However, `FileList` is *far* more flexible than the `--pattern` option. Specifically, it - # supports individual files and directories, as well as arrays of files, directories and globs, - # as well as other `FileList` objects. + # However, `FileList` is *far* more flexible than the `--pattern` + # option. Specifically, it supports individual files and directories, + # as well as arrays of files, directories and globs, as well as other + # `FileList` objects. # - # For backwards compatibility, we have to fall back to using FileList if the user has passed - # a `pattern` option that will not work with `--pattern`. + # For backwards compatibility, we have to fall back to using FileList + # if the user has passed a `pattern` option that will not work with + # `--pattern`. # - # TODO: consider deprecating support for this and removing it in RSpec 4. + # TODO: consider deprecating support for this and removing it in + # RSpec 4. FileList[pattern].sort.map { |file| escape file } end end diff --git a/lib/rspec/core/reporter.rb b/lib/rspec/core/reporter.rb index 18744ad36d..c3e272022c 100644 --- a/lib/rspec/core/reporter.rb +++ b/lib/rspec/core/reporter.rb @@ -21,11 +21,13 @@ def reset @pending_examples = [] end - # Registers a listener to a list of notifications. The reporter will send notification of - # events to all registered listeners + # Registers a listener to a list of notifications. The reporter will send + # notification of events to all registered listeners # - # @param listener [Object] An obect that wishes to be notified of reporter events - # @param notifications [Array] Array of symbols represents the events a listener wishes to subscribe too + # @param listener [Object] An obect that wishes to be notified of reporter + # events + # @param notifications [Array] Array of symbols represents the events a + # listener wishes to subscribe too def register_listener(listener, *notifications) notifications.each do |notification| @listeners[notification.to_sym] << listener @@ -145,7 +147,8 @@ def notify(event, notification) private def mute_profile_output? - # Don't print out profiled info if there are failures and `--fail-fast` is used, it just clutters the output + # Don't print out profiled info if there are failures and `--fail-fast` is + # used, it just clutters the output !@configuration.profile_examples? || (@configuration.fail_fast? && @failed_examples.size > 0) end diff --git a/lib/rspec/core/runner.rb b/lib/rspec/core/runner.rb index 5b495ae5a9..4347f7bb12 100644 --- a/lib/rspec/core/runner.rb +++ b/lib/rspec/core/runner.rb @@ -31,7 +31,8 @@ def self.autorun @installed_at_exit = true end - # Runs the suite of specs and exits the process with an appropriate exit code. + # Runs the suite of specs and exits the process with an appropriate exit + # code. def self.invoke disable_autorun! status = run(ARGV, $stderr, $stdout).to_i diff --git a/lib/rspec/core/shared_example_group.rb b/lib/rspec/core/shared_example_group.rb index f744f24209..e0ce44589f 100644 --- a/lib/rspec/core/shared_example_group.rb +++ b/lib/rspec/core/shared_example_group.rb @@ -15,16 +15,20 @@ module Core # groups defined at the top level can be included from any example group. module SharedExampleGroup # @overload shared_examples(name, &block) - # @param name [String, Symbol, Module] identifer to use when looking up this shared group + # @param name [String, Symbol, Module] identifer to use when looking up + # this shared group # @param block The block to be eval'd # @overload shared_examples(name, metadata, &block) - # @param name [String, Symbol, Module] identifer to use when looking up this shared group - # @param metadata [Array, Hash] metadata to attach to this group; any example group - # with matching metadata will automatically include this shared example group. + # @param name [String, Symbol, Module] identifer to use when looking up + # this shared group + # @param metadata [Array, Hash] metadata to attach to this + # group; any example group with matching metadata will automatically + # include this shared example group. # @param block The block to be eval'd # @overload shared_examples(metadata, &block) - # @param metadata [Array, Hash] metadata to attach to this group; any example group - # with matching metadata will automatically include this shared example group. + # @param metadata [Array, Hash] metadata to attach to this + # group; any example group with matching metadata will automatically + # include this shared example group. # @param block The block to be eval'd # # Stores the block for later use. The block will be evaluated From 5fc29a15b9af9dc1c9815e278caca869c4769767 Mon Sep 17 00:00:00 2001 From: ChaYoung You Date: Sat, 11 Oct 2014 11:10:34 +0900 Subject: [PATCH 059/275] Fix grammer - End sentence with period - Start sentence with upper case letter - Remove extra spacing --- lib/rspec/core.rb | 4 +- lib/rspec/core/backport_random.rb | 18 ++--- lib/rspec/core/configuration.rb | 80 +++++++++---------- lib/rspec/core/dsl.rb | 8 +- lib/rspec/core/example.rb | 10 +-- lib/rspec/core/example_group.rb | 25 +++--- lib/rspec/core/filter_manager.rb | 2 +- lib/rspec/core/formatters.rb | 12 +-- lib/rspec/core/formatters/base_formatter.rb | 4 +- .../core/formatters/deprecation_formatter.rb | 2 +- lib/rspec/core/formatters/helpers.rb | 2 +- lib/rspec/core/formatters/html_formatter.rb | 6 +- lib/rspec/core/formatters/html_printer.rb | 2 +- .../core/formatters/profile_formatter.rb | 2 +- lib/rspec/core/formatters/protocol.rb | 4 +- lib/rspec/core/hooks.rb | 54 ++++++------- lib/rspec/core/memoized_helpers.rb | 10 +-- lib/rspec/core/minitest_assertions_adapter.rb | 4 +- lib/rspec/core/mocking_adapters/flexmock.rb | 2 +- lib/rspec/core/mocking_adapters/mocha.rb | 16 ++-- lib/rspec/core/notifications.rb | 6 +- lib/rspec/core/option_parser.rb | 12 +-- lib/rspec/core/pending.rb | 8 +- lib/rspec/core/project_initializer.rb | 2 +- .../project_initializer/spec/spec_helper.rb | 6 +- lib/rspec/core/rake_task.rb | 8 +- lib/rspec/core/reporter.rb | 4 +- lib/rspec/core/runner.rb | 2 +- lib/rspec/core/shared_example_group.rb | 6 +- .../core/test_unit_assertions_adapter.rb | 6 +- lib/rspec/core/warnings.rb | 4 +- lib/rspec/core/world.rb | 22 ++--- 32 files changed, 176 insertions(+), 177 deletions(-) diff --git a/lib/rspec/core.rb b/lib/rspec/core.rb index 4c226678f4..b985d55d77 100644 --- a/lib/rspec/core.rb +++ b/lib/rspec/core.rb @@ -134,7 +134,7 @@ def self.thread_local_metadata end # @private - # Internal container for global non-configuration data + # Internal container for global non-configuration data. def self.world @world ||= RSpec::Core::World.new end @@ -151,7 +151,7 @@ class << self end end - # @private path to executable file + # @private path to executable file. def self.path_to_executable @path_to_executable ||= File.expand_path('../../../exe/rspec', __FILE__) end diff --git a/lib/rspec/core/backport_random.rb b/lib/rspec/core/backport_random.rb index f9b857a3a6..1b8afaf56a 100644 --- a/lib/rspec/core/backport_random.rb +++ b/lib/rspec/core/backport_random.rb @@ -60,14 +60,14 @@ def self.coerce_to_int(obj) coerce_to(obj, Integer, :to_int) end - # Used internally to make it easy to deal with optional arguments + # Used internally to make it easy to deal with optional arguments. # (from Rubinius) Undefined = Object.new # @private class Random # @private - # An implementation of Mersenne Twister MT19937 in Ruby + # An implementation of Mersenne Twister MT19937 in Ruby. class MT19937 STATE_SIZE = 624 LAST_STATE = STATE_SIZE - 1 @@ -93,9 +93,9 @@ def next_state # Seed must be either an Integer (only the first 32 bits will be used) # or an Array of Integers (of which only the first 32 bits will be - # used) + # used). # - # No conversion or type checking is done at this level + # No conversion or type checking is done at this level. def seed=(seed) case seed when Integer @@ -130,7 +130,7 @@ def seed=(seed) end end - # Returns a random Integer from the range 0 ... (1 << 32) + # Returns a random Integer from the range 0 ... (1 << 32). def random_32_bits next_state if @last_read >= LAST_STATE @last_read += 1 @@ -147,12 +147,12 @@ def random_32_bits # No argument checking is done here either. FLOAT_FACTOR = 1.0/9007199254740992.0 - # generates a random number on [0,1) with 53-bit resolution + # Generates a random number on [0, 1) with 53-bit resolution. def random_float ((random_32_bits >> 5) * 67108864.0 + (random_32_bits >> 6)) * FLOAT_FACTOR; end - # Returns an integer within 0...upto + # Returns an integer within 0...upto. def random_integer(upto) n = upto - 1 nb_full_32 = 0 @@ -204,7 +204,7 @@ def marshal_load(ary) end # Convert an Integer seed of arbitrary size to either a single 32 bit - # integer, or an Array of 32 bit integers + # integer, or an Array of 32 bit integers. def self.convert_seed(seed) seed = seed.abs long_values = [] @@ -213,7 +213,7 @@ def self.convert_seed(seed) seed >>= 32 end until seed == 0 - # Done to allow any kind of sequence of integers + # Done to allow any kind of sequence of integers. long_values.pop if long_values[-1] == 1 && long_values.size > 1 long_values.size > 1 ? long_values : long_values.first diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index e073d4b6fd..86896f96f3 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -79,7 +79,7 @@ def self.add_setting(name, opts={}) # @private # - # As `add_setting` but only add the reader + # As `add_setting` but only add the reader. def self.add_read_only_setting(name, opts={}) raise "Use the instance add_setting method if you want to set a default" if opts.key?(:default) define_reader name @@ -168,7 +168,7 @@ def deprecation_stream=(value) add_setting :failure_exit_code # @macro define_reader - # Indicates files configured to be required + # Indicates files configured to be required. define_reader :requires # @macro define_reader @@ -181,7 +181,7 @@ def deprecation_stream=(value) # Default: `$stdout`. define_reader :output_stream - # Set the output stream for reporter + # Set the output stream for reporter. # @attr value [IO] value for output, defaults to $stdout def output_stream=(value) if @reporter && !value.equal?(@output_stream) @@ -195,20 +195,20 @@ def output_stream=(value) end # @macro define_reader - # Load files matching this pattern (default: `'**{,/*/**}/*_spec.rb'`) + # Load files matching this pattern (default: `'**{,/*/**}/*_spec.rb'`). define_reader :pattern - # Set pattern to match files to load + # Set pattern to match files to load. # @attr value [String] the filename pattern to filter spec files by def pattern=(value) update_pattern_attr :pattern, value end # @macro define_reader - # Exclude files matching this pattern + # Exclude files matching this pattern. define_reader :exclude_pattern - # Set pattern to match files to exclude + # Set pattern to match files to exclude. # @attr value [String] the filename pattern to exclude spec files by def exclude_pattern=(value) update_pattern_attr :exclude_pattern, value @@ -275,7 +275,7 @@ def treat_symbols_as_metadata_keys_with_true_values=(_value) "is deprecated, it is now set to true as default and setting it to false has no effect.") end - # Record the start time of the spec suite to measure load time + # Record the start time of the spec suite to measure load time. add_setting :start_time # @private @@ -360,7 +360,7 @@ def reset_filters # @overload add_setting(name, opts) # @option opts [Symbol] :default # - # set a default value for the generated getter and predicate methods: + # Set a default value for the generated getter and predicate methods: # # add_setting(:foo, :default => "default value") # @@ -390,7 +390,7 @@ def reset_filters # # RSpec.configuration.foo=(value) # RSpec.configuration.foo - # RSpec.configuration.foo? # returns true if foo returns anything but nil or false + # RSpec.configuration.foo? # Returns true if foo returns anything but nil or false. def add_setting(name, opts={}) default = opts.delete(:default) (class << self; self; end).class_exec do @@ -399,7 +399,7 @@ def add_setting(name, opts={}) __send__("#{name}=", default) if default end - # Returns the configured mock framework adapter module + # Returns the configured mock framework adapter module. def mock_framework if @mock_framework.nil? begin @@ -411,7 +411,7 @@ def mock_framework @mock_framework end - # Delegates to mock_framework=(framework) + # Delegates to mock_framework=(framework). def mock_framework=(framework) mock_with framework end @@ -419,19 +419,19 @@ def mock_framework=(framework) # Regexps used to exclude lines from backtraces. # # Excludes lines from ruby (and jruby) source, installed gems, anything - # in any "bin" directory, and any of the rspec libs (outside gem + # in any "bin" directory, and any of the RSpec libs (outside gem # installs) by default. # # You can modify the list via the getter, or replace it with the setter. # # To override this behaviour and display a full backtrace, use - # `--backtrace`on the command line, in a `.rspec` file, or in the + # `--backtrace` on the command line, in a `.rspec` file, or in the # `rspec_options` attribute of RSpec's rake task. def backtrace_exclusion_patterns @backtrace_formatter.exclusion_patterns end - # Set regular expressions used to exclude lines in backtrace + # Set regular expressions used to exclude lines in backtrace. # @param patterns [Regexp] set the backtrace exlusion pattern def backtrace_exclusion_patterns=(patterns) @backtrace_formatter.exclusion_patterns = patterns @@ -449,7 +449,7 @@ def backtrace_inclusion_patterns @backtrace_formatter.inclusion_patterns end - # Set regular expressions used to include lines in backtrace + # Set regular expressions used to include lines in backtrace. # @attr patterns [Regexp] set backtrace_formatter inclusion_patterns def backtrace_inclusion_patterns=(patterns) @backtrace_formatter.inclusion_patterns = patterns @@ -558,7 +558,7 @@ def expectation_frameworks @expectation_frameworks end - # Delegates to expect_with(framework) + # Delegates to expect_with(framework). def expectation_framework=(framework) expect_with(framework) end @@ -619,13 +619,13 @@ def expect_with(*frameworks) @expectation_frameworks.push(*modules) end - # Check if full backtrace is enabled + # Check if full backtrace is enabled. # @return [Boolean] is full backtrace enabled def full_backtrace? @backtrace_formatter.full_backtrace? end - # Toggle full backtrace + # Toggle full backtrace. # @attr true_or_false [Boolean] toggle full backtrace display def full_backtrace=(true_or_false) @backtrace_formatter.full_backtrace = true_or_false @@ -640,7 +640,7 @@ def color value_for(:color) { @color } end - # Check if color is enabled for a particular output + # Check if color is enabled for a particular output. # @param output [IO] an output stream to use, defaults to the current # `output_stream` # @return [Boolean] @@ -648,7 +648,7 @@ def color_enabled?(output=output_stream) output_to_tty?(output) && color end - # Toggle output color + # Toggle output color. # @attr true_or_false [Boolean] toggle color enabled def color=(true_or_false) return unless true_or_false @@ -786,7 +786,7 @@ def files_or_directories_to_run=(*files) @files_to_run = nil end - # The spec files RSpec will run + # The spec files RSpec will run. # @return [Array] specified files about to run def files_to_run @files_to_run ||= get_files_to_run(@files_or_directories_to_run) @@ -800,8 +800,8 @@ def files_to_run # @note The specific example alias below (`pending`) is already # defined for you. # @note Use with caution. This extends the language used in your - # specs, but does not add any additional documentation. We use this - # in rspec to define methods like `focus` and `xit`, but we also add + # specs, but does not add any additional documentation. We use this + # in RSpec to define methods like `focus` and `xit`, but we also add # docs for those methods. # # @example @@ -884,8 +884,8 @@ def alias_example_group_to(new_name, *args) # # ...sortability examples here # # @note Use with caution. This extends the language used in your - # specs, but does not add any additional documentation. We use this - # in rspec to define `it_should_behave_like` (for backward + # specs, but does not add any additional documentation. We use this + # in RSpec to define `it_should_behave_like` (for backward # compatibility), but we also add docs for that method. def alias_it_behaves_like_to(new_name, report_label='') RSpec::Core::ExampleGroup.define_nested_shared_group_method(new_name, report_label) @@ -902,22 +902,22 @@ def alias_it_behaves_like_to(new_name, report_label='') # or config files (e.g. `.rspec`). # # @example - # # given this declaration + # # Given this declaration. # describe "something", :foo => 'bar' do # # ... # end # - # # any of the following will include that group + # # Any of the following will include that group. # config.filter_run_including :foo => 'bar' # config.filter_run_including :foo => /^ba/ # config.filter_run_including :foo => lambda {|v| v == 'bar'} # config.filter_run_including :foo => lambda {|v,m| m[:foo] == 'bar'} # - # # given a proc with an arity of 1, the lambda is passed the value + # # Given a proc with an arity of 1, the lambda is passed the value # # related to the key, e.g. # config.filter_run_including :foo => lambda {|v| v == 'bar'} # - # # given a proc with an arity of 2, the lambda is passed the value + # # Given a proc with an arity of 2, the lambda is passed the value # # related to the key, and the metadata itself e.g. # config.filter_run_including :foo => lambda {|v,m| m[:foo] == 'bar'} # @@ -962,22 +962,22 @@ def inclusion_filter # or config files (e.g. `.rspec`). # # @example - # # given this declaration + # # Given this declaration. # describe "something", :foo => 'bar' do # # ... # end # - # # any of the following will exclude that group + # # Any of the following will exclude that group. # config.filter_run_excluding :foo => 'bar' # config.filter_run_excluding :foo => /^ba/ # config.filter_run_excluding :foo => lambda {|v| v == 'bar'} # config.filter_run_excluding :foo => lambda {|v,m| m[:foo] == 'bar'} # - # # given a proc with an arity of 1, the lambda is passed the value + # # Given a proc with an arity of 1, the lambda is passed the value # # related to the key, e.g. # config.filter_run_excluding :foo => lambda {|v| v == 'bar'} # - # # given a proc with an arity of 2, the lambda is passed the value + # # Given a proc with an arity of 2, the lambda is passed the value # # related to the key, and the metadata itself e.g. # config.filter_run_excluding :foo => lambda {|v,m| m[:foo] == 'bar'} # @@ -1007,7 +1007,7 @@ def exclusion_filter end # Tells RSpec to include `mod` in example groups. Methods defined in - # `mod` are exposed to examples (not example groups). Use `filters` to + # `mod` are exposed to examples (not example groups). Use `filters` to # constrain the groups in which to include the module. # # @example @@ -1043,8 +1043,8 @@ def include(mod, *filters) include_or_extend_modules << [:include, mod, meta] end - # Tells RSpec to extend example groups with `mod`. Methods defined in - # `mod` are exposed to example groups (not examples). Use `filters` to + # Tells RSpec to extend example groups with `mod`. Methods defined in + # `mod` are exposed to example groups (not examples). Use `filters` to # constrain the groups to extend. # # Similar to `include`, but behavior is added to example groups, which @@ -1221,7 +1221,7 @@ def self.delegate_to_ordering_manager(*methods) # @private delegate_to_ordering_manager :seed_used?, :ordering_registry - # Set Ruby warnings on or off + # Set Ruby warnings on or off. def warnings=(value) $VERBOSE = !!value end @@ -1284,7 +1284,7 @@ def raise_errors_for_deprecations! # `shared_examples_for`, etc) onto `main` and `Module`, instead # requiring you to prefix these methods with `RSpec.`. It enables # expect-only syntax for rspec-mocks and rspec-expectations. It - # simply disables monkey patching on whatever pieces of rspec + # simply disables monkey patching on whatever pieces of RSpec # the user is using. # # @note It configures rspec-mocks and rspec-expectations only @@ -1297,7 +1297,7 @@ def raise_errors_for_deprecations! # # @example # - # # It disables all monkey patching + # # It disables all monkey patching. # RSpec.configure do |config| # config.disable_monkey_patching! # end diff --git a/lib/rspec/core/dsl.rb b/lib/rspec/core/dsl.rb index bd78cc610e..8b21fac4e2 100644 --- a/lib/rspec/core/dsl.rb +++ b/lib/rspec/core/dsl.rb @@ -8,7 +8,7 @@ module Core # By default the methods `describe`, `context` and `example_group` # are exposed. These methods define a named context for one or # more examples. The given block is evaluated in the context of - # a generated subclass of {RSpec::Core::ExampleGroup} + # a generated subclass of {RSpec::Core::ExampleGroup}. # # ## Examples: # @@ -49,7 +49,7 @@ class << self attr_accessor :top_level end - # Adds the describe method to Module and the top level binding + # Adds the describe method to Module and the top level binding. # @api private def self.expose_globally! return if exposed_globally? @@ -61,7 +61,7 @@ def self.expose_globally! @exposed_globally = true end - # Removes the describe method from Module and the top level binding + # Removes the describe method from Module and the top level binding. # @api private def self.remove_globally! return unless exposed_globally? @@ -89,5 +89,5 @@ def self.change_global_dsl(&changes) end end -# capture main without an eval +# Capture main without an eval. ::RSpec::Core::DSL.top_level = self diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 25f7300db5..a5603da39c 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -44,7 +44,7 @@ module Core class Example # @private # - # Used to define methods that delegate to this example's metadata + # Used to define methods that delegate to this example's metadata. def self.delegate_to_metadata(key) define_method(key) { @metadata[key] } end @@ -95,7 +95,7 @@ def inspect_output # @attr_reader # # Returns the first exception raised in the context of running this - # example (nil if no exception is raised) + # example (nil if no exception is raised). attr_reader :exception # @attr_reader @@ -207,7 +207,7 @@ def run(example_group_instance, reporter) # if ex.metadata[:key] == :some_value && some_global_condition # raise "some message" # end - # ex.run # run delegates to ex.call + # ex.run # run delegates to ex.call. # end # end # @@ -317,7 +317,7 @@ def fail_with_exception(reporter, exception) # @private # # Used internally to skip without actually executing the example when - # skip is used in before(:context) + # skip is used in before(:context). def skip_with_exception(reporter, exception) start(reporter) Pending.mark_skipped! self, exception.argument @@ -507,7 +507,7 @@ def initialize super(AnonymousExampleGroup, "", {}) end - # To ensure we don't silence errors... + # To ensure we don't silence errors. def set_exception(exception, _context=nil) raise exception end diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index bb6a3117f5..854b16de8f 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -3,7 +3,7 @@ module RSpec module Core # ExampleGroup and {Example} are the main structural elements of - # rspec-core. Consider this example: + # rspec-core. Consider this example: # # describe Thing do # it "does something" do @@ -77,7 +77,6 @@ def self.description # end # end # - # def described_class self.class.described_class end @@ -144,25 +143,25 @@ def self.define_example_method(name, extra_options={}) # end define_example_method :specify - # Shortcut to define an example with `:focus => true` + # Shortcut to define an example with `:focus => true`. # @see example define_example_method :focus, :focus => true - # Shortcut to define an example with `:focus => true` + # Shortcut to define an example with `:focus => true`. # @see example define_example_method :fexample, :focus => true - # Shortcut to define an example with `:focus => true` + # Shortcut to define an example with `:focus => true`. # @see example define_example_method :fit, :focus => true - # Shortcut to define an example with `:focus => true` + # Shortcut to define an example with `:focus => true`. # @see example define_example_method :fspecify, :focus => true - # Shortcut to define an example with `:skip => 'Temporarily skipped with xexample'` + # Shortcut to define an example with `:skip => 'Temporarily skipped with xexample'`. # @see example define_example_method :xexample, :skip => 'Temporarily skipped with xexample' - # Shortcut to define an example with `:skip => 'Temporarily skipped with xit'` + # Shortcut to define an example with `:skip => 'Temporarily skipped with xit'`. # @see example define_example_method :xit, :skip => 'Temporarily skipped with xit' - # Shortcut to define an example with `:skip => 'Temporarily skipped with xspecify'` + # Shortcut to define an example with `:skip => 'Temporarily skipped with xspecify'`. # @see example define_example_method :xspecify, :skip => 'Temporarily skipped with xspecify' # Shortcut to define an example with `:skip => true` @@ -278,8 +277,8 @@ def self.define_example_group_method(name, metadata={}) # @see SharedExampleGroup def self.define_nested_shared_group_method(new_name, report_label="it should behave like") define_singleton_method(new_name) do |name, *args, &customization_block| - # Pass :caller so the :location metadata is set properly... - # otherwise, it'll be set to the next line because that's + # Pass :caller so the :location metadata is set properly. + # Otherwise, it'll be set to the next line because that's # the block's source_location. group = example_group("#{report_label} #{name}", :caller => caller) do find_and_eval_shared("examples", name, *args, &customization_block) @@ -452,7 +451,7 @@ def self.run_after_context_hooks(example_group_instance) end end - # Runs all the examples in this group + # Runs all the examples in this group. def self.run(reporter) if RSpec.world.wants_to_quit RSpec.world.clear_remaining_example_groups if top_level? @@ -630,7 +629,7 @@ def self.constant_scope_for(group) def self.base_name_for(group) return "Anonymous" if group.description.empty? - # convert to CamelCase + # Convert to CamelCase. name = ' ' + group.description name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) { Regexp.last_match[1].upcase } diff --git a/lib/rspec/core/filter_manager.rb b/lib/rspec/core/filter_manager.rb index 92411ca1c7..8ddd000fbe 100644 --- a/lib/rspec/core/filter_manager.rb +++ b/lib/rspec/core/filter_manager.rb @@ -31,7 +31,7 @@ module Core # ## Options files and command line overrides # # Tag declarations can be stored in `.rspec`, `~/.rspec`, or a custom - # options file. This is useful for storing defaults. For example, let's + # options file. This is useful for storing defaults. For example, let's # say you've got some slow specs that you want to suppress most of the # time. You can tag them like this: # diff --git a/lib/rspec/core/formatters.rb b/lib/rspec/core/formatters.rb index 84e850c627..4f9165f518 100644 --- a/lib/rspec/core/formatters.rb +++ b/lib/rspec/core/formatters.rb @@ -1,12 +1,12 @@ RSpec::Support.require_rspec_support "directory_maker" # ## Built-in Formatters # -# * progress (default) - prints dots for passing examples, `F` for failures, `*` -# for pending -# * documentation - prints the docstrings passed to `describe` and `it` methods -# (and their aliases) +# * progress (default) - Prints dots for passing examples, `F` for failures, `*` +# for pending. +# * documentation - Prints the docstrings passed to `describe` and `it` methods +# (and their aliases). # * html -# * json - useful for archiving data for subsequent analysis +# * json - Useful for archiving data for subsequent analysis. # # The progress formatter is the default, but you can choose any one or more of # the other formatters by passing with the `--format` (or `-f` for short) @@ -91,7 +91,7 @@ def self.register(formatter_class, *notifications) class Loader # @api private # - # Internal formatters are stored here when loaded + # Internal formatters are stored here when loaded. def self.formatters @formatters ||= {} end diff --git a/lib/rspec/core/formatters/base_formatter.rb b/lib/rspec/core/formatters/base_formatter.rb index 2f302e01d8..e4d7731de3 100644 --- a/lib/rspec/core/formatters/base_formatter.rb +++ b/lib/rspec/core/formatters/base_formatter.rb @@ -11,8 +11,8 @@ module Formatters # @see RSpec::Core::Reporter # @see RSpec::Core::Formatters::Protocol class BaseFormatter - # all formatters inheriting from this formatter will receive these - # notifications + # All formatters inheriting from this formatter will receive these + # notifications. Formatters.register self, :start, :example_group_started, :close attr_accessor :example_group attr_reader :output diff --git a/lib/rspec/core/formatters/deprecation_formatter.rb b/lib/rspec/core/formatters/deprecation_formatter.rb index 231fee830e..5a5c2562d8 100644 --- a/lib/rspec/core/formatters/deprecation_formatter.rb +++ b/lib/rspec/core/formatters/deprecation_formatter.rb @@ -216,7 +216,7 @@ def summarize(summary_stream, deprecation_count) end end - # Deprecation Error + # Deprecation Error. DeprecationError = Class.new(StandardError) end end diff --git a/lib/rspec/core/formatters/helpers.rb b/lib/rspec/core/formatters/helpers.rb index c314e72792..f6acca2a1c 100644 --- a/lib/rspec/core/formatters/helpers.rb +++ b/lib/rspec/core/formatters/helpers.rb @@ -1,7 +1,7 @@ module RSpec module Core module Formatters - # Formatters helpers + # Formatters helpers. module Helpers # @private SUB_SECOND_PRECISION = 5 diff --git a/lib/rspec/core/formatters/html_formatter.rb b/lib/rspec/core/formatters/html_formatter.rb index 907449c668..a808efbcd9 100644 --- a/lib/rspec/core/formatters/html_formatter.rb +++ b/lib/rspec/core/formatters/html_formatter.rb @@ -112,15 +112,15 @@ def dump_summary(summary) private # If these methods are declared with attr_reader Ruby will issue a - # warning because they are private + # warning because they are private. # rubocop:disable Style/TrivialAccessors - # The number of the currently running example_group + # The number of the currently running example_group. def example_group_number @example_group_number end - # The number of the currently running example (a global counter) + # The number of the currently running example (a global counter). def example_number @example_number end diff --git a/lib/rspec/core/formatters/html_printer.rb b/lib/rspec/core/formatters/html_printer.rb index 13c3802467..8e5dce2023 100644 --- a/lib/rspec/core/formatters/html_printer.rb +++ b/lib/rspec/core/formatters/html_printer.rb @@ -5,7 +5,7 @@ module Core module Formatters # @private class HtmlPrinter - include ERB::Util # for the #h method + include ERB::Util # For the #h method. def initialize(output) @output = output end diff --git a/lib/rspec/core/formatters/profile_formatter.rb b/lib/rspec/core/formatters/profile_formatter.rb index a6624037e6..bfbfd41b5e 100644 --- a/lib/rspec/core/formatters/profile_formatter.rb +++ b/lib/rspec/core/formatters/profile_formatter.rb @@ -4,7 +4,7 @@ module RSpec module Core module Formatters # @api private - # Formatter for providing profile output + # Formatter for providing profile output. class ProfileFormatter Formatters.register self, :dump_profile diff --git a/lib/rspec/core/formatters/protocol.rb b/lib/rspec/core/formatters/protocol.rb index ea4b3b3c0c..7d0355efb7 100644 --- a/lib/rspec/core/formatters/protocol.rb +++ b/lib/rspec/core/formatters/protocol.rb @@ -117,7 +117,7 @@ class Protocol # This method is invoked after all of the examples have executed. The # next method to be invoked after this one is {#dump_failures} # (BaseTextFormatter then calls {#dump_failure} once for each failed - # example.) + # example). # # @param notification [NullNotification] @@ -153,7 +153,7 @@ class Protocol # @api public # @group Suite Notifications # - # Outputs a report of pending examples. This gets invoked + # Outputs a report of pending examples. This gets invoked # after the summary if option is set to do so. # # @param notification [NullNotification] diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index 88f90e40cb..e7edae1b49 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -39,7 +39,7 @@ module Hooks # can also be shared across multiple groups. # # You can also use `before(:suite)` to run a block of code before any - # example groups are run. This should be declared in {RSpec.configure} + # example groups are run. This should be declared in {RSpec.configure}. # # Instance variables declared in `before(:example)` or `before(:context)` # are accessible within each example. @@ -51,13 +51,13 @@ module Hooks # several different places: `RSpec.configure`, a parent group, the current # group. They are run in the following order: # - # before(:suite) # declared in RSpec.configure - # before(:context) # declared in RSpec.configure - # before(:context) # declared in a parent group - # before(:context) # declared in the current group - # before(:example) # declared in RSpec.configure - # before(:example) # declared in a parent group - # before(:example) # declared in the current group + # before(:suite) # Declared in RSpec.configure. + # before(:context) # Declared in RSpec.configure. + # before(:context) # Declared in a parent group. + # before(:context) # Declared in the current group. + # before(:example) # Declared in RSpec.configure. + # before(:example) # Declared in a parent group. + # before(:example) # Declared in the current group. # # If more than one `before` is declared within any one scope, they are run # in the order in which they are declared. @@ -75,16 +75,16 @@ module Hooks # end # # describe Something, :authorized => true do - # # the before hook will run in before each example in this group + # # The before hook will run in before each example in this group. # end # # describe SomethingElse do # it "does something", :authorized => true do - # # the before hook will run before this example + # # The before hook will run before this example. # end # # it "does something else" do - # # the hook will not run before this example + # # The hook will not run before this example. # end # end # @@ -115,19 +115,19 @@ module Hooks # recommend that you avoid this as there are a number of gotchas, as well # as things that simply don't work. # - # #### context + # #### Context # # `before(:context)` is run in an example that is generated to provide # group context for the block. # - # #### instance variables + # #### Instance variables # # Instance variables declared in `before(:context)` are shared across all # the examples in the group. This means that each example can change the # state of a shared object, resulting in an ordering dependency that can # make it difficult to reason about failures. # - # #### unsupported rspec constructs + # #### Unsupported RSpec constructs # # RSpec has several constructs that reset state between each example # automatically. These are not intended for use from within @@ -158,7 +158,7 @@ module Hooks # end # # it "does something" do - # # here you can access @thing + # # Here you can access @thing. # end # end # @@ -230,7 +230,7 @@ def prepend_before(*args, &block) # ### Exceptions # # `after` hooks are guaranteed to run even when there are exceptions in - # `before` hooks or examples. When an exception is raised in an after + # `before` hooks or examples. When an exception is raised in an after # block, the exception is captured for later reporting, and subsequent # `after` blocks are run. # @@ -241,13 +241,13 @@ def prepend_before(*args, &block) # several different places: `RSpec.configure`, a parent group, the current # group. They are run in the following order: # - # after(:example) # declared in the current group - # after(:example) # declared in a parent group - # after(:example) # declared in RSpec.configure - # after(:context) # declared in the current group - # after(:context) # declared in a parent group - # after(:context) # declared in RSpec.configure - # after(:suite) # declared in RSpec.configure + # after(:example) # Declared in the current group. + # after(:example) # Declared in a parent group. + # after(:example) # Declared in RSpec.configure. + # after(:context) # Declared in the current group. + # after(:context) # Declared in a parent group. + # after(:context) # Declared in RSpec.configure. + # after(:suite) # Declared in RSpec.configure. # # This is the reverse of the order in which `before` hooks are run. # Similarly, if more than one `after` is declared within any one scope, @@ -303,13 +303,13 @@ def append_after(*args, &block) # after the example. It is your responsibility to run the example: # # around(:example) do |ex| - # # do some stuff before + # # Do some stuff before. # ex.run - # # do some stuff after + # # Do some stuff after. # end # # The yielded example aliases `run` with `call`, which lets you treat it - # like a `Proc`. This is especially handy when working with libaries + # like a `Proc`. This is especially handy when working with libaries # that manage their own setup and teardown using a block or proc syntax, # e.g. # @@ -366,7 +366,7 @@ class AfterContextHook < Hook def run(example) example.instance_exec(example, &block) rescue Exception => e - # TODO: come up with a better solution for this. + # TODO: Come up with a better solution for this. RSpec.configuration.reporter.message <<-EOS An error occurred in an `after(:context)` hook. diff --git a/lib/rspec/core/memoized_helpers.rb b/lib/rspec/core/memoized_helpers.rb index c96a8760ad..bc95a07ded 100644 --- a/lib/rspec/core/memoized_helpers.rb +++ b/lib/rspec/core/memoized_helpers.rb @@ -20,7 +20,7 @@ module MemoizedHelpers # # @example # - # # explicit declaration of subject + # # Explicit declaration of subject. # describe Person do # subject { Person.new(:birthdate => 19.years.ago) } # it "should be eligible to vote" do @@ -29,7 +29,7 @@ module MemoizedHelpers # end # end # - # # implicit subject => { Person.new } + # # Implicit subject => { Person.new }. # describe Person do # it "should be eligible to vote" do # subject.should be_eligible_to_vote @@ -37,7 +37,7 @@ module MemoizedHelpers # end # end # - # # one-liner syntax - expectation is set on the subject + # # One-liner syntax - expectation is set on the subject. # describe Person do # it { is_expected.to be_eligible_to_vote } # # or @@ -221,10 +221,10 @@ module ClassMethods # let(:thing) { Thing.new } # # it "does something" do - # # first invocation, executes block, memoizes and returns result + # # First invocation, executes block, memoizes and returns result. # thing.do_something # - # # second invocation, returns the memoized value + # # Second invocation, returns the memoized value. # thing.should be_something # end # end diff --git a/lib/rspec/core/minitest_assertions_adapter.rb b/lib/rspec/core/minitest_assertions_adapter.rb index 3509f6d356..d0f436b355 100644 --- a/lib/rspec/core/minitest_assertions_adapter.rb +++ b/lib/rspec/core/minitest_assertions_adapter.rb @@ -1,9 +1,9 @@ begin - # Only the minitest 5.x gem includes the minitest.rb and assertions.rb files + # Only the minitest 5.x gem includes the minitest.rb and assertions.rb files. require 'minitest' require 'minitest/assertions' rescue LoadError - # We must be using Ruby Core's MiniTest or the Minitest gem 4.x + # We must be using Ruby Core's MiniTest or the Minitest gem 4.x. require 'minitest/unit' Minitest = MiniTest end diff --git a/lib/rspec/core/mocking_adapters/flexmock.rb b/lib/rspec/core/mocking_adapters/flexmock.rb index 1202f866d7..91475ae7f8 100644 --- a/lib/rspec/core/mocking_adapters/flexmock.rb +++ b/lib/rspec/core/mocking_adapters/flexmock.rb @@ -15,7 +15,7 @@ def self.framework_name end def setup_mocks_for_rspec - # No setup required + # No setup required. end def verify_mocks_for_rspec diff --git a/lib/rspec/core/mocking_adapters/mocha.rb b/lib/rspec/core/mocking_adapters/mocha.rb index f2e56753d8..8caf7b6442 100644 --- a/lib/rspec/core/mocking_adapters/mocha.rb +++ b/lib/rspec/core/mocking_adapters/mocha.rb @@ -2,22 +2,22 @@ # hoops here. # # mocha >= '0.13.0': -# require 'mocha/api' is required -# require 'mocha/object' raises a LoadError b/c the file no longer exists +# require 'mocha/api' is required. +# require 'mocha/object' raises a LoadError b/c the file no longer exists. # mocha < '0.13.0', >= '0.9.7' -# require 'mocha/api' is required -# require 'mocha/object' is required +# require 'mocha/api' is required. +# require 'mocha/object' is required. # mocha < '0.9.7': -# require 'mocha/api' raises a LoadError b/c the file does not yet exist -# require 'mocha/standalone' is required -# require 'mocha/object' is required +# require 'mocha/api' raises a LoadError b/c the file does not yet exist. +# require 'mocha/standalone' is required. +# require 'mocha/object' is required. begin require 'mocha/api' begin require 'mocha/object' rescue LoadError - # Mocha >= 0.13.0 no longer contains this file nor needs it to be loaded + # Mocha >= 0.13.0 no longer contains this file nor needs it to be loaded. end rescue LoadError require 'mocha/standalone' diff --git a/lib/rspec/core/notifications.rb b/lib/rspec/core/notifications.rb index 8c896c2def..5173a012a1 100644 --- a/lib/rspec/core/notifications.rb +++ b/lib/rspec/core/notifications.rb @@ -292,7 +292,7 @@ def find_failed_line class PendingExampleFixedNotification < FailedExampleNotification public_class_method :new - # Returns the examples description + # Returns the examples description. # # @return [String] The example description def description @@ -334,10 +334,10 @@ def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes) MessageNotification = Struct.new(:message) # The `SeedNotification` holds the seed used to randomize examples and - # wether that seed has been used or not. + # whether that seed has been used or not. # # @attr seed [Fixnum] the seed used to randomize ordering - # @attr used [Boolean] wether the seed has been used or not + # @attr used [Boolean] whether the seed has been used or not SeedNotification = Struct.new(:seed, :used) do # @api # @return [Boolean] has the seed been used? diff --git a/lib/rspec/core/option_parser.rb b/lib/rspec/core/option_parser.rb index 1aa27a0f6c..ef53e583ed 100644 --- a/lib/rspec/core/option_parser.rb +++ b/lib/rspec/core/option_parser.rb @@ -198,21 +198,21 @@ def parser(options) exit end - # these options would otherwise be confusing to users, so we forcibly - # prevent them from executing + # These options would otherwise be confusing to users, so we forcibly + # prevent them from executing. # - # * --I is too similar to -I + # * --I is too similar to -I. # * -d was a shorthand for --debugger, which is removed, but now would - # trigger --default-path + # trigger --default-path. invalid_options = %w[-d --I] parser.on_tail('-h', '--help', "You're looking at it.") do - # removing the blank invalid options from the output + # Removing the blank invalid options from the output. puts parser.to_s.gsub(/^\s+(#{invalid_options.join('|')})\s*$\n/, '') exit end - # this prevents usage of the invalid_options + # This prevents usage of the invalid_options. invalid_options.each do |option| parser.on(option) do raise OptionParser::InvalidOption.new diff --git a/lib/rspec/core/pending.rb b/lib/rspec/core/pending.rb index 47b53b7c89..f04e3be3b7 100644 --- a/lib/rspec/core/pending.rb +++ b/lib/rspec/core/pending.rb @@ -73,7 +73,7 @@ def pending(message=nil) if block_given? raise ArgumentError, <<-EOS.gsub(/^\s+\|/, '') |The semantics of `RSpec::Core::Pending#pending` have changed in - |RSpec 3. In RSpec 2.x, it caused the example to be skipped. In + |RSpec 3. In RSpec 2.x, it caused the example to be skipped. In |RSpec 3, the rest of the example is still run but is expected to |fail, and will be marked as a failure (rather than as pending) if |the example passes. @@ -125,7 +125,7 @@ def skip(message=nil) # @private # - # Mark example as skipped + # Mark example as skipped. # # @param example [RSpec::Core::Example] the example to mark as skipped # @param message_or_bool [Boolean, String] the message to use, or true @@ -136,7 +136,7 @@ def self.mark_skipped!(example, message_or_bool) # @private # - # Mark example as pending + # Mark example as pending. # # @param example [RSpec::Core::Example] the example to mark as pending # @param message_or_bool [Boolean, String] the message to use, or true @@ -154,7 +154,7 @@ def self.mark_pending!(example, message_or_bool) # @private # - # Mark example as fixed + # Mark example as fixed. # # @param example [RSpec::Core::Example] the example to mark as fixed def self.mark_fixed!(example) diff --git a/lib/rspec/core/project_initializer.rb b/lib/rspec/core/project_initializer.rb index 2a0a4168ee..ca707e0367 100644 --- a/lib/rspec/core/project_initializer.rb +++ b/lib/rspec/core/project_initializer.rb @@ -3,7 +3,7 @@ module RSpec module Core # @private - # Generates conventional files for an rspec project + # Generates conventional files for an RSpec project. class ProjectInitializer attr_reader :destination, :stream, :template_path diff --git a/lib/rspec/core/project_initializer/spec/spec_helper.rb b/lib/rspec/core/project_initializer/spec/spec_helper.rb index ea1578dc7c..b598abee98 100644 --- a/lib/rspec/core/project_initializer/spec/spec_helper.rb +++ b/lib/rspec/core/project_initializer/spec/spec_helper.rb @@ -24,10 +24,10 @@ # This option will default to `true` in RSpec 4. It makes the `description` # and `failure_message` of custom matchers include text for helper methods # defined using `chain`, e.g.: - # be_bigger_than(2).and_smaller_than(4).description - # # => "be bigger than 2 and smaller than 4" + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" # ...rather than: - # # => "be bigger than 2" + # # => "be bigger than 2" expectations.include_chain_clauses_in_custom_matcher_descriptions = true end diff --git a/lib/rspec/core/rake_task.rb b/lib/rspec/core/rake_task.rb index e088d92289..6fa06ceade 100644 --- a/lib/rspec/core/rake_task.rb +++ b/lib/rspec/core/rake_task.rb @@ -4,13 +4,13 @@ module RSpec module Core - # Rspec rake task + # RSpec rake task # # @see Rakefile class RakeTask < ::Rake::TaskLib include ::Rake::DSL if defined?(::Rake::DSL) - # Default path to the rspec executable + # Default path to the RSpec executable. DEFAULT_RSPEC_PATH = File.expand_path('../../../../exe/rspec', __FILE__) # Default pattern for spec files. @@ -57,13 +57,13 @@ class RakeTask < ::Rake::TaskLib # nil attr_accessor :ruby_opts - # Path to rspec + # Path to RSpec. # # default: # 'rspec' attr_accessor :rspec_path - # Command line options to pass to rspec. + # Command line options to pass to RSpec. # # default: # nil diff --git a/lib/rspec/core/reporter.rb b/lib/rspec/core/reporter.rb index c3e272022c..627d148c37 100644 --- a/lib/rspec/core/reporter.rb +++ b/lib/rspec/core/reporter.rb @@ -22,7 +22,7 @@ def reset end # Registers a listener to a list of notifications. The reporter will send - # notification of events to all registered listeners + # notification of events to all registered listeners. # # @param listener [Object] An obect that wishes to be notified of reporter # events @@ -148,7 +148,7 @@ def notify(event, notification) def mute_profile_output? # Don't print out profiled info if there are failures and `--fail-fast` is - # used, it just clutters the output + # used, it just clutters the output. !@configuration.profile_examples? || (@configuration.fail_fast? && @failed_examples.size > 0) end diff --git a/lib/rspec/core/runner.rb b/lib/rspec/core/runner.rb index 4347f7bb12..db7d921b3d 100644 --- a/lib/rspec/core/runner.rb +++ b/lib/rspec/core/runner.rb @@ -24,7 +24,7 @@ def self.autorun next unless $!.nil? || $!.is_a?(SystemExit) # We got here because either the end of the program was reached or - # somebody called Kernel#exit. Run the specs and then override any + # somebody called Kernel#exit. Run the specs and then override any # existing exit status with RSpec's exit status if any specs failed. invoke end diff --git a/lib/rspec/core/shared_example_group.rb b/lib/rspec/core/shared_example_group.rb index e0ce44589f..6e369b5e31 100644 --- a/lib/rspec/core/shared_example_group.rb +++ b/lib/rspec/core/shared_example_group.rb @@ -66,7 +66,7 @@ def shared_examples(name, *args, &block) # @api private # - # Shared examples top level DSL + # Shared examples top level DSL. module TopLevelDSL # @private def self.definitions @@ -86,7 +86,7 @@ def self.exposed_globally? # @api private # - # Adds the top level DSL methods to Module and the top level binding + # Adds the top level DSL methods to Module and the top level binding. def self.expose_globally! return if exposed_globally? Core::DSL.change_global_dsl(&definitions) @@ -95,7 +95,7 @@ def self.expose_globally! # @api private # - # Removes the top level DSL methods to Module and the top level binding + # Removes the top level DSL methods to Module and the top level binding. def self.remove_globally! return unless exposed_globally? diff --git a/lib/rspec/core/test_unit_assertions_adapter.rb b/lib/rspec/core/test_unit_assertions_adapter.rb index 8fb09eb914..d84ecb1441 100644 --- a/lib/rspec/core/test_unit_assertions_adapter.rb +++ b/lib/rspec/core/test_unit_assertions_adapter.rb @@ -16,14 +16,14 @@ module TestUnitAssertionsAdapter # adding a shim for the new updates. Thus instead of checking on the # RUBY_VERSION we need to check ancestors. begin - # MiniTest is 4.x - # Minitest is 5.x + # MiniTest is 4.x. + # Minitest is 5.x. if ancestors.include?(::Minitest::Assertions) require 'rspec/core/minitest_assertions_adapter' include ::RSpec::Core::MinitestAssertionsAdapter end rescue NameError - # No-op. Minitest 5.x was not loaded + # No-op. Minitest 5.x was not loaded. end end end diff --git a/lib/rspec/core/warnings.rb b/lib/rspec/core/warnings.rb index ce324e62d8..b8800591bc 100644 --- a/lib/rspec/core/warnings.rb +++ b/lib/rspec/core/warnings.rb @@ -6,7 +6,7 @@ module Core module Warnings # @private # - # Used internally to print deprecation warnings + # Used internally to print deprecation warnings. def deprecate(deprecated, data={}) RSpec.configuration.reporter.deprecation( { @@ -18,7 +18,7 @@ def deprecate(deprecated, data={}) # @private # - # Used internally to print deprecation warnings + # Used internally to print deprecation warnings. def warn_deprecation(message, opts={}) RSpec.configuration.reporter.deprecation opts.merge(:message => message) end diff --git a/lib/rspec/core/world.rb b/lib/rspec/core/world.rb index 7830b355d6..81da405278 100644 --- a/lib/rspec/core/world.rb +++ b/lib/rspec/core/world.rb @@ -2,14 +2,14 @@ module RSpec module Core # @api private # - # Internal container for global non-configuration data + # Internal container for global non-configuration data. class World include RSpec::Core::Hooks # @private attr_reader :example_groups, :filtered_examples - # Used internally to determine what to do when a SIGINT is received + # Used internally to determine what to do when a SIGINT is received. attr_accessor :wants_to_quit def initialize(configuration=RSpec.configuration) @@ -26,14 +26,14 @@ def initialize(configuration=RSpec.configuration) end # @private - # Used internally to clear remaining groups when fail_fast is set + # Used internally to clear remaining groups when fail_fast is set. def clear_remaining_example_groups example_groups.clear end # @api private # - # Apply ordering strategy from configuration to example groups + # Apply ordering strategy from configuration to example groups. def ordered_example_groups ordering_strategy = @configuration.ordering_registry.fetch(:global) ordering_strategy.order(@example_groups) @@ -41,7 +41,7 @@ def ordered_example_groups # @api private # - # Reset world to 'scratch' before running suite + # Reset world to 'scratch' before running suite. def reset example_groups.clear @shared_example_group_registry = nil @@ -54,7 +54,7 @@ def filter_manager # @api private # - # Register an example group + # Register an example group. def register(example_group) example_groups << example_group example_group @@ -82,7 +82,7 @@ def configure_group(group) # @api private # - # Get count of examples to be run + # Get count of examples to be run. def example_count(groups=example_groups) FlatMap.flat_map(groups) { |g| g.descendants }. inject(0) { |a, e| a + e.filtered_examples.size } @@ -90,7 +90,7 @@ def example_count(groups=example_groups) # @api private # - # Find line number of previous declaration + # Find line number of previous declaration. def preceding_declaration_line(filter_line) declaration_line_numbers.sort.inject(nil) do |highest_prior_declaration_line, line| line <= filter_line ? line : highest_prior_declaration_line @@ -104,7 +104,7 @@ def reporter # @api private # - # Notify reporter of filters + # Notify reporter of filters. def announce_filters filter_announcements = [] @@ -148,7 +148,7 @@ def everything_filtered_message # @api private # - # Add inclusion filters to announcement message + # Add inclusion filters to announcement message. def announce_inclusion_filter(announcements) return if inclusion_filter.empty? @@ -157,7 +157,7 @@ def announce_inclusion_filter(announcements) # @api private # - # Add exclusion filters to announcement message + # Add exclusion filters to announcement message. def announce_exclusion_filter(announcements) return if exclusion_filter.empty? From e3cd4b70283500f64669d7963ac1b6b5b22ef0aa Mon Sep 17 00:00:00 2001 From: ChaYoung You Date: Sat, 11 Oct 2014 11:47:14 +0900 Subject: [PATCH 060/275] Reduce line length to 130 --- .rubocop.yml | 4 +-- lib/rspec/core/configuration.rb | 26 +++++++++----- lib/rspec/core/example_group.rb | 3 +- lib/rspec/core/formatters.rb | 3 +- .../core/formatters/deprecation_formatter.rb | 3 +- .../formatters/documentation_formatter.rb | 14 +++++--- lib/rspec/core/formatters/html_formatter.rb | 8 +++-- lib/rspec/core/formatters/html_printer.rb | 34 ++++++++++++++----- .../core/formatters/profile_formatter.rb | 10 ++++-- lib/rspec/core/hooks.rb | 19 ++++++++--- lib/rspec/core/option_parser.rb | 9 +++-- lib/rspec/core/reporter.rb | 6 ++-- 12 files changed, 98 insertions(+), 41 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 4dc0e01e8a..368bdfb4d4 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -11,7 +11,7 @@ ClassLength: # This should go down over time. LineLength: - Max: 180 + Max: 130 Lint/HandleExceptions: Exclude: @@ -25,7 +25,7 @@ Lint/LiteralInInterpolation: # This should go down over time. MethodLength: - Max: 152 + Max: 155 # Exclude the default spec_helper to make it easier to uncomment out # default settings (for both users and the Cucumber suite). diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index 86896f96f3..dcf59c7a6a 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -270,9 +270,12 @@ def exclude_pattern=(value) # for this being the default behavior in RSpec 3. Now this option is # a no-op. def treat_symbols_as_metadata_keys_with_true_values=(_value) - RSpec.deprecate("RSpec::Core::Configuration#treat_symbols_as_metadata_keys_with_true_values=", - :message => "RSpec::Core::Configuration#treat_symbols_as_metadata_keys_with_true_values= " \ - "is deprecated, it is now set to true as default and setting it to false has no effect.") + RSpec.deprecate( + "RSpec::Core::Configuration#treat_symbols_as_metadata_keys_with_true_values=", + :message => "RSpec::Core::Configuration#treat_symbols_as_metadata_keys_with_true_values= " \ + "is deprecated, it is now set to true as default and " \ + "setting it to false has no effect." + ) end # Record the start time of the spec suite to measure load time. @@ -539,7 +542,8 @@ def mock_with(framework) end if block_given? - raise "#{framework_module} must respond to `configuration` so that mock_with can yield it." unless framework_module.respond_to?(:configuration) + raise "#{framework_module} must respond to `configuration` so that " \ + "mock_with can yield it." unless framework_module.respond_to?(:configuration) yield framework_module.configuration end @@ -611,8 +615,11 @@ def expect_with(*frameworks) end if block_given? - raise "expect_with only accepts a block with a single argument. Call expect_with #{modules.length} times, once with each argument, instead." if modules.length > 1 - raise "#{modules.first} must respond to `configuration` so that expect_with can yield it." unless modules.first.respond_to?(:configuration) + raise "expect_with only accepts a block with a single argument. " \ + "Call expect_with #{modules.length} times, " \ + "once with each argument, instead." if modules.length > 1 + raise "#{modules.first} must respond to `configuration` so that " \ + "expect_with can yield it." unless modules.first.respond_to?(:configuration) yield modules.first.configuration end @@ -654,7 +661,9 @@ def color=(true_or_false) return unless true_or_false if RSpec::Support::OS.windows? && !ENV['ANSICON'] - RSpec.warning "You must use ANSICON 1.31 or later (http://adoxa.3eeweb.com/ansicon/) to use colour on Windows" + RSpec.warning "You must use ANSICON 1.31 or later " \ + "(http://adoxa.3eeweb.com/ansicon/) to use colour " \ + "on Windows" @color = false else @color = true @@ -1462,7 +1471,8 @@ def rspec_expectations_loaded? def update_pattern_attr(name, value) if @spec_files_loaded - RSpec.warning "Configuring `#{name}` to #{value} has no effect since RSpec has already loaded the spec files." + RSpec.warning "Configuring `#{name}` to #{value} has no effect since " \ + "RSpec has already loaded the spec files." end instance_variable_set(:"@#{name}", value) diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index 854b16de8f..914cccbde4 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -381,7 +381,8 @@ def self.filtered_examples # @private def self.descendant_filtered_examples - @descendant_filtered_examples ||= filtered_examples + children.inject([]) { |a, e| a + e.descendant_filtered_examples } + @descendant_filtered_examples ||= filtered_examples + + children.inject([]) { |a, e| a + e.descendant_filtered_examples } end # @private diff --git a/lib/rspec/core/formatters.rb b/lib/rspec/core/formatters.rb index 4f9165f518..765c2e1f45 100644 --- a/lib/rspec/core/formatters.rb +++ b/lib/rspec/core/formatters.rb @@ -168,7 +168,8 @@ def add(formatter_to_use, *paths) def find_formatter(formatter_to_use) built_in_formatter(formatter_to_use) || custom_formatter(formatter_to_use) || - (raise ArgumentError, "Formatter '#{formatter_to_use}' unknown - maybe you meant 'documentation' or 'progress'?.") + (raise ArgumentError, "Formatter '#{formatter_to_use}' unknown - " \ + "maybe you meant 'documentation' or 'progress'?.") end def duplicate_formatter_exists?(new_formatter) diff --git a/lib/rspec/core/formatters/deprecation_formatter.rb b/lib/rspec/core/formatters/deprecation_formatter.rb index 5a5c2562d8..5cbe73593a 100644 --- a/lib/rspec/core/formatters/deprecation_formatter.rb +++ b/lib/rspec/core/formatters/deprecation_formatter.rb @@ -21,7 +21,8 @@ def initialize(deprecation_stream, summary_stream) def printer @printer ||= case deprecation_stream when File - ImmediatePrinter.new(FileStream.new(deprecation_stream), summary_stream, self) + ImmediatePrinter.new(FileStream.new(deprecation_stream), + summary_stream, self) when RaiseErrorStream ImmediatePrinter.new(deprecation_stream, summary_stream, self) else diff --git a/lib/rspec/core/formatters/documentation_formatter.rb b/lib/rspec/core/formatters/documentation_formatter.rb index 5cf6e178e3..5deb4a754f 100644 --- a/lib/rspec/core/formatters/documentation_formatter.rb +++ b/lib/rspec/core/formatters/documentation_formatter.rb @@ -29,11 +29,13 @@ def example_passed(passed) end def example_pending(pending) - output.puts pending_output(pending.example, pending.example.execution_result.pending_message) + output.puts pending_output(pending.example, + pending.example.execution_result.pending_message) end def example_failed(failure) - output.puts failure_output(failure.example, failure.example.execution_result.exception) + output.puts failure_output(failure.example, + failure.example.execution_result.exception) end private @@ -43,11 +45,15 @@ def passed_output(example) end def pending_output(example, message) - ConsoleCodes.wrap("#{current_indentation}#{example.description.strip} (PENDING: #{message})", :pending) + ConsoleCodes.wrap("#{current_indentation}#{example.description.strip} " \ + "(PENDING: #{message})", + :pending) end def failure_output(example, _exception) - ConsoleCodes.wrap("#{current_indentation}#{example.description.strip} (FAILED - #{next_failure_index})", :failure) + ConsoleCodes.wrap("#{current_indentation}#{example.description.strip} " \ + "(FAILED - #{next_failure_index})", + :failure) end def next_failure_index diff --git a/lib/rspec/core/formatters/html_formatter.rb b/lib/rspec/core/formatters/html_formatter.rb index a808efbcd9..30868b45ba 100644 --- a/lib/rspec/core/formatters/html_formatter.rb +++ b/lib/rspec/core/formatters/html_formatter.rb @@ -31,7 +31,9 @@ def example_group_started(notification) @example_group_number += 1 @printer.print_example_group_end unless example_group_number == 1 - @printer.print_example_group_start(example_group_number, notification.group.description, notification.group.parent_groups.size) + @printer.print_example_group_start(example_group_number, + notification.group.description, + notification.group.parent_groups.size) @printer.flush end @@ -139,7 +141,9 @@ def percent_done # produced during the specs. def extra_failure_content(failure) RSpec::Support.require_rspec_core "formatters/snippet_extractor" - backtrace = failure.exception.backtrace.map { |line| RSpec.configuration.backtrace_formatter.backtrace_line(line) } + backtrace = failure.exception.backtrace.map do |line| + RSpec.configuration.backtrace_formatter.backtrace_line(line) + end backtrace.compact! @snippet_extractor ||= SnippetExtractor.new "
#{@snippet_extractor.snippet(backtrace)}
" diff --git a/lib/rspec/core/formatters/html_printer.rb b/lib/rspec/core/formatters/html_printer.rb index 8e5dce2023..32a9f97d39 100644 --- a/lib/rspec/core/formatters/html_printer.rb +++ b/lib/rspec/core/formatters/html_printer.rb @@ -28,11 +28,14 @@ def print_example_group_start(group_id, description, number_of_parents) def print_example_passed(description, run_time) formatted_run_time = "%.5f" % run_time - @output.puts "
#{h(description)}#{formatted_run_time}s
" + @output.puts "
" \ + "#{h(description)}" \ + "#{formatted_run_time}s
" end # rubocop:disable Style/ParameterLists - def print_example_failed(pending_fixed, description, run_time, failure_id, exception, extra_content, escape_backtrace=false) + def print_example_failed(pending_fixed, description, run_time, failure_id, + exception, extra_content, escape_backtrace=false) # rubocop:enable Style/ParameterLists formatted_run_time = "%.5f" % run_time @@ -54,7 +57,9 @@ def print_example_failed(pending_fixed, description, run_time, failure_id, excep end def print_example_pending(description, pending_message) - @output.puts "
#{h(description)} (PENDING: #{h(pending_message)})
" + @output.puts "
" \ + "#{h(description)} " \ + "(PENDING: #{h(pending_message)})
" end def print_summary(duration, example_count, failure_count, pending_count) @@ -64,8 +69,11 @@ def print_summary(duration, example_count, failure_count, pending_count) formatted_duration = "%.5f" % duration - @output.puts "" - @output.puts "" + @output.puts "" + @output.puts "" @output.puts "" @output.puts "" @output.puts "" @@ -90,13 +98,17 @@ def make_header_yellow end def make_example_group_header_red(group_id) - @output.puts " " - @output.puts " " + @output.puts " " + @output.puts " " end def make_example_group_header_yellow(group_id) - @output.puts " " - @output.puts " " + @output.puts " " + @output.puts " " end private @@ -105,6 +117,7 @@ def indentation_style(number_of_parents) "style=\"margin-left: #{(number_of_parents - 1) * 15}px;\"" end + # rubocop:disable LineLength REPORT_HEADER = <<-EOF
@@ -128,7 +141,9 @@ def indentation_style(number_of_parents)
EOF + # rubocop:enable LineLength + # rubocop:disable LineLength GLOBAL_SCRIPTS = <<-EOF function addClass(element_id, classname) { @@ -205,6 +220,7 @@ def indentation_style(number_of_parents) } } EOF + # rubocop:enable LineLength GLOBAL_STYLES = <<-EOF #rspec-header { diff --git a/lib/rspec/core/formatters/profile_formatter.rb b/lib/rspec/core/formatters/profile_formatter.rb index bfbfd41b5e..4d846637d7 100644 --- a/lib/rspec/core/formatters/profile_formatter.rb +++ b/lib/rspec/core/formatters/profile_formatter.rb @@ -31,11 +31,14 @@ def dump_profile(profile) private def dump_profile_slowest_examples(profile) - @output.puts "\nTop #{profile.slowest_examples.size} slowest examples (#{Helpers.format_seconds(profile.slow_duration)} seconds, #{profile.percentage}% of total time):\n" + @output.puts "\nTop #{profile.slowest_examples.size} slowest " \ + "examples (#{Helpers.format_seconds(profile.slow_duration)} " \ + "seconds, #{profile.percentage}% of total time):\n" profile.slowest_examples.each do |example| @output.puts " #{example.full_description}" - @output.puts " #{bold(Helpers.format_seconds(example.execution_result.run_time))} #{bold("seconds")} #{format_caller(example.location)}" + @output.puts " #{bold(Helpers.format_seconds(example.execution_result.run_time))} " \ + "#{bold("seconds")} #{format_caller(example.location)}" end end @@ -53,7 +56,8 @@ def dump_profile_slowest_example_groups(profile) end def format_caller(caller_info) - RSpec.configuration.backtrace_formatter.backtrace_line(caller_info.to_s.split(':in `block').first) + RSpec.configuration.backtrace_formatter.backtrace_line( + caller_info.to_s.split(':in `block').first) end def bold(text) diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index e7edae1b49..b1b9c98215 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -326,8 +326,12 @@ def hooks @hooks ||= HookCollections.new( self, :around => { :example => AroundHookCollection.new }, - :before => { :example => HookCollection.new, :context => HookCollection.new, :suite => HookCollection.new }, - :after => { :example => HookCollection.new, :context => HookCollection.new, :suite => HookCollection.new } + :before => { :example => HookCollection.new, + :context => HookCollection.new, + :suite => HookCollection.new }, + :after => { :example => HookCollection.new, + :context => HookCollection.new, + :suite => HookCollection.new } ) end @@ -382,7 +386,8 @@ class AroundHook < Hook def execute_with(example, procsy) example.instance_exec(procsy, &block) return if procsy.executed? - Pending.mark_skipped!(example, "#{hook_description} did not execute the example") + Pending.mark_skipped!(example, + "#{hook_description} did not execute the example") end if Proc.method_defined?(:source_location) @@ -491,7 +496,8 @@ def around_example_hooks_for(example, initial_procsy=nil) def register(prepend_or_append, hook, *args, &block) scope, options = scope_and_options_from(*args) - self[hook][scope].__send__(prepend_or_append, HOOK_TYPES[hook][scope].new(block, options)) + self[hook][scope].__send__(prepend_or_append, + HOOK_TYPES[hook][scope].new(block, options)) end # @private @@ -535,7 +541,10 @@ def extract_scope_from(args) if known_scope?(args.first) normalized_scope_for(args.shift) elsif args.any? { |a| a.is_a?(Symbol) } - error_message = "You must explicitly give a scope (#{SCOPES.join(", ")}) or scope alias (#{SCOPE_ALIASES.keys.join(", ")}) when using symbols as metadata for a hook." + error_message = "You must explicitly give a scope " \ + "(#{SCOPES.join(", ")}) or scope alias " \ + "(#{SCOPE_ALIASES.keys.join(", ")}) when using symbols as " \ + "metadata for a hook." raise ArgumentError.new error_message else :example diff --git a/lib/rspec/core/option_parser.rb b/lib/rspec/core/option_parser.rb index ef53e583ed..1a10c8d458 100644 --- a/lib/rspec/core/option_parser.rb +++ b/lib/rspec/core/option_parser.rb @@ -59,7 +59,8 @@ def parser(options) options[:fail_fast] = false end - parser.on('--failure-exit-code CODE', Integer, 'Override the exit code used when there are failing specs.') do |code| + parser.on('--failure-exit-code CODE', Integer, + 'Override the exit code used when there are failing specs.') do |code| options[:failure_exit_code] = code end @@ -115,7 +116,8 @@ def parser(options) options[:color] = o end - parser.on('-p', '--[no-]profile [COUNT]', 'Enable profiling of examples and list the slowest examples (default: 10).') do |argument| + parser.on('-p', '--[no-]profile [COUNT]', + 'Enable profiling of examples and list the slowest examples (default: 10).') do |argument| options[:profile_examples] = if argument.nil? true elsif argument == false @@ -153,7 +155,8 @@ def parser(options) options[:pattern] = o end - parser.on('--exclude-pattern PATTERN', 'Load files except those matching pattern. Opposite effect of --pattern.') do |o| + parser.on('--exclude-pattern PATTERN', + 'Load files except those matching pattern. Opposite effect of --pattern.') do |o| options[:exclude_pattern] = o end diff --git a/lib/rspec/core/reporter.rb b/lib/rspec/core/reporter.rb index 627d148c37..4f5c6aae3d 100644 --- a/lib/rspec/core/reporter.rb +++ b/lib/rspec/core/reporter.rb @@ -122,9 +122,11 @@ def finish notify :dump_pending, Notifications::ExamplesNotification.new(self) notify :dump_failures, Notifications::ExamplesNotification.new(self) notify :deprecation_summary, Notifications::NullNotification - notify :dump_summary, Notifications::SummaryNotification.new(@duration, @examples, @failed_examples, @pending_examples, @load_time) + notify :dump_summary, Notifications::SummaryNotification.new(@duration, @examples, @failed_examples, + @pending_examples, @load_time) unless mute_profile_output? - notify :dump_profile, Notifications::ProfileNotification.new(@duration, @examples, @configuration.profile_examples) + notify :dump_profile, Notifications::ProfileNotification.new(@duration, @examples, + @configuration.profile_examples) end notify :seed, Notifications::SeedNotification.new(@configuration.seed, seed_used?) ensure From bc9ef0d05015bc383a3098607e4ba6753320aebe Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 11 Oct 2014 20:44:34 -0700 Subject: [PATCH 061/275] Forwardport 3.1.7 release notes. [ci skip] --- Changelog.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index ae0773b104..b969752dc4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ ### 3.2.0 Development -[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.6...master) +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.7...master) Enhancements: @@ -11,8 +11,8 @@ Enhancements: * Reduce string allocations when defining and running examples by 70% and 50% respectively. (Myron Marston, #1738) -### 3.1.7 Development -[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.6...3-1-maintenance) +### 3.1.7 / 2014-10-11 +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.6...v3.1.7) Bug Fixes: From ca3d7fe38ff59c6689f8479f8bb4aa6e0806ba2b Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Thu, 11 Sep 2014 09:49:04 +0300 Subject: [PATCH 062/275] Remove pathname require from ruby_project.rb Achieved by implement ascend simply ourselves. --- lib/rspec/core/ruby_project.rb | 16 ++++++++++++---- spec/rspec/core/ruby_project_spec.rb | 27 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/lib/rspec/core/ruby_project.rb b/lib/rspec/core/ruby_project.rb index 2e9a23d2a9..10c89f9762 100644 --- a/lib/rspec/core/ruby_project.rb +++ b/lib/rspec/core/ruby_project.rb @@ -1,9 +1,6 @@ # This is borrowed (slightly modified) from Scott Taylor's # project_path project: # http://github.com/smtlaissezfaire/project_path - -require 'pathname' - module RSpec module Core # @private @@ -29,8 +26,19 @@ def find_first_parent_containing(dir) end def ascend_until - Pathname(File.expand_path('.')).ascend do |path| + fs = File::SEPARATOR + escaped_slash = "\\#{fs}" + special = "_RSPEC_ESCAPED_SLASH_" + project_path = File.expand_path(".") + parts = project_path.gsub(escaped_slash, special).squeeze(fs).split(fs).map do |x| + x.gsub(special, escaped_slash) + end + + until parts.empty? + path = parts.join(fs) + path = fs if path == "" return path if yield(path) + parts.pop end end diff --git a/spec/rspec/core/ruby_project_spec.rb b/spec/rspec/core/ruby_project_spec.rb index 7091c188b9..15ea777ca8 100644 --- a/spec/rspec/core/ruby_project_spec.rb +++ b/spec/rspec/core/ruby_project_spec.rb @@ -21,6 +21,33 @@ module Core end end + + describe "#ascend_until" do + subject { RubyProject } + + def expect_ascend(source_path, *yielded_paths) + expect { |probe| + allow(File).to receive(:expand_path).with('.') { source_path } + subject.ascend_until(&probe) + }.to yield_successive_args(*yielded_paths) + end + + it "works with a normal path" do + expect_ascend("/var//ponies/", "/var/ponies", "/var", "/") + end + + it "works with a path with a trailing slash" do + expect_ascend("/var//ponies/", "/var/ponies", "/var", "/") + end + + it "works with a path with double slashes" do + expect_ascend("/var//ponies/", "/var/ponies", "/var", "/") + end + + it "works with a path with escaped slashes" do + expect_ascend("/var\\/ponies/", "/var\\/ponies", "/") + end + end end end end From b5deca65b6aada5e96096dd3f691c82367928581 Mon Sep 17 00:00:00 2001 From: Sam Phippen Date: Sun, 12 Oct 2014 16:11:04 +0100 Subject: [PATCH 063/275] Add changelog entry for #1703 --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index ae0773b104..15c8dcefc8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Enhancements: spec runs, whilst retaining user configuration. (Alexey Fedorov, #1706) * Reduce string allocations when defining and running examples by 70% and 50% respectively. (Myron Marston, #1738) +* Removed dependency on pathname from stdlib. (Sam Phippen, #1703) ### 3.1.7 Development [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.6...3-1-maintenance) From 8cb2dc78a47143c4005cf03b583766dddd6a5367 Mon Sep 17 00:00:00 2001 From: Alex Chaffee Date: Tue, 23 Sep 2014 15:50:33 -0400 Subject: [PATCH 064/275] Change "Interrupt again" message Everyone I've ever paired with is trained to mash ^C again and again until they get the prompt back. When you hit ^C Rspec says "Interrupt again to exit immediately". This seems to indicate that you *should* interrupt again, so everyone I've ever paired with just keeps mashing ^C at this point. But if you just wait a *second* longer, RSpec will kindly show you the report of specs run up til now. This is a great feature and more people should use it! This patch updates the message to indicate what great rewards await if you *don't* interrupt. --- lib/rspec/core/runner.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/core/runner.rb b/lib/rspec/core/runner.rb index db7d921b3d..39f60e2ca1 100644 --- a/lib/rspec/core/runner.rb +++ b/lib/rspec/core/runner.rb @@ -151,7 +151,7 @@ def self.trap_interrupt trap('INT') do exit!(1) if RSpec.world.wants_to_quit RSpec.world.wants_to_quit = true - STDERR.puts "\nExiting... Interrupt again to exit immediately." + STDERR.puts "\nRSpec is shutting down... Interrupt again to force quit." end end end From e61fdd9234583d30e729c2dbdf1ef1c59e13cebd Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 20 Oct 2014 11:23:19 +1100 Subject: [PATCH 065/275] changelog for #1742 --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 80cd6a3b4c..fd2bef459c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,8 @@ Enhancements: * Reduce string allocations when defining and running examples by 70% and 50% respectively. (Myron Marston, #1738) * Removed dependency on pathname from stdlib. (Sam Phippen, #1703) +* Improve the message presented when a user hits Ctrl-C. + (Alex Chaffee #1717, #1742) ### 3.1.7 / 2014-10-11 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.6...v3.1.7) From c3570190bb135c7b98f3db7017c18c84c5750e01 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 22 Oct 2014 00:14:39 -0700 Subject: [PATCH 066/275] Apply --seed before loading --require files. Fixes #1744. --- Changelog.md | 7 +++++++ features/command_line/randomization.feature | 7 +++++-- lib/rspec/core/configuration_options.rb | 5 +++++ spec/rspec/core/configuration_options_spec.rb | 10 ++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index fd2bef459c..372d0d31f0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,13 @@ Enhancements: * Improve the message presented when a user hits Ctrl-C. (Alex Chaffee #1717, #1742) +### 3.1.8 Development + +Bug Fixes: + +* Apply `--seed` before loading `--require` files so that required files + can access the provided seed. (Myron Marston, #1745) + ### 3.1.7 / 2014-10-11 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.6...v3.1.7) diff --git a/features/command_line/randomization.feature b/features/command_line/randomization.feature index f71cc8bd8c..f975334b26 100644 --- a/features/command_line/randomization.feature +++ b/features/command_line/randomization.feature @@ -34,10 +34,13 @@ Feature: Randomization can be reproduced across test runs is necessary to replicate a given test run's randomness. Background: + Given a file named ".rspec" with: + """ + --require spec_helper + """ + Given a file named "spec/random_spec.rb" with: """ruby - require 'spec_helper' - RSpec.describe 'randomized example' do it 'prints random numbers' do puts 5.times.map { rand(99) }.join("-") diff --git a/lib/rspec/core/configuration_options.rb b/lib/rspec/core/configuration_options.rb index 3e70bdb642..36b9f5d05b 100644 --- a/lib/rspec/core/configuration_options.rb +++ b/lib/rspec/core/configuration_options.rb @@ -89,6 +89,11 @@ def order(keys) # `-rspec_helper` option is used. :files_or_directories_to_run, :pattern, :exclude_pattern, + # Necessary so that the `--seed` option is applied before requires, + # in case required files do something with the provided seed. + # (such as seed global randomization with it). + :order, + # In general, we want to require the specified files as early as # possible. The `--require` option is specifically intended to allow # early requires. For later requires, they can just put the require in diff --git a/spec/rspec/core/configuration_options_spec.rb b/spec/rspec/core/configuration_options_spec.rb index 206c40f411..8e797352d7 100644 --- a/spec/rspec/core/configuration_options_spec.rb +++ b/spec/rspec/core/configuration_options_spec.rb @@ -102,6 +102,16 @@ opts.configure(config) end + it 'configures the seed (via `order`) before requires so that required files can use the configured seed' do + opts = config_options_object(*%w[ --seed 1234 --require spec_helper ]) + + config = RSpec::Core::Configuration.new + expect(config).to receive(:force).with(:order => "rand:1234").ordered + expect(config).to receive(:requires=).ordered + + opts.configure(config) + end + { "pattern" => :pattern, "exclude-pattern" => :exclude_pattern }.each do |flag, attr| it "sets #{attr} before `requires` so users can check `files_to_run` in a `spec_helper` loaded by `--require`" do opts = config_options_object(*%W[--require spec_helpe --#{flag} **/*.spec]) From dbb90747cd28f49b84e83cd0c130aac5c230df0f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 23 Oct 2014 11:24:39 -0700 Subject: [PATCH 067/275] Update interrupt message. As discussed in #1742, it's useful to mention that the summary report will be printed if you want. --- lib/rspec/core/runner.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/core/runner.rb b/lib/rspec/core/runner.rb index 39f60e2ca1..50bdfc55a2 100644 --- a/lib/rspec/core/runner.rb +++ b/lib/rspec/core/runner.rb @@ -151,7 +151,7 @@ def self.trap_interrupt trap('INT') do exit!(1) if RSpec.world.wants_to_quit RSpec.world.wants_to_quit = true - STDERR.puts "\nRSpec is shutting down... Interrupt again to force quit." + STDERR.puts "\nRSpec is shutting down and will print the summary report... Interrupt again to force quit." end end end From 5c47c515cc7f4faebbeeb884d38c33a50e935185 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Oct 2014 10:35:51 -0700 Subject: [PATCH 068/275] Fix typo. --- features/configuration/overriding_global_ordering.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/configuration/overriding_global_ordering.feature b/features/configuration/overriding_global_ordering.feature index aa88fb78c8..aafb4d2756 100644 --- a/features/configuration/overriding_global_ordering.feature +++ b/features/configuration/overriding_global_ordering.feature @@ -11,7 +11,7 @@ Feature: Overriding global ordering `:global`, it will be the global default, used by all groups that do not have `:order` metadata (and by RSpec to order the top-level groups). - Scenario: Running a specific examples group in order + Scenario: Running a specific example group in order Given a file named "order_dependent_spec.rb" with: """ruby RSpec.describe "examples only pass when they are run in order", :order => :defined do From 2e2724524d6bd92b04eba17ba1d3ca24cbd94662 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Oct 2014 14:23:28 -0700 Subject: [PATCH 069/275] Fix docs for example/example_group alias methods. We were getting YARD doc warnings: $ bin/yard server --reload [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 122 [warn]: @param tag has unknown parameter name: extra_options in file `lib/rspec/core/example_group.rb' near line 122 [warn]: @param tag has unknown parameter name: implementation in file `lib/rspec/core/example_group.rb' near line 122 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 125 [warn]: @param tag has unknown parameter name: extra_options in file `lib/rspec/core/example_group.rb' near line 125 [warn]: @param tag has unknown parameter name: implementation in file `lib/rspec/core/example_group.rb' near line 125 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 134 [warn]: @param tag has unknown parameter name: extra_options in file `lib/rspec/core/example_group.rb' near line 134 [warn]: @param tag has unknown parameter name: implementation in file `lib/rspec/core/example_group.rb' near line 134 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 138 [warn]: @param tag has unknown parameter name: extra_options in file `lib/rspec/core/example_group.rb' near line 138 [warn]: @param tag has unknown parameter name: implementation in file `lib/rspec/core/example_group.rb' near line 138 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 141 [warn]: @param tag has unknown parameter name: extra_options in file `lib/rspec/core/example_group.rb' near line 141 [warn]: @param tag has unknown parameter name: implementation in file `lib/rspec/core/example_group.rb' near line 141 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 144 [warn]: @param tag has unknown parameter name: extra_options in file `lib/rspec/core/example_group.rb' near line 144 [warn]: @param tag has unknown parameter name: implementation in file `lib/rspec/core/example_group.rb' near line 144 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 147 [warn]: @param tag has unknown parameter name: extra_options in file `lib/rspec/core/example_group.rb' near line 147 [warn]: @param tag has unknown parameter name: implementation in file `lib/rspec/core/example_group.rb' near line 147 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 150 [warn]: @param tag has unknown parameter name: extra_options in file `lib/rspec/core/example_group.rb' near line 150 [warn]: @param tag has unknown parameter name: implementation in file `lib/rspec/core/example_group.rb' near line 150 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 153 [warn]: @param tag has unknown parameter name: extra_options in file `lib/rspec/core/example_group.rb' near line 153 [warn]: @param tag has unknown parameter name: implementation in file `lib/rspec/core/example_group.rb' near line 153 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 156 [warn]: @param tag has unknown parameter name: extra_options in file `lib/rspec/core/example_group.rb' near line 156 [warn]: @param tag has unknown parameter name: implementation in file `lib/rspec/core/example_group.rb' near line 156 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 159 [warn]: @param tag has unknown parameter name: extra_options in file `lib/rspec/core/example_group.rb' near line 159 [warn]: @param tag has unknown parameter name: implementation in file `lib/rspec/core/example_group.rb' near line 159 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 162 [warn]: @param tag has unknown parameter name: extra_options in file `lib/rspec/core/example_group.rb' near line 162 [warn]: @param tag has unknown parameter name: implementation in file `lib/rspec/core/example_group.rb' near line 162 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 231 [warn]: @param tag has unknown parameter name: metadata in file `lib/rspec/core/example_group.rb' near line 231 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 236 [warn]: @param tag has unknown parameter name: metadata in file `lib/rspec/core/example_group.rb' near line 236 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 241 [warn]: @param tag has unknown parameter name: metadata in file `lib/rspec/core/example_group.rb' near line 241 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 245 [warn]: @param tag has unknown parameter name: metadata in file `lib/rspec/core/example_group.rb' near line 245 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 249 [warn]: @param tag has unknown parameter name: metadata in file `lib/rspec/core/example_group.rb' near line 249 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 253 [warn]: @param tag has unknown parameter name: metadata in file `lib/rspec/core/example_group.rb' near line 253 [warn]: @param tag has unknown parameter name: name in file `lib/rspec/core/example_group.rb' near line 257 [warn]: @param tag has unknown parameter name: metadata in file `lib/rspec/core/example_group.rb' near line 257 --- lib/rspec/core/example_group.rb | 35 +++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index c9e04e501d..c564271099 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -88,9 +88,20 @@ def described_class # @private # @macro [attach] define_example_method # @!scope class - # @param name [String] - # @param extra_options [Hash] - # @param implementation [Block] + # @overload $1 + # @overload $1(&example_implementation) + # @param example_implementation [Block] The implementation of the example. + # @overload $1(doc_string, *metadata_keys, metadata={}) + # @param doc_string [String] The example's doc string. + # @param metadata [Hash] Metadata for the example. + # @param metadata_keys [Array] Metadata tags for the example. + # Will be transformed into hash entries with `true` values. + # @overload $1(doc_string, *metadata_keys, metadata={}, &example_implementation) + # @param doc_string [String] The example's doc string. + # @param metadata [Hash] Metadata for the example. + # @param metadata_keys [Array] Metadata tags for the example. + # Will be transformed into hash entries with `true` values. + # @param example_implementation [Block] The implementation of the example. # @yield [Example] the example object # @example # $1 do @@ -99,6 +110,9 @@ def described_class # $1 "does something" do # end # + # $1 "does something", :slow, :uses_js do + # end + # # $1 "does something", :with => 'additional metadata' do # end # @@ -166,12 +180,17 @@ def self.define_example_method(name, extra_options={}) # @!group Defining Example Groups # @private - # @macro [attach] alias_example_group_to + # @macro [attach] define_example_group_method # @!scope class - # @param name [String] The example group doc string - # @param metadata [Hash] Additional metadata to attach to the example - # group - # @yield The example group definition + # @overload $1 + # @overload $1(&example_group_definition) + # @param example_group_definition [Block] The definition of the example group. + # @overload $1(doc_string, *metadata_keys, metadata={}, &example_implementation) + # @param doc_string [String] The group's doc string. + # @param metadata [Hash] Metadata for the group. + # @param metadata_keys [Array] Metadata tags for the group. + # Will be transformed into hash entries with `true` values. + # @param example_group_definition [Block] The definition of the example group. # # Generates a subclass of this example group which inherits # everything except the examples themselves. From a4e32e5df5ac8becd6c91ad7017b8076de7056b0 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Oct 2014 14:45:15 -0700 Subject: [PATCH 070/275] Address remaining yard warnings. [warn]: @param tag has unknown parameter name: notification in file `lib/rspec/core/formatters/base_formatter.rb' near line 50 [warn]: @param tag has unknown parameter name: profile in file `lib/rspec/core/formatters/profile_formatter.rb' near line 26 [warn]: @param tag has unknown parameter name: notification in file `lib/rspec/core/formatters/base_text_formatter.rb' near line 23 [warn]: @param tag has unknown parameter name: notification in file `lib/rspec/core/formatters/base_text_formatter.rb' near line 33 [warn]: @param tag has unknown parameter name: summary in file `lib/rspec/core/formatters/base_text_formatter.rb' near line 46 [warn]: @param tag has unknown parameter name: notification in file `lib/rspec/core/formatters/base_text_formatter.rb' near line 68 --- lib/rspec/core/formatters/base_formatter.rb | 2 +- lib/rspec/core/formatters/base_text_formatter.rb | 5 +---- lib/rspec/core/formatters/profile_formatter.rb | 1 - 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/rspec/core/formatters/base_formatter.rb b/lib/rspec/core/formatters/base_formatter.rb index e4d7731de3..84248aa174 100644 --- a/lib/rspec/core/formatters/base_formatter.rb +++ b/lib/rspec/core/formatters/base_formatter.rb @@ -45,7 +45,7 @@ def example_group_started(notification) # @api public # - # @param notification [NullNotification] + # @param _notification [NullNotification] (Ignored) # @see RSpec::Core::Formatters::Protocol#close def close(_notification) restore_sync_output diff --git a/lib/rspec/core/formatters/base_text_formatter.rb b/lib/rspec/core/formatters/base_text_formatter.rb index 5cc0ea0201..c378702314 100644 --- a/lib/rspec/core/formatters/base_text_formatter.rb +++ b/lib/rspec/core/formatters/base_text_formatter.rb @@ -14,7 +14,6 @@ class BaseTextFormatter < BaseFormatter Formatters.register self, :message, :dump_summary, :dump_failures, :dump_pending, :seed - # @method message # @api public # # Used by the reporter to send messages to the output stream. @@ -24,7 +23,6 @@ def message(notification) output.puts notification.message end - # @method dump_failures # @api public # # Dumps detailed information about each example failure. @@ -35,7 +33,6 @@ def dump_failures(notification) output.puts notification.fully_formatted_failed_examples end - # @method dump_summary # @api public # # This method is invoked after the dumping of examples and failures. @@ -64,7 +61,7 @@ def seed(notification) # Invoked at the very end, `close` allows the formatter to clean # up resources, e.g. open streams, etc. # - # @param notification [NullNotification] + # @param _notification [NullNotification] (Ignored) def close(_notification) return unless IO === output return if output.closed? || output == $stdout diff --git a/lib/rspec/core/formatters/profile_formatter.rb b/lib/rspec/core/formatters/profile_formatter.rb index 4d846637d7..4b95d9386f 100644 --- a/lib/rspec/core/formatters/profile_formatter.rb +++ b/lib/rspec/core/formatters/profile_formatter.rb @@ -15,7 +15,6 @@ def initialize(output) # @private attr_reader :output - # @method dump_profile # @api public # # This method is invoked after the dumping the summary if profiling is From d2633abc5a20334bac48a3c2513ab84a0c587284 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Oct 2014 15:18:48 -0700 Subject: [PATCH 071/275] Reify shared example groups as a first-class type. - Allows us to provide pretty `inspect` output, which is far better than the anonymous module output we normally get. - Simplifies the creation of the shared example group. Previously each group instantiated a module and defined `included` as a singleton method. --- lib/rspec/core/example_group.rb | 13 +- lib/rspec/core/hooks.rb | 4 +- lib/rspec/core/shared_example_group.rb | 149 +++++++++++-------- spec/rspec/core/shared_example_group_spec.rb | 19 ++- spec/support/matchers.rb | 1 + spec/support/sandboxing.rb | 2 +- 6 files changed, 113 insertions(+), 75 deletions(-) diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index c9e04e501d..96f7e14f19 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -22,15 +22,16 @@ module Core # # Besides the class methods defined here, there are other interesting macros # defined in {Hooks}, {MemoizedHelpers::ClassMethods} and - # {SharedExampleGroup}. There are additional instance methods available to - # your examples defined in {MemoizedHelpers} and {Pending}. + # {SharedExampleGroup::DefinitionAPI}. There are additional instance + # methods available to your examples defined in {MemoizedHelpers} and + # {Pending}. class ExampleGroup extend Hooks include MemoizedHelpers extend MemoizedHelpers::ClassMethods include Pending - extend SharedExampleGroup + extend SharedExampleGroup::DefinitionAPI unless respond_to?(:define_singleton_method) # @private @@ -264,7 +265,7 @@ def self.define_example_group_method(name, metadata={}) # @macro [attach] define_nested_shared_group_method # @!scope class # - # @see SharedExampleGroup + # @see SharedExampleGroup::DefinitionAPI def self.define_nested_shared_group_method(new_name, report_label="it should behave like") define_singleton_method(new_name) do |name, *args, &customization_block| # Pass :caller so the :location metadata is set properly. @@ -290,7 +291,7 @@ def self.define_nested_shared_group_method(new_name, report_label="it should beh # group. If given a block, that block is also eval'd in the current # context. # - # @see SharedExampleGroup + # @see SharedExampleGroup::DefinitionAPI def self.include_context(name, *args, &block) find_and_eval_shared("context", name, *args, &block) end @@ -300,7 +301,7 @@ def self.include_context(name, *args, &block) # group. If given a block, that block is also eval'd in the current # context. # - # @see SharedExampleGroup + # @see SharedExampleGroup::DefinitionAPI def self.include_examples(name, *args, &block) find_and_eval_shared("examples", name, *args, &block) end diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index b1b9c98215..58f2111044 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -30,7 +30,7 @@ module Hooks # @see #around # @see ExampleGroup # @see SharedContext - # @see SharedExampleGroup + # @see SharedExampleGroup::DefinitionAPI # @see Configuration # # Declare a block of code to be run before each example (using `:example`) @@ -220,7 +220,7 @@ def prepend_before(*args, &block) # @see #around # @see ExampleGroup # @see SharedContext - # @see SharedExampleGroup + # @see SharedExampleGroup::DefinitionAPI # @see Configuration # # Declare a block of code to be run after each example (using `:example`) diff --git a/lib/rspec/core/shared_example_group.rb b/lib/rspec/core/shared_example_group.rb index 6e369b5e31..e5367a4184 100644 --- a/lib/rspec/core/shared_example_group.rb +++ b/lib/rspec/core/shared_example_group.rb @@ -1,68 +1,92 @@ module RSpec module Core - # Shared example groups let you define common context and/or common - # examples that you wish to use in multiple example groups. - # - # When defined, the shared group block is stored for later evaluation. - # It can later be included in an example group either explicitly - # (using `include_examples`, `include_context` or `it_behaves_like`) - # or implicitly (via matching metadata). - # - # Named shared example groups are scoped based on where they are - # defined. Shared groups defined in an example group are available - # for inclusion in that example group or any child example groups, - # but not in any parent or sibling example groups. Shared example - # groups defined at the top level can be included from any example group. - module SharedExampleGroup - # @overload shared_examples(name, &block) - # @param name [String, Symbol, Module] identifer to use when looking up - # this shared group - # @param block The block to be eval'd - # @overload shared_examples(name, metadata, &block) - # @param name [String, Symbol, Module] identifer to use when looking up - # this shared group - # @param metadata [Array, Hash] metadata to attach to this - # group; any example group with matching metadata will automatically - # include this shared example group. - # @param block The block to be eval'd - # @overload shared_examples(metadata, &block) - # @param metadata [Array, Hash] metadata to attach to this - # group; any example group with matching metadata will automatically - # include this shared example group. - # @param block The block to be eval'd - # - # Stores the block for later use. The block will be evaluated - # in the context of an example group via `include_examples`, - # `include_context`, or `it_behaves_like`. - # - # @example - # shared_examples "auditable" do - # it "stores an audit record on save!" do - # expect { auditable.save! }.to change(Audit, :count).by(1) - # end - # end + # Represents some functionality that is shared with multiple example groups. + # The functionality is defined by the provided block, which is lazily + # eval'd when the `SharedExampleGroup` instance is included in an example + # group. + class SharedExampleGroup < Module + def initialize(description, definition) + @description = description + @definition = definition + end + + # Provides a human-readable representation of this module. + def inspect + "#<#{self.class.name} #{@description.inspect}>" + end + alias to_s inspect + + # Ruby callback for when a module is included in another module is class. + # Our definition evaluates the shared group block in the context of the + # including example group. + def included(klass) + klass.class_exec(&@definition) + end + + # Shared example groups let you define common context and/or common + # examples that you wish to use in multiple example groups. # - # describe Account do - # it_behaves_like "auditable" do - # let(:auditable) { Account.new } - # end - # end + # When defined, the shared group block is stored for later evaluation. + # It can later be included in an example group either explicitly + # (using `include_examples`, `include_context` or `it_behaves_like`) + # or implicitly (via matching metadata). # - # @see ExampleGroup.it_behaves_like - # @see ExampleGroup.include_examples - # @see ExampleGroup.include_context - def shared_examples(name, *args, &block) - top_level = self == ExampleGroup - if top_level && RSpec.thread_local_metadata[:in_example_group] - raise "Creating isolated shared examples from within a context is " \ - "not allowed. Remove `RSpec.` prefix or move this to a " \ - "top-level scope." - end + # Named shared example groups are scoped based on where they are + # defined. Shared groups defined in an example group are available + # for inclusion in that example group or any child example groups, + # but not in any parent or sibling example groups. Shared example + # groups defined at the top level can be included from any example group. + module DefinitionAPI + # @overload shared_examples(name, &block) + # @param name [String, Symbol, Module] identifer to use when looking up + # this shared group + # @param block The block to be eval'd + # @overload shared_examples(name, metadata, &block) + # @param name [String, Symbol, Module] identifer to use when looking up + # this shared group + # @param metadata [Array, Hash] metadata to attach to this + # group; any example group with matching metadata will automatically + # include this shared example group. + # @param block The block to be eval'd + # @overload shared_examples(metadata, &block) + # @param metadata [Array, Hash] metadata to attach to this + # group; any example group with matching metadata will automatically + # include this shared example group. + # @param block The block to be eval'd + # + # Stores the block for later use. The block will be evaluated + # in the context of an example group via `include_examples`, + # `include_context`, or `it_behaves_like`. + # + # @example + # shared_examples "auditable" do + # it "stores an audit record on save!" do + # expect { auditable.save! }.to change(Audit, :count).by(1) + # end + # end + # + # describe Account do + # it_behaves_like "auditable" do + # let(:auditable) { Account.new } + # end + # end + # + # @see ExampleGroup.it_behaves_like + # @see ExampleGroup.include_examples + # @see ExampleGroup.include_context + def shared_examples(name, *args, &block) + top_level = self == ExampleGroup + if top_level && RSpec.thread_local_metadata[:in_example_group] + raise "Creating isolated shared examples from within a context is " \ + "not allowed. Remove `RSpec.` prefix or move this to a " \ + "top-level scope." + end - RSpec.world.shared_example_group_registry.add(self, name, *args, &block) + RSpec.world.shared_example_group_registry.add(self, name, *args, &block) + end + alias shared_context shared_examples + alias shared_examples_for shared_examples end - alias shared_context shared_examples - alias shared_examples_for shared_examples # @api private # @@ -122,12 +146,7 @@ def add(context, name, *metadata_args, &block) end return if metadata_args.empty? - - mod = Module.new - (class << mod; self; end).__send__(:define_method, :included) do |host| - host.class_exec(&block) - end - RSpec.configuration.include mod, *metadata_args + RSpec.configuration.include SharedExampleGroup.new(name, block), *metadata_args end def find(lookup_contexts, name) diff --git a/spec/rspec/core/shared_example_group_spec.rb b/spec/rspec/core/shared_example_group_spec.rb index 7dcfc4a9fe..577707cdbf 100644 --- a/spec/rspec/core/shared_example_group_spec.rb +++ b/spec/rspec/core/shared_example_group_spec.rb @@ -17,7 +17,7 @@ module Core ExampleClass = Class.new it 'does not add a bunch of private methods to Module' do - seg_methods = RSpec::Core::SharedExampleGroup.private_instance_methods + seg_methods = RSpec::Core::SharedExampleGroup::DefinitionAPI.private_instance_methods expect(Module.private_methods & seg_methods).to eq([]) end @@ -74,6 +74,23 @@ module Core ExampleGroup.describe('example group') { include_context 'top level in module' } end + it 'generates a named (rather than anonymous) module' do + define_shared_group("shared behaviors", :include_it) { } + group = RSpec.describe("Group", :include_it) { } + + anonymous_module_regex = /#/ + expect(Module.new.inspect).to match(anonymous_module_regex) + + include_a_named_rather_than_anonymous_module = ( + include(a_string_including( + "# Date: Thu, 30 Oct 2014 08:23:40 -0700 Subject: [PATCH 072/275] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 3 +- script/clone_all_rspec_repos | 4 +- script/functions.sh | 104 ++++++++-------------------------- script/predicate_functions.sh | 48 ++++++++++++++++ script/run_build | 12 ++-- script/travis_functions.sh | 66 +++++++++++++++++++++ 7 files changed, 150 insertions(+), 89 deletions(-) create mode 100644 script/predicate_functions.sh create mode 100644 script/travis_functions.sh diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 8de3db2762..55fee1927d 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. +# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 67cc663934..2367ca1b94 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. +# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -19,6 +19,7 @@ rvm: - 2.1.1 - 2.1.2 - 2.1.3 + - 2.1.4 - ruby-head - ree - jruby-18mode diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index d03e9f6f36..fb840d7aba 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,8 +1,8 @@ #!/bin/bash -# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. +# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. -set -e -x +set -e source script/functions.sh if is_mri; then diff --git a/script/functions.sh b/script/functions.sh index daf776e19a..0ff87a8baf 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,80 +1,14 @@ -# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. +# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. +source script/travis_functions.sh +source script/predicate_functions.sh + # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html export JRUBY_OPTS="${JRUBY_OPTS} -X-C" # disable JIT since these processes are so short lived SPECS_HAVE_RUN_FILE=specs.out MAINTENANCE_BRANCH=`cat maintenance-branch` -# Taken from: -# https://github.com/travis-ci/travis-build/blob/e9314616e182a23e6a280199cd9070bfc7cae548/lib/travis/build/script/templates/header.sh#L34-L53 -travis_retry() { - local result=0 - local count=1 - while [ $count -le 3 ]; do - [ $result -ne 0 ] && { - echo -e "\n\033[33;1mThe command \"$@\" failed. Retrying, $count of 3.\033[0m\n" >&2 - } - "$@" - result=$? - [ $result -eq 0 ] && break - count=$(($count + 1)) - sleep 1 - done - - [ $count -eq 3 ] && { - echo "\n\033[33;1mThe command \"$@\" failed 3 times.\033[0m\n" >&2 - } - - return $result -} - -function is_mri { - if ruby -e "exit(!defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby')"; then - # RUBY_ENGINE only returns 'ruby' on MRI. - # MRI 1.8.7 lacks the constant but all other rubies have it (including JRuby in 1.8 mode) - return 0 - else - return 1 - fi; -} - -function is_mri_192 { - if is_mri; then - if ruby -e "exit(RUBY_VERSION == '1.9.2')"; then - return 0 - else - return 1 - fi - else - return 1 - fi -} - -function rspec_support_compatible { - if [ "$MAINTENANCE_BRANCH" != "2-99-maintenance" ] && [ "$MAINTENANCE_BRANCH" != "2-14-maintenance" ]; then - return 0 - else - return 1 - fi -} - -function documentation_enforced { - if [ -x ./bin/yard ]; then - return 0 - else - return 1 - fi -} - -function style_and_lint_enforced { - if [ -x ./bin/rubocop ]; then - return 0 - else - return 1 - fi -} - function clone_repo { if [ ! -d $1 ]; then # don't clone if the dir is already there travis_retry eval "git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH" @@ -90,6 +24,7 @@ function run_specs_and_record_done { rspec_bin=script/rspec_with_simplecov fi; + echo "${PWD}/bin/rspec" $rspec_bin spec --backtrace --format progress --profile --format progress --out $SPECS_HAVE_RUN_FILE } @@ -102,6 +37,8 @@ function run_cukes { # spec failures in our spec suite due to problems with this mode. export JAVA_OPTS='-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' + echo "${PWD}/bin/cucumber" + if is_mri_192; then # For some reason we get SystemStackError on 1.9.2 when using # the bin/cucumber approach below. That approach is faster @@ -118,6 +55,8 @@ function run_cukes { } function run_specs_one_by_one { + echo "Running each spec file, one-by-one..." + for file in `find spec -iname '*_spec.rb'`; do bin/rspec $file -b --format progress done @@ -125,10 +64,8 @@ function run_specs_one_by_one { function run_spec_suite_for { if [ ! -f ../$1/$SPECS_HAVE_RUN_FILE ]; then # don't rerun specs that have already run - pushd ../$1 - echo echo "Running specs for $1" - echo + pushd ../$1 unset BUNDLE_GEMFILE bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` travis_retry eval "bundle install $bundle_install_flags" @@ -138,8 +75,11 @@ function run_spec_suite_for { } function check_documentation_coverage { + echo "bin/yard stats --list-undoc" + bin/yard stats --list-undoc | ruby -e " while line = gets + has_warnings ||= line.start_with?('[warn]:') coverage ||= line[/([\d\.]+)% documented/, 1] puts line end @@ -148,21 +88,27 @@ function check_documentation_coverage { puts \"\n\nMissing documentation coverage (currently at #{coverage}%)\" exit(1) end + + if has_warnings + puts \"\n\nYARD emitted documentation warnings.\" + exit(1) + end " } function check_style_and_lint { + echo "bin/rubucop lib" bin/rubocop lib } function run_all_spec_suites { - run_specs_one_by_one - run_spec_suite_for "rspec-core" - run_spec_suite_for "rspec-expectations" - run_spec_suite_for "rspec-mocks" - run_spec_suite_for "rspec-rails" + fold "one-by-one specs" run_specs_one_by_one + fold "rspec-core specs" run_spec_suite_for "rspec-core" + fold "rspec-expectations specs" run_spec_suite_for "rspec-expectations" + fold "rspec-mocks specs" run_spec_suite_for "rspec-mocks" + fold "rspec-rails specs" run_spec_suite_for "rspec-rails" if rspec_support_compatible; then - run_spec_suite_for "rspec-support" + fold "rspec-support specs" run_spec_suite_for "rspec-support" fi } diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh new file mode 100644 index 0000000000..cc4a73dbbf --- /dev/null +++ b/script/predicate_functions.sh @@ -0,0 +1,48 @@ +# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# DO NOT modify it by hand as your changes will get lost the next time it is generated. + +function is_mri { + if ruby -e "exit(!defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby')"; then + # RUBY_ENGINE only returns 'ruby' on MRI. + # MRI 1.8.7 lacks the constant but all other rubies have it (including JRuby in 1.8 mode) + return 0 + else + return 1 + fi; +} + +function is_mri_192 { + if is_mri; then + if ruby -e "exit(RUBY_VERSION == '1.9.2')"; then + return 0 + else + return 1 + fi + else + return 1 + fi +} + +function rspec_support_compatible { + if [ "$MAINTENANCE_BRANCH" != "2-99-maintenance" ] && [ "$MAINTENANCE_BRANCH" != "2-14-maintenance" ]; then + return 0 + else + return 1 + fi +} + +function documentation_enforced { + if [ -x ./bin/yard ]; then + return 0 + else + return 1 + fi +} + +function style_and_lint_enforced { + if [ -x ./bin/rubocop ]; then + return 0 + else + return 1 + fi +} diff --git a/script/run_build b/script/run_build index eb356b8bf8..6618219085 100755 --- a/script/run_build +++ b/script/run_build @@ -1,8 +1,8 @@ #!/bin/bash -# This file was generated on 2014-09-26T16:56:14+10:00 from the rspec-dev repo. +# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. -set -e -x +set -e source script/functions.sh # Allow repos to override the default functions and add their own @@ -10,15 +10,15 @@ if [ -f script/custom_build_functions.sh ]; then source script/custom_build_functions.sh fi -run_specs_and_record_done -run_cukes +fold "specs" run_specs_and_record_done +fold "cukes" run_cukes if documentation_enforced; then - check_documentation_coverage + fold "doc check" check_documentation_coverage fi if style_and_lint_enforced; then - check_style_and_lint + fold "rubocop" check_style_and_lint fi if is_mri; then diff --git a/script/travis_functions.sh b/script/travis_functions.sh new file mode 100644 index 0000000000..0e1715dba4 --- /dev/null +++ b/script/travis_functions.sh @@ -0,0 +1,66 @@ +# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# DO NOT modify it by hand as your changes will get lost the next time it is generated. + +# Taken from: +# https://github.com/travis-ci/travis-build/blob/e9314616e182a23e6a280199cd9070bfc7cae548/lib/travis/build/script/templates/header.sh#L34-L53 +travis_retry() { + local result=0 + local count=1 + while [ $count -le 3 ]; do + [ $result -ne 0 ] && { + echo -e "\n\033[33;1mThe command \"$@\" failed. Retrying, $count of 3.\033[0m\n" >&2 + } + "$@" + result=$? + [ $result -eq 0 ] && break + count=$(($count + 1)) + sleep 1 + done + + [ $count -eq 3 ] && { + echo "\n\033[33;1mThe command \"$@\" failed 3 times.\033[0m\n" >&2 + } + + return $result +} + +# Taken from https://github.com/vcr/vcr/commit/fa96819c92b783ec0c794f788183e170e4f684b2 +# and https://github.com/vcr/vcr/commit/040aaac5370c68cd13c847c076749cd547a6f9b1 +nano_cmd="$(type -p gdate date | head -1)" +nano_format="+%s%N" +[ "$(uname -s)" != "Darwin" ] || nano_format="${nano_format/%N/000000000}" + +travis_time_start() { + travis_timer_id=$(printf %08x $(( RANDOM * RANDOM ))) + travis_start_time=$($nano_cmd -u "$nano_format") + printf "travis_time:start:%s\r\e[0m" $travis_timer_id +} + +travis_time_finish() { + local travis_end_time=$($nano_cmd -u "$nano_format") + local duration=$(($travis_end_time-$travis_start_time)) + printf "travis_time:end:%s:start=%s,finish=%s,duration=%s\r\e[0m" \ + $travis_timer_id $travis_start_time $travis_end_time $duration +} + +fold() { + local name="$1" + local status=0 + shift 1 + if [ -n "$TRAVIS" ]; then + printf "travis_fold:start:%s\r\e[0m" "$name" + travis_time_start + fi + + "$@" || status=$? + + [ -z "$TRAVIS" ] || travis_time_finish + + if [ "$status" -eq 0 ]; then + if [ -n "$TRAVIS" ]; then + printf "travis_fold:end:%s\r\e[0m" "$name" + fi + else + STATUS="$status" + fi +} From 3d0a3861b2e5438627330c942d9b294e4f6390d8 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 31 Oct 2014 00:09:26 -0700 Subject: [PATCH 073/275] Update require to new support file. See https://github.com/rspec/rspec-support/pull/119 for the background to this change. --- lib/rspec/core/rake_task.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/core/rake_task.rb b/lib/rspec/core/rake_task.rb index 6fa06ceade..f583128a28 100644 --- a/lib/rspec/core/rake_task.rb +++ b/lib/rspec/core/rake_task.rb @@ -1,6 +1,6 @@ require 'rake' require 'rake/tasklib' -require 'rspec/support/os' +require 'rspec/support/ruby_features' module RSpec module Core From 965390469f24995d0b1a7b710aa1765de4d167f4 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 4 Nov 2014 22:19:31 -0800 Subject: [PATCH 074/275] Revert "Reify shared example groups as a first-class type." This reverts commit d2633abc5a20334bac48a3c2513ab84a0c587284. I realized this broke SemVer by renaming `SharedExampleGroup` to `SharedExampleGroup::DefinitionAPI`. I'm going to re-work it in the next commit from scratch. --- lib/rspec/core/example_group.rb | 13 +- lib/rspec/core/hooks.rb | 4 +- lib/rspec/core/shared_example_group.rb | 149 ++++++++----------- spec/rspec/core/shared_example_group_spec.rb | 19 +-- spec/support/matchers.rb | 1 - spec/support/sandboxing.rb | 2 +- 6 files changed, 75 insertions(+), 113 deletions(-) diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index b40a29e94e..c564271099 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -22,16 +22,15 @@ module Core # # Besides the class methods defined here, there are other interesting macros # defined in {Hooks}, {MemoizedHelpers::ClassMethods} and - # {SharedExampleGroup::DefinitionAPI}. There are additional instance - # methods available to your examples defined in {MemoizedHelpers} and - # {Pending}. + # {SharedExampleGroup}. There are additional instance methods available to + # your examples defined in {MemoizedHelpers} and {Pending}. class ExampleGroup extend Hooks include MemoizedHelpers extend MemoizedHelpers::ClassMethods include Pending - extend SharedExampleGroup::DefinitionAPI + extend SharedExampleGroup unless respond_to?(:define_singleton_method) # @private @@ -284,7 +283,7 @@ def self.define_example_group_method(name, metadata={}) # @macro [attach] define_nested_shared_group_method # @!scope class # - # @see SharedExampleGroup::DefinitionAPI + # @see SharedExampleGroup def self.define_nested_shared_group_method(new_name, report_label="it should behave like") define_singleton_method(new_name) do |name, *args, &customization_block| # Pass :caller so the :location metadata is set properly. @@ -310,7 +309,7 @@ def self.define_nested_shared_group_method(new_name, report_label="it should beh # group. If given a block, that block is also eval'd in the current # context. # - # @see SharedExampleGroup::DefinitionAPI + # @see SharedExampleGroup def self.include_context(name, *args, &block) find_and_eval_shared("context", name, *args, &block) end @@ -320,7 +319,7 @@ def self.include_context(name, *args, &block) # group. If given a block, that block is also eval'd in the current # context. # - # @see SharedExampleGroup::DefinitionAPI + # @see SharedExampleGroup def self.include_examples(name, *args, &block) find_and_eval_shared("examples", name, *args, &block) end diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index 58f2111044..b1b9c98215 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -30,7 +30,7 @@ module Hooks # @see #around # @see ExampleGroup # @see SharedContext - # @see SharedExampleGroup::DefinitionAPI + # @see SharedExampleGroup # @see Configuration # # Declare a block of code to be run before each example (using `:example`) @@ -220,7 +220,7 @@ def prepend_before(*args, &block) # @see #around # @see ExampleGroup # @see SharedContext - # @see SharedExampleGroup::DefinitionAPI + # @see SharedExampleGroup # @see Configuration # # Declare a block of code to be run after each example (using `:example`) diff --git a/lib/rspec/core/shared_example_group.rb b/lib/rspec/core/shared_example_group.rb index e5367a4184..6e369b5e31 100644 --- a/lib/rspec/core/shared_example_group.rb +++ b/lib/rspec/core/shared_example_group.rb @@ -1,92 +1,68 @@ module RSpec module Core - # Represents some functionality that is shared with multiple example groups. - # The functionality is defined by the provided block, which is lazily - # eval'd when the `SharedExampleGroup` instance is included in an example - # group. - class SharedExampleGroup < Module - def initialize(description, definition) - @description = description - @definition = definition - end - - # Provides a human-readable representation of this module. - def inspect - "#<#{self.class.name} #{@description.inspect}>" - end - alias to_s inspect - - # Ruby callback for when a module is included in another module is class. - # Our definition evaluates the shared group block in the context of the - # including example group. - def included(klass) - klass.class_exec(&@definition) - end - - # Shared example groups let you define common context and/or common - # examples that you wish to use in multiple example groups. + # Shared example groups let you define common context and/or common + # examples that you wish to use in multiple example groups. + # + # When defined, the shared group block is stored for later evaluation. + # It can later be included in an example group either explicitly + # (using `include_examples`, `include_context` or `it_behaves_like`) + # or implicitly (via matching metadata). + # + # Named shared example groups are scoped based on where they are + # defined. Shared groups defined in an example group are available + # for inclusion in that example group or any child example groups, + # but not in any parent or sibling example groups. Shared example + # groups defined at the top level can be included from any example group. + module SharedExampleGroup + # @overload shared_examples(name, &block) + # @param name [String, Symbol, Module] identifer to use when looking up + # this shared group + # @param block The block to be eval'd + # @overload shared_examples(name, metadata, &block) + # @param name [String, Symbol, Module] identifer to use when looking up + # this shared group + # @param metadata [Array, Hash] metadata to attach to this + # group; any example group with matching metadata will automatically + # include this shared example group. + # @param block The block to be eval'd + # @overload shared_examples(metadata, &block) + # @param metadata [Array, Hash] metadata to attach to this + # group; any example group with matching metadata will automatically + # include this shared example group. + # @param block The block to be eval'd # - # When defined, the shared group block is stored for later evaluation. - # It can later be included in an example group either explicitly - # (using `include_examples`, `include_context` or `it_behaves_like`) - # or implicitly (via matching metadata). + # Stores the block for later use. The block will be evaluated + # in the context of an example group via `include_examples`, + # `include_context`, or `it_behaves_like`. # - # Named shared example groups are scoped based on where they are - # defined. Shared groups defined in an example group are available - # for inclusion in that example group or any child example groups, - # but not in any parent or sibling example groups. Shared example - # groups defined at the top level can be included from any example group. - module DefinitionAPI - # @overload shared_examples(name, &block) - # @param name [String, Symbol, Module] identifer to use when looking up - # this shared group - # @param block The block to be eval'd - # @overload shared_examples(name, metadata, &block) - # @param name [String, Symbol, Module] identifer to use when looking up - # this shared group - # @param metadata [Array, Hash] metadata to attach to this - # group; any example group with matching metadata will automatically - # include this shared example group. - # @param block The block to be eval'd - # @overload shared_examples(metadata, &block) - # @param metadata [Array, Hash] metadata to attach to this - # group; any example group with matching metadata will automatically - # include this shared example group. - # @param block The block to be eval'd - # - # Stores the block for later use. The block will be evaluated - # in the context of an example group via `include_examples`, - # `include_context`, or `it_behaves_like`. - # - # @example - # shared_examples "auditable" do - # it "stores an audit record on save!" do - # expect { auditable.save! }.to change(Audit, :count).by(1) - # end - # end - # - # describe Account do - # it_behaves_like "auditable" do - # let(:auditable) { Account.new } - # end - # end - # - # @see ExampleGroup.it_behaves_like - # @see ExampleGroup.include_examples - # @see ExampleGroup.include_context - def shared_examples(name, *args, &block) - top_level = self == ExampleGroup - if top_level && RSpec.thread_local_metadata[:in_example_group] - raise "Creating isolated shared examples from within a context is " \ - "not allowed. Remove `RSpec.` prefix or move this to a " \ - "top-level scope." - end - - RSpec.world.shared_example_group_registry.add(self, name, *args, &block) + # @example + # shared_examples "auditable" do + # it "stores an audit record on save!" do + # expect { auditable.save! }.to change(Audit, :count).by(1) + # end + # end + # + # describe Account do + # it_behaves_like "auditable" do + # let(:auditable) { Account.new } + # end + # end + # + # @see ExampleGroup.it_behaves_like + # @see ExampleGroup.include_examples + # @see ExampleGroup.include_context + def shared_examples(name, *args, &block) + top_level = self == ExampleGroup + if top_level && RSpec.thread_local_metadata[:in_example_group] + raise "Creating isolated shared examples from within a context is " \ + "not allowed. Remove `RSpec.` prefix or move this to a " \ + "top-level scope." end - alias shared_context shared_examples - alias shared_examples_for shared_examples + + RSpec.world.shared_example_group_registry.add(self, name, *args, &block) end + alias shared_context shared_examples + alias shared_examples_for shared_examples # @api private # @@ -146,7 +122,12 @@ def add(context, name, *metadata_args, &block) end return if metadata_args.empty? - RSpec.configuration.include SharedExampleGroup.new(name, block), *metadata_args + + mod = Module.new + (class << mod; self; end).__send__(:define_method, :included) do |host| + host.class_exec(&block) + end + RSpec.configuration.include mod, *metadata_args end def find(lookup_contexts, name) diff --git a/spec/rspec/core/shared_example_group_spec.rb b/spec/rspec/core/shared_example_group_spec.rb index 577707cdbf..7dcfc4a9fe 100644 --- a/spec/rspec/core/shared_example_group_spec.rb +++ b/spec/rspec/core/shared_example_group_spec.rb @@ -17,7 +17,7 @@ module Core ExampleClass = Class.new it 'does not add a bunch of private methods to Module' do - seg_methods = RSpec::Core::SharedExampleGroup::DefinitionAPI.private_instance_methods + seg_methods = RSpec::Core::SharedExampleGroup.private_instance_methods expect(Module.private_methods & seg_methods).to eq([]) end @@ -74,23 +74,6 @@ module Core ExampleGroup.describe('example group') { include_context 'top level in module' } end - it 'generates a named (rather than anonymous) module' do - define_shared_group("shared behaviors", :include_it) { } - group = RSpec.describe("Group", :include_it) { } - - anonymous_module_regex = /#/ - expect(Module.new.inspect).to match(anonymous_module_regex) - - include_a_named_rather_than_anonymous_module = ( - include(a_string_including( - "# Date: Tue, 4 Nov 2014 22:30:36 -0800 Subject: [PATCH 075/275] Re-implement shared example group reification. This is a reimplementation of d2633abc5a20334bac48a3c2513ab84a0c587284 from #1753. - Allows us to provide pretty `inspect` output, which is far better than the anonymous module output we normally get. - Simplifies the creation of the shared example group. Previously each group instantiated a module and defined `included` as a singleton method. --- lib/rspec/core/shared_example_group.rb | 31 ++++++++++++++++---- spec/rspec/core/shared_example_group_spec.rb | 17 +++++++++++ spec/support/matchers.rb | 1 + 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/lib/rspec/core/shared_example_group.rb b/lib/rspec/core/shared_example_group.rb index 6e369b5e31..c6bae95b7a 100644 --- a/lib/rspec/core/shared_example_group.rb +++ b/lib/rspec/core/shared_example_group.rb @@ -1,5 +1,29 @@ module RSpec module Core + # Represents some functionality that is shared with multiple example groups. + # The functionality is defined by the provided block, which is lazily + # eval'd when the `SharedExampleGroupModule` instance is included in an example + # group. + class SharedExampleGroupModule < Module + def initialize(description, definition) + @description = description + @definition = definition + end + + # Provides a human-readable representation of this module. + def inspect + "#<#{self.class.name} #{@description.inspect}>" + end + alias to_s inspect + + # Ruby callback for when a module is included in another module is class. + # Our definition evaluates the shared group block in the context of the + # including example group. + def included(klass) + klass.class_exec(&@definition) + end + end + # Shared example groups let you define common context and/or common # examples that you wish to use in multiple example groups. # @@ -122,12 +146,7 @@ def add(context, name, *metadata_args, &block) end return if metadata_args.empty? - - mod = Module.new - (class << mod; self; end).__send__(:define_method, :included) do |host| - host.class_exec(&block) - end - RSpec.configuration.include mod, *metadata_args + RSpec.configuration.include SharedExampleGroupModule.new(name, block), *metadata_args end def find(lookup_contexts, name) diff --git a/spec/rspec/core/shared_example_group_spec.rb b/spec/rspec/core/shared_example_group_spec.rb index 7dcfc4a9fe..83847b0dbd 100644 --- a/spec/rspec/core/shared_example_group_spec.rb +++ b/spec/rspec/core/shared_example_group_spec.rb @@ -74,6 +74,23 @@ module Core ExampleGroup.describe('example group') { include_context 'top level in module' } end + it 'generates a named (rather than anonymous) module' do + define_shared_group("shared behaviors", :include_it) { } + group = RSpec.describe("Group", :include_it) { } + + anonymous_module_regex = /#/ + expect(Module.new.inspect).to match(anonymous_module_regex) + + include_a_named_rather_than_anonymous_module = ( + include(a_string_including( + "# Date: Tue, 4 Nov 2014 13:07:59 -0800 Subject: [PATCH 076/275] Add shared group inclusion backtrace to example metadata. --- lib/rspec/core.rb | 2 +- lib/rspec/core/example_group.rb | 35 ++++++++-- lib/rspec/core/metadata.rb | 1 + lib/rspec/core/shared_example_group.rb | 4 +- spec/rspec/core/example_group_spec.rb | 34 ++++++++++ spec/rspec/core/metadata_spec.rb | 93 ++++++++++++++++++++++++++ spec/support/matchers.rb | 1 + 7 files changed, 161 insertions(+), 9 deletions(-) diff --git a/lib/rspec/core.rb b/lib/rspec/core.rb index b985d55d77..4b2d45d612 100644 --- a/lib/rspec/core.rb +++ b/lib/rspec/core.rb @@ -130,7 +130,7 @@ def self.current_example=(example) # A single thread local variable so we don't excessively pollute that # namespace. def self.thread_local_metadata - Thread.current[:_rspec] ||= {} + Thread.current[:_rspec] ||= { :shared_example_group_inclusions => [] } end # @private diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index b40a29e94e..d3cb0f7746 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -290,8 +290,8 @@ def self.define_nested_shared_group_method(new_name, report_label="it should beh # Pass :caller so the :location metadata is set properly. # Otherwise, it'll be set to the next line because that's # the block's source_location. - group = example_group("#{report_label} #{name}", :caller => caller) do - find_and_eval_shared("examples", name, *args, &customization_block) + group = example_group("#{report_label} #{name}", :caller => (the_caller = caller)) do + find_and_eval_shared("examples", name, the_caller.first, *args, &customization_block) end group.metadata[:shared_group_name] = name group @@ -312,7 +312,7 @@ def self.define_nested_shared_group_method(new_name, report_label="it should beh # # @see SharedExampleGroup::DefinitionAPI def self.include_context(name, *args, &block) - find_and_eval_shared("context", name, *args, &block) + find_and_eval_shared("context", name, caller.first, *args, &block) end # Includes shared content mapped to `name` directly in the group in which @@ -322,19 +322,21 @@ def self.include_context(name, *args, &block) # # @see SharedExampleGroup::DefinitionAPI def self.include_examples(name, *args, &block) - find_and_eval_shared("examples", name, *args, &block) + find_and_eval_shared("examples", name, caller.first, *args, &block) end # @private - def self.find_and_eval_shared(label, name, *args, &customization_block) + def self.find_and_eval_shared(label, name, inclusion_location, *args, &customization_block) shared_block = RSpec.world.shared_example_group_registry.find(parent_groups, name) unless shared_block raise ArgumentError, "Could not find shared #{label} #{name.inspect}" end - module_exec(*args, &shared_block) - module_exec(&customization_block) if customization_block + SharedExampleGroupInclusionStackFrame.with_frame(name, inclusion_location) do + module_exec(*args, &shared_block) + module_exec(&customization_block) if customization_block + end end # @!endgroup @@ -590,6 +592,25 @@ def self.metadata {} end end + + # Contains information about the inclusion site of a shared example group. + # @attr shared_group_name [String] the name of the shared example group + # @attr inclusion_location [String] the location where the shared example was included + SharedExampleGroupInclusionStackFrame = Struct.new(:shared_group_name, :inclusion_location) do + # @private + def self.current_backtrace + RSpec.thread_local_metadata[:shared_example_group_inclusions].reverse + end + + # @private + def self.with_frame(name, location) + current_stack = RSpec.thread_local_metadata[:shared_example_group_inclusions] + current_stack << new(name, location) + yield + ensure + current_stack.pop + end + end end # @private diff --git a/lib/rspec/core/metadata.rb b/lib/rspec/core/metadata.rb index 6d5e7d22dd..fe02db8af1 100644 --- a/lib/rspec/core/metadata.rb +++ b/lib/rspec/core/metadata.rb @@ -181,6 +181,7 @@ def self.create(group_metadata, user_metadata, description, block) group_metadata.update(example_metadata) example_metadata[:example_group] = group_metadata + example_metadata[:shared_group_inclusion_backtrace] = SharedExampleGroupInclusionStackFrame.current_backtrace example_metadata.delete(:parent_example_group) hash = new(example_metadata, user_metadata, [description].compact, block) diff --git a/lib/rspec/core/shared_example_group.rb b/lib/rspec/core/shared_example_group.rb index e5367a4184..a741f012b6 100644 --- a/lib/rspec/core/shared_example_group.rb +++ b/lib/rspec/core/shared_example_group.rb @@ -20,7 +20,9 @@ def inspect # Our definition evaluates the shared group block in the context of the # including example group. def included(klass) - klass.class_exec(&@definition) + SharedExampleGroupInclusionStackFrame.with_frame(@description, RSpec::CallerFilter.first_non_rspec_line) do + klass.class_exec(&@definition) + end end # Shared example groups let you define common context and/or common diff --git a/spec/rspec/core/example_group_spec.rb b/spec/rspec/core/example_group_spec.rb index 49ce5c3722..67f2e51169 100644 --- a/spec/rspec/core/example_group_spec.rb +++ b/spec/rspec/core/example_group_spec.rb @@ -1424,6 +1424,20 @@ def extract_execution_results(group) end.to raise_error(ArgumentError, /Could not find .* "shared stuff"/) end + it "leaves RSpec's thread metadata unchanged" do + expect { + group.send(name, "named this") + }.to avoid_changing(RSpec, :thread_local_metadata) + end + + it "leaves RSpec's thread metadata unchanged, even when an error occurs during evaluation" do + expect { + group.send(name, "named this") do + raise "boom" + end + }.to raise_error("boom").and avoid_changing(RSpec, :thread_local_metadata) + end + it "passes parameters to the shared content" do passed_params = {} group = ExampleGroup.describe @@ -1583,6 +1597,26 @@ def foo; end end end.to raise_error(ArgumentError,%q|Could not find shared examples "shared stuff"|) end + + it "leaves RSpec's thread metadata unchanged" do + expect { + RSpec.describe do + shared_examples_for("stuff") { } + it_should_behave_like "stuff" + end + }.to avoid_changing(RSpec, :thread_local_metadata) + end + + it "leaves RSpec's thread metadata unchanged, even when an error occurs during evaluation" do + expect { + RSpec.describe do + shared_examples_for("stuff") { } + it_should_behave_like "stuff" do + raise "boom" + end + end + }.to raise_error("boom").and avoid_changing(RSpec, :thread_local_metadata) + end end it 'minimizes the number of methods that users could inadvertantly overwrite' do diff --git a/spec/rspec/core/metadata_spec.rb b/spec/rspec/core/metadata_spec.rb index 39e3df6bc6..d3d11528a1 100644 --- a/spec/rspec/core/metadata_spec.rb +++ b/spec/rspec/core/metadata_spec.rb @@ -161,6 +161,99 @@ def metadata_for(*args) end end + describe ":shared_group_inclusion_backtrace" do + context "for an example group" do + it "is not set since we do not yet need it internally (but we can add it in the future if needed)" do + group = RSpec.describe("group") + expect(group.metadata).not_to include(:shared_group_inclusion_backtrace) + end + end + + context "for an example" do + context "not generated by a shared group" do + it "is a blank array" do + meta = nil + RSpec.describe { meta = example { }.metadata } + expect(meta).to include(:shared_group_inclusion_backtrace => []) + end + end + + context "generated by an unnested shared group included via metadata" do + it "is an array containing an object with shared group name and inclusion location" do + meta = nil + + RSpec.shared_examples_for("some shared behavior", :include_it => true) do + meta = example { }.metadata + end + + line = __LINE__ + 1 + RSpec.describe("Group", :include_it => true) { } + + expect(meta[:shared_group_inclusion_backtrace]).to match [ an_object_having_attributes( + :shared_group_name => "some shared behavior", + :inclusion_location => a_string_including("#{__FILE__}:#{line}") + ) ] + end + end + + { + :it_behaves_like => "generates a nested group", + :include_examples => "adds the examples directly to the host group" + }.each do |inclusion_method, description| + context "generated by an unnested shared group using an inclusion method that #{description}" do + it "is an array containing an object with shared group name and inclusion location" do + meta = nil + + RSpec.shared_examples_for("some shared behavior") do + meta = example { }.metadata + end + + line = __LINE__ + 2 + RSpec.describe do + __send__ inclusion_method, "some shared behavior" + end + + expect(meta[:shared_group_inclusion_backtrace]).to match [ an_object_having_attributes( + :shared_group_name => "some shared behavior", + :inclusion_location => a_string_including("#{__FILE__}:#{line}") + ) ] + end + end + + context "generated by a nested shared group using an inclusion method that #{description}" do + it "contains a stack frame for each inclusion, in the same order as ruby backtraces" do + meta = nil + + RSpec.shared_examples_for "inner" do + meta = example { }.metadata + end + + inner_line = __LINE__ + 2 + RSpec.shared_examples_for "outer" do + __send__ inclusion_method, "inner" + end + + outer_line = __LINE__ + 2 + RSpec.describe do + __send__ inclusion_method, "outer" + end + + expect(meta[:shared_group_inclusion_backtrace]).to match [ + an_object_having_attributes( + :shared_group_name => "inner", + :inclusion_location => a_string_including("#{__FILE__}:#{inner_line}") + ), + an_object_having_attributes( + :shared_group_name => "outer", + :inclusion_location => a_string_including("#{__FILE__}:#{outer_line}") + ), + ] + end + end + end + end + end + describe ":described_class" do value_from = lambda do |group| group.metadata[:described_class] diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index 60233823fd..ce038cff22 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -104,3 +104,4 @@ def failure_reason(example) RSpec::Matchers.define_negated_matcher :avoid_outputting, :output RSpec::Matchers.define_negated_matcher :exclude, :include +RSpec::Matchers.define_negated_matcher :avoid_changing, :change From a0f02d01b59fe23174af83534cd1629cd28ac2d9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 4 Nov 2014 15:35:30 -0800 Subject: [PATCH 077/275] Improve shared group backtrace in failure output. - Show all shared group inclusion stack frames, not just the inner-most one. - Make it work for all methods of including shared example groups. Previously, it only worked for the methods that generate a nested group. Fixes #1209. --- Changelog.md | 4 + lib/rspec/core/example_group.rb | 30 ++++++- lib/rspec/core/notifications.rb | 29 ++----- .../formatters/base_text_formatter_spec.rb | 81 +++++++++++++------ spec/rspec/core/notifications_spec.rb | 4 +- spec/support/formatter_support.rb | 4 +- 6 files changed, 99 insertions(+), 53 deletions(-) diff --git a/Changelog.md b/Changelog.md index 372d0d31f0..74b18ac24f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,6 +13,10 @@ Enhancements: * Removed dependency on pathname from stdlib. (Sam Phippen, #1703) * Improve the message presented when a user hits Ctrl-C. (Alex Chaffee #1717, #1742) +* Improve shared example group inclusion backtrace displayed + in failed example output so that it works for all methods + of including shared example groups and shows all inclusion + locations. (Myron Marston, #1763) ### 3.1.8 Development diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index d3cb0f7746..7b0f820f4f 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -594,9 +594,33 @@ def self.metadata end # Contains information about the inclusion site of a shared example group. - # @attr shared_group_name [String] the name of the shared example group - # @attr inclusion_location [String] the location where the shared example was included - SharedExampleGroupInclusionStackFrame = Struct.new(:shared_group_name, :inclusion_location) do + class SharedExampleGroupInclusionStackFrame + # @return [String] the name of the shared example group + attr_reader :shared_group_name + # @return [String] the location where the shared example was included + attr_reader :inclusion_location + + def initialize(shared_group_name, inclusion_location) + @shared_group_name = shared_group_name + @inclusion_location = inclusion_location + end + + # @return [String] The {#inclusion_location}, formatted for display by a formatter. + def formatted_inclusion_location + @formatted_inclusion_location ||= begin + RSpec.configuration.backtrace_formatter.backtrace_line( + inclusion_location.sub(/(:\d+):in .+$/, '\1') + ) + end + end + + # @return [String] Description of this stack frame, in the form used by + # RSpec's built-in formatters. + def description + @description ||= "Shared Example Group: #{shared_group_name.inspect} " \ + "called from #{formatted_inclusion_location}" + end + # @private def self.current_backtrace RSpec.thread_local_metadata[:shared_example_group_inclusions].reverse diff --git a/lib/rspec/core/notifications.rb b/lib/rspec/core/notifications.rb index 5173a012a1..1122d6d7d1 100644 --- a/lib/rspec/core/notifications.rb +++ b/lib/rspec/core/notifications.rb @@ -153,7 +153,7 @@ def description # # @return [Array(String)] The example failure message def message_lines - add_shared_group_line(failure_lines, NullColorizer) + add_shared_group_lines(failure_lines, NullColorizer) end # Returns the message generated for this failure colorized line by line. @@ -161,7 +161,7 @@ def message_lines # @param colorizer [#wrap] An object to colorize the message_lines by # @return [Array(String)] The example failure message colorized def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes) - add_shared_group_line(failure_lines, colorizer).map do |line| + add_shared_group_lines(failure_lines, colorizer).map do |line| colorizer.wrap line, RSpec.configuration.failure_color end end @@ -232,29 +232,12 @@ def failure_lines end end - def add_shared_group_line(lines, colorizer) - unless shared_group_line == "" - lines << colorizer.wrap(shared_group_line, RSpec.configuration.default_color) + def add_shared_group_lines(lines, colorizer) + example.metadata[:shared_group_inclusion_backtrace].each do |frame| + lines << colorizer.wrap(frame.description, RSpec.configuration.default_color) end - lines - end - - def shared_group - @shared_group ||= group_and_parent_groups.find { |group| group.metadata[:shared_group_name] } - end - def shared_group_line - @shared_group_line ||= - if shared_group - "Shared Example Group: \"#{shared_group.metadata[:shared_group_name]}\"" \ - " called from #{backtrace_formatter.backtrace_line(shared_group.location)}" - else - "" - end - end - - def group_and_parent_groups - example.example_group.parent_groups + [example.example_group] + lines end def read_failed_line diff --git a/spec/rspec/core/formatters/base_text_formatter_spec.rb b/spec/rspec/core/formatters/base_text_formatter_spec.rb index 16eb163f10..51dbddc755 100644 --- a/spec/rspec/core/formatters/base_text_formatter_spec.rb +++ b/spec/rspec/core/formatters/base_text_formatter_spec.rb @@ -129,40 +129,71 @@ def run_all_and_dump_failures end end - context 'for #shared_examples' do - it 'outputs the name and location' do - group.shared_examples 'foo bar' do - it("example name") { expect("this").to eq("that") } - end - - line = __LINE__.next - group.it_should_behave_like('foo bar') - - run_all_and_dump_failures - - expect(output.string).to include( - 'Shared Example Group: "foo bar" called from ' + - "#{RSpec::Core::Metadata.relative_path(__FILE__)}:#{line}" - ) - end - - context 'that contains nested example groups' do + %w[ include_examples it_should_behave_like ].each do |inclusion_method| + context "for #shared_examples included using #{inclusion_method}" do it 'outputs the name and location' do group.shared_examples 'foo bar' do - describe 'nested group' do - it("example name") { expect("this").to eq("that") } - end + it("example name") { expect("this").to eq("that") } end line = __LINE__.next - group.it_should_behave_like('foo bar') + group.__send__(inclusion_method, 'foo bar') run_all_and_dump_failures - expect(output.string).to include( + expect(output.string.lines).to include(a_string_ending_with( 'Shared Example Group: "foo bar" called from ' + - "./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{line}" - ) + "#{RSpec::Core::Metadata.relative_path(__FILE__)}:#{line}\n" + )) + end + + context 'that contains nested example groups' do + it 'outputs the name and location' do + group.shared_examples 'foo bar' do + describe 'nested group' do + it("example name") { expect("this").to eq("that") } + end + end + + line = __LINE__.next + group.__send__(inclusion_method, 'foo bar') + + run_all_and_dump_failures + + expect(output.string.lines).to include(a_string_ending_with( + 'Shared Example Group: "foo bar" called from ' + + "./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{line}\n" + )) + end + end + + context "that contains shared group nesting" do + it 'includes each inclusion location in the output' do + group.shared_examples "inner" do + example { expect(1).to eq(2) } + end + + inner_line = __LINE__ + 2 + group.shared_examples "outer" do + __send__(inclusion_method, "inner") + end + + outer_line = __LINE__ + 1 + group.__send__(inclusion_method, 'outer') + + run_all_and_dump_failures + + expect(output.string.lines.grep(/Shared Example Group/)).to match [ + a_string_ending_with( + 'Shared Example Group: "inner" called from ' + + "./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{inner_line}\n" + ), + a_string_ending_with( + 'Shared Example Group: "outer" called from ' + + "./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{outer_line}\n" + ), + ] + end end end end diff --git a/spec/rspec/core/notifications_spec.rb b/spec/rspec/core/notifications_spec.rb index 2d3eded4fd..47f0ac93b7 100644 --- a/spec/rspec/core/notifications_spec.rb +++ b/spec/rspec/core/notifications_spec.rb @@ -83,7 +83,9 @@ end it 'returns failures_lines without color when they are part of a shared example group' do - allow(example_group).to receive(:metadata) { {:shared_group_name => 'double shared group'} } + example.metadata[:shared_group_inclusion_backtrace] << + RSpec::Core::SharedExampleGroupInclusionStackFrame.new("foo", "bar") + lines = notification.message_lines expect(lines[0]).to match %r{\AFailure\/Error} expect(lines[1]).to match %r{\A\s*Test exception\z} diff --git a/spec/support/formatter_support.rb b/spec/support/formatter_support.rb index 64455f165d..50df710528 100644 --- a/spec/support/formatter_support.rb +++ b/spec/support/formatter_support.rb @@ -203,7 +203,9 @@ def example :full_description => "Example", :execution_result => result, :location => "", - :metadata => {} + :metadata => { + :shared_group_inclusion_backtrace => [] + } ) end end From 43f01a09db8e5680784e3b561824e73e823d7ca8 Mon Sep 17 00:00:00 2001 From: Arlandis Lawrence Date: Mon, 3 Nov 2014 18:55:37 -0600 Subject: [PATCH 078/275] Show seed at start of suite run --- lib/rspec/core/reporter.rb | 1 + spec/rspec/core/reporter_spec.rb | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/lib/rspec/core/reporter.rb b/lib/rspec/core/reporter.rb index 4f5c6aae3d..a6866d333b 100644 --- a/lib/rspec/core/reporter.rb +++ b/lib/rspec/core/reporter.rb @@ -70,6 +70,7 @@ def start(expected_example_count, time=RSpec::Core::Time.now) @start = time @load_time = (@start - @configuration.start_time).to_f notify :start, Notifications::StartNotification.new(expected_example_count, @load_time) + notify :seed, Notifications::SeedNotification.new(@configuration.seed, seed_used?) end # @private diff --git a/spec/rspec/core/reporter_spec.rb b/spec/rspec/core/reporter_spec.rb index 409db087a6..fed725da10 100644 --- a/spec/rspec/core/reporter_spec.rb +++ b/spec/rspec/core/reporter_spec.rb @@ -49,6 +49,16 @@ module RSpec::Core reporter.start 3, (start_time + 5) end + + it 'notifies formatters of the seed used' do + formatter = double("formatter") + reporter.register_listener formatter, :seed + + expect(formatter).to receive(:seed).with( + an_object_having_attributes(:seed => config.seed, :seed_used? => config.seed_used?) + ) + reporter.start 1 + end end context "given one formatter" do From bc6adc8e22a512ea21a7761f2a697c4236ade664 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 6 Nov 2014 13:16:59 +1100 Subject: [PATCH 079/275] changelog for #1761 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 74b18ac24f..d8387d7289 100644 --- a/Changelog.md +++ b/Changelog.md @@ -17,6 +17,8 @@ Enhancements: in failed example output so that it works for all methods of including shared example groups and shows all inclusion locations. (Myron Marston, #1763) +* Issue seed notification at start (as well as the end) of the reporter + run. (Arlandis Word, #1761) ### 3.1.8 Development From 969f249e517cea8f8057afeae27fe87313a76d17 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 6 Nov 2014 20:56:24 -0800 Subject: [PATCH 080/275] Remove extra blank line from seed output. The seed output had two new lines, which worked well at the end (since we want a blank line after our output) but it looked funny for the new seed notification at the start. --- lib/rspec/core/formatters/base_text_formatter.rb | 6 ++++-- lib/rspec/core/notifications.rb | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/rspec/core/formatters/base_text_formatter.rb b/lib/rspec/core/formatters/base_text_formatter.rb index c378702314..03b79fb18a 100644 --- a/lib/rspec/core/formatters/base_text_formatter.rb +++ b/lib/rspec/core/formatters/base_text_formatter.rb @@ -64,9 +64,11 @@ def seed(notification) # @param _notification [NullNotification] (Ignored) def close(_notification) return unless IO === output - return if output.closed? || output == $stdout + return if output.closed? - output.close + output.puts + + output.close unless output == $stdout end end end diff --git a/lib/rspec/core/notifications.rb b/lib/rspec/core/notifications.rb index 1122d6d7d1..37e657ff4c 100644 --- a/lib/rspec/core/notifications.rb +++ b/lib/rspec/core/notifications.rb @@ -332,7 +332,7 @@ def seed_used? # @return [String] The seed information fully formatted in the way that # RSpec's built-in formatters emit. def fully_formatted - "\nRandomized with seed #{seed}\n\n" + "\nRandomized with seed #{seed}\n" end end From 258f656f0508320d8afcf73963843e283a98a7a3 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 9 Nov 2014 15:23:28 -0800 Subject: [PATCH 081/275] Specify relationship of `after` hooks and mock verification. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While it’s not recommended to rely on this behavior (it's pretty weird to satisfy a mock expectation in an `after` hook...), this is consistent with how we treat `after` hooks: we don't set the example's `execution_status` until after the `after` hooks run because if an error happens in them we need to set the examples's status to `:failed`. Thus, we should not verify mock expectations until after `after` hooks since the example has not fully completed until those hooks have run. This clarifies the behavior in question in #1769. --- spec/rspec/core/example_spec.rb | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/spec/rspec/core/example_spec.rb b/spec/rspec/core/example_spec.rb index b6e6e00fb7..4dc83bc5c3 100644 --- a/spec/rspec/core/example_spec.rb +++ b/spec/rspec/core/example_spec.rb @@ -659,4 +659,35 @@ def expect_pending_result(example) expect(current_examples).to eq([example1, example2]) end end + + describe "mock framework integration" do + it 'verifies mock expectations after each example' do + ex = nil + + RSpec.describe do + ex = example do + dbl = double + expect(dbl).to receive(:foo) + end + end.run + + expect(ex).to fail_with(RSpec::Mocks::MockExpectationError) + end + + it 'allows `after(:example)` hooks to satisfy mock expectations, since examples are not complete until their `after` hooks run' do + ex = nil + + RSpec.describe do + let(:dbl) { double } + + ex = example do + expect(dbl).to receive(:foo) + end + + after { dbl.foo } + end.run + + expect(ex).to pass + end + end end From c2c7d23a0323c37be15e0ec1e6046d01dc605f58 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 9 Nov 2014 15:40:40 -0800 Subject: [PATCH 082/275] Clarify relationship of `after` hook errors and generated descriptions. Errors in `after` hooks should not prevent generated descriptions from being assigned. I thought that there was a bug here based on the `rescue` clause as it looked like errors in `after` hooks would bypass the generated description assignment, but `hooks.run` internally rescues errors so the rescue clause is never reached. The spec still has value in specifying an under-specified area. --- lib/rspec/core/example.rb | 2 -- spec/rspec/core/example_spec.rb | 13 +++++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 85c7f27ea2..5c304f295a 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -387,8 +387,6 @@ def run_after_example @example_group_class.hooks.run(:after, :example, self) verify_mocks assign_generated_description if RSpec.configuration.expecting_with_rspec? - rescue Exception => e - set_exception(e, "in an `after(:example)` hook") ensure @example_group_instance.teardown_mocks_for_rspec end diff --git a/spec/rspec/core/example_spec.rb b/spec/rspec/core/example_spec.rb index 4dc83bc5c3..f72d9afc66 100644 --- a/spec/rspec/core/example_spec.rb +++ b/spec/rspec/core/example_spec.rb @@ -115,6 +115,19 @@ def assert(val) expect(example.description).to match(/example at #{relative_path(__FILE__)}:#{__LINE__ - 2}/) end end + + context "when an `after(:example)` hook raises an error" do + it 'still assigns the description' do + ex = nil + + RSpec.describe do + ex = example { expect(2).to eq(2) } + after { raise "boom" } + end.run + + expect(ex.description).to eq("should eq 2") + end + end end context "when `expect_with :rspec, :stdlib` is configured" do From abd83d29fa55daf9e8c85a630a55c6f393086b22 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 9 Nov 2014 15:56:00 -0800 Subject: [PATCH 083/275] Surface matcher.description errors in generated doc string. This is better than printing it to the console when it occurs. --- lib/rspec/core/example.rb | 16 ++++++++++++---- spec/rspec/core/example_spec.rb | 23 +++++++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 5c304f295a..daee0b6945 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -75,7 +75,7 @@ def self.delegate_to_metadata(key) # the location of the example. def description description = if metadata[:description].to_s.empty? - "example at #{location}" + location_description else metadata[:description] end @@ -407,16 +407,24 @@ def mocks_need_verification? end def assign_generated_description - if metadata[:description].empty? && (description = RSpec::Matchers.generated_description) + if metadata[:description].empty? && (description = get_generated_description) metadata[:description] = description metadata[:full_description] << description end - rescue Exception => e - set_exception(e, "while assigning the example description") ensure RSpec::Matchers.clear_generated_description end + def get_generated_description + RSpec::Matchers.generated_description + rescue Exception => e + location_description + " (Got an error when generating description from matcher: #{e.class}: #{e.message} -- #{e.backtrace.first})" + end + + def location_description + "example at #{location}" + end + def skip_message if String === skip skip diff --git a/spec/rspec/core/example_spec.rb b/spec/rspec/core/example_spec.rb index f72d9afc66..f64f1b5c8f 100644 --- a/spec/rspec/core/example_spec.rb +++ b/spec/rspec/core/example_spec.rb @@ -128,6 +128,29 @@ def assert(val) expect(ex.description).to eq("should eq 2") end end + + context "when the matcher's `description` method raises an error" do + description_line = __LINE__ + 3 + RSpec::Matchers.define :matcher_with_failing_description do + match { true } + description { raise ArgumentError, "boom" } + end + + it 'allows the example to pass and surfaces the failing description in the example description' do + ex = nil + + RSpec.describe do + ex = example { expect(2).to matcher_with_failing_description } + end.run + + expect(ex).to pass.and have_attributes(description: a_string_including( + "example at #{ex.location}", + "ArgumentError", + "boom", + "#{__FILE__}:#{description_line}" + )) + end + end end context "when `expect_with :rspec, :stdlib` is configured" do From 20377ab93b6a2d069e8c604be518ef8f773169b2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 9 Nov 2014 15:58:25 -0800 Subject: [PATCH 084/275] Don't consider `after` hook expectations for generated descriptions. Otherwise, in an example like this: RSpec.describe SomeClass do it { is_expected.to do_x } it { is_expected.to do_y } after { expect(foo).to satisfy_some_invariant } end ...each example would get the "should satisfy some invariant" doc string, rather than "should do x" and "should do y." --- lib/rspec/core/example.rb | 2 +- spec/rspec/core/example_spec.rb | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index daee0b6945..9a504b93fe 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -384,9 +384,9 @@ def run_before_example end def run_after_example + assign_generated_description if RSpec.configuration.expecting_with_rspec? @example_group_class.hooks.run(:after, :example, self) verify_mocks - assign_generated_description if RSpec.configuration.expecting_with_rspec? ensure @example_group_instance.teardown_mocks_for_rspec end diff --git a/spec/rspec/core/example_spec.rb b/spec/rspec/core/example_spec.rb index f64f1b5c8f..dfc7f54f75 100644 --- a/spec/rspec/core/example_spec.rb +++ b/spec/rspec/core/example_spec.rb @@ -151,6 +151,19 @@ def assert(val) )) end end + + context "when an `after(:example)` hook has an expectation" do + it "assigns the description based on the example's last expectation, ignoring the `after` expectation since it can apply to many examples" do + ex = nil + + RSpec.describe do + ex = example { expect(nil).to be_nil } + after { expect(true).to eq(true) } + end.run + + expect(ex).to pass.and have_attributes(description: "should be nil") + end + end end context "when `expect_with :rspec, :stdlib` is configured" do From cf907501ff72a298b555b327971cab04e1a7d227 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 9 Nov 2014 16:02:35 -0800 Subject: [PATCH 085/275] Add changelog entries. --- Changelog.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Changelog.md b/Changelog.md index d8387d7289..31c733a9c5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,14 @@ Enhancements: * Issue seed notification at start (as well as the end) of the reporter run. (Arlandis Word, #1761) +Bug Fixes: + +* When assigning generated example descriptions, surface errors + raised by `matcher.description` in the example description. + (Myron Marston, #1771) +* Don't consider expectations from `after` hooks when generating + example descriptions. (Myron Marston, #1771) + ### 3.1.8 Development Bug Fixes: From 96d60a3aafa8ce9cb51b50487c51a7388d4cd09e Mon Sep 17 00:00:00 2001 From: Kevin Mook Date: Mon, 10 Nov 2014 01:04:55 -0500 Subject: [PATCH 086/275] Fix an exception when the DeprecationFormatter is initialized with an IO stream that doesn't respond to :path. --- lib/rspec/core/formatters/deprecation_formatter.rb | 3 ++- spec/rspec/core/formatters/deprecation_formatter_spec.rb | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/rspec/core/formatters/deprecation_formatter.rb b/lib/rspec/core/formatters/deprecation_formatter.rb index 5cbe73593a..118fe88761 100644 --- a/lib/rspec/core/formatters/deprecation_formatter.rb +++ b/lib/rspec/core/formatters/deprecation_formatter.rb @@ -210,7 +210,8 @@ def puts(*args) end def summarize(summary_stream, deprecation_count) - summary_stream.puts "\n#{Helpers.pluralize(deprecation_count, 'deprecation')} logged to #{@file.path}" + path = @file.respond_to?(:path) ? @file.path : @file.inspect + summary_stream.puts "\n#{Helpers.pluralize(deprecation_count, 'deprecation')} logged to #{path}" puts RAISE_ERROR_CONFIG_NOTICE end end diff --git a/spec/rspec/core/formatters/deprecation_formatter_spec.rb b/spec/rspec/core/formatters/deprecation_formatter_spec.rb index e1401b90c4..33698c265f 100644 --- a/spec/rspec/core/formatters/deprecation_formatter_spec.rb +++ b/spec/rspec/core/formatters/deprecation_formatter_spec.rb @@ -111,6 +111,12 @@ def notification(hash) expect(summary_stream.string).to match(/1 deprecation/) expect(File.read(deprecation_stream.path)).to eq("foo is deprecated.\n#{DeprecationFormatter::RAISE_ERROR_CONFIG_NOTICE}") end + + it "can handle when the stream is reopened to a system stream" do + send_notification :deprecation, notification(:deprecated => 'foo') + deprecation_stream.reopen(IO.for_fd(IO.sysopen('/dev/null', "w+"))) + send_notification :deprecation_summary, null_notification + end end context "with an Error deprecation_stream" do From 06cdba596eb636fa3b30408e7117168143703544 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 11 Nov 2014 12:01:19 +1100 Subject: [PATCH 087/275] changelog for #1757 [skip ci] --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 31c733a9c5..43b559d102 100644 --- a/Changelog.md +++ b/Changelog.md @@ -34,6 +34,9 @@ Bug Fixes: * Apply `--seed` before loading `--require` files so that required files can access the provided seed. (Myron Marston, #1745) +* Handle `RSpec::Core::Formatters::DeprecationFormatter::FileStream` being + reopened with an IO stream, which sometimes happens with spring. + (Kevin Mook, #1757) ### 3.1.7 / 2014-10-11 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.6...v3.1.7) From aac2248e70466e15107c078faa02e730846d103e Mon Sep 17 00:00:00 2001 From: Jim Kingdon Date: Tue, 11 Nov 2014 08:35:05 -0800 Subject: [PATCH 088/275] Also document around/exception behavior. --- features/hooks/around_hooks.feature | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/features/hooks/around_hooks.feature b/features/hooks/around_hooks.feature index 47a71e0197..1ce45f1e40 100644 --- a/features/hooks/around_hooks.feature +++ b/features/hooks/around_hooks.feature @@ -85,6 +85,29 @@ Feature: `around` hooks When I run `rspec example_spec.rb` Then the output should contain "this should show up in the output" + Scenario: An around hook continues to run even if the example throws an exception + Given a file named "example_spec.rb" with: + """ruby + RSpec.describe "something" do + around(:example) do |example| + puts "around example setup" + example.run + puts "around example cleanup" + end + + it "still executes the entire around hook" do + fail "the example blows up" + end + end + """ + When I run `rspec example_spec.rb` + Then the output should contain "1 example, 1 failure" + And the output should contain: + """ + around example setup + around example cleanup + """ + Scenario: Define a global `around` hook Given a file named "example_spec.rb" with: """ruby From e83230dc790e856bcd466ba830c857eada610b9d Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 12 Nov 2014 08:12:05 +1100 Subject: [PATCH 089/275] Changelog for #1772 [skip ci] --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 43b559d102..1e402f61da 100644 --- a/Changelog.md +++ b/Changelog.md @@ -19,6 +19,7 @@ Enhancements: locations. (Myron Marston, #1763) * Issue seed notification at start (as well as the end) of the reporter run. (Arlandis Word, #1761) +* Improve the documentation of around hooks. (Jim Kingdon, #1772) Bug Fixes: From 25809a27923a7535a7cd08b703735daa0542d0fa Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 12 Nov 2014 00:37:08 -0800 Subject: [PATCH 090/275] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 16 +++++++++++++++- script/predicate_functions.sh | 20 ++++++++++++++++++-- script/run_build | 2 +- script/travis_functions.sh | 4 +++- 7 files changed, 40 insertions(+), 8 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 55fee1927d..4008c08099 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 2367ca1b94..5702a058d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index fb840d7aba..74651bd6ba 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 0ff87a8baf..903b45ed9a 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. source script/travis_functions.sh @@ -94,6 +94,20 @@ function check_documentation_coverage { exit(1) end " + + # Some warnings only show up when generating docs, so do that as well. + bin/yard doc --no-cache | ruby -e " + while line = gets + has_warnings ||= line.start_with?('[warn]:') + has_errors ||= line.start_with?('[error]:') + puts line + end + + if has_warnings || has_errors + puts \"\n\nYARD emitted documentation warnings or errors.\" + exit(1) + end + " } function check_style_and_lint { diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index cc4a73dbbf..19053f0be8 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { @@ -23,6 +23,18 @@ function is_mri_192 { fi } +function is_mri_2plus { + if is_mri; then + if ruby -e "exit(RUBY_VERSION.to_f > 2.0)"; then + return 0 + else + return 1 + fi + else + return 1 + fi +} + function rspec_support_compatible { if [ "$MAINTENANCE_BRANCH" != "2-99-maintenance" ] && [ "$MAINTENANCE_BRANCH" != "2-14-maintenance" ]; then return 0 @@ -33,7 +45,11 @@ function rspec_support_compatible { function documentation_enforced { if [ -x ./bin/yard ]; then - return 0 + if is_mri_2plus; then + return 0 + else + return 1 + fi else return 1 fi diff --git a/script/run_build b/script/run_build index 6618219085..2075b66a24 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 0e1715dba4..db6b014d66 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-10-30T08:23:40-07:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: @@ -63,4 +63,6 @@ fold() { else STATUS="$status" fi + + return $status } From 1e13b5f821ff9edc591c4956c1e229596dde5093 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 10 Nov 2014 23:56:51 -0800 Subject: [PATCH 091/275] Use 1.8.7 compatible ruby syntax. --- spec/rspec/core/example_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/rspec/core/example_spec.rb b/spec/rspec/core/example_spec.rb index dfc7f54f75..9946e0da19 100644 --- a/spec/rspec/core/example_spec.rb +++ b/spec/rspec/core/example_spec.rb @@ -143,7 +143,7 @@ def assert(val) ex = example { expect(2).to matcher_with_failing_description } end.run - expect(ex).to pass.and have_attributes(description: a_string_including( + expect(ex).to pass.and have_attributes(:description => a_string_including( "example at #{ex.location}", "ArgumentError", "boom", @@ -161,7 +161,7 @@ def assert(val) after { expect(true).to eq(true) } end.run - expect(ex).to pass.and have_attributes(description: "should be nil") + expect(ex).to pass.and have_attributes(:description => "should be nil") end end end From ce09f250660dabebfaf1edcba364814aea7243dc Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 10 Nov 2014 23:59:23 -0800 Subject: [PATCH 092/275] Satisfy rubocop. Offenses: lib/rspec/core/example.rb:418:11: C: Do not prefix reader method names with get_. def get_generated_description ^^^^^^^^^^^^^^^^^^^^^^^^^ lib/rspec/core/example.rb:421:131: C: Line is too long. [139/130] location_description + " (Got an error when generating description from matcher: #{e.class}: #{e.message} -- #{e.backtrace.first})" ^^^^^^^^^ --- lib/rspec/core/example.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 9a504b93fe..c909a61f6b 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -407,7 +407,7 @@ def mocks_need_verification? end def assign_generated_description - if metadata[:description].empty? && (description = get_generated_description) + if metadata[:description].empty? && (description = generate_description) metadata[:description] = description metadata[:full_description] << description end @@ -415,10 +415,11 @@ def assign_generated_description RSpec::Matchers.clear_generated_description end - def get_generated_description + def generate_description RSpec::Matchers.generated_description rescue Exception => e - location_description + " (Got an error when generating description from matcher: #{e.class}: #{e.message} -- #{e.backtrace.first})" + location_description + " (Got an error when generating description " \ + "from matcher: #{e.class}: #{e.message} -- #{e.backtrace.first})" end def location_description From 86f1097399a05a3f1aff19548c8ffe3888f31136 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 11 Nov 2014 00:09:06 -0800 Subject: [PATCH 093/275] Fix invalid YARD method reference. [warn]: In file `lib/rspec/core/formatters/protocol.rb':3: Cannot resolve link to #dump_failure from text: [warn]: ...{#dump_failure}... --- lib/rspec/core/formatters/protocol.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/core/formatters/protocol.rb b/lib/rspec/core/formatters/protocol.rb index 7d0355efb7..f9e2ae5762 100644 --- a/lib/rspec/core/formatters/protocol.rb +++ b/lib/rspec/core/formatters/protocol.rb @@ -116,7 +116,7 @@ class Protocol # # This method is invoked after all of the examples have executed. The # next method to be invoked after this one is {#dump_failures} - # (BaseTextFormatter then calls {#dump_failure} once for each failed + # (BaseTextFormatter then calls {#dump_failures} once for each failed # example). # # @param notification [NullNotification] From 7e6d1e4ac99433e286c057ec23f28608ac01206c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 11 Nov 2014 10:38:06 -0800 Subject: [PATCH 094/275] Fix YARD warnings. [warn]: In file `lib/rspec/core/rake_task.rb':29: Cannot resolve link to ,// from text: [warn]: ...{,//}... [warn]: In file `lib/rspec/core/rake_task.rb':29: Cannot resolve link to ,// from text: [warn]: ...{,//}... Note that I haven't been able to repro these warnings locally with YARD but they happen on travis. While I was at it, I fixed up some inaccuracies in the documented defaults for rake task options. --- lib/rspec/core/rake_task.rb | 39 +++++++++---------------------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/lib/rspec/core/rake_task.rb b/lib/rspec/core/rake_task.rb index f583128a28..4d6ea9083e 100644 --- a/lib/rspec/core/rake_task.rb +++ b/lib/rspec/core/rake_task.rb @@ -16,57 +16,36 @@ class RakeTask < ::Rake::TaskLib # Default pattern for spec files. DEFAULT_PATTERN = 'spec/**{,/*/**}/*_spec.rb' - # Name of task. - # - # default: - # :spec + # Name of task. Defaults to `:spec`. attr_accessor :name # Files matching this pattern will be loaded. - # - # default: - # 'spec/**{,/*/**}/*_spec.rb' + # Defaults to `'spec/**{,/*/**}/*_spec.rb'`. attr_accessor :pattern # Files matching this pattern will be excluded. - # - # default: - # 'spec/**/*_spec.rb' + # Defaults to `nil`. attr_accessor :exclude_pattern # Whether or not to fail Rake when an error occurs (typically when - # examples fail). - # - # default: - # true + # examples fail). Defaults to `true`. attr_accessor :fail_on_error # A message to print to stderr when there are failures. attr_accessor :failure_message # Use verbose output. If this is set to true, the task will print the - # executed spec command to stdout. - # - # default: - # true + # executed spec command to stdout. Defaults to `true`. attr_accessor :verbose - # Command line options to pass to ruby. - # - # default: - # nil + # Command line options to pass to ruby. Defaults to `nil`. attr_accessor :ruby_opts - # Path to RSpec. - # - # default: - # 'rspec' + # Path to RSpec. Defaults to the absolute path to the + # rspec binary from the loaded rspec-core gem. attr_accessor :rspec_path - # Command line options to pass to RSpec. - # - # default: - # nil + # Command line options to pass to RSpec. Defaults to `nil`. attr_accessor :rspec_opts def initialize(*args, &task_block) From 3a57caff5ca0870129bf44148b6cbceeb49bdef9 Mon Sep 17 00:00:00 2001 From: Aaron Kromer Date: Wed, 12 Nov 2014 12:16:20 -0500 Subject: [PATCH 095/275] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 7 ++++--- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 4008c08099..d9af13b17d 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T12:16:20-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 5702a058d1..e55bccb49c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T12:16:20-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 74651bd6ba..9b38d96bc5 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T12:16:20-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 903b45ed9a..c9dc186741 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,8 +1,9 @@ -# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T12:16:20-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. -source script/travis_functions.sh -source script/predicate_functions.sh +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +source $SCRIPT_DIR/travis_functions.sh +source $SCRIPT_DIR/predicate_functions.sh # idea taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html export JRUBY_OPTS="${JRUBY_OPTS} -X-C" # disable JIT since these processes are so short lived diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 19053f0be8..2b6fba1091 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T12:16:20-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 2075b66a24..898d46c4a5 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T12:16:20-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index db6b014d66..3175d6af46 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T00:37:08-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-12T12:16:20-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From b8ead8dbd7554370be42747eea0a1ef5ec13da64 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 13 Nov 2014 15:30:21 -0800 Subject: [PATCH 096/275] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 25 +++++++++++++++++++++++++ script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 appveyor.yml diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index d9af13b17d..34d4f8cd94 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T12:16:20-05:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index e55bccb49c..05cad069d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T12:16:20-05:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000000..495be45952 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,25 @@ +# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# DO NOT modify it by hand as your changes will get lost the next time it is generated. + +version: "{build}" + +# Disable normal Windows builds in favor of our test script. +build: off + +install: + - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% + - ruby --version + - gem --version + - gem install bundler + - bundler --version + - bundle install + - cinst ansicon + +test_script: + - bundle exec rspec + +environment: + matrix: + # ruby_version: '20' doesn't work for some reason + - ruby_version: '193' + - ruby_version: '21' diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 9b38d96bc5..e0ec88131c 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-12T12:16:20-05:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index c9dc186741..e5fb9eab4c 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T12:16:20-05:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 2b6fba1091..12e96326e4 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T12:16:20-05:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 898d46c4a5..dbbfcb22e3 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-12T12:16:20-05:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 3175d6af46..54c92a215b 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-12T12:16:20-05:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 3fc056db6447ec6f64dc8389c427a7e02f164249 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 13 Nov 2014 15:41:32 -0800 Subject: [PATCH 097/275] =?UTF-8?q?`[.]`=20doesn=E2=80=99t=20appear=20to?= =?UTF-8?q?=20work=20well=20on=20windows.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/rspec/core/configuration.rb | 2 +- spec/rspec/core/configuration_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index dcf59c7a6a..bc30e8d21a 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1374,7 +1374,7 @@ def get_files_to_run(paths) def paths_to_check(paths) return paths if pattern_might_load_specs_from_vendored_dirs? - paths + ['.'] + paths + [Dir.getwd] end def pattern_might_load_specs_from_vendored_dirs? diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index a5357f6276..1ba814b758 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -578,11 +578,11 @@ def loaded_files end def specify_consistent_ordering_of_files_to_run + allow(File).to receive(:directory?).and_call_original allow(File).to receive(:directory?).with('a') { true } - allow(File).to receive(:directory?).with('.') { true } globbed_files = nil allow(Dir).to receive(:[]).with(/^\{?a/) { globbed_files } - allow(Dir).to receive(:[]).with(/^\{?\./) { [] } + allow(Dir).to receive(:[]).with(a_string_starting_with(Dir.getwd)) { [] } orderings = [ %w[ a/1.rb a/2.rb a/3.rb ], From ed932b8fcdb5fa68efc811b7e584625d2ca31bd2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 13 Nov 2014 15:41:46 -0800 Subject: [PATCH 098/275] Pend symlink specs on windows. --- spec/support/shared_example_groups.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/support/shared_example_groups.rb b/spec/support/shared_example_groups.rb index 5751babf60..4a732c1baa 100644 --- a/spec/support/shared_example_groups.rb +++ b/spec/support/shared_example_groups.rb @@ -15,6 +15,10 @@ include_context "isolated directory" let(:project_dir) { Dir.getwd } + before(:example) do + pending "Windows does not support symlinking" + end if RSpec::Support::OS.windows? + it "finds the files" do foos_dir = File.join(project_dir, "spec/foos") FileUtils.mkdir_p foos_dir From a4e4baecf9b5b4432ee397fa55f1e56d0ca64a38 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 13 Nov 2014 15:44:03 -0800 Subject: [PATCH 099/275] Fix ansicon specs on windows. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Explicitly clear `ANSICON` ENV var in the context concerning when it’s not set. On appveyor we install ANSICON so it is in fact normally set. - Move the specs out of the context that sets `config.color = true` in a `before` hook. This was causing problems because it triggered warnings before our `allow_warnings` code had a chance to silence them. --- spec/rspec/core/configuration_spec.rb | 78 ++++++++++++++------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index 1ba814b758..f5796f203f 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -1003,54 +1003,56 @@ def metadata_hash(*args) expect(config.color_enabled?(output)).to be_falsey end end + end - context "on windows" do - before do - @original_host = RbConfig::CONFIG['host_os'] - RbConfig::CONFIG['host_os'] = 'mingw' - allow(config).to receive(:require) - end + context "on windows" do + before do + @original_host = RbConfig::CONFIG['host_os'] + RbConfig::CONFIG['host_os'] = 'mingw' + allow(config).to receive(:require) + end - after do - RbConfig::CONFIG['host_os'] = @original_host - end + after do + RbConfig::CONFIG['host_os'] = @original_host + end - context "with ANSICON available" do - around(:each) { |e| with_env_vars('ANSICON' => 'ANSICON', &e) } + context "with ANSICON available" do + around(:each) { |e| with_env_vars('ANSICON' => 'ANSICON', &e) } - it "enables colors" do - config.output_stream = StringIO.new - allow(config.output_stream).to receive_messages :tty? => true - config.color = true - expect(config.color).to be_truthy - end + it "enables colors" do + config.output_stream = StringIO.new + allow(config.output_stream).to receive_messages :tty? => true + config.color = true + expect(config.color).to be_truthy + end - it "leaves output stream intact" do - config.output_stream = $stdout - allow(config).to receive(:require) do |what| - config.output_stream = 'foo' if what =~ /Win32/ - end - config.color = true - expect(config.output_stream).to eq($stdout) + it "leaves output stream intact" do + config.output_stream = $stdout + allow(config).to receive(:require) do |what| + config.output_stream = 'foo' if what =~ /Win32/ end + config.color = true + expect(config.output_stream).to eq($stdout) end + end - context "with ANSICON NOT available" do - before do - allow_warning - end + context "with ANSICON NOT available" do + around { |e| without_env_vars('ANSICON', &e) } - it "warns to install ANSICON" do - allow(config).to receive(:require) { raise LoadError } - expect_warning_with_call_site(__FILE__, __LINE__ + 1, /You must use ANSICON/) - config.color = true - end + before do + allow_warning + end - it "sets color to false" do - allow(config).to receive(:require) { raise LoadError } - config.color = true - expect(config.color).to be_falsey - end + it "warns to install ANSICON" do + allow(config).to receive(:require) { raise LoadError } + expect_warning_with_call_site(__FILE__, __LINE__ + 1, /You must use ANSICON/) + config.color = true + end + + it "sets color to false" do + allow(config).to receive(:require) { raise LoadError } + config.color = true + expect(config.color).to be_falsey end end end From e7b821c516f1b28abcd27dbbb287d95829e74786 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 13 Nov 2014 15:52:25 -0800 Subject: [PATCH 100/275] Fix rake task specs on windows. - Skip specs that rely on shell escaping (not available on windows). - On windows we wrap all args in single quotes, so change our expectations to allow for that. --- spec/rspec/core/rake_task_spec.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index bfd6ee593f..9cd65b4551 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -58,10 +58,10 @@ def spec_command context "with pattern" do it "adds the pattern" do task.pattern = "complex_pattern" - expect(spec_command).to include(" --pattern complex_pattern") + expect(spec_command).to match(/ --pattern '?complex_pattern'?/) end - it "shellescapes the pattern as necessary" do + it "shellescapes the pattern as necessary", :unless => RSpec::Support::OS.windows? do task.pattern = "foo'bar" expect(spec_command).to include(" --pattern foo\\'bar") end @@ -160,7 +160,7 @@ def self.it_configures_rspec_load_path(description, path_template) path_template % "rake" ]) - expect(spec_command).to include(" -I#{path_template % "rspec-core"}:#{path_template % "rspec-support"} ") + expect(spec_command).to match(/ -I'?#{path_template % "rspec-core"}'?:'?#{path_template % "rspec-support"}'? /) end it "avoids adding the same load path entries twice" do @@ -171,7 +171,7 @@ def self.it_configures_rspec_load_path(description, path_template) path_template % "rspec-support" ]) - expect(spec_command).to include(" -I#{path_template % "rspec-core"}:#{path_template % "rspec-support"} ") + expect(spec_command).to match(/ -I'?#{path_template % "rspec-core"}'?:'?#{path_template % "rspec-support"}'? /) end end end @@ -342,7 +342,7 @@ def make_files_in_dir(dir) make_files_in_dir "acceptance" end - it "shellescapes the pattern as necessary" do + it "shellescapes the pattern as necessary", :unless => RSpec::Support::OS.windows? do task.exclude_pattern = "foo'bar" expect(spec_command).to include(" --exclude-pattern foo\\'bar") end From d166ac197a8226e993a9bfd9c35ba0d533ed5b19 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 13 Nov 2014 18:29:08 -0800 Subject: [PATCH 101/275] Use platform-agnostic constant for file path separator. This should (hopefully) fix the rake task specs on windows. --- spec/rspec/core/rake_task_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index 9cd65b4551..e3a6d3fbfb 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -160,7 +160,7 @@ def self.it_configures_rspec_load_path(description, path_template) path_template % "rake" ]) - expect(spec_command).to match(/ -I'?#{path_template % "rspec-core"}'?:'?#{path_template % "rspec-support"}'? /) + expect(spec_command).to match(/ -I'?#{path_template % "rspec-core"}'?#{File::PATH_SEPARATOR}'?#{path_template % "rspec-support"}'? /) end it "avoids adding the same load path entries twice" do @@ -171,7 +171,7 @@ def self.it_configures_rspec_load_path(description, path_template) path_template % "rspec-support" ]) - expect(spec_command).to match(/ -I'?#{path_template % "rspec-core"}'?:'?#{path_template % "rspec-support"}'? /) + expect(spec_command).to match(/ -I'?#{path_template % "rspec-core"}'?#{File::PATH_SEPARATOR}'?#{path_template % "rspec-support"}'? /) end end end From 6c155d0e4b3dae0f72a685cca0a55284e19f4ec7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 13 Nov 2014 21:04:36 -0800 Subject: [PATCH 102/275] =?UTF-8?q?Skip=20a=20couple=20of=20specs=20on=20w?= =?UTF-8?q?indows=20that=20don=E2=80=99t=20make=20sense=20to=20run=20there?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/rspec/core/configuration_options_spec.rb | 2 +- spec/rspec/core/formatters/deprecation_formatter_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/rspec/core/configuration_options_spec.rb b/spec/rspec/core/configuration_options_spec.rb index 8e797352d7..25855672d3 100644 --- a/spec/rspec/core/configuration_options_spec.rb +++ b/spec/rspec/core/configuration_options_spec.rb @@ -5,7 +5,7 @@ RSpec.describe RSpec::Core::ConfigurationOptions, :isolated_directory => true, :isolated_home => true do include ConfigOptionsHelper - it "warns when HOME env var is not set", :unless => (RUBY_PLATFORM == 'java') do + it "warns when HOME env var is not set", :unless => (RUBY_PLATFORM == 'java' || RSpec::Support::OS.windows?) do without_env_vars 'HOME' do expect_warning_with_call_site(__FILE__, __LINE__ + 1) RSpec::Core::ConfigurationOptions.new([]).options diff --git a/spec/rspec/core/formatters/deprecation_formatter_spec.rb b/spec/rspec/core/formatters/deprecation_formatter_spec.rb index 33698c265f..a6503e5f8e 100644 --- a/spec/rspec/core/formatters/deprecation_formatter_spec.rb +++ b/spec/rspec/core/formatters/deprecation_formatter_spec.rb @@ -112,7 +112,7 @@ def notification(hash) expect(File.read(deprecation_stream.path)).to eq("foo is deprecated.\n#{DeprecationFormatter::RAISE_ERROR_CONFIG_NOTICE}") end - it "can handle when the stream is reopened to a system stream" do + it "can handle when the stream is reopened to a system stream", :unless => RSpec::Support::OS.windows? do send_notification :deprecation, notification(:deprecated => 'foo') deprecation_stream.reopen(IO.for_fd(IO.sysopen('/dev/null', "w+"))) send_notification :deprecation_summary, null_notification From 9a8a5a26e4410bc2c99bf28835459ec09ab7cbbb Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 13 Nov 2014 18:33:17 -0800 Subject: [PATCH 103/275] Pend remaining failing appveyor specs. --- spec/rspec/core/backtrace_formatter_spec.rb | 2 +- spec/rspec/core/configuration_spec.rb | 6 ++++-- spec/rspec/core/formatters/html_formatter_spec.rb | 7 +++++-- spec/rspec/core/rake_task_spec.rb | 6 ++++-- spec/rspec/core_spec.rb | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/spec/rspec/core/backtrace_formatter_spec.rb b/spec/rspec/core/backtrace_formatter_spec.rb index d5276be943..39e1273282 100644 --- a/spec/rspec/core/backtrace_formatter_spec.rb +++ b/spec/rspec/core/backtrace_formatter_spec.rb @@ -115,7 +115,7 @@ def make_backtrace_formatter(exclusion_patterns=nil, inclusion_patterns=nil) expect(BacktraceFormatter.new.format_backtrace(backtrace)).to eq(["./my_spec.rb:5"]) end - it "excludes lines from rspec libs by default", :if => RSpec::Support::OS.windows? do + it "excludes lines from rspec libs by default", :failing_on_appveyor, :if => RSpec::Support::OS.windows? do backtrace = [ "\\path\\to\\rspec-expectations\\lib\\rspec\\expectations\\foo.rb:37", "\\path\\to\\rspec-expectations\\lib\\rspec\\matchers\\foo.rb:37", diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index f5796f203f..c2d4f75e86 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -447,7 +447,9 @@ def stub_expectation_adapters expect(config.files_to_run).to contain_files("./spec/rspec/core/resources/a_spec.rb") end - it "supports absolute path patterns" do + it "supports absolute path patterns", :failing_on_appveyor, + :pending => false, + :skip => (ENV['APPVEYOR'] ? "Failing on AppVeyor but :pending isn't working for some reason" : false) do dir = File.expand_path("../resources", __FILE__) config.pattern = File.join(dir, "**/*_spec.rb") assign_files_or_directories_to_run "spec" @@ -543,7 +545,7 @@ def stub_expectation_adapters expect(config.files_to_run).to contain_files("C:/path/to/project/spec/sub/foo_spec.rb") end - it "loads files in Windows when directory is specified", :if => RSpec::Support::OS.windows? do + it "loads files in Windows when directory is specified", :failing_on_appveyor, :if => RSpec::Support::OS.windows? do assign_files_or_directories_to_run "spec\\rspec\\core\\resources" expect(config.files_to_run).to contain_files("spec/rspec/core/resources/a_spec.rb") end diff --git a/spec/rspec/core/formatters/html_formatter_spec.rb b/spec/rspec/core/formatters/html_formatter_spec.rb index 2b177a9c2b..830dcb6715 100644 --- a/spec/rspec/core/formatters/html_formatter_spec.rb +++ b/spec/rspec/core/formatters/html_formatter_spec.rb @@ -1,12 +1,15 @@ # encoding: utf-8 require 'spec_helper' require 'rspec/core/formatters/html_formatter' -require 'nokogiri' + +# For some reason we get load errors when loading nokogiri on AppVeyor +# on Ruby 2.1. On 1.9.3 it works just fine. No idea why. +require 'nokogiri' unless ENV['APPVEYOR'] && RUBY_VERSION.to_f >= 2.1 module RSpec module Core module Formatters - RSpec.describe HtmlFormatter do + RSpec.describe HtmlFormatter, :failing_on_appveyor => (RUBY_VERSION.to_f >= 2.1) do include FormatterSupport let(:root) { File.expand_path("#{File.dirname(__FILE__)}/../../../..") } diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index e3a6d3fbfb..ad4c349665 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -233,7 +233,9 @@ def self.it_configures_rspec_load_path(description, path_template) end context "that is an absolute path file glob" do - it "loads the matching spec files" do + it "loads the matching spec files", :failing_on_appveyor, + :pending => false, + :skip => (ENV['APPVEYOR'] ? "Failing on AppVeyor but :pending isn't working for some reason" : false) do dir = File.expand_path("../resources", __FILE__) task.pattern = File.join(dir, "**/*_spec.rb") @@ -366,7 +368,7 @@ def make_files_in_dir(dir) context "with paths with quotes or spaces" do include_context "isolated directory" - it "matches files with quotes and spaces" do + it "matches files with quotes and spaces", :failing_on_appveyor do spec_dir = File.join(Dir.getwd, "spec") task.pattern = "spec/*spec.rb" FileUtils.mkdir_p(spec_dir) diff --git a/spec/rspec/core_spec.rb b/spec/rspec/core_spec.rb index 356f11f605..9a114e3ab6 100644 --- a/spec/rspec/core_spec.rb +++ b/spec/rspec/core_spec.rb @@ -176,7 +176,7 @@ def some_example end describe "::Core.path_to_executable" do - it 'returns the absolute location of the exe/rspec file' do + it 'returns the absolute location of the exe/rspec file', :failing_on_appveyor do expect(File.exist? RSpec::Core.path_to_executable).to be_truthy expect(File.executable? RSpec::Core.path_to_executable).to be_truthy end From 1103d05ea66e38e1d4b5a8fc0622454efebae197 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 13 Nov 2014 23:24:09 -0800 Subject: [PATCH 104/275] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 10 +++++++++- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 16 insertions(+), 8 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 34d4f8cd94..6d6230caf5 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 05cad069d8..94f3ae6f07 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 495be45952..fb9bb19926 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,8 +1,16 @@ -# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" +# This will build all PRs targetting matching branches. +# Without this, each PR builds twice -- once for the PR branch HEAD, +# and once for the merge commit that github creates for each mergable PR. +branches: + only: + - master + - /.*-maintenance$/ + # Disable normal Windows builds in favor of our test script. build: off diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index e0ec88131c..2dea8c852c 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index e5fb9eab4c..235e92e798 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 12e96326e4..019286f5a3 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index dbbfcb22e3..de108c1d2d 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 54c92a215b..f3f2f85b8f 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T15:30:21-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 120bfd57962261998b066d9bf23365c5e14cfa8d Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sat, 15 Nov 2014 14:32:55 +1100 Subject: [PATCH 105/275] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 3 ++- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 6d6230caf5..4c98391944 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 94f3ae6f07..307c1c3395 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -20,6 +20,7 @@ rvm: - 2.1.2 - 2.1.3 - 2.1.4 + - 2.1.5 - ruby-head - ree - jruby-18mode diff --git a/appveyor.yml b/appveyor.yml index fb9bb19926..47d1d045b1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 2dea8c852c..131da0b0a1 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 235e92e798..21778f9f7e 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 019286f5a3..5e6eb4ad02 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index de108c1d2d..4f4764d0de 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index f3f2f85b8f..82da2bc3ee 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-13T23:24:09-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 1e207d0d5ebe7177c9a48cb7c3c5ebcaed805176 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 25 Nov 2014 14:35:01 +1100 Subject: [PATCH 106/275] Switch badges to svg --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3cd7e7290f..af363fd915 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# rspec-core [![Build Status](https://secure.travis-ci.org/rspec/rspec-core.png?branch=master)](http://travis-ci.org/rspec/rspec-core) [![Code Climate](https://codeclimate.com/github/rspec/rspec-core.png)](https://codeclimate.com/github/rspec/rspec-core) +# rspec-core [![Build Status](https://secure.travis-ci.org/rspec/rspec-core.svg?branch=master)](http://travis-ci.org/rspec/rspec-core) [![Code Climate](https://codeclimate.com/github/rspec/rspec-core.svg)](https://codeclimate.com/github/rspec/rspec-core) rspec-core provides the structure for writing executable examples of how your code should behave, and an `rspec` command with tools to constrain which From cc54fc208f38d7bf34f1febb7ef91154940a9305 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 27 Nov 2014 22:49:54 -0800 Subject: [PATCH 107/275] Fix typo: expecations -> expectations --- .../configure_expectation_framework.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/expectation_framework_integration/configure_expectation_framework.feature b/features/expectation_framework_integration/configure_expectation_framework.feature index a7852fb73f..9d0ce6236c 100644 --- a/features/expectation_framework_integration/configure_expectation_framework.feature +++ b/features/expectation_framework_integration/configure_expectation_framework.feature @@ -120,7 +120,7 @@ Feature: configure expectation framework When I run `rspec example_spec.rb` Then the examples should all pass - Scenario: Configure rspec/expecations AND minitest assertions + Scenario: Configure rspec/expectations AND minitest assertions Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| From e590fc1e20374e4f3b1c59bdb04750bb8910bcd1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 29 Nov 2014 14:56:49 -0800 Subject: [PATCH 108/275] Don't wrongly apply metadata-filtered hooks to examples that opt out. --- Changelog.md | 3 +++ lib/rspec/core/example.rb | 2 +- spec/rspec/core/hooks_filtering_spec.rb | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 1e402f61da..bfcb6e17bf 100644 --- a/Changelog.md +++ b/Changelog.md @@ -28,6 +28,9 @@ Bug Fixes: (Myron Marston, #1771) * Don't consider expectations from `after` hooks when generating example descriptions. (Myron Marston, #1771) +* Don't apply metadata-filtered config hooks to examples in groups + with matching metadata when those example override the parent + metadata value to not match. (Myron Marston, #1796) ### 3.1.8 Development diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index c909a61f6b..529c3f4240 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -272,7 +272,7 @@ def any_apply?(filters) # @private def all_apply?(filters) - MetadataFilter.all_apply?(filters, metadata) || @example_group_class.all_apply?(filters) + MetadataFilter.all_apply?(filters, metadata) end # @private diff --git a/spec/rspec/core/hooks_filtering_spec.rb b/spec/rspec/core/hooks_filtering_spec.rb index 9d2804cd87..a37bf4232f 100644 --- a/spec/rspec/core/hooks_filtering_spec.rb +++ b/spec/rspec/core/hooks_filtering_spec.rb @@ -222,6 +222,21 @@ def filters group.run expect(filters).to eq([]) end + + it "does not run for examples that do not match, even if their group matches" do + filters = [] + + RSpec.configure do |c| + c.before(:each, :apply_it) { filters << :before_each } + end + + RSpec.describe "Group", :apply_it do + example("ex1") { filters << :matching_example } + example("ex2", :apply_it => false) { filters << :nonmatching_example } + end.run + + expect(filters).to eq([:before_each, :matching_example, :nonmatching_example]) + end end end end From 9aed76ae56233af04f143066b3ecbb513743b209 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 29 Nov 2014 23:43:46 -0800 Subject: [PATCH 109/275] Merge `(any|all)_apply?` methods with predicate arg. There's no reason to keep these separated now that the implementations aren't any different besides the predicate. --- lib/rspec/core/configuration.rb | 4 ++-- lib/rspec/core/example.rb | 9 ++------- lib/rspec/core/example_group.rb | 9 ++------- lib/rspec/core/filter_manager.rb | 4 ++-- lib/rspec/core/hooks.rb | 2 +- lib/rspec/core/metadata_filter.rb | 9 ++------- spec/rspec/core/metadata_spec.rb | 3 ++- 7 files changed, 13 insertions(+), 27 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index bc30e8d21a..aed98161b2 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1092,7 +1092,7 @@ def extend(mod, *filters) # `extend`. def configure_group(group) include_or_extend_modules.each do |include_or_extend, mod, filters| - next unless filters.empty? || group.any_apply?(filters) + next unless filters.empty? || group.apply?(:any?, filters) __send__("safe_#{include_or_extend}", mod, group) end end @@ -1359,7 +1359,7 @@ def define_derived_metadata(*filters, &block) # @private def apply_derived_metadata_to(metadata) @derived_metadata_blocks.each do |filter, block| - block.call(metadata) if filter.empty? || MetadataFilter.any_apply?(filter, metadata) + block.call(metadata) if filter.empty? || MetadataFilter.apply?(:any?, filter, metadata) end end diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 529c3f4240..3b70d51e77 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -266,13 +266,8 @@ def inspect end # @private - def any_apply?(filters) - MetadataFilter.any_apply?(filters, metadata) - end - - # @private - def all_apply?(filters) - MetadataFilter.all_apply?(filters, metadata) + def apply?(predicate, filters) + MetadataFilter.apply?(predicate, filters, metadata) end # @private diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index 95bfdd9a53..cc67f55c4a 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -536,13 +536,8 @@ def self.fail_fast? end # @private - def self.any_apply?(filters) - MetadataFilter.any_apply?(filters, metadata) - end - - # @private - def self.all_apply?(filters) - MetadataFilter.all_apply?(filters, metadata) + def self.apply?(predicate, filters) + MetadataFilter.apply?(predicate, filters, metadata) end # @private diff --git a/lib/rspec/core/filter_manager.rb b/lib/rspec/core/filter_manager.rb index 8ddd000fbe..c00799622b 100644 --- a/lib/rspec/core/filter_manager.rb +++ b/lib/rspec/core/filter_manager.rb @@ -217,7 +217,7 @@ def use(*args) end def include_example?(example) - @rules.empty? ? true : example.any_apply?(@rules) + @rules.empty? ? true : example.apply?(:any?, @rules) end def standalone? @@ -252,7 +252,7 @@ class ExclusionRules < FilterRules }.freeze def include_example?(example) - example.any_apply?(@rules) || example.any_apply?(CONDITIONAL_FILTERS) + example.apply?(:any?, @rules) || example.apply?(:any?, CONDITIONAL_FILTERS) end end end diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index b1b9c98215..7bd64e19b9 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -347,7 +347,7 @@ def initialize(block, options) end def options_apply?(example_or_group) - example_or_group.all_apply?(options) + example_or_group.apply?(:all?, options) end end diff --git a/lib/rspec/core/metadata_filter.rb b/lib/rspec/core/metadata_filter.rb index 7c8cac6e3c..79dfb87eda 100644 --- a/lib/rspec/core/metadata_filter.rb +++ b/lib/rspec/core/metadata_filter.rb @@ -8,13 +8,8 @@ module Core module MetadataFilter class << self # @private - def any_apply?(filters, metadata) - filters.any? { |k, v| filter_applies?(k, v, metadata) } - end - - # @private - def all_apply?(filters, metadata) - filters.all? { |k, v| filter_applies?(k, v, metadata) } + def apply?(predicate, filters, metadata) + filters.__send__(predicate) { |k, v| filter_applies?(k, v, metadata) } end # @private diff --git a/spec/rspec/core/metadata_spec.rb b/spec/rspec/core/metadata_spec.rb index d3d11528a1..326180acbf 100644 --- a/spec/rspec/core/metadata_spec.rb +++ b/spec/rspec/core/metadata_spec.rb @@ -662,7 +662,8 @@ def value_for(*args) line = __LINE__ + 1 RSpec.describe("group") { meta = metadata } - applies = MetadataFilter.any_apply?( + applies = MetadataFilter.apply?( + :any?, { :example_group => { :line_number => line } }, meta ) From 11a8f445ad7d5451b3f8417fbda5f8aa8622783e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 2 Dec 2014 19:48:58 -0800 Subject: [PATCH 110/275] Add missing spec coverage for `apply?(:any)` vs `apply?(:all)`. We were a bit underspecified here. --- spec/rspec/core/filter_manager_spec.rb | 13 +++++++++++ spec/rspec/core/hooks_filtering_spec.rb | 30 ++++++++++++++++--------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/spec/rspec/core/filter_manager_spec.rb b/spec/rspec/core/filter_manager_spec.rb index a421bf8b27..2dfb7f6c33 100644 --- a/spec/rspec/core/filter_manager_spec.rb +++ b/spec/rspec/core/filter_manager_spec.rb @@ -183,6 +183,19 @@ def example_with(*args) filter_manager.include_with_low_priority :foo => :bar expect(filter_manager.prune([included, excluded])).to eq([included]) end + + context "with multiple inclusion filters" do + it 'includes objects that match any of them' do + examples = [ + included_1 = example_with(:foo => true), + included_2 = example_with(:bar => true), + example_with(:bazz => true) + ] + + filter_manager.include :foo => true, :bar => true + expect(filter_manager.prune(examples)).to contain_exactly(included_1, included_2) + end + end end describe "#inclusions#description" do diff --git a/spec/rspec/core/hooks_filtering_spec.rb b/spec/rspec/core/hooks_filtering_spec.rb index a37bf4232f..2df8235e6d 100644 --- a/spec/rspec/core/hooks_filtering_spec.rb +++ b/spec/rspec/core/hooks_filtering_spec.rb @@ -209,18 +209,28 @@ def filters end it "does not run if some hook filters don't match the group's filters" do - filters = [] + sequence = [] + RSpec.configure do |c| - c.before(:all, :one => 1, :four => 4) { filters << "before all in config"} - c.around(:each, :two => 2, :four => 4) {|example| filters << "around each in config"; example.run} - c.before(:each, :one => 1, :two => 2, :four => 4) { filters << "before each in config"} - c.after(:each, :one => 1, :two => 2, :three => 3, :four => 4) { filters << "after each in config"} - c.after(:all, :one => 1, :three => 3, :four => 4) { filters << "after all in config"} + c.before(:all, :one => 1, :four => 4) { sequence << "before all in config"} + c.around(:each, :two => 2, :four => 4) {|example| sequence << "around each in config"; example.run} + c.before(:each, :one => 1, :two => 2, :four => 4) { sequence << "before each in config"} + c.after(:each, :one => 1, :two => 2, :three => 3, :four => 4) { sequence << "after each in config"} + c.after(:all, :one => 1, :three => 3, :four => 4) { sequence << "after all in config"} end - group = ExampleGroup.describe(:one => 1, :two => 2, :three => 3) - group.example("example") {} - group.run - expect(filters).to eq([]) + + RSpec.describe "group", :one => 1, :two => 2, :three => 3 do + example("ex1") { sequence << "ex1" } + example("ex2", :four => 4) { sequence << "ex2" } + end.run + + expect(sequence).to eq([ + "ex1", + "around each in config", + "before each in config", + "ex2", + "after each in config", + ]) end it "does not run for examples that do not match, even if their group matches" do From f1b937bf7a6b8f09a26de7aeb230092ccbd2c3cd Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 8 Dec 2014 12:33:45 -0800 Subject: [PATCH 111/275] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 6 +++--- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 4c98391944..d99160647a 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 307c1c3395..f86a4388e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 47d1d045b1..d330880799 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" @@ -20,11 +20,11 @@ install: - gem --version - gem install bundler - bundler --version - - bundle install + - bundle install --standalone --binstubs - cinst ansicon test_script: - - bundle exec rspec + - bin\rspec environment: matrix: diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 131da0b0a1..f56157b064 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 21778f9f7e..fa67767719 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 5e6eb4ad02..e5e9d17cf9 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 4f4764d0de..f6197695e3 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 82da2bc3ee..9a84511829 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 0b272058c0a8f88a2f7938bb9a78b755026b5a08 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 8 Dec 2014 14:35:36 -0800 Subject: [PATCH 112/275] Revert "Updated travis build scripts (from rspec-dev)" This reverts commit f1b937bf7a6b8f09a26de7aeb230092ccbd2c3cd. [ci skip] --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 6 +++--- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index d99160647a..4c98391944 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index f86a4388e1..307c1c3395 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index d330880799..47d1d045b1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" @@ -20,11 +20,11 @@ install: - gem --version - gem install bundler - bundler --version - - bundle install --standalone --binstubs + - bundle install - cinst ansicon test_script: - - bin\rspec + - bundle exec rspec environment: matrix: diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index f56157b064..131da0b0a1 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index fa67767719..21778f9f7e 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index e5e9d17cf9..5e6eb4ad02 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index f6197695e3..4f4764d0de 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 9a84511829..82da2bc3ee 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:33:45-08:00 from the rspec-dev repo. +# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 0d6ca463d4ef1429fef04c1b331a8a43793ef6a1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 8 Dec 2014 12:46:26 -0800 Subject: [PATCH 113/275] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 5 +++-- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 4c98391944..0180db0c2c 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 307c1c3395..f6e810cfc6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 47d1d045b1..288c205220 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" @@ -18,7 +18,8 @@ install: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - ruby --version - gem --version - - gem install bundler + # We lock to 1.7.7 to avoid warnings from 1.7.8 + - gem install bundler -v=1.7.7 - bundler --version - bundle install - cinst ansicon diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 131da0b0a1..5f0a2e4ec8 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 21778f9f7e..7eb77aba82 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 5e6eb4ad02..2cf429a835 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 4f4764d0de..8a2f2eb1ae 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 82da2bc3ee..1fec63caf6 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-11-15T14:32:55+11:00 from the rspec-dev repo. +# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 6aec8da85f254408b3b59d3d2fd8ab09dc957b08 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 9 Dec 2014 14:31:37 +1100 Subject: [PATCH 114/275] change a mention of type to class as it makes sense here --- features/example_groups/shared_examples.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/example_groups/shared_examples.feature b/features/example_groups/shared_examples.feature index 01183b4014..dd2f96fb43 100644 --- a/features/example_groups/shared_examples.feature +++ b/features/example_groups/shared_examples.feature @@ -1,6 +1,6 @@ Feature: shared examples - Shared examples let you describe behaviour of types or modules. When declared, + Shared examples let you describe behaviour of classes or modules. When declared, a shared group's content is stored. It is only realized in the context of another example group, which provides any context the shared group needs to run. From 55af8911c0e39d43f5fda54b3c27f171b4f2a963 Mon Sep 17 00:00:00 2001 From: Arlandis Lawrence Date: Tue, 25 Nov 2014 21:36:02 -0500 Subject: [PATCH 115/275] Add 'prepend' support to RSpec configuration --- lib/rspec/core/configuration.rb | 62 +++++++++++++++--- spec/rspec/core/configuration_spec.rb | 66 ++++++++++++++++++-- spec/rspec/core/shared_example_group_spec.rb | 4 +- 3 files changed, 117 insertions(+), 15 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index aed98161b2..21efddfe84 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -284,7 +284,7 @@ def treat_symbols_as_metadata_keys_with_true_values=(_value) # @private add_setting :tty # @private - add_setting :include_or_extend_modules + add_setting :include_extend_or_prepend_modules # @private attr_writer :files_to_run # @private @@ -301,7 +301,7 @@ def initialize @start_time = $_rspec_core_load_started_at || ::RSpec::Core::Time.now # rubocop:enable Style/GlobalVars @expectation_frameworks = [] - @include_or_extend_modules = [] + @include_extend_or_prepend_modules = [] @mock_framework = nil @files_or_directories_to_run = [] @color = false @@ -1047,9 +1047,10 @@ def exclusion_filter # end # # @see #extend + # @see #prepend def include(mod, *filters) meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering) - include_or_extend_modules << [:include, mod, meta] + include_extend_or_prepend_modules << [:include, mod, meta] end # Tells RSpec to extend example groups with `mod`. Methods defined in @@ -1081,19 +1082,57 @@ def include(mod, *filters) # end # # @see #include + # @see #prepend def extend(mod, *filters) meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering) - include_or_extend_modules << [:extend, mod, meta] + include_extend_or_prepend_modules << [:extend, mod, meta] + end + + # Tells RSpec to prepend example groups with `mod`. Methods defined in + # `mod` are exposed to examples (not example groups). Use `filters` to + # constrain the groups in which to prepend the module. + # + # Similar to `include`, but module is included before the example group's class + # in the ancestor chain. + # + # @example + # + # module OverrideMod + # def override_me + # "overridden" + # end + # end + # + # RSpec.configure do |config| + # config.prepend(OverrideMod, :method => :prepend) + # end + # + # describe "overriding example's class", :method => :prepend do + # it "finds the user" do + # self.class.class_eval do + # def override_me + # end + # end + # override_me # => "overridden" + # # ... + # end + # end + # + # @see #include + # @see #extend + def prepend(mod, *filters) + meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering) + include_extend_or_prepend_modules << [:prepend, mod, meta] end # @private # - # Used internally to extend a group with modules using `include` and/or + # Used internally to extend a group with modules using `include`, `prepend` and/or # `extend`. def configure_group(group) - include_or_extend_modules.each do |include_or_extend, mod, filters| + include_extend_or_prepend_modules.each do |include_extend_or_prepend, mod, filters| next unless filters.empty? || group.apply?(:any?, filters) - __send__("safe_#{include_or_extend}", mod, group) + __send__("safe_#{include_extend_or_prepend}", mod, group) end end @@ -1102,6 +1141,15 @@ def safe_include(mod, host) host.__send__(:include, mod) unless host < mod end + # @private + def safe_prepend(mod, host) + if Module.respond_to?(:prepend) + host.__send__(:prepend, mod) unless host < mod + else + raise NotImplementedError, "'prepend' not supported in this version of Ruby" + end + end + # @private def requires=(paths) directories = ['lib', default_path].select { |p| File.directory? p } diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index c2d4f75e86..c24c3abf5b 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -882,7 +882,7 @@ def you_call_this_a_blt? it_behaves_like "metadata hash builder" do def metadata_hash(*args) config.include(InstanceLevelMethods, *args) - config.include_or_extend_modules.last.last + config.include_extend_or_prepend_modules.last.last end end @@ -909,7 +909,6 @@ def metadata_hash(*args) expect(group.new.you_call_this_a_blt?).to eq("egad man, where's the mayo?!?!?") end end - end describe "#extend" do @@ -923,7 +922,7 @@ def that_thing it_behaves_like "metadata hash builder" do def metadata_hash(*args) config.extend(ThatThingISentYou, *args) - config.include_or_extend_modules.last.last + config.include_extend_or_prepend_modules.last.last end end @@ -938,6 +937,46 @@ def metadata_hash(*args) end + describe "#prepend", :if => Module.respond_to?(:prepend) do + include_examples "warning of deprecated `:example_group` during filtering configuration", :prepend, Enumerable + + module SomeRandomMod + def foo + "foobar" + end + end + + it_behaves_like "metadata hash builder" do + def metadata_hash(*args) + config.prepend(SomeRandomMod, *args) + config.include_extend_or_prepend_modules.last.last + end + end + + context "with no filter" do + it "prepends the given module into each example group" do + RSpec.configure do |c| + c.prepend(SomeRandomMod) + end + + group = ExampleGroup.describe('yo') { } + expect(group.new.foo).to eq("foobar") + end + end + + context "with a filter" do + it "prepends the given module into each matching example group" do + RSpec.configure do |c| + c.prepend(SomeRandomMod, :magic_key => :include) + end + + group = ExampleGroup.describe('yo', :magic_key => :include) { } + expect(group.new.foo).to eq("foobar") + end + end + + end + describe "#run_all_when_everything_filtered?" do it "defaults to false" do @@ -1509,7 +1548,7 @@ def self.included(host) expect(group.included_modules).to include(mod2) end - module IncludeOrExtendMeOnce + module IncludeExtendOrPrependMeOnce def self.included(host) raise "included again" if host.instance_methods.include?(:foobar) host.class_exec { def foobar; end } @@ -1519,10 +1558,15 @@ def self.extended(host) raise "extended again" if host.respond_to?(:foobar) def host.foobar; end end + + def self.prepended(host) + raise "prepended again" if host.instance_methods.include?(:barbaz) + host.class_exec { def barbaz; end } + end end it "doesn't include a module when already included in ancestor" do - config.include(IncludeOrExtendMeOnce, :foo => :bar) + config.include(IncludeExtendOrPrependMeOnce, :foo => :bar) group = ExampleGroup.describe("group", :foo => :bar) child = group.describe("child") @@ -1532,7 +1576,17 @@ def host.foobar; end end it "doesn't extend when ancestor is already extended with same module" do - config.extend(IncludeOrExtendMeOnce, :foo => :bar) + config.extend(IncludeExtendOrPrependMeOnce, :foo => :bar) + + group = ExampleGroup.describe("group", :foo => :bar) + child = group.describe("child") + + config.configure_group(group) + config.configure_group(child) + end + + it "doesn't prepend a module when already present in ancestor chain", :if => Module.respond_to?(:prepend) do + config.prepend(IncludeExtendOrPrependMeOnce, :foo => :bar) group = ExampleGroup.describe("group", :foo => :bar) child = group.describe("child") diff --git a/spec/rspec/core/shared_example_group_spec.rb b/spec/rspec/core/shared_example_group_spec.rb index 83847b0dbd..35574ce5e0 100644 --- a/spec/rspec/core/shared_example_group_spec.rb +++ b/spec/rspec/core/shared_example_group_spec.rb @@ -106,7 +106,7 @@ module Core it "delegates include on configuration" do implementation = Proc.new { def bar; 'bar'; end } define_shared_group(:foo => :bar, &implementation) - a = RSpec.configuration.include_or_extend_modules.first + a = RSpec.configuration.include_extend_or_prepend_modules.first expect(a[0]).to eq(:include) expect(Class.new.send(:include, a[1]).new.bar).to eq('bar') expect(a[2]).to eq(:foo => :bar) @@ -123,7 +123,7 @@ module Core it "delegates include on configuration" do implementation = Proc.new { def bar; 'bar'; end } define_shared_group("name", :foo => :bar, &implementation) - a = RSpec.configuration.include_or_extend_modules.first + a = RSpec.configuration.include_extend_or_prepend_modules.first expect(a[0]).to eq(:include) expect(Class.new.send(:include, a[1]).new.bar).to eq('bar') expect(a[2]).to eq(:foo => :bar) From b33bf4b56abe046c6c603646af900ccd8ad6b507 Mon Sep 17 00:00:00 2001 From: Arlandis Lawrence Date: Sun, 14 Dec 2014 21:46:41 -0600 Subject: [PATCH 116/275] Use feature-checking from RSpec support, no-op when unsupported --- lib/rspec/core/configuration.rb | 8 ++++---- spec/rspec/core/configuration_spec.rb | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index 21efddfe84..f6e3ef1e40 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1142,12 +1142,12 @@ def safe_include(mod, host) end # @private - def safe_prepend(mod, host) - if Module.respond_to?(:prepend) + if RSpec::Support::RubyFeatures.module_prepends_supported? + def safe_prepend(mod, host) host.__send__(:prepend, mod) unless host < mod - else - raise NotImplementedError, "'prepend' not supported in this version of Ruby" end + else + def safe_prepend(mod, host); end end # @private diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index c24c3abf5b..22549f103f 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -937,7 +937,7 @@ def metadata_hash(*args) end - describe "#prepend", :if => Module.respond_to?(:prepend) do + describe "#prepend", :if => RSpec::Support::RubyFeatures.module_prepends_supported? do include_examples "warning of deprecated `:example_group` during filtering configuration", :prepend, Enumerable module SomeRandomMod @@ -1585,7 +1585,8 @@ def self.prepended(host) config.configure_group(child) end - it "doesn't prepend a module when already present in ancestor chain", :if => Module.respond_to?(:prepend) do + it "doesn't prepend a module when already present in ancestor chain", + :if => RSpec::Support::RubyFeatures.module_prepends_supported? do config.prepend(IncludeExtendOrPrependMeOnce, :foo => :bar) group = ExampleGroup.describe("group", :foo => :bar) From 049a29e5ca6fa7c737f2289501b8b34cbaa76297 Mon Sep 17 00:00:00 2001 From: Arlandis Lawrence Date: Sun, 14 Dec 2014 22:49:09 -0600 Subject: [PATCH 117/275] Some refactoring in configuration --- lib/rspec/core/configuration.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index f6e3ef1e40..b8b86a9d56 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1120,9 +1120,11 @@ def extend(mod, *filters) # # @see #include # @see #extend - def prepend(mod, *filters) - meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering) - include_extend_or_prepend_modules << [:prepend, mod, meta] + if RSpec::Support::RubyFeatures.module_prepends_supported? + def prepend(mod, *filters) + meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering) + include_extend_or_prepend_modules << [:prepend, mod, meta] + end end # @private @@ -1146,8 +1148,6 @@ def safe_include(mod, host) def safe_prepend(mod, host) host.__send__(:prepend, mod) unless host < mod end - else - def safe_prepend(mod, host); end end # @private From 2a4a8acdf851082510dc619de34e92967ae80d2d Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 15 Dec 2014 22:40:22 +1100 Subject: [PATCH 118/275] Changelog for #1806 --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index bfcb6e17bf..cfcedc854c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,8 @@ Enhancements: * Issue seed notification at start (as well as the end) of the reporter run. (Arlandis Word, #1761) * Improve the documentation of around hooks. (Jim Kingdon, #1772) +* Support prepending of modules into example groups from config and allow + filtering based on metadata. (Arlandis Word, #1806) Bug Fixes: From b8474267a41d258acc4c49dd468dc5f23625bc81 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 6 Nov 2014 22:30:50 -0800 Subject: [PATCH 119/275] Refactor how context hooks get run. - Move `descendant_filtered_examples.empty?` check out into the `run` method. - This allows us to only create the instances for the context hooks if we need to actually run the hooks. - Move `before_context_ivars.clear` into `run_after_context_hooks`. This gives it parity with `run_before_context_hooks`. --- lib/rspec/core/example_group.rb | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index cc67f55c4a..7ffea69f76 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -441,26 +441,24 @@ def self.store_before_context_ivars(example_group_instance) # @private def self.run_before_context_hooks(example_group_instance) - return if descendant_filtered_examples.empty? - begin - set_ivars(example_group_instance, superclass.before_context_ivars) + set_ivars(example_group_instance, superclass.before_context_ivars) - ContextHookMemoizedHash::Before.isolate_for_context_hook(example_group_instance) do - hooks.run(:before, :context, example_group_instance) - end - ensure - store_before_context_ivars(example_group_instance) + ContextHookMemoizedHash::Before.isolate_for_context_hook(example_group_instance) do + hooks.run(:before, :context, example_group_instance) end + ensure + store_before_context_ivars(example_group_instance) end # @private def self.run_after_context_hooks(example_group_instance) - return if descendant_filtered_examples.empty? set_ivars(example_group_instance, before_context_ivars) ContextHookMemoizedHash::After.isolate_for_context_hook(example_group_instance) do hooks.run(:after, :context, example_group_instance) end + ensure + before_context_ivars.clear end # Runs all the examples in this group. @@ -471,9 +469,9 @@ def self.run(reporter) end reporter.example_group_started(self) + should_run_context_hooks = descendant_filtered_examples.any? begin - instance = new('before(:context) hook') - run_before_context_hooks(instance) + run_before_context_hooks(new('before(:context) hook')) if should_run_context_hooks result_for_this_group = run_examples(reporter) results_for_descendants = ordering_strategy.order(children).map { |child| child.run(reporter) }.all? result_for_this_group && results_for_descendants @@ -483,9 +481,7 @@ def self.run(reporter) RSpec.world.wants_to_quit = true if fail_fast? for_filtered_examples(reporter) { |example| example.fail_with_exception(reporter, ex) } ensure - instance = new('after(:context) hook') - run_after_context_hooks(instance) - before_context_ivars.clear + run_after_context_hooks(new('after(:context) hook')) if should_run_context_hooks reporter.example_group_finished(self) end end From 8232f61e0ba17b71dfd426eaf07da48ee2cad47e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 7 Nov 2014 09:36:52 -0800 Subject: [PATCH 120/275] Refactor: extract `config.with_suite_hooks` method. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The runner has no business understanding the details of running the config object’s :suite hooks. --- lib/rspec/core/configuration.rb | 9 +++++++++ lib/rspec/core/runner.rb | 6 +----- spec/rspec/core/hooks_spec.rb | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index b8b86a9d56..ea0425f61c 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1411,6 +1411,15 @@ def apply_derived_metadata_to(metadata) end end + # @private + def with_suite_hooks + hook_context = SuiteHookContext.new + hooks.run(:before, :suite, hook_context) + yield + ensure + hooks.run(:after, :suite, hook_context) + end + private def get_files_to_run(paths) diff --git a/lib/rspec/core/runner.rb b/lib/rspec/core/runner.rb index 50bdfc55a2..af5612b92c 100644 --- a/lib/rspec/core/runner.rb +++ b/lib/rspec/core/runner.rb @@ -106,12 +106,8 @@ def setup(err, out) # failed. def run_specs(example_groups) @configuration.reporter.report(@world.example_count(example_groups)) do |reporter| - begin - hook_context = SuiteHookContext.new - @configuration.hooks.run(:before, :suite, hook_context) + @configuration.with_suite_hooks do example_groups.map { |g| g.run(reporter) }.all? ? 0 : @configuration.failure_exit_code - ensure - @configuration.hooks.run(:after, :suite, hook_context) end end end diff --git a/spec/rspec/core/hooks_spec.rb b/spec/rspec/core/hooks_spec.rb index 50ebee6be2..f268534fb6 100644 --- a/spec/rspec/core/hooks_spec.rb +++ b/spec/rspec/core/hooks_spec.rb @@ -81,7 +81,7 @@ def parent_groups RSpec.configuration.after(:suite) { 1 / 0 } expect { - RSpec.configuration.hooks.run(:after, :suite, SuiteHookContext.new) + RSpec.configuration.with_suite_hooks { } }.to raise_error(ZeroDivisionError) end end @@ -91,7 +91,7 @@ def parent_groups RSpec.configuration.before(:suite) { 1 / 0 } expect { - RSpec.configuration.hooks.run(:before, :suite, SuiteHookContext.new) + RSpec.configuration.with_suite_hooks { } }.to raise_error(ZeroDivisionError) end end From e35fe0d04d53a29339ffe24e926b6527d2313f2c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 7 Nov 2014 09:49:08 -0800 Subject: [PATCH 121/275] Refactor: extract :suite hook specs into their own file. This includes new spec coverage for `(prepend|append)_(before|after)(:suite)`. Also, only reference these hooks on the config object. I plan to remove them from the `Hooks` module since they are not general purpose hooks. --- spec/rspec/core/hooks_spec.rb | 22 +-------- spec/rspec/core/runner_spec.rb | 39 +-------------- spec/rspec/core/suite_hooks_spec.rb | 73 +++++++++++++++++++++++++++++ spec/spec_helper.rb | 6 ++- spec/support/runner_support.rb | 16 +++++++ 5 files changed, 97 insertions(+), 59 deletions(-) create mode 100644 spec/rspec/core/suite_hooks_spec.rb create mode 100644 spec/support/runner_support.rb diff --git a/spec/rspec/core/hooks_spec.rb b/spec/rspec/core/hooks_spec.rb index f268534fb6..8ad8a01501 100644 --- a/spec/rspec/core/hooks_spec.rb +++ b/spec/rspec/core/hooks_spec.rb @@ -48,7 +48,7 @@ def parent_groups end [:before, :after].each do |type| - [:example, :context, :suite].each do |scope| + [:example, :context].each do |scope| describe "##{type}(#{scope.inspect})" do let(:instance) { HooksHost.new } let!(:hook) do @@ -76,26 +76,6 @@ def parent_groups end end - context "when an error happens in `after(:suite)`" do - it 'allows the error to propagate to the user' do - RSpec.configuration.after(:suite) { 1 / 0 } - - expect { - RSpec.configuration.with_suite_hooks { } - }.to raise_error(ZeroDivisionError) - end - end - - context "when an error happens in `before(:suite)`" do - it 'allows the error to propagate to the user' do - RSpec.configuration.before(:suite) { 1 / 0 } - - expect { - RSpec.configuration.with_suite_hooks { } - }.to raise_error(ZeroDivisionError) - end - end - describe "#around" do context "when it does not run the example" do context "for a hook declared in the group" do diff --git a/spec/rspec/core/runner_spec.rb b/spec/rspec/core/runner_spec.rb index 80e961739f..0197e457d1 100644 --- a/spec/rspec/core/runner_spec.rb +++ b/spec/rspec/core/runner_spec.rb @@ -1,5 +1,6 @@ require 'spec_helper' require 'rspec/core/drb' +require 'support/runner_support' module RSpec::Core RSpec.describe Runner do @@ -142,10 +143,7 @@ def run_specs end context "when run" do - let(:out) { StringIO.new } - let(:err) { StringIO.new } - let(:config) { RSpec.configuration } - let(:world) { RSpec.world } + include_context "Runner support" before do allow(config.hooks).to receive(:run) @@ -257,31 +255,6 @@ def run_specs expect(runner.run(err, out)).to eq 2 end end - - context "running hooks" do - before { allow(config).to receive :load_spec_files } - - it "runs before suite hooks" do - expect(config.hooks).to receive(:run).with(:before, :suite, instance_of(SuiteHookContext)) - runner = build_runner - runner.run err, out - end - - it "runs after suite hooks" do - expect(config.hooks).to receive(:run).with(:after, :suite, instance_of(SuiteHookContext)) - runner = build_runner - runner.run err, out - end - - it "runs after suite hooks even after an error" do - expect(config.hooks).to receive(:run).with(:before, :suite, instance_of(SuiteHookContext)).and_raise "this error" - expect(config.hooks).to receive(:run).with(:after , :suite, instance_of(SuiteHookContext)) - expect do - runner = build_runner - runner.run err, out - end.to raise_error("this error") - end - end end describe "#run with custom output" do @@ -296,14 +269,6 @@ def run_specs expect(runner.instance_exec { @configuration.output_stream }).to eq output_file end end - - def build_runner *args - Runner.new build_config_options(*args) - end - - def build_config_options *args - ConfigurationOptions.new args - end end end end diff --git a/spec/rspec/core/suite_hooks_spec.rb b/spec/rspec/core/suite_hooks_spec.rb new file mode 100644 index 0000000000..a23b5114b5 --- /dev/null +++ b/spec/rspec/core/suite_hooks_spec.rb @@ -0,0 +1,73 @@ +require "spec_helper" +require "support/runner_support" + +module RSpec::Core + RSpec.describe "Configuration :suite hooks" do + [:before, :after, :prepend_before, :append_before, :prepend_after, :append_after].each do |type| + describe "a(n) #{type} hook" do + it 'is skipped when in dry run mode' do + RSpec.configuration.dry_run = true + + expect { |b| + RSpec.configuration.__send__(type, :suite, &b) + RSpec.configuration.with_suite_hooks { } + }.not_to yield_control + end + + it 'allows errors in the hook to propagate to the user' do + RSpec.configuration.__send__(type, :suite) { 1 / 0 } + + expect { + RSpec.configuration.with_suite_hooks { } + }.to raise_error(ZeroDivisionError) + end + end + end + + it 'always runs `after(:suite)` hooks even in the face of errors' do + expect { |b| + RSpec.configuration.after(:suite, &b) + RSpec.configuration.with_suite_hooks { raise "boom" } + }.to raise_error("boom").and yield_control + end + + describe "the runner" do + include_context "Runner support" + + it "runs :suite hooks before and after example groups in the correct order" do + sequence = [] + + config.before(:suite) { sequence << :before_suite_2 } + config.before(:suite) { sequence << :before_suite_3 } + config.append_before(:suite) { sequence << :before_suite_4 } + config.prepend_before(:suite) { sequence << :before_suite_1 } + config.after(:suite) { sequence << :after_suite_3 } + config.after(:suite) { sequence << :after_suite_2 } + config.prepend_after(:suite) { sequence << :after_suite_1 } + config.append_after(:suite) { sequence << :after_suite_4 } + + + example_group = class_double(ExampleGroup, :descendants => []) + + allow(example_group).to receive(:run) { sequence << :example_groups } + allow(world).to receive_messages(:ordered_example_groups => [example_group]) + allow(config).to receive :load_spec_files + + runner = build_runner + runner.run err, out + + expect(sequence).to eq([ + :before_suite_1, + :before_suite_2, + :before_suite_3, + :before_suite_4, + :example_groups, + :after_suite_1, + :after_suite_2, + :after_suite_3, + :after_suite_4 + ]) + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index cebad55b16..75f512e825 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -32,7 +32,11 @@ def self.new(*args, &block) end end -Dir['./spec/support/**/*.rb'].map {|f| require f} +Dir['./spec/support/**/*.rb'].map do |file| + # Ensure requires are relative to `spec`, which is on the + # load path. This helps prevent double requires on 1.8.7. + require file.gsub("./spec/support", "support") +end module EnvHelpers def with_env_vars(vars) diff --git a/spec/support/runner_support.rb b/spec/support/runner_support.rb new file mode 100644 index 0000000000..d4b1d62849 --- /dev/null +++ b/spec/support/runner_support.rb @@ -0,0 +1,16 @@ +module RSpec::Core + RSpec.shared_context "Runner support" do + let(:out) { StringIO.new } + let(:err) { StringIO.new } + let(:config) { RSpec.configuration } + let(:world) { RSpec.world } + + def build_runner(*args) + Runner.new(build_config_options(*args)) + end + + def build_config_options(*args) + ConfigurationOptions.new(args) + end + end +end From 8a32b23266365ae3ca72b97774149bc83307663b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 7 Nov 2014 10:31:29 -0800 Subject: [PATCH 122/275] Refactor: handle :suite hooks within configuration. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ...rather than delegating it to the Hooks module. This will allow us to remove :suite support from the hooks module, since it’s not something that is generally supported in other hook contexts. --- lib/rspec/core/configuration.rb | 91 +++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 4 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index ea0425f61c..e62e0df423 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1411,17 +1411,100 @@ def apply_derived_metadata_to(metadata) end end + # Defines a `before` hook. See {Hooks#before} for full docs. + # + # This method differs from {Hooks#before} in only one way: it supports + # the `:suite` scope. Hooks with the `:suite` scope will be run once before + # the first example of the entire suite is executed. + # + # @see #prepend_before + # @see #after + # @see #append_after + def before(*args, &block) + handle_suite_hook(args.first, before_suite_hooks, :append, + Hooks::BeforeHook, block) || super(*args, &block) + end + alias_method :append_before, :before + + # Adds `block` to the start of the list of `before` blocks in the same + # scope (`:example`, `:context`, or `:suite`), in contrast to {#before}, + # which adds the hook to the end of the list. + # + # See {Hooks#before} for full `before` hook docs. + # + # This method differs from {Hooks#prepend_before} in only one way: it supports + # the `:suite` scope. Hooks with the `:suite` scope will be run once before + # the first example of the entire suite is executed. + # + # @see #before + # @see #after + # @see #append_after + def prepend_before(*args, &block) + handle_suite_hook(args.first, before_suite_hooks, :prepend, + Hooks::BeforeHook, block) || super(*args, &block) + end + + # Defines a `after` hook. See {Hooks#after} for full docs. + # + # This method differs from {Hooks#after} in only one way: it supports + # the `:suite` scope. Hooks with the `:suite` scope will be run once after + # the last example of the entire suite is executed. + # + # @see #append_after + # @see #before + # @see #prepend_before + def after(*args, &block) + handle_suite_hook(args.first, after_suite_hooks, :prepend, + Hooks::AfterHook, block) || super(*args, &block) + end + alias_method :prepend_after, :after + + # Adds `block` to the end of the list of `after` blocks in the same + # scope (`:example`, `:context`, or `:suite`), in contrast to {#after}, + # which adds the hook to the start of the list. + # + # See {Hooks#after} for full `after` hook docs. + # + # This method differs from {Hooks#append_after} in only one way: it supports + # the `:suite` scope. Hooks with the `:suite` scope will be run once after + # the last example of the entire suite is executed. + # + # @see #append_after + # @see #before + # @see #prepend_before + def append_after(*args, &block) + handle_suite_hook(args.first, after_suite_hooks, :append, + Hooks::AfterHook, block) || super(*args, &block) + end + # @private def with_suite_hooks + return yield if dry_run? + hook_context = SuiteHookContext.new - hooks.run(:before, :suite, hook_context) - yield - ensure - hooks.run(:after, :suite, hook_context) + begin + before_suite_hooks.with(hook_context).run + yield + ensure + after_suite_hooks.with(hook_context).run + end end private + def handle_suite_hook(scope, collection, append_or_prepend, hook_type, block) + return nil unless scope == :suite + collection.__send__(append_or_prepend, hook_type.new(block, {})) + end + + def before_suite_hooks + @before_suite_hooks ||= Hooks::HookCollection.new + end + + def after_suite_hooks + @after_suite_hooks ||= Hooks::HookCollection.new + end + def get_files_to_run(paths) FlatMap.flat_map(paths_to_check(paths)) do |path| path = path.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR From bbb7f7730e6e2b74ca0f377573764323ebc6c022 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 7 Nov 2014 16:11:32 -0800 Subject: [PATCH 123/275] Explicitly ignore :suite hooks from Hooks module. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Configuration class now handles these and it’s the only place they are supported. This allows us to reduce object allocations a bit, and also clarifies the semantics of :suite hooks. --- .../allocations/1000_groups_1_example.rb | 40 ++++++++++++++++--- lib/rspec/core/hooks.rb | 30 +++++++++----- spec/rspec/core/suite_hooks_spec.rb | 14 +++++++ 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/benchmarks/allocations/1000_groups_1_example.rb b/benchmarks/allocations/1000_groups_1_example.rb index f2d7cc02d0..2ee32592d8 100644 --- a/benchmarks/allocations/1000_groups_1_example.rb +++ b/benchmarks/allocations/1000_groups_1_example.rb @@ -15,23 +15,51 @@ class_plus count ---------------------------------------- ----- -String 32000 -Array 14000 +String 28000 +Array 15000 RubyVM::Env 9000 -Hash 9000 Proc 9000 +Hash 9000 RSpec::Core::Hooks::HookCollection 6000 Array 5000 MatchData 3000 -RSpec::Core::Example::ExecutionResult 2000 Array 2000 Array 2000 Module 2000 +RSpec::Core::Example::ExecutionResult 2000 +RSpec::Core::Metadata::ExampleGroupHash 1000 Class 1000 Array 1000 -RSpec::Core::Metadata::ExampleGroupHash 1000 +RSpec::Core::Hooks::AroundHookCollection 1000 +RSpec::Core::Hooks::HookCollections 1000 RSpec::Core::Metadata::ExampleHash 1000 RSpec::Core::Example 1000 Array 1000 -RSpec::Core::Hooks::AroundHookCollection 1000 + + +After removing `:suite` support from `Hooks` module, +it cut Array and RSpec::Core::Hooks::HookCollection +allocations by 2000 each: + + class_plus count +---------------------------------------- ----- +String 28000 +Array 13000 +Proc 9000 +RubyVM::Env 9000 +Hash 9000 +Array 5000 +RSpec::Core::Hooks::HookCollection 4000 +MatchData 3000 +Array 2000 +RSpec::Core::Example::ExecutionResult 2000 +Module 2000 +Array 2000 RSpec::Core::Hooks::HookCollections 1000 +RSpec::Core::Example 1000 +Array 1000 +RSpec::Core::Metadata::ExampleHash 1000 +RSpec::Core::Hooks::AroundHookCollection 1000 +RSpec::Core::Metadata::ExampleGroupHash 1000 +Class 1000 +Array 1000 diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index 7bd64e19b9..edda5fdc00 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -184,6 +184,9 @@ module Hooks # # @note The `:example` and `:context` scopes are also available as # `:each` and `:all`, respectively. Use whichever you prefer. + # @note The `:scope` alias is only supported for hooks registered on + # `RSpec.configuration` since they exist independently of any + # example or example group. def before(*args, &block) hooks.register :append, :before, *args, &block end @@ -255,6 +258,9 @@ def prepend_before(*args, &block) # # @note The `:example` and `:context` scopes are also available as # `:each` and `:all`, respectively. Use whichever you prefer. + # @note The `:scope` alias is only supported for hooks registered on + # `RSpec.configuration` since they exist independently of any + # example or example group. def after(*args, &block) hooks.register :prepend, :after, *args, &block end @@ -326,12 +332,8 @@ def hooks @hooks ||= HookCollections.new( self, :around => { :example => AroundHookCollection.new }, - :before => { :example => HookCollection.new, - :context => HookCollection.new, - :suite => HookCollection.new }, - :after => { :example => HookCollection.new, - :context => HookCollection.new, - :suite => HookCollection.new } + :before => { :example => HookCollection.new, :context => HookCollection.new }, + :after => { :example => HookCollection.new, :context => HookCollection.new } ) end @@ -496,6 +498,17 @@ def around_example_hooks_for(example, initial_procsy=nil) def register(prepend_or_append, hook, *args, &block) scope, options = scope_and_options_from(*args) + + if scope == :suite + # TODO: consider making this an error in RSpec 4. For SemVer reasons, + # we are only warning in RSpec 3. + RSpec.warn_with "WARNING: `#{hook}(:suite)` hooks are only supported on " \ + "the RSpec configuration object. This " \ + "`#{hook}(:suite)` hook, registered on an example " \ + "group, will be ignored." + return + end + self[hook][scope].__send__(prepend_or_append, HOOK_TYPES[hook][scope].new(block, options)) end @@ -509,7 +522,7 @@ def run(hook, scope, example_or_group, initial_procsy=nil) find_hook(hook, scope, example_or_group, initial_procsy).run end - SCOPES = [:example, :context, :suite] + SCOPES = [:example, :context] SCOPE_ALIASES = { :each => :example, :all => :context } @@ -532,6 +545,7 @@ def process(host, globals, position, scope) end def scope_and_options_from(*args) + return :suite if args.first == :suite scope = extract_scope_from(args) meta = Metadata.build_hash_from(args, :warn_about_example_group_filtering) return scope, meta @@ -573,8 +587,6 @@ def find_hook(hook, scope, example_or_group, initial_procsy) before_example_hooks_for(example_or_group) when [:after, :example] after_example_hooks_for(example_or_group) - when [:before, :suite], [:after, :suite] - self[hook][:suite].with(example_or_group) end end diff --git a/spec/rspec/core/suite_hooks_spec.rb b/spec/rspec/core/suite_hooks_spec.rb index a23b5114b5..b0569879d6 100644 --- a/spec/rspec/core/suite_hooks_spec.rb +++ b/spec/rspec/core/suite_hooks_spec.rb @@ -21,6 +21,20 @@ module RSpec::Core RSpec.configuration.with_suite_hooks { } }.to raise_error(ZeroDivisionError) end + + context "registered on an example group" do + it "is ignored with a clear warning" do + sequence = [] + + expect { + RSpec.describe "Group" do + __send__(type, :suite) { sequence << :suite_hook } + example { sequence << :example } + end.run + }.to change { sequence }.to([:example]). + and output(a_string_including("#{type.to_s.split('_').last}(:suite)")).to_stderr + end + end end end From cb35a2b2947ce480c65d63ffe1591f2767cdc216 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 11 Dec 2014 16:18:01 -0800 Subject: [PATCH 124/275] Warn when ignored metadata is passed with a `:suite` hook. Fixes #1741. --- lib/rspec/core/configuration.rb | 21 +++++++++++---- spec/rspec/core/suite_hooks_spec.rb | 42 ++++++++++++++++++++++------- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index e62e0df423..bbc4ac18b6 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1421,7 +1421,7 @@ def apply_derived_metadata_to(metadata) # @see #after # @see #append_after def before(*args, &block) - handle_suite_hook(args.first, before_suite_hooks, :append, + handle_suite_hook(args, before_suite_hooks, :append, Hooks::BeforeHook, block) || super(*args, &block) end alias_method :append_before, :before @@ -1440,7 +1440,7 @@ def before(*args, &block) # @see #after # @see #append_after def prepend_before(*args, &block) - handle_suite_hook(args.first, before_suite_hooks, :prepend, + handle_suite_hook(args, before_suite_hooks, :prepend, Hooks::BeforeHook, block) || super(*args, &block) end @@ -1454,7 +1454,7 @@ def prepend_before(*args, &block) # @see #before # @see #prepend_before def after(*args, &block) - handle_suite_hook(args.first, after_suite_hooks, :prepend, + handle_suite_hook(args, after_suite_hooks, :prepend, Hooks::AfterHook, block) || super(*args, &block) end alias_method :prepend_after, :after @@ -1473,7 +1473,7 @@ def after(*args, &block) # @see #before # @see #prepend_before def append_after(*args, &block) - handle_suite_hook(args.first, after_suite_hooks, :append, + handle_suite_hook(args, after_suite_hooks, :append, Hooks::AfterHook, block) || super(*args, &block) end @@ -1492,8 +1492,19 @@ def with_suite_hooks private - def handle_suite_hook(scope, collection, append_or_prepend, hook_type, block) + def handle_suite_hook(args, collection, append_or_prepend, hook_type, block) + scope, meta = *args return nil unless scope == :suite + + if meta + # TODO: in RSpec 4, consider raising an error here. + # We warn only for backwards compatibility. + RSpec.warn_with "WARNING: `:suite` hooks do not support metadata since " \ + "they apply to the suite as a whole rather than " \ + "any individual example or example group that has metadata. " \ + "The metadata you have provided (#{meta.inspect}) will be ignored." + end + collection.__send__(append_or_prepend, hook_type.new(block, {})) end diff --git a/spec/rspec/core/suite_hooks_spec.rb b/spec/rspec/core/suite_hooks_spec.rb index b0569879d6..9cef0a8699 100644 --- a/spec/rspec/core/suite_hooks_spec.rb +++ b/spec/rspec/core/suite_hooks_spec.rb @@ -35,6 +35,16 @@ module RSpec::Core and output(a_string_including("#{type.to_s.split('_').last}(:suite)")).to_stderr end end + + context "registered with metadata" do + it "explicitly warns that the metadata is ignored" do + expect { + RSpec.configure do |c| + c.__send__(type, :suite, :some => :metadata) + end + }.to output(a_string_including(":suite", "metadata")).to_stderr + end + end end end @@ -48,6 +58,28 @@ module RSpec::Core describe "the runner" do include_context "Runner support" + def define_and_run_example_group(&block) + example_group = class_double(ExampleGroup, :descendants => []) + + allow(example_group).to receive(:run, &block) + allow(world).to receive_messages(:ordered_example_groups => [example_group]) + allow(config).to receive :load_spec_files + + runner = build_runner + runner.run err, out + end + + it "still runs :suite hooks with metadata even though the metadata is ignored" do + sequence = [] + allow(RSpec).to receive(:warn_with) + + config.before(:suite, :foo) { sequence << :before_suite } + config.after(:suite, :foo) { sequence << :after_suite } + define_and_run_example_group { sequence << :example_groups } + + expect(sequence).to eq([ :before_suite, :example_groups, :after_suite ]) + end + it "runs :suite hooks before and after example groups in the correct order" do sequence = [] @@ -60,15 +92,7 @@ module RSpec::Core config.prepend_after(:suite) { sequence << :after_suite_1 } config.append_after(:suite) { sequence << :after_suite_4 } - - example_group = class_double(ExampleGroup, :descendants => []) - - allow(example_group).to receive(:run) { sequence << :example_groups } - allow(world).to receive_messages(:ordered_example_groups => [example_group]) - allow(config).to receive :load_spec_files - - runner = build_runner - runner.run err, out + define_and_run_example_group { sequence << :example_groups } expect(sequence).to eq([ :before_suite_1, From 87d4f1552004a43d96f0906784657a0cc8dbb5ed Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 11 Dec 2014 16:27:45 -0800 Subject: [PATCH 125/275] Clarify terms used in spec. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `append_after` is the registration method, not the type — the type is `after`. --- spec/rspec/core/suite_hooks_spec.rb | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/spec/rspec/core/suite_hooks_spec.rb b/spec/rspec/core/suite_hooks_spec.rb index 9cef0a8699..54f9eec880 100644 --- a/spec/rspec/core/suite_hooks_spec.rb +++ b/spec/rspec/core/suite_hooks_spec.rb @@ -3,19 +3,21 @@ module RSpec::Core RSpec.describe "Configuration :suite hooks" do - [:before, :after, :prepend_before, :append_before, :prepend_after, :append_after].each do |type| - describe "a(n) #{type} hook" do + [:before, :after, :prepend_before, :append_before, :prepend_after, :append_after].each do |registration_method| + type = registration_method.to_s.split('_').last + + describe "a `:suite` hook registered with `#{registration_method}" do it 'is skipped when in dry run mode' do RSpec.configuration.dry_run = true expect { |b| - RSpec.configuration.__send__(type, :suite, &b) + RSpec.configuration.__send__(registration_method, :suite, &b) RSpec.configuration.with_suite_hooks { } }.not_to yield_control end it 'allows errors in the hook to propagate to the user' do - RSpec.configuration.__send__(type, :suite) { 1 / 0 } + RSpec.configuration.__send__(registration_method, :suite) { 1 / 0 } expect { RSpec.configuration.with_suite_hooks { } @@ -28,11 +30,11 @@ module RSpec::Core expect { RSpec.describe "Group" do - __send__(type, :suite) { sequence << :suite_hook } + __send__(registration_method, :suite) { sequence << :suite_hook } example { sequence << :example } end.run }.to change { sequence }.to([:example]). - and output(a_string_including("#{type.to_s.split('_').last}(:suite)")).to_stderr + and output(a_string_including("#{type}(:suite)")).to_stderr end end @@ -40,7 +42,7 @@ module RSpec::Core it "explicitly warns that the metadata is ignored" do expect { RSpec.configure do |c| - c.__send__(type, :suite, :some => :metadata) + c.__send__(registration_method, :suite, :some => :metadata) end }.to output(a_string_including(":suite", "metadata")).to_stderr end From 900ec2c7000d71aa12ba43d1040d959d6e57224a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 11 Dec 2014 16:29:04 -0800 Subject: [PATCH 126/275] Add changelog. --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index cfcedc854c..c9135d8c30 100644 --- a/Changelog.md +++ b/Changelog.md @@ -22,6 +22,9 @@ Enhancements: * Improve the documentation of around hooks. (Jim Kingdon, #1772) * Support prepending of modules into example groups from config and allow filtering based on metadata. (Arlandis Word, #1806) +* Emit warnings when `:suite` hooks are registered on an example group + (where it has always been ignored) or are registered with metadata + (which has always been ignored). (Myron Marston, #1805) Bug Fixes: From 92e1e27aad4deb67d2554c3b9152565a35bf46af Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 15 Dec 2014 15:36:23 -0800 Subject: [PATCH 127/275] Prefer `RSpec.describe` to `ExampleGroup.describe`. The former is a public API and is shorter. --- spec/rspec/core/configuration_spec.rb | 28 +-- spec/rspec/core/example_group_spec.rb | 208 +++++++++--------- spec/rspec/core/example_spec.rb | 70 +++--- .../core/failed_example_notification_spec.rb | 2 +- .../formatters/base_text_formatter_spec.rb | 4 +- .../documentation_formatter_spec.rb | 4 +- .../core/formatters/json_formatter_spec.rb | 8 +- .../core/formatters/profile_formatter_spec.rb | 8 +- spec/rspec/core/hooks_filtering_spec.rb | 16 +- spec/rspec/core/hooks_spec.rb | 22 +- spec/rspec/core/memoized_helpers_spec.rb | 36 +-- spec/rspec/core/pending_example_spec.rb | 26 +-- spec/rspec/core/reporter_spec.rb | 4 +- spec/rspec/core/shared_context_spec.rb | 6 +- spec/rspec/core/shared_example_group_spec.rb | 4 +- spec/rspec/core/world_spec.rb | 6 +- spec/rspec/core_spec.rb | 2 +- 17 files changed, 227 insertions(+), 227 deletions(-) diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index 22549f103f..3b6a7991fb 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -892,7 +892,7 @@ def metadata_hash(*args) c.include(InstanceLevelMethods) end - group = ExampleGroup.describe('does like, stuff and junk', :magic_key => :include) { } + group = RSpec.describe('does like, stuff and junk', :magic_key => :include) { } expect(group).not_to respond_to(:you_call_this_a_blt?) expect(group.new.you_call_this_a_blt?).to eq("egad man, where's the mayo?!?!?") end @@ -904,7 +904,7 @@ def metadata_hash(*args) c.include(InstanceLevelMethods, :magic_key => :include) end - group = ExampleGroup.describe('does like, stuff and junk', :magic_key => :include) { } + group = RSpec.describe('does like, stuff and junk', :magic_key => :include) { } expect(group).not_to respond_to(:you_call_this_a_blt?) expect(group.new.you_call_this_a_blt?).to eq("egad man, where's the mayo?!?!?") end @@ -931,7 +931,7 @@ def metadata_hash(*args) c.extend(ThatThingISentYou, :magic_key => :extend) end - group = ExampleGroup.describe(ThatThingISentYou, :magic_key => :extend) { } + group = RSpec.describe(ThatThingISentYou, :magic_key => :extend) { } expect(group).to respond_to(:that_thing) end @@ -959,7 +959,7 @@ def metadata_hash(*args) c.prepend(SomeRandomMod) end - group = ExampleGroup.describe('yo') { } + group = RSpec.describe('yo') { } expect(group.new.foo).to eq("foobar") end end @@ -970,7 +970,7 @@ def metadata_hash(*args) c.prepend(SomeRandomMod, :magic_key => :include) end - group = ExampleGroup.describe('yo', :magic_key => :include) { } + group = RSpec.describe('yo', :magic_key => :include) { } expect(group.new.foo).to eq("foobar") end end @@ -1506,7 +1506,7 @@ def exclude?(line) describe "#configure_group" do it "extends with 'extend'" do mod = Module.new - group = ExampleGroup.describe("group", :foo => :bar) + group = RSpec.describe("group", :foo => :bar) config.extend(mod, :foo => :bar) config.configure_group(group) @@ -1515,7 +1515,7 @@ def exclude?(line) it "extends with 'module'" do mod = Module.new - group = ExampleGroup.describe("group", :foo => :bar) + group = RSpec.describe("group", :foo => :bar) config.include(mod, :foo => :bar) config.configure_group(group) @@ -1524,7 +1524,7 @@ def exclude?(line) it "requires only one matching filter" do mod = Module.new - group = ExampleGroup.describe("group", :foo => :bar) + group = RSpec.describe("group", :foo => :bar) config.include(mod, :foo => :bar, :baz => :bam) config.configure_group(group) @@ -1539,7 +1539,7 @@ def self.included(host) end mod2 = Module.new - group = ExampleGroup.describe("group") + group = RSpec.describe("group") config.include(mod1) config.include(mod2, :foo => :bar) @@ -1568,7 +1568,7 @@ def self.prepended(host) it "doesn't include a module when already included in ancestor" do config.include(IncludeExtendOrPrependMeOnce, :foo => :bar) - group = ExampleGroup.describe("group", :foo => :bar) + group = RSpec.describe("group", :foo => :bar) child = group.describe("child") config.configure_group(group) @@ -1578,7 +1578,7 @@ def self.prepended(host) it "doesn't extend when ancestor is already extended with same module" do config.extend(IncludeExtendOrPrependMeOnce, :foo => :bar) - group = ExampleGroup.describe("group", :foo => :bar) + group = RSpec.describe("group", :foo => :bar) child = group.describe("child") config.configure_group(group) @@ -1589,7 +1589,7 @@ def self.prepended(host) :if => RSpec::Support::RubyFeatures.module_prepends_supported? do config.prepend(IncludeExtendOrPrependMeOnce, :foo => :bar) - group = ExampleGroup.describe("group", :foo => :bar) + group = RSpec.describe("group", :foo => :bar) child = group.describe("child") config.configure_group(group) @@ -1661,7 +1661,7 @@ class << self end def metadata_hash(*args) config.alias_example_to :my_example_method, *args - group = ExampleGroup.describe("group") + group = RSpec.describe("group") example = group.my_example_method("description") example.metadata end @@ -1884,7 +1884,7 @@ def strategy.order(list) value_1 = value_2 = nil - ExampleGroup.describe "Group" do + RSpec.describe "Group" do it "works" do value_1 = the_example value_2 = another_example_helper diff --git a/spec/rspec/core/example_group_spec.rb b/spec/rspec/core/example_group_spec.rb index 67f2e51169..bae30ad556 100644 --- a/spec/rspec/core/example_group_spec.rb +++ b/spec/rspec/core/example_group_spec.rb @@ -5,7 +5,7 @@ module RSpec::Core RSpec.describe ExampleGroup do it_behaves_like "metadata hash builder" do def metadata_hash(*args) - group = ExampleGroup.describe('example description', *args) + group = RSpec.describe('example description', *args) group.metadata end end @@ -13,18 +13,18 @@ def metadata_hash(*args) context "when RSpec.configuration.format_docstrings is set to a block" do it "formats the description with that block" do RSpec.configuration.format_docstrings { |s| s.upcase } - group = ExampleGroup.describe(' an example ') + group = RSpec.describe(' an example ') expect(group.description).to eq(' AN EXAMPLE ') end end it 'does not treat the first argument as a metadata key even if it is a symbol' do - group = ExampleGroup.describe(:symbol) + group = RSpec.describe(:symbol) expect(group.metadata).not_to include(:symbol) end it 'treats the first argument as part of the description when it is a symbol' do - group = ExampleGroup.describe(:symbol) + group = RSpec.describe(:symbol) expect(group.description).to eq("symbol") end @@ -63,12 +63,12 @@ def metadata_hash(*args) it 'gives groups friendly human readable class names' do stub_const("MyGem::Klass", Class.new) - parent = ExampleGroup.describe(MyGem::Klass) + parent = RSpec.describe(MyGem::Klass) expect(parent).to have_class_const("MyGemKlass") end it 'nests constants to match the group nesting' do - grandparent = ExampleGroup.describe("The grandparent") + grandparent = RSpec.describe("The grandparent") parent = grandparent.describe("the parent") child = parent.describe("the child") @@ -77,7 +77,7 @@ def metadata_hash(*args) end it 'removes non-ascii characters from the const name since some rubies barf on that' do - group = ExampleGroup.describe("A chinese character: 们") + group = RSpec.describe("A chinese character: 们") expect(group).to have_class_const("AChineseCharacter") end @@ -86,12 +86,12 @@ def metadata_hash(*args) ExampleGroup.const_set("1B", Object.new) }.to raise_error(NameError) - group = ExampleGroup.describe("1B") + group = RSpec.describe("1B") expect(group).to have_class_const("Nested1B") end it 'does not warn when defining a Config example group (since RbConfig triggers warnings when Config is referenced)' do - expect { ExampleGroup.describe("Config") }.not_to output.to_stderr + expect { RSpec.describe("Config") }.not_to output.to_stderr end it 'ignores top level constants that have the same name' do @@ -102,7 +102,7 @@ def metadata_hash(*args) end it 'disambiguates name collisions by appending a number', :unless => RUBY_VERSION == '1.9.2' do - groups = 10.times.map { ExampleGroup.describe("Collision") } + groups = 10.times.map { RSpec.describe("Collision") } expect(groups[0]).to have_class_const("Collision") expect(groups[1]).to have_class_const("Collision_2") expect(groups[8]).to have_class_const("Collision_9") @@ -118,34 +118,34 @@ def metadata_hash(*args) # so the presence of another anonymous group in our # test suite doesn't cause an unexpected number # to be appended. - group = ExampleGroup.describe("name of unnamed group") + group = RSpec.describe("name of unnamed group") subgroup = group.describe expect(subgroup).to have_class_const("NameOfUnnamedGroup::Anonymous") end it 'assigns the const before evaling the group so error messages include the name' do expect { - ExampleGroup.describe("Calling an undefined method") { foo } + RSpec.describe("Calling an undefined method") { foo } }.to raise_error(/ExampleGroups::CallingAnUndefinedMethod/) end it 'does not have problems with example groups named "Core"', :unless => RUBY_VERSION == '1.9.2' do - ExampleGroup.describe("Core") + RSpec.describe("Core") expect(defined?(::RSpec::ExampleGroups::Core)).to be_truthy # The original bug was triggered when a group was defined AFTER one named `Core`, # due to it not using the fully qualified `::RSpec::Core::ExampleGroup` constant. - group = ExampleGroup.describe("Another group") + group = RSpec.describe("Another group") expect(group).to have_class_const("AnotherGroup") end it 'does not have problems with example groups named "RSpec"', :unless => RUBY_VERSION == '1.9.2' do - ExampleGroup.describe("RSpec") + RSpec.describe("RSpec") expect(defined?(::RSpec::ExampleGroups::RSpec)).to be_truthy # The original bug was triggered when a group was defined AFTER one named `RSpec`, # due to it not using the fully qualified `::RSpec::Core::ExampleGroup` constant. - group = ExampleGroup.describe("Yet Another group") + group = RSpec.describe("Yet Another group") expect(group).to have_class_const("YetAnotherGroup") end end @@ -156,7 +156,7 @@ def metadata_hash(*args) RSpec.configuration.order = :random run_order = [] - group = ExampleGroup.describe "outer", :order => :defined do + group = RSpec.describe "outer", :order => :defined do context "subgroup 1" do example { run_order << :g1_e1 } example { run_order << :g1_e2 } @@ -179,7 +179,7 @@ def metadata_hash(*args) let(:group) do order = self.run_order - ExampleGroup.describe "group", :order => :unrecognized do + RSpec.describe "group", :order => :unrecognized do example { order << :ex_1 } example { order << :ex_2 } end @@ -219,7 +219,7 @@ def ascending_numbers end run_order = [] - group = ExampleGroup.describe "outer", :order => :custom do + group = RSpec.describe "outer", :order => :custom do example("e2") { run_order << :e2 } example("e1") { run_order << :e1 } @@ -257,7 +257,7 @@ def ascending_numbers describe "top level group" do it "runs its children" do examples_run = [] - group = ExampleGroup.describe("parent") do + group = RSpec.describe("parent") do describe("child") do it "does something" do |ex| examples_run << ex @@ -272,7 +272,7 @@ def ascending_numbers context "with a failure in the top level group" do it "runs its children " do examples_run = [] - group = ExampleGroup.describe("parent") do + group = RSpec.describe("parent") do it "fails" do |ex| examples_run << ex raise "fail" @@ -291,7 +291,7 @@ def ascending_numbers describe "descendants" do it "returns self + all descendants" do - group = ExampleGroup.describe("parent") do + group = RSpec.describe("parent") do describe("child") do describe("grandchild 1") {} describe("grandchild 2") {} @@ -304,14 +304,14 @@ def ascending_numbers describe "child" do it "is known by parent" do - parent = ExampleGroup.describe + parent = RSpec.describe child = parent.describe expect(parent.children).to eq([child]) end it "is not registered in world" do world = RSpec::Core::World.new - parent = ExampleGroup.describe + parent = RSpec.describe world.register(parent) parent.describe expect(world.example_groups).to eq([parent]) @@ -331,14 +331,14 @@ def ascending_numbers end it "includes examples in groups matching filter" do - group = ExampleGroup.describe("does something", spec_metadata) + group = RSpec.describe("does something", spec_metadata) all_examples = [ group.example("first"), group.example("second") ] expect(group.filtered_examples).to eq(all_examples) end it "includes examples directly matching filter" do - group = ExampleGroup.describe("does something") + group = RSpec.describe("does something") filtered_examples = [ group.example("first", spec_metadata), group.example("second", spec_metadata) @@ -357,14 +357,14 @@ def ascending_numbers end it "excludes examples in groups matching filter" do - group = ExampleGroup.describe("does something", spec_metadata) + group = RSpec.describe("does something", spec_metadata) [ group.example("first"), group.example("second") ] expect(group.filtered_examples).to be_empty end it "excludes examples directly matching filter" do - group = ExampleGroup.describe("does something") + group = RSpec.describe("does something") [ group.example("first", spec_metadata), group.example("second", spec_metadata) @@ -444,7 +444,7 @@ def ascending_numbers context "with no filters" do it "returns all" do - group = ExampleGroup.describe + group = RSpec.describe allow(group).to receive(:world) { world } example = group.example("does something") expect(group.filtered_examples).to eq([example]) @@ -456,7 +456,7 @@ def ascending_numbers filter_manager = FilterManager.new filter_manager.include :awesome => false allow(world).to receive_messages(:filter_manager => filter_manager) - group = ExampleGroup.describe + group = RSpec.describe allow(group).to receive(:world) { world } group.example("does something") expect(group.filtered_examples).to eq([]) @@ -468,20 +468,20 @@ def ascending_numbers context "with a constant as the first parameter" do it "is that constant" do - expect(ExampleGroup.describe(Object) { }.described_class).to eq(Object) + expect(RSpec.describe(Object) { }.described_class).to eq(Object) end end context "with a string as the first parameter" do it "is nil" do - expect(ExampleGroup.describe("i'm a computer") { }.described_class).to be_nil + expect(RSpec.describe("i'm a computer") { }.described_class).to be_nil end end context "with a constant in an outer group" do context "and a string in an inner group" do it "is the top level constant" do - group = ExampleGroup.describe(String) do + group = RSpec.describe(String) do describe "inner" do example "described_class is String" do expect(described_class).to eq(String) @@ -495,7 +495,7 @@ def ascending_numbers context "and metadata redefinition after `described_class` call" do it "is the redefined level constant" do - group = ExampleGroup.describe(String) do + group = RSpec.describe(String) do described_class metadata[:described_class] = Object describe "inner" do @@ -512,7 +512,7 @@ def ascending_numbers context "in a nested group" do it "inherits the described class/module from the outer group" do - group = ExampleGroup.describe(String) do + group = RSpec.describe(String) do describe "nested" do example "describes is String" do expect(described_class).to eq(String) @@ -527,7 +527,7 @@ def ascending_numbers def described_class_value value = nil - ExampleGroup.describe(String) do + RSpec.describe(String) do yield if block_given? describe Array do example { value = described_class } @@ -551,7 +551,7 @@ def described_class_value def define_and_run_group(define_outer_example = false) outer_described_class = inner_described_class = nil - ExampleGroup.describe("some string") do + RSpec.describe("some string") do example { outer_described_class = described_class } if define_outer_example describe Array do @@ -588,32 +588,32 @@ def define_and_run_group(define_outer_example = false) describe '#description' do it "grabs the description from the metadata" do - group = ExampleGroup.describe(Object, "my desc") { } + group = RSpec.describe(Object, "my desc") { } expect(group.description).to eq(group.metadata[:description]) end end describe '#metadata' do it "adds the third parameter to the metadata" do - expect(ExampleGroup.describe(Object, nil, 'foo' => 'bar') { }.metadata).to include({ "foo" => 'bar' }) + expect(RSpec.describe(Object, nil, 'foo' => 'bar') { }.metadata).to include({ "foo" => 'bar' }) end it "adds the the file_path to metadata" do - expect(ExampleGroup.describe(Object) { }.metadata[:file_path]).to eq(relative_path(__FILE__)) + expect(RSpec.describe(Object) { }.metadata[:file_path]).to eq(relative_path(__FILE__)) end it "has a reader for file_path" do - expect(ExampleGroup.describe(Object) { }.file_path).to eq(relative_path(__FILE__)) + expect(RSpec.describe(Object) { }.file_path).to eq(relative_path(__FILE__)) end it "adds the line_number to metadata" do - expect(ExampleGroup.describe(Object) { }.metadata[:line_number]).to eq(__LINE__) + expect(RSpec.describe(Object) { }.metadata[:line_number]).to eq(__LINE__) end end [:focus, :fexample, :fit, :fspecify].each do |example_alias| describe ".#{example_alias}" do - let(:focused_example) { ExampleGroup.describe.send example_alias, "a focused example" } + let(:focused_example) { RSpec.describe.send example_alias, "a focused example" } it 'defines an example that can be filtered with :focus => true' do expect(focused_example.metadata[:focus]).to be_truthy @@ -624,7 +624,7 @@ def define_and_run_group(define_outer_example = false) describe "#before, after, and around hooks" do describe "scope aliasing" do it "aliases the `:context` hook scope to `:all` for before-hooks" do - group = ExampleGroup.describe + group = RSpec.describe order = [] group.before(:context) { order << :before_context } group.example("example") { order << :example } @@ -635,7 +635,7 @@ def define_and_run_group(define_outer_example = false) end it "aliases the `:example` hook scope to `:each` for before-hooks" do - group = ExampleGroup.describe + group = RSpec.describe order = [] group.before(:example) { order << :before_example } group.example("example") { order << :example } @@ -646,7 +646,7 @@ def define_and_run_group(define_outer_example = false) end it "aliases the `:context` hook scope to `:all` for after-hooks" do - group = ExampleGroup.describe + group = RSpec.describe order = [] group.example("example") { order << :example } group.example("example") { order << :example } @@ -657,7 +657,7 @@ def define_and_run_group(define_outer_example = false) end it "aliases the `:example` hook scope to `:each` for after-hooks" do - group = ExampleGroup.describe + group = RSpec.describe order = [] group.example("example") { order << :example } group.example("example") { order << :example } @@ -669,7 +669,7 @@ def define_and_run_group(define_outer_example = false) end it "runs the before alls in order" do - group = ExampleGroup.describe + group = RSpec.describe order = [] group.before(:all) { order << 1 } group.before(:all) { order << 2 } @@ -682,7 +682,7 @@ def define_and_run_group(define_outer_example = false) end it "does not set RSpec.world.wants_to_quit in case of an error in before all (without fail_fast?)" do - group = ExampleGroup.describe + group = RSpec.describe group.before(:all) { raise "error in before all" } group.example("example") {} @@ -691,7 +691,7 @@ def define_and_run_group(define_outer_example = false) end it "runs the before eachs in order" do - group = ExampleGroup.describe + group = RSpec.describe order = [] group.before(:each) { order << 1 } group.before(:each) { order << 2 } @@ -704,7 +704,7 @@ def define_and_run_group(define_outer_example = false) end it "runs the after eachs in reverse order" do - group = ExampleGroup.describe + group = RSpec.describe order = [] group.after(:each) { order << 1 } group.after(:each) { order << 2 } @@ -717,7 +717,7 @@ def define_and_run_group(define_outer_example = false) end it "runs the after alls in reverse order" do - group = ExampleGroup.describe + group = RSpec.describe order = [] group.after(:all) { order << 1 } group.after(:all) { order << 2 } @@ -736,7 +736,7 @@ def define_and_run_group(define_outer_example = false) c.filter_run :focus => true end - unfiltered_group = ExampleGroup.describe "unfiltered" do + unfiltered_group = RSpec.describe "unfiltered" do before(:all) { hooks_run << :unfiltered_before_all } after(:all) { hooks_run << :unfiltered_after_all } @@ -745,7 +745,7 @@ def define_and_run_group(define_outer_example = false) end end - filtered_group = ExampleGroup.describe "filtered", :focus => true do + filtered_group = RSpec.describe "filtered", :focus => true do before(:all) { hooks_run << :filtered_before_all } after(:all) { hooks_run << :filtered_after_all } @@ -768,7 +768,7 @@ def define_and_run_group(define_outer_example = false) c.after(:all) { order << :after_all_defined_in_config } end - group = ExampleGroup.describe + group = RSpec.describe group.before(:all) { order << :top_level_before_all } group.before(:each) { order << :before_each } group.after(:each) { order << :after_each } @@ -805,7 +805,7 @@ def define_and_run_group(define_outer_example = false) end context "after(:all)" do - let(:outer) { ExampleGroup.describe } + let(:outer) { RSpec.describe } let(:inner) { outer.describe } it "has access to state defined before(:all)" do @@ -838,7 +838,7 @@ def define_and_run_group(define_outer_example = false) end it "treats an error in before(:each) as a failure" do - group = ExampleGroup.describe + group = RSpec.describe group.before(:each) { raise "error in before each" } example = group.example("equality") { expect(1).to eq(2) } expect(group.run).to be(false) @@ -847,7 +847,7 @@ def define_and_run_group(define_outer_example = false) end it "treats an error in before(:all) as a failure" do - group = ExampleGroup.describe + group = RSpec.describe group.before(:all) { raise "error in before all" } example = group.example("equality") { expect(1).to eq(2) } expect(group.run).to be_falsey @@ -860,7 +860,7 @@ def define_and_run_group(define_outer_example = false) it "exposes instance variables set in before(:all) from after(:all) even if a before(:all) error occurs" do ivar_value_in_after_hook = nil - group = ExampleGroup.describe do + group = RSpec.describe do before(:all) do @an_ivar = :set_in_before_all raise "fail" @@ -877,7 +877,7 @@ def define_and_run_group(define_outer_example = false) it "treats an error in before(:all) as a failure for a spec in a nested group" do example = nil - group = ExampleGroup.describe do + group = RSpec.describe do before(:all) { raise "error in before all" } describe "nested" do @@ -900,7 +900,7 @@ def define_and_run_group(define_outer_example = false) end let(:group) do - ExampleGroup.describe do + RSpec.describe do after(:all) { hooks_run << :one; raise "An error in an after(:all) hook" } after(:all) { hooks_run << :two; raise "A different hook raising an error" } it("equality") { expect(1).to eq(1) } @@ -927,7 +927,7 @@ def define_and_run_group(define_outer_example = false) end describe ".pending" do - let(:group) { ExampleGroup.describe { pending { fail } } } + let(:group) { RSpec.describe { pending { fail } } } it "generates a pending example" do group.run @@ -942,7 +942,7 @@ def define_and_run_group(define_outer_example = false) it 'sets the backtrace to the example definition so it can be located by the user' do file = RSpec::Core::Metadata.relative_path(__FILE__) expected = [file, __LINE__ + 2].map(&:to_s) - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do pending { } end group.run @@ -960,7 +960,7 @@ def define_and_run_group(define_outer_example = false) end describe "pending with metadata" do - let(:group) { ExampleGroup.describe { + let(:group) { RSpec.describe { example("unimplemented", :pending => true) { fail } } } @@ -976,7 +976,7 @@ def define_and_run_group(define_outer_example = false) end describe "pending with message in metadata" do - let(:group) { ExampleGroup.describe { + let(:group) { RSpec.describe { example("unimplemented", :pending => 'not done') { fail } } } @@ -992,7 +992,7 @@ def define_and_run_group(define_outer_example = false) end describe ".skip" do - let(:group) { ExampleGroup.describe { skip("skip this") { } } } + let(:group) { RSpec.describe { skip("skip this") { } } } it "generates a skipped example" do group.run @@ -1006,7 +1006,7 @@ def define_and_run_group(define_outer_example = false) end describe "skip with metadata" do - let(:group) { ExampleGroup.describe { + let(:group) { RSpec.describe { example("skip this", :skip => true) { } } } @@ -1022,7 +1022,7 @@ def define_and_run_group(define_outer_example = false) end describe "skip with message in metadata" do - let(:group) { ExampleGroup.describe { + let(:group) { RSpec.describe { example("skip this", :skip => 'not done') { } } } @@ -1039,7 +1039,7 @@ def define_and_run_group(define_outer_example = false) %w[xit xspecify xexample].each do |method_name| describe ".#{method_name}" do - let(:group) { ExampleGroup.describe.tap {|x| + let(:group) { RSpec.describe.tap {|x| x.send(method_name, "is pending") { } }} @@ -1090,7 +1090,7 @@ def executed_examples_of(group) it "generates an example group that can be filtered with :focus" do RSpec.configuration.filter_run :focus - parent_group = ExampleGroup.describe do + parent_group = RSpec.describe do describe "not focused" do example("not focused example") { } end @@ -1116,7 +1116,7 @@ def extract_execution_results(group) end it 'marks every example as pending' do - group = ExampleGroup.describe("group", :pending => true) do + group = RSpec.describe("group", :pending => true) do it("passes") { } it("fails", :pending => 'unimplemented') { fail } end @@ -1138,13 +1138,13 @@ def extract_execution_results(group) describe "adding examples" do it "allows adding an example using 'it'" do - group = ExampleGroup.describe + group = RSpec.describe group.it("should do something") { } expect(group.examples.size).to eq(1) end it "exposes all examples at examples" do - group = ExampleGroup.describe + group = RSpec.describe group.it("should do something 1") { } group.it("should do something 2") { } group.it("should do something 3") { } @@ -1152,7 +1152,7 @@ def extract_execution_results(group) end it "maintains the example order" do - group = ExampleGroup.describe + group = RSpec.describe group.it("should 1") { } group.it("should 2") { } group.it("should 3") { } @@ -1190,7 +1190,7 @@ def extract_execution_results(group) let(:reporter) { double("reporter").as_null_object } it "returns true if all examples pass" do - group = ExampleGroup.describe('group') do + group = RSpec.describe('group') do example('ex 1') { expect(1).to eq(1) } example('ex 2') { expect(1).to eq(1) } end @@ -1199,7 +1199,7 @@ def extract_execution_results(group) end it "returns false if any of the examples fail" do - group = ExampleGroup.describe('group') do + group = RSpec.describe('group') do example('ex 1') { expect(1).to eq(1) } example('ex 2') { expect(1).to eq(2) } end @@ -1208,7 +1208,7 @@ def extract_execution_results(group) end it "runs all examples, regardless of any of them failing" do - group = ExampleGroup.describe('group') do + group = RSpec.describe('group') do example('ex 1') { expect(1).to eq(2) } example('ex 2') { expect(1).to eq(1) } end @@ -1283,7 +1283,7 @@ def extract_execution_results(group) describe "#top_level_description" do it "returns the description from the outermost example group" do group = nil - ExampleGroup.describe("top") do + RSpec.describe("top") do context "middle" do group = describe "bottom" do end @@ -1299,7 +1299,7 @@ def extract_execution_results(group) context "with fail_fast? => true" do let(:group) do - group = RSpec::Core::ExampleGroup.describe + group = RSpec.describe allow(group).to receive(:fail_fast?) { true } group end @@ -1324,7 +1324,7 @@ def extract_execution_results(group) end context "with RSpec.world.wants_to_quit=true" do - let(:group) { RSpec::Core::ExampleGroup.describe } + let(:group) { RSpec.describe } before do allow(RSpec.world).to receive(:wants_to_quit) { true } @@ -1354,7 +1354,7 @@ def extract_execution_results(group) context "with all examples passing" do it "returns true" do - group = RSpec::Core::ExampleGroup.describe("something") do + group = RSpec.describe("something") do it "does something" do # pass end @@ -1371,7 +1371,7 @@ def extract_execution_results(group) context "with top level example failing" do it "returns false" do - group = RSpec::Core::ExampleGroup.describe("something") do + group = RSpec.describe("something") do it "does something (wrong - fail)" do raise "fail" end @@ -1388,7 +1388,7 @@ def extract_execution_results(group) context "with nested example failing" do it "returns true" do - group = RSpec::Core::ExampleGroup.describe("something") do + group = RSpec.describe("something") do it "does something" do # pass end @@ -1406,7 +1406,7 @@ def extract_execution_results(group) %w[include_examples include_context].each do |name| describe "##{name}" do - let(:group) { ExampleGroup.describe } + let(:group) { RSpec.describe } before do group.shared_examples "named this" do example("does something") {} @@ -1440,7 +1440,7 @@ def extract_execution_results(group) it "passes parameters to the shared content" do passed_params = {} - group = ExampleGroup.describe + group = RSpec.describe group.shared_examples "named this with params" do |param1, param2| it("has access to the given parameters") do @@ -1456,7 +1456,7 @@ def extract_execution_results(group) end it "adds shared instance methods to the group" do - group = ExampleGroup.describe('fake group') + group = RSpec.describe('fake group') group.shared_examples "named this with params" do |param1| def foo; end end @@ -1466,7 +1466,7 @@ def foo; end it "evals the shared example group only once" do eval_count = 0 - group = ExampleGroup.describe('fake group') + group = RSpec.describe('fake group') group.shared_examples("named this with params") { |p| eval_count += 1 } group.send(name, "named this with params", :a) expect(eval_count).to eq(1) @@ -1474,7 +1474,7 @@ def foo; end it "evals the block when given" do key = "#{__FILE__}:#{__LINE__}" - group = ExampleGroup.describe do + group = RSpec.describe do shared_examples(key) do it("does something") do expect(foo).to eq("bar") @@ -1492,7 +1492,7 @@ def foo; "bar"; end describe "#it_should_behave_like" do it "creates a nested group" do - group = ExampleGroup.describe('fake group') + group = RSpec.describe('fake group') group.shared_examples_for("thing") {} group.it_should_behave_like("thing") expect(group.children.count).to eq(1) @@ -1500,14 +1500,14 @@ def foo; "bar"; end it "creates a nested group for a class" do klass = Class.new - group = ExampleGroup.describe('fake group') + group = RSpec.describe('fake group') group.shared_examples_for(klass) {} group.it_should_behave_like(klass) expect(group.children.count).to eq(1) end it "adds shared examples to nested group" do - group = ExampleGroup.describe('fake group') + group = RSpec.describe('fake group') group.shared_examples_for("thing") do it("does something") end @@ -1516,7 +1516,7 @@ def foo; "bar"; end end it "adds shared instance methods to nested group" do - group = ExampleGroup.describe('fake group') + group = RSpec.describe('fake group') group.shared_examples_for("thing") do def foo; end end @@ -1525,7 +1525,7 @@ def foo; end end it "adds shared class methods to nested group" do - group = ExampleGroup.describe('fake group') + group = RSpec.describe('fake group') group.shared_examples_for("thing") do def self.foo; end end @@ -1536,7 +1536,7 @@ def self.foo; end it "passes parameters to the shared example group" do passed_params = {} - group = ExampleGroup.describe("group") do + group = RSpec.describe("group") do shared_examples_for("thing") do |param1, param2| it("has access to the given parameters") do passed_params[:param1] = param1 @@ -1553,7 +1553,7 @@ def self.foo; end end it "adds shared instance methods to nested group" do - group = ExampleGroup.describe('fake group') + group = RSpec.describe('fake group') group.shared_examples_for("thing") do |param1| def foo; end end @@ -1563,7 +1563,7 @@ def foo; end it "evals the shared example group only once" do eval_count = 0 - group = ExampleGroup.describe('fake group') + group = RSpec.describe('fake group') group.shared_examples_for("thing") { |p| eval_count += 1 } group.it_should_behave_like("thing", :a) expect(eval_count).to eq(1) @@ -1572,7 +1572,7 @@ def foo; end context "given a block" do it "evaluates the block in nested group" do scopes = [] - group = ExampleGroup.describe("group") do + group = RSpec.describe("group") do shared_examples_for("thing") do it("gets run in the nested group") do scopes << self.class @@ -1592,7 +1592,7 @@ def foo; end it "raises a helpful error message when shared context is not found" do expect do - ExampleGroup.describe do + RSpec.describe do it_should_behave_like "shared stuff" end end.to raise_error(ArgumentError,%q|Could not find shared examples "shared stuff"|) @@ -1646,7 +1646,7 @@ def foo; end it 'prevents defining nested isolated shared contexts' do expect { - ExampleGroup.describe do + RSpec.describe do ExampleGroup.shared_examples("common functionality") {} end }.to raise_error(/not allowed/) @@ -1664,7 +1664,7 @@ def foo; end an_example = nil line = __LINE__ + 2 - group = ExampleGroup.describe 'SomeClass1' do + group = RSpec.describe 'SomeClass1' do example 'an example' do an_example = self end @@ -1682,7 +1682,7 @@ def foo; end an_example = nil line = __LINE__ + 2 - group = ExampleGroup.describe 'SomeClass2' do + group = RSpec.describe 'SomeClass2' do example do an_example = self end @@ -1698,7 +1698,7 @@ def foo; end it 'handles before context hooks' do a_before_hook = nil - group = ExampleGroup.describe 'SomeClass3' do + group = RSpec.describe 'SomeClass3' do before(:context) do a_before_hook = self end @@ -1713,7 +1713,7 @@ def foo; end it 'handles after context hooks' do an_after_hook = nil - group = ExampleGroup.describe 'SomeClass4' do + group = RSpec.describe 'SomeClass4' do after(:context) do an_after_hook = self end @@ -1729,7 +1729,7 @@ def foo; end inspect_output = nil line = __LINE__ + 2 - group = ExampleGroup.describe do + group = RSpec.describe do example do inspect_output = inspect end diff --git a/spec/rspec/core/example_spec.rb b/spec/rspec/core/example_spec.rb index 9946e0da19..fe842de0a7 100644 --- a/spec/rspec/core/example_spec.rb +++ b/spec/rspec/core/example_spec.rb @@ -5,7 +5,7 @@ RSpec.describe RSpec::Core::Example, :parent_metadata => 'sample' do let(:example_group) do - RSpec::Core::ExampleGroup.describe('group description') + RSpec.describe('group description') end let(:example_instance) do @@ -235,7 +235,7 @@ def assert(val) describe "#run" do it "sets its reference to the example group instance to nil" do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do example('example') { expect(1).to eq(1) } end group.run @@ -243,7 +243,7 @@ def assert(val) end it "generates a description before tearing down mocks in case a mock object is used in the description" do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do example { test = double('Test'); expect(test).to eq test } end @@ -255,7 +255,7 @@ def assert(val) it "runs after(:each) when the example passes" do after_run = false - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do after(:each) { after_run = true } example('example') { expect(1).to eq(1) } end @@ -265,7 +265,7 @@ def assert(val) it "runs after(:each) when the example fails" do after_run = false - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do after(:each) { after_run = true } example('example') { expect(1).to eq(2) } end @@ -275,7 +275,7 @@ def assert(val) it "runs after(:each) when the example raises an Exception" do after_run = false - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do after(:each) { after_run = true } example('example') { raise "this error" } end @@ -286,7 +286,7 @@ def assert(val) context "with an after(:each) that raises" do it "runs subsequent after(:each)'s" do after_run = false - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do after(:each) { after_run = true } after(:each) { raise "FOO" } example('example') { expect(1).to eq(1) } @@ -296,7 +296,7 @@ def assert(val) end it "stores the exception" do - group = RSpec::Core::ExampleGroup.describe + group = RSpec.describe group.after(:each) { raise "FOO" } example = group.example('example') { expect(1).to eq(1) } @@ -308,7 +308,7 @@ def assert(val) it "wraps before/after(:each) inside around" do results = [] - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do around(:each) do |e| results << "around (before)" e.run @@ -331,7 +331,7 @@ def assert(val) context "clearing ivars" do it "sets ivars to nil to prep them for GC" do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do before(:all) { @before_all = :before_all } before(:each) { @before_each = :before_each } after(:each) { @after_each = :after_each } @@ -350,7 +350,7 @@ def assert(val) end it "does not impact the before_all_ivars which are copied to each example" do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do before(:all) { @before_all = "abc" } example("first") { expect(@before_all).not_to be_nil } example("second") { expect(@before_all).not_to be_nil } @@ -371,7 +371,7 @@ def run_and_capture_reported_message(group) end it "prints any around hook errors rather than silencing them" do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do around(:each) { |e| e.run; raise "around" } example("e") { raise "example" } end @@ -381,7 +381,7 @@ def run_and_capture_reported_message(group) end it "prints any after hook errors rather than silencing them" do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do after(:each) { raise "after" } example("e") { raise "example" } end @@ -391,7 +391,7 @@ def run_and_capture_reported_message(group) end it "does not print mock expectation errors" do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do example do foo = double expect(foo).to receive(:bar) @@ -410,7 +410,7 @@ def run_and_capture_reported_message(group) exception = StandardError.new exception.set_backtrace([]) - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do example { raise exception.freeze } end group.run @@ -434,7 +434,7 @@ def run_and_capture_reported_message(group) c.around(:each) { |ex| executed << :around_each_config; ex.run } end - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do before(:all) { executed << :before_all } before(:each) { executed << :before_each } after(:all) { executed << :after_all } @@ -469,7 +469,7 @@ def expect_pending_result(example) context "in the example" do it "sets the example to pending" do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do example { pending; fail } end group.run @@ -478,7 +478,7 @@ def expect_pending_result(example) it "allows post-example processing in around hooks (see https://github.com/rspec/rspec-core/issues/322)" do blah = nil - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do around do |example| example.run blah = :success @@ -492,7 +492,7 @@ def expect_pending_result(example) it 'sets the backtrace to the example definition so it can be located by the user' do file = RSpec::Core::Metadata.relative_path(__FILE__) expected = [file, __LINE__ + 2].map(&:to_s) - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do example { pending } @@ -506,7 +506,7 @@ def expect_pending_result(example) context "in before(:each)" do it "sets each example to pending" do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do before(:each) { pending } example { fail } example { fail } @@ -517,7 +517,7 @@ def expect_pending_result(example) end it 'sets example to pending when failure occurs in before(:each)' do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do before(:each) { pending; fail } example {} end @@ -528,7 +528,7 @@ def expect_pending_result(example) context "in before(:all)" do it "is forbidden" do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do before(:all) { pending } example { fail } example { fail } @@ -540,7 +540,7 @@ def expect_pending_result(example) end it "fails with an ArgumentError if a block is provided" do - group = RSpec::Core::ExampleGroup.describe('group') do + group = RSpec.describe('group') do before(:all) do pending { :no_op } end @@ -557,7 +557,7 @@ def expect_pending_result(example) context "in around(:each)" do it "sets the example to pending" do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do around(:each) { pending } example { fail } end @@ -566,7 +566,7 @@ def expect_pending_result(example) end it 'sets example to pending when failure occurs in around(:each)' do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do around(:each) { pending; fail } example {} end @@ -577,7 +577,7 @@ def expect_pending_result(example) context "in after(:each)" do it "sets each example to pending" do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do after(:each) { pending; fail } example { } example { } @@ -593,7 +593,7 @@ def expect_pending_result(example) describe "#skip" do context "in the example" do it "sets the example to skipped" do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do example { skip } end group.run @@ -602,7 +602,7 @@ def expect_pending_result(example) it "allows post-example processing in around hooks (see https://github.com/rspec/rspec-core/issues/322)" do blah = nil - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do around do |example| example.run blah = :success @@ -615,7 +615,7 @@ def expect_pending_result(example) context "with a message" do it "sets the example to skipped with the provided message" do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do example { skip "lorem ipsum" } end group.run @@ -626,7 +626,7 @@ def expect_pending_result(example) context "in before(:each)" do it "sets each example to skipped" do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do before(:each) { skip } example {} example {} @@ -639,7 +639,7 @@ def expect_pending_result(example) context "in before(:all)" do it "sets each example to pending" do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do before(:all) { skip("not done"); fail } example {} example {} @@ -652,7 +652,7 @@ def expect_pending_result(example) context "in around(:each)" do it "sets the example to skipped" do - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do around(:each) { skip } example {} end @@ -665,7 +665,7 @@ def expect_pending_result(example) describe "timing" do it "uses RSpec::Core::Time as to not be affected by changes to time in examples" do reporter = double(:reporter).as_null_object - group = RSpec::Core::ExampleGroup.describe + group = RSpec.describe example = group.example example.__send__ :start, reporter allow(Time).to receive_messages(:now => Time.utc(2012, 10, 1)) @@ -679,7 +679,7 @@ def expect_pending_result(example) RSpec.configuration.order = :random - RSpec::Core::ExampleGroup.describe do + RSpec.describe do # The bug was only triggered when the examples # were in nested contexts; see https://github.com/rspec/rspec-core/pull/837 context { example { values << rand } } @@ -698,7 +698,7 @@ def expect_pending_result(example) describe "setting the current example" do it "sets RSpec.current_example to the example that is currently running" do - group = RSpec::Core::ExampleGroup.describe("an example group") + group = RSpec.describe("an example group") current_examples = [] example1 = group.example("example 1") { current_examples << RSpec.current_example } diff --git a/spec/rspec/core/failed_example_notification_spec.rb b/spec/rspec/core/failed_example_notification_spec.rb index cf99646b3a..e5fe900325 100644 --- a/spec/rspec/core/failed_example_notification_spec.rb +++ b/spec/rspec/core/failed_example_notification_spec.rb @@ -8,7 +8,7 @@ module RSpec::Core::Notifications it "uses the default color for the shared example backtrace line" do example = nil - group = RSpec::Core::ExampleGroup.describe "testing" do + group = RSpec.describe "testing" do shared_examples_for "a" do example = it "fails" do expect(1).to eq(2) diff --git a/spec/rspec/core/formatters/base_text_formatter_spec.rb b/spec/rspec/core/formatters/base_text_formatter_spec.rb index 51dbddc755..1d325484fb 100644 --- a/spec/rspec/core/formatters/base_text_formatter_spec.rb +++ b/spec/rspec/core/formatters/base_text_formatter_spec.rb @@ -32,7 +32,7 @@ end it "includes command to re-run each failed example" do - group = RSpec::Core::ExampleGroup.describe("example group") do + group = RSpec.describe("example group") do it("fails") { fail } end line = __LINE__ - 2 @@ -44,7 +44,7 @@ end describe "#dump_failures" do - let(:group) { RSpec::Core::ExampleGroup.describe("group name") } + let(:group) { RSpec.describe("group name") } before { allow(RSpec.configuration).to receive(:color_enabled?) { false } } diff --git a/spec/rspec/core/formatters/documentation_formatter_spec.rb b/spec/rspec/core/formatters/documentation_formatter_spec.rb index ff23c886e2..a15e6ac9a4 100644 --- a/spec/rspec/core/formatters/documentation_formatter_spec.rb +++ b/spec/rspec/core/formatters/documentation_formatter_spec.rb @@ -31,7 +31,7 @@ def execution_result(values) end it "represents nested group using hierarchy tree" do - group = RSpec::Core::ExampleGroup.describe("root") + group = RSpec.describe("root") context1 = group.describe("context 1") context1.example("nested example 1.1"){} context1.example("nested example 1.2"){} @@ -61,7 +61,7 @@ def execution_result(values) end it "strips whitespace for each row" do - group = RSpec::Core::ExampleGroup.describe(" root ") + group = RSpec.describe(" root ") context1 = group.describe(" nested ") context1.example(" example 1 ") {} context1.example(" example 2 ", :pending => true){ fail } diff --git a/spec/rspec/core/formatters/json_formatter_spec.rb b/spec/rspec/core/formatters/json_formatter_spec.rb index 3aeaa7d237..ab0de1d9d0 100644 --- a/spec/rspec/core/formatters/json_formatter_spec.rb +++ b/spec/rspec/core/formatters/json_formatter_spec.rb @@ -15,7 +15,7 @@ include FormatterSupport it "outputs json (brittle high level functional test)" do - group = RSpec::Core::ExampleGroup.describe("one apiece") do + group = RSpec.describe("one apiece") do it("succeeds") { expect(1).to eq 1 } it("fails") { fail "eek" } it("pends") { pending "world peace"; fail "eek" } @@ -128,7 +128,7 @@ def profile *groups context "with one example group" do before do - profile( RSpec::Core::ExampleGroup.describe("group") do + profile( RSpec.describe("group") do example("example") { } end) end @@ -154,13 +154,13 @@ def profile *groups before do example_clock = class_double(RSpec::Core::Time, :now => RSpec::Core::Time.now + 0.5) - group1 = RSpec::Core::ExampleGroup.describe("slow group") do + group1 = RSpec.describe("slow group") do example("example") do |example| # make it look slow without actually taking up precious time example.clock = example_clock end end - group2 = RSpec::Core::ExampleGroup.describe("fast group") do + group2 = RSpec.describe("fast group") do example("example 1") { } example("example 2") { } end diff --git a/spec/rspec/core/formatters/profile_formatter_spec.rb b/spec/rspec/core/formatters/profile_formatter_spec.rb index dd31448e12..49544760c0 100644 --- a/spec/rspec/core/formatters/profile_formatter_spec.rb +++ b/spec/rspec/core/formatters/profile_formatter_spec.rb @@ -41,7 +41,7 @@ def profile *groups before do example_clock = class_double(RSpec::Core::Time, :now => RSpec::Core::Time.now + 0.5) - profile(RSpec::Core::ExampleGroup.describe("group") do + profile(RSpec.describe("group") do example("example") do |example| # make it look slow without actually taking up precious time example.clock = example_clock @@ -57,14 +57,14 @@ def profile *groups before do example_clock = class_double(RSpec::Core::Time, :now => RSpec::Core::Time.now + 0.5) - group1 = RSpec::Core::ExampleGroup.describe("slow group") do + group1 = RSpec.describe("slow group") do example("example") do |example| # make it look slow without actually taking up precious time example.clock = example_clock end example_line_number = __LINE__ - 4 end - group2 = RSpec::Core::ExampleGroup.describe("fast group") do + group2 = RSpec.describe("fast group") do example("example 1") { } example("example 2") { } end @@ -86,7 +86,7 @@ def profile *groups it "depends on parent_groups to get the top level example group" do ex = nil - group = RSpec::Core::ExampleGroup.describe + group = RSpec.describe group.describe("group 2") do describe "group 3" do ex = example("nested example 1") diff --git a/spec/rspec/core/hooks_filtering_spec.rb b/spec/rspec/core/hooks_filtering_spec.rb index 2df8235e6d..192f8240c7 100644 --- a/spec/rspec/core/hooks_filtering_spec.rb +++ b/spec/rspec/core/hooks_filtering_spec.rb @@ -12,7 +12,7 @@ module RSpec::Core c.after(:each) { filters << "after each in config"} c.after(:all) { filters << "after all in config"} end - group = ExampleGroup.describe + group = RSpec.describe group.example("example") {} group.run expect(filters).to eq([ @@ -35,7 +35,7 @@ module RSpec::Core c.before(:match => true) { filters << "before each in config"} c.after(:match => true) { filters << "after each in config"} end - group = ExampleGroup.describe("group", :match => true) + group = RSpec.describe("group", :match => true) group.example("example") {} group.run expect(filters).to eq([ @@ -55,7 +55,7 @@ module RSpec::Core c.after(:each, :match => true) { filters << "after each in config"} c.after(:all, :match => true) { filters << "after all in config"} end - group = ExampleGroup.describe("group", :match => true) + group = RSpec.describe("group", :match => true) group.example("example") {} group.run expect(filters).to eq([ @@ -76,7 +76,7 @@ module RSpec::Core example_1_filters = example_2_filters = nil - group = ExampleGroup.describe "group" do + group = RSpec.describe "group" do it("example 1") { example_1_filters = filters.dup } describe "subgroup", :match => true do it("example 2") { example_2_filters = filters.dup } @@ -98,7 +98,7 @@ module RSpec::Core example_1_filters = example_2_filters = example_3_filters = nil - group = ExampleGroup.describe "group", :match => true do + group = RSpec.describe "group", :match => true do it("example 1") { example_1_filters = filters.dup } describe "subgroup", :match => true do it("example 2") { example_2_filters = filters.dup } @@ -125,7 +125,7 @@ module RSpec::Core c.after(:each, :match => false) { filters << "after each in config"} c.after(:all, :match => false) { filters << "after all in config"} end - group = ExampleGroup.describe(:match => true) + group = RSpec.describe(:match => true) group.example("example") {} group.run expect(filters).to eq([]) @@ -137,7 +137,7 @@ module RSpec::Core let(:group) do md = example_metadata - ExampleGroup.describe do + RSpec.describe do it("example", md) { } end end @@ -196,7 +196,7 @@ def filters c.after(:each, :one => 1, :two => 2, :three => 3) { filters << "after each in config"} c.after(:all, :one => 1, :three => 3) { filters << "after all in config"} end - group = ExampleGroup.describe("group", :one => 1, :two => 2, :three => 3) + group = RSpec.describe("group", :one => 1, :two => 2, :three => 3) group.example("example") {} group.run expect(filters).to eq([ diff --git a/spec/rspec/core/hooks_spec.rb b/spec/rspec/core/hooks_spec.rb index 8ad8a01501..0031ed9331 100644 --- a/spec/rspec/core/hooks_spec.rb +++ b/spec/rspec/core/hooks_spec.rb @@ -162,7 +162,7 @@ def transactionally; end context "when not running the example within the around block" do it "does not run the example" do examples = [] - group = ExampleGroup.describe do + group = RSpec.describe do around do end it "foo" do @@ -177,7 +177,7 @@ def transactionally; end context "when running the example within the around block" do it "runs the example" do examples = [] - group = ExampleGroup.describe do + group = RSpec.describe do around do |example| example.run end @@ -191,7 +191,7 @@ def transactionally; end it "exposes example metadata to each around hook" do foos = {} - group = ExampleGroup.describe do + group = RSpec.describe do around do |ex| foos[:first] = ex.metadata[:foo] ex.run @@ -213,7 +213,7 @@ def transactionally; end data_2 = {} ex = nil - group = ExampleGroup.describe do + group = RSpec.describe do def self.data_from(ex) { :description => ex.description, @@ -246,7 +246,7 @@ def self.data_from(ex) it "exposes a sensible inspect value" do inspect_value = nil - group = ExampleGroup.describe do + group = RSpec.describe do around do |ex| inspect_value = ex.inspect end @@ -263,7 +263,7 @@ def self.data_from(ex) context "when running the example within a block passed to a method" do it "runs the example" do examples = [] - group = ExampleGroup.describe do + group = RSpec.describe do def yielder yield end @@ -292,7 +292,7 @@ def yielder RSpec.configure { |config| config.before(scope) { messages << "config 4" } } RSpec.configure { |config| config.prepend_before(scope) { messages << "config 1" } } - group = ExampleGroup.describe { example {} } + group = RSpec.describe { example {} } group.before(scope) { messages << "group 3" } group.prepend_before(scope) { messages << "group 2" } group.before(scope) { messages << "group 4" } @@ -321,7 +321,7 @@ def yielder RSpec.configure { |config| config.append_before(scope) { messages << "config 2" } } RSpec.configure { |config| config.before(scope) { messages << "config 3" } } - group = ExampleGroup.describe { example {} } + group = RSpec.describe { example {} } group.before(scope) { messages << "group 1" } group.append_before(scope) { messages << "group 2" } group.before(scope) { messages << "group 3" } @@ -347,7 +347,7 @@ def yielder RSpec.configure { |config| config.prepend_after(scope) { messages << "config 2" } } RSpec.configure { |config| config.after(scope) { messages << "config 1" } } - group = ExampleGroup.describe { example {} } + group = RSpec.describe { example {} } group.after(scope) { messages << "group 3" } group.prepend_after(scope) { messages << "group 2" } group.after(scope) { messages << "group 1" } @@ -374,7 +374,7 @@ def yielder RSpec.configure { |config| config.after(scope) { messages << "config 1" } } RSpec.configure { |config| config.append_after(scope) { messages << "config 4" } } - group = ExampleGroup.describe { example {} } + group = RSpec.describe { example {} } group.after(scope) { messages << "group 2" } group.append_after(scope) { messages << "group 3" } group.after(scope) { messages << "group 1" } @@ -407,7 +407,7 @@ def yielder c.around(:each, &hook) end - group = ExampleGroup.describe { example { messages << "example" } } + group = RSpec.describe { example { messages << "example" } } group.run expect(messages).to eq ["hook 1", "hook 2", "example"] end diff --git a/spec/rspec/core/memoized_helpers_spec.rb b/spec/rspec/core/memoized_helpers_spec.rb index e4c8cf6a76..3713012319 100644 --- a/spec/rspec/core/memoized_helpers_spec.rb +++ b/spec/rspec/core/memoized_helpers_spec.rb @@ -5,7 +5,7 @@ module RSpec::Core before(:each) { RSpec.configuration.configure_expectation_framework } def subject_value_for(describe_arg, &block) - group = ExampleGroup.describe(describe_arg, &block) + group = RSpec.describe(describe_arg, &block) subject_value = nil group.example { subject_value = subject } group.run @@ -70,7 +70,7 @@ def subject_value_for(describe_arg, &block) it "can be overriden and super'd to from a nested group" do outer_subject_value = inner_subject_value = nil - ExampleGroup.describe(Array) do + RSpec.describe(Array) do subject { super() << :parent_group } example { outer_subject_value = subject } @@ -90,7 +90,7 @@ def subject_value_for(describe_arg, &block) example_yielded_to_subject = nil example_yielded_to_example = nil - group = ExampleGroup.describe + group = RSpec.describe group.subject { |e| example_yielded_to_subject = e } group.example { |e| subject; example_yielded_to_example = e } group.run @@ -113,7 +113,7 @@ def working_with?(double) [false, nil].each do |falsy_value| context "with a value of #{falsy_value.inspect}" do it "is evaluated once per example" do - group = ExampleGroup.describe(Array) + group = RSpec.describe(Array) group.before do expect(Object).to receive(:this_question?).once.and_return(falsy_value) end @@ -140,7 +140,7 @@ def working_with?(double) describe "defined in a top level group" do let(:group) do - ExampleGroup.describe do + RSpec.describe do subject{ [4, 5, 6] } end end @@ -180,7 +180,7 @@ def working_with?(double) result = nil line = nil - ExampleGroup.describe do + RSpec.describe do subject { nil } send(hook, :all) { result = (subject rescue $!) }; line = __LINE__ example { } @@ -197,7 +197,7 @@ def working_with?(double) example_yielded_to_subject = nil example_yielded_to_example = nil - group = ExampleGroup.describe + group = RSpec.describe group.subject(:foo) { |e| example_yielded_to_subject = e } group.example { |e| foo; example_yielded_to_example = e } group.run @@ -208,7 +208,7 @@ def working_with?(double) it "defines a method that returns the memoized subject" do list_value_1 = list_value_2 = subject_value_1 = subject_value_2 = nil - ExampleGroup.describe do + RSpec.describe do subject(:list) { [1, 2, 3] } example do list_value_1 = list @@ -228,7 +228,7 @@ def working_with?(double) it "is referred from inside subject by the name" do inner_subject_value = nil - ExampleGroup.describe do + RSpec.describe do subject(:list) { [1, 2, 3] } describe 'first' do subject(:first_element) { list.first } @@ -242,7 +242,7 @@ def working_with?(double) it 'can continue to be referenced by the name even when an inner group redefines the subject' do named_value = nil - ExampleGroup.describe do + RSpec.describe do subject(:named) { :outer } describe "inner" do @@ -260,7 +260,7 @@ def working_with?(double) it 'can continue to reference an inner subject after the outer subject name is referenced' do subject_value = nil - ExampleGroup.describe do + RSpec.describe do subject(:named) { :outer } describe "inner" do @@ -278,7 +278,7 @@ def working_with?(double) it 'is not overriden when an inner group defines a new method with the same name' do subject_value = nil - ExampleGroup.describe do + RSpec.describe do subject(:named) { :outer_subject } describe "inner" do @@ -294,7 +294,7 @@ def working_with?(double) def should_raise_not_supported_error(&block) ex = nil - ExampleGroup.describe do + RSpec.describe do let(:list) { ["a", "b", "c"] } subject { [1, 2, 3] } @@ -330,7 +330,7 @@ def should_raise_not_supported_error(&block) context "using 'self' as an explicit subject" do it "delegates matcher to the ExampleGroup" do - group = ExampleGroup.describe("group") do + group = RSpec.describe("group") do subject { self } def ok?; true; end def not_ok?; false; end @@ -344,7 +344,7 @@ def not_ok?; false; end end it 'supports a new expect-based syntax' do - group = ExampleGroup.describe([1, 2, 3]) do + group = RSpec.describe([1, 2, 3]) do it { is_expected.to be_an Array } it { is_expected.not_to include 4 } end @@ -419,7 +419,7 @@ def count it 'raises a useful error when called without a block' do expect do - ExampleGroup.describe { let(:list) } + RSpec.describe { let(:list) } end.to raise_error(/#let or #subject called without a block/) end @@ -455,7 +455,7 @@ def count result = nil line = nil - ExampleGroup.describe do + RSpec.describe do let(:foo) { nil } send(hook, :all) { result = (foo rescue $!) }; line = __LINE__ example { } @@ -468,7 +468,7 @@ def count context "when included modules have hooks that define memoized helpers" do it "allows memoized helpers to override methods in previously included modules" do - group = ExampleGroup.describe do + group = RSpec.describe do include Module.new { def self.included(m); m.let(:unrelated) { :unrelated }; end } diff --git a/spec/rspec/core/pending_example_spec.rb b/spec/rspec/core/pending_example_spec.rb index 99c19e27b1..f306205df3 100644 --- a/spec/rspec/core/pending_example_spec.rb +++ b/spec/rspec/core/pending_example_spec.rb @@ -3,7 +3,7 @@ RSpec.describe "an example" do context "declared pending with metadata" do it "uses the value assigned to :pending as the message" do - group = RSpec::Core::ExampleGroup.describe('group') do + group = RSpec.describe('group') do example "example", :pending => 'just because' do fail end @@ -14,7 +14,7 @@ end it "sets the message to 'No reason given' if :pending => true" do - group = RSpec::Core::ExampleGroup.describe('group') do + group = RSpec.describe('group') do example "example", :pending => true do fail end @@ -25,7 +25,7 @@ end it "passes if a mock expectation is not satisifed" do - group = RSpec::Core::ExampleGroup.describe('group') do + group = RSpec.describe('group') do example "example", :pending => "because" do expect(RSpec).to receive(:a_message_in_a_bottle) end @@ -38,7 +38,7 @@ end it "does not mutate the :pending attribute of the user metadata when handling mock expectation errors" do - group = RSpec::Core::ExampleGroup.describe('group') do + group = RSpec.describe('group') do example "example", :pending => "because" do expect(RSpec).to receive(:a_message_in_a_bottle) end @@ -86,7 +86,7 @@ context "with no block" do it "is listed as pending with 'Not yet implemented'" do - group = RSpec::Core::ExampleGroup.describe('group') do + group = RSpec.describe('group') do it "has no block" end example = group.examples.first @@ -97,7 +97,7 @@ context "with no args" do it "is listed as pending with the default message" do - group = RSpec::Core::ExampleGroup.describe('group') do + group = RSpec.describe('group') do it "does something" do pending fail @@ -110,7 +110,7 @@ it "fails when the rest of the example passes" do called = false - group = RSpec::Core::ExampleGroup.describe('group') do + group = RSpec.describe('group') do it "does something" do pending called = true @@ -126,7 +126,7 @@ end it "does not mutate the :pending attribute of the user metadata when the rest of the example passes" do - group = RSpec::Core::ExampleGroup.describe('group') do + group = RSpec.describe('group') do it "does something" do pending end @@ -141,7 +141,7 @@ context "with no docstring" do context "declared with the pending method" do it "has an auto-generated description" do - group = RSpec::Core::ExampleGroup.describe('group') do + group = RSpec.describe('group') do it "checks something" do expect((3+4)).to eq(7) end @@ -157,7 +157,7 @@ context "after another example with some assertion" do it "does not show any message" do - group = RSpec::Core::ExampleGroup.describe('group') do + group = RSpec.describe('group') do it "checks something" do expect((3+4)).to eq(7) end @@ -174,7 +174,7 @@ context "with a message" do it "is listed as pending with the supplied message" do - group = RSpec::Core::ExampleGroup.describe('group') do + group = RSpec.describe('group') do it "does something" do pending("just because") fail @@ -188,7 +188,7 @@ context "with a block" do it "fails with an ArgumentError stating the syntax is deprecated" do - group = RSpec::Core::ExampleGroup.describe('group') do + group = RSpec.describe('group') do it "calls pending with a block" do pending("with invalid syntax") do :no_op @@ -206,7 +206,7 @@ it "does not yield to the block" do example_to_have_yielded = :did_not_yield - group = RSpec::Core::ExampleGroup.describe('group') do + group = RSpec.describe('group') do it "calls pending with a block" do pending("just because") do example_to_have_yielded = :pending_block diff --git a/spec/rspec/core/reporter_spec.rb b/spec/rspec/core/reporter_spec.rb index fed725da10..4f535e117e 100644 --- a/spec/rspec/core/reporter_spec.rb +++ b/spec/rspec/core/reporter_spec.rb @@ -82,7 +82,7 @@ module RSpec::Core reporter.register_listener formatter, :example_group_started, :example_group_finished - group = ExampleGroup.describe("root") + group = RSpec.describe("root") group.describe("context 1") do example("ignore") {} end @@ -111,7 +111,7 @@ module RSpec::Core reporter.register_listener formatter, :example_group_started, :example_group_finished - group = ExampleGroup.describe("root") + group = RSpec.describe("root") group.run(reporter) end diff --git a/spec/rspec/core/shared_context_spec.rb b/spec/rspec/core/shared_context_spec.rb index 6bf0c78225..1f8f832e10 100644 --- a/spec/rspec/core/shared_context_spec.rb +++ b/spec/rspec/core/shared_context_spec.rb @@ -21,7 +21,7 @@ after(:each) { after_each_hook = true } after(:all) { after_all_hook = true } end - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do include shared example { } end @@ -74,7 +74,7 @@ subject { 17 } end - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do include shared end @@ -89,7 +89,7 @@ example {} end end - group = RSpec::Core::ExampleGroup.describe do + group = RSpec.describe do include shared end diff --git a/spec/rspec/core/shared_example_group_spec.rb b/spec/rspec/core/shared_example_group_spec.rb index 35574ce5e0..7b410f46d7 100644 --- a/spec/rspec/core/shared_example_group_spec.rb +++ b/spec/rspec/core/shared_example_group_spec.rb @@ -39,7 +39,7 @@ module Core %w[shared_examples shared_examples_for shared_context].each do |shared_method_name| describe shared_method_name do - let(:group) { ExampleGroup.describe('example group') } + let(:group) { RSpec.describe('example group') } define_method :define_shared_group do |*args, &block| group.send(shared_method_name, *args, &block) @@ -71,7 +71,7 @@ module Core it 'works with top level defined examples in modules' do expect(RSpec::configuration.reporter).to_not receive(:deprecation) - ExampleGroup.describe('example group') { include_context 'top level in module' } + RSpec.describe('example group') { include_context 'top level in module' } end it 'generates a named (rather than anonymous) module' do diff --git a/spec/rspec/core/world_spec.rb b/spec/rspec/core/world_spec.rb index 330418d624..0c5515c121 100644 --- a/spec/rspec/core/world_spec.rb +++ b/spec/rspec/core/world_spec.rb @@ -19,7 +19,7 @@ module RSpec::Core describe "#example_groups" do it "contains all registered example groups" do - group = RSpec::Core::ExampleGroup.describe("group"){} + group = RSpec.describe("group"){} world.register(group) expect(world.example_groups).to include(group) end @@ -27,7 +27,7 @@ module RSpec::Core describe "#preceding_declaration_line (again)" do let(:group) do - RSpec::Core::ExampleGroup.describe("group") do + RSpec.describe("group") do example("example") {} @@ -35,7 +35,7 @@ module RSpec::Core end let(:second_group) do - RSpec::Core::ExampleGroup.describe("second_group") do + RSpec.describe("second_group") do example("second_example") {} diff --git a/spec/rspec/core_spec.rb b/spec/rspec/core_spec.rb index 9a114e3ab6..58bfa0ccd2 100644 --- a/spec/rspec/core_spec.rb +++ b/spec/rspec/core_spec.rb @@ -68,7 +68,7 @@ describe ".current_example" do it "sets the example being executed" do - group = RSpec::Core::ExampleGroup.describe("an example group") + group = RSpec.describe("an example group") example = group.example("an example") RSpec.current_example = example From 2f51178af0476c590e8efcf9f3757868ebaf3f27 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 15 Dec 2014 15:49:43 -0800 Subject: [PATCH 128/275] Require spec_helper only once from .rspec. --- .rspec | 1 + spec/command_line/order_spec.rb | 2 -- spec/rspec/core/backtrace_formatter_spec.rb | 2 -- spec/rspec/core/configuration_options_spec.rb | 1 - spec/rspec/core/configuration_spec.rb | 1 - spec/rspec/core/drb_spec.rb | 1 - spec/rspec/core/dsl_spec.rb | 1 - spec/rspec/core/example_execution_result_spec.rb | 2 -- spec/rspec/core/example_group_constants_spec.rb | 1 - spec/rspec/core/example_group_spec.rb | 1 - spec/rspec/core/example_spec.rb | 1 - spec/rspec/core/failed_example_notification_spec.rb | 2 -- spec/rspec/core/filter_manager_spec.rb | 2 -- spec/rspec/core/formatters/base_text_formatter_spec.rb | 1 - spec/rspec/core/formatters/console_codes_spec.rb | 1 - spec/rspec/core/formatters/deprecation_formatter_spec.rb | 1 - spec/rspec/core/formatters/documentation_formatter_spec.rb | 1 - spec/rspec/core/formatters/helpers_spec.rb | 1 - spec/rspec/core/formatters/html_formatter_spec.rb | 1 - spec/rspec/core/formatters/json_formatter_spec.rb | 1 - spec/rspec/core/formatters/profile_formatter_spec.rb | 1 - spec/rspec/core/formatters/progress_formatter_spec.rb | 1 - spec/rspec/core/formatters/snippet_extractor_spec.rb | 1 - spec/rspec/core/formatters_spec.rb | 1 - spec/rspec/core/hooks_filtering_spec.rb | 2 -- spec/rspec/core/hooks_spec.rb | 2 -- spec/rspec/core/memoized_helpers_spec.rb | 2 -- spec/rspec/core/metadata_filter_spec.rb | 2 -- spec/rspec/core/metadata_spec.rb | 2 -- spec/rspec/core/notifications_spec.rb | 1 - spec/rspec/core/option_parser_spec.rb | 2 -- spec/rspec/core/ordering_spec.rb | 2 -- spec/rspec/core/pending_example_spec.rb | 2 -- spec/rspec/core/pending_spec.rb | 2 -- spec/rspec/core/project_initializer_spec.rb | 1 - spec/rspec/core/rake_task_spec.rb | 1 - spec/rspec/core/random_spec.rb | 2 -- spec/rspec/core/reporter_spec.rb | 2 -- spec/rspec/core/rspec_matchers_spec.rb | 2 -- spec/rspec/core/ruby_project_spec.rb | 2 -- spec/rspec/core/runner_spec.rb | 1 - spec/rspec/core/shared_context_spec.rb | 2 -- spec/rspec/core/shared_example_group_spec.rb | 1 - spec/rspec/core/suite_hooks_spec.rb | 1 - spec/rspec/core/warnings_spec.rb | 2 -- spec/rspec/core/world_spec.rb | 2 -- spec/rspec/core_spec.rb | 1 - 47 files changed, 1 insertion(+), 67 deletions(-) diff --git a/.rspec b/.rspec index 32fe547d9d..eb8599d92e 100644 --- a/.rspec +++ b/.rspec @@ -1,2 +1,3 @@ --order rand --warnings +--require spec_helper diff --git a/spec/command_line/order_spec.rb b/spec/command_line/order_spec.rb index 741705086c..681704b596 100644 --- a/spec/command_line/order_spec.rb +++ b/spec/command_line/order_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - RSpec.describe 'command line', :ui, :slow do let(:stderr) { StringIO.new } let(:stdout) { StringIO.new } diff --git a/spec/rspec/core/backtrace_formatter_spec.rb b/spec/rspec/core/backtrace_formatter_spec.rb index 39e1273282..972eaca15b 100644 --- a/spec/rspec/core/backtrace_formatter_spec.rb +++ b/spec/rspec/core/backtrace_formatter_spec.rb @@ -1,5 +1,3 @@ -require "spec_helper" - module RSpec::Core RSpec.describe BacktraceFormatter do def make_backtrace_formatter(exclusion_patterns=nil, inclusion_patterns=nil) diff --git a/spec/rspec/core/configuration_options_spec.rb b/spec/rspec/core/configuration_options_spec.rb index 25855672d3..6367b1dfb1 100644 --- a/spec/rspec/core/configuration_options_spec.rb +++ b/spec/rspec/core/configuration_options_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'ostruct' require 'rspec/core/drb' diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index 3b6a7991fb..f913b020d0 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'tmpdir' require 'rspec/support/spec/in_sub_process' diff --git a/spec/rspec/core/drb_spec.rb b/spec/rspec/core/drb_spec.rb index 46350bdb6f..505294d565 100644 --- a/spec/rspec/core/drb_spec.rb +++ b/spec/rspec/core/drb_spec.rb @@ -1,4 +1,3 @@ -require "spec_helper" require 'rspec/core/drb' RSpec.describe RSpec::Core::DRbRunner, :isolated_directory => true, :isolated_home => true, :type => :drb, :unless => RUBY_PLATFORM == 'java' do diff --git a/spec/rspec/core/dsl_spec.rb b/spec/rspec/core/dsl_spec.rb index b22a8ff7a5..df820cd860 100644 --- a/spec/rspec/core/dsl_spec.rb +++ b/spec/rspec/core/dsl_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'rspec/support/spec/in_sub_process' main = self diff --git a/spec/rspec/core/example_execution_result_spec.rb b/spec/rspec/core/example_execution_result_spec.rb index 148b212810..da60e6240d 100644 --- a/spec/rspec/core/example_execution_result_spec.rb +++ b/spec/rspec/core/example_execution_result_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Core class Example diff --git a/spec/rspec/core/example_group_constants_spec.rb b/spec/rspec/core/example_group_constants_spec.rb index 0519b05733..ebeefb79a8 100644 --- a/spec/rspec/core/example_group_constants_spec.rb +++ b/spec/rspec/core/example_group_constants_spec.rb @@ -1,5 +1,4 @@ # encoding: utf-8 -require 'spec_helper' RSpec.describe "::RSpec::Core::ExampleGroup" do context "does not cause problems when users reference a top level constant of the same name" do diff --git a/spec/rspec/core/example_group_spec.rb b/spec/rspec/core/example_group_spec.rb index bae30ad556..4a5dc8d9d8 100644 --- a/spec/rspec/core/example_group_spec.rb +++ b/spec/rspec/core/example_group_spec.rb @@ -1,5 +1,4 @@ # encoding: utf-8 -require 'spec_helper' module RSpec::Core RSpec.describe ExampleGroup do diff --git a/spec/rspec/core/example_spec.rb b/spec/rspec/core/example_spec.rb index fe842de0a7..e8fb371dfe 100644 --- a/spec/rspec/core/example_spec.rb +++ b/spec/rspec/core/example_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'pp' require 'stringio' diff --git a/spec/rspec/core/failed_example_notification_spec.rb b/spec/rspec/core/failed_example_notification_spec.rb index e5fe900325..6fc3ecf2b6 100644 --- a/spec/rspec/core/failed_example_notification_spec.rb +++ b/spec/rspec/core/failed_example_notification_spec.rb @@ -1,5 +1,3 @@ -require "spec_helper" - module RSpec::Core::Notifications describe FailedExampleNotification do before do diff --git a/spec/rspec/core/filter_manager_spec.rb b/spec/rspec/core/filter_manager_spec.rb index 2dfb7f6c33..9f5751392a 100644 --- a/spec/rspec/core/filter_manager_spec.rb +++ b/spec/rspec/core/filter_manager_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec::Core RSpec.describe FilterManager do def opposite(name) diff --git a/spec/rspec/core/formatters/base_text_formatter_spec.rb b/spec/rspec/core/formatters/base_text_formatter_spec.rb index 1d325484fb..a64eceba71 100644 --- a/spec/rspec/core/formatters/base_text_formatter_spec.rb +++ b/spec/rspec/core/formatters/base_text_formatter_spec.rb @@ -1,5 +1,4 @@ # encoding: utf-8 -require 'spec_helper' require 'rspec/core/formatters/base_text_formatter' RSpec.describe RSpec::Core::Formatters::BaseTextFormatter do diff --git a/spec/rspec/core/formatters/console_codes_spec.rb b/spec/rspec/core/formatters/console_codes_spec.rb index af5edf77eb..04a9c7d830 100644 --- a/spec/rspec/core/formatters/console_codes_spec.rb +++ b/spec/rspec/core/formatters/console_codes_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'rspec/core/formatters/console_codes' RSpec.describe "RSpec::Core::Formatters::ConsoleCodes" do diff --git a/spec/rspec/core/formatters/deprecation_formatter_spec.rb b/spec/rspec/core/formatters/deprecation_formatter_spec.rb index a6503e5f8e..122eb510a0 100644 --- a/spec/rspec/core/formatters/deprecation_formatter_spec.rb +++ b/spec/rspec/core/formatters/deprecation_formatter_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'rspec/core/reporter' require 'rspec/core/formatters/deprecation_formatter' require 'tempfile' diff --git a/spec/rspec/core/formatters/documentation_formatter_spec.rb b/spec/rspec/core/formatters/documentation_formatter_spec.rb index a15e6ac9a4..aa4b5c3c3e 100644 --- a/spec/rspec/core/formatters/documentation_formatter_spec.rb +++ b/spec/rspec/core/formatters/documentation_formatter_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'rspec/core/formatters/documentation_formatter' module RSpec::Core::Formatters diff --git a/spec/rspec/core/formatters/helpers_spec.rb b/spec/rspec/core/formatters/helpers_spec.rb index a49ee2fff6..6fe50854b0 100644 --- a/spec/rspec/core/formatters/helpers_spec.rb +++ b/spec/rspec/core/formatters/helpers_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'rspec/core/formatters/helpers' RSpec.describe RSpec::Core::Formatters::Helpers do diff --git a/spec/rspec/core/formatters/html_formatter_spec.rb b/spec/rspec/core/formatters/html_formatter_spec.rb index 830dcb6715..255c29eb88 100644 --- a/spec/rspec/core/formatters/html_formatter_spec.rb +++ b/spec/rspec/core/formatters/html_formatter_spec.rb @@ -1,5 +1,4 @@ # encoding: utf-8 -require 'spec_helper' require 'rspec/core/formatters/html_formatter' # For some reason we get load errors when loading nokogiri on AppVeyor diff --git a/spec/rspec/core/formatters/json_formatter_spec.rb b/spec/rspec/core/formatters/json_formatter_spec.rb index ab0de1d9d0..4e42215be1 100644 --- a/spec/rspec/core/formatters/json_formatter_spec.rb +++ b/spec/rspec/core/formatters/json_formatter_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'rspec/core/formatters/json_formatter' require 'json' require 'rspec/core/reporter' diff --git a/spec/rspec/core/formatters/profile_formatter_spec.rb b/spec/rspec/core/formatters/profile_formatter_spec.rb index 49544760c0..ee53ce3b42 100644 --- a/spec/rspec/core/formatters/profile_formatter_spec.rb +++ b/spec/rspec/core/formatters/profile_formatter_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'rspec/core/formatters/profile_formatter' RSpec.describe RSpec::Core::Formatters::ProfileFormatter do diff --git a/spec/rspec/core/formatters/progress_formatter_spec.rb b/spec/rspec/core/formatters/progress_formatter_spec.rb index bf143aaad9..a92a9fdb0f 100644 --- a/spec/rspec/core/formatters/progress_formatter_spec.rb +++ b/spec/rspec/core/formatters/progress_formatter_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'rspec/core/formatters/progress_formatter' RSpec.describe RSpec::Core::Formatters::ProgressFormatter do diff --git a/spec/rspec/core/formatters/snippet_extractor_spec.rb b/spec/rspec/core/formatters/snippet_extractor_spec.rb index 341007bb2f..5227e2532f 100644 --- a/spec/rspec/core/formatters/snippet_extractor_spec.rb +++ b/spec/rspec/core/formatters/snippet_extractor_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'rspec/core/formatters/snippet_extractor' module RSpec diff --git a/spec/rspec/core/formatters_spec.rb b/spec/rspec/core/formatters_spec.rb index 57423e37b7..e9bc04f54c 100644 --- a/spec/rspec/core/formatters_spec.rb +++ b/spec/rspec/core/formatters_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'pathname' module RSpec::Core::Formatters diff --git a/spec/rspec/core/hooks_filtering_spec.rb b/spec/rspec/core/hooks_filtering_spec.rb index 192f8240c7..cce42897ec 100644 --- a/spec/rspec/core/hooks_filtering_spec.rb +++ b/spec/rspec/core/hooks_filtering_spec.rb @@ -1,5 +1,3 @@ -require "spec_helper" - module RSpec::Core RSpec.describe "config block hook filtering" do describe "unfiltered hooks" do diff --git a/spec/rspec/core/hooks_spec.rb b/spec/rspec/core/hooks_spec.rb index 0031ed9331..3766cfc58e 100644 --- a/spec/rspec/core/hooks_spec.rb +++ b/spec/rspec/core/hooks_spec.rb @@ -1,5 +1,3 @@ -require "spec_helper" - module RSpec::Core RSpec.describe Hooks do class HooksHost diff --git a/spec/rspec/core/memoized_helpers_spec.rb b/spec/rspec/core/memoized_helpers_spec.rb index 3713012319..981debc309 100644 --- a/spec/rspec/core/memoized_helpers_spec.rb +++ b/spec/rspec/core/memoized_helpers_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec::Core RSpec.describe MemoizedHelpers do before(:each) { RSpec.configuration.configure_expectation_framework } diff --git a/spec/rspec/core/metadata_filter_spec.rb b/spec/rspec/core/metadata_filter_spec.rb index 35b16183c2..44bc62fbd9 100644 --- a/spec/rspec/core/metadata_filter_spec.rb +++ b/spec/rspec/core/metadata_filter_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Core RSpec.describe MetadataFilter do diff --git a/spec/rspec/core/metadata_spec.rb b/spec/rspec/core/metadata_spec.rb index 326180acbf..7b7fe8fe4b 100644 --- a/spec/rspec/core/metadata_spec.rb +++ b/spec/rspec/core/metadata_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Core RSpec.describe Metadata do diff --git a/spec/rspec/core/notifications_spec.rb b/spec/rspec/core/notifications_spec.rb index 47f0ac93b7..0430c5d42c 100644 --- a/spec/rspec/core/notifications_spec.rb +++ b/spec/rspec/core/notifications_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'rspec/core/notifications' RSpec.describe "FailedExampleNotification" do diff --git a/spec/rspec/core/option_parser_spec.rb b/spec/rspec/core/option_parser_spec.rb index feee8c1010..738b621db9 100644 --- a/spec/rspec/core/option_parser_spec.rb +++ b/spec/rspec/core/option_parser_spec.rb @@ -1,5 +1,3 @@ -require "spec_helper" - module RSpec::Core RSpec.describe OptionParser do before do diff --git a/spec/rspec/core/ordering_spec.rb b/spec/rspec/core/ordering_spec.rb index 2a96328cc0..2cda655899 100644 --- a/spec/rspec/core/ordering_spec.rb +++ b/spec/rspec/core/ordering_spec.rb @@ -1,5 +1,3 @@ -require "spec_helper" - module RSpec module Core module Ordering diff --git a/spec/rspec/core/pending_example_spec.rb b/spec/rspec/core/pending_example_spec.rb index f306205df3..2688630ce4 100644 --- a/spec/rspec/core/pending_example_spec.rb +++ b/spec/rspec/core/pending_example_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - RSpec.describe "an example" do context "declared pending with metadata" do it "uses the value assigned to :pending as the message" do diff --git a/spec/rspec/core/pending_spec.rb b/spec/rspec/core/pending_spec.rb index 8556503e69..8fe1631e9c 100644 --- a/spec/rspec/core/pending_spec.rb +++ b/spec/rspec/core/pending_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - RSpec.describe RSpec::Core::Pending do it 'only defines methods that are part of the DSL' do expect(RSpec::Core::Pending.instance_methods(false).map(&:to_sym)).to \ diff --git a/spec/rspec/core/project_initializer_spec.rb b/spec/rspec/core/project_initializer_spec.rb index 24d7780e17..2ae9467bbd 100644 --- a/spec/rspec/core/project_initializer_spec.rb +++ b/spec/rspec/core/project_initializer_spec.rb @@ -1,4 +1,3 @@ -require "spec_helper" require 'rspec/core/project_initializer' module RSpec::Core diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index ad4c349665..6889fbd8ba 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -1,4 +1,3 @@ -require "spec_helper" require "rspec/core/rake_task" require 'tempfile' diff --git a/spec/rspec/core/random_spec.rb b/spec/rspec/core/random_spec.rb index 0875d4cec9..f1e8ea9c9a 100644 --- a/spec/rspec/core/random_spec.rb +++ b/spec/rspec/core/random_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Core RSpec.describe RandomNumberGenerator do diff --git a/spec/rspec/core/reporter_spec.rb b/spec/rspec/core/reporter_spec.rb index 4f535e117e..2b20eaa145 100644 --- a/spec/rspec/core/reporter_spec.rb +++ b/spec/rspec/core/reporter_spec.rb @@ -1,5 +1,3 @@ -require "spec_helper" - module RSpec::Core RSpec.describe Reporter do include FormatterSupport diff --git a/spec/rspec/core/rspec_matchers_spec.rb b/spec/rspec/core/rspec_matchers_spec.rb index 70e51b3c5d..564021637f 100644 --- a/spec/rspec/core/rspec_matchers_spec.rb +++ b/spec/rspec/core/rspec_matchers_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec::Matchers def __method_with_super super diff --git a/spec/rspec/core/ruby_project_spec.rb b/spec/rspec/core/ruby_project_spec.rb index 15ea777ca8..985a721231 100644 --- a/spec/rspec/core/ruby_project_spec.rb +++ b/spec/rspec/core/ruby_project_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module RSpec module Core RSpec.describe RubyProject do diff --git a/spec/rspec/core/runner_spec.rb b/spec/rspec/core/runner_spec.rb index 0197e457d1..82c917c9a4 100644 --- a/spec/rspec/core/runner_spec.rb +++ b/spec/rspec/core/runner_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'rspec/core/drb' require 'support/runner_support' diff --git a/spec/rspec/core/shared_context_spec.rb b/spec/rspec/core/shared_context_spec.rb index 1f8f832e10..2fb1cc9228 100644 --- a/spec/rspec/core/shared_context_spec.rb +++ b/spec/rspec/core/shared_context_spec.rb @@ -1,5 +1,3 @@ -require "spec_helper" - RSpec.describe RSpec::SharedContext do it "is accessible as RSpec::Core::SharedContext" do RSpec::Core::SharedContext diff --git a/spec/rspec/core/shared_example_group_spec.rb b/spec/rspec/core/shared_example_group_spec.rb index 7b410f46d7..9b0951e8b9 100644 --- a/spec/rspec/core/shared_example_group_spec.rb +++ b/spec/rspec/core/shared_example_group_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'rspec/support/spec/in_sub_process' module RandomTopLevelModule diff --git a/spec/rspec/core/suite_hooks_spec.rb b/spec/rspec/core/suite_hooks_spec.rb index 54f9eec880..2af89f8e06 100644 --- a/spec/rspec/core/suite_hooks_spec.rb +++ b/spec/rspec/core/suite_hooks_spec.rb @@ -1,4 +1,3 @@ -require "spec_helper" require "support/runner_support" module RSpec::Core diff --git a/spec/rspec/core/warnings_spec.rb b/spec/rspec/core/warnings_spec.rb index 09ad6329d9..5b45053a8f 100644 --- a/spec/rspec/core/warnings_spec.rb +++ b/spec/rspec/core/warnings_spec.rb @@ -1,5 +1,3 @@ -require "spec_helper" - RSpec.describe "rspec warnings and deprecations" do describe "#deprecate" do diff --git a/spec/rspec/core/world_spec.rb b/spec/rspec/core/world_spec.rb index 0c5515c121..0bda0ebe89 100644 --- a/spec/rspec/core/world_spec.rb +++ b/spec/rspec/core/world_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - class Bar; end class Foo; end diff --git a/spec/rspec/core_spec.rb b/spec/rspec/core_spec.rb index 58bfa0ccd2..82fd0a0197 100644 --- a/spec/rspec/core_spec.rb +++ b/spec/rspec/core_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'rspec/support/spec/prevent_load_time_warnings' RSpec.describe RSpec do From d2c1376df59034d7f5b64bb5ab37f888f11c9956 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 15 Dec 2014 15:51:25 -0800 Subject: [PATCH 129/275] Disable all monkey patching in our spec environment. --- spec/rspec/core/failed_example_notification_spec.rb | 2 +- spec/rspec/core/formatters_spec.rb | 2 +- spec/spec_helper.rb | 9 +-------- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/spec/rspec/core/failed_example_notification_spec.rb b/spec/rspec/core/failed_example_notification_spec.rb index 6fc3ecf2b6..846b658f9d 100644 --- a/spec/rspec/core/failed_example_notification_spec.rb +++ b/spec/rspec/core/failed_example_notification_spec.rb @@ -1,5 +1,5 @@ module RSpec::Core::Notifications - describe FailedExampleNotification do + RSpec.describe FailedExampleNotification do before do allow(RSpec.configuration).to receive(:color_enabled?).and_return(true) end diff --git a/spec/rspec/core/formatters_spec.rb b/spec/rspec/core/formatters_spec.rb index e9bc04f54c..1cf61feaba 100644 --- a/spec/rspec/core/formatters_spec.rb +++ b/spec/rspec/core/formatters_spec.rb @@ -1,7 +1,7 @@ require 'pathname' module RSpec::Core::Formatters - describe Loader do + RSpec.describe Loader do let(:output) { StringIO.new } let(:reporter) { instance_double "Reporter", :register_listener => nil } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 75f512e825..16c45fad7f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -67,14 +67,7 @@ def without_env_vars(*vars) c.alias_it_behaves_like_to 'it_has_behavior' c.include(RSpecHelpers) c.include Aruba::Api, :file_path => /spec\/command_line/ - - c.expect_with :rspec do |expectations| - expectations.syntax = :expect - end - - c.mock_with :rspec do |mocks| - mocks.syntax = :expect - end + c.disable_monkey_patching! # runtime options c.raise_errors_for_deprecations! From 3c9f4b4bc0de2f79766599cb8ba7f55ccd628037 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 17 Dec 2014 17:12:36 -0800 Subject: [PATCH 130/275] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 5 ++++- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 0180db0c2c..34d3c7461c 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-17T17:12:36-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index f6e810cfc6..7cb6ad6768 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,9 @@ -# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-17T17:12:36-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby sudo: false +cache: bundler before_install: - "script/clone_all_rspec_repos" # Downgrade bundler to work around https://github.com/bundler/bundler/issues/3004 @@ -21,6 +22,7 @@ rvm: - 2.1.3 - 2.1.4 - 2.1.5 + - 2.2 - ruby-head - ree - jruby-18mode @@ -35,4 +37,5 @@ matrix: - rvm: jruby-head - rvm: ruby-head - rvm: rbx + - rvm: 2.2 fast_finish: true diff --git a/appveyor.yml b/appveyor.yml index 288c205220..f3abe97571 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-17T17:12:36-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 5f0a2e4ec8..8fa1c7323d 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-17T17:12:36-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 7eb77aba82..34830e4765 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-17T17:12:36-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 2cf429a835..c546ea8b7d 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-17T17:12:36-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 8a2f2eb1ae..2bc6e80047 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-17T17:12:36-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 1fec63caf6..532df312e5 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-08T12:46:26-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-17T17:12:36-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 5ae94378b716c2cec1d52cf4b2676aa3c173bb2c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 18 Dec 2014 09:11:09 -0800 Subject: [PATCH 131/275] Fix intermittently failing specs. These specs would fail when run in isolation but would pass when run after other specs that used the `match` and `include` matchers first (causing them to be loaded). To make them consistently pass, we need to get the matchers before manipulating the load path to ensure they are already loaded. --- spec/rspec/core/rake_task_spec.rb | 35 +++++++++++++++++++------------ 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index 6889fbd8ba..fc2d57c828 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -150,6 +150,12 @@ def specify_consistent_ordering_of_files_to_run(pattern, file_searcher) describe "load path manipulation" do def self.it_configures_rspec_load_path(description, path_template) context "when rspec is installed as #{description}" do + # Matchers are lazily loaded via `autoload`, so we need to get the matcher before + # the load path is manipulated, so we're using `let!` here to do that. + let!(:include_expected_load_path_option) do + match(/ -I'?#{path_template % "rspec-core"}'?#{File::PATH_SEPARATOR}'?#{path_template % "rspec-support"}'? /) + end + it "adds the current rspec-core and rspec-support dirs to the load path to ensure the current version is used" do $LOAD_PATH.replace([ path_template % "rspec-core", @@ -159,7 +165,7 @@ def self.it_configures_rspec_load_path(description, path_template) path_template % "rake" ]) - expect(spec_command).to match(/ -I'?#{path_template % "rspec-core"}'?#{File::PATH_SEPARATOR}'?#{path_template % "rspec-support"}'? /) + expect(spec_command).to include_expected_load_path_option end it "avoids adding the same load path entries twice" do @@ -170,7 +176,7 @@ def self.it_configures_rspec_load_path(description, path_template) path_template % "rspec-support" ]) - expect(spec_command).to match(/ -I'?#{path_template % "rspec-core"}'?#{File::PATH_SEPARATOR}'?#{path_template % "rspec-support"}'? /) + expect(spec_command).to include_expected_load_path_option end end end @@ -185,22 +191,25 @@ def self.it_configures_rspec_load_path(description, path_template) "/Users/myron/.gem/ruby/1.9.3/gems/%s-3.1.0.beta1/lib" it "does not include extra load path entries for other gems that have `rspec-core` in its path" do + # matchers are lazily loaded with autoload, so we need to get the matcher before manipulating the load path. + include_extra_load_path_entries = include("simplecov", "minitest", "rspec-core/spec") + # these are items on my load path due to `bundle install --standalone`, # and my initial logic caused all these to be included in the `-I` option. $LOAD_PATH.replace([ - "/Users/myron/code/rspec-dev/repos/rspec-core/spec", - "/Users/myron/code/rspec-dev/repos/rspec-core/bundle/ruby/1.9.1/gems/simplecov-0.8.2/lib", - "/Users/myron/code/rspec-dev/repos/rspec-core/bundle/ruby/1.9.1/gems/simplecov-html-0.8.0/lib", - "/Users/myron/code/rspec-dev/repos/rspec-core/bundle/ruby/1.9.1/gems/minitest-5.3.3/lib", - "/Users/myron/code/rspec-dev/repos/rspec/lib", - "/Users/myron/code/rspec-dev/repos/rspec-mocks/lib", - "/Users/myron/code/rspec-dev/repos/rspec-core/lib", - "/Users/myron/code/rspec-dev/repos/rspec-expectations/lib", - "/Users/myron/code/rspec-dev/repos/rspec-support/lib", - "/Users/myron/code/rspec-dev/repos/rspec-core/bundle", + "/Users/user/code/rspec-dev/repos/rspec-core/spec", + "/Users/user/code/rspec-dev/repos/rspec-core/bundle/ruby/1.9.1/gems/simplecov-0.8.2/lib", + "/Users/user/code/rspec-dev/repos/rspec-core/bundle/ruby/1.9.1/gems/simplecov-html-0.8.0/lib", + "/Users/user/code/rspec-dev/repos/rspec-core/bundle/ruby/1.9.1/gems/minitest-5.3.3/lib", + "/Users/user/code/rspec-dev/repos/rspec/lib", + "/Users/user/code/rspec-dev/repos/rspec-mocks/lib", + "/Users/user/code/rspec-dev/repos/rspec-core/lib", + "/Users/user/code/rspec-dev/repos/rspec-expectations/lib", + "/Users/user/code/rspec-dev/repos/rspec-support/lib", + "/Users/user/code/rspec-dev/repos/rspec-core/bundle", ]) - expect(spec_command).not_to include("simplecov", "minitest", "rspec-core/spec") + expect(spec_command).not_to include_extra_load_path_entries end end From 1febdccaa13b7479c6d0f807cd01ff82d62963e1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 18 Dec 2014 22:35:48 -0800 Subject: [PATCH 132/275] Update benchmark allocations helper to support min count. This is useful when we have many groups because each group is a separate class. --- benchmarks/allocations/helper.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/benchmarks/allocations/helper.rb b/benchmarks/allocations/helper.rb index 6932ae5ed2..f87541e973 100644 --- a/benchmarks/allocations/helper.rb +++ b/benchmarks/allocations/helper.rb @@ -2,7 +2,7 @@ require 'rspec/core' require 'allocation_stats' -def benchmark_allocations(burn: 1) +def benchmark_allocations(burn: 1, min_allocations: 0) stats = AllocationStats.new(burn: burn).trace do yield end @@ -13,5 +13,11 @@ def benchmark_allocations(burn: 1) [:class_plus] end - puts stats.allocations(alias_paths: true).group_by(*columns).from_pwd.sort_by_size.to_text + results = stats.allocations(alias_paths: true).group_by(*columns).from_pwd.sort_by_size.to_text + filtered = results.split("\n").select do |line| + count = line[/\s+(\d+)\z/, 1] + count.nil? || Integer(count) >= min_allocations + end.join("\n") + + puts filtered end From 53b8c0be5c814c3d51cc006319f4b00835555135 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 16 Dec 2014 11:00:49 -0800 Subject: [PATCH 133/275] Refactor specs to not use private config API. `config.include_extend_or_prepend_modules` is an internal API that I plan to change in an upcoming commit. --- spec/rspec/core/shared_example_group_spec.rb | 26 +++++++++++--------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/spec/rspec/core/shared_example_group_spec.rb b/spec/rspec/core/shared_example_group_spec.rb index 9b0951e8b9..3e28bcf39a 100644 --- a/spec/rspec/core/shared_example_group_spec.rb +++ b/spec/rspec/core/shared_example_group_spec.rb @@ -102,13 +102,15 @@ module Core end context "given a hash" do - it "delegates include on configuration" do - implementation = Proc.new { def bar; 'bar'; end } + it "includes itself in matching example groups" do + implementation = Proc.new { def self.bar; 'bar'; end } define_shared_group(:foo => :bar, &implementation) - a = RSpec.configuration.include_extend_or_prepend_modules.first - expect(a[0]).to eq(:include) - expect(Class.new.send(:include, a[1]).new.bar).to eq('bar') - expect(a[2]).to eq(:foo => :bar) + + matching_group = RSpec.describe "Group", :foo => :bar + non_matching_group = RSpec.describe "Group" + + expect(matching_group.bar).to eq("bar") + expect(non_matching_group).not_to respond_to(:bar) end end @@ -120,12 +122,14 @@ module Core end it "delegates include on configuration" do - implementation = Proc.new { def bar; 'bar'; end } + implementation = Proc.new { def self.bar; 'bar'; end } define_shared_group("name", :foo => :bar, &implementation) - a = RSpec.configuration.include_extend_or_prepend_modules.first - expect(a[0]).to eq(:include) - expect(Class.new.send(:include, a[1]).new.bar).to eq('bar') - expect(a[2]).to eq(:foo => :bar) + + matching_group = RSpec.describe "Group", :foo => :bar + non_matching_group = RSpec.describe "Group" + + expect(matching_group.bar).to eq("bar") + expect(non_matching_group).not_to respond_to(:bar) end end From fa61873f6929a0ed2bd467a1929ea422b8c2c061 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 16 Dec 2014 23:43:41 -0800 Subject: [PATCH 134/275] Remove spec for behavior we will no longer be supporting. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fully supporting metadata mutation is fraught with difficulties see (rspec/rspec-rails#829 and the attempted fix in #1089). For RSpec 3, we decided to expose `define_derived_metadata` as an officially supported way to accomplish the sorts of things metadata mutation was used for in RSpec 2. This spec got left behind (since it was passing at the time, it wasn't noticed) but isn’t something we want to support going forward since module inclusion shouldn't mutate metadata. It’s also getting in the way of a perf optimization I'm implementing. --- spec/rspec/core/configuration_spec.rb | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index f913b020d0..07fd08c1f4 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -1530,23 +1530,6 @@ def exclude?(line) expect(group.included_modules).to include(mod) end - it "includes each one before deciding whether to include the next" do - mod1 = Module.new do - def self.included(host) - host.metadata[:foo] = :bar - end - end - mod2 = Module.new - - group = RSpec.describe("group") - - config.include(mod1) - config.include(mod2, :foo => :bar) - config.configure_group(group) - expect(group.included_modules).to include(mod1) - expect(group.included_modules).to include(mod2) - end - module IncludeExtendOrPrependMeOnce def self.included(host) raise "included again" if host.instance_methods.include?(:foobar) From d4e6b53823c910cd907c5d827798d23d4f40d32b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 16 Dec 2014 12:07:21 -0800 Subject: [PATCH 135/275] Reify new FilterableItemRepository abstraction. --- lib/rspec/core/metadata_filter.rb | 94 ++++++++++ .../core/filterable_item_repository_spec.rb | 174 ++++++++++++++++++ 2 files changed, 268 insertions(+) create mode 100644 spec/rspec/core/filterable_item_repository_spec.rb diff --git a/lib/rspec/core/metadata_filter.rb b/lib/rspec/core/metadata_filter.rb index 79dfb87eda..3c692b28a0 100644 --- a/lib/rspec/core/metadata_filter.rb +++ b/lib/rspec/core/metadata_filter.rb @@ -86,5 +86,99 @@ def silence_metadata_example_group_deprecations end end end + + # Tracks a collection of filterable items (e.g. modules, hooks, etc) + # and provides an optimized API to get the applicable items for the + # metadata of an example or example group. + # @private + class FilterableItemRepository + def initialize(applies_predicate) + @applies_predicate = applies_predicate + @items = [] + @applicable_keys = Set.new + @proc_keys = Set.new + @memoized_lookups = Hash.new do |hash, applicable_metadata| + hash[applicable_metadata] = find_items_for(applicable_metadata) + end + end + + def add(item, metadata) + @items << [item, metadata] + @applicable_keys.merge(metadata.keys) + @proc_keys.merge(proc_keys_from metadata) + @memoized_lookups.clear + end + + def items_for(metadata) + # The filtering of `metadata` to `applicable_metadata` is the key thing + # that makes the memoization actually useful in practice, since each + # example and example group have different metadata (e.g. location and + # description). By filtering to the metadata keys our items care about, + # we can ignore extra metadata keys that differ for each example/group. + # For example, given `config.include DBHelpers, :db`, example groups + # can be split into those two sets: those that are tagged with `:db` and those + # that are not. For each set, this method for the first group in the set is + # still an `O(N)` calculation, but all subsequent groups in the set will be + # constant time lookups when they call this method. + applicable_metadata = applicable_metadata_from(metadata) + + if applicable_metadata.keys.any? { |k| @proc_keys.include?(k) } + # It's unsafe to memoize lookups involving procs (since they can + # be non-deterministic), so we skip the memoization in this case. + find_items_for(applicable_metadata) + else + @memoized_lookups[applicable_metadata] + end + end + + private + + def applicable_metadata_from(metadata) + metadata.select do |key, _value| + @applicable_keys.include?(key) + end + end + + def find_items_for(request_meta) + @items.each_with_object([]) do |(item, item_meta), to_return| + to_return << item if item_meta.empty? || + MetadataFilter.apply?(@applies_predicate, item_meta, request_meta) + end + end + + def proc_keys_from(metadata) + metadata.each_with_object([]) do |(key, value), to_return| + to_return << key if Proc === value + end + end + + if {}.select {} == [] # For 1.8.7 + undef applicable_metadata_from + def applicable_metadata_from(metadata) + Hash[metadata.select do |key, _value| + @applicable_keys.include?(key) + end] + end + end + + unless [].respond_to?(:each_with_object) # For 1.8.7 + undef find_items_for + def find_items_for(request_meta) + @items.inject([]) do |to_return, (item, item_meta)| + to_return << item if item_meta.empty? || + MetadataFilter.apply?(@applies_predicate, item_meta, request_meta) + to_return + end + end + + undef proc_keys_from + def proc_keys_from(metadata) + metadata.inject([]) do |to_return, (key, value)| + to_return << key if Proc === value + to_return + end + end + end + end end end diff --git a/spec/rspec/core/filterable_item_repository_spec.rb b/spec/rspec/core/filterable_item_repository_spec.rb new file mode 100644 index 0000000000..3f55cdc9ff --- /dev/null +++ b/spec/rspec/core/filterable_item_repository_spec.rb @@ -0,0 +1,174 @@ +module RSpec + module Core + RSpec.describe FilterableItemRepository, "#items_for" do + FilterableItem = Struct.new(:name) + + let(:repo) { FilterableItemRepository.new(:any?) } + let(:item_1) { FilterableItem.new("Item 1") } + let(:item_2) { FilterableItem.new("Item 2") } + let(:item_3) { FilterableItem.new("Item 3") } + let(:item_4) { FilterableItem.new("Item 4") } + + context "when the repository is empty" do + it 'returns an empty list' do + expect(repo.items_for(:foo => "bar")).to eq([]) + end + end + + context "when the repository has items that have no metadata" do + before do + repo.add item_1, {} + repo.add item_2, {} + end + + it "returns those items, regardless of the provided argument" do + expect(repo.items_for({})).to contain_exactly(item_1, item_2) + expect(repo.items_for(:foo => "bar")).to contain_exactly(item_1, item_2) + end + end + + context "when the repository has items that have metadata" do + before do + repo.add item_1, :foo => "bar" + repo.add item_2, :slow => true + repo.add item_3, :foo => "bar" + end + + it 'return an empty list when given empty metadata' do + expect(repo.items_for({})).to eq([]) + end + + it 'return an empty list when given metadata that matches no items' do + expect(repo.items_for(:slow => false, :foo => "bazz")).to eq([]) + end + + it 'returns matching items for the provided metadata' do + expect(repo.items_for(:slow => true)).to contain_exactly(item_2) + expect(repo.items_for(:foo => "bar")).to contain_exactly(item_1, item_3) + expect(repo.items_for(:slow => true, :foo => "bar")).to contain_exactly(item_1, item_2, item_3) + end + + it 'returns the matching items in the order they were added' do + expect(repo.items_for(:slow => true, :foo => "bar")).to eq [item_1, item_2, item_3] + end + + it 'ignores other metadata keys that are not related to the added items' do + expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) + end + + it 'differentiates between an applicable key being missing and having an explicit `nil` value' do + repo.add item_4, :bar => nil + + expect(repo.items_for({})).to eq([]) + expect(repo.items_for(:bar => nil)).to contain_exactly(item_4) + end + + it 'returns the correct items when they are added after a memoized lookup' do + expect { + repo.add item_4, :slow => true + }.to change { repo.items_for(:slow => true) }. + from(a_collection_containing_exactly(item_2)). + to(a_collection_containing_exactly(item_2, item_4)) + end + + let(:flip_proc) do + return_val = true + Proc.new { return_val.tap { |v| return_val = !v } } + end + + context "with proc values" do + before do + repo.add item_4, { :include_it => flip_proc } + end + + it 'evaluates the proc each time since the logic can return a different value each time' do + expect(repo.items_for(:include_it => nil)).to contain_exactly(item_4) + expect(repo.items_for(:include_it => nil)).to eq([]) + expect(repo.items_for(:include_it => nil)).to contain_exactly(item_4) + expect(repo.items_for(:include_it => nil)).to eq([]) + end + end + + context "when initialized with the `:any?` predicate" do + let(:repo) { FilterableItemRepository.new(:any?) } + + it 'matches against multi-entry items when any of the metadata entries match' do + repo.add item_4, :key_1 => "val_1", :key_2 => "val_2" + + expect(repo.items_for(:key_1 => "val_1")).to contain_exactly(item_4) + expect(repo.items_for(:key_2 => "val_2")).to contain_exactly(item_4) + expect(repo.items_for(:key_1 => "val_1", :key_2 => "val_2")).to contain_exactly(item_4) + end + end + + context "when initialized with the `:all?` predicate" do + let(:repo) { FilterableItemRepository.new(:all?) } + + it 'matches against multi-entry items when all of the metadata entries match' do + repo.add item_4, :key_1 => "val_1", :key_2 => "val_2" + + expect(repo.items_for(:key_1 => "val_1")).to eq([]) + expect(repo.items_for(:key_2 => "val_2")).to eq([]) + expect(repo.items_for(:key_1 => "val_1", :key_2 => "val_2")).to contain_exactly(item_4) + end + end + + describe "performance optimization" do + # NOTE: the specs in this context are potentially brittle because they are + # coupled to the implementation's usage of `MetadataFilter.apply?`. However, + # they demonstrate the perf optimization that was the reason we created + # this class, and thus have value in demonstrating the memoization is working + # properly and in documenting the reason the class exists in the first place. + # Still, if these prove to be brittle in the future, feel free to delete them since + # they are not concerned with externally visible behaviors. + + it 'is optimized to check metadata filter application for a given pair of metadata hashes only once' do + # TODO: use mock expectations for this once https://github.com/rspec/rspec-mocks/pull/841 is fixed. + call_counts = track_metadata_filter_apply_calls + + 3.times do + expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) + end + + expect(call_counts[:slow => true]).to eq(1) + end + + it 'ignores extraneous metadata keys when doing memoized lookups' do + # TODO: use mock expectations for this once https://github.com/rspec/rspec-mocks/pull/841 is fixed. + call_counts = track_metadata_filter_apply_calls + + expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) + expect(repo.items_for(:slow => true, :other => "bar")).to contain_exactly(item_2) + expect(repo.items_for(:slow => true, :goo => "bazz")).to contain_exactly(item_2) + + expect(call_counts[:slow => true]).to eq(1) + end + + context "when there are some proc keys" do + before do + repo.add item_4, { :include_it => flip_proc } + end + + it 'still performs memoization for metadata hashes that lack those keys' do + call_counts = track_metadata_filter_apply_calls + + expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) + expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) + + expect(call_counts[:slow => true]).to eq(1) + end + end + + def track_metadata_filter_apply_calls + Hash.new(0).tap do |call_counts| + allow(MetadataFilter).to receive(:apply?).and_wrap_original do |original, predicate, item_meta, request_meta| + call_counts[item_meta] += 1 + original.call(predicate, item_meta, request_meta) + end + end + end + end + end + end + end +end From 8bf526311c74dd4ad64f594476ba33319217dc23 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 16 Dec 2014 22:07:29 -0800 Subject: [PATCH 136/275] Refactor config metadata filtering to use new FilterableItemRepository. This should perform better. --- lib/rspec/core/configuration.rb | 32 ++++++++++++++++----------- lib/rspec/core/metadata_filter.rb | 2 ++ spec/rspec/core/configuration_spec.rb | 6 ++--- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index bbc4ac18b6..bdc1694a84 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -284,8 +284,6 @@ def treat_symbols_as_metadata_keys_with_true_values=(_value) # @private add_setting :tty # @private - add_setting :include_extend_or_prepend_modules - # @private attr_writer :files_to_run # @private add_setting :expecting_with_rspec @@ -301,7 +299,9 @@ def initialize @start_time = $_rspec_core_load_started_at || ::RSpec::Core::Time.now # rubocop:enable Style/GlobalVars @expectation_frameworks = [] - @include_extend_or_prepend_modules = [] + @include_modules = FilterableItemRepository.new(:any?) + @extend_modules = FilterableItemRepository.new(:any?) + @prepend_modules = FilterableItemRepository.new(:any?) @mock_framework = nil @files_or_directories_to_run = [] @color = false @@ -330,7 +330,7 @@ def initialize @profile_examples = false @requires = [] @libs = [] - @derived_metadata_blocks = [] + @derived_metadata_blocks = FilterableItemRepository.new(:any?) end # @private @@ -1050,7 +1050,7 @@ def exclusion_filter # @see #prepend def include(mod, *filters) meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering) - include_extend_or_prepend_modules << [:include, mod, meta] + @include_modules.add(mod, meta) end # Tells RSpec to extend example groups with `mod`. Methods defined in @@ -1085,7 +1085,7 @@ def include(mod, *filters) # @see #prepend def extend(mod, *filters) meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering) - include_extend_or_prepend_modules << [:extend, mod, meta] + @extend_modules.add(mod, meta) end # Tells RSpec to prepend example groups with `mod`. Methods defined in @@ -1123,7 +1123,7 @@ def extend(mod, *filters) if RSpec::Support::RubyFeatures.module_prepends_supported? def prepend(mod, *filters) meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering) - include_extend_or_prepend_modules << [:prepend, mod, meta] + @prepend_modules.add(mod, meta) end end @@ -1132,9 +1132,15 @@ def prepend(mod, *filters) # Used internally to extend a group with modules using `include`, `prepend` and/or # `extend`. def configure_group(group) - include_extend_or_prepend_modules.each do |include_extend_or_prepend, mod, filters| - next unless filters.empty? || group.apply?(:any?, filters) - __send__("safe_#{include_extend_or_prepend}", mod, group) + configure_group_with group, @include_modules, :safe_include + configure_group_with group, @extend_modules, :safe_extend + configure_group_with group, @prepend_modules, :safe_prepend + end + + # @private + def configure_group_with(group, module_list, application_method) + module_list.items_for(group.metadata).each do |mod| + __send__(application_method, mod, group) end end @@ -1401,13 +1407,13 @@ def disable_monkey_patching! # end def define_derived_metadata(*filters, &block) meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering) - @derived_metadata_blocks << [meta, block] + @derived_metadata_blocks.add(block, meta) end # @private def apply_derived_metadata_to(metadata) - @derived_metadata_blocks.each do |filter, block| - block.call(metadata) if filter.empty? || MetadataFilter.apply?(:any?, filter, metadata) + @derived_metadata_blocks.items_for(metadata).each do |block| + block.call(metadata) end end diff --git a/lib/rspec/core/metadata_filter.rb b/lib/rspec/core/metadata_filter.rb index 3c692b28a0..57ffdfbeae 100644 --- a/lib/rspec/core/metadata_filter.rb +++ b/lib/rspec/core/metadata_filter.rb @@ -92,6 +92,8 @@ def silence_metadata_example_group_deprecations # metadata of an example or example group. # @private class FilterableItemRepository + attr_reader :items + def initialize(applies_predicate) @applies_predicate = applies_predicate @items = [] diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index 07fd08c1f4..d67af79248 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -881,7 +881,7 @@ def you_call_this_a_blt? it_behaves_like "metadata hash builder" do def metadata_hash(*args) config.include(InstanceLevelMethods, *args) - config.include_extend_or_prepend_modules.last.last + config.instance_variable_get(:@include_modules).items.last.last end end @@ -921,7 +921,7 @@ def that_thing it_behaves_like "metadata hash builder" do def metadata_hash(*args) config.extend(ThatThingISentYou, *args) - config.include_extend_or_prepend_modules.last.last + config.instance_variable_get(:@extend_modules).items.last.last end end @@ -948,7 +948,7 @@ def foo it_behaves_like "metadata hash builder" do def metadata_hash(*args) config.prepend(SomeRandomMod, *args) - config.include_extend_or_prepend_modules.last.last + config.instance_variable_get(:@prepend_modules).items.last.last end end From 35c80bd2d88dbd7e6f0e23eec932cf75426b0078 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 16 Dec 2014 23:53:59 -0800 Subject: [PATCH 137/275] Add benchmarks demonstrating that our optimization is in fact faster. --- benchmarks/module_inclusion_filtering.rb | 90 ++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 benchmarks/module_inclusion_filtering.rb diff --git a/benchmarks/module_inclusion_filtering.rb b/benchmarks/module_inclusion_filtering.rb new file mode 100644 index 0000000000..121b5d9caa --- /dev/null +++ b/benchmarks/module_inclusion_filtering.rb @@ -0,0 +1,90 @@ +require_relative "../bundle/bundler/setup" # configures load paths +require 'rspec/core' + +class << RSpec + attr_writer :world +end + +# Here we are restoring the old implementation of `configure_group`, so that +# we can toggle the new vs old implementation in the benchmark by aliasing it. +module RSpecConfigurationOverrides + def initialize(*args) + super + @include_extend_or_prepend_modules = [] + end + + def include(mod, *filters) + meta = RSpec::Core::Metadata.build_hash_from(filters, :warn_about_example_group_filtering) + @include_extend_or_prepend_modules << [:include, mod, meta] + super + end + + def old_configure_group(group) + @include_extend_or_prepend_modules.each do |include_extend_or_prepend, mod, filters| + next unless filters.empty? || group.apply?(:any?, filters) + __send__("safe_#{include_extend_or_prepend}", mod, group) + end + end + + def self.prepare_implementation(prefix) + RSpec.world = RSpec::Core::World.new # clear our state + RSpec::Core::Configuration.class_eval do + alias_method :configure_group, :"#{prefix}_configure_group" + end + end +end + +RSpec::Core::Configuration.class_eval do + prepend RSpecConfigurationOverrides + alias new_configure_group configure_group +end + +RSpec.configure do |c| + 50.times { c.include Module.new, :include_it } +end + +require 'benchmark/ips' + +Benchmark.ips do |x| + x.report("Old linear search: non-matching metadata") do |times| + RSpecConfigurationOverrides.prepare_implementation(:old) + times.times { |i| RSpec.describe "Old linear search: non-matching metadata #{i}" } + end + + x.report("New memoized search: non-matching metadata") do |times| + RSpecConfigurationOverrides.prepare_implementation(:new) + times.times { |i| RSpec.describe "New memoized search: non-matching metadata #{i}" } + end + + x.report("Old linear search: matching metadata") do |times| + RSpecConfigurationOverrides.prepare_implementation(:old) + times.times { |i| RSpec.describe "Old linear search: matching metadata #{i}", :include_it } + end + + x.report("New memoized search: matching metadata") do |times| + RSpecConfigurationOverrides.prepare_implementation(:new) + times.times { |i| RSpec.describe "New memoized search: matching metadata #{i}", :include_it } + end +end + +__END__ + +Calculating ------------------------------------- +Old linear search: non-matching metadata + 86.000 i/100ms +New memoized search: non-matching metadata + 93.000 i/100ms +Old linear search: matching metadata + 79.000 i/100ms +New memoized search: matching metadata + 90.000 i/100ms +------------------------------------------------- +Old linear search: non-matching metadata + 884.109 (±61.9%) i/s - 3.268k +New memoized search: non-matching metadata + 1.099k (±81.2%) i/s - 3.441k +Old linear search: matching metadata + 822.348 (±57.5%) i/s - 3.081k +New memoized search: matching metadata + 1.116k (±76.6%) i/s - 3.510k + From 2dadb693665a328c863ccc3c98b00b9c04754a14 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 18 Dec 2014 16:02:36 -0800 Subject: [PATCH 138/275] Reduce BaseHookCollection API. We don't need all array methods. The class definition will load faster with fewer method defs. Also, static method defs perform better than dynamic `define_method` method defs since it doesn't need to activate the closure environment. --- lib/rspec/core/hooks.rb | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index edda5fdc00..e41f3bba2f 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -405,18 +405,35 @@ def hook_description # @private class BaseHookCollection - Array.public_instance_methods(false).each do |name| - define_method(name) { |*a, &b| hooks.__send__(name, *a, &b) } + def initialize(hooks=[]) + @hooks = hooks end attr_reader :hooks protected :hooks - alias append push - alias prepend unshift + def to_ary + @hooks + end - def initialize(hooks=[]) - @hooks = hooks + def empty? + @hooks.empty? + end + + def append(hook) + @hooks.push hook + end + + def prepend(hook) + @hooks.unshift hook + end + + def include?(hook) + @hooks.include?(hook) + end + + def each + @hooks.each { |h| yield h } end end @@ -466,7 +483,7 @@ def for(group) end def run - hooks.shift.run(@group) until hooks.empty? + hooks.each { |h| h.run(@group) } end end @@ -540,7 +557,7 @@ def process(host, globals, position, scope) globals[position][scope].each do |hook| next unless scope == :example || hook.options_apply?(host) next if host.parent_groups.any? { |a| a.hooks[position][scope].include?(hook) } - self[position][scope] << hook + self[position][scope].append hook end end From d66950552c9c41f7377ca8bce1ba99eccd7c269c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 18 Dec 2014 16:41:18 -0800 Subject: [PATCH 139/275] Move hook selection into base hook collection. Also, flatten the inheritance hierarchy a bit, which should perform ever-so-slightly better since there are fewer modules in the ancestor chain to check for method defs when dispatching messages. --- lib/rspec/core/configuration.rb | 4 ++-- lib/rspec/core/hooks.rb | 41 ++++++++++++--------------------- 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index bdc1694a84..5ca1f35963 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1489,10 +1489,10 @@ def with_suite_hooks hook_context = SuiteHookContext.new begin - before_suite_hooks.with(hook_context).run + before_suite_hooks.for(hook_context).run yield ensure - after_suite_hooks.with(hook_context).run + after_suite_hooks.for(hook_context).run end end diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index e41f3bba2f..14ceb0f49e 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -404,13 +404,14 @@ def hook_description end # @private - class BaseHookCollection - def initialize(hooks=[]) - @hooks = hooks + class HookCollection + def initialize(hooks=[], example_or_group=nil) + @hooks = hooks + @example_or_group = example_or_group end - attr_reader :hooks - protected :hooks + attr_reader :hooks, :example_or_group + protected :hooks, :example_or_group def to_ary @hooks @@ -432,51 +433,39 @@ def include?(hook) @hooks.include?(hook) end - def each - @hooks.each { |h| yield h } - end - end - - # @private - class HookCollection < BaseHookCollection def for(example_or_group) - self.class. - new(hooks.select { |hook| hook.options_apply?(example_or_group) }). - with(example_or_group) + self.class.new(@hooks.select { |hook| hook.options_apply?(example_or_group) }, example_or_group) end - def with(example) - @example = example - self + def each + @hooks.each { |h| yield h } end def run - hooks.each { |h| h.run(@example) } + hooks.each { |h| h.run(example_or_group) } end end # @private - class AroundHookCollection < BaseHookCollection + class AroundHookCollection < HookCollection def for(example, initial_procsy=nil) - self.class.new(hooks.select { |hook| hook.options_apply?(example) }). - with(example, initial_procsy) + super(example).with(initial_procsy) end - def with(example, initial_procsy) - @example = example + def with(initial_procsy) @initial_procsy = initial_procsy self end def run hooks.inject(@initial_procsy) do |procsy, around_hook| - procsy.wrap { around_hook.execute_with(@example, procsy) } + procsy.wrap { around_hook.execute_with(example_or_group, procsy) } end.call end end # @private - class GroupHookCollection < BaseHookCollection + class GroupHookCollection < HookCollection def for(group) @group = group self From a778bcd28f829e85984e7be1bf30b6240f23039c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 18 Dec 2014 21:11:03 -0800 Subject: [PATCH 140/275] Combine filtering and running hooks into a single method. Before, these things were split into separate methods (`for` and `run`), even though they are never needed individually. The split caused us to create extra unnecessary instances of `HookCollection` in order to maintain the extra state between `for` and `run` without mutating the existing instance. The allocation benchmarks show that this reduces the number of objects created per example group by 2 arrays and 2 HookCollection instances. --- .../running_1000_groups_1_example.rb | 74 +++++++++++++++ lib/rspec/core/configuration.rb | 4 +- lib/rspec/core/example.rb | 11 +-- lib/rspec/core/hooks.rb | 92 ++++++++----------- 4 files changed, 113 insertions(+), 68 deletions(-) create mode 100644 benchmarks/allocations/running_1000_groups_1_example.rb diff --git a/benchmarks/allocations/running_1000_groups_1_example.rb b/benchmarks/allocations/running_1000_groups_1_example.rb new file mode 100644 index 0000000000..f5a82595df --- /dev/null +++ b/benchmarks/allocations/running_1000_groups_1_example.rb @@ -0,0 +1,74 @@ +require_relative "helper" + +1000.times do |i| + RSpec.describe "group #{i}" do + it "has one example" do + end + end +end + +benchmark_allocations(burn: 0, min_allocations: 50) do + RSpec::Core::Runner.run([]) +end + +__END__ + +Before optimization: + + class_plus count +----------------------------------------------------------------------------------------------- ----- +Array 26021 +String 21331 +Array 19402 +Array 6001 +Array 6001 +RSpec::Core::Hooks::HookCollection 4004 +Array 4004 +Hash 3098 +Proc 3096 +RubyVM::Env 3056 +Time 2002 +Random 2001 +RSpec::Core::Hooks::AroundHookCollection 2000 +RSpec::Core::Notifications::GroupNotification 2000 +RSpec::Core::Notifications::ExampleNotification 2000 +RSpec::Core::Hooks::GroupHookCollection 2000 +Array 1003 +Array 1002 +Array 1002 +RSpec::Core::Example::Procsy 1000 +RubyVM::InstructionSequence 506 +Array 391 +Array 205 +Array 52 + + +After optimization, we allocate 2000 less arrays and 2000 less RSpec::Core::Hooks::HookCollection +instances. That's 2 less of each per example group. + + class_plus count +----------------------------------------------------------------------------------------------- ----- +Array 26021 +String 21331 +Array 17400 +Array 6001 +Array 6001 +Array 4004 +Hash 3098 +Proc 3096 +RubyVM::Env 3056 +RSpec::Core::Hooks::HookCollection 2002 +Time 2002 +Random 2001 +RSpec::Core::Notifications::ExampleNotification 2000 +RSpec::Core::Notifications::GroupNotification 2000 +RSpec::Core::Hooks::GroupHookCollection 2000 +Array 1003 +Array 1002 +Array 1002 +RSpec::Core::Example::Procsy 1000 +RSpec::Core::Hooks::AroundHookCollection 1000 +RubyVM::InstructionSequence 506 +Array 391 +Array 205 +Array 52 diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index 5ca1f35963..e3e0a2365b 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1489,10 +1489,10 @@ def with_suite_hooks hook_context = SuiteHookContext.new begin - before_suite_hooks.for(hook_context).run + before_suite_hooks.run_with(hook_context) yield ensure - after_suite_hooks.for(hook_context).run + after_suite_hooks.run_with(hook_context) end end diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 3b70d51e77..3103dfd0c7 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -270,11 +270,6 @@ def apply?(predicate, filters) MetadataFilter.apply?(predicate, filters, metadata) end - # @private - def around_example_hooks - @around_example_hooks ||= example_group.hooks.around_example_hooks_for(self) - end - # @private # # Used internally to set an exception in an after hook, which @@ -335,11 +330,7 @@ def instance_exec(*args, &block) private def with_around_example_hooks(&block) - if around_example_hooks.empty? - yield - else - @example_group_class.hooks.run(:around, :example, self, Procsy.new(self, &block)) - end + @example_group_class.hooks.run(:around, :example, self, Procsy.new(self, &block)) rescue Exception => e set_exception(e, "in an `around(:example)` hook") end diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index 14ceb0f49e..e4da09356f 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -405,13 +405,12 @@ def hook_description # @private class HookCollection - def initialize(hooks=[], example_or_group=nil) - @hooks = hooks - @example_or_group = example_or_group + def initialize(hooks=[]) + @hooks = hooks end - attr_reader :hooks, :example_or_group - protected :hooks, :example_or_group + attr_reader :hooks + protected :hooks def to_ary @hooks @@ -433,46 +432,30 @@ def include?(hook) @hooks.include?(hook) end - def for(example_or_group) - self.class.new(@hooks.select { |hook| hook.options_apply?(example_or_group) }, example_or_group) - end - def each @hooks.each { |h| yield h } end - def run - hooks.each { |h| h.run(example_or_group) } + def run_with(example_or_group) + @hooks.each do |hook| + hook.run(example_or_group) if hook.options_apply?(example_or_group) + end end end # @private class AroundHookCollection < HookCollection - def for(example, initial_procsy=nil) - super(example).with(initial_procsy) - end - - def with(initial_procsy) - @initial_procsy = initial_procsy - self - end - - def run - hooks.inject(@initial_procsy) do |procsy, around_hook| - procsy.wrap { around_hook.execute_with(example_or_group, procsy) } + def run_with(example, initial_procsy) + hooks.select { |hook| hook.options_apply?(example) }.inject(initial_procsy) do |procsy, around_hook| + procsy.wrap { around_hook.execute_with(example, procsy) } end.call end end # @private class GroupHookCollection < HookCollection - def for(group) - @group = group - self - end - - def run - hooks.each { |h| h.run(@group) } + def run_with(group) + hooks.each { |h| h.run(group) } end end @@ -496,10 +479,10 @@ def register_globals(host, globals) process(host, globals, :after, :context) end - def around_example_hooks_for(example, initial_procsy=nil) + def run_around_example_hooks_for(example, initial_procsy=nil) AroundHookCollection.new(FlatMap.flat_map(@owner.parent_groups) do |a| a.hooks[:around][:example] - end).for(example, initial_procsy) + end).run_with(example, initial_procsy) end def register(prepend_or_append, hook, *args, &block) @@ -525,7 +508,19 @@ def register(prepend_or_append, hook, *args, &block) # example. If no example is provided, just calls the hook directly. def run(hook, scope, example_or_group, initial_procsy=nil) return if RSpec.configuration.dry_run? - find_hook(hook, scope, example_or_group, initial_procsy).run + + case [hook, scope] + when [:before, :context] + run_before_context_hooks_for(example_or_group) + when [:after, :context] + run_after_context_hooks_for(example_or_group) + when [:around, :example] + run_around_example_hooks_for(example_or_group, initial_procsy) + when [:before, :example] + run_before_example_hooks_for(example_or_group) + when [:after, :example] + run_after_example_hooks_for(example_or_group) + end end SCOPES = [:example, :context] @@ -581,39 +576,24 @@ def normalized_scope_for(scope) SCOPE_ALIASES[scope] || scope end - def find_hook(hook, scope, example_or_group, initial_procsy) - case [hook, scope] - when [:before, :context] - before_context_hooks_for(example_or_group) - when [:after, :context] - after_context_hooks_for(example_or_group) - when [:around, :example] - around_example_hooks_for(example_or_group, initial_procsy) - when [:before, :example] - before_example_hooks_for(example_or_group) - when [:after, :example] - after_example_hooks_for(example_or_group) - end - end - - def before_context_hooks_for(group) - GroupHookCollection.new(self[:before][:context]).for(group) + def run_before_context_hooks_for(group) + GroupHookCollection.new(self[:before][:context]).run_with(group) end - def after_context_hooks_for(group) - GroupHookCollection.new(self[:after][:context]).for(group) + def run_after_context_hooks_for(group) + GroupHookCollection.new(self[:after][:context]).run_with(group) end - def before_example_hooks_for(example) + def run_before_example_hooks_for(example) HookCollection.new(FlatMap.flat_map(@owner.parent_groups.reverse) do |a| a.hooks[:before][:example] - end).for(example) + end).run_with(example) end - def after_example_hooks_for(example) + def run_after_example_hooks_for(example) HookCollection.new(FlatMap.flat_map(@owner.parent_groups) do |a| a.hooks[:after][:example] - end).for(example) + end).run_with(example) end end end From 4f68f2eef44c331bad3ca8c630b08bc4e7a0c68f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 18 Dec 2014 22:45:12 -0800 Subject: [PATCH 141/275] Move method def to be nearer to similar methods. --- lib/rspec/core/hooks.rb | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index e4da09356f..8e646e385f 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -479,12 +479,6 @@ def register_globals(host, globals) process(host, globals, :after, :context) end - def run_around_example_hooks_for(example, initial_procsy=nil) - AroundHookCollection.new(FlatMap.flat_map(@owner.parent_groups) do |a| - a.hooks[:around][:example] - end).run_with(example, initial_procsy) - end - def register(prepend_or_append, hook, *args, &block) scope, options = scope_and_options_from(*args) @@ -566,12 +560,10 @@ def extract_scope_from(args) end end - # @api private def known_scope?(scope) SCOPES.include?(scope) || SCOPE_ALIASES.keys.include?(scope) end - # @api private def normalized_scope_for(scope) SCOPE_ALIASES[scope] || scope end @@ -595,6 +587,12 @@ def run_after_example_hooks_for(example) a.hooks[:after][:example] end).run_with(example) end + + def run_around_example_hooks_for(example, initial_procsy=nil) + AroundHookCollection.new(FlatMap.flat_map(@owner.parent_groups) do |a| + a.hooks[:around][:example] + end).run_with(example, initial_procsy) + end end end end From 0c4f4fbcdc04aac6e27efad934bd40d3eaaf0b6c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 18 Dec 2014 21:09:09 -0800 Subject: [PATCH 142/275] Refactor around hook execution. - Prefer `yield` to `&block` -- it performs much better. - Restore optimization from cd9185712375e7f81ac70531c681c1fa71a25a3e. - This allows us to remove `empty?` from HookCollection. --- benchmarks/capture_block_vs_yield.rb | 50 ++++++++++++++++++++++++++++ lib/rspec/core/example.rb | 4 +-- lib/rspec/core/hooks.rb | 17 +++++----- 3 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 benchmarks/capture_block_vs_yield.rb diff --git a/benchmarks/capture_block_vs_yield.rb b/benchmarks/capture_block_vs_yield.rb new file mode 100644 index 0000000000..84951d3734 --- /dev/null +++ b/benchmarks/capture_block_vs_yield.rb @@ -0,0 +1,50 @@ +require 'benchmark/ips' + +def yield_control + yield +end + +def capture_block_and_yield(&block) + yield +end + +def capture_block_and_call(&block) + block.call +end + +Benchmark.ips do |x| + x.report("yield ") do + yield_control { } + end + + x.report("capture block and yield") do + capture_block_and_yield { } + end + + x.report("capture block and call ") do + capture_block_and_call { } + end +end + +__END__ + +This benchmark demonstrates that `yield` is much, much faster +than capturing `&block` and calling it. In fact, the simple act +of capturing `&block`, even if we don't later reference `&block`, +incurs most of the cost, so we should avoid capturing blocks unless +we absolutely need to. + +Calculating ------------------------------------- +yield + 93.104k i/100ms +capture block and yield + 52.682k i/100ms +capture block and call + 51.115k i/100ms +------------------------------------------------- +yield + 5.161M (±10.6%) i/s - 25.231M +capture block and yield + 1.141M (±22.0%) i/s - 5.426M +capture block and call + 1.027M (±21.8%) i/s - 4.856M diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 3103dfd0c7..1f587a6eee 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -329,8 +329,8 @@ def instance_exec(*args, &block) private - def with_around_example_hooks(&block) - @example_group_class.hooks.run(:around, :example, self, Procsy.new(self, &block)) + def with_around_example_hooks + @example_group_class.hooks.run(:around, :example, self) { yield } rescue Exception => e set_exception(e, "in an `around(:example)` hook") end diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index 8e646e385f..e55ab1fb3c 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -416,10 +416,6 @@ def to_ary @hooks end - def empty? - @hooks.empty? - end - def append(hook) @hooks.push hook end @@ -445,7 +441,10 @@ def run_with(example_or_group) # @private class AroundHookCollection < HookCollection - def run_with(example, initial_procsy) + def run_with(example) + return yield if hooks.empty? # exit early to avoid the extra allocation cost of `Example::Procsy` + + initial_procsy = Example::Procsy.new(example) { yield } hooks.select { |hook| hook.options_apply?(example) }.inject(initial_procsy) do |procsy, around_hook| procsy.wrap { around_hook.execute_with(example, procsy) } end.call @@ -500,7 +499,7 @@ def register(prepend_or_append, hook, *args, &block) # # Runs all of the blocks stored with the hook in the context of the # example. If no example is provided, just calls the hook directly. - def run(hook, scope, example_or_group, initial_procsy=nil) + def run(hook, scope, example_or_group) return if RSpec.configuration.dry_run? case [hook, scope] @@ -509,7 +508,7 @@ def run(hook, scope, example_or_group, initial_procsy=nil) when [:after, :context] run_after_context_hooks_for(example_or_group) when [:around, :example] - run_around_example_hooks_for(example_or_group, initial_procsy) + run_around_example_hooks_for(example_or_group) { yield } when [:before, :example] run_before_example_hooks_for(example_or_group) when [:after, :example] @@ -588,10 +587,10 @@ def run_after_example_hooks_for(example) end).run_with(example) end - def run_around_example_hooks_for(example, initial_procsy=nil) + def run_around_example_hooks_for(example) AroundHookCollection.new(FlatMap.flat_map(@owner.parent_groups) do |a| a.hooks[:around][:example] - end).run_with(example, initial_procsy) + end).run_with(example) { yield } end end end From dcd66dee90972ffdfa57e28791bf871aa3bd2e1c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 18 Dec 2014 23:45:32 -0800 Subject: [PATCH 143/275] Remove unneeded flattening and extra object allocations. --- lib/rspec/core/hooks.rb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index e55ab1fb3c..2b87d6f83e 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -332,8 +332,8 @@ def hooks @hooks ||= HookCollections.new( self, :around => { :example => AroundHookCollection.new }, - :before => { :example => HookCollection.new, :context => HookCollection.new }, - :after => { :example => HookCollection.new, :context => HookCollection.new } + :before => { :example => HookCollection.new, :context => GroupHookCollection.new }, + :after => { :example => HookCollection.new, :context => GroupHookCollection.new } ) end @@ -568,23 +568,23 @@ def normalized_scope_for(scope) end def run_before_context_hooks_for(group) - GroupHookCollection.new(self[:before][:context]).run_with(group) + self[:before][:context].run_with(group) end def run_after_context_hooks_for(group) - GroupHookCollection.new(self[:after][:context]).run_with(group) + self[:after][:context].run_with(group) end def run_before_example_hooks_for(example) - HookCollection.new(FlatMap.flat_map(@owner.parent_groups.reverse) do |a| - a.hooks[:before][:example] - end).run_with(example) + @owner.parent_groups.reverse.each do |group| + group.hooks[:before][:example].run_with(example) + end end def run_after_example_hooks_for(example) - HookCollection.new(FlatMap.flat_map(@owner.parent_groups) do |a| - a.hooks[:after][:example] - end).run_with(example) + @owner.parent_groups.each do |group| + group.hooks[:after][:example].run_with(example) + end end def run_around_example_hooks_for(example) From 99abf42c714655b40eff9c9d4a3fe8b7a2b610be Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 18 Dec 2014 23:54:22 -0800 Subject: [PATCH 144/275] Reduce duplication of methods that run hooks. --- lib/rspec/core/hooks.rb | 39 +++++++++++++-------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index 2b87d6f83e..3089c46035 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -502,17 +502,14 @@ def register(prepend_or_append, hook, *args, &block) def run(hook, scope, example_or_group) return if RSpec.configuration.dry_run? - case [hook, scope] - when [:before, :context] - run_before_context_hooks_for(example_or_group) - when [:after, :context] - run_after_context_hooks_for(example_or_group) - when [:around, :example] - run_around_example_hooks_for(example_or_group) { yield } - when [:before, :example] - run_before_example_hooks_for(example_or_group) - when [:after, :example] - run_after_example_hooks_for(example_or_group) + if scope == :context + run_context_hooks_for(example_or_group, hook) + else + case hook + when :before then run_example_hooks_for(example_or_group, :before, :reverse_each) + when :after then run_example_hooks_for(example_or_group, :after, :each) + when :around then run_around_example_hooks_for(example_or_group) { yield } + end end end @@ -567,23 +564,13 @@ def normalized_scope_for(scope) SCOPE_ALIASES[scope] || scope end - def run_before_context_hooks_for(group) - self[:before][:context].run_with(group) - end - - def run_after_context_hooks_for(group) - self[:after][:context].run_with(group) - end - - def run_before_example_hooks_for(example) - @owner.parent_groups.reverse.each do |group| - group.hooks[:before][:example].run_with(example) - end + def run_context_hooks_for(group, type) + self[type][:context].run_with(group) end - def run_after_example_hooks_for(example) - @owner.parent_groups.each do |group| - group.hooks[:after][:example].run_with(example) + def run_example_hooks_for(example, type, each_method) + @owner.parent_groups.__send__(each_method) do |group| + group.hooks[type][:example].run_with(example) end end From 82017d13dfa99796babd98401b9d32bea3b65ce9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Dec 2014 00:08:31 -0800 Subject: [PATCH 145/275] Refactor: make AroundHookCollection not subclass HookCollection. At this point it is not needed at all. This allows us to change HookCollection to no longer take an `initialize` argument, meaning `hooks` is 100% internal to it now. --- lib/rspec/core/hooks.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index 3089c46035..85aa5bdc5f 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -331,7 +331,7 @@ def around(*args, &block) def hooks @hooks ||= HookCollections.new( self, - :around => { :example => AroundHookCollection.new }, + :around => { :example => HookCollection.new }, :before => { :example => HookCollection.new, :context => GroupHookCollection.new }, :after => { :example => HookCollection.new, :context => GroupHookCollection.new } ) @@ -405,8 +405,8 @@ def hook_description # @private class HookCollection - def initialize(hooks=[]) - @hooks = hooks + def initialize + @hooks = [] end attr_reader :hooks @@ -440,12 +440,16 @@ def run_with(example_or_group) end # @private - class AroundHookCollection < HookCollection + class AroundHookCollection + def initialize(hooks) + @hooks = hooks + end + def run_with(example) - return yield if hooks.empty? # exit early to avoid the extra allocation cost of `Example::Procsy` + return yield if @hooks.empty? # exit early to avoid the extra allocation cost of `Example::Procsy` initial_procsy = Example::Procsy.new(example) { yield } - hooks.select { |hook| hook.options_apply?(example) }.inject(initial_procsy) do |procsy, around_hook| + @hooks.select { |hook| hook.options_apply?(example) }.inject(initial_procsy) do |procsy, around_hook| procsy.wrap { around_hook.execute_with(example, procsy) } end.call end From 1cbe818aca340eaa3b260d35714cfe11f7256017 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Dec 2014 00:12:30 -0800 Subject: [PATCH 146/275] Split FilterableItemRepository#add into `append` and `prepend`. This prepares it for use by hook collections, which need both methods. --- lib/rspec/core/configuration.rb | 8 +- lib/rspec/core/metadata_filter.rb | 17 +- .../core/filterable_item_repository_spec.rb | 254 ++++++++++-------- 3 files changed, 152 insertions(+), 127 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index e3e0a2365b..39be90839b 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1050,7 +1050,7 @@ def exclusion_filter # @see #prepend def include(mod, *filters) meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering) - @include_modules.add(mod, meta) + @include_modules.append(mod, meta) end # Tells RSpec to extend example groups with `mod`. Methods defined in @@ -1085,7 +1085,7 @@ def include(mod, *filters) # @see #prepend def extend(mod, *filters) meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering) - @extend_modules.add(mod, meta) + @extend_modules.append(mod, meta) end # Tells RSpec to prepend example groups with `mod`. Methods defined in @@ -1123,7 +1123,7 @@ def extend(mod, *filters) if RSpec::Support::RubyFeatures.module_prepends_supported? def prepend(mod, *filters) meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering) - @prepend_modules.add(mod, meta) + @prepend_modules.append(mod, meta) end end @@ -1407,7 +1407,7 @@ def disable_monkey_patching! # end def define_derived_metadata(*filters, &block) meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering) - @derived_metadata_blocks.add(block, meta) + @derived_metadata_blocks.append(block, meta) end # @private diff --git a/lib/rspec/core/metadata_filter.rb b/lib/rspec/core/metadata_filter.rb index 57ffdfbeae..fe297ff59c 100644 --- a/lib/rspec/core/metadata_filter.rb +++ b/lib/rspec/core/metadata_filter.rb @@ -104,11 +104,14 @@ def initialize(applies_predicate) end end - def add(item, metadata) + def append(item, metadata) @items << [item, metadata] - @applicable_keys.merge(metadata.keys) - @proc_keys.merge(proc_keys_from metadata) - @memoized_lookups.clear + handle_mutation(metadata) + end + + def prepend(item, metadata) + @items.unshift [item, metadata] + handle_mutation(metadata) end def items_for(metadata) @@ -135,6 +138,12 @@ def items_for(metadata) private + def handle_mutation(metadata) + @applicable_keys.merge(metadata.keys) + @proc_keys.merge(proc_keys_from metadata) + @memoized_lookups.clear + end + def applicable_metadata_from(metadata) metadata.select do |key, _value| @applicable_keys.include?(key) diff --git a/spec/rspec/core/filterable_item_repository_spec.rb b/spec/rspec/core/filterable_item_repository_spec.rb index 3f55cdc9ff..fb050d1c18 100644 --- a/spec/rspec/core/filterable_item_repository_spec.rb +++ b/spec/rspec/core/filterable_item_repository_spec.rb @@ -3,7 +3,7 @@ module Core RSpec.describe FilterableItemRepository, "#items_for" do FilterableItem = Struct.new(:name) - let(:repo) { FilterableItemRepository.new(:any?) } + let(:repo) { FilterableItemRepository.new(:any?) } let(:item_1) { FilterableItem.new("Item 1") } let(:item_2) { FilterableItem.new("Item 2") } let(:item_3) { FilterableItem.new("Item 3") } @@ -15,160 +15,176 @@ module Core end end - context "when the repository has items that have no metadata" do - before do - repo.add item_1, {} - repo.add item_2, {} - end + shared_examples_for "adding items to the repository" do |add_method| + describe "adding items using `#{add_method}`" do + define_method :add_item do |*args| + repo.__send__ add_method, *args + end - it "returns those items, regardless of the provided argument" do - expect(repo.items_for({})).to contain_exactly(item_1, item_2) - expect(repo.items_for(:foo => "bar")).to contain_exactly(item_1, item_2) - end - end + context "when the repository has items that have no metadata" do + before do + add_item item_1, {} + add_item item_2, {} + end - context "when the repository has items that have metadata" do - before do - repo.add item_1, :foo => "bar" - repo.add item_2, :slow => true - repo.add item_3, :foo => "bar" - end + it "returns those items, regardless of the provided argument" do + expect(repo.items_for({})).to contain_exactly(item_1, item_2) + expect(repo.items_for(:foo => "bar")).to contain_exactly(item_1, item_2) + end + end - it 'return an empty list when given empty metadata' do - expect(repo.items_for({})).to eq([]) - end + context "when the repository has items that have metadata" do + before do + add_item item_1, :foo => "bar" + add_item item_2, :slow => true + add_item item_3, :foo => "bar" + end - it 'return an empty list when given metadata that matches no items' do - expect(repo.items_for(:slow => false, :foo => "bazz")).to eq([]) - end + it 'return an empty list when given empty metadata' do + expect(repo.items_for({})).to eq([]) + end - it 'returns matching items for the provided metadata' do - expect(repo.items_for(:slow => true)).to contain_exactly(item_2) - expect(repo.items_for(:foo => "bar")).to contain_exactly(item_1, item_3) - expect(repo.items_for(:slow => true, :foo => "bar")).to contain_exactly(item_1, item_2, item_3) - end + it 'return an empty list when given metadata that matches no items' do + expect(repo.items_for(:slow => false, :foo => "bazz")).to eq([]) + end - it 'returns the matching items in the order they were added' do - expect(repo.items_for(:slow => true, :foo => "bar")).to eq [item_1, item_2, item_3] - end + it 'returns matching items for the provided metadata' do + expect(repo.items_for(:slow => true)).to contain_exactly(item_2) + expect(repo.items_for(:foo => "bar")).to contain_exactly(item_1, item_3) + expect(repo.items_for(:slow => true, :foo => "bar")).to contain_exactly(item_1, item_2, item_3) + end - it 'ignores other metadata keys that are not related to the added items' do - expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) - end + it 'returns the matching items in the correct order' do + expect(repo.items_for(:slow => true, :foo => "bar")).to eq items_in_expected_order + end - it 'differentiates between an applicable key being missing and having an explicit `nil` value' do - repo.add item_4, :bar => nil + it 'ignores other metadata keys that are not related to the appended items' do + expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) + end - expect(repo.items_for({})).to eq([]) - expect(repo.items_for(:bar => nil)).to contain_exactly(item_4) - end + it 'differentiates between an applicable key being missing and having an explicit `nil` value' do + add_item item_4, :bar => nil - it 'returns the correct items when they are added after a memoized lookup' do - expect { - repo.add item_4, :slow => true - }.to change { repo.items_for(:slow => true) }. - from(a_collection_containing_exactly(item_2)). - to(a_collection_containing_exactly(item_2, item_4)) - end + expect(repo.items_for({})).to eq([]) + expect(repo.items_for(:bar => nil)).to contain_exactly(item_4) + end - let(:flip_proc) do - return_val = true - Proc.new { return_val.tap { |v| return_val = !v } } - end + it 'returns the correct items when they are appended after a memoized lookup' do + expect { + add_item item_4, :slow => true + }.to change { repo.items_for(:slow => true) }. + from(a_collection_containing_exactly(item_2)). + to(a_collection_containing_exactly(item_2, item_4)) + end - context "with proc values" do - before do - repo.add item_4, { :include_it => flip_proc } - end + let(:flip_proc) do + return_val = true + Proc.new { return_val.tap { |v| return_val = !v } } + end - it 'evaluates the proc each time since the logic can return a different value each time' do - expect(repo.items_for(:include_it => nil)).to contain_exactly(item_4) - expect(repo.items_for(:include_it => nil)).to eq([]) - expect(repo.items_for(:include_it => nil)).to contain_exactly(item_4) - expect(repo.items_for(:include_it => nil)).to eq([]) - end - end + context "with proc values" do + before do + add_item item_4, { :include_it => flip_proc } + end - context "when initialized with the `:any?` predicate" do - let(:repo) { FilterableItemRepository.new(:any?) } + it 'evaluates the proc each time since the logic can return a different value each time' do + expect(repo.items_for(:include_it => nil)).to contain_exactly(item_4) + expect(repo.items_for(:include_it => nil)).to eq([]) + expect(repo.items_for(:include_it => nil)).to contain_exactly(item_4) + expect(repo.items_for(:include_it => nil)).to eq([]) + end + end - it 'matches against multi-entry items when any of the metadata entries match' do - repo.add item_4, :key_1 => "val_1", :key_2 => "val_2" + context "when initialized with the `:any?` predicate" do + let(:repo) { FilterableItemRepository.new(:any?) } - expect(repo.items_for(:key_1 => "val_1")).to contain_exactly(item_4) - expect(repo.items_for(:key_2 => "val_2")).to contain_exactly(item_4) - expect(repo.items_for(:key_1 => "val_1", :key_2 => "val_2")).to contain_exactly(item_4) - end - end + it 'matches against multi-entry items when any of the metadata entries match' do + add_item item_4, :key_1 => "val_1", :key_2 => "val_2" - context "when initialized with the `:all?` predicate" do - let(:repo) { FilterableItemRepository.new(:all?) } + expect(repo.items_for(:key_1 => "val_1")).to contain_exactly(item_4) + expect(repo.items_for(:key_2 => "val_2")).to contain_exactly(item_4) + expect(repo.items_for(:key_1 => "val_1", :key_2 => "val_2")).to contain_exactly(item_4) + end + end - it 'matches against multi-entry items when all of the metadata entries match' do - repo.add item_4, :key_1 => "val_1", :key_2 => "val_2" + context "when initialized with the `:all?` predicate" do + let(:repo) { FilterableItemRepository.new(:all?) } - expect(repo.items_for(:key_1 => "val_1")).to eq([]) - expect(repo.items_for(:key_2 => "val_2")).to eq([]) - expect(repo.items_for(:key_1 => "val_1", :key_2 => "val_2")).to contain_exactly(item_4) - end - end + it 'matches against multi-entry items when all of the metadata entries match' do + add_item item_4, :key_1 => "val_1", :key_2 => "val_2" - describe "performance optimization" do - # NOTE: the specs in this context are potentially brittle because they are - # coupled to the implementation's usage of `MetadataFilter.apply?`. However, - # they demonstrate the perf optimization that was the reason we created - # this class, and thus have value in demonstrating the memoization is working - # properly and in documenting the reason the class exists in the first place. - # Still, if these prove to be brittle in the future, feel free to delete them since - # they are not concerned with externally visible behaviors. + expect(repo.items_for(:key_1 => "val_1")).to eq([]) + expect(repo.items_for(:key_2 => "val_2")).to eq([]) + expect(repo.items_for(:key_1 => "val_1", :key_2 => "val_2")).to contain_exactly(item_4) + end + end - it 'is optimized to check metadata filter application for a given pair of metadata hashes only once' do - # TODO: use mock expectations for this once https://github.com/rspec/rspec-mocks/pull/841 is fixed. - call_counts = track_metadata_filter_apply_calls + describe "performance optimization" do + # NOTE: the specs in this context are potentially brittle because they are + # coupled to the implementation's usage of `MetadataFilter.apply?`. However, + # they demonstrate the perf optimization that was the reason we created + # this class, and thus have value in demonstrating the memoization is working + # properly and in documenting the reason the class exists in the first place. + # Still, if these prove to be brittle in the future, feel free to delete them since + # they are not concerned with externally visible behaviors. - 3.times do - expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) - end + it 'is optimized to check metadata filter application for a given pair of metadata hashes only once' do + # TODO: use mock expectations for this once https://github.com/rspec/rspec-mocks/pull/841 is fixed. + call_counts = track_metadata_filter_apply_calls - expect(call_counts[:slow => true]).to eq(1) - end + 3.times do + expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) + end - it 'ignores extraneous metadata keys when doing memoized lookups' do - # TODO: use mock expectations for this once https://github.com/rspec/rspec-mocks/pull/841 is fixed. - call_counts = track_metadata_filter_apply_calls + expect(call_counts[:slow => true]).to eq(1) + end - expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) - expect(repo.items_for(:slow => true, :other => "bar")).to contain_exactly(item_2) - expect(repo.items_for(:slow => true, :goo => "bazz")).to contain_exactly(item_2) + it 'ignores extraneous metadata keys when doing memoized lookups' do + # TODO: use mock expectations for this once https://github.com/rspec/rspec-mocks/pull/841 is fixed. + call_counts = track_metadata_filter_apply_calls - expect(call_counts[:slow => true]).to eq(1) - end + expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) + expect(repo.items_for(:slow => true, :other => "bar")).to contain_exactly(item_2) + expect(repo.items_for(:slow => true, :goo => "bazz")).to contain_exactly(item_2) - context "when there are some proc keys" do - before do - repo.add item_4, { :include_it => flip_proc } - end + expect(call_counts[:slow => true]).to eq(1) + end - it 'still performs memoization for metadata hashes that lack those keys' do - call_counts = track_metadata_filter_apply_calls + context "when there are some proc keys" do + before do + add_item item_4, { :include_it => flip_proc } + end - expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) - expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) + it 'still performs memoization for metadata hashes that lack those keys' do + call_counts = track_metadata_filter_apply_calls - expect(call_counts[:slow => true]).to eq(1) - end - end + expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) + expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) + + expect(call_counts[:slow => true]).to eq(1) + end + end - def track_metadata_filter_apply_calls - Hash.new(0).tap do |call_counts| - allow(MetadataFilter).to receive(:apply?).and_wrap_original do |original, predicate, item_meta, request_meta| - call_counts[item_meta] += 1 - original.call(predicate, item_meta, request_meta) + def track_metadata_filter_apply_calls + Hash.new(0).tap do |call_counts| + allow(MetadataFilter).to receive(:apply?).and_wrap_original do |original, predicate, item_meta, request_meta| + call_counts[item_meta] += 1 + original.call(predicate, item_meta, request_meta) + end + end end end end end end + + it_behaves_like "adding items to the repository", :append do + let(:items_in_expected_order) { [item_1, item_2, item_3] } + end + + it_behaves_like "adding items to the repository", :prepend do + let(:items_in_expected_order) { [item_3, item_2, item_1] } + end end end end From 207d97949ae51225ccbfc150a8edee0ef590896b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Dec 2014 02:08:29 -0800 Subject: [PATCH 147/275] Rename `items` to `items_and_filters`. It contains both, so the name should reflect that. --- lib/rspec/core/metadata_filter.rb | 14 +++++++------- spec/rspec/core/configuration_spec.rb | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/rspec/core/metadata_filter.rb b/lib/rspec/core/metadata_filter.rb index fe297ff59c..7b4b1abf10 100644 --- a/lib/rspec/core/metadata_filter.rb +++ b/lib/rspec/core/metadata_filter.rb @@ -92,11 +92,11 @@ def silence_metadata_example_group_deprecations # metadata of an example or example group. # @private class FilterableItemRepository - attr_reader :items + attr_reader :items_and_filters def initialize(applies_predicate) @applies_predicate = applies_predicate - @items = [] + @items_and_filters = [] @applicable_keys = Set.new @proc_keys = Set.new @memoized_lookups = Hash.new do |hash, applicable_metadata| @@ -105,12 +105,12 @@ def initialize(applies_predicate) end def append(item, metadata) - @items << [item, metadata] + @items_and_filters << [item, metadata] handle_mutation(metadata) end def prepend(item, metadata) - @items.unshift [item, metadata] + @items_and_filters.unshift [item, metadata] handle_mutation(metadata) end @@ -121,7 +121,7 @@ def items_for(metadata) # description). By filtering to the metadata keys our items care about, # we can ignore extra metadata keys that differ for each example/group. # For example, given `config.include DBHelpers, :db`, example groups - # can be split into those two sets: those that are tagged with `:db` and those + # can be split into these two sets: those that are tagged with `:db` and those # that are not. For each set, this method for the first group in the set is # still an `O(N)` calculation, but all subsequent groups in the set will be # constant time lookups when they call this method. @@ -151,7 +151,7 @@ def applicable_metadata_from(metadata) end def find_items_for(request_meta) - @items.each_with_object([]) do |(item, item_meta), to_return| + @items_and_filters.each_with_object([]) do |(item, item_meta), to_return| to_return << item if item_meta.empty? || MetadataFilter.apply?(@applies_predicate, item_meta, request_meta) end @@ -175,7 +175,7 @@ def applicable_metadata_from(metadata) unless [].respond_to?(:each_with_object) # For 1.8.7 undef find_items_for def find_items_for(request_meta) - @items.inject([]) do |to_return, (item, item_meta)| + @items_and_filters.inject([]) do |to_return, (item, item_meta)| to_return << item if item_meta.empty? || MetadataFilter.apply?(@applies_predicate, item_meta, request_meta) to_return diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index d67af79248..17c032a231 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -881,7 +881,7 @@ def you_call_this_a_blt? it_behaves_like "metadata hash builder" do def metadata_hash(*args) config.include(InstanceLevelMethods, *args) - config.instance_variable_get(:@include_modules).items.last.last + config.instance_variable_get(:@include_modules).items_and_filters.last.last end end @@ -921,7 +921,7 @@ def that_thing it_behaves_like "metadata hash builder" do def metadata_hash(*args) config.extend(ThatThingISentYou, *args) - config.instance_variable_get(:@extend_modules).items.last.last + config.instance_variable_get(:@extend_modules).items_and_filters.last.last end end @@ -948,7 +948,7 @@ def foo it_behaves_like "metadata hash builder" do def metadata_hash(*args) config.prepend(SomeRandomMod, *args) - config.instance_variable_get(:@prepend_modules).items.last.last + config.instance_variable_get(:@prepend_modules).items_and_filters.last.last end end From 4a097ac9770b604e359c69be51f2c4c5aae9d753 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Dec 2014 01:55:00 -0800 Subject: [PATCH 148/275] Refactor hook specs to be less coupled to internal hook structure. --- spec/rspec/core/hooks_spec.rb | 36 +++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/spec/rspec/core/hooks_spec.rb b/spec/rspec/core/hooks_spec.rb index 3766cfc58e..ea8ea05362 100644 --- a/spec/rspec/core/hooks_spec.rb +++ b/spec/rspec/core/hooks_spec.rb @@ -6,6 +6,16 @@ class HooksHost def parent_groups [] end + + def register_hook(type, scope, *args, &block) + block ||= Proc.new { } + __send__(type, scope, *args, &block) + hook_collection_for(type, scope).first + end + + def hook_collection_for(type, scope) + hooks[type][scope].to_ary + end end [:before, :after, :around].each do |type| @@ -15,10 +25,7 @@ def parent_groups describe "##{type}(#{scope})" do it_behaves_like "metadata hash builder" do define_method :metadata_hash do |*args| - instance = HooksHost.new - args.unshift scope if scope - hooks = instance.send(type, *args) {} - hooks.first.options + HooksHost.new.register_hook(type, scope, *args).options end end end @@ -28,19 +35,19 @@ def parent_groups let(:instance) { HooksHost.new } it "defaults to :example scope if no arguments are given" do - hooks = instance.send(type) {} - hook = hooks.first - expect(instance.hooks[type][:example]).to include(hook) + expect { + instance.__send__(type) {} + }.to change { instance.hook_collection_for(type, :example).count }.by(1) end it "defaults to :example scope if the only argument is a metadata hash" do - hooks = instance.send(type, :foo => :bar) {} - hook = hooks.first - expect(instance.hooks[type][:example]).to include(hook) + expect { + instance.__send__(type, :foo => :bar) {} + }.to change { instance.hook_collection_for(type, :example).count }.by(1) end it "raises an error if only metadata symbols are given as arguments" do - expect { instance.send(type, :foo, :bar) {} }.to raise_error(ArgumentError) + expect { instance.__send__(type, :foo, :bar) {} }.to raise_error(ArgumentError) end end end @@ -49,17 +56,14 @@ def parent_groups [:example, :context].each do |scope| describe "##{type}(#{scope.inspect})" do let(:instance) { HooksHost.new } - let!(:hook) do - hooks = instance.send(type, scope) {} - hooks.first - end + let!(:hook) { instance.register_hook(type, scope) } it "does not make #{scope.inspect} a metadata key" do expect(hook.options).to be_empty end it "is scoped to #{scope.inspect}" do - expect(instance.hooks[type][scope]).to include(hook) + expect(instance.hook_collection_for(type, scope)).to include(hook) end it 'does not run when in dry run mode' do From 445877de4114b1be2bbc08f3395129b27e4a5734 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Dec 2014 01:38:55 -0800 Subject: [PATCH 149/275] Switch hooks to use FilterableItemRepository. This allows us to kill off `Hook#options_apply?`. --- lib/rspec/core/hooks.rb | 57 ++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index 85aa5bdc5f..005d45fc7c 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -332,8 +332,8 @@ def hooks @hooks ||= HookCollections.new( self, :around => { :example => HookCollection.new }, - :before => { :example => HookCollection.new, :context => GroupHookCollection.new }, - :after => { :example => HookCollection.new, :context => GroupHookCollection.new } + :before => { :example => HookCollection.new, :context => HookCollection.new }, + :after => { :example => HookCollection.new, :context => HookCollection.new } ) end @@ -347,10 +347,6 @@ def initialize(block, options) @block = block @options = options end - - def options_apply?(example_or_group) - example_or_group.apply?(:all?, options) - end end # @private @@ -406,35 +402,45 @@ def hook_description # @private class HookCollection def initialize - @hooks = [] + @repository = FilterableItemRepository.new(:all?) + @hooks = [] end - attr_reader :hooks - protected :hooks - def to_ary @hooks end def append(hook) - @hooks.push hook + @hooks << hook + @repository.append hook, hook.options end def prepend(hook) @hooks.unshift hook + @repository.prepend hook, hook.options end def include?(hook) @hooks.include?(hook) end - def each - @hooks.each { |h| yield h } + def hooks_for(example_or_group) + # It would be nice to not have to switch on type here, but + # we don't want to define `ExampleGroup#metadata` because then + # `metadata` from within an individual example would return the + # group's metadata but the user would probably expect it to be + # the example's metadata. + metadata = case example_or_group + when ExampleGroup then example_or_group.class.metadata + else example_or_group.metadata + end + + @repository.items_for(metadata) end def run_with(example_or_group) - @hooks.each do |hook| - hook.run(example_or_group) if hook.options_apply?(example_or_group) + hooks_for(example_or_group).each do |hook| + hook.run(example_or_group) end end end @@ -449,19 +455,12 @@ def run_with(example) return yield if @hooks.empty? # exit early to avoid the extra allocation cost of `Example::Procsy` initial_procsy = Example::Procsy.new(example) { yield } - @hooks.select { |hook| hook.options_apply?(example) }.inject(initial_procsy) do |procsy, around_hook| + @hooks.inject(initial_procsy) do |procsy, around_hook| procsy.wrap { around_hook.execute_with(example, procsy) } end.call end end - # @private - class GroupHookCollection < HookCollection - def run_with(group) - hooks.each { |h| h.run(group) } - end - end - # @private class HookCollections def initialize(owner, data) @@ -532,8 +531,14 @@ def run(hook, scope, example_or_group) private def process(host, globals, position, scope) - globals[position][scope].each do |hook| - next unless scope == :example || hook.options_apply?(host) + hooks = globals[position][scope] + hooks = if scope == :example + hooks.to_ary + else + hooks.hooks_for(host) + end + + hooks.each do |hook| next if host.parent_groups.any? { |a| a.hooks[position][scope].include?(hook) } self[position][scope].append hook end @@ -580,7 +585,7 @@ def run_example_hooks_for(example, type, each_method) def run_around_example_hooks_for(example) AroundHookCollection.new(FlatMap.flat_map(@owner.parent_groups) do |a| - a.hooks[:around][:example] + a.hooks[:around][:example].hooks_for(example) end).run_with(example) { yield } end end From 4877eb98e3412d59a56c2baf4f6e11c6940a0645 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Dec 2014 02:15:15 -0800 Subject: [PATCH 150/275] Lazily instantiate HookCollection instances when needed. --- .../allocations/1000_groups_1_example.rb | 60 +++++++++++++++++++ lib/rspec/core/hooks.rb | 15 +++-- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/benchmarks/allocations/1000_groups_1_example.rb b/benchmarks/allocations/1000_groups_1_example.rb index 2ee32592d8..90de144ad3 100644 --- a/benchmarks/allocations/1000_groups_1_example.rb +++ b/benchmarks/allocations/1000_groups_1_example.rb @@ -63,3 +63,63 @@ RSpec::Core::Metadata::ExampleGroupHash 1000 Class 1000 Array 1000 + +.... + +Later, our allocations were: + + class_plus count +--------------------------------------- ----- +String 26000 +Hash 19000 +Array 18000 +Set 10000 +Proc 9000 +RubyVM::Env 9000 +RSpec::Core::Hooks::HookCollection 5000 +RSpec::Core::FilterableItemRepository 5000 +Array 5000 +MatchData 3000 +RSpec::Core::Example::ExecutionResult 2000 +Array 2000 +Module 2000 +Array 2000 +RSpec::Core::Metadata::ExampleGroupHash 1000 +Class 1000 +RSpec::Core::Metadata::ExampleHash 1000 +RSpec::Core::Example 1000 +Array 1000 +Array 1000 +RSpec::Core::Hooks::HookCollections 1000 + + +After changing the hooks implementation to lazily +instantiate `HookCollection` instances, it dropped +our allocations by: + - 8K hashes + - 10K arrays + - 10K sets + - 5K FilterableItemRepository + - 5K HookCollecion + + class_plus count +--------------------------------------- ----- +String 26000 +Hash 11000 +Array 8000 +Proc 5000 +RubyVM::Env 5000 +Array 5000 +MatchData 3000 +Array 2000 +Array 2000 +RSpec::Core::Example::ExecutionResult 2000 +Module 2000 +Array 1000 +RSpec::Core::Metadata::ExampleGroupHash 1000 +Class 1000 +RSpec::Core::Metadata::ExampleHash 1000 +RSpec::Core::Example 1000 +Array 1000 +RSpec::Core::Hooks::HookCollections 1000 + diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index 005d45fc7c..4a844ef7e5 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -329,12 +329,7 @@ def around(*args, &block) # @private # Holds the various registered hooks. def hooks - @hooks ||= HookCollections.new( - self, - :around => { :example => HookCollection.new }, - :before => { :example => HookCollection.new, :context => HookCollection.new }, - :after => { :example => HookCollection.new, :context => HookCollection.new } - ) + @hooks ||= HookCollections.new(self) end private @@ -463,9 +458,13 @@ def run_with(example) # @private class HookCollections - def initialize(owner, data) + def initialize(owner) @owner = owner - @data = data + @data = Hash.new do |type_hash, type| + type_hash[type] = Hash.new do |scope_hash, scope| + scope_hash[scope] = HookCollection.new + end + end end def [](key) From a86f6da043f574d95b94979d4c8e20591e7f1224 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Dec 2014 02:38:15 -0800 Subject: [PATCH 151/275] Remove `Example/ExampleGroup.apply?`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want filtering to primarily go through FilterableItemRepository so it’s better to not even support it directly off of these classes. Ideally, we’d refactor FilterManager to use FilterableItemRepository but they are essentially inverted: FilterManager tracks filtering rules, and then is asked individually about whether or not an example (the “filterable item”) is filtered. --- lib/rspec/core/example.rb | 5 ----- lib/rspec/core/example_group.rb | 5 ----- lib/rspec/core/filter_manager.rb | 7 +++++-- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 1f587a6eee..c532a39614 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -265,11 +265,6 @@ def inspect end end - # @private - def apply?(predicate, filters) - MetadataFilter.apply?(predicate, filters, metadata) - end - # @private # # Used internally to set an exception in an after hook, which diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index 7ffea69f76..72052d9787 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -531,11 +531,6 @@ def self.fail_fast? RSpec.configuration.fail_fast? end - # @private - def self.apply?(predicate, filters) - MetadataFilter.apply?(predicate, filters, metadata) - end - # @private def self.declaration_line_numbers @declaration_line_numbers ||= [metadata[:line_number]] + diff --git a/lib/rspec/core/filter_manager.rb b/lib/rspec/core/filter_manager.rb index c00799622b..6bc1d03c27 100644 --- a/lib/rspec/core/filter_manager.rb +++ b/lib/rspec/core/filter_manager.rb @@ -217,7 +217,8 @@ def use(*args) end def include_example?(example) - @rules.empty? ? true : example.apply?(:any?, @rules) + return true if @rules.empty? + MetadataFilter.apply?(:any?, @rules, example.metadata) end def standalone? @@ -252,7 +253,9 @@ class ExclusionRules < FilterRules }.freeze def include_example?(example) - example.apply?(:any?, @rules) || example.apply?(:any?, CONDITIONAL_FILTERS) + example_meta = example.metadata + return true if MetadataFilter.apply?(:any?, @rules, example_meta) + MetadataFilter.apply?(:any?, CONDITIONAL_FILTERS, example_meta) end end end From 836f3f6ecaecb7a89ee3994c2248fa2d72f8894b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Dec 2014 02:50:19 -0800 Subject: [PATCH 152/275] Remove unnecessary `AroundHookCollection` abstraction. This also saves us an extra object allocation per example. --- lib/rspec/core/hooks.rb | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index 4a844ef7e5..24ff5c4423 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -440,22 +440,6 @@ def run_with(example_or_group) end end - # @private - class AroundHookCollection - def initialize(hooks) - @hooks = hooks - end - - def run_with(example) - return yield if @hooks.empty? # exit early to avoid the extra allocation cost of `Example::Procsy` - - initial_procsy = Example::Procsy.new(example) { yield } - @hooks.inject(initial_procsy) do |procsy, around_hook| - procsy.wrap { around_hook.execute_with(example, procsy) } - end.call - end - end - # @private class HookCollections def initialize(owner) @@ -583,9 +567,16 @@ def run_example_hooks_for(example, type, each_method) end def run_around_example_hooks_for(example) - AroundHookCollection.new(FlatMap.flat_map(@owner.parent_groups) do |a| - a.hooks[:around][:example].hooks_for(example) - end).run_with(example) { yield } + hooks = FlatMap.flat_map(@owner.parent_groups) do |group| + group.hooks[:around][:example].hooks_for(example) + end + + return yield if hooks.empty? # exit early to avoid the extra allocation cost of `Example::Procsy` + + initial_procsy = Example::Procsy.new(example) { yield } + hooks.inject(initial_procsy) do |procsy, around_hook| + procsy.wrap { around_hook.execute_with(example, procsy) } + end.call end end end From 9dfc6c779e1209e07e34e30226a062f9365321cd Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Dec 2014 03:07:07 -0800 Subject: [PATCH 153/275] Remove `[]` indexer for accessing sub-groups of hook collections. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don’t ever need to access all `before` hooks (of all scopes), all `after` hooks or all `around` hooks. The position and scope always go together when accessing a set of hooks so it makes more sense to make one method that takes both. --- lib/rspec/core/hooks.rb | 30 +++++++++++++++--------------- spec/rspec/core/hooks_spec.rb | 10 +++++----- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index 24ff5c4423..2beca590ea 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -451,8 +451,8 @@ def initialize(owner) end end - def [](key) - @data[key] + def hooks_for(position, scope) + @data[position][scope] end def register_globals(host, globals) @@ -464,21 +464,21 @@ def register_globals(host, globals) process(host, globals, :after, :context) end - def register(prepend_or_append, hook, *args, &block) + def register(prepend_or_append, position, *args, &block) scope, options = scope_and_options_from(*args) if scope == :suite # TODO: consider making this an error in RSpec 4. For SemVer reasons, # we are only warning in RSpec 3. - RSpec.warn_with "WARNING: `#{hook}(:suite)` hooks are only supported on " \ + RSpec.warn_with "WARNING: `#{position}(:suite)` hooks are only supported on " \ "the RSpec configuration object. This " \ - "`#{hook}(:suite)` hook, registered on an example " \ + "`#{position}(:suite)` hook, registered on an example " \ "group, will be ignored." return end - self[hook][scope].__send__(prepend_or_append, - HOOK_TYPES[hook][scope].new(block, options)) + hook = HOOK_TYPES[position][scope].new(block, options) + hooks_for(position, scope).__send__(prepend_or_append, hook) end # @private @@ -514,7 +514,7 @@ def run(hook, scope, example_or_group) private def process(host, globals, position, scope) - hooks = globals[position][scope] + hooks = globals.hooks_for(position, scope) hooks = if scope == :example hooks.to_ary else @@ -522,8 +522,8 @@ def process(host, globals, position, scope) end hooks.each do |hook| - next if host.parent_groups.any? { |a| a.hooks[position][scope].include?(hook) } - self[position][scope].append hook + next if host.parent_groups.any? { |a| a.hooks.hooks_for(position, scope).include?(hook) } + hooks_for(position, scope).append hook end end @@ -556,19 +556,19 @@ def normalized_scope_for(scope) SCOPE_ALIASES[scope] || scope end - def run_context_hooks_for(group, type) - self[type][:context].run_with(group) + def run_context_hooks_for(group, position) + hooks_for(position, :context).run_with(group) end - def run_example_hooks_for(example, type, each_method) + def run_example_hooks_for(example, position, each_method) @owner.parent_groups.__send__(each_method) do |group| - group.hooks[type][:example].run_with(example) + group.hooks.hooks_for(position, :example).run_with(example) end end def run_around_example_hooks_for(example) hooks = FlatMap.flat_map(@owner.parent_groups) do |group| - group.hooks[:around][:example].hooks_for(example) + group.hooks.hooks_for(:around, :example).hooks_for(example) end return yield if hooks.empty? # exit early to avoid the extra allocation cost of `Example::Procsy` diff --git a/spec/rspec/core/hooks_spec.rb b/spec/rspec/core/hooks_spec.rb index ea8ea05362..9e7f8450b4 100644 --- a/spec/rspec/core/hooks_spec.rb +++ b/spec/rspec/core/hooks_spec.rb @@ -7,14 +7,14 @@ def parent_groups [] end - def register_hook(type, scope, *args, &block) + def register_hook(position, scope, *args, &block) block ||= Proc.new { } - __send__(type, scope, *args, &block) - hook_collection_for(type, scope).first + __send__(position, scope, *args, &block) + hook_collection_for(position, scope).first end - def hook_collection_for(type, scope) - hooks[type][scope].to_ary + def hook_collection_for(position, scope) + hooks.hooks_for(position, scope).to_ary end end From 61dba19f69ab6b48e041da9fc7fadb7b1cf44a7f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Dec 2014 10:05:41 -0800 Subject: [PATCH 154/275] Rename `to_ary` to `all`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `to_ary` is used for implicit coercion and we don’t want that here. --- lib/rspec/core/hooks.rb | 16 +++++++--------- spec/rspec/core/hooks_spec.rb | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index 2beca590ea..a33b7f4628 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -396,27 +396,25 @@ def hook_description # @private class HookCollection + attr_reader :all + def initialize @repository = FilterableItemRepository.new(:all?) - @hooks = [] - end - - def to_ary - @hooks + @all = [] end def append(hook) - @hooks << hook + @all << hook @repository.append hook, hook.options end def prepend(hook) - @hooks.unshift hook + @all.unshift hook @repository.prepend hook, hook.options end def include?(hook) - @hooks.include?(hook) + @all.include?(hook) end def hooks_for(example_or_group) @@ -516,7 +514,7 @@ def run(hook, scope, example_or_group) def process(host, globals, position, scope) hooks = globals.hooks_for(position, scope) hooks = if scope == :example - hooks.to_ary + hooks.all else hooks.hooks_for(host) end diff --git a/spec/rspec/core/hooks_spec.rb b/spec/rspec/core/hooks_spec.rb index 9e7f8450b4..95ba5bd457 100644 --- a/spec/rspec/core/hooks_spec.rb +++ b/spec/rspec/core/hooks_spec.rb @@ -14,7 +14,7 @@ def register_hook(position, scope, *args, &block) end def hook_collection_for(position, scope) - hooks.hooks_for(position, scope).to_ary + hooks.hooks_for(position, scope).all end end From 062bdfff4cf6989aedcda42e572436aa26e727e9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Dec 2014 02:56:04 -0800 Subject: [PATCH 155/275] Delay instantiation of `HookCollection` until a hook is added. This allows us to avoid the allocation for example groups that do not have hooks. --- .../running_1000_groups_1_example.rb | 27 +++++ lib/rspec/core/hooks.rb | 98 ++++++++++++++----- spec/rspec/core/hooks_spec.rb | 2 +- 3 files changed, 102 insertions(+), 25 deletions(-) diff --git a/benchmarks/allocations/running_1000_groups_1_example.rb b/benchmarks/allocations/running_1000_groups_1_example.rb index f5a82595df..204fb158b2 100644 --- a/benchmarks/allocations/running_1000_groups_1_example.rb +++ b/benchmarks/allocations/running_1000_groups_1_example.rb @@ -72,3 +72,30 @@ Array 391 Array 205 Array 52 + +After yet further optimization (where HookCollection instances are only created when hooks are added), +we've reduced allocations significantly further: + + class_plus count +----------------------------------------------------------------------------------------------- ----- +String 21332 +Array 13412 +Array 6021 +Array 6001 +Array 6001 +Hash 3105 +Array 3004 +Proc 2101 +RubyVM::Env 2061 +Time 2002 +Random 2001 +RSpec::Core::Notifications::GroupNotification 2000 +RSpec::Core::Notifications::ExampleNotification 2000 +Array 1003 +Array 1002 +Array 1002 +RubyVM::InstructionSequence 506 +Array 391 +Array 208 +Array 52 + diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index a33b7f4628..b48439ea9c 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -439,18 +439,24 @@ def run_with(example_or_group) end # @private + # + # This provides the primary API used by other parts of rspec-core. By hiding all + # implementation details behind this facade, it's allowed us to heavily optimize + # this, so that, for example, hook collection objects are only instantiated when + # a hook is added. This allows us to avoid many object allocations for the common + # case of a group having no hooks. + # + # This is only possible because this interface provides a "tell, don't ask"-style + # API, so that callers _tell_ this class what to do with the hooks, rather than + # asking this class for a list of hooks, and then doing something with them. class HookCollections def initialize(owner) @owner = owner - @data = Hash.new do |type_hash, type| - type_hash[type] = Hash.new do |scope_hash, scope| - scope_hash[scope] = HookCollection.new - end - end - end - - def hooks_for(position, scope) - @data[position][scope] + @before_example_hooks = nil + @after_example_hooks = nil + @before_context_hooks = nil + @after_context_hooks = nil + @around_example_hooks = nil end def register_globals(host, globals) @@ -476,20 +482,20 @@ def register(prepend_or_append, position, *args, &block) end hook = HOOK_TYPES[position][scope].new(block, options) - hooks_for(position, scope).__send__(prepend_or_append, hook) + ensure_hooks_initialized_for(position, scope).__send__(prepend_or_append, hook) end # @private # # Runs all of the blocks stored with the hook in the context of the # example. If no example is provided, just calls the hook directly. - def run(hook, scope, example_or_group) + def run(position, scope, example_or_group) return if RSpec.configuration.dry_run? if scope == :context - run_context_hooks_for(example_or_group, hook) + run_owned_hooks_for(position, :context, example_or_group) else - case hook + case position when :before then run_example_hooks_for(example_or_group, :before, :reverse_each) when :after then run_example_hooks_for(example_or_group, :after, :each) when :around then run_around_example_hooks_for(example_or_group) { yield } @@ -509,19 +515,67 @@ def run(hook, scope, example_or_group) HOOK_TYPES[:after][:context] = AfterContextHook + protected + + EMPTY_HOOK_ARRAY = [].freeze + + def matching_hooks_for(position, scope, example_or_group) + hooks_for(position, scope) { return EMPTY_HOOK_ARRAY }.hooks_for(example_or_group) + end + + def all_hooks_for(position, scope) + hooks_for(position, scope) { return EMPTY_HOOK_ARRAY }.all + end + + def run_owned_hooks_for(position, scope, example_or_group) + hooks_for(position, scope) { return nil }.run_with(example_or_group) + end + + def hook_included?(position, scope, hook) + hooks_for(position, scope) { return false }.include?(hook) + end + private + def hooks_for(position, scope) + if position == :before + scope == :example ? @before_example_hooks : @before_context_hooks + elsif position == :after + scope == :example ? @after_example_hooks : @after_context_hooks + else # around + @around_example_hooks + end || yield + end + + def ensure_hooks_initialized_for(position, scope) + if position == :before + if scope == :example + @before_example_hooks ||= HookCollection.new + else + @before_context_hooks ||= HookCollection.new + end + elsif position == :after + if scope == :example + @after_example_hooks ||= HookCollection.new + else + @after_context_hooks ||= HookCollection.new + end + else # around + @around_example_hooks ||= HookCollection.new + end + end + def process(host, globals, position, scope) - hooks = globals.hooks_for(position, scope) hooks = if scope == :example - hooks.all + globals.all_hooks_for(position, scope) else - hooks.hooks_for(host) + globals.matching_hooks_for(position, scope, host) end hooks.each do |hook| - next if host.parent_groups.any? { |a| a.hooks.hooks_for(position, scope).include?(hook) } - hooks_for(position, scope).append hook + # TODO: find a way to avoid this nested linear search. that should allow us to remove `include?`. + next if host.parent_groups.any? { |a| a.hooks.hook_included?(position, scope, hook) } + ensure_hooks_initialized_for(position, scope).append hook end end @@ -554,19 +608,15 @@ def normalized_scope_for(scope) SCOPE_ALIASES[scope] || scope end - def run_context_hooks_for(group, position) - hooks_for(position, :context).run_with(group) - end - def run_example_hooks_for(example, position, each_method) @owner.parent_groups.__send__(each_method) do |group| - group.hooks.hooks_for(position, :example).run_with(example) + group.hooks.run_owned_hooks_for(position, :example, example) end end def run_around_example_hooks_for(example) hooks = FlatMap.flat_map(@owner.parent_groups) do |group| - group.hooks.hooks_for(:around, :example).hooks_for(example) + group.hooks.matching_hooks_for(:around, :example, example) end return yield if hooks.empty? # exit early to avoid the extra allocation cost of `Example::Procsy` diff --git a/spec/rspec/core/hooks_spec.rb b/spec/rspec/core/hooks_spec.rb index 95ba5bd457..d77b6f4b95 100644 --- a/spec/rspec/core/hooks_spec.rb +++ b/spec/rspec/core/hooks_spec.rb @@ -14,7 +14,7 @@ def register_hook(position, scope, *args, &block) end def hook_collection_for(position, scope) - hooks.hooks_for(position, scope).all + hooks.send(:all_hooks_for, position, scope) end end From df92b4a434c61cd500c6f23a6b902d50129f90c7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Dec 2014 21:30:00 -0800 Subject: [PATCH 156/275] Optimize HookCollections#process. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, it had a triple-nested-loop: hooks.each -> parent_groups.any? -> include? We’ve optimized it by flat mapping over the parent group list to get all hooks once, which allows us to subtract them and immediately get the list of hooks to add. It’s simpler, too. This also allows us to remove a few methods that were only used by this logic. --- lib/rspec/core/hooks.rb | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index b48439ea9c..000cb5ca89 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -413,10 +413,6 @@ def prepend(hook) @repository.prepend hook, hook.options end - def include?(hook) - @all.include?(hook) - end - def hooks_for(example_or_group) # It would be nice to not have to switch on type here, but # we don't want to define `ExampleGroup#metadata` because then @@ -531,8 +527,12 @@ def run_owned_hooks_for(position, scope, example_or_group) hooks_for(position, scope) { return nil }.run_with(example_or_group) end - def hook_included?(position, scope, hook) - hooks_for(position, scope) { return false }.include?(hook) + def processable_hooks_for(position, scope, host) + if scope == :example + all_hooks_for(position, scope) + else + matching_hooks_for(position, scope, host) + end end private @@ -566,15 +566,14 @@ def ensure_hooks_initialized_for(position, scope) end def process(host, globals, position, scope) - hooks = if scope == :example - globals.all_hooks_for(position, scope) - else - globals.matching_hooks_for(position, scope, host) - end - - hooks.each do |hook| - # TODO: find a way to avoid this nested linear search. that should allow us to remove `include?`. - next if host.parent_groups.any? { |a| a.hooks.hook_included?(position, scope, hook) } + host_hooks = globals.processable_hooks_for(position, scope, host) + return if host_hooks.empty? + + parent_group_hooks = FlatMap.flat_map(host.parent_groups) do |group| + group.hooks.all_hooks_for(position, scope) + end + + (host_hooks - parent_group_hooks).each do |hook| ensure_hooks_initialized_for(position, scope).append hook end end From c84e5b6c3104568c4179dca745ff8c7229cb2aa6 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Dec 2014 21:55:41 -0800 Subject: [PATCH 157/275] Prefer simple arrays for tracking suite hooks. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HookCollection no longer offers us anything over an array for the purpose of suite hooks since they don’t support metadata filtering, anyway. --- lib/rspec/core/configuration.rb | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index 39be90839b..72de8e9f1b 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -302,6 +302,10 @@ def initialize @include_modules = FilterableItemRepository.new(:any?) @extend_modules = FilterableItemRepository.new(:any?) @prepend_modules = FilterableItemRepository.new(:any?) + + @before_suite_hooks = [] + @after_suite_hooks = [] + @mock_framework = nil @files_or_directories_to_run = [] @color = false @@ -1427,7 +1431,7 @@ def apply_derived_metadata_to(metadata) # @see #after # @see #append_after def before(*args, &block) - handle_suite_hook(args, before_suite_hooks, :append, + handle_suite_hook(args, @before_suite_hooks, :push, Hooks::BeforeHook, block) || super(*args, &block) end alias_method :append_before, :before @@ -1446,7 +1450,7 @@ def before(*args, &block) # @see #after # @see #append_after def prepend_before(*args, &block) - handle_suite_hook(args, before_suite_hooks, :prepend, + handle_suite_hook(args, @before_suite_hooks, :unshift, Hooks::BeforeHook, block) || super(*args, &block) end @@ -1460,7 +1464,7 @@ def prepend_before(*args, &block) # @see #before # @see #prepend_before def after(*args, &block) - handle_suite_hook(args, after_suite_hooks, :prepend, + handle_suite_hook(args, @after_suite_hooks, :unshift, Hooks::AfterHook, block) || super(*args, &block) end alias_method :prepend_after, :after @@ -1479,7 +1483,7 @@ def after(*args, &block) # @see #before # @see #prepend_before def append_after(*args, &block) - handle_suite_hook(args, after_suite_hooks, :append, + handle_suite_hook(args, @after_suite_hooks, :push, Hooks::AfterHook, block) || super(*args, &block) end @@ -1489,10 +1493,10 @@ def with_suite_hooks hook_context = SuiteHookContext.new begin - before_suite_hooks.run_with(hook_context) + run_hooks_with(@before_suite_hooks, hook_context) yield ensure - after_suite_hooks.run_with(hook_context) + run_hooks_with(@after_suite_hooks, hook_context) end end @@ -1514,12 +1518,8 @@ def handle_suite_hook(args, collection, append_or_prepend, hook_type, block) collection.__send__(append_or_prepend, hook_type.new(block, {})) end - def before_suite_hooks - @before_suite_hooks ||= Hooks::HookCollection.new - end - - def after_suite_hooks - @after_suite_hooks ||= Hooks::HookCollection.new + def run_hooks_with(hooks, hook_context) + hooks.each { |h| h.run(hook_context) } end def get_files_to_run(paths) From 6719d0eb5f469097539047e7493aa83cb2829160 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Dec 2014 21:50:37 -0800 Subject: [PATCH 158/275] Remove HookCollection in favor of FilterableItemRepository. This simplifies things, removes an unneeded abstraction and reduces our object allocations further. --- lib/rspec/core/hooks.rb | 74 +++++++++++++---------------------------- 1 file changed, 24 insertions(+), 50 deletions(-) diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index 000cb5ca89..69a6f20b2f 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -394,46 +394,6 @@ def hook_description end end - # @private - class HookCollection - attr_reader :all - - def initialize - @repository = FilterableItemRepository.new(:all?) - @all = [] - end - - def append(hook) - @all << hook - @repository.append hook, hook.options - end - - def prepend(hook) - @all.unshift hook - @repository.prepend hook, hook.options - end - - def hooks_for(example_or_group) - # It would be nice to not have to switch on type here, but - # we don't want to define `ExampleGroup#metadata` because then - # `metadata` from within an individual example would return the - # group's metadata but the user would probably expect it to be - # the example's metadata. - metadata = case example_or_group - when ExampleGroup then example_or_group.class.metadata - else example_or_group.metadata - end - - @repository.items_for(metadata) - end - - def run_with(example_or_group) - hooks_for(example_or_group).each do |hook| - hook.run(example_or_group) - end - end - end - # @private # # This provides the primary API used by other parts of rspec-core. By hiding all @@ -478,7 +438,7 @@ def register(prepend_or_append, position, *args, &block) end hook = HOOK_TYPES[position][scope].new(block, options) - ensure_hooks_initialized_for(position, scope).__send__(prepend_or_append, hook) + ensure_hooks_initialized_for(position, scope).__send__(prepend_or_append, hook, options) end # @private @@ -516,15 +476,29 @@ def run(position, scope, example_or_group) EMPTY_HOOK_ARRAY = [].freeze def matching_hooks_for(position, scope, example_or_group) - hooks_for(position, scope) { return EMPTY_HOOK_ARRAY }.hooks_for(example_or_group) + repository = hooks_for(position, scope) { return EMPTY_HOOK_ARRAY } + + # It would be nice to not have to switch on type here, but + # we don't want to define `ExampleGroup#metadata` because then + # `metadata` from within an individual example would return the + # group's metadata but the user would probably expect it to be + # the example's metadata. + metadata = case example_or_group + when ExampleGroup then example_or_group.class.metadata + else example_or_group.metadata + end + + repository.items_for(metadata) end def all_hooks_for(position, scope) - hooks_for(position, scope) { return EMPTY_HOOK_ARRAY }.all + hooks_for(position, scope) { return EMPTY_HOOK_ARRAY }.items_and_filters.map(&:first) end def run_owned_hooks_for(position, scope, example_or_group) - hooks_for(position, scope) { return nil }.run_with(example_or_group) + matching_hooks_for(position, scope, example_or_group).each do |hook| + hook.run(example_or_group) + end end def processable_hooks_for(position, scope, host) @@ -550,18 +524,18 @@ def hooks_for(position, scope) def ensure_hooks_initialized_for(position, scope) if position == :before if scope == :example - @before_example_hooks ||= HookCollection.new + @before_example_hooks ||= FilterableItemRepository.new(:all?) else - @before_context_hooks ||= HookCollection.new + @before_context_hooks ||= FilterableItemRepository.new(:all?) end elsif position == :after if scope == :example - @after_example_hooks ||= HookCollection.new + @after_example_hooks ||= FilterableItemRepository.new(:all?) else - @after_context_hooks ||= HookCollection.new + @after_context_hooks ||= FilterableItemRepository.new(:all?) end else # around - @around_example_hooks ||= HookCollection.new + @around_example_hooks ||= FilterableItemRepository.new(:all?) end end @@ -574,7 +548,7 @@ def process(host, globals, position, scope) end (host_hooks - parent_group_hooks).each do |hook| - ensure_hooks_initialized_for(position, scope).append hook + ensure_hooks_initialized_for(position, scope).append hook, hook.options end end From 9eb63b095a3412dd614d8ea8e1e1ea0ec7f2b0f3 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 20 Dec 2014 00:23:30 -0800 Subject: [PATCH 159/275] Refactor: store hook repository in a local rather than re-fetch in loop. --- lib/rspec/core/hooks.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index 69a6f20b2f..e5a66b3ac9 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -540,16 +540,16 @@ def ensure_hooks_initialized_for(position, scope) end def process(host, globals, position, scope) - host_hooks = globals.processable_hooks_for(position, scope, host) - return if host_hooks.empty? + hooks_to_process = globals.processable_hooks_for(position, scope, host) + return if hooks_to_process.empty? - parent_group_hooks = FlatMap.flat_map(host.parent_groups) do |group| + hooks_to_process -= FlatMap.flat_map(host.parent_groups) do |group| group.hooks.all_hooks_for(position, scope) end + return if hooks_to_process.empty? - (host_hooks - parent_group_hooks).each do |hook| - ensure_hooks_initialized_for(position, scope).append hook, hook.options - end + repository = ensure_hooks_initialized_for(position, scope) + hooks_to_process.each { |hook| repository.append hook, hook.options } end def scope_and_options_from(*args) From d6b622cb92c1764c0a4cc140d819da4c2b3791d9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 20 Dec 2014 00:27:47 -0800 Subject: [PATCH 160/275] Add totals to allocation output. --- benchmarks/allocations/helper.rb | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/benchmarks/allocations/helper.rb b/benchmarks/allocations/helper.rb index f87541e973..644f233f33 100644 --- a/benchmarks/allocations/helper.rb +++ b/benchmarks/allocations/helper.rb @@ -14,10 +14,17 @@ def benchmark_allocations(burn: 1, min_allocations: 0) end results = stats.allocations(alias_paths: true).group_by(*columns).from_pwd.sort_by_size.to_text + count_regex = /\s+(\d+)\z/ + + total_objects = results.split("\n").map { |line| line[count_regex, 1] }.compact.map { |c| Integer(c) }.inject(0, :+) + filtered = results.split("\n").select do |line| - count = line[/\s+(\d+)\z/, 1] + count = line[count_regex, 1] count.nil? || Integer(count) >= min_allocations - end.join("\n") + end - puts filtered + puts filtered.join("\n") + line_length = filtered.last.length + puts "-" * line_length + puts "Total:#{total_objects.to_s.rjust(line_length - "Total:".length)}" end From 98ae573be3b626172055c317f7da31a5e16c0f51 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 20 Dec 2014 08:11:35 -0800 Subject: [PATCH 161/275] Fewer string allocations. --- lib/rspec/core/metadata.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/core/metadata.rb b/lib/rspec/core/metadata.rb index fe02db8af1..f9a2d7e2c1 100644 --- a/lib/rspec/core/metadata.rb +++ b/lib/rspec/core/metadata.rb @@ -147,7 +147,7 @@ def description_separator(parent_part, child_part) def build_description_from(parent_description=nil, my_description=nil) return parent_description.to_s unless my_description separator = description_separator(parent_description, my_description) - parent_description.to_s + separator + my_description.to_s + (parent_description.to_s + separator) << my_description.to_s end def ensure_valid_user_keys From 0488b8806c49f6d66902141489346a3ad7830557 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 20 Dec 2014 08:15:18 -0800 Subject: [PATCH 162/275] Remove an unneeded `compact` call. This also reduces array allocations, since `compact` would create an extra array. --- lib/rspec/core/metadata.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rspec/core/metadata.rb b/lib/rspec/core/metadata.rb index f9a2d7e2c1..f44b737005 100644 --- a/lib/rspec/core/metadata.rb +++ b/lib/rspec/core/metadata.rb @@ -184,7 +184,8 @@ def self.create(group_metadata, user_metadata, description, block) example_metadata[:shared_group_inclusion_backtrace] = SharedExampleGroupInclusionStackFrame.current_backtrace example_metadata.delete(:parent_example_group) - hash = new(example_metadata, user_metadata, [description].compact, block) + description_args = description.nil? ? [] : [description] + hash = new(example_metadata, user_metadata, description_args, block) hash.populate hash.metadata end From 217fed5ccb863a726c830bbb0cc59fa46a46052a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 20 Dec 2014 08:17:01 -0800 Subject: [PATCH 163/275] Hash#keys.any? creates an extra array compared to Hash#any? --- lib/rspec/core/metadata_filter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/core/metadata_filter.rb b/lib/rspec/core/metadata_filter.rb index 7b4b1abf10..b0f18efa64 100644 --- a/lib/rspec/core/metadata_filter.rb +++ b/lib/rspec/core/metadata_filter.rb @@ -127,7 +127,7 @@ def items_for(metadata) # constant time lookups when they call this method. applicable_metadata = applicable_metadata_from(metadata) - if applicable_metadata.keys.any? { |k| @proc_keys.include?(k) } + if applicable_metadata.any? { |k, _| @proc_keys.include?(k) } # It's unsafe to memoize lookups involving procs (since they can # be non-deterministic), so we skip the memoization in this case. find_items_for(applicable_metadata) From 8e005145927c8cfbdef6c3b1df3cfcd79053eedc Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 20 Dec 2014 08:34:27 -0800 Subject: [PATCH 164/275] =?UTF-8?q?When=20selecting=20a=20hash=20subset,?= =?UTF-8?q?=20it=E2=80=99s=20better=20to=20iterate=20over=20the=20smaller?= =?UTF-8?q?=20key=20list.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We expect `applicable_keys` to be very small — for example, in rspec-rails, it may just contain `:type`. `metadata`, in contrast, typically has at least 9 items. --- lib/rspec/core/metadata_filter.rb | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/lib/rspec/core/metadata_filter.rb b/lib/rspec/core/metadata_filter.rb index b0f18efa64..a13e0044cd 100644 --- a/lib/rspec/core/metadata_filter.rb +++ b/lib/rspec/core/metadata_filter.rb @@ -145,8 +145,9 @@ def handle_mutation(metadata) end def applicable_metadata_from(metadata) - metadata.select do |key, _value| - @applicable_keys.include?(key) + @applicable_keys.inject({}) do |hash, key| + hash[key] = metadata[key] if metadata.key?(key) + hash end end @@ -163,15 +164,6 @@ def proc_keys_from(metadata) end end - if {}.select {} == [] # For 1.8.7 - undef applicable_metadata_from - def applicable_metadata_from(metadata) - Hash[metadata.select do |key, _value| - @applicable_keys.include?(key) - end] - end - end - unless [].respond_to?(:each_with_object) # For 1.8.7 undef find_items_for def find_items_for(request_meta) From 70bc97d7333d93130edf6d71b21d64a5855ae616 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 20 Dec 2014 08:44:30 -0800 Subject: [PATCH 165/275] Use fewer string allocations when creating the example group constant. --- lib/rspec/core/example_group.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index 72052d9787..d143dfa85a 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -647,15 +647,19 @@ def self.base_name_for(group) return "Anonymous" if group.description.empty? # Convert to CamelCase. - name = ' ' + group.description - name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) { Regexp.last_match[1].upcase } + name = ' ' << group.description + name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) do + match = Regexp.last_match[1] + match.upcase! + match + end name.lstrip! # Remove leading whitespace - name.gsub!(/\W/, '') # JRuby, RBX and others don't like non-ascii in const names + name.gsub!(/\W/, ''.freeze) # JRuby, RBX and others don't like non-ascii in const names # Ruby requires first const letter to be A-Z. Use `Nested` # as necessary to enforce that. - name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1') + name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1'.freeze) name end From 9a2d0ec7a2690fcc03c740e5c8bdd74dbc503682 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 20 Dec 2014 08:55:52 -0800 Subject: [PATCH 166/275] Use fewer string allocations in console_code_for. --- lib/rspec/core/formatters/console_codes.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/rspec/core/formatters/console_codes.rb b/lib/rspec/core/formatters/console_codes.rb index 9c2a11a7cc..4f31bc197a 100644 --- a/lib/rspec/core/formatters/console_codes.rb +++ b/lib/rspec/core/formatters/console_codes.rb @@ -22,14 +22,19 @@ module ConsoleCodes module_function + CONFIG_COLORS_TO_METHODS = Configuration.instance_methods.grep(/_color\z/).inject({}) do |hash, method| + hash[method.to_s.sub(/_color\z/, '').to_sym] = method + hash + end + # Fetches the correct code for the supplied symbol, or checks # that a code is valid. Defaults to white (37). # # @param code_or_symbol [Symbol, Fixnum] Symbol or code to check # @return [Fixnum] a console code def console_code_for(code_or_symbol) - if RSpec.configuration.respond_to?(:"#{code_or_symbol}_color") - console_code_for configuration_color(code_or_symbol) + if (config_method = CONFIG_COLORS_TO_METHODS[code_or_symbol]) + console_code_for RSpec.configuration.__send__(config_method) elsif VT100_CODE_VALUES.key?(code_or_symbol) code_or_symbol else @@ -53,11 +58,6 @@ def wrap(text, code_or_symbol) text end end - - # @private - def configuration_color(code) - RSpec.configuration.__send__(:"#{code}_color") - end end end end From c1356c7f5913aab2f4b940f4b38f7d616ce651a1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 20 Dec 2014 08:58:16 -0800 Subject: [PATCH 167/275] Fewer array allocations. --- lib/rspec/core/example.rb | 2 +- lib/rspec/core/example_group.rb | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index c532a39614..2663048b42 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -185,7 +185,7 @@ def run(example_group_instance, reporter) rescue Exception => e set_exception(e) ensure - ExampleGroup.instance_variables_for_example(@example_group_instance).each do |ivar| + ExampleGroup.each_instance_variable_for_example(@example_group_instance) do |ivar| @example_group_instance.instance_variable_set(ivar, nil) end @example_group_instance = nil diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index d143dfa85a..751fad5b6d 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -434,7 +434,7 @@ def self.before_context_ivars # @private def self.store_before_context_ivars(example_group_instance) - instance_variables_for_example(example_group_instance).each do |ivar| + each_instance_variable_for_example(example_group_instance) do |ivar| before_context_ivars[ivar] = example_group_instance.instance_variable_get(ivar) end end @@ -549,14 +549,15 @@ def self.set_ivars(instance, ivars) end if RUBY_VERSION.to_f < 1.9 - # @private - def self.instance_variables_for_example(group) - group.instance_variables - ['@__inspect_output'] - end + INSTANCE_VARIABLE_TO_IGNORE = '@__inspect_output'.freeze else - # @private - def self.instance_variables_for_example(group) - group.instance_variables - [:@__inspect_output] + INSTANCE_VARIABLE_TO_IGNORE = :@__inspect_output + end + + # @private + def self.each_instance_variable_for_example(group) + group.instance_variables.each do |ivar| + yield ivar unless ivar == INSTANCE_VARIABLE_TO_IGNORE end end From f61bb6ff2952a66645a2ade660addcc769f6ddde Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 22 Dec 2014 19:31:28 +1100 Subject: [PATCH 168/275] whitespace cleanup --- benchmarks/allocations/1000_groups_1_example.rb | 1 - benchmarks/allocations/running_1000_groups_1_example.rb | 1 - benchmarks/module_inclusion_filtering.rb | 1 - lib/rspec/core/example_group.rb | 2 +- 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/benchmarks/allocations/1000_groups_1_example.rb b/benchmarks/allocations/1000_groups_1_example.rb index 90de144ad3..756a2e0599 100644 --- a/benchmarks/allocations/1000_groups_1_example.rb +++ b/benchmarks/allocations/1000_groups_1_example.rb @@ -122,4 +122,3 @@ RSpec::Core::Example 1000 Array 1000 RSpec::Core::Hooks::HookCollections 1000 - diff --git a/benchmarks/allocations/running_1000_groups_1_example.rb b/benchmarks/allocations/running_1000_groups_1_example.rb index 204fb158b2..b340f6d1f0 100644 --- a/benchmarks/allocations/running_1000_groups_1_example.rb +++ b/benchmarks/allocations/running_1000_groups_1_example.rb @@ -98,4 +98,3 @@ Array 391 Array 208 Array 52 - diff --git a/benchmarks/module_inclusion_filtering.rb b/benchmarks/module_inclusion_filtering.rb index 121b5d9caa..dc80c50ed7 100644 --- a/benchmarks/module_inclusion_filtering.rb +++ b/benchmarks/module_inclusion_filtering.rb @@ -87,4 +87,3 @@ def self.prepare_implementation(prefix) 822.348 (±57.5%) i/s - 3.081k New memoized search: matching metadata 1.116k (±76.6%) i/s - 3.510k - diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index 751fad5b6d..f04e3b4a70 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -655,7 +655,7 @@ def self.base_name_for(group) match end - name.lstrip! # Remove leading whitespace + name.lstrip! # Remove leading whitespace name.gsub!(/\W/, ''.freeze) # JRuby, RBX and others don't like non-ascii in const names # Ruby requires first const letter to be A-Z. Use `Nested` From 1160685bb559e785b5f3d9fd6db5f2a47571ac6e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 28 Oct 2014 13:31:11 -0700 Subject: [PATCH 169/275] Remove unneeded layer of indirection. --- lib/rspec/core/example_group.rb | 2 +- lib/rspec/core/world.rb | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index f04e3b4a70..87a3d0e882 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -377,7 +377,7 @@ def self.set_it_up(*args, &example_group_block) ) hooks.register_globals(self, RSpec.configuration.hooks) - RSpec.world.configure_group(self) + RSpec.configuration.configure_group(self) end # @private diff --git a/lib/rspec/core/world.rb b/lib/rspec/core/world.rb index 81da405278..73abc127dc 100644 --- a/lib/rspec/core/world.rb +++ b/lib/rspec/core/world.rb @@ -75,11 +75,6 @@ def exclusion_filter @configuration.exclusion_filter end - # @private - def configure_group(group) - @configuration.configure_group(group) - end - # @api private # # Get count of examples to be run. From 38e4ea1b8f9e0912c2e38d3e50036a680d586a6e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 28 Oct 2014 14:30:16 -0700 Subject: [PATCH 170/275] Fix doc string. --- spec/rspec/core/configuration_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index 17c032a231..29681a40cc 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -1512,7 +1512,7 @@ def exclude?(line) expect(group).to be_a(mod) end - it "extends with 'module'" do + it "includes with 'include'" do mod = Module.new group = RSpec.describe("group", :foo => :bar) From d1023ea85085c530aa1e40d06008088b0e3dfd32 Mon Sep 17 00:00:00 2001 From: Tom Schady Date: Tue, 23 Dec 2014 08:10:57 -0600 Subject: [PATCH 171/275] correct specs by swapping right code into right example --- features/metadata/user_defined.feature | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/metadata/user_defined.feature b/features/metadata/user_defined.feature index 2f00f5dc5d..b235035e0b 100644 --- a/features/metadata/user_defined.feature +++ b/features/metadata/user_defined.feature @@ -26,11 +26,11 @@ Feature: User-defined metadata describe 'a sub-group with user-defined metadata', :bar => 12 do it 'has access to the sub-group metadata' do |example| - expect(example.metadata[:foo]).to eq(17) + expect(example.metadata[:bar]).to eq(12) end it 'also has access to metadata defined on parent groups' do |example| - expect(example.metadata[:bar]).to eq(12) + expect(example.metadata[:foo]).to eq(17) end end end From 917a8dbad5762e4590bf0f350c7a209bada8593d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 24 Dec 2014 11:09:07 -0800 Subject: [PATCH 172/275] Fix typo in changelog. [ci skip] --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index c9135d8c30..4cf7cf44dd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -34,7 +34,7 @@ Bug Fixes: * Don't consider expectations from `after` hooks when generating example descriptions. (Myron Marston, #1771) * Don't apply metadata-filtered config hooks to examples in groups - with matching metadata when those example override the parent + with matching metadata when those examples override the parent metadata value to not match. (Myron Marston, #1796) ### 3.1.8 Development From 2ef77a39a45a876087beb87a8a35bc5ae8caaa4e Mon Sep 17 00:00:00 2001 From: Ben Moss Date: Fri, 26 Dec 2014 10:56:50 -0500 Subject: [PATCH 173/275] Fix backtrace features to not be false greens --- .../backtrace_exclusion_patterns.feature | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/features/configuration/backtrace_exclusion_patterns.feature b/features/configuration/backtrace_exclusion_patterns.feature index 3945f0a28a..95fee755b2 100644 --- a/features/configuration/backtrace_exclusion_patterns.feature +++ b/features/configuration/backtrace_exclusion_patterns.feature @@ -38,17 +38,13 @@ Feature: Excluding lines from the backtrace /spec_helper/ ] end - - def foo - "bar" - end """ And a file named "spec/example_spec.rb" with: """ruby require 'spec_helper' RSpec.describe "foo" do it "returns baz" do - expect(foo).to eq("baz") + expect("foo").to eq("baz") end end """ @@ -57,55 +53,65 @@ Feature: Excluding lines from the backtrace And the output should contain "lib/rspec/expectations" Scenario: Appending to `backtrace_exclusion_patterns` - Given a file named "spec/matchers/be_baz_matcher.rb" with: + Given a file named "spec/support/assert_baz.rb" with: """ruby - RSpec::Matchers.define :be_baz do |_| - match do |actual| - actual == "baz" - end + require "support/really_assert_baz" + + def assert_baz(arg) + really_assert_baz(arg) + end + """ + And a file named "spec/support/really_assert_baz.rb" with: + """ruby + def really_assert_baz(arg) + expect(arg).to eq("baz") end """ And a file named "spec/example_spec.rb" with: """ruby + require "support/assert_baz" RSpec.configure do |config| - config.backtrace_exclusion_patterns << /be_baz_matcher/ + config.backtrace_exclusion_patterns << /really/ end RSpec.describe "bar" do it "is baz" do - expect("bar").to be_baz + assert_baz("bar") end end """ When I run `rspec` Then the output should contain "1 example, 1 failure" - But the output should not contain "be_baz_matcher" + And the output should contain "assert_baz" + But the output should not contain "really_assert_baz" And the output should not contain "lib/rspec/expectations" - Scenario: Running `rspec` with the `--backtrace` option - Given a file named "spec/matchers/be_baz_matcher.rb" with: + Scenario: Running `rspec` with `--backtrace` prints unfiltered backtraces + Given a file named "spec/support/custom_helper.rb" with: """ruby - RSpec::Matchers.define :be_baz do |_| - match do |actual| - actual == "baz" - end + def assert_baz(arg) + expect(arg).to eq("baz") end """ And a file named "spec/example_spec.rb" with: """ruby + require "support/custom_helper" + RSpec.configure do |config| - config.backtrace_exclusion_patterns << /be_baz_matcher/ + config.backtrace_exclusion_patterns << /custom_helper/ end RSpec.describe "bar" do it "is baz" do - expect("bar").to be_baz + assert_baz("bar") end end """ When I run `rspec --backtrace` Then the output should contain "1 example, 1 failure" - And the output should not contain "be_baz_matcher" + And the output should contain "spec/support/custom_helper.rb:2:in `assert_baz'" + And the output should contain "lib/rspec/expectations" + And the output should contain "lib/rspec/core" Scenario: Using `filter_gems_from_backtrace` to filter the named gem Given a vendored gem named "my_gem" containing a file named "lib/my_gem.rb" with: From dea984a7cfdb9b964db1c07a72305dae97a0c244 Mon Sep 17 00:00:00 2001 From: Ben Moss Date: Sat, 27 Dec 2014 13:03:38 -0500 Subject: [PATCH 174/275] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 5 +++-- 8 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 34d3c7461c..85ab01e42d 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-17T17:12:36-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 7cb6ad6768..a6b6fba9f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-17T17:12:36-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index f3abe97571..e784692db5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-17T17:12:36-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 8fa1c7323d..8ec4e67346 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-17T17:12:36-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 34830e4765..848756328b 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-17T17:12:36-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index c546ea8b7d..5508da0614 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-17T17:12:36-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 2bc6e80047..dbdd5d3c68 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-17T17:12:36-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 532df312e5..8d1578e990 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-17T17:12:36-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: @@ -52,7 +52,8 @@ fold() { travis_time_start fi - "$@" || status=$? + "$@" + status=$? [ -z "$TRAVIS" ] || travis_time_finish From a5d9b01baae5e1d9f6a3509cabe526ede9db23ba Mon Sep 17 00:00:00 2001 From: Ben Moss Date: Sat, 27 Dec 2014 23:22:02 -0500 Subject: [PATCH 175/275] Fix existing documentation so yard finds it --- lib/rspec/core/configuration.rb | 66 ++++++++++++++++----------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index 72de8e9f1b..f4ee7e7579 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1092,39 +1092,39 @@ def extend(mod, *filters) @extend_modules.append(mod, meta) end - # Tells RSpec to prepend example groups with `mod`. Methods defined in - # `mod` are exposed to examples (not example groups). Use `filters` to - # constrain the groups in which to prepend the module. - # - # Similar to `include`, but module is included before the example group's class - # in the ancestor chain. - # - # @example - # - # module OverrideMod - # def override_me - # "overridden" - # end - # end - # - # RSpec.configure do |config| - # config.prepend(OverrideMod, :method => :prepend) - # end - # - # describe "overriding example's class", :method => :prepend do - # it "finds the user" do - # self.class.class_eval do - # def override_me - # end - # end - # override_me # => "overridden" - # # ... - # end - # end - # - # @see #include - # @see #extend if RSpec::Support::RubyFeatures.module_prepends_supported? + # Tells RSpec to prepend example groups with `mod`. Methods defined in + # `mod` are exposed to examples (not example groups). Use `filters` to + # constrain the groups in which to prepend the module. + # + # Similar to `include`, but module is included before the example group's class + # in the ancestor chain. + # + # @example + # + # module OverrideMod + # def override_me + # "overridden" + # end + # end + # + # RSpec.configure do |config| + # config.prepend(OverrideMod, :method => :prepend) + # end + # + # describe "overriding example's class", :method => :prepend do + # it "finds the user" do + # self.class.class_eval do + # def override_me + # end + # end + # override_me # => "overridden" + # # ... + # end + # end + # + # @see #include + # @see #extend def prepend(mod, *filters) meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering) @prepend_modules.append(mod, meta) @@ -1153,8 +1153,8 @@ def safe_include(mod, host) host.__send__(:include, mod) unless host < mod end - # @private if RSpec::Support::RubyFeatures.module_prepends_supported? + # @private def safe_prepend(mod, host) host.__send__(:prepend, mod) unless host < mod end From 1d5020b6e8af6c9b5c350185cd2fcef202f36ec4 Mon Sep 17 00:00:00 2001 From: Ben Moss Date: Sat, 27 Dec 2014 23:22:27 -0500 Subject: [PATCH 176/275] Add missing private doc markers --- lib/rspec/core/example_group.rb | 2 ++ lib/rspec/core/formatters/console_codes.rb | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index 87a3d0e882..b13e7722ed 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -549,8 +549,10 @@ def self.set_ivars(instance, ivars) end if RUBY_VERSION.to_f < 1.9 + # @private INSTANCE_VARIABLE_TO_IGNORE = '@__inspect_output'.freeze else + # @private INSTANCE_VARIABLE_TO_IGNORE = :@__inspect_output end diff --git a/lib/rspec/core/formatters/console_codes.rb b/lib/rspec/core/formatters/console_codes.rb index 4f31bc197a..1419dbc075 100644 --- a/lib/rspec/core/formatters/console_codes.rb +++ b/lib/rspec/core/formatters/console_codes.rb @@ -22,6 +22,7 @@ module ConsoleCodes module_function + # @private CONFIG_COLORS_TO_METHODS = Configuration.instance_methods.grep(/_color\z/).inject({}) do |hash, method| hash[method.to_s.sub(/_color\z/, '').to_sym] = method hash From 1094306457088e198d419959c5be95ef253608af Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 27 Dec 2014 01:21:18 -0800 Subject: [PATCH 177/275] Relax nokigiri constraint since 1.5.2 can't install on MRI 2.2 --- rspec-core.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rspec-core.gemspec b/rspec-core.gemspec index 08b258849e..010fc7b37e 100644 --- a/rspec-core.gemspec +++ b/rspec-core.gemspec @@ -44,7 +44,7 @@ Gem::Specification.new do |s| s.add_development_dependency "minitest", "~> 5.3" s.add_development_dependency "aruba", "~> 0.5" - s.add_development_dependency "nokogiri", "1.5.2" + s.add_development_dependency "nokogiri", "~> 1.5" s.add_development_dependency "coderay", "~> 1.0.9" s.add_development_dependency "mocha", "~> 0.13.0" From fd300946c86e85b414066857a1e94b8617316864 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 28 Dec 2014 16:18:25 -0800 Subject: [PATCH 178/275] Address MRI 2.2 warnings (possible reference to past scope). From https://github.com/ruby/ruby/commit/f5f6218a23fdbe45d1a80c202a131f02c3eef0ce. It causes 280 warnings in our spec suite :(. The fix is to change the names of variables and/or `let` definitions so they do not clash. See https://bugs.ruby-lang.org/issues/10661 for more discussion. --- spec/rspec/core/backtrace_formatter_spec.rb | 14 +-- spec/rspec/core/configuration_options_spec.rb | 46 +++---- spec/rspec/core/configuration_spec.rb | 12 +- spec/rspec/core/example_group_spec.rb | 118 +++++++++--------- spec/rspec/core/example_spec.rb | 2 +- .../formatters/base_text_formatter_spec.rb | 12 +- spec/rspec/core/hooks_filtering_spec.rb | 6 +- spec/rspec/core/memoized_helpers_spec.rb | 24 ++-- spec/rspec/core/metadata_spec.rb | 18 +-- spec/rspec/core/option_parser_spec.rb | 3 +- spec/rspec/core/rake_task_spec.rb | 4 +- spec/rspec/core/shared_example_group_spec.rb | 16 +-- spec/rspec/core/world_spec.rb | 6 +- 13 files changed, 135 insertions(+), 146 deletions(-) diff --git a/spec/rspec/core/backtrace_formatter_spec.rb b/spec/rspec/core/backtrace_formatter_spec.rb index 972eaca15b..133f121719 100644 --- a/spec/rspec/core/backtrace_formatter_spec.rb +++ b/spec/rspec/core/backtrace_formatter_spec.rb @@ -136,11 +136,11 @@ def make_backtrace_formatter(exclusion_patterns=nil, inclusion_patterns=nil) end it "includes full backtrace" do - expect(BacktraceFormatter.new.format_backtrace(backtrace).take(4)).to eq backtrace + expect(BacktraceFormatter.new.format_backtrace(self.backtrace).take(4)).to eq self.backtrace end it "adds a message explaining everything was filtered" do - expect(BacktraceFormatter.new.format_backtrace(backtrace).drop(4).join).to match(/Showing full backtrace/) + expect(BacktraceFormatter.new.format_backtrace(self.backtrace).drop(4).join).to match(/Showing full backtrace/) end end @@ -208,18 +208,18 @@ def make_backtrace_formatter(exclusion_patterns=nil, inclusion_patterns=nil) let(:formatter) { BacktraceFormatter.new } it "trims current working directory" do - expect(formatter.__send__(:backtrace_line, File.expand_path(__FILE__))).to eq("./spec/rspec/core/backtrace_formatter_spec.rb") + expect(self.formatter.__send__(:backtrace_line, File.expand_path(__FILE__))).to eq("./spec/rspec/core/backtrace_formatter_spec.rb") end it "preserves the original line" do original_line = File.expand_path(__FILE__) - formatter.__send__(:backtrace_line, original_line) + self.formatter.__send__(:backtrace_line, original_line) expect(original_line).to eq(File.expand_path(__FILE__)) end it "deals gracefully with a security error" do safely do - formatter.__send__(:backtrace_line, __FILE__) + self.formatter.__send__(:backtrace_line, __FILE__) # on some rubies, this doesn't raise a SecurityError; this test just # assures that if it *does* raise an error, the error is caught inside end @@ -237,7 +237,7 @@ def make_backtrace_formatter(exclusion_patterns=nil, inclusion_patterns=nil) let(:line) { File.join(Dir.getwd, "foo.rb:13") } it 'does not exclude lines from files in the current directory' do - expect(make_backtrace_formatter.exclude? line).to be false + expect(make_backtrace_formatter.exclude? self.line).to be false end context "with inclusion_patterns cleared" do @@ -245,7 +245,7 @@ def make_backtrace_formatter(exclusion_patterns=nil, inclusion_patterns=nil) formatter = make_backtrace_formatter formatter.inclusion_patterns.clear - expect(formatter.exclude? line).to be true + expect(formatter.exclude? self.line).to be true end end end diff --git a/spec/rspec/core/configuration_options_spec.rb b/spec/rspec/core/configuration_options_spec.rb index 6367b1dfb1..f0a3794f36 100644 --- a/spec/rspec/core/configuration_options_spec.rb +++ b/spec/rspec/core/configuration_options_spec.rb @@ -22,46 +22,45 @@ it "configures deprecation_stream before loading requires (since required files may issue deprecations)" do opts = config_options_object(*%w[--deprecation-out path/to/log --require foo]) - config = instance_double(RSpec::Core::Configuration).as_null_object + configuration = instance_double(RSpec::Core::Configuration).as_null_object - opts.configure(config) + opts.configure(configuration) - expect(config).to have_received(:force).with(:deprecation_stream => "path/to/log").ordered - expect(config).to have_received(:requires=).ordered + expect(configuration).to have_received(:force).with(:deprecation_stream => "path/to/log").ordered + expect(configuration).to have_received(:requires=).ordered end it "configures deprecation_stream before configuring filter_manager" do opts = config_options_object(*%w[--deprecation-out path/to/log --tag foo]) filter_manager = instance_double(RSpec::Core::FilterManager).as_null_object - config = instance_double(RSpec::Core::Configuration, :filter_manager => filter_manager).as_null_object + configuration = instance_double(RSpec::Core::Configuration, :filter_manager => filter_manager).as_null_object - opts.configure(config) + opts.configure(configuration) - expect(config).to have_received(:force).with(:deprecation_stream => "path/to/log").ordered + expect(configuration).to have_received(:force).with(:deprecation_stream => "path/to/log").ordered expect(filter_manager).to have_received(:include).with(:foo => true).ordered end it "configures deprecation_stream before configuring formatters" do opts = config_options_object(*%w[--deprecation-out path/to/log --format doc]) - config = instance_double(RSpec::Core::Configuration).as_null_object + configuration = instance_double(RSpec::Core::Configuration).as_null_object - opts.configure(config) + opts.configure(configuration) - expect(config).to have_received(:force).with(:deprecation_stream => "path/to/log").ordered - expect(config).to have_received(:add_formatter).ordered + expect(configuration).to have_received(:force).with(:deprecation_stream => "path/to/log").ordered + expect(configuration).to have_received(:add_formatter).ordered end it "sends libs before requires" do opts = config_options_object(*%w[--require a/path -I a/lib]) - config = double("config").as_null_object - expect(config).to receive(:libs=).ordered - expect(config).to receive(:requires=).ordered - opts.configure(config) + configuration = double("config").as_null_object + expect(configuration).to receive(:libs=).ordered + expect(configuration).to receive(:requires=).ordered + opts.configure(configuration) end it "loads requires before loading specs" do opts = config_options_object(*%w[-rspec_helper]) - config = RSpec::Core::Configuration.new expect(config).to receive(:requires=).ordered expect(config).to receive(:get_files_to_run).ordered opts.configure(config) @@ -70,15 +69,14 @@ it "sets up load path and requires before formatter" do opts = config_options_object(*%w[--require a/path -f a/formatter]) - config = double("config").as_null_object - expect(config).to receive(:requires=).ordered - expect(config).to receive(:add_formatter).ordered - opts.configure(config) + configuration = double("config").as_null_object + expect(configuration).to receive(:requires=).ordered + expect(configuration).to receive(:add_formatter).ordered + opts.configure(configuration) end it "sets default_path before loading specs" do opts = config_options_object(*%w[--default-path spec]) - config = RSpec::Core::Configuration.new expect(config).to receive(:force).with(:default_path => 'spec').ordered expect(config).to receive(:get_files_to_run).ordered opts.configure(config) @@ -87,7 +85,6 @@ it "sets `files_or_directories_to_run` before `requires` so users can check `files_to_run` in a spec_helper loaded by `--require`" do opts = config_options_object(*%w[--require spec_helper]) - config = RSpec::Core::Configuration.new expect(config).to receive(:files_or_directories_to_run=).ordered expect(config).to receive(:requires=).ordered opts.configure(config) @@ -95,7 +92,6 @@ it "sets default_path before `files_or_directories_to_run` since it relies on it" do opts = config_options_object(*%w[--default-path spec]) - config = RSpec::Core::Configuration.new expect(config).to receive(:force).with(:default_path => 'spec').ordered expect(config).to receive(:files_or_directories_to_run=).ordered opts.configure(config) @@ -104,7 +100,6 @@ it 'configures the seed (via `order`) before requires so that required files can use the configured seed' do opts = config_options_object(*%w[ --seed 1234 --require spec_helper ]) - config = RSpec::Core::Configuration.new expect(config).to receive(:force).with(:order => "rand:1234").ordered expect(config).to receive(:requires=).ordered @@ -114,7 +109,6 @@ { "pattern" => :pattern, "exclude-pattern" => :exclude_pattern }.each do |flag, attr| it "sets #{attr} before `requires` so users can check `files_to_run` in a `spec_helper` loaded by `--require`" do opts = config_options_object(*%W[--require spec_helpe --#{flag} **/*.spec]) - config = RSpec::Core::Configuration.new expect(config).to receive(:force).with(attr => '**/*.spec').ordered expect(config).to receive(:requires=).ordered opts.configure(config) @@ -135,7 +129,6 @@ it "forces color" do opts = config_options_object(*%w[--color]) - config = RSpec::Core::Configuration.new expect(config).to receive(:force).with(:color => true) opts.configure(config) end @@ -151,7 +144,6 @@ ].each do |cli_option, cli_value, config_key, config_value| it "forces #{config_key}" do opts = config_options_object(cli_option, cli_value) - config = RSpec::Core::Configuration.new expect(config).to receive(:force) do |pair| expect(pair.keys.first).to eq(config_key) expect(pair.values.first).to eq(config_value) diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index 29681a40cc..bf2f360e5d 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -1158,10 +1158,10 @@ def metadata_hash(*args) it 'remembers changes' do legacy_formatter = Class.new - config = RSpec.configuration - config.default_formatter = legacy_formatter - config.reporter - expect(config.default_formatter).to eq(legacy_formatter) + configuration = RSpec.configuration + configuration.default_formatter = legacy_formatter + configuration.reporter + expect(configuration.default_formatter).to eq(legacy_formatter) end end @@ -1270,7 +1270,6 @@ def metadata_hash(*args) describe "#backtrace_exclusion_patterns=" do it "actually receives the new filter values" do - config = Configuration.new config.backtrace_exclusion_patterns = [/.*/] expect(config.backtrace_formatter.exclude? "this").to be_truthy end @@ -1290,7 +1289,6 @@ def metadata_hash(*args) describe "#backtrace_exclusion_patterns" do it "can be appended to" do - config = Configuration.new config.backtrace_exclusion_patterns << /.*/ expect(config.backtrace_formatter.exclude? "this").to be_truthy end @@ -1367,7 +1365,7 @@ def exclude?(line) group_bar_value = example_bar_value = nil RSpec.describe "Group", :foo do - group_bar_value = metadata[:bar] + group_bar_value = self.metadata[:bar] example_bar_value = example("ex", :foo).metadata[:bar] end diff --git a/spec/rspec/core/example_group_spec.rb b/spec/rspec/core/example_group_spec.rb index 4a5dc8d9d8..0118ce1e79 100644 --- a/spec/rspec/core/example_group_spec.rb +++ b/spec/rspec/core/example_group_spec.rb @@ -186,19 +186,19 @@ def metadata_hash(*args) before do RSpec.configuration.register_ordering(:global, &:reverse) - allow(group).to receive(:warn) + allow(self.group).to receive(:warn) end it 'falls back to the global ordering' do - group.run - expect(run_order).to eq([:ex_2, :ex_1]) + self.group.run + expect(self.run_order).to eq([:ex_2, :ex_1]) end it 'prints a warning so users are notified of their mistake' do warning = nil - allow(group).to receive(:warn) { |msg| warning = msg } + allow(self.group).to receive(:warn) { |msg| warning = msg } - group.run + self.group.run expect(warning).to match(/unrecognized/) expect(warning).to match(/#{File.basename __FILE__}:#{definition_line}/) @@ -319,14 +319,14 @@ def ascending_numbers describe "filtering" do let(:world) { World.new } - before { allow(RSpec).to receive_messages(:world => world) } + before { allow(RSpec).to receive_messages(:world => self.world) } shared_examples "matching filters" do context "inclusion" do before do filter_manager = FilterManager.new filter_manager.include filter_metadata - allow(world).to receive_messages(:filter_manager => filter_manager) + allow(self.world).to receive_messages(:filter_manager => filter_manager) end it "includes examples in groups matching filter" do @@ -352,7 +352,7 @@ def ascending_numbers before do filter_manager = FilterManager.new filter_manager.exclude filter_metadata - allow(world).to receive_messages(:filter_manager => filter_manager) + allow(self.world).to receive_messages(:filter_manager => filter_manager) end it "excludes examples in groups matching filter" do @@ -444,7 +444,7 @@ def ascending_numbers context "with no filters" do it "returns all" do group = RSpec.describe - allow(group).to receive(:world) { world } + allow(group).to receive(:world) { self.world } example = group.example("does something") expect(group.filtered_examples).to eq([example]) end @@ -454,9 +454,9 @@ def ascending_numbers it "returns none" do filter_manager = FilterManager.new filter_manager.include :awesome => false - allow(world).to receive_messages(:filter_manager => filter_manager) + allow(self.world).to receive_messages(:filter_manager => filter_manager) group = RSpec.describe - allow(group).to receive(:world) { world } + allow(group).to receive(:world) { self.world } group.example("does something") expect(group.filtered_examples).to eq([]) end @@ -907,19 +907,19 @@ def define_and_run_group(define_outer_example = false) end it "allows the example to pass" do - group.run - example = group.examples.first + self.group.run + example = self.group.examples.first expect(example.execution_result.status).to eq(:passed) end it "rescues any error(s) and prints them out" do expect(RSpec.configuration.reporter).to receive(:message).with(/An error in an after\(:all\) hook/) expect(RSpec.configuration.reporter).to receive(:message).with(/A different hook raising an error/) - group.run + self.group.run end it "still runs both after blocks" do - group.run + self.group.run expect(hooks_run).to eq [:two,:one] end end @@ -929,13 +929,13 @@ def define_and_run_group(define_outer_example = false) let(:group) { RSpec.describe { pending { fail } } } it "generates a pending example" do - group.run - expect(group.examples.first).to be_pending + self.group.run + expect(self.group.examples.first).to be_pending end it "sets the pending message" do - group.run - expect(group.examples.first.execution_result.pending_message).to eq(RSpec::Core::Pending::NO_REASON_GIVEN) + self.group.run + expect(self.group.examples.first.execution_result.pending_message).to eq(RSpec::Core::Pending::NO_REASON_GIVEN) end it 'sets the backtrace to the example definition so it can be located by the user' do @@ -964,13 +964,13 @@ def define_and_run_group(define_outer_example = false) } } it "generates a pending example" do - group.run - expect(group.examples.first).to be_pending + self.group.run + expect(self.group.examples.first).to be_pending end it "sets the pending message" do - group.run - expect(group.examples.first.execution_result.pending_message).to eq(RSpec::Core::Pending::NO_REASON_GIVEN) + self.group.run + expect(self.group.examples.first.execution_result.pending_message).to eq(RSpec::Core::Pending::NO_REASON_GIVEN) end end @@ -980,13 +980,13 @@ def define_and_run_group(define_outer_example = false) } } it "generates a pending example" do - group.run - expect(group.examples.first).to be_pending + self.group.run + expect(self.group.examples.first).to be_pending end it "sets the pending message" do - group.run - expect(group.examples.first.execution_result.pending_message).to eq("not done") + self.group.run + expect(self.group.examples.first.execution_result.pending_message).to eq("not done") end end @@ -994,13 +994,13 @@ def define_and_run_group(define_outer_example = false) let(:group) { RSpec.describe { skip("skip this") { } } } it "generates a skipped example" do - group.run - expect(group.examples.first).to be_skipped + self.group.run + expect(self.group.examples.first).to be_skipped end it "sets the pending message" do - group.run - expect(group.examples.first.execution_result.pending_message).to eq(RSpec::Core::Pending::NO_REASON_GIVEN) + self.group.run + expect(self.group.examples.first.execution_result.pending_message).to eq(RSpec::Core::Pending::NO_REASON_GIVEN) end end @@ -1010,13 +1010,13 @@ def define_and_run_group(define_outer_example = false) } } it "generates a skipped example" do - group.run - expect(group.examples.first).to be_skipped + self.group.run + expect(self.group.examples.first).to be_skipped end it "sets the pending message" do - group.run - expect(group.examples.first.execution_result.pending_message).to eq(RSpec::Core::Pending::NO_REASON_GIVEN) + self.group.run + expect(self.group.examples.first.execution_result.pending_message).to eq(RSpec::Core::Pending::NO_REASON_GIVEN) end end @@ -1026,13 +1026,13 @@ def define_and_run_group(define_outer_example = false) } } it "generates a skipped example" do - group.run - expect(group.examples.first).to be_skipped + self.group.run + expect(self.group.examples.first).to be_skipped end it "sets the pending message" do - group.run - expect(group.examples.first.execution_result.pending_message).to eq('not done') + self.group.run + expect(self.group.examples.first.execution_result.pending_message).to eq('not done') end end @@ -1043,13 +1043,13 @@ def define_and_run_group(define_outer_example = false) }} it "generates a skipped example" do - group.run - expect(group.examples.first).to be_skipped + self.group.run + expect(self.group.examples.first).to be_skipped end it "sets the pending message" do - group.run - expect(group.examples.first.execution_result.pending_message).to eq("Temporarily skipped with #{method_name}") + self.group.run + expect(self.group.examples.first.execution_result.pending_message).to eq("Temporarily skipped with #{method_name}") end end end @@ -1305,19 +1305,19 @@ def extract_execution_results(group) it "does not run examples after the failed example" do examples_run = [] - group.example('example 1') { examples_run << self } - group.example('example 2') { examples_run << self; fail; } - group.example('example 3') { examples_run << self } + self.group.example('example 1') { examples_run << self } + self.group.example('example 2') { examples_run << self; fail; } + self.group.example('example 3') { examples_run << self } - group.run + self.group.run expect(examples_run.length).to eq(2) end it "sets RSpec.world.wants_to_quit flag if encountering an exception in before(:all)" do - group.before(:all) { raise "error in before all" } - group.example("equality") { expect(1).to eq(2) } - expect(group.run).to be_falsey + self.group.before(:all) { raise "error in before all" } + self.group.example("equality") { expect(1).to eq(2) } + expect(self.group.run).to be_falsey expect(RSpec.world.wants_to_quit).to be_truthy end end @@ -1332,19 +1332,19 @@ def extract_execution_results(group) it "returns without starting the group" do expect(reporter).not_to receive(:example_group_started) - group.run(reporter) + self.group.run(reporter) end context "at top level" do it "purges remaining groups" do expect(RSpec.world).to receive(:clear_remaining_example_groups) - group.run(reporter) + self.group.run(reporter) end end context "in a nested group" do it "does not purge remaining groups" do - nested_group = group.describe + nested_group = self.group.describe expect(RSpec.world).not_to receive(:clear_remaining_example_groups) nested_group.run(reporter) end @@ -1407,31 +1407,31 @@ def extract_execution_results(group) describe "##{name}" do let(:group) { RSpec.describe } before do - group.shared_examples "named this" do + self.group.shared_examples "named this" do example("does something") {} end end it "includes the named examples" do - group.send(name, "named this") - expect(group.examples.first.description).to eq("does something") + self.group.send(name, "named this") + expect(self.group.examples.first.description).to eq("does something") end it "raises a helpful error message when shared content is not found" do expect do - group.send(name, "shared stuff") + self.group.send(name, "shared stuff") end.to raise_error(ArgumentError, /Could not find .* "shared stuff"/) end it "leaves RSpec's thread metadata unchanged" do expect { - group.send(name, "named this") + self.group.send(name, "named this") }.to avoid_changing(RSpec, :thread_local_metadata) end it "leaves RSpec's thread metadata unchanged, even when an error occurs during evaluation" do expect { - group.send(name, "named this") do + self.group.send(name, "named this") do raise "boom" end }.to raise_error("boom").and avoid_changing(RSpec, :thread_local_metadata) diff --git a/spec/rspec/core/example_spec.rb b/spec/rspec/core/example_spec.rb index e8fb371dfe..d254741fd7 100644 --- a/spec/rspec/core/example_spec.rb +++ b/spec/rspec/core/example_spec.rb @@ -713,8 +713,8 @@ def expect_pending_result(example) ex = nil RSpec.describe do + let(:dbl) { double } ex = example do - dbl = double expect(dbl).to receive(:foo) end end.run diff --git a/spec/rspec/core/formatters/base_text_formatter_spec.rb b/spec/rspec/core/formatters/base_text_formatter_spec.rb index a64eceba71..73a3fbf593 100644 --- a/spec/rspec/core/formatters/base_text_formatter_spec.rb +++ b/spec/rspec/core/formatters/base_text_formatter_spec.rb @@ -6,9 +6,9 @@ context "when closing the formatter", :isolated_directory => true do it 'does not close an already closed output stream' do - output = File.new("./output_to_close", "w") - formatter = described_class.new(output) - output.close + output_to_close = File.new("./output_to_close", "w") + formatter = described_class.new(output_to_close) + output_to_close.close expect { formatter.close(RSpec::Core::Notifications::NullNotification) }.not_to raise_error end @@ -31,12 +31,12 @@ end it "includes command to re-run each failed example" do - group = RSpec.describe("example group") do + example_group = RSpec.describe("example group") do it("fails") { fail } end line = __LINE__ - 2 - group.run(reporter) - examples = group.examples + example_group.run(reporter) + examples = example_group.examples send_notification :dump_summary, summary_notification(1, examples, examples, [], 0) expect(output.string).to include("rspec #{RSpec::Core::Metadata::relative_path("#{__FILE__}:#{line}")} # example group fails") end diff --git a/spec/rspec/core/hooks_filtering_spec.rb b/spec/rspec/core/hooks_filtering_spec.rb index cce42897ec..d35ef38272 100644 --- a/spec/rspec/core/hooks_filtering_spec.rb +++ b/spec/rspec/core/hooks_filtering_spec.rb @@ -133,7 +133,7 @@ module RSpec::Core let(:each_filters) { [] } let(:all_filters) { [] } - let(:group) do + let(:example_group) do md = example_metadata RSpec.describe do it("example", md) { } @@ -155,7 +155,7 @@ def filters c.after(:all, :foo => :bar) { af << "after all in config"} end - group.run + example_group.run end describe 'an example with matching metadata' do @@ -178,7 +178,7 @@ def filters let(:example_metadata) { { :foo => :bazz } } it "does not run any of the hooks" do - expect(filters).to be_empty + expect(self.filters).to be_empty end end end diff --git a/spec/rspec/core/memoized_helpers_spec.rb b/spec/rspec/core/memoized_helpers_spec.rb index 981debc309..7c9ad11105 100644 --- a/spec/rspec/core/memoized_helpers_spec.rb +++ b/spec/rspec/core/memoized_helpers_spec.rb @@ -3,10 +3,10 @@ module RSpec::Core before(:each) { RSpec.configuration.configure_expectation_framework } def subject_value_for(describe_arg, &block) - group = RSpec.describe(describe_arg, &block) + example_group = RSpec.describe(describe_arg, &block) subject_value = nil - group.example { subject_value = subject } - group.run + example_group.example { subject_value = subject } + example_group.run subject_value end @@ -88,10 +88,10 @@ def subject_value_for(describe_arg, &block) example_yielded_to_subject = nil example_yielded_to_example = nil - group = RSpec.describe - group.subject { |e| example_yielded_to_subject = e } - group.example { |e| subject; example_yielded_to_example = e } - group.run + example_group = RSpec.describe + example_group.subject { |e| example_yielded_to_subject = e } + example_group.example { |e| subject; example_yielded_to_example = e } + example_group.run expect(example_yielded_to_subject).to eq example_yielded_to_example end @@ -111,18 +111,18 @@ def working_with?(double) [false, nil].each do |falsy_value| context "with a value of #{falsy_value.inspect}" do it "is evaluated once per example" do - group = RSpec.describe(Array) - group.before do + example_group = RSpec.describe(Array) + example_group.before do expect(Object).to receive(:this_question?).once.and_return(falsy_value) end - group.subject do + example_group.subject do Object.this_question? end - group.example do + example_group.example do subject subject end - expect(group.run).to be_truthy, "expected subject block to be evaluated only once" + expect(example_group.run).to be_truthy, "expected subject block to be evaluated only once" end end end diff --git a/spec/rspec/core/metadata_spec.rb b/spec/rspec/core/metadata_spec.rb index 7b7fe8fe4b..f454e3eb9b 100644 --- a/spec/rspec/core/metadata_spec.rb +++ b/spec/rspec/core/metadata_spec.rb @@ -70,9 +70,9 @@ def metadata_for(*args) RSpec::Matchers.define :have_value do |value| chain(:for) { |key| @key = key } - match do |metadata| - expect(metadata.fetch(@key)).to eq(value) - expect(metadata[@key]).to eq(value) + match do |meta| + expect(meta.fetch(@key)).to eq(value) + expect(meta[@key]).to eq(value) end end @@ -128,15 +128,15 @@ def metadata_for(*args) end it 'does not include example-group specific keys' do - metadata = nil + meta = nil RSpec.describe "group" do context "nested" do - metadata = example("foo").metadata + meta = example("foo").metadata end end - expect(metadata.keys).not_to include(:parent_example_group) + expect(meta.keys).not_to include(:parent_example_group) end end @@ -615,10 +615,10 @@ def value_for(*args) end it 'allows integration libraries like VCR to infer a fixture name from the example description by walking up nesting structure' do - fixture_name_for = lambda do |metadata| - description = metadata[:description] + fixture_name_for = lambda do |meta| + description = meta[:description] - if example_group = metadata[:example_group] + if example_group = meta[:example_group] [fixture_name_for[example_group], description].join('/') else description diff --git a/spec/rspec/core/option_parser_spec.rb b/spec/rspec/core/option_parser_spec.rb index 738b621db9..e735d93fe7 100644 --- a/spec/rspec/core/option_parser_spec.rb +++ b/spec/rspec/core/option_parser_spec.rb @@ -77,9 +77,8 @@ def generate_help_text %w[--out -o].each do |option| describe option do - let(:options) { Parser.parse([option, 'out.txt']) } - it "sets the output stream for the formatter" do + options = Parser.parse([option, 'out.txt']) expect(options[:formatters].last).to eq(['progress', 'out.txt']) end diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index fc2d57c828..71876e963a 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -23,11 +23,11 @@ def spec_command context "with args passed to the rake task" do it "correctly passes along task arguments" do - task = RakeTask.new(:rake_task_args, :files) do |t, args| + the_task = RakeTask.new(:rake_task_args, :files) do |t, args| expect(args[:files]).to eq "first_spec.rb" end - expect(task).to receive(:run_task) { true } + expect(the_task).to receive(:run_task) { true } expect(Rake.application.invoke_task("rake_task_args[first_spec.rb]")).to be_truthy end end diff --git a/spec/rspec/core/shared_example_group_spec.rb b/spec/rspec/core/shared_example_group_spec.rb index 3e28bcf39a..ead01dead8 100644 --- a/spec/rspec/core/shared_example_group_spec.rb +++ b/spec/rspec/core/shared_example_group_spec.rb @@ -26,13 +26,13 @@ module Core end RSpec::Matchers.define :have_example_descriptions do |*descriptions| - match do |group| - group.examples.map(&:description) == descriptions + match do |example_group| + example_group.examples.map(&:description) == descriptions end - failure_message do |group| - actual = group.examples.map(&:description) - "expected #{group.name} to have descriptions: #{descriptions.inspect} but had #{actual.inspect}" + failure_message do |example_group| + actual = example_group.examples.map(&:description) + "expected #{example_group.name} to have descriptions: #{descriptions.inspect} but had #{actual.inspect}" end end @@ -75,7 +75,7 @@ module Core it 'generates a named (rather than anonymous) module' do define_shared_group("shared behaviors", :include_it) { } - group = RSpec.describe("Group", :include_it) { } + example_group = RSpec.describe("Group", :include_it) { } anonymous_module_regex = /#/ expect(Module.new.inspect).to match(anonymous_module_regex) @@ -86,8 +86,8 @@ module Core )).and exclude(a_string_matching(anonymous_module_regex)) ) - expect(group.ancestors.map(&:inspect)).to include_a_named_rather_than_anonymous_module - expect(group.ancestors.map(&:to_s)).to include_a_named_rather_than_anonymous_module + expect(example_group.ancestors.map(&:inspect)).to include_a_named_rather_than_anonymous_module + expect(example_group.ancestors.map(&:to_s)).to include_a_named_rather_than_anonymous_module end ["name", :name, ExampleModule, ExampleClass].each do |object| diff --git a/spec/rspec/core/world_spec.rb b/spec/rspec/core/world_spec.rb index 0bda0ebe89..a837a13c05 100644 --- a/spec/rspec/core/world_spec.rb +++ b/spec/rspec/core/world_spec.rb @@ -17,9 +17,9 @@ module RSpec::Core describe "#example_groups" do it "contains all registered example groups" do - group = RSpec.describe("group"){} - world.register(group) - expect(world.example_groups).to include(group) + example_group = RSpec.describe("group") {} + world.register(example_group) + expect(world.example_groups).to include(example_group) end end From 5178c699afaa613070001c1bbf23730010ddbc4e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 28 Dec 2014 16:46:05 -0800 Subject: [PATCH 179/275] Mathn is deprecated in Ruby 2.2, so skip those specs. --- spec/support/mathn_integration_support.rb | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/spec/support/mathn_integration_support.rb b/spec/support/mathn_integration_support.rb index 2c8833d391..be6cba7a8b 100644 --- a/spec/support/mathn_integration_support.rb +++ b/spec/support/mathn_integration_support.rb @@ -3,10 +3,16 @@ module MathnIntegrationSupport include RSpec::Support::InSubProcess - def with_mathn_loaded - in_sub_process do - require 'mathn' - yield + if RUBY_VERSION.to_f >= 2.2 + def with_mathn_loaded + skip "lib/mathn.rb is deprecated in Ruby 2.2" + end + else + def with_mathn_loaded + in_sub_process do + require 'mathn' + yield + end end end end From b04697c6ae0be9418497078d900bd9caab6c2312 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 28 Dec 2014 17:15:49 -0800 Subject: [PATCH 180/275] Ruby 1.8 and 1.9.2 still need nokogiri 1.5.2. --- rspec-core.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rspec-core.gemspec b/rspec-core.gemspec index 010fc7b37e..44d4b4edb9 100644 --- a/rspec-core.gemspec +++ b/rspec-core.gemspec @@ -44,7 +44,7 @@ Gem::Specification.new do |s| s.add_development_dependency "minitest", "~> 5.3" s.add_development_dependency "aruba", "~> 0.5" - s.add_development_dependency "nokogiri", "~> 1.5" + s.add_development_dependency "nokogiri", (RUBY_VERSION < '1.9.3' ? "1.5.2" : "~> 1.5") s.add_development_dependency "coderay", "~> 1.0.9" s.add_development_dependency "mocha", "~> 0.13.0" From ca90e3342f4e7f4e19e89a3978dd49088f7c23f6 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 28 Dec 2014 17:28:16 -0800 Subject: [PATCH 181/275] Test::Unit is not bundled in ruby 2.2. --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index 6e7ae838b0..7726d771cc 100644 --- a/Gemfile +++ b/Gemfile @@ -30,5 +30,6 @@ end gem 'simplecov', '~> 0.8' gem 'rubocop', "~> 0.23.0", :platform => [:ruby_19, :ruby_20, :ruby_21] +gem 'test-unit', '~> 3.0' if RUBY_VERSION.to_f >= 2.2 eval File.read('Gemfile-custom') if File.exist?('Gemfile-custom') From 13c6674f8021ab7f00dc70559871169d2595cc12 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 28 Dec 2014 23:05:49 -0800 Subject: [PATCH 182/275] Provide friendly errors when users call RSpec APIs from the wrong context. --- Changelog.md | 7 +++++ lib/rspec/core/example_group.rb | 33 ++++++++++++++++++++++ spec/rspec/core/example_group_spec.rb | 40 +++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/Changelog.md b/Changelog.md index 4cf7cf44dd..819d15a0ff 100644 --- a/Changelog.md +++ b/Changelog.md @@ -25,6 +25,13 @@ Enhancements: * Emit warnings when `:suite` hooks are registered on an example group (where it has always been ignored) or are registered with metadata (which has always been ignored). (Myron Marston, #1805) +* Provide a friendly error message when users call RSpec example group + APIs (e.g. `context`, `describe`, `it`, `let`, `before`, etc) from + within an example where those APIs are unavailable. (Myron Marston, #1819) +* Provide a friendly error message when users call RSpec example + APIs (e.g. `expect`, `double`, `stub_const`, etc) from + within an example group where those APIs are unavailable. + (Myron Marston, #1819) Bug Fixes: diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index b13e7722ed..4f79698889 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -571,6 +571,39 @@ def initialize(inspect_output=nil) def inspect "#<#{self.class} #{@__inspect_output}>" end + + # Raised when an RSpec API is called in the wrong scope, such as `before` + # being called from within an example rather than from within an example + # group block. + WrongScopeError = Class.new(NoMethodError) + + def self.method_missing(name, *args) + if method_defined?(name) + raise WrongScopeError, + "`#{name}` is not available on an example group (e.g. a " \ + "`describe` or `context` block). It is only available from " \ + "within individual examples (e.g. `it` blocks) or from " \ + "constructs that run in the scope of an example (e.g. " \ + "`before`, `let`, etc)." + end + + super + end + private_class_method :method_missing + + private + + def method_missing(name, *args) + if self.class.respond_to?(name) + raise WrongScopeError, + "`#{name}` is not available from within an example (e.g. an " \ + "`it` block) or from constructs that run in the scope of an " \ + "example (e.g. `before`, `let`, etc). It is only available " \ + "on an example group (e.g. a `describe` or `context` block)." + end + + super + end end # @private diff --git a/spec/rspec/core/example_group_spec.rb b/spec/rspec/core/example_group_spec.rb index 4a5dc8d9d8..ae58979d87 100644 --- a/spec/rspec/core/example_group_spec.rb +++ b/spec/rspec/core/example_group_spec.rb @@ -9,6 +9,46 @@ def metadata_hash(*args) end end + %w[ expect double spy ].each do |method| + context "when calling `#{method}`, an example API, on an example group" do + it "tells the user they are in the wrong scope for that API" do + expect { + RSpec.describe { __send__(method, "foo") } + }.to raise_error(ExampleGroup::WrongScopeError) + end + end + end + + %w[ describe context let before it it_behaves_like ].each do |method| + context "when calling `#{method}`, an example group API, from within an example" do + it "tells the user they are in the wrong scope for that API" do + ex = nil + + RSpec.describe do + ex = example { __send__(method, "foo") } + end.run + + expect(ex).to fail_with(ExampleGroup::WrongScopeError) + end + end + end + + it "surfaces NameError from an example group for other missing APIs, like normal" do + expect { + RSpec.describe { foobar } + }.to raise_error(NameError, /foobar/) + end + + it "surfaces NameError from an example for other missing APIs, like normal" do + ex = nil + + RSpec.describe do + ex = example { foobar } + end.run + + expect(ex).to fail_with(NameError) + end + context "when RSpec.configuration.format_docstrings is set to a block" do it "formats the description with that block" do RSpec.configuration.format_docstrings { |s| s.upcase } From 52dd81489d8c763d64391643ce1d8a731c270766 Mon Sep 17 00:00:00 2001 From: Jonathan Rochkind Date: Mon, 29 Dec 2014 17:34:15 -0500 Subject: [PATCH 183/275] for expect_with :minitest, skip method still works. Fixes #1820 --- .../configure_expectation_framework.feature | 6 +++++- lib/rspec/core/minitest_assertions_adapter.rb | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/features/expectation_framework_integration/configure_expectation_framework.feature b/features/expectation_framework_integration/configure_expectation_framework.feature index 9d0ce6236c..6a28436aca 100644 --- a/features/expectation_framework_integration/configure_expectation_framework.feature +++ b/features/expectation_framework_integration/configure_expectation_framework.feature @@ -89,6 +89,10 @@ Feature: configure expectation framework it "is empty (intentional failure)" do assert_empty [1], "errantly expected [1] to be empty" end + + it "marks pending for skip method" do + skip "intentionally" + end end """ When I run `rspec -b example_spec.rb` @@ -97,7 +101,7 @@ Feature: configure expectation framework MiniT|test::Assertion: errantly expected \[1\] to be empty """ - And the output should contain "3 examples, 1 failure" + And the output should contain "4 examples, 1 failure, 1 pending" And the output should not contain "Warning: you should require 'minitest/autorun' instead." Scenario: Configure rspec/expectations AND test/unit assertions diff --git a/lib/rspec/core/minitest_assertions_adapter.rb b/lib/rspec/core/minitest_assertions_adapter.rb index d0f436b355..943b6ab182 100644 --- a/lib/rspec/core/minitest_assertions_adapter.rb +++ b/lib/rspec/core/minitest_assertions_adapter.rb @@ -23,6 +23,13 @@ module MinitestAssertionsAdapter def assertions @assertions ||= 0 end + + RSPEC_SKIP_IMPLEMENTATION = ::RSpec::Core::Pending.instance_method(:skip) + # Minitest::Assertions has it's own `skip`, we need to make sure + # RSpec::Core::Pending#skip is used instead. + def skip(*args) + RSPEC_SKIP_IMPLEMENTATION.bind(self).call(*args) + end end end end From 4917ab1a3d9dd2a9d94a45d509ce58f8b10b0c4a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 29 Dec 2014 15:16:37 -0800 Subject: [PATCH 184/275] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 6 ++++-- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 85ab01e42d..c97db9cefb 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. +# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index a6b6fba9f3..4617231632 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. +# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -37,5 +37,7 @@ matrix: - rvm: jruby-head - rvm: ruby-head - rvm: rbx - - rvm: 2.2 + # These two are temporary until https://github.com/travis-ci/travis-ci/issues/3067 is solved. + - rvm: jruby-18mode + - rvm: jruby fast_finish: true diff --git a/appveyor.yml b/appveyor.yml index e784692db5..f61ade0489 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. +# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 8ec4e67346..a5c20d550a 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. +# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 848756328b..db2c87644d 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. +# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 5508da0614..2a5e5e75b9 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. +# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index dbdd5d3c68..3bc820b1bc 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. +# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 8d1578e990..cc330d3ac7 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-27T13:03:38-05:00 from the rspec-dev repo. +# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 5a1d1dc0f5c0f8354f9b2baad7c4baf6e7dc7c0e Mon Sep 17 00:00:00 2001 From: Jonathan Rochkind Date: Tue, 30 Dec 2014 07:40:02 -0500 Subject: [PATCH 185/275] expect_with :minitest, skip via include --- lib/rspec/core/minitest_assertions_adapter.rb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/rspec/core/minitest_assertions_adapter.rb b/lib/rspec/core/minitest_assertions_adapter.rb index 943b6ab182..25db7514a2 100644 --- a/lib/rspec/core/minitest_assertions_adapter.rb +++ b/lib/rspec/core/minitest_assertions_adapter.rb @@ -13,6 +13,9 @@ module Core # @private module MinitestAssertionsAdapter include ::Minitest::Assertions + # Need to forcefully include Pending after Minitest::Assertions + # to make sure our own #skip method beats Minitest's. + include ::RSpec::Core::Pending # Minitest 5.x requires this accessor to be available. See # https://github.com/seattlerb/minitest/blob/38f0a5fcbd9c37c3f80a3eaad4ba84d3fc9947a0/lib/minitest/assertions.rb#L8 @@ -23,13 +26,6 @@ module MinitestAssertionsAdapter def assertions @assertions ||= 0 end - - RSPEC_SKIP_IMPLEMENTATION = ::RSpec::Core::Pending.instance_method(:skip) - # Minitest::Assertions has it's own `skip`, we need to make sure - # RSpec::Core::Pending#skip is used instead. - def skip(*args) - RSPEC_SKIP_IMPLEMENTATION.bind(self).call(*args) - end end end end From a83668c7396590ba849ac187b4c07113914c6753 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 30 Dec 2014 11:15:40 -0800 Subject: [PATCH 186/275] Changelog for #1822. --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 819d15a0ff..515edd4d35 100644 --- a/Changelog.md +++ b/Changelog.md @@ -43,6 +43,8 @@ Bug Fixes: * Don't apply metadata-filtered config hooks to examples in groups with matching metadata when those examples override the parent metadata value to not match. (Myron Marston, #1796) +* Fix `config.expect_with :minitest` so that `skip` uses RSpec's + implementation rather than Minitest's. (Jonathan Rochkind, #1822) ### 3.1.8 Development From eb89cbe336da6d5e70329e3c9d6ff66209efd7f7 Mon Sep 17 00:00:00 2001 From: Aaron Kromer Date: Tue, 30 Dec 2014 15:47:02 -0500 Subject: [PATCH 187/275] Create only one example group alias per name. It's possible to register the same alias multiple times. The last alias defined, including it's metadata, will be used. This prevents the aliases from being defined multiple times on `RSpec` and the other main objects. This also prevents a `NameError` when the second duplicate alias is undefined when the DSL is no longer globally exposed. Fix #1824 --- lib/rspec/core/dsl.rb | 2 ++ spec/rspec/core/dsl_spec.rb | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/rspec/core/dsl.rb b/lib/rspec/core/dsl.rb index 8b21fac4e2..8548eae78c 100644 --- a/lib/rspec/core/dsl.rb +++ b/lib/rspec/core/dsl.rb @@ -35,6 +35,8 @@ def self.exposed_globally? # @private def self.expose_example_group_alias(name) + return if example_group_aliases.include?(name) + example_group_aliases << name (class << RSpec; self; end).__send__(:define_method, name) do |*args, &example_group_block| diff --git a/spec/rspec/core/dsl_spec.rb b/spec/rspec/core/dsl_spec.rb index df820cd860..cb204f7a8a 100644 --- a/spec/rspec/core/dsl_spec.rb +++ b/spec/rspec/core/dsl_spec.rb @@ -79,6 +79,27 @@ def changing_expose_dsl_globally end end end + + context "when adding duplicate aliases" do + it "only a single alias is created" do + in_sub_process do + RSpec.configuration.alias_example_group_to(:detail) + RSpec.configuration.alias_example_group_to(:detail) + expect(RSpec::Core::DSL.example_group_aliases.count(:detail)).to eq(1) + end + end + + it "does not undefine the alias multiple times", :issue => 1824 do + in_sub_process do + RSpec.configuration.expose_dsl_globally = true + RSpec.configuration.alias_example_group_to(:detail) + RSpec.configuration.alias_example_group_to(:detail) + + expect { + RSpec.configuration.expose_dsl_globally = false + }.not_to raise_error + end + end + end end end - From 3ed1f424f99e3146c2bbc72cfd7da07c982d0709 Mon Sep 17 00:00:00 2001 From: Aaron Kromer Date: Tue, 30 Dec 2014 16:28:58 -0500 Subject: [PATCH 188/275] Note fix in the Changelog. [ci skip] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 515edd4d35..c9db058e93 100644 --- a/Changelog.md +++ b/Changelog.md @@ -45,6 +45,8 @@ Bug Fixes: metadata value to not match. (Myron Marston, #1796) * Fix `config.expect_with :minitest` so that `skip` uses RSpec's implementation rather than Minitest's. (Jonathan Rochkind, #1822) +* Fix `NameError` caused when duplicate example group aliases are defined and + the DSL is not globally exposed. (Aaron Kromer, #1825) ### 3.1.8 Development From 81da012a152d59e432159b93fdd7f48424d642f2 Mon Sep 17 00:00:00 2001 From: tyler-ball Date: Tue, 16 Dec 2014 14:48:57 -0800 Subject: [PATCH 189/275] Refactoring Sandboxing to be in RSpec support Adding full yardoc coverage --- lib/rspec/core.rb | 6 +++ lib/rspec/core/example_group.rb | 2 +- lib/rspec/core/reporter.rb | 10 +++++ lib/rspec/core/sandbox.rb | 37 +++++++++++++++++++ spec/rspec/core/example_spec.rb | 2 +- spec/spec_helper.rb | 4 -- spec/support/formatter_support.rb | 27 +++++--------- spec/support/sandboxing.rb | 61 +++++++++---------------------- 8 files changed, 82 insertions(+), 67 deletions(-) create mode 100644 lib/rspec/core/sandbox.rb diff --git a/lib/rspec/core.rb b/lib/rspec/core.rb index 4b2d45d612..77d93705e8 100644 --- a/lib/rspec/core.rb +++ b/lib/rspec/core.rb @@ -43,6 +43,12 @@ module RSpec extend RSpec::Core::Warnings + class << self + # Setters for shared global objects + # @api private + attr_writer :configuration, :world + end + # Used to ensure examples get reloaded and user configuration gets reset to # defaults between multiple runs in the same process. # diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index 4f79698889..d7b3a28c57 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -462,7 +462,7 @@ def self.run_after_context_hooks(example_group_instance) end # Runs all the examples in this group. - def self.run(reporter) + def self.run(reporter=RSpec::Core::NullReporter.new) if RSpec.world.wants_to_quit RSpec.world.clear_remaining_example_groups if top_level? return diff --git a/lib/rspec/core/reporter.rb b/lib/rspec/core/reporter.rb index a6866d333b..da910164c1 100644 --- a/lib/rspec/core/reporter.rb +++ b/lib/rspec/core/reporter.rb @@ -159,4 +159,14 @@ def seed_used? @configuration.seed && @configuration.seed_used? end end + + # @private + # # Used in place of a {Reporter} for situations where we don't want reporting output. + class NullReporter + private + + def method_missing(_method, *_args, &_block) + # ignore + end + end end diff --git a/lib/rspec/core/sandbox.rb b/lib/rspec/core/sandbox.rb new file mode 100644 index 0000000000..3094f3b3f6 --- /dev/null +++ b/lib/rspec/core/sandbox.rb @@ -0,0 +1,37 @@ +module RSpec + module Core + # A sandbox isolates the enclosed code into an environment that looks 'new' + # meaning globally accessed objects are reset for the duration of the + # sandbox. + module Sandbox + # Execute a provided block with RSpec global objects (configuration, + # world) reset. This is used to test RSpec with RSpec. + # + # When calling this the configuration is passed into the provided block. + # Use this to set custom configs for your sandboxed examples. + # + # ``` + # Sandbox.sandboxed do |config| + # config.before(:context) { RSpec.current_example = nil } + # end + # ``` + def self.sandboxed + orig_config = RSpec.configuration + orig_world = RSpec.world + orig_example = RSpec.current_example + + new_config = RSpec::Core::Configuration.new + new_world = RSpec::Core::World.new(new_config) + + RSpec.configuration = new_config + RSpec.world = new_world + + yield new_config + ensure + RSpec.configuration = orig_config + RSpec.world = orig_world + RSpec.current_example = orig_example + end + end + end +end diff --git a/spec/rspec/core/example_spec.rb b/spec/rspec/core/example_spec.rb index d254741fd7..afb8aef29a 100644 --- a/spec/rspec/core/example_spec.rb +++ b/spec/rspec/core/example_spec.rb @@ -637,7 +637,7 @@ def expect_pending_result(example) end context "in before(:all)" do - it "sets each example to pending" do + it "sets each example to skipped" do group = RSpec.describe do before(:all) { skip("not done"); fail } example {} diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 16c45fad7f..4dc0ef73e9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -9,10 +9,6 @@ module ArubaLoader end end -class << RSpec - attr_writer :configuration, :world -end - if RUBY_PLATFORM == 'java' # Works around https://jira.codehaus.org/browse/JRUBY-5678 require 'fileutils' diff --git a/spec/support/formatter_support.rb b/spec/support/formatter_support.rb index 50df710528..f41d9b8adb 100644 --- a/spec/support/formatter_support.rb +++ b/spec/support/formatter_support.rb @@ -54,13 +54,10 @@ def expected_summary_output_for_example_specs | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:31 - | # ./spec/support/sandboxing.rb:31:in `run' | # ./spec/support/formatter_support.rb:13:in `run_example_specs_with_formatter' - | # ./spec/support/sandboxing.rb:2 - | # ./spec/support/sandboxing.rb:36:in `instance_exec' - | # ./spec/support/sandboxing.rb:36:in `sandboxed' - | # ./spec/support/sandboxing.rb:35:in `sandboxed' - | # ./spec/support/sandboxing.rb:2 + | # ./spec/support/sandboxing.rb:18 + | # ./spec/support/sandboxing.rb:18 + | # ./spec/support/sandboxing.rb:9 | | 3) a failing spec with odd backtraces fails with a backtrace that has no file | Failure/Error: Unable to find matching line from backtrace @@ -110,13 +107,10 @@ def expected_summary_output_for_example_specs | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:31:in `block (2 levels) in ' - | # ./spec/support/sandboxing.rb:31:in `run' | # ./spec/support/formatter_support.rb:13:in `run_example_specs_with_formatter' - | # ./spec/support/sandboxing.rb:2:in `block (3 levels) in ' - | # ./spec/support/sandboxing.rb:36:in `instance_exec' - | # ./spec/support/sandboxing.rb:36:in `block in sandboxed' - | # ./spec/support/sandboxing.rb:35:in `sandboxed' - | # ./spec/support/sandboxing.rb:2:in `block (2 levels) in ' + | # ./spec/support/sandboxing.rb:18:in `block (4 levels) in ' + | # ./spec/support/sandboxing.rb:18:in `block (3 levels) in ' + | # ./spec/support/sandboxing.rb:9:in `block (2 levels) in ' | | 3) a failing spec with odd backtraces fails with a backtrace that has no file | Failure/Error: ERB.new("<%= raise 'foo' %>").result @@ -124,13 +118,10 @@ def expected_summary_output_for_example_specs | foo | # (erb):1:in `
' | # ./spec/rspec/core/resources/formatter_specs.rb:39:in `block (2 levels) in ' - | # ./spec/support/sandboxing.rb:31:in `run' | # ./spec/support/formatter_support.rb:13:in `run_example_specs_with_formatter' - | # ./spec/support/sandboxing.rb:2:in `block (3 levels) in ' - | # ./spec/support/sandboxing.rb:36:in `instance_exec' - | # ./spec/support/sandboxing.rb:36:in `block in sandboxed' - | # ./spec/support/sandboxing.rb:35:in `sandboxed' - | # ./spec/support/sandboxing.rb:2:in `block (2 levels) in ' + | # ./spec/support/sandboxing.rb:18:in `block (4 levels) in ' + | # ./spec/support/sandboxing.rb:18:in `block (3 levels) in ' + | # ./spec/support/sandboxing.rb:9:in `block (2 levels) in ' | | 4) a failing spec with odd backtraces fails with a backtrace containing an erb file | Failure/Error: Unable to find matching line from backtrace diff --git a/spec/support/sandboxing.rb b/spec/support/sandboxing.rb index 1eeb33461f..26b93e2373 100644 --- a/spec/support/sandboxing.rb +++ b/spec/support/sandboxing.rb @@ -1,50 +1,25 @@ -RSpec.configure do |c| - c.around { |ex| Sandboxing.sandboxed { ex.run } } -end - -class NullObject - private - def method_missing(method, *args, &block) - # ignore - end -end +require 'rspec/core/sandbox' +require 'rspec/mocks' -module Sandboxing - def self.sandboxed(&block) - orig_load_path = $LOAD_PATH.dup - orig_config = RSpec.configuration - orig_world = RSpec.world - orig_example = RSpec.current_example - new_config = RSpec::Core::Configuration.new - new_config.expose_dsl_globally = false - new_config.expecting_with_rspec = true - new_world = RSpec::Core::World.new(new_config) - RSpec.configuration = new_config - RSpec.world = new_world - object = Object.new - object.extend(RSpec::Core::SharedExampleGroup) +# Because testing RSpec with RSpec tries to modify the same global +# objects, we sandbox every test. +RSpec.configure do |c| + c.around do |ex| - (class << RSpec::Core::ExampleGroup; self; end).class_exec do - alias_method :orig_run, :run - def run(reporter=nil) - RSpec.current_example = nil - orig_run(reporter || NullObject.new) + RSpec::Core::Sandbox.sandboxed do |config| + config.expose_dsl_globally = false + config.expecting_with_rspec = true + # If there is an example-within-an-example, we want to make sure the inner example + # does not get a reference to the outer example (the real spec) if it calls + # something like `pending` + config.before(:context) { RSpec.current_example = nil } + begin + orig_load_path = $LOAD_PATH.dup + RSpec::Mocks.with_temporary_scope { ex.run } + ensure + $LOAD_PATH.replace(orig_load_path) end end - RSpec::Mocks.with_temporary_scope do - object.instance_exec(&block) - end - ensure - (class << RSpec::Core::ExampleGroup; self; end).class_exec do - remove_method :run - alias_method :run, :orig_run - remove_method :orig_run - end - - RSpec.configuration = orig_config - RSpec.world = orig_world - RSpec.current_example = orig_example - $LOAD_PATH.replace(orig_load_path) end end From dd715dc482f391af6ebacd81ed9876e6cd7d71c1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 30 Dec 2014 18:55:30 -0800 Subject: [PATCH 190/275] Use more dependable method of checking RSpec::Matchers availability. Before this change, we had a transient failure. Running `bin/rspec --seed 45090` would have one failure: ./spec/rspec/core/pending_example_spec.rb:157 The problem is that with our sandboxing `RSpec.configuration` gets cleared and reset, sometimes causing `expecting_with_rspec?` to have the wrong value. Checking if the `RSpec::Matchers` constant is defined is simpler and faster. --- lib/rspec/core/example.rb | 2 +- spec/rspec/core/example_spec.rb | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 2663048b42..e11748c649 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -365,7 +365,7 @@ def run_before_example end def run_after_example - assign_generated_description if RSpec.configuration.expecting_with_rspec? + assign_generated_description if defined?(::RSpec::Matchers) @example_group_class.hooks.run(:after, :example, self) verify_mocks ensure diff --git a/spec/rspec/core/example_spec.rb b/spec/rspec/core/example_spec.rb index afb8aef29a..4129d6dd18 100644 --- a/spec/rspec/core/example_spec.rb +++ b/spec/rspec/core/example_spec.rb @@ -202,10 +202,6 @@ def assert(val) example_group.run expect(example.description).to match(/example at #{relative_path(__FILE__)}:#{__LINE__ - 2}/) end - - # Needed since `expecting_with_rspec?` in this context returns false - # so it won't automatically clear it for us. - after { RSpec::Matchers.clear_generated_description } end end From 4f9eb1b57cb40aa35a054f0139347a437eff7f35 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 30 Dec 2014 18:39:46 -0800 Subject: [PATCH 191/275] Improve a couple specs. These specs are written assuming the entire group runs, not just the one spec, so we should call `group.run`. --- spec/rspec/core/pending_example_spec.rb | 28 +++++++++++++------------ 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/spec/rspec/core/pending_example_spec.rb b/spec/rspec/core/pending_example_spec.rb index 2688630ce4..5e2f25ed4a 100644 --- a/spec/rspec/core/pending_example_spec.rb +++ b/spec/rspec/core/pending_example_spec.rb @@ -138,34 +138,36 @@ context "with no docstring" do context "declared with the pending method" do - it "has an auto-generated description" do - group = RSpec.describe('group') do + it "has an auto-generated description if it has an expectation" do + ex = nil + + RSpec.describe('group') do it "checks something" do expect((3+4)).to eq(7) end - pending do + ex = pending do expect("string".reverse).to eq("gnirts") end - end - example = group.examples.last - example.run(group.new, double.as_null_object) - expect(example.description).to eq('should eq "gnirts"') + end.run + + expect(ex.description).to eq('should eq "gnirts"') end end context "after another example with some assertion" do it "does not show any message" do - group = RSpec.describe('group') do + ex = nil + + RSpec.describe('group') do it "checks something" do expect((3+4)).to eq(7) end - specify do + ex = specify do pending end - end - example = group.examples.last - example.run(group.new, double.as_null_object) - expect(example.description).to match(/example at/) + end.run + + expect(ex.description).to match(/example at/) end end end From 0e0944e744ca1ecab202b0c7bc279d0f45f6fad6 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 30 Dec 2014 18:57:57 -0800 Subject: [PATCH 192/275] Remove `expecting_with_rspec` as it is no longer necessary. --- lib/rspec/core/configuration.rb | 3 --- spec/rspec/core/configuration_spec.rb | 30 --------------------------- spec/rspec/core/example_spec.rb | 2 -- spec/support/sandboxing.rb | 2 +- 4 files changed, 1 insertion(+), 36 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index f4ee7e7579..6d1f3874d5 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -286,8 +286,6 @@ def treat_symbols_as_metadata_keys_with_true_values=(_value) # @private attr_writer :files_to_run # @private - add_setting :expecting_with_rspec - # @private attr_accessor :filter_manager # @private attr_accessor :static_config_filter_manager @@ -601,7 +599,6 @@ def expect_with(*frameworks) framework when :rspec require 'rspec/expectations' - self.expecting_with_rspec = true ::RSpec::Matchers when :test_unit require 'rspec/core/test_unit_assertions_adapter' diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index bf2f360e5d..61f4a9f447 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -392,36 +392,6 @@ def stub_expectation_adapters end end - describe "#expecting_with_rspec?" do - before do - stub_expectation_adapters - end - - it "returns false by default" do - expect(config).not_to be_expecting_with_rspec - end - - it "returns true when `expect_with :rspec` has been configured" do - config.expect_with :rspec - expect(config).to be_expecting_with_rspec - end - - it "returns true when `expect_with :rspec, :minitest` has been configured" do - config.expect_with :rspec, :minitest - expect(config).to be_expecting_with_rspec - end - - it "returns true when `expect_with :minitest, :rspec` has been configured" do - config.expect_with :minitest, :rspec - expect(config).to be_expecting_with_rspec - end - - it "returns false when `expect_with :minitest` has been configured" do - config.expect_with :minitest - expect(config).not_to be_expecting_with_rspec - end - end - describe "#files_to_run" do it "loads files not following pattern if named explicitly" do assign_files_or_directories_to_run "spec/rspec/core/resources/a_bar.rb" diff --git a/spec/rspec/core/example_spec.rb b/spec/rspec/core/example_spec.rb index 4129d6dd18..7fa62cba8a 100644 --- a/spec/rspec/core/example_spec.rb +++ b/spec/rspec/core/example_spec.rb @@ -52,8 +52,6 @@ def metadata_hash(*args) describe "when there is no explicit description" do def expect_with(*frameworks) - RSpec.configuration.expecting_with_rspec = frameworks.include?(:rspec) - if frameworks.include?(:stdlib) example_group.class_exec do def assert(val) diff --git a/spec/support/sandboxing.rb b/spec/support/sandboxing.rb index 26b93e2373..8940f56941 100644 --- a/spec/support/sandboxing.rb +++ b/spec/support/sandboxing.rb @@ -8,7 +8,7 @@ RSpec::Core::Sandbox.sandboxed do |config| config.expose_dsl_globally = false - config.expecting_with_rspec = true + # If there is an example-within-an-example, we want to make sure the inner example # does not get a reference to the outer example (the real spec) if it calls # something like `pending` From 350fe5053764d33f67f73ae46f6b3bd0ff170b17 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 30 Dec 2014 19:15:04 -0800 Subject: [PATCH 193/275] Make example group alias definitions idempotent w/ no warnings. --- lib/rspec/core/dsl.rb | 9 +++++++-- lib/rspec/core/example_group.rb | 17 +++++++++-------- spec/rspec/core/configuration_spec.rb | 26 ++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/lib/rspec/core/dsl.rb b/lib/rspec/core/dsl.rb index 8548eae78c..0659a9e7b9 100644 --- a/lib/rspec/core/dsl.rb +++ b/lib/rspec/core/dsl.rb @@ -39,8 +39,12 @@ def self.expose_example_group_alias(name) example_group_aliases << name - (class << RSpec; self; end).__send__(:define_method, name) do |*args, &example_group_block| - RSpec.world.register RSpec::Core::ExampleGroup.__send__(name, *args, &example_group_block) + (class << RSpec; self; end).instance_exec do + remove_method(name) if method_defined?(name) + + define_method(name) do |*args, &example_group_block| + RSpec.world.register RSpec::Core::ExampleGroup.__send__(name, *args, &example_group_block) + end end expose_example_group_alias_globally(name) if exposed_globally? @@ -78,6 +82,7 @@ def self.remove_globally! # @private def self.expose_example_group_alias_globally(method_name) change_global_dsl do + remove_method(method_name) if method_defined?(method_name) define_method(method_name) { |*a, &b| ::RSpec.__send__(method_name, *a, &b) } end end diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index d7b3a28c57..c94ffd88f8 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -32,10 +32,11 @@ class ExampleGroup include Pending extend SharedExampleGroup - unless respond_to?(:define_singleton_method) - # @private - def self.define_singleton_method(*a, &b) - (class << self; self; end).__send__(:define_method, *a, &b) + # @private + def self.idempotently_define_singleton_method(name, &definition) + (class << self; self; end).module_exec do + remove_method(name) if method_defined?(name) + define_method(name, &definition) end end @@ -56,7 +57,7 @@ def self.superclass_metadata # @private def self.delegate_to_metadata(*names) names.each do |name| - define_singleton_method(name) { metadata.fetch(name) } + idempotently_define_singleton_method(name) { metadata.fetch(name) } end end @@ -120,7 +121,7 @@ def described_class # # ex is the Example object that contains metadata about the example # end def self.define_example_method(name, extra_options={}) - define_singleton_method(name) do |*all_args, &block| + idempotently_define_singleton_method(name) do |*all_args, &block| desc, *args = *all_args options = Metadata.build_hash_from(args) @@ -214,7 +215,7 @@ def self.define_example_method(name, extra_options={}) # # @see DSL#describe def self.define_example_group_method(name, metadata={}) - define_singleton_method(name) do |*args, &example_group_block| + idempotently_define_singleton_method(name) do |*args, &example_group_block| thread_data = RSpec.thread_local_metadata top_level = self == ExampleGroup @@ -285,7 +286,7 @@ def self.define_example_group_method(name, metadata={}) # # @see SharedExampleGroup def self.define_nested_shared_group_method(new_name, report_label="it should behave like") - define_singleton_method(new_name) do |name, *args, &customization_block| + idempotently_define_singleton_method(new_name) do |name, *args, &customization_block| # Pass :caller so the :location metadata is set properly. # Otherwise, it'll be set to the next line because that's # the block's source_location. diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index 61f4a9f447..fe90cdd2af 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -1562,6 +1562,10 @@ class << self undef :my_group_method if method_defined? :my_group_method end end + + Module.class_exec do + undef :my_group_method if method_defined? :my_group_method + end end it_behaves_like "metadata hash builder" do @@ -1572,6 +1576,28 @@ def metadata_hash(*args) end end + it 'overrides existing definitions of the aliased method name without issueing warnings' do + config.expose_dsl_globally = true + + class << ExampleGroup + def my_group_method; :original; end + end + + class << RSpec + def my_group_method; :original; end + end + + Module.class_exec do + def my_group_method; :original; end + end + + config.alias_example_group_to :my_group_method + + expect(ExampleGroup.my_group_method).to be < ExampleGroup + expect(RSpec.my_group_method).to be < ExampleGroup + expect(Module.new.my_group_method).to be < ExampleGroup + end + it "allows adding additional metadata" do config.alias_example_group_to :my_group_method, { :some => "thing" } group = ExampleGroup.my_group_method("a group", :another => "thing") From 26db92198cab685a5c7ca07fce71424b8f4590ca Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 30 Dec 2014 19:16:20 -0800 Subject: [PATCH 194/275] Remove unneeded sandbox config setting. --- spec/rspec/core/shared_example_group_spec.rb | 1 + spec/support/sandboxing.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/rspec/core/shared_example_group_spec.rb b/spec/rspec/core/shared_example_group_spec.rb index ead01dead8..65123b3a89 100644 --- a/spec/rspec/core/shared_example_group_spec.rb +++ b/spec/rspec/core/shared_example_group_spec.rb @@ -52,6 +52,7 @@ module Core end it "is not exposed to the global namespace when monkey patching is disabled" do + RSpec.configuration.expose_dsl_globally = false expect(Kernel).to_not respond_to(shared_method_name) end diff --git a/spec/support/sandboxing.rb b/spec/support/sandboxing.rb index 8940f56941..e920f3af54 100644 --- a/spec/support/sandboxing.rb +++ b/spec/support/sandboxing.rb @@ -7,7 +7,7 @@ c.around do |ex| RSpec::Core::Sandbox.sandboxed do |config| - config.expose_dsl_globally = false + # If there is an example-within-an-example, we want to make sure the inner example # does not get a reference to the outer example (the real spec) if it calls From 2347d97bf8f0128f42bcb9eca1b0faa626aca7f0 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 30 Dec 2014 19:27:07 -0800 Subject: [PATCH 195/275] Cleanup sandboxing.rb formatting. There's no need for the begin/ensure because RSpec handles errors within examples and so an example failure will not propagate up to an around hook. --- spec/support/formatter_support.rb | 18 +++++++++--------- spec/support/sandboxing.rb | 10 +++------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/spec/support/formatter_support.rb b/spec/support/formatter_support.rb index f41d9b8adb..c81c88c9ca 100644 --- a/spec/support/formatter_support.rb +++ b/spec/support/formatter_support.rb @@ -55,9 +55,9 @@ def expected_summary_output_for_example_specs | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:31 | # ./spec/support/formatter_support.rb:13:in `run_example_specs_with_formatter' - | # ./spec/support/sandboxing.rb:18 - | # ./spec/support/sandboxing.rb:18 - | # ./spec/support/sandboxing.rb:9 + | # ./spec/support/sandboxing.rb:16 + | # ./spec/support/sandboxing.rb:14 + | # ./spec/support/sandboxing.rb:8 | | 3) a failing spec with odd backtraces fails with a backtrace that has no file | Failure/Error: Unable to find matching line from backtrace @@ -108,9 +108,9 @@ def expected_summary_output_for_example_specs | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:31:in `block (2 levels) in ' | # ./spec/support/formatter_support.rb:13:in `run_example_specs_with_formatter' - | # ./spec/support/sandboxing.rb:18:in `block (4 levels) in ' - | # ./spec/support/sandboxing.rb:18:in `block (3 levels) in ' - | # ./spec/support/sandboxing.rb:9:in `block (2 levels) in ' + | # ./spec/support/sandboxing.rb:16:in `block (4 levels) in ' + | # ./spec/support/sandboxing.rb:14:in `block (3 levels) in ' + | # ./spec/support/sandboxing.rb:8:in `block (2 levels) in ' | | 3) a failing spec with odd backtraces fails with a backtrace that has no file | Failure/Error: ERB.new("<%= raise 'foo' %>").result @@ -119,9 +119,9 @@ def expected_summary_output_for_example_specs | # (erb):1:in `
' | # ./spec/rspec/core/resources/formatter_specs.rb:39:in `block (2 levels) in ' | # ./spec/support/formatter_support.rb:13:in `run_example_specs_with_formatter' - | # ./spec/support/sandboxing.rb:18:in `block (4 levels) in ' - | # ./spec/support/sandboxing.rb:18:in `block (3 levels) in ' - | # ./spec/support/sandboxing.rb:9:in `block (2 levels) in ' + | # ./spec/support/sandboxing.rb:16:in `block (4 levels) in ' + | # ./spec/support/sandboxing.rb:14:in `block (3 levels) in ' + | # ./spec/support/sandboxing.rb:8:in `block (2 levels) in ' | | 4) a failing spec with odd backtraces fails with a backtrace containing an erb file | Failure/Error: Unable to find matching line from backtrace diff --git a/spec/support/sandboxing.rb b/spec/support/sandboxing.rb index e920f3af54..dc04bb0c97 100644 --- a/spec/support/sandboxing.rb +++ b/spec/support/sandboxing.rb @@ -5,21 +5,17 @@ # objects, we sandbox every test. RSpec.configure do |c| c.around do |ex| - RSpec::Core::Sandbox.sandboxed do |config| - - # If there is an example-within-an-example, we want to make sure the inner example # does not get a reference to the outer example (the real spec) if it calls # something like `pending` config.before(:context) { RSpec.current_example = nil } - begin + + RSpec::Mocks.with_temporary_scope do orig_load_path = $LOAD_PATH.dup - RSpec::Mocks.with_temporary_scope { ex.run } - ensure + ex.run $LOAD_PATH.replace(orig_load_path) end end - end end From 55680fd51cb901342a63504289831704c7d2f204 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 30 Dec 2014 19:29:50 -0800 Subject: [PATCH 196/275] Cleanup sandbox implementation a bit. - No need for `new_world` or `new_config` locals. - Align assignments. --- lib/rspec/core/sandbox.rb | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/rspec/core/sandbox.rb b/lib/rspec/core/sandbox.rb index 3094f3b3f6..902436800b 100644 --- a/lib/rspec/core/sandbox.rb +++ b/lib/rspec/core/sandbox.rb @@ -16,20 +16,17 @@ module Sandbox # end # ``` def self.sandboxed - orig_config = RSpec.configuration - orig_world = RSpec.world + orig_config = RSpec.configuration + orig_world = RSpec.world orig_example = RSpec.current_example - new_config = RSpec::Core::Configuration.new - new_world = RSpec::Core::World.new(new_config) + RSpec.configuration = RSpec::Core::Configuration.new + RSpec.world = RSpec::Core::World.new(RSpec.configuration) - RSpec.configuration = new_config - RSpec.world = new_world - - yield new_config + yield RSpec.configuration ensure - RSpec.configuration = orig_config - RSpec.world = orig_world + RSpec.configuration = orig_config + RSpec.world = orig_world RSpec.current_example = orig_example end end From fd8263f02b54fdaaa8f61cf702ff8074912e1c50 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 30 Dec 2014 19:30:41 -0800 Subject: [PATCH 197/275] No need to name args since we ignore them all. --- lib/rspec/core/reporter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/core/reporter.rb b/lib/rspec/core/reporter.rb index da910164c1..3671a6750f 100644 --- a/lib/rspec/core/reporter.rb +++ b/lib/rspec/core/reporter.rb @@ -165,7 +165,7 @@ def seed_used? class NullReporter private - def method_missing(_method, *_args, &_block) + def method_missing(*) # ignore end end From 543ec2416cf816ce925476a9de54172c5dcdc3bf Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 30 Dec 2014 19:32:20 -0800 Subject: [PATCH 198/275] Clarify that this file must be required. --- lib/rspec/core/sandbox.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/rspec/core/sandbox.rb b/lib/rspec/core/sandbox.rb index 902436800b..e7d518c2e9 100644 --- a/lib/rspec/core/sandbox.rb +++ b/lib/rspec/core/sandbox.rb @@ -3,6 +3,9 @@ module Core # A sandbox isolates the enclosed code into an environment that looks 'new' # meaning globally accessed objects are reset for the duration of the # sandbox. + # + # @note This module is not normally available. You must require + # `rspec/core/sandbox` to load it. module Sandbox # Execute a provided block with RSpec global objects (configuration, # world) reset. This is used to test RSpec with RSpec. From a1acd24e131032fe709e6e12c563fdd0afd5ada9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 30 Dec 2014 19:33:37 -0800 Subject: [PATCH 199/275] Add changelog for #1808. --- Changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog.md b/Changelog.md index c9db058e93..5cc33ac86e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -32,6 +32,10 @@ Enhancements: APIs (e.g. `expect`, `double`, `stub_const`, etc) from within an example group where those APIs are unavailable. (Myron Marston, #1819) +* Add new `RSpec::Core::Sandbox.sandboxed { }` API that facilitates + testing RSpec with RSpec, allowing you to define example groups + and example from within an example without affecting the global + `RSpec.world` state. (Tyler Ball, 1808) Bug Fixes: From ee67a70b090c628be6809d25689646cd60d0b16e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 31 Dec 2014 09:50:11 -0800 Subject: [PATCH 200/275] We don't need the `method_defined?` check on `RSpec`. The `return if example_group_aliases.include?(name)` guard clause renders it unnecessary. The one case where it does something is when users manually define methods on `RSpec` but in that case we want to allow the warning. --- lib/rspec/core/dsl.rb | 8 ++------ spec/rspec/core/configuration_spec.rb | 5 ----- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/lib/rspec/core/dsl.rb b/lib/rspec/core/dsl.rb index 0659a9e7b9..9f290b997d 100644 --- a/lib/rspec/core/dsl.rb +++ b/lib/rspec/core/dsl.rb @@ -39,12 +39,8 @@ def self.expose_example_group_alias(name) example_group_aliases << name - (class << RSpec; self; end).instance_exec do - remove_method(name) if method_defined?(name) - - define_method(name) do |*args, &example_group_block| - RSpec.world.register RSpec::Core::ExampleGroup.__send__(name, *args, &example_group_block) - end + (class << RSpec; self; end).__send__(:define_method, name) do |*args, &example_group_block| + RSpec.world.register RSpec::Core::ExampleGroup.__send__(name, *args, &example_group_block) end expose_example_group_alias_globally(name) if exposed_globally? diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index fe90cdd2af..280aeb62f4 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -1583,10 +1583,6 @@ class << ExampleGroup def my_group_method; :original; end end - class << RSpec - def my_group_method; :original; end - end - Module.class_exec do def my_group_method; :original; end end @@ -1594,7 +1590,6 @@ def my_group_method; :original; end config.alias_example_group_to :my_group_method expect(ExampleGroup.my_group_method).to be < ExampleGroup - expect(RSpec.my_group_method).to be < ExampleGroup expect(Module.new.my_group_method).to be < ExampleGroup end From b4a9c75dc2e196e12b3ba72bfbd6fe3a83d0f2be Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 31 Dec 2014 09:56:03 -0800 Subject: [PATCH 201/275] =?UTF-8?q?Silence=20warnings=20from=201.8.7?= =?UTF-8?q?=E2=80=99s=20mathn=20library.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/support/mathn_integration_support.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/support/mathn_integration_support.rb b/spec/support/mathn_integration_support.rb index be6cba7a8b..f6ca400418 100644 --- a/spec/support/mathn_integration_support.rb +++ b/spec/support/mathn_integration_support.rb @@ -7,6 +7,13 @@ module MathnIntegrationSupport def with_mathn_loaded skip "lib/mathn.rb is deprecated in Ruby 2.2" end + elsif RUBY_VERSION.to_f < 1.9 + def with_mathn_loaded + in_sub_process do + expect { require 'mathn' }.to output.to_stderr + yield + end + end else def with_mathn_loaded in_sub_process do From e9307c82bf50ce15f07202d1967cdf365a48ed10 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 31 Dec 2014 23:38:56 -0800 Subject: [PATCH 202/275] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 19 ++++++++++--------- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 16 ++++++++++++---- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 28 insertions(+), 19 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index c97db9cefb..043dd0e95d 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 4617231632..dc0d119c44 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,27 +1,28 @@ -# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby sudo: false -cache: bundler +cache: + directories: + - ../rspec-core/bundle + - ../rspec-expectations/bundle + - ../rspec-mocks/bundle + - ../rspec-rails/bundle + - ../rspec-support/bundle before_install: - "script/clone_all_rspec_repos" # Downgrade bundler to work around https://github.com/bundler/bundler/issues/3004 # Note this doesn't work on JRUBY 2.0.0 mode so we don't do it - if [ -z "$JRUBY_OPTS" ]; then gem install bundler -v=1.5.3 && alias bundle="bundle _1.5.3_"; fi -bundler_args: "--binstubs --standalone --without documentation --path ../bundle" +bundler_args: "--binstubs --standalone --without documentation --path bundle" script: "script/run_build" rvm: - 1.8.7 - 1.9.2 - 1.9.3 - 2.0.0 - - 2.1.0 - - 2.1.1 - - 2.1.2 - - 2.1.3 - - 2.1.4 - - 2.1.5 + - 2.1 - 2.2 - ruby-head - ree diff --git a/appveyor.yml b/appveyor.yml index f61ade0489..b746c818bf 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index a5c20d550a..e9258f0b8f 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index db2c87644d..acf5245473 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -11,8 +11,15 @@ SPECS_HAVE_RUN_FILE=specs.out MAINTENANCE_BRANCH=`cat maintenance-branch` function clone_repo { - if [ ! -d $1 ]; then # don't clone if the dir is already there - travis_retry eval "git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH" + if [ ! -e $1/Gemfile ]; then # don't reclone + # deal with our bundler cache directory. + # Git won't clone into a non-empty dir so we have to move it aside and move it back. + mkdir -p $1/bundle + pushd $1 + mv bundle ../$1-bundle + travis_retry eval "git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH ." + mv ../$1-bundle bundle + popd fi; } @@ -48,7 +55,7 @@ function run_cukes { else # Prepare RUBYOPT for scenarios that are shelling out to ruby, # and PATH for those that are using `rspec` or `rake`. - RUBYOPT="-I${PWD}/../bundle -rbundler/setup" \ + RUBYOPT="-I${PWD}/bundle -rbundler/setup" \ PATH="${PWD}/bin:$PATH" \ bin/cucumber --strict fi @@ -71,6 +78,7 @@ function run_spec_suite_for { bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` travis_retry eval "bundle install $bundle_install_flags" run_specs_and_record_done + bundle clean # prep for travis caching popd fi; } diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 2a5e5e75b9..50cdcdb0c6 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 3bc820b1bc..608eab31c0 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index cc330d3ac7..23afb9accb 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-29T15:16:37-08:00 from the rspec-dev repo. +# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 2dfca61c94c051d33ba3ae910446c22da341ecf1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 31 Dec 2014 12:22:27 -0800 Subject: [PATCH 203/275] Refactor `find_failed_line`. --- lib/rspec/core/notifications.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rspec/core/notifications.rb b/lib/rspec/core/notifications.rb index 37e657ff4c..333cdbdc05 100644 --- a/lib/rspec/core/notifications.rb +++ b/lib/rspec/core/notifications.rb @@ -259,10 +259,10 @@ def read_failed_line end def find_failed_line - path = File.expand_path(example.file_path) + example_path = File.expand_path(example.file_path).downcase exception.backtrace.find do |line| - match = line.match(/(.+?):(\d+)(|:\d+)/) - match && match[1].downcase == path.downcase + next unless (line_path = line[/(.+?):(\d+)(|:\d+)/, 1]) + line_path.downcase == example_path end end end From 3c0e7c73bd00b29fc98035da6c8ac9bfd0234206 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 31 Dec 2014 12:16:54 -0800 Subject: [PATCH 204/275] Fix failed line detection to work with relative paths in backtrace. This happens when the user uses `rspec/autorun` and runs the spec file(s) through `ruby`, since the spec file will appear in backtraces using whatever relative form the user used for the file when invoking `ruby`. Fixes #1827. --- Changelog.md | 3 +++ features/command_line/ruby.feature | 8 ++++++-- lib/rspec/core/notifications.rb | 2 +- spec/rspec/core/notifications_spec.rb | 11 +++++++++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 5cc33ac86e..d852960142 100644 --- a/Changelog.md +++ b/Changelog.md @@ -520,6 +520,9 @@ Bug Fixes: directory (was broken in beta1). (Jon Rowe) * Prevent RSpec mangling file names that have substrings containing `line_number` or `default_path`. (Matijs van Zuijlen) +* Fix failure line detection so that it handles relative file paths + (which can happen when running specs through `ruby` using `rspec/autorun`). + (Myron Marston, #1829) ### 3.0.0.beta1 / 2013-11-07 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.99.1...v3.0.0.beta1) diff --git a/features/command_line/ruby.feature b/features/command_line/ruby.feature index a9026918a6..d0e50ba006 100644 --- a/features/command_line/ruby.feature +++ b/features/command_line/ruby.feature @@ -16,8 +16,12 @@ Feature: run with `ruby` command it "is < 2" do expect(1).to be < 2 end + + it "has an intentional failure" do + expect(1).to be > 2 + end end """ When I run `ruby example_spec.rb` - Then the output should contain "1 example, 0 failures" - + Then the output should contain "2 examples, 1 failure" + And the output should contain "expect(1).to be > 2" diff --git a/lib/rspec/core/notifications.rb b/lib/rspec/core/notifications.rb index 333cdbdc05..9793c0b5e4 100644 --- a/lib/rspec/core/notifications.rb +++ b/lib/rspec/core/notifications.rb @@ -262,7 +262,7 @@ def find_failed_line example_path = File.expand_path(example.file_path).downcase exception.backtrace.find do |line| next unless (line_path = line[/(.+?):(\d+)(|:\d+)/, 1]) - line_path.downcase == example_path + File.expand_path(line_path).downcase == example_path end end end diff --git a/spec/rspec/core/notifications_spec.rb b/spec/rspec/core/notifications_spec.rb index 0430c5d42c..665b69c400 100644 --- a/spec/rspec/core/notifications_spec.rb +++ b/spec/rspec/core/notifications_spec.rb @@ -1,4 +1,5 @@ require 'rspec/core/notifications' +require 'pathname' RSpec.describe "FailedExampleNotification" do include FormatterSupport @@ -44,6 +45,16 @@ end end + context "when the stacktrace includes relative paths (which can happen when using `rspec/autorun` and running files through `ruby`)" do + let(:relative_file) { Pathname(__FILE__).relative_path_from(Pathname(Dir.pwd)) } + line = __LINE__ + let(:exception) { instance_double(Exception, :backtrace => ["#{relative_file}:#{line}"]) } + + it 'still finds the backtrace line' do + expect(notification.send(:read_failed_line)).to include("line = __LINE__") + end + end + context "when String alias to_int to_i" do before do String.class_exec do From 7d8f069805cfd3b326c5e657a7e8dd2fa32b4808 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 1 Jan 2015 12:50:25 -0800 Subject: [PATCH 205/275] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 10 +++------- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 27 ++++++++++++++------------- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 23 insertions(+), 26 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 043dd0e95d..903ac50adb 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index dc0d119c44..d3dc6276ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,17 @@ -# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby sudo: false cache: directories: - - ../rspec-core/bundle - - ../rspec-expectations/bundle - - ../rspec-mocks/bundle - - ../rspec-rails/bundle - - ../rspec-support/bundle + - ../bundle before_install: - "script/clone_all_rspec_repos" # Downgrade bundler to work around https://github.com/bundler/bundler/issues/3004 # Note this doesn't work on JRUBY 2.0.0 mode so we don't do it - if [ -z "$JRUBY_OPTS" ]; then gem install bundler -v=1.5.3 && alias bundle="bundle _1.5.3_"; fi -bundler_args: "--binstubs --standalone --without documentation --path bundle" +bundler_args: "--binstubs --standalone --without documentation --path ../bundle" script: "script/run_build" rvm: - 1.8.7 diff --git a/appveyor.yml b/appveyor.yml index b746c818bf..1d99a0c879 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index e9258f0b8f..e410c500b6 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index acf5245473..aecebb0cb1 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -11,15 +11,8 @@ SPECS_HAVE_RUN_FILE=specs.out MAINTENANCE_BRANCH=`cat maintenance-branch` function clone_repo { - if [ ! -e $1/Gemfile ]; then # don't reclone - # deal with our bundler cache directory. - # Git won't clone into a non-empty dir so we have to move it aside and move it back. - mkdir -p $1/bundle - pushd $1 - mv bundle ../$1-bundle - travis_retry eval "git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH ." - mv ../$1-bundle bundle - popd + if [ ! -d $1 ]; then # don't clone if the dir is already there + travis_retry eval "git clone git://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH" fi; } @@ -55,7 +48,7 @@ function run_cukes { else # Prepare RUBYOPT for scenarios that are shelling out to ruby, # and PATH for those that are using `rspec` or `rake`. - RUBYOPT="-I${PWD}/bundle -rbundler/setup" \ + RUBYOPT="-I${PWD}/../bundle -rbundler/setup" \ PATH="${PWD}/bin:$PATH" \ bin/cucumber --strict fi @@ -75,10 +68,18 @@ function run_spec_suite_for { echo "Running specs for $1" pushd ../$1 unset BUNDLE_GEMFILE - bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` + + if [ $1 == "rspec-rails" ]; then + bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` + else + # The other repos' master branch have `./bundle` as the bundler path, but we need them to all + # go into `../bundle`, so we are forcing it here rather than reading from `.travis.yml`. + # Once all the PRs are merged we can revert this. + bundle_install_flags="--binstubs --standalone --without documentation --path ../bundle" + fi + travis_retry eval "bundle install $bundle_install_flags" run_specs_and_record_done - bundle clean # prep for travis caching popd fi; } diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 50cdcdb0c6..94bcdec7c4 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index 608eab31c0..fb1ed5539c 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 23afb9accb..4a07d6e627 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2014-12-31T23:38:56-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 8f9ad96c3a3750b4de19f3d504846ed8260b5bad Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 1 Jan 2015 16:46:16 -0800 Subject: [PATCH 206/275] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 13 ++----------- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 9 insertions(+), 18 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 903ac50adb..91d8cfb405 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index d3dc6276ea..94ebd55c43 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 1d99a0c879..365f61c286 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index e410c500b6..5db1115440 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index aecebb0cb1..75fe0465d3 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -68,16 +68,7 @@ function run_spec_suite_for { echo "Running specs for $1" pushd ../$1 unset BUNDLE_GEMFILE - - if [ $1 == "rspec-rails" ]; then - bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` - else - # The other repos' master branch have `./bundle` as the bundler path, but we need them to all - # go into `../bundle`, so we are forcing it here rather than reading from `.travis.yml`. - # Once all the PRs are merged we can revert this. - bundle_install_flags="--binstubs --standalone --without documentation --path ../bundle" - fi - + bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` travis_retry eval "bundle install $bundle_install_flags" run_specs_and_record_done popd diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 94bcdec7c4..a9956fc55b 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index fb1ed5539c..d7ac729082 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 4a07d6e627..3928cc6b66 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T12:50:25-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From a8c3877d168accc219f02907b5961818c198c460 Mon Sep 17 00:00:00 2001 From: Aaron Kromer Date: Wed, 31 Dec 2014 20:55:08 -0500 Subject: [PATCH 207/275] Include details on how to run against `master`. A very common issue is when a user attempts to run against the `master` branch. Simply pointing to `master` doesn't work. This adds the steps necessary to use `master`. We need all of the repos in order to also satisfy any other gems which may have a dependency on the `rspec` gem. [ci skip] --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index af363fd915..d47f17b39c 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,15 @@ examples get run and tailor the output. gem install rspec-core # for rspec-core only rspec --help +Want to run against the `master` branch? You'll need to include the dependent +RSpec repos as well. Add the following to your `Gemfile`: + +```ruby +%w[rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib| + gem lib, :git => "git://github.com/rspec/#{lib}.git", :branch => 'master' +end +``` + ## basic structure RSpec uses the words "describe" and "it" so we can express concepts like a conversation: From 75bf144cfe0070cd9a2a497bd7660ea7ed8d07f2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 29 Dec 2014 22:55:39 -0800 Subject: [PATCH 208/275] Fix indentation. --- features/example_groups/shared_examples.feature | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/features/example_groups/shared_examples.feature b/features/example_groups/shared_examples.feature index dd2f96fb43..27563be13a 100644 --- a/features/example_groups/shared_examples.feature +++ b/features/example_groups/shared_examples.feature @@ -224,11 +224,11 @@ Feature: shared examples RSpec.describe String, :a => :b do end """ - When I run `rspec shared_example_metadata_spec.rb` - Then the output should contain: - """ - 1 example, 0 failures - """ + When I run `rspec shared_example_metadata_spec.rb` + Then the output should contain: + """ + 1 example, 0 failures + """ Scenario: Shared examples are nestable by context Given a file named "context_specific_examples_spec.rb" with: From 315f4c4c8a8ea2347b129656ff41cbc932432a89 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 2 Jan 2015 21:10:57 -0800 Subject: [PATCH 209/275] Refactor aruba spec setup. This will facilitate writing other specs in using aruba. --- .../order_spec.rb | 15 +++-------- spec/spec_helper.rb | 8 ------ spec/support/aruba_support.rb | 26 +++++++++++++++++++ 3 files changed, 30 insertions(+), 19 deletions(-) rename spec/{command_line => integration}/order_spec.rb (96%) create mode 100644 spec/support/aruba_support.rb diff --git a/spec/command_line/order_spec.rb b/spec/integration/order_spec.rb similarity index 96% rename from spec/command_line/order_spec.rb rename to spec/integration/order_spec.rb index 681704b596..5d5921e136 100644 --- a/spec/command_line/order_spec.rb +++ b/spec/integration/order_spec.rb @@ -1,6 +1,7 @@ -RSpec.describe 'command line', :ui, :slow do - let(:stderr) { StringIO.new } - let(:stdout) { StringIO.new } +require 'support/aruba_support' + +RSpec.describe 'command line', :ui do + include_context "aruba support" before :all do write_file 'spec/simple_spec.rb', """ @@ -200,12 +201,4 @@ def split_in_half(array) length, midpoint = array.length, array.length / 2 return array.slice(0, midpoint), array.slice(midpoint, length) end - - def run_command(cmd) - in_current_dir do - RSpec::Core::Runner.run(cmd.split, stderr, stdout) - end - ensure - RSpec.reset - end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4dc0ef73e9..81bc9b3a65 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,13 +2,6 @@ require 'rspec/support/spec' -module ArubaLoader - extend RSpec::Support::WithIsolatedStdErr - with_isolated_stderr do - require 'aruba/api' - end -end - if RUBY_PLATFORM == 'java' # Works around https://jira.codehaus.org/browse/JRUBY-5678 require 'fileutils' @@ -62,7 +55,6 @@ def without_env_vars(*vars) # structural c.alias_it_behaves_like_to 'it_has_behavior' c.include(RSpecHelpers) - c.include Aruba::Api, :file_path => /spec\/command_line/ c.disable_monkey_patching! # runtime options diff --git a/spec/support/aruba_support.rb b/spec/support/aruba_support.rb new file mode 100644 index 0000000000..1d80883818 --- /dev/null +++ b/spec/support/aruba_support.rb @@ -0,0 +1,26 @@ +module ArubaLoader + extend RSpec::Support::WithIsolatedStdErr + with_isolated_stderr do + require 'aruba/api' + end +end + +RSpec.shared_context "aruba support" do + include Aruba::Api + let(:stderr) { StringIO.new } + let(:stdout) { StringIO.new } + + def run_command(cmd) + in_current_dir do + RSpec::Core::Runner.run(cmd.split, stderr, stdout) + end + ensure + RSpec.reset + end +end + +RSpec.configure do |c| + c.define_derived_metadata(:file_path => %r{spec/integration}) do |meta| + meta[:slow] = true + end +end From 2470f996a9bc5644a448790419e404f002226368 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 3 Jan 2015 00:39:25 -0800 Subject: [PATCH 210/275] Allow relative_path_regex to be recompiled. This is necessary when we are running integration tests that change the current directory. --- lib/rspec/core/metadata.rb | 6 ++++-- spec/support/aruba_support.rb | 7 +++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/rspec/core/metadata.rb b/lib/rspec/core/metadata.rb index f44b737005..b8e192650c 100644 --- a/lib/rspec/core/metadata.rb +++ b/lib/rspec/core/metadata.rb @@ -33,14 +33,16 @@ module Metadata # http://rubular.com/r/fT0gmX6VJX # http://rubular.com/r/duOrD4i3wb # http://rubular.com/r/sbAMHFrOx1 - RELATIVE_PATH_REGEX = /(\A|\s)#{File.expand_path('.')}(#{File::SEPARATOR}|\s|\Z)/ + def self.relative_path_regex + @relative_path_regex ||= /(\A|\s)#{File.expand_path('.')}(#{File::SEPARATOR}|\s|\Z)/ + end # @api private # # @param line [String] current code line # @return [String] relative path to line def self.relative_path(line) - line = line.sub(RELATIVE_PATH_REGEX, "\\1.\\2".freeze) + line = line.sub(relative_path_regex, "\\1.\\2".freeze) line = line.sub(/\A([^:]+:\d+)$/, '\\1'.freeze) return nil if line == '-e:1'.freeze line diff --git a/spec/support/aruba_support.rb b/spec/support/aruba_support.rb index 1d80883818..5d9a773da2 100644 --- a/spec/support/aruba_support.rb +++ b/spec/support/aruba_support.rb @@ -17,6 +17,13 @@ def run_command(cmd) ensure RSpec.reset end + + def in_current_dir + RSpec::Core::Metadata.instance_variable_set(:@relative_path_regex, nil) + super { yield } + ensure + RSpec::Core::Metadata.instance_variable_set(:@relative_path_regex, nil) + end end RSpec.configure do |c| From f008e775db032b4cb114c8c514c1130c97a5e453 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 3 Jan 2015 00:27:17 -0800 Subject: [PATCH 211/275] Print a usable re-run command for specs defined in external files. Fixes #793. --- Changelog.md | 3 ++ lib/rspec/core/configuration.rb | 10 ++++- lib/rspec/core/example.rb | 9 +++++ lib/rspec/core/metadata.rb | 13 +++++++ lib/rspec/core/notifications.rb | 4 +- .../shared_example_rerun_commands_spec.rb | 39 +++++++++++++++++++ .../formatters/base_text_formatter_spec.rb | 20 +++++++++- spec/support/aruba_support.rb | 12 +++++- spec/support/formatter_support.rb | 1 + 9 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 spec/integration/shared_example_rerun_commands_spec.rb diff --git a/Changelog.md b/Changelog.md index d852960142..1c389f4209 100644 --- a/Changelog.md +++ b/Changelog.md @@ -51,6 +51,9 @@ Bug Fixes: implementation rather than Minitest's. (Jonathan Rochkind, #1822) * Fix `NameError` caused when duplicate example group aliases are defined and the DSL is not globally exposed. (Aaron Kromer, #1825) +* When a shared example defined in an external file fails, use the host + example group (from a loaded spec file) for the re-run command to + ensure the command will actually work. (Myron Marston, #1835) ### 3.1.8 Development diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index 6d1f3874d5..8553b34c69 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -290,7 +290,7 @@ def treat_symbols_as_metadata_keys_with_true_values=(_value) # @private attr_accessor :static_config_filter_manager # @private - attr_reader :backtrace_formatter, :ordering_manager + attr_reader :backtrace_formatter, :ordering_manager, :loaded_spec_files def initialize # rubocop:disable Style/GlobalVars @@ -306,6 +306,7 @@ def initialize @mock_framework = nil @files_or_directories_to_run = [] + @loaded_spec_files = Set.new @color = false @pattern = '**{,/*/**}/*_spec.rb' @exclude_pattern = '' @@ -1194,7 +1195,12 @@ def configure_expectation_framework # @private def load_spec_files - files_to_run.uniq.each { |f| load File.expand_path(f) } + files_to_run.uniq.each do |f| + file = File.expand_path(f) + load file + loaded_spec_files << file + end + @spec_files_loaded = true end diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index e11748c649..6864ef2b89 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -92,6 +92,15 @@ def inspect_output inspect_output end + # Returns the argument that can be passed to the `rspec` command to rerun this example. + def rerun_argument + loaded_spec_files = RSpec.configuration.loaded_spec_files + + Metadata.ascend(metadata) do |meta| + return meta[:location] if loaded_spec_files.include?(File.expand_path meta[:file_path]) + end + end + # @attr_reader # # Returns the first exception raised in the context of running this diff --git a/lib/rspec/core/metadata.rb b/lib/rspec/core/metadata.rb index b8e192650c..a0459f44fb 100644 --- a/lib/rspec/core/metadata.rb +++ b/lib/rspec/core/metadata.rb @@ -50,6 +50,19 @@ def self.relative_path(line) nil end + # @private + # Iteratively walks up from the given example metadata through all + # example group ancestors, yielding each metadata hash along the way. + def self.ascend(example_metadata) + yield example_metadata + group_metadata = example_metadata.fetch(:example_group) + + loop do + yield group_metadata + break unless (group_metadata = group_metadata[:parent_example_group]) + end + end + # @private # Used internally to build a hash from an args array. # Symbols are converted into hash keys with a value of `true`. diff --git a/lib/rspec/core/notifications.rb b/lib/rspec/core/notifications.rb index 9793c0b5e4..f0a678c2cc 100644 --- a/lib/rspec/core/notifications.rb +++ b/lib/rspec/core/notifications.rb @@ -404,8 +404,8 @@ def colorized_totals_line(colorizer=::RSpec::Core::Formatters::ConsoleCodes) def colorized_rerun_commands(colorizer=::RSpec::Core::Formatters::ConsoleCodes) "\nFailed examples:\n\n" + failed_examples.map do |example| - colorizer.wrap("rspec #{example.location}", RSpec.configuration.failure_color) + " " + - colorizer.wrap("# #{example.full_description}", RSpec.configuration.detail_color) + colorizer.wrap("rspec #{example.rerun_argument}", RSpec.configuration.failure_color) + " " + + colorizer.wrap("# #{example.full_description}", RSpec.configuration.detail_color) end.join("\n") end diff --git a/spec/integration/shared_example_rerun_commands_spec.rb b/spec/integration/shared_example_rerun_commands_spec.rb new file mode 100644 index 0000000000..a506c536cb --- /dev/null +++ b/spec/integration/shared_example_rerun_commands_spec.rb @@ -0,0 +1,39 @@ +require 'support/aruba_support' + +RSpec.describe 'Shared Example Rerun Commands' do + include_context "aruba support" + before { clean_current_dir } + + it 'prints a rerun command for shared examples in external files that works to rerun' do + write_file "spec/support/shared_examples.rb", """ + RSpec.shared_examples 'a failing example' do + example { expect(1).to eq(2) } + end + """ + + write_file "spec/host_group_spec.rb", """ + load File.expand_path('../support/shared_examples.rb', __FILE__) + + RSpec.describe 'A group with shared examples' do + include_examples 'a failing example' + end + + RSpec.describe 'A group with a passing example' do + example { expect(1).to eq(1) } + end + """ + + run_command "" + expect(last_cmd_stdout).to include("2 examples, 1 failure") + run_rerun_command_for_failing_spec + expect(last_cmd_stdout).to include("1 example, 1 failure") + # There was originally a bug when doing it again... + run_rerun_command_for_failing_spec + expect(last_cmd_stdout).to include("1 example, 1 failure") + end + + def run_rerun_command_for_failing_spec + command = last_cmd_stdout[/Failed examples:\s+rspec (\S+) #/, 1] + run_command command + end +end diff --git a/spec/rspec/core/formatters/base_text_formatter_spec.rb b/spec/rspec/core/formatters/base_text_formatter_spec.rb index 73a3fbf593..bd5d476137 100644 --- a/spec/rspec/core/formatters/base_text_formatter_spec.rb +++ b/spec/rspec/core/formatters/base_text_formatter_spec.rb @@ -35,10 +35,28 @@ it("fails") { fail } end line = __LINE__ - 2 + + expect(output_from_running example_group).to include("rspec #{RSpec::Core::Metadata::relative_path("#{__FILE__}:#{line}")} # example group fails") + end + + context "for an example defined in an file required by the user rather than loaded by rspec" do + it "looks through ancestor metadata to find a workable re-run command" do + line = __LINE__ + 1 + example_group = RSpec.describe("example group") do + # Using eval in order to make it think this got defined in an external file. + instance_eval "it('fails') { fail }", "some/external/file.rb", 1 + end + + expect(output_from_running example_group).to include("rspec #{RSpec::Core::Metadata::relative_path("#{__FILE__}:#{line}")} # example group fails") + end + end + + def output_from_running(example_group) + allow(RSpec.configuration).to receive(:loaded_spec_files) { [File.expand_path(__FILE__)].to_set } example_group.run(reporter) examples = example_group.examples send_notification :dump_summary, summary_notification(1, examples, examples, [], 0) - expect(output.string).to include("rspec #{RSpec::Core::Metadata::relative_path("#{__FILE__}:#{line}")} # example group fails") + output.string end end diff --git a/spec/support/aruba_support.rb b/spec/support/aruba_support.rb index 5d9a773da2..1499232894 100644 --- a/spec/support/aruba_support.rb +++ b/spec/support/aruba_support.rb @@ -10,12 +10,22 @@ module ArubaLoader let(:stderr) { StringIO.new } let(:stdout) { StringIO.new } + attr_reader :last_cmd_stdout, :last_cmd_stderr + def run_command(cmd) + temp_stdout = StringIO.new + temp_stderr = StringIO.new + in_current_dir do - RSpec::Core::Runner.run(cmd.split, stderr, stdout) + RSpec::Core::Runner.run(cmd.split, temp_stderr, temp_stdout) end ensure RSpec.reset + + @last_cmd_stdout = temp_stdout.string + @last_cmd_stderr = temp_stderr.string + stdout.write(@last_cmd_stdout) + stderr.write(@last_cmd_stderr) end def in_current_dir diff --git a/spec/support/formatter_support.rb b/spec/support/formatter_support.rb index c81c88c9ca..d2b82624d9 100644 --- a/spec/support/formatter_support.rb +++ b/spec/support/formatter_support.rb @@ -194,6 +194,7 @@ def example :full_description => "Example", :execution_result => result, :location => "", + :rerun_argument => "", :metadata => { :shared_group_inclusion_backtrace => [] } From ef7746b9afb96a503dd6935c30bfc635522de862 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 3 Jan 2015 14:01:00 -0800 Subject: [PATCH 212/275] Refactor to support 1.9.2. On 1.9.2, this was getting an error: NotImplementedError: super from singleton method that is defined to multiple classes is not supported; this will be fixed in 1.9.3 or later --- spec/support/aruba_support.rb | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/spec/support/aruba_support.rb b/spec/support/aruba_support.rb index 1499232894..c6864d8751 100644 --- a/spec/support/aruba_support.rb +++ b/spec/support/aruba_support.rb @@ -15,25 +15,20 @@ module ArubaLoader def run_command(cmd) temp_stdout = StringIO.new temp_stderr = StringIO.new + RSpec::Core::Metadata.instance_variable_set(:@relative_path_regex, nil) in_current_dir do RSpec::Core::Runner.run(cmd.split, temp_stderr, temp_stdout) end ensure RSpec.reset + RSpec::Core::Metadata.instance_variable_set(:@relative_path_regex, nil) @last_cmd_stdout = temp_stdout.string @last_cmd_stderr = temp_stderr.string stdout.write(@last_cmd_stdout) stderr.write(@last_cmd_stderr) end - - def in_current_dir - RSpec::Core::Metadata.instance_variable_set(:@relative_path_regex, nil) - super { yield } - ensure - RSpec::Core::Metadata.instance_variable_set(:@relative_path_regex, nil) - end end RSpec.configure do |c| From 15de144e970d418bbca7f8d2cef909fac4b9f771 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 3 Jan 2015 14:24:39 -0800 Subject: [PATCH 213/275] Prefer `flat_map` to `inject`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s faster and simpler. --- benchmarks/flat_map_vs_inject.rb | 24 ++++++++++++++++++++++++ lib/rspec/core/example_group.rb | 6 +++--- lib/rspec/core/world.rb | 4 +--- 3 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 benchmarks/flat_map_vs_inject.rb diff --git a/benchmarks/flat_map_vs_inject.rb b/benchmarks/flat_map_vs_inject.rb new file mode 100644 index 0000000000..ce9bfd4a93 --- /dev/null +++ b/benchmarks/flat_map_vs_inject.rb @@ -0,0 +1,24 @@ +require 'benchmark/ips' +require 'rspec/core/flat_map' + +words = %w[ foo bar bazz big small medium large tiny less more good bad mediocre ] + +Benchmark.ips do |x| + x.report("flat_map") do + words.flat_map(&:codepoints) + end + + x.report("inject") do + words.inject([]) { |a, w| a + w.codepoints } + end + + x.report("FlatMap.flat_map") do + RSpec::Core::FlatMap.flat_map(words, &:codepoints) + end +end + +__END__ + + flat_map 135.128k (± 9.1%) i/s - 680.089k + inject 98.048k (±10.5%) i/s - 491.370k +FlatMap.flat_map 118.231k (± 7.3%) i/s - 596.530k diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index c94ffd88f8..5d022b519b 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -394,7 +394,7 @@ def self.filtered_examples # @private def self.descendant_filtered_examples @descendant_filtered_examples ||= filtered_examples + - children.inject([]) { |a, e| a + e.descendant_filtered_examples } + FlatMap.flat_map(children, &:descendant_filtered_examples) end # @private @@ -404,7 +404,7 @@ def self.children # @private def self.descendants - @_descendants ||= [self] + children.inject([]) { |a, e| a + e.descendants } + @_descendants ||= [self] + FlatMap.flat_map(children, &:descendants) end ## @private @@ -536,7 +536,7 @@ def self.fail_fast? def self.declaration_line_numbers @declaration_line_numbers ||= [metadata[:line_number]] + examples.map { |e| e.metadata[:line_number] } + - children.inject([]) { |a, e| a + e.declaration_line_numbers } + FlatMap.flat_map(children, &:declaration_line_numbers) end # @private diff --git a/lib/rspec/core/world.rb b/lib/rspec/core/world.rb index 73abc127dc..94211edfa6 100644 --- a/lib/rspec/core/world.rb +++ b/lib/rspec/core/world.rb @@ -162,9 +162,7 @@ def announce_exclusion_filter(announcements) private def declaration_line_numbers - @line_numbers ||= example_groups.inject([]) do |lines, g| - lines + g.declaration_line_numbers - end + @declaration_line_numbers ||= FlatMap.flat_map(example_groups, &:declaration_line_numbers) end end end From 8ea024e6acc37a6a8f0a62365a43f5cc34bccce9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 4 Jan 2015 09:02:24 -0800 Subject: [PATCH 214/275] Add inject/concat benchmark as recommended by @yujinakayama. [ci skip] --- benchmarks/flat_map_vs_inject.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/benchmarks/flat_map_vs_inject.rb b/benchmarks/flat_map_vs_inject.rb index ce9bfd4a93..64eab6d09d 100644 --- a/benchmarks/flat_map_vs_inject.rb +++ b/benchmarks/flat_map_vs_inject.rb @@ -8,17 +8,21 @@ words.flat_map(&:codepoints) end - x.report("inject") do + x.report("inject (+)") do words.inject([]) { |a, w| a + w.codepoints } end + x.report("inject (concat)") do + words.inject([]) { |a, w| a.concat w.codepoints } + end + x.report("FlatMap.flat_map") do RSpec::Core::FlatMap.flat_map(words, &:codepoints) end end __END__ - - flat_map 135.128k (± 9.1%) i/s - 680.089k - inject 98.048k (±10.5%) i/s - 491.370k -FlatMap.flat_map 118.231k (± 7.3%) i/s - 596.530k + flat_map 136.445k (± 5.8%) i/s - 682.630k + inject (+) 99.557k (±10.0%) i/s - 496.368k + inject (concat) 120.902k (±14.6%) i/s - 598.400k +FlatMap.flat_map 121.461k (± 8.5%) i/s - 608.826k From cd52601a7fe0ea4c3f4f3f290f4336921afa11f7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 4 Jan 2015 09:07:34 -0800 Subject: [PATCH 215/275] `flat_map` only flattens one layer, so we should use `flatten(1)` --- lib/rspec/core/flat_map.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/core/flat_map.rb b/lib/rspec/core/flat_map.rb index cc4e5a4d30..b426aebece 100644 --- a/lib/rspec/core/flat_map.rb +++ b/lib/rspec/core/flat_map.rb @@ -8,7 +8,7 @@ def flat_map(array) end else # for 1.8.7 def flat_map(array) - array.map { |item| yield item }.flatten + array.map { |item| yield item }.flatten(1) end end From 940b7abda7e3a02ddd4d0ba44c4f9c8b16761b35 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 4 Jan 2015 14:19:22 -0800 Subject: [PATCH 216/275] Use forwarded block rather than `yield`. This appears to be faster in our benchmarks, and based also on what @yujinakayama has said. --- benchmarks/capture_block_vs_yield.rb | 180 +++++++++++++++++++++++++-- benchmarks/flat_map_vs_inject.rb | 41 ++++-- lib/rspec/core/flat_map.rb | 8 +- 3 files changed, 207 insertions(+), 22 deletions(-) diff --git a/benchmarks/capture_block_vs_yield.rb b/benchmarks/capture_block_vs_yield.rb index 84951d3734..2705527678 100644 --- a/benchmarks/capture_block_vs_yield.rb +++ b/benchmarks/capture_block_vs_yield.rb @@ -12,6 +12,8 @@ def capture_block_and_call(&block) block.call end +puts "Using the block directly" + Benchmark.ips do |x| x.report("yield ") do yield_control { } @@ -26,25 +28,181 @@ def capture_block_and_call(&block) end end +puts "Forwarding the block to another method" + +def tap_with_yield + 5.tap { |i| yield i } +end + +def tap_with_forwarded_block(&block) + 5.tap(&block) +end + +Benchmark.ips do |x| + x.report("tap { |i| yield i }") do + tap_with_yield { |i| } + end + + x.report("tap(&block) ") do + tap_with_forwarded_block { |i| } + end +end + +def yield_n_times(n) + n.times { yield } +end + +def forward_block_to_n_times(n, &block) + n.times(&block) +end + +def call_block_n_times(n, &block) + n.times { block.call } +end + +[10, 25, 50, 100, 1000, 10000].each do |count| + puts "Invoking the block #{count} times" + + Benchmark.ips do |x| + x.report("#{count}.times { yield } ") do + yield_n_times(count) { } + end + + x.report("#{count}.times(&block) ") do + forward_block_to_n_times(count) { } + end + + x.report("#{count}.times { block.call }") do + call_block_n_times(count) { } + end + end +end + __END__ -This benchmark demonstrates that `yield` is much, much faster -than capturing `&block` and calling it. In fact, the simple act -of capturing `&block`, even if we don't later reference `&block`, -incurs most of the cost, so we should avoid capturing blocks unless -we absolutely need to. +This benchmark demonstrates that capturing a block (e.g. `&block`) has +a high constant cost, taking about 5x longer than a single `yield` +(even if the block is never used!). + +However, fowarding a captured block can be faster than using `yield` +if the block is used many times (the breakeven point is at about 20-25 +invocations), so it appears that he per-invocation cost of `yield` +is higher than that of a captured-and-forwarded block. + +Note that there is no circumstance where using `block.call` is faster. +See also `flat_map_vs_inject.rb`, which appears to contradict these +results a little bit. + +Using the block directly Calculating ------------------------------------- yield - 93.104k i/100ms + 91.539k i/100ms capture block and yield - 52.682k i/100ms + 50.945k i/100ms capture block and call - 51.115k i/100ms + 50.923k i/100ms ------------------------------------------------- yield - 5.161M (±10.6%) i/s - 25.231M + 4.757M (± 6.0%) i/s - 23.709M capture block and yield - 1.141M (±22.0%) i/s - 5.426M + 1.112M (±20.7%) i/s - 5.349M capture block and call - 1.027M (±21.8%) i/s - 4.856M + 964.475k (±20.3%) i/s - 4.634M +Forwarding the block to another method +Calculating ------------------------------------- + tap { |i| yield i } 74.620k i/100ms + tap(&block) 51.382k i/100ms +------------------------------------------------- + tap { |i| yield i } 3.213M (± 6.3%) i/s - 16.043M + tap(&block) 970.418k (±18.6%) i/s - 4.727M +Invoking the block 10 times +Calculating ------------------------------------- +10.times { yield } + 49.151k i/100ms +10.times(&block) + 40.682k i/100ms +10.times { block.call } + 27.576k i/100ms +------------------------------------------------- +10.times { yield } + 908.673k (± 4.9%) i/s - 4.571M +10.times(&block) + 674.565k (±16.1%) i/s - 3.336M +10.times { block.call } + 385.056k (±10.3%) i/s - 1.930M +Invoking the block 25 times +Calculating ------------------------------------- +25.times { yield } + 29.874k i/100ms +25.times(&block) + 30.934k i/100ms +25.times { block.call } + 17.119k i/100ms +------------------------------------------------- +25.times { yield } + 416.342k (± 3.6%) i/s - 2.091M +25.times(&block) + 446.108k (±10.6%) i/s - 2.227M +25.times { block.call } + 201.264k (± 7.2%) i/s - 1.010M +Invoking the block 50 times +Calculating ------------------------------------- +50.times { yield } + 17.690k i/100ms +50.times(&block) + 21.760k i/100ms +50.times { block.call } + 9.961k i/100ms +------------------------------------------------- +50.times { yield } + 216.195k (± 5.7%) i/s - 1.079M +50.times(&block) + 280.217k (± 9.9%) i/s - 1.393M +50.times { block.call } + 112.754k (± 5.6%) i/s - 567.777k +Invoking the block 100 times +Calculating ------------------------------------- +100.times { yield } + 10.143k i/100ms +100.times(&block) + 13.688k i/100ms +100.times { block.call } + 5.551k i/100ms +------------------------------------------------- +100.times { yield } + 111.700k (± 3.6%) i/s - 568.008k +100.times(&block) + 163.638k (± 7.7%) i/s - 821.280k +100.times { block.call } + 58.472k (± 5.6%) i/s - 294.203k +Invoking the block 1000 times +Calculating ------------------------------------- +1000.times { yield } + 1.113k i/100ms +1000.times(&block) + 1.817k i/100ms +1000.times { block.call } + 603.000 i/100ms +------------------------------------------------- +1000.times { yield } + 11.156k (± 8.4%) i/s - 56.763k +1000.times(&block) + 18.551k (±10.1%) i/s - 92.667k +1000.times { block.call } + 6.206k (± 3.5%) i/s - 31.356k +Invoking the block 10000 times +Calculating ------------------------------------- +10000.times { yield } + 113.000 i/100ms +10000.times(&block) + 189.000 i/100ms +10000.times { block.call } + 61.000 i/100ms +------------------------------------------------- +10000.times { yield } + 1.150k (± 3.6%) i/s - 5.763k +10000.times(&block) + 1.896k (± 6.9%) i/s - 9.450k +10000.times { block.call } + 624.401 (± 3.0%) i/s - 3.172k diff --git a/benchmarks/flat_map_vs_inject.rb b/benchmarks/flat_map_vs_inject.rb index 64eab6d09d..6a99d190f1 100644 --- a/benchmarks/flat_map_vs_inject.rb +++ b/benchmarks/flat_map_vs_inject.rb @@ -1,8 +1,15 @@ require 'benchmark/ips' -require 'rspec/core/flat_map' words = %w[ foo bar bazz big small medium large tiny less more good bad mediocre ] +def flat_map_using_yield(array) + array.flat_map { |item| yield item } +end + +def flat_map_using_block(array, &block) + array.flat_map(&block) +end + Benchmark.ips do |x| x.report("flat_map") do words.flat_map(&:codepoints) @@ -16,13 +23,33 @@ words.inject([]) { |a, w| a.concat w.codepoints } end - x.report("FlatMap.flat_map") do - RSpec::Core::FlatMap.flat_map(words, &:codepoints) + x.report("flat_map_using_yield") do + flat_map_using_yield(words, &:codepoints) + end + + x.report("flat_map_using_block") do + flat_map_using_block(words, &:codepoints) end end __END__ - flat_map 136.445k (± 5.8%) i/s - 682.630k - inject (+) 99.557k (±10.0%) i/s - 496.368k - inject (concat) 120.902k (±14.6%) i/s - 598.400k -FlatMap.flat_map 121.461k (± 8.5%) i/s - 608.826k + +Surprisingly, `flat_map(&block)` appears to be faster than +`flat_map { yield }` in spite of the fact that our array here +is smaller than the break-even point of 20-25 measured in the +`capture_block_vs_yield.rb` benchmark. In fact, the forwaded-block +version remains faster in my benchmarks here no matter how small +I shrink the `words` array. I'm not sure why! + +Calculating ------------------------------------- + flat_map 10.594k i/100ms + inject (+) 8.357k i/100ms + inject (concat) 10.404k i/100ms +flat_map_using_yield 10.081k i/100ms +flat_map_using_block 11.683k i/100ms +------------------------------------------------- + flat_map 136.442k (±10.4%) i/s - 678.016k + inject (+) 98.024k (± 9.7%) i/s - 493.063k + inject (concat) 119.822k (±10.5%) i/s - 593.028k +flat_map_using_yield 112.284k (± 9.7%) i/s - 564.536k +flat_map_using_block 134.533k (± 6.3%) i/s - 677.614k diff --git a/lib/rspec/core/flat_map.rb b/lib/rspec/core/flat_map.rb index b426aebece..6e5e35385f 100644 --- a/lib/rspec/core/flat_map.rb +++ b/lib/rspec/core/flat_map.rb @@ -3,12 +3,12 @@ module Core # @private module FlatMap if [].respond_to?(:flat_map) - def flat_map(array) - array.flat_map { |item| yield item } + def flat_map(array, &block) + array.flat_map(&block) end else # for 1.8.7 - def flat_map(array) - array.map { |item| yield item }.flatten(1) + def flat_map(array, &block) + array.map(&block).flatten(2) end end From 4e31ae54111fe932cb91c4f883874f75fccc0342 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 7 Jan 2015 19:27:07 -0800 Subject: [PATCH 217/275] =?UTF-8?q?Ensure=20`relative=5Fpath=5Fregex`=20do?= =?UTF-8?q?esn=E2=80=99t=20get=20set=20to=20an=20invalid=20value.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/support/aruba_support.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/support/aruba_support.rb b/spec/support/aruba_support.rb index c6864d8751..062d89f1aa 100644 --- a/spec/support/aruba_support.rb +++ b/spec/support/aruba_support.rb @@ -24,6 +24,11 @@ def run_command(cmd) RSpec.reset RSpec::Core::Metadata.instance_variable_set(:@relative_path_regex, nil) + # Ensure it gets cached with a proper value -- if we leave it set to nil, + # and the next spec operates in a different dir, it could get set to an + # invalid value. + RSpec::Core::Metadata.relative_path_regex + @last_cmd_stdout = temp_stdout.string @last_cmd_stderr = temp_stderr.string stdout.write(@last_cmd_stdout) From 2c1a4a53becf618d1996fb1e91d953f278f835b2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 7 Jan 2015 17:39:13 -0800 Subject: [PATCH 218/275] Refactor: leverage `Metadata.ascend` rather than recursing in `relavant_line_numbers`. We also don't have to check if `metadata` is `nil`; it's not possible to get into `relavant_line_numbers` in that case. --- lib/rspec/core/example.rb | 2 +- lib/rspec/core/metadata.rb | 15 +++++++++++---- lib/rspec/core/metadata_filter.rb | 14 ++------------ 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 6864ef2b89..7d7e622813 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -96,7 +96,7 @@ def inspect_output def rerun_argument loaded_spec_files = RSpec.configuration.loaded_spec_files - Metadata.ascend(metadata) do |meta| + Metadata.ascending(metadata) do |meta| return meta[:location] if loaded_spec_files.include?(File.expand_path meta[:file_path]) end end diff --git a/lib/rspec/core/metadata.rb b/lib/rspec/core/metadata.rb index a0459f44fb..37331e4e51 100644 --- a/lib/rspec/core/metadata.rb +++ b/lib/rspec/core/metadata.rb @@ -51,11 +51,11 @@ def self.relative_path(line) end # @private - # Iteratively walks up from the given example metadata through all + # Iteratively walks up from the given metadata through all # example group ancestors, yielding each metadata hash along the way. - def self.ascend(example_metadata) - yield example_metadata - group_metadata = example_metadata.fetch(:example_group) + def self.ascending(metadata) + yield metadata + return unless (group_metadata = metadata.fetch(:example_group) { metadata[:parent_example_group] }) loop do yield group_metadata @@ -63,6 +63,13 @@ def self.ascend(example_metadata) end end + # @private + # Returns an enumerator that iteratively walks up the given metadata through all + # example group ancestors, yielding each metadata hash along the way. + def self.ascend(metadata) + enum_for(:ascending, metadata) + end + # @private # Used internally to build a hash from an args array. # Symbols are converted into hash keys with a value of `true`. diff --git a/lib/rspec/core/metadata_filter.rb b/lib/rspec/core/metadata_filter.rb index a13e0044cd..9b308a9467 100644 --- a/lib/rspec/core/metadata_filter.rb +++ b/lib/rspec/core/metadata_filter.rb @@ -54,13 +54,11 @@ def line_number_filter_applies?(line_numbers, metadata) end def relevant_line_numbers(metadata) - return [] unless metadata - [metadata[:line_number]].compact + (relevant_line_numbers(parent_of metadata)) + Metadata.ascend(metadata).map { |meta| meta[:line_number] } end def example_group_declaration_line(locations, metadata) - parent = parent_of(metadata) - return nil unless parent + return nil unless (parent = metadata.fetch(:example_group) { metadata[:parent_example_group] }) locations[File.expand_path(parent[:file_path])] end @@ -70,14 +68,6 @@ def filters_apply?(key, value, metadata) value.all? { |k, v| filter_applies?(k, v, subhash) } end - def parent_of(metadata) - if metadata.key?(:example_group) - metadata[:example_group] - else - metadata[:parent_example_group] - end - end - def silence_metadata_example_group_deprecations RSpec.thread_local_metadata[:silence_metadata_example_group_deprecations] = true yield From 10791a24ac5b9a45d580a8c825477352f8a14825 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 7 Jan 2015 19:36:51 -0800 Subject: [PATCH 219/275] Fix location filtering. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For examples defined in an example group in a shared example group in an external file, it did not work properly because the location filtering only considered the immediate parent example group’s file path, which did not match the passed location filter. By considering all parent example group file paths, it filters properly. Fixes #835. Note: this commit began from a failing spec that originated with @soulcutter and was fixed up by @xaviershay. --- Changelog.md | 3 +++ lib/rspec/core/metadata_filter.rb | 12 ++++----- .../shared_example_rerun_commands_spec.rb | 27 +++++++++++++++++++ spec/rspec/core/metadata_filter_spec.rb | 4 --- spec/support/aruba_support.rb | 14 ++++++++++ 5 files changed, 50 insertions(+), 10 deletions(-) diff --git a/Changelog.md b/Changelog.md index 1c389f4209..cdad07d069 100644 --- a/Changelog.md +++ b/Changelog.md @@ -54,6 +54,9 @@ Bug Fixes: * When a shared example defined in an external file fails, use the host example group (from a loaded spec file) for the re-run command to ensure the command will actually work. (Myron Marston, #1835) +* Fix location filtering to work properly for examples defined in + a nested example group within a shared example group defined in + an external file. (Bradley Schaefer, Xavier Shay, Myron Marston, #1837) ### 3.1.8 Development diff --git a/lib/rspec/core/metadata_filter.rb b/lib/rspec/core/metadata_filter.rb index 9b308a9467..ec25763768 100644 --- a/lib/rspec/core/metadata_filter.rb +++ b/lib/rspec/core/metadata_filter.rb @@ -43,9 +43,8 @@ def filter_applies_to_any_value?(key, value, metadata) end def location_filter_applies?(locations, metadata) - # it ignores location filters for other files - line_number = example_group_declaration_line(locations, metadata) - line_number ? line_number_filter_applies?(line_number, metadata) : true + line_numbers = example_group_declaration_lines(locations, metadata) + line_numbers ? line_number_filter_applies?(line_numbers, metadata) : true end def line_number_filter_applies?(line_numbers, metadata) @@ -57,9 +56,10 @@ def relevant_line_numbers(metadata) Metadata.ascend(metadata).map { |meta| meta[:line_number] } end - def example_group_declaration_line(locations, metadata) - return nil unless (parent = metadata.fetch(:example_group) { metadata[:parent_example_group] }) - locations[File.expand_path(parent[:file_path])] + def example_group_declaration_lines(locations, metadata) + FlatMap.flat_map(Metadata.ascend(metadata)) do |meta| + locations[File.expand_path(meta[:file_path])] + end.uniq end def filters_apply?(key, value, metadata) diff --git a/spec/integration/shared_example_rerun_commands_spec.rb b/spec/integration/shared_example_rerun_commands_spec.rb index a506c536cb..2964148267 100644 --- a/spec/integration/shared_example_rerun_commands_spec.rb +++ b/spec/integration/shared_example_rerun_commands_spec.rb @@ -36,4 +36,31 @@ def run_rerun_command_for_failing_spec command = last_cmd_stdout[/Failed examples:\s+rspec (\S+) #/, 1] run_command command end + + context "with a shared example containing a context in a separate file" do + it "runs the example nested inside the shared" do + write_file_formatted 'spec/shared_example.rb', """ + RSpec.shared_examples_for 'a shared example' do + it 'succeeds' do + end + + context 'with a nested context' do + it 'succeeds (nested)' do + end + end + end + """ + + write_file_formatted 'spec/simple_spec.rb', """ + require File.join(File.dirname(__FILE__), 'shared_example.rb') + + RSpec.describe 'top level' do + it_behaves_like 'a shared example' + end + """ + + run_command 'spec/simple_spec.rb:3 -fd' + expect(last_cmd_stdout).to match(/2 examples, 0 failures/) + end + end end diff --git a/spec/rspec/core/metadata_filter_spec.rb b/spec/rspec/core/metadata_filter_spec.rb index 44bc62fbd9..80032d5860 100644 --- a/spec/rspec/core/metadata_filter_spec.rb +++ b/spec/rspec/core/metadata_filter_spec.rb @@ -84,10 +84,6 @@ def filter_applies?(key, value, metadata) end end - it "ignores location filters for other files" do - expect(filter_applies?(:locations, {"/path/to/other_spec.rb" => [3,5,7]}, example_metadata)).to be_truthy - end - it "matches a proc with no arguments that evaluates to true" do expect(filter_applies?(:if, lambda { true }, example_metadata)).to be_truthy end diff --git a/spec/support/aruba_support.rb b/spec/support/aruba_support.rb index 062d89f1aa..662ddbb97d 100644 --- a/spec/support/aruba_support.rb +++ b/spec/support/aruba_support.rb @@ -34,6 +34,20 @@ def run_command(cmd) stdout.write(@last_cmd_stdout) stderr.write(@last_cmd_stderr) end + + def write_file_formatted(file_name, contents) + # remove blank line at the start of the string and + # strip extra indentation. + formatted_contents = unindent(contents.sub(/\A\n/, "")) + write_file file_name, formatted_contents + end + + # Intended for use with indented heredocs. + # taken from Ruby Tapas: + # https://rubytapas.dpdcart.com/subscriber/post?id=616#files + def unindent(s) + s.gsub(/^#{s.scan(/^[ \t]+(?=\S)/).min}/, "") + end end RSpec.configure do |c| From f0128415501a0899db11ca1817c620b51239d7f5 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 7 Jan 2015 22:08:46 -0800 Subject: [PATCH 220/275] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 5 +---- appveyor.yml | 2 +- script/clone_all_rspec_repos | 2 +- script/functions.sh | 2 +- script/predicate_functions.sh | 2 +- script/run_build | 2 +- script/travis_functions.sh | 2 +- 8 files changed, 8 insertions(+), 11 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 91d8cfb405..f7bea1c203 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index 94ebd55c43..ea4f1b0fcc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby @@ -34,7 +34,4 @@ matrix: - rvm: jruby-head - rvm: ruby-head - rvm: rbx - # These two are temporary until https://github.com/travis-ci/travis-ci/issues/3067 is solved. - - rvm: jruby-18mode - - rvm: jruby fast_finish: true diff --git a/appveyor.yml b/appveyor.yml index 365f61c286..9e4f457e30 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index 5db1115440..f83d2e910f 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/functions.sh b/script/functions.sh index 75fe0465d3..a96a5c71b4 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index a9956fc55b..fc5d372c50 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { diff --git a/script/run_build b/script/run_build index d7ac729082..e1edcef3a9 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 3928cc6b66..77829b3638 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-01T16:46:16-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From dcf0e07e108c2edf3b615fa16c11d8857d723c5d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 7 Jan 2015 23:37:30 -0800 Subject: [PATCH 221/275] Rename spec file to better reflect its contents. --- .../{shared_example_rerun_commands_spec.rb => filtering_spec.rb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename spec/integration/{shared_example_rerun_commands_spec.rb => filtering_spec.rb} (100%) diff --git a/spec/integration/shared_example_rerun_commands_spec.rb b/spec/integration/filtering_spec.rb similarity index 100% rename from spec/integration/shared_example_rerun_commands_spec.rb rename to spec/integration/filtering_spec.rb From 735325d707836fab637be2d60ccbaa78cb79b6cf Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 7 Jan 2015 23:40:50 -0800 Subject: [PATCH 222/275] Apply line number filters only to the scoped file. Fixes #952. --- Changelog.md | 2 ++ lib/rspec/core/metadata_filter.rb | 2 +- spec/integration/filtering_spec.rb | 22 ++++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index cdad07d069..c4686a05a4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -36,6 +36,8 @@ Enhancements: testing RSpec with RSpec, allowing you to define example groups and example from within an example without affecting the global `RSpec.world` state. (Tyler Ball, 1808) +* Apply line-number filters only to the files they are scoped to, + allowing you to mix filtered and unfiltered files. (Myron Marston, #1839) Bug Fixes: diff --git a/lib/rspec/core/metadata_filter.rb b/lib/rspec/core/metadata_filter.rb index ec25763768..5e36422772 100644 --- a/lib/rspec/core/metadata_filter.rb +++ b/lib/rspec/core/metadata_filter.rb @@ -44,7 +44,7 @@ def filter_applies_to_any_value?(key, value, metadata) def location_filter_applies?(locations, metadata) line_numbers = example_group_declaration_lines(locations, metadata) - line_numbers ? line_number_filter_applies?(line_numbers, metadata) : true + line_numbers.empty? || line_number_filter_applies?(line_numbers, metadata) end def line_number_filter_applies?(line_numbers, metadata) diff --git a/spec/integration/filtering_spec.rb b/spec/integration/filtering_spec.rb index 2964148267..450111395a 100644 --- a/spec/integration/filtering_spec.rb +++ b/spec/integration/filtering_spec.rb @@ -63,4 +63,26 @@ def run_rerun_command_for_failing_spec expect(last_cmd_stdout).to match(/2 examples, 0 failures/) end end + + context "passing a line-number-filtered file and a non-filtered file" do + it "applies the line number filtering only to the filtered file, running all specs in the non-filtered file" do + write_file_formatted "spec/file_1_spec.rb", """ + RSpec.describe 'File 1' do + it('passes') { } + it('fails') { fail } + end + """ + + write_file_formatted "spec/file_2_spec.rb", """ + RSpec.describe 'File 2' do + it('passes') { } + it('passes') { } + end + """ + + run_command "spec/file_1_spec.rb:2 spec/file_2_spec.rb -fd" + expect(last_cmd_stdout).to match(/3 examples, 0 failures/) + expect(last_cmd_stdout).not_to match(/fails/) + end + end end From a4a3a5865870a286495682b88d9a47c8bdad5354 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 8 Jan 2015 19:12:50 -0800 Subject: [PATCH 223/275] Updated travis build scripts (from rspec-dev) --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 27 +++++++++++++-------------- script/functions.sh | 21 +++++++++++++++------ script/predicate_functions.sh | 14 +++++++++++++- script/run_build | 8 ++++---- script/travis_functions.sh | 2 +- 8 files changed, 49 insertions(+), 29 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index f7bea1c203..a8c48a397e 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index ea4f1b0fcc..da3b49f159 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index 9e4f457e30..d9bbbf668d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index f83d2e910f..c146d24a37 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,24 +1,23 @@ #!/bin/bash -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e source script/functions.sh -if is_mri; then - pushd .. +pushd .. - clone_repo "rspec" - clone_repo "rspec-core" - clone_repo "rspec-expectations" - clone_repo "rspec-mocks" - clone_repo "rspec-rails" +clone_repo "rspec" +clone_repo "rspec-core" +clone_repo "rspec-expectations" +clone_repo "rspec-mocks" - if rspec_support_compatible; then - clone_repo "rspec-support" - fi +if is_mri; then + clone_repo "rspec-rails" +fi - popd -else - echo "Not cloning all repos since we are not on MRI and they are only needed for the MRI build" +if rspec_support_compatible; then + clone_repo "rspec-support" fi + +popd diff --git a/script/functions.sh b/script/functions.sh index a96a5c71b4..256e3855a8 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -58,9 +58,13 @@ function run_cukes { function run_specs_one_by_one { echo "Running each spec file, one-by-one..." - for file in `find spec -iname '*_spec.rb'`; do - bin/rspec $file -b --format progress - done + if is_mri; then + for file in `find spec -iname '*_spec.rb'`; do + bin/rspec $file -b --format progress + done + else + echo "Skipping one-by-one specs on non-MRI rubies as they tend to have long boot times" + fi } function run_spec_suite_for { @@ -69,7 +73,7 @@ function run_spec_suite_for { pushd ../$1 unset BUNDLE_GEMFILE bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` - travis_retry eval "bundle install $bundle_install_flags" + travis_retry eval "time bundle install $bundle_install_flags" run_specs_and_record_done popd fi; @@ -121,7 +125,12 @@ function run_all_spec_suites { fold "rspec-core specs" run_spec_suite_for "rspec-core" fold "rspec-expectations specs" run_spec_suite_for "rspec-expectations" fold "rspec-mocks specs" run_spec_suite_for "rspec-mocks" - fold "rspec-rails specs" run_spec_suite_for "rspec-rails" + + if is_mri; then + fold "rspec-rails specs" run_spec_suite_for "rspec-rails" + else + echo "Skipping rspec-rails specs on non-MRI rubies" + fi if rspec_support_compatible; then fold "rspec-support specs" run_spec_suite_for "rspec-support" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index fc5d372c50..22ddee6a9c 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { @@ -11,6 +11,18 @@ function is_mri { fi; } +function is_jruby_20_mode { + if [ -z "$JRUBY_OPTS" ]; then + if ruby -e "exit(RUBY_VERSION == '2.0.0')"; then + return 0 + else + return 1 + fi + else + return 1 + fi +} + function is_mri_192 { if is_mri; then if ruby -e "exit(RUBY_VERSION == '1.9.2')"; then diff --git a/script/run_build b/script/run_build index e1edcef3a9..e804fe9e5e 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e @@ -21,8 +21,8 @@ if style_and_lint_enforced; then fold "rubocop" check_style_and_lint fi -if is_mri; then - run_all_spec_suites +if is_jruby_20_mode; then + echo "Skipping other specs suites on JRuby 2.0 mode because it is so much slower" else - echo "Skipping the rest of the build on non-MRI rubies" + run_all_spec_suites fi diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 77829b3638..893d302d62 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 3cb6f39f32df096fa4bb9afe137ff6567dbfda77 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 8 Jan 2015 13:56:22 -0800 Subject: [PATCH 224/275] Add missing integration spec coverage. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are unit specs on the filter manager that cover the filter manager’s involvement in this behavior but nothing covered it as a whole, and I’m working on a bug fix that may require changes to the filter manager’s behavior, so covering it with an integration spec ensures we don’t regress. --- spec/integration/filtering_spec.rb | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/spec/integration/filtering_spec.rb b/spec/integration/filtering_spec.rb index 450111395a..f0a3bce348 100644 --- a/spec/integration/filtering_spec.rb +++ b/spec/integration/filtering_spec.rb @@ -64,6 +64,39 @@ def run_rerun_command_for_failing_spec end end + context "passing a line-number filter" do + it "trumps exclusions, except for :if/:unless (which are absolute exclusions)" do + write_file_formatted 'spec/a_spec.rb', """ + RSpec.configure do |c| + c.filter_run_excluding :slow + end + + RSpec.describe 'A slow group', :slow do + example('ex 1') { } + example('ex 2') { } + end + + RSpec.describe 'A group with a slow example' do + example('ex 3' ) { } + example('ex 4', :slow ) { } + example('ex 5', :if => false) { } + end + """ + + run_command "spec/a_spec.rb -fd" + expect(last_cmd_stdout).to include("1 example, 0 failures", "ex 3").and exclude("ex 1", "ex 2", "ex 4", "ex 5") + + run_command "spec/a_spec.rb:5 -fd" # selecting 'A slow group' + expect(last_cmd_stdout).to include("2 examples, 0 failures", "ex 1", "ex 2").and exclude("ex 3", "ex 4", "ex 5") + + run_command "spec/a_spec.rb:12 -fd" # selecting slow example + expect(last_cmd_stdout).to include("1 example, 0 failures", "ex 4").and exclude("ex 1", "ex 2", "ex 3", "ex 5") + + run_command "spec/a_spec.rb:13 -fd" # selecting :if => false example + expect(last_cmd_stdout).to include("0 examples, 0 failures").and exclude("ex 1", "ex 2", "ex 3", "ex 4", "ex 5") + end + end + context "passing a line-number-filtered file and a non-filtered file" do it "applies the line number filtering only to the filtered file, running all specs in the non-filtered file" do write_file_formatted "spec/file_1_spec.rb", """ From c6fc8fc23ad24fc38ad7e6709d2973d9d291b591 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 8 Jan 2015 22:32:57 -0800 Subject: [PATCH 225/275] Shrink API surface area of FilterManager. Less API surface area makes refactoring easier. --- lib/rspec/core/filter_manager.rb | 10 ++++++---- spec/rspec/core/filter_manager_spec.rb | 20 ++++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/rspec/core/filter_manager.rb b/lib/rspec/core/filter_manager.rb index 6bc1d03c27..4a68d87eaf 100644 --- a/lib/rspec/core/filter_manager.rb +++ b/lib/rspec/core/filter_manager.rb @@ -111,10 +111,6 @@ def exclude_with_low_priority(*args) exclusions.add_with_low_priority(args.last) end - def exclude?(example) - exclusions.include_example?(example) - end - def include(*args) inclusions.add(args.last) end @@ -127,6 +123,12 @@ def include_with_low_priority(*args) inclusions.add_with_low_priority(args.last) end + private + + def exclude?(example) + exclusions.include_example?(example) + end + def include?(example) inclusions.include_example?(example) end diff --git a/spec/rspec/core/filter_manager_spec.rb b/spec/rspec/core/filter_manager_spec.rb index 9f5751392a..acb113e57f 100644 --- a/spec/rspec/core/filter_manager_spec.rb +++ b/spec/rspec/core/filter_manager_spec.rb @@ -260,49 +260,53 @@ def example_with_metadata(metadata) value end + def exclude?(example) + filter_manager.prune([example]).empty? + end + describe "the default :if filter" do it "does not exclude a spec with { :if => true } metadata" do example = example_with_metadata(:if => true) - expect(filter_manager.exclude?(example)).to be_falsey + expect(exclude?(example)).to be_falsey end it "excludes a spec with { :if => false } metadata" do example = example_with_metadata(:if => false) - expect(filter_manager.exclude?(example)).to be_truthy + expect(exclude?(example)).to be_truthy end it "excludes a spec with { :if => nil } metadata" do example = example_with_metadata(:if => nil) - expect(filter_manager.exclude?(example)).to be_truthy + expect(exclude?(example)).to be_truthy end it "continues to be an exclusion even if exclusions are cleared" do example = example_with_metadata(:if => false) filter_manager.exclusions.clear - expect(filter_manager.exclude?(example)).to be_truthy + expect(exclude?(example)).to be_truthy end end describe "the default :unless filter" do it "excludes a spec with { :unless => true } metadata" do example = example_with_metadata(:unless => true) - expect(filter_manager.exclude?(example)).to be_truthy + expect(exclude?(example)).to be_truthy end it "does not exclude a spec with { :unless => false } metadata" do example = example_with_metadata(:unless => false) - expect(filter_manager.exclude?(example)).to be_falsey + expect(exclude?(example)).to be_falsey end it "does not exclude a spec with { :unless => nil } metadata" do example = example_with_metadata(:unless => nil) - expect(filter_manager.exclude?(example)).to be_falsey + expect(exclude?(example)).to be_falsey end it "continues to be an exclusion even if exclusions are cleared" do example = example_with_metadata(:unless => true) filter_manager.exclusions.clear - expect(filter_manager.exclude?(example)).to be_truthy + expect(exclude?(example)).to be_truthy end end end From ce4ff601b4bb5c6ee9232630554d9cac77f5111c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 8 Jan 2015 23:45:23 -0800 Subject: [PATCH 226/275] Refactor: apply conditional filters as a first, separate step. --- lib/rspec/core/filter_manager.rb | 36 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/rspec/core/filter_manager.rb b/lib/rspec/core/filter_manager.rb index 4a68d87eaf..92fe2be0ae 100644 --- a/lib/rspec/core/filter_manager.rb +++ b/lib/rspec/core/filter_manager.rb @@ -91,9 +91,10 @@ def empty? end def prune(examples) + examples = prune_conditionally_filtered_examples(examples) + if inclusions.standalone? - base_exclusions = ExclusionRules.new - examples.select { |e| !base_exclusions.include_example?(e) && include?(e) } + examples.select { |e| include?(e) } else examples.select { |e| !exclude?(e) && include?(e) } end @@ -132,6 +133,13 @@ def exclude?(example) def include?(example) inclusions.include_example?(example) end + + def prune_conditionally_filtered_examples(examples) + examples.reject do |ex| + meta = ex.metadata + !meta.fetch(:if, true) || meta[:unless] + end + end end # @private @@ -196,8 +204,15 @@ def each_pair(&block) def description rules.inspect.gsub(PROC_HEX_NUMBER, '').gsub(PROJECT_DIR, '.').gsub(' (lambda)', '') end + + def include_example?(example) + MetadataFilter.apply?(:any?, @rules, example.metadata) + end end + # @private + ExclusionRules = FilterRules + # @private class InclusionRules < FilterRules STANDALONE_FILTERS = [:locations, :full_description] @@ -219,8 +234,7 @@ def use(*args) end def include_example?(example) - return true if @rules.empty? - MetadataFilter.apply?(:any?, @rules, example.metadata) + @rules.empty? || super end def standalone? @@ -246,19 +260,5 @@ def is_standalone_filter?(rules) STANDALONE_FILTERS.any? { |key| rules.key?(key) } end end - - # @private - class ExclusionRules < FilterRules - CONDITIONAL_FILTERS = { - :if => lambda { |value| !value }, - :unless => lambda { |value| value } - }.freeze - - def include_example?(example) - example_meta = example.metadata - return true if MetadataFilter.apply?(:any?, @rules, example_meta) - MetadataFilter.apply?(:any?, CONDITIONAL_FILTERS, example_meta) - end - end end end From 35137e0c9691f75fd9ad1aa7627698fbaba6c4f1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 8 Jan 2015 13:30:14 -0800 Subject: [PATCH 227/275] Stop treating location filters as global standalone filters. Location filters are contextual: they only apply to specific spec files. Examples from other files should be unaffected by them, and should still have exclusions applied to them. This is a follow up to #1839. --- lib/rspec/core/filter_manager.rb | 23 ++++++----- spec/integration/filtering_spec.rb | 7 +++- spec/rspec/core/configuration_spec.rb | 15 ------- spec/rspec/core/filter_manager_spec.rb | 55 +++++++++++++++++--------- 4 files changed, 56 insertions(+), 44 deletions(-) diff --git a/lib/rspec/core/filter_manager.rb b/lib/rspec/core/filter_manager.rb index 92fe2be0ae..b61bd547f8 100644 --- a/lib/rspec/core/filter_manager.rb +++ b/lib/rspec/core/filter_manager.rb @@ -83,7 +83,7 @@ def add_location(file_path, line_numbers) # { "path/to/file.rb" => [37, 42] } locations = inclusions.delete(:locations) || Hash.new { |h, k| h[k] = [] } locations[File.expand_path(file_path)].push(*line_numbers) - inclusions.add_location(locations) + inclusions.add(:locations => locations) end def empty? @@ -96,7 +96,8 @@ def prune(examples) if inclusions.standalone? examples.select { |e| include?(e) } else - examples.select { |e| !exclude?(e) && include?(e) } + locations = inclusions.fetch(:locations) { Hash.new([]) } + examples.select { |e| priority_include?(e, locations) || (!exclude?(e) && include?(e)) } end end @@ -140,6 +141,16 @@ def prune_conditionally_filtered_examples(examples) !meta.fetch(:if, true) || meta[:unless] end end + + # When a user specifies a particular spec location, that takes priority + # over any exclusion filters (such as if the spec is tagged with `:slow` + # and there is a `:slow => true` exclusion filter), but only for specs + # defined in the same file as the location filters. Excluded specs in + # other files should still be excluded. + def priority_include?(example, locations) + return false if locations[File.expand_path(example.metadata[:file_path])].empty? + MetadataFilter.filter_applies?(:locations, locations, example.metadata) + end end # @private @@ -215,12 +226,6 @@ def include_example?(example) # @private class InclusionRules < FilterRules - STANDALONE_FILTERS = [:locations, :full_description] - - def add_location(locations) - replace_filters(:locations => locations) - end - def add(*args) apply_standalone_filter(*args) || super end @@ -257,7 +262,7 @@ def replace_filters(new_rules) end def is_standalone_filter?(rules) - STANDALONE_FILTERS.any? { |key| rules.key?(key) } + rules.key?(:full_description) end end end diff --git a/spec/integration/filtering_spec.rb b/spec/integration/filtering_spec.rb index f0a3bce348..5be76d7c5e 100644 --- a/spec/integration/filtering_spec.rb +++ b/spec/integration/filtering_spec.rb @@ -98,7 +98,7 @@ def run_rerun_command_for_failing_spec end context "passing a line-number-filtered file and a non-filtered file" do - it "applies the line number filtering only to the filtered file, running all specs in the non-filtered file" do + it "applies the line number filtering only to the filtered file, running all specs in the non-filtered file except excluded ones" do write_file_formatted "spec/file_1_spec.rb", """ RSpec.describe 'File 1' do it('passes') { } @@ -107,9 +107,14 @@ def run_rerun_command_for_failing_spec """ write_file_formatted "spec/file_2_spec.rb", """ + RSpec.configure do |c| + c.filter_run_excluding :exclude_me + end + RSpec.describe 'File 2' do it('passes') { } it('passes') { } + it('fails', :exclude_me) { fail } end """ diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index 280aeb62f4..eae31f8f0d 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -472,14 +472,6 @@ def stub_expectation_adapters end context "with :" do - it "overrides inclusion filters set on config" do - config.filter_run_including :foo => :bar - assign_files_or_directories_to_run "path/to/file.rb:37" - expect(inclusion_filter.size).to eq(1) - expect(inclusion_filter[:locations].keys.first).to match(/path\/to\/file\.rb$/) - expect(inclusion_filter[:locations].values.first).to eq([37]) - end - it "overrides inclusion filters set before config" do config.force(:inclusion_filter => {:foo => :bar}) assign_files_or_directories_to_run "path/to/file.rb:37" @@ -488,13 +480,6 @@ def stub_expectation_adapters expect(inclusion_filter[:locations].values.first).to eq([37]) end - it "clears exclusion filters set on config" do - config.exclusion_filter = { :foo => :bar } - assign_files_or_directories_to_run "path/to/file.rb:37" - expect(exclusion_filter).to be_empty, - "expected exclusion filter to be empty:\n#{exclusion_filter}" - end - it "clears exclusion filters set before config" do config.force(:exclusion_filter => { :foo => :bar }) assign_files_or_directories_to_run "path/to/file.rb:37" diff --git a/spec/rspec/core/filter_manager_spec.rb b/spec/rspec/core/filter_manager_spec.rb index acb113e57f..676a6ba3f4 100644 --- a/spec/rspec/core/filter_manager_spec.rb +++ b/spec/rspec/core/filter_manager_spec.rb @@ -34,25 +34,23 @@ def opposite(name) end if name == "include" - [:locations, :full_description].each do |filter| - context "with :#{filter}" do - it "clears previous inclusions" do - filter_manager.include :foo => :bar - filter_manager.include filter => "value" - expect(rules).to eq({filter => "value"}) - end - - it "clears previous exclusion" do - filter_manager.include :foo => :bar - filter_manager.include filter => "value" - expect(opposite_rules).to be_empty - end - - it "does nothing when :#{filter} previously set" do - filter_manager.include filter => "a_value" - filter_manager.include :foo => :bar - expect(rules).to eq(filter => "a_value") - end + context "with :full_description" do + it "clears previous inclusions" do + filter_manager.include :foo => :bar + filter_manager.include :full_description => "value" + expect(rules).to eq(:full_description => "value") + end + + it "clears previous exclusion" do + filter_manager.include :foo => :bar + filter_manager.include :full_description => "value" + expect(opposite_rules).to be_empty + end + + it "does nothing when :full_description previously set" do + filter_manager.include :full_description => "a_value" + filter_manager.include :foo => :bar + expect(rules).to eq(:full_description => "a_value") end end end @@ -127,6 +125,25 @@ def example_with(*args) expect(filter_manager.prune([included, excluded])).to eq([included]) end + context "with examples from multiple spec source files" do + it "applies exclusions only to examples defined in files with no location filters" do + group = RSpec.describe("group") + line = __LINE__ + 1 + this_file_example = group.example("ex 1", :slow) { } + + # Using eval in order to make ruby think this got defined in another file. + other_file_example = instance_eval "ex = nil; RSpec.describe('group') { ex = it('ex 2', :slow) { } }; ex", "some/external/file.rb", 1 + + filter_manager.exclude_with_low_priority :slow => true + + expect { + filter_manager.add_location(__FILE__, [line]) + }.to change { + filter_manager.prune([this_file_example, other_file_example]).map(&:description) + }.from([]).to([this_file_example.description]) + end + end + it "prefers description to exclusion filter" do group = RSpec.describe("group") included = group.example("include", :slow => true) {} From 4a62488cb96cbc7c95960580bbbf72423233e109 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 9 Jan 2015 00:26:18 -0800 Subject: [PATCH 228/275] Fix example group doc string to be more accurate. --- spec/integration/filtering_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/integration/filtering_spec.rb b/spec/integration/filtering_spec.rb index 5be76d7c5e..46feb427a7 100644 --- a/spec/integration/filtering_spec.rb +++ b/spec/integration/filtering_spec.rb @@ -1,6 +1,6 @@ require 'support/aruba_support' -RSpec.describe 'Shared Example Rerun Commands' do +RSpec.describe 'Filtering' do include_context "aruba support" before { clean_current_dir } From 0a7eba306cf42101519a1d5d65a44205a3b3ca32 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 9 Jan 2015 00:35:19 -0800 Subject: [PATCH 229/275] Expand `metadata[:file_path]` only once. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before we were calling `File.expand_path` and passing `metadata[:file_path]` in many places, re-doing the same calculation over and over again. It’s more performant to only do that calculation once and store it in the metadata hash for later re-use. Note: since this now treats `:absolute_file_path` as a reserved metadata key, this could be a breaking change for some people, but I think it’s highly unlikely any users would use that as a metadata key. --- .../precalculate_absolute_file_path_or_not.rb | 29 +++++++++++++++++++ lib/rspec/core/example.rb | 2 +- lib/rspec/core/filter_manager.rb | 2 +- lib/rspec/core/metadata.rb | 10 ++++--- lib/rspec/core/metadata_filter.rb | 2 +- lib/rspec/core/notifications.rb | 2 +- spec/rspec/core/metadata_filter_spec.rb | 6 ++-- spec/rspec/core/notifications_spec.rb | 2 +- 8 files changed, 43 insertions(+), 12 deletions(-) create mode 100644 benchmarks/precalculate_absolute_file_path_or_not.rb diff --git a/benchmarks/precalculate_absolute_file_path_or_not.rb b/benchmarks/precalculate_absolute_file_path_or_not.rb new file mode 100644 index 0000000000..3810bce4a2 --- /dev/null +++ b/benchmarks/precalculate_absolute_file_path_or_not.rb @@ -0,0 +1,29 @@ +require 'benchmark/ips' + +metadata = { :file_path => "some/path.rb" } +meta_with_absolute = metadata.merge(:absolute_file_path => File.expand_path(metadata[:file_path])) + +Benchmark.ips do |x| + x.report("fetch absolute path from hash") do + meta_with_absolute[:absolute_file_path] + end + + x.report("calculate absolute path") do + File.expand_path(metadata[:file_path]) + end +end + +__END__ + +Precalculating the absolute file path is much, much faster! + +Calculating ------------------------------------- +fetch absolute path from hash + 102.164k i/100ms +calculate absolute path + 9.331k i/100ms +------------------------------------------------- +fetch absolute path from hash + 7.091M (±11.6%) i/s - 34.736M +calculate absolute path + 113.141k (± 8.6%) i/s - 569.191k diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 7d7e622813..683cebdf9d 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -97,7 +97,7 @@ def rerun_argument loaded_spec_files = RSpec.configuration.loaded_spec_files Metadata.ascending(metadata) do |meta| - return meta[:location] if loaded_spec_files.include?(File.expand_path meta[:file_path]) + return meta[:location] if loaded_spec_files.include?(meta[:absolute_file_path]) end end diff --git a/lib/rspec/core/filter_manager.rb b/lib/rspec/core/filter_manager.rb index b61bd547f8..07938832da 100644 --- a/lib/rspec/core/filter_manager.rb +++ b/lib/rspec/core/filter_manager.rb @@ -148,7 +148,7 @@ def prune_conditionally_filtered_examples(examples) # defined in the same file as the location filters. Excluded specs in # other files should still be excluded. def priority_include?(example, locations) - return false if locations[File.expand_path(example.metadata[:file_path])].empty? + return false if locations[example.metadata[:absolute_file_path]].empty? MetadataFilter.filter_applies?(:locations, locations, example.metadata) end end diff --git a/lib/rspec/core/metadata.rb b/lib/rspec/core/metadata.rb index 37331e4e51..6f71c4bf09 100644 --- a/lib/rspec/core/metadata.rb +++ b/lib/rspec/core/metadata.rb @@ -146,10 +146,11 @@ def populate_location_attributes file_path_and_line_number_from(caller) end - file_path = Metadata.relative_path(file_path) - metadata[:file_path] = file_path - metadata[:line_number] = line_number.to_i - metadata[:location] = "#{file_path}:#{line_number}" + relative_file_path = Metadata.relative_path(file_path) + metadata[:file_path] = relative_file_path + metadata[:line_number] = line_number.to_i + metadata[:location] = "#{relative_file_path}:#{line_number}" + metadata[:absolute_file_path] = File.expand_path(relative_file_path) end def file_path_and_line_number_from(backtrace) @@ -311,6 +312,7 @@ def full_description :parent_example_group, :execution_result, :file_path, + :absolute_file_path, :full_description, :line_number, :location, diff --git a/lib/rspec/core/metadata_filter.rb b/lib/rspec/core/metadata_filter.rb index 5e36422772..c4fcd919fb 100644 --- a/lib/rspec/core/metadata_filter.rb +++ b/lib/rspec/core/metadata_filter.rb @@ -58,7 +58,7 @@ def relevant_line_numbers(metadata) def example_group_declaration_lines(locations, metadata) FlatMap.flat_map(Metadata.ascend(metadata)) do |meta| - locations[File.expand_path(meta[:file_path])] + locations[meta[:absolute_file_path]] end.uniq end diff --git a/lib/rspec/core/notifications.rb b/lib/rspec/core/notifications.rb index f0a678c2cc..41e77fceb8 100644 --- a/lib/rspec/core/notifications.rb +++ b/lib/rspec/core/notifications.rb @@ -259,7 +259,7 @@ def read_failed_line end def find_failed_line - example_path = File.expand_path(example.file_path).downcase + example_path = example.metadata[:absolute_file_path].downcase exception.backtrace.find do |line| next unless (line_path = line[/(.+?):(\d+)(|:\d+)/, 1]) File.expand_path(line_path).downcase == example_path diff --git a/spec/rspec/core/metadata_filter_spec.rb b/spec/rspec/core/metadata_filter_spec.rb index 80032d5860..98b77b32fb 100644 --- a/spec/rspec/core/metadata_filter_spec.rb +++ b/spec/rspec/core/metadata_filter_spec.rb @@ -7,9 +7,9 @@ module Core def create_metadatas container = self - RSpec.describe "parent group", :caller => ["foo_spec.rb:#{__LINE__}"] do; container.parent_group_metadata = metadata - describe "group", :caller => ["foo_spec.rb:#{__LINE__}"] do; container.group_metadata = metadata - container.example_metadata = it("example", :caller => ["foo_spec.rb:#{__LINE__}"], :if => true).metadata + RSpec.describe "parent group", :caller => ["/foo_spec.rb:#{__LINE__}"] do; container.parent_group_metadata = metadata + describe "group", :caller => ["/foo_spec.rb:#{__LINE__}"] do; container.group_metadata = metadata + container.example_metadata = it("example", :caller => ["/foo_spec.rb:#{__LINE__}"], :if => true).metadata end end end diff --git a/spec/rspec/core/notifications_spec.rb b/spec/rspec/core/notifications_spec.rb index 665b69c400..83428f5026 100644 --- a/spec/rspec/core/notifications_spec.rb +++ b/spec/rspec/core/notifications_spec.rb @@ -7,7 +7,7 @@ let(:notification) { ::RSpec::Core::Notifications::FailedExampleNotification.new(example) } before do - allow(example).to receive(:file_path) { __FILE__ } + example.metadata[:absolute_file_path] = __FILE__ end # ported from `base_formatter_spec` should be refactored by final From 0cf9f7f1b12084ca0d831fa2227997255e09829f Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 9 Jan 2015 09:43:10 -0800 Subject: [PATCH 230/275] Clarify how `order --defined` works. Closes #1756. --- features/command_line/order.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/features/command_line/order.md b/features/command_line/order.md index 0ffc6f5467..92eeb28251 100644 --- a/features/command_line/order.md +++ b/features/command_line/order.md @@ -2,7 +2,9 @@ Use the `--order` option to tell RSpec how to order the files, groups, and examples. The available ordering schemes are `defined` and `rand`. `defined` is the default, which executes groups and examples in the order they -are defined as the spec files are loaded. +are defined as the spec files are loaded, with the caveat that each group +runs its examples before running its nested example groups, even if the +nested groups are defined before the examples. Use `rand` to randomize the order of groups and examples within the groups. Nested groups are always run from top-level to bottom-level in order to avoid From b7f0632dc295837599e098ec6c456c820d5e57f7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 9 Jan 2015 11:04:32 -0800 Subject: [PATCH 231/275] Move filtering docs to a place where YARD will actually render them. Closes #1437. --- .yardopts | 1 + Filtering.md | 61 ++++++++++++++++++++++++++++++ lib/rspec/core/filter_manager.rb | 65 -------------------------------- 3 files changed, 62 insertions(+), 65 deletions(-) create mode 100644 Filtering.md diff --git a/.yardopts b/.yardopts index 0feceb4a30..0ab6943666 100644 --- a/.yardopts +++ b/.yardopts @@ -3,5 +3,6 @@ --markup markdown --default-return void - +Filtering.md Changelog.md License.txt diff --git a/Filtering.md b/Filtering.md new file mode 100644 index 0000000000..db896fd92b --- /dev/null +++ b/Filtering.md @@ -0,0 +1,61 @@ +RSpec supports the filtering of examples and groups by matching tags declared on +the command line or options files, or filters declared via +`RSpec.configure`, with hash key/values submitted within example group +and/or example declarations. For example, given this declaration: + + describe Thing, :awesome => true do + it "does something" do + # ... + end + end + +That group (or any other with `:awesome => true`) would be filtered in +with any of the following commands: + + rspec --tag awesome:true + rspec --tag awesome + rspec -t awesome:true + rspec -t awesome + +Prefixing the tag names with `~` negates the tags, thus excluding this +group with any of: + + rspec --tag ~awesome:true + rspec --tag ~awesome + rspec -t ~awesome:true + rspec -t ~awesome + +## Options files and command line overrides + +Tag declarations can be stored in `.rspec`, `~/.rspec`, or a custom +options file. This is useful for storing defaults. For example, let's +say you've got some slow specs that you want to suppress most of the +time. You can tag them like this: + + describe Something, :slow => true do + +And then store this in `.rspec`: + + --tag ~slow:true + +Now when you run `rspec`, that group will be excluded. + +## Overriding + +Of course, you probably want to run them sometimes, so you can override +this tag on the command line like this: + + rspec --tag slow:true + +## RSpec.configure + +You can also store default tags with `RSpec.configure`. We use `tag` on +the command line (and in options files like `.rspec`), but for historical +reasons we use the term `filter` in `RSpec.configure: + + RSpec.configure do |c| + c.filter_run_including :foo => :bar + c.filter_run_excluding :foo => :bar + end + +These declarations can also be overridden from the command line. diff --git a/lib/rspec/core/filter_manager.rb b/lib/rspec/core/filter_manager.rb index 6bc1d03c27..0da958ea2a 100644 --- a/lib/rspec/core/filter_manager.rb +++ b/lib/rspec/core/filter_manager.rb @@ -1,71 +1,6 @@ module RSpec module Core # @private - # Manages the filtering of examples and groups by matching tags declared on - # the command line or options files, or filters declared via - # `RSpec.configure`, with hash key/values submitted within example group - # and/or example declarations. For example, given this declaration: - # - # describe Thing, :awesome => true do - # it "does something" do - # # ... - # end - # end - # - # That group (or any other with `:awesome => true`) would be filtered in - # with any of the following commands: - # - # rspec --tag awesome:true - # rspec --tag awesome - # rspec -t awesome:true - # rspec -t awesome - # - # Prefixing the tag names with `~` negates the tags, thus excluding this - # group with any of: - # - # rspec --tag ~awesome:true - # rspec --tag ~awesome - # rspec -t ~awesome:true - # rspec -t ~awesome - # - # ## Options files and command line overrides - # - # Tag declarations can be stored in `.rspec`, `~/.rspec`, or a custom - # options file. This is useful for storing defaults. For example, let's - # say you've got some slow specs that you want to suppress most of the - # time. You can tag them like this: - # - # describe Something, :slow => true do - # - # And then store this in `.rspec`: - # - # --tag ~slow:true - # - # Now when you run `rspec`, that group will be excluded. - # - # ## Overriding - # - # Of course, you probably want to run them sometimes, so you can override - # this tag on the command line like this: - # - # rspec --tag slow:true - # - # ## RSpec.configure - # - # You can also store default tags with `RSpec.configure`. We use `tag` on - # the command line (and in options files like `.rspec`), but for historical - # reasons we use the term `filter` in `RSpec.configure: - # - # RSpec.configure do |c| - # c.filter_run_including :foo => :bar - # c.filter_run_excluding :foo => :bar - # end - # - # These declarations can also be overridden from the command line. - # - # @see RSpec.configure - # @see Configuration#filter_run_including - # @see Configuration#filter_run_excluding class FilterManager attr_reader :exclusions, :inclusions From 1bc875fbd9d65faaaadbc246b696d71634be13d9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 9 Jan 2015 11:17:15 -0800 Subject: [PATCH 232/275] Generated descriptions cannot be used for filtering. Closes #1628. --- features/command_line/example_name_option.feature | 2 ++ features/subject/one_liner_syntax.feature | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/features/command_line/example_name_option.feature b/features/command_line/example_name_option.feature index b6297aeae3..f41a9c92c2 100644 --- a/features/command_line/example_name_option.feature +++ b/features/command_line/example_name_option.feature @@ -12,6 +12,8 @@ Feature: `--example` option You can also use the option more than once to specify multiple example matches. + Note: description-less examples that have generated descriptions (typical when using the [one-liner syntax](../subject/one-liner-syntax)) cannot be directly filtered with this option, because it is necessary to execute the example to generate the description, so RSpec is unable to use the not-yet-generated description to decide whether or not to execute an example. You can, of course, pass part of a group's description to select all examples defined in the group (including those that have no description). + Background: Given a file named "first_spec.rb" with: """ruby diff --git a/features/subject/one_liner_syntax.feature b/features/subject/one_liner_syntax.feature index 94af6e5cac..07860fd062 100644 --- a/features/subject/one_liner_syntax.feature +++ b/features/subject/one_liner_syntax.feature @@ -18,7 +18,10 @@ Feature: One-liner syntax `:should` syntax is disabled (since that merely removes `Object#should` but this is `RSpec::Core::ExampleGroup#should`). - Note: this feature is only available when using rspec-expectations. + Notes: + + * This feature is only available when using rspec-expectations. + * Examples defined using this one-liner syntax cannot be directly selected from the command line using the [`--example` option](../command-line/example-option). Scenario: Implicit subject Given a file named "example_spec.rb" with: From 8aa34255c8bc3f79e9a97f4cc9de7ae3457fba34 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 9 Jan 2015 13:10:36 -0800 Subject: [PATCH 233/275] Improve filtering docs. This includes clarifying about description filtering and description-less examples (for #1628). --- Filtering.md | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/Filtering.md b/Filtering.md index db896fd92b..ebf82e1bce 100644 --- a/Filtering.md +++ b/Filtering.md @@ -1,4 +1,12 @@ -RSpec supports the filtering of examples and groups by matching tags declared on +# Filtering + +RSpec supports filtering examples and example groups in multiple ways, +allowing you to run a targeted subset of your suite that you are +currently interested in. + +## Filtering by Tag + +Examples and groups can be filtered by matching tags declared on the command line or options files, or filters declared via `RSpec.configure`, with hash key/values submitted within example group and/or example declarations. For example, given this declaration: @@ -25,9 +33,83 @@ group with any of: rspec -t ~awesome:true rspec -t ~awesome +## Filtering by Example description + +RSpec provides the `--example` (short form: `-e`) option to allow you to +select examples or groups by their description. All loaded examples +whose full description (computed based on the description of the example +plus that of all ancestor groups) contains the provided argument will be +executed. + + rspec --example "Homepage when logged in" + rspec -e "Homepage when logged in" + +You can specify this option multiple times to select multiple sets of examples: + + rspec -e "Homepage when logged in" -e "User" + +Note that RSpec will load all spec files in these situations, which can +incur considerable start-up costs (particularly for Rails apps). If you +know that the examples you are targeting are in particular files, you can +also pass the file or directory name so that RSpec loads only those spec +files, speeding things up: + + rspec spec/homepage_spec.rb -e "Homepage when logged in" + rspec -e "Homepage when logged in" spec/homepage_spec.rb + +Note also that description-less examples that have generated descriptions +(typical when using the one-liner syntax) cannot be directly filtered with +this option, because it is necessary to execute the example to generate the +description, so RSpec is unable to use the not-yet-generated description to +decide whether or not to execute an example. You can, of course, pass part +of a group's description to select all examples defined in the group +(including those that have no description). + +## Filtering by Example Location + +Examples and groups can be selected from the command line by passing the +file and line number where they are defined, separated by a colon: + + rspec spec/homepage_spec.rb:14 spec/widgets_spec.rb:40 spec/users_spec.rb + +This command would run the example or group defined on line 14 of +`spec/homepage_spec.rb`, the example or group defined on line 40 of +`spec/widgets_spec.rb`, and all examples and groups defined in +`spec/users_spec.rb`. + +If there is no example or group defined at the specified line, RSpec +will run the last example or group defined before the line. + +## Focusing + +RSpec supports configuration options that make it easy to select +examples by temporarily tweaking them. In your `spec_helper.rb` (or +a similar file), put this configuration: + + RSpec.configure do |config| + config.filter_run :focus + config.run_all_when_everything_filtered = true + end + +This configuration is generated for you by `rspec --init` in the +commented-out section of recommendations. With that in place, you +can tag any example group or example with `:focus` metadata to +select it: + + it "does something" do + # becomes... + it "does something", :focus do + +RSpec also ships with aliases of the common example group definition +methods (`describe`, `context`) and example methods (`it`, `specify`, +`example`) with an `f` prefix that automatically includes `:focus => +true` metadata, allowing you to easily change `it` to `fit` (think +"focused it"), `describe` to `fdescribe`, etc in order to temporarily +focus them. + ## Options files and command line overrides -Tag declarations can be stored in `.rspec`, `~/.rspec`, or a custom +Command line option declarations can be stored in `.rspec`, `~/.rspec`, or a custom options file. This is useful for storing defaults. For example, let's say you've got some slow specs that you want to suppress most of the time. You can tag them like this: @@ -47,6 +129,14 @@ this tag on the command line like this: rspec --tag slow:true +## Precedence + +Location and description filters have priority over tag filters since +they express a desire by the user to run specific examples. Thus, you +could specify a location or description at the command line to run an +example or example group that would normally be excluded due to a +`:slow` tag if you were using the above configuration. + ## RSpec.configure You can also store default tags with `RSpec.configure`. We use `tag` on From 25bb1ae7eda13f078d10e8f62e622debaefc6a6b Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 9 Jan 2015 14:10:07 -0800 Subject: [PATCH 234/275] =?UTF-8?q?Ensure=20structs=20are=20doc=E2=80=99d?= =?UTF-8?q?=20by=20yard.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #1765. --- lib/rspec/core/notifications.rb | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/rspec/core/notifications.rb b/lib/rspec/core/notifications.rb index f0a678c2cc..901b77d7a7 100644 --- a/lib/rspec/core/notifications.rb +++ b/lib/rspec/core/notifications.rb @@ -32,7 +32,8 @@ def wrap(line, _code_or_symbol) # end # # @attr example [RSpec::Core::Example] the current example - ExampleNotification = Struct.new(:example) do + ExampleNotification = Struct.new(:example) + class ExampleNotification # @private def self.for(example) if example.execution_result.pending_fixed? @@ -321,7 +322,8 @@ def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes) # # @attr seed [Fixnum] the seed used to randomize ordering # @attr used [Boolean] whether the seed has been used or not - SeedNotification = Struct.new(:seed, :used) do + SeedNotification = Struct.new(:seed, :used) + class SeedNotification # @api # @return [Boolean] has the seed been used? def seed_used? @@ -346,8 +348,8 @@ def fully_formatted # @attr pending_examples [Array(RSpec::Core::Example)] the pending examples # @attr load_time [Float] the number of seconds taken to boot RSpec # and load the spec files - SummaryNotification = Struct.new(:duration, :examples, :failed_examples, :pending_examples, :load_time) do - + SummaryNotification = Struct.new(:duration, :examples, :failed_examples, :pending_examples, :load_time) + class SummaryNotification # @api # @return [Fixnum] the number of examples run def example_count @@ -443,8 +445,8 @@ def fully_formatted(colorizer=::RSpec::Core::Formatters::ConsoleCodes) # @attr duration [Float] the time taken (in seconds) to run the suite # @attr examples [Array(RSpec::Core::Example)] the examples run # @attr number_of_examples [Fixnum] the number of examples to profile - ProfileNotification = Struct.new(:duration, :examples, :number_of_examples) do - + ProfileNotification = Struct.new(:duration, :examples, :number_of_examples) + class ProfileNotification # @return [Array(RSpec::Core::Example)] the slowest examples def slowest_examples @slowest_examples ||= @@ -511,7 +513,8 @@ def calculate_slowest_groups # @attr replacement [String] An optional replacement for the deprecation # @attr call_site [String] An optional call site from which the deprecation # was issued - DeprecationNotification = Struct.new(:deprecated, :message, :replacement, :call_site) do + DeprecationNotification = Struct.new(:deprecated, :message, :replacement, :call_site) + class DeprecationNotification private_class_method :new # @api From 20aabe90201d3fcd8395d3e3f7cc49394442bdff Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 10 Jan 2015 19:09:50 -0800 Subject: [PATCH 235/275] Improve doc formatting. - Use `RSpec.describe` rather than `describe`. - Add missing back tick. - Wrap ruby snippets in ``` with ruby identifier so it renders with syntax highlighting on github. --- Filtering.md | 48 +++++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/Filtering.md b/Filtering.md index ebf82e1bce..5c00bdd18a 100644 --- a/Filtering.md +++ b/Filtering.md @@ -11,11 +11,13 @@ the command line or options files, or filters declared via `RSpec.configure`, with hash key/values submitted within example group and/or example declarations. For example, given this declaration: - describe Thing, :awesome => true do - it "does something" do - # ... - end - end +``` ruby +RSpec.describe Thing, :awesome => true do + it "does something" do + # ... + end +end +``` That group (or any other with `:awesome => true`) would be filtered in with any of the following commands: @@ -86,19 +88,23 @@ RSpec supports configuration options that make it easy to select examples by temporarily tweaking them. In your `spec_helper.rb` (or a similar file), put this configuration: - RSpec.configure do |config| - config.filter_run :focus - config.run_all_when_everything_filtered = true - end +``` ruby +RSpec.configure do |config| + config.filter_run :focus + config.run_all_when_everything_filtered = true +end +``` This configuration is generated for you by `rspec --init` in the commented-out section of recommendations. With that in place, you can tag any example group or example with `:focus` metadata to select it: - it "does something" do - # becomes... - it "does something", :focus do +``` ruby +it "does something" do +# becomes... +it "does something", :focus do +``` RSpec also ships with aliases of the common example group definition methods (`describe`, `context`) and example methods (`it`, `specify`, @@ -114,7 +120,9 @@ options file. This is useful for storing defaults. For example, let's say you've got some slow specs that you want to suppress most of the time. You can tag them like this: - describe Something, :slow => true do +``` ruby +RSpec.describe Something, :slow => true do +``` And then store this in `.rspec`: @@ -141,11 +149,13 @@ example or example group that would normally be excluded due to a You can also store default tags with `RSpec.configure`. We use `tag` on the command line (and in options files like `.rspec`), but for historical -reasons we use the term `filter` in `RSpec.configure: - - RSpec.configure do |c| - c.filter_run_including :foo => :bar - c.filter_run_excluding :foo => :bar - end +reasons we use the term `filter` in `RSpec.configure`: + +``` ruby +RSpec.configure do |c| + c.filter_run_including :foo => :bar + c.filter_run_excluding :foo => :bar +end +``` These declarations can also be overridden from the command line. From 6c787ba443fdb743fadca9b4edd5ad83ac761e04 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 11 Jan 2015 12:30:00 -0800 Subject: [PATCH 236/275] Use latest matcher protocol. --- spec/support/matchers.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index ce038cff22..5124d205f8 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -9,7 +9,7 @@ @file = file end - failure_message_for_should do + failure_message do "expected #{@autotest.class} to map #{@specs.inspect} to #{@file.inspect}\ngot #{@actual.inspect}" end @@ -25,7 +25,7 @@ def prepare(autotest) failure_reason(example, exception_klass).nil? end - failure_message_for_should do |example| + failure_message do |example| "expected example to fail with a #{exception_klass} exception, but #{failure_reason(example, exception_klass)}" end @@ -45,7 +45,7 @@ def failure_reason(example, exception_klass) failure_reason(example).nil? end - failure_message_for_should do |example| + failure_message do |example| "expected example to pass, but #{failure_reason(example)}" end @@ -71,7 +71,7 @@ def failure_reason(example) example.execution_result.pending_message == message end - failure_message_for_should do |example| + failure_message do |example| "expected: example pending with #{message.inspect}\n got: #{example.execution_result.pending_message.inspect}" end end @@ -83,7 +83,7 @@ def failure_reason(example) example.execution_result.pending_message == message end - failure_message_for_should do |example| + failure_message do |example| "expected: example skipped with #{message.inspect}\n got: #{example.execution_result.pending_message.inspect}" end end From aeb92e907bb1e19da949f0fe816fbfe8ba4c5db5 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 12 Jan 2015 07:47:36 +1100 Subject: [PATCH 237/275] Revert "Updates from rspec-dev (2015-01-08)" --- .rubocop_rspec_base.yml | 2 +- .travis.yml | 2 +- appveyor.yml | 2 +- script/clone_all_rspec_repos | 27 ++++++++++++++------------- script/functions.sh | 21 ++++++--------------- script/predicate_functions.sh | 14 +------------- script/run_build | 8 ++++---- script/travis_functions.sh | 2 +- 8 files changed, 29 insertions(+), 49 deletions(-) diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index a8c48a397e..f7bea1c203 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml index da3b49f159..ea4f1b0fcc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. language: ruby diff --git a/appveyor.yml b/appveyor.yml index d9bbbf668d..9e4f457e30 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: "{build}" diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index c146d24a37..f83d2e910f 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,23 +1,24 @@ #!/bin/bash -# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e source script/functions.sh -pushd .. - -clone_repo "rspec" -clone_repo "rspec-core" -clone_repo "rspec-expectations" -clone_repo "rspec-mocks" - if is_mri; then + pushd .. + + clone_repo "rspec" + clone_repo "rspec-core" + clone_repo "rspec-expectations" + clone_repo "rspec-mocks" clone_repo "rspec-rails" -fi -if rspec_support_compatible; then - clone_repo "rspec-support" -fi + if rspec_support_compatible; then + clone_repo "rspec-support" + fi -popd + popd +else + echo "Not cloning all repos since we are not on MRI and they are only needed for the MRI build" +fi diff --git a/script/functions.sh b/script/functions.sh index 256e3855a8..a96a5c71b4 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -58,13 +58,9 @@ function run_cukes { function run_specs_one_by_one { echo "Running each spec file, one-by-one..." - if is_mri; then - for file in `find spec -iname '*_spec.rb'`; do - bin/rspec $file -b --format progress - done - else - echo "Skipping one-by-one specs on non-MRI rubies as they tend to have long boot times" - fi + for file in `find spec -iname '*_spec.rb'`; do + bin/rspec $file -b --format progress + done } function run_spec_suite_for { @@ -73,7 +69,7 @@ function run_spec_suite_for { pushd ../$1 unset BUNDLE_GEMFILE bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` - travis_retry eval "time bundle install $bundle_install_flags" + travis_retry eval "bundle install $bundle_install_flags" run_specs_and_record_done popd fi; @@ -125,12 +121,7 @@ function run_all_spec_suites { fold "rspec-core specs" run_spec_suite_for "rspec-core" fold "rspec-expectations specs" run_spec_suite_for "rspec-expectations" fold "rspec-mocks specs" run_spec_suite_for "rspec-mocks" - - if is_mri; then - fold "rspec-rails specs" run_spec_suite_for "rspec-rails" - else - echo "Skipping rspec-rails specs on non-MRI rubies" - fi + fold "rspec-rails specs" run_spec_suite_for "rspec-rails" if rspec_support_compatible; then fold "rspec-support specs" run_spec_suite_for "rspec-support" diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 22ddee6a9c..fc5d372c50 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { @@ -11,18 +11,6 @@ function is_mri { fi; } -function is_jruby_20_mode { - if [ -z "$JRUBY_OPTS" ]; then - if ruby -e "exit(RUBY_VERSION == '2.0.0')"; then - return 0 - else - return 1 - fi - else - return 1 - fi -} - function is_mri_192 { if is_mri; then if ruby -e "exit(RUBY_VERSION == '1.9.2')"; then diff --git a/script/run_build b/script/run_build index e804fe9e5e..e1edcef3a9 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e @@ -21,8 +21,8 @@ if style_and_lint_enforced; then fold "rubocop" check_style_and_lint fi -if is_jruby_20_mode; then - echo "Skipping other specs suites on JRuby 2.0 mode because it is so much slower" -else +if is_mri; then run_all_spec_suites +else + echo "Skipping the rest of the build on non-MRI rubies" fi diff --git a/script/travis_functions.sh b/script/travis_functions.sh index 893d302d62..77829b3638 100644 --- a/script/travis_functions.sh +++ b/script/travis_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2015-01-08T19:12:50-08:00 from the rspec-dev repo. +# This file was generated on 2015-01-07T22:08:46-08:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: From 18d2f7cba40c9e8322d5756e63728020dcc862a1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 11 Jan 2015 16:12:18 -0800 Subject: [PATCH 238/275] ExecutionResult#status should be a symbol. --- spec/rspec/core/formatters/documentation_formatter_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/rspec/core/formatters/documentation_formatter_spec.rb b/spec/rspec/core/formatters/documentation_formatter_spec.rb index aa4b5c3c3e..43eac1beaa 100644 --- a/spec/rspec/core/formatters/documentation_formatter_spec.rb +++ b/spec/rspec/core/formatters/documentation_formatter_spec.rb @@ -18,11 +18,11 @@ def execution_result(values) it "numbers the failures" do send_notification :example_failed, example_notification( double("example 1", :description => "first example", - :execution_result => execution_result(:status => 'failed', :exception => Exception.new) + :execution_result => execution_result(:status => :failed, :exception => Exception.new) )) send_notification :example_failed, example_notification( double("example 2", :description => "second example", - :execution_result => execution_result(:status => 'failed', :exception => Exception.new) + :execution_result => execution_result(:status => :failed, :exception => Exception.new) )) expect(output.string).to match(/first example \(FAILED - 1\)/m) From 61da3233f893f1427097e6a41b645016cdcddf34 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 11 Jan 2015 16:20:35 -0800 Subject: [PATCH 239/275] Use real example objects. Using a double here is making refactoring harder. --- spec/rspec/core_spec.rb | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/spec/rspec/core_spec.rb b/spec/rspec/core_spec.rb index 82fd0a0197..a367e92bc8 100644 --- a/spec/rspec/core_spec.rb +++ b/spec/rspec/core_spec.rb @@ -96,10 +96,6 @@ RSpec.configuration.error_stream = StringIO.new end - def some_example - double("example").as_null_object - end - it "clears example groups" do RSpec.world.example_groups << :example_group @@ -118,9 +114,18 @@ def some_example it "clears examples, failed_examples and pending_examples" do reporter.start(3) - reporter.example_started(some_example) - reporter.example_failed(some_example) - reporter.example_pending(some_example) + pending_ex = failing_ex = nil + + RSpec.describe do + pending_ex = pending { fail } + failing_ex = example { fail } + end.run + + reporter.example_started(failing_ex) + reporter.example_failed(failing_ex) + + reporter.example_started(pending_ex) + reporter.example_pending(pending_ex) reporter.finish reporter.register_listener(listener, :dump_summary) From 20a8bc27fe6e2d38398664e575adc1fed5256ef4 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 10 Jan 2015 13:24:41 -0800 Subject: [PATCH 240/275] Store `pending_exception` when a mock expectation fails a pending example. --- Changelog.md | 3 +++ lib/rspec/core/example.rb | 1 + spec/support/matchers.rb | 5 ++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index c4686a05a4..2c296712c2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -59,6 +59,9 @@ Bug Fixes: * Fix location filtering to work properly for examples defined in a nested example group within a shared example group defined in an external file. (Bradley Schaefer, Xavier Shay, Myron Marston, #1837) +* When a pending example fails (as expected) due to a mock expectation, + set `RSpec::Core::Example::ExecutionResult#pending_exception` -- + previously it was not being set but should have been. (Myron Marston, #1844) ### 3.1.8 Development diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 683cebdf9d..1778700cc3 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -386,6 +386,7 @@ def verify_mocks rescue Exception => e if pending? execution_result.pending_fixed = false + execution_result.pending_exception = e @exception = nil else set_exception(e) diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index 5124d205f8..a8ddb670b8 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -67,12 +67,15 @@ def failure_reason(example) RSpec::Matchers.define :be_pending_with do |message| match do |example| example.pending? && + example.execution_result.pending_exception && example.execution_result.status == :pending && example.execution_result.pending_message == message end failure_message do |example| - "expected: example pending with #{message.inspect}\n got: #{example.execution_result.pending_message.inspect}" + "expected: example pending with #{message.inspect}\n got: #{example.execution_result.pending_message.inspect}".tap do |msg| + msg << " (but had no pending exception)" unless example.execution_result.pending_exception + end end end From 376cd8065772f12d4510b0dcc7e61d4af50ecb1c Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 10 Jan 2015 13:24:41 -0800 Subject: [PATCH 241/275] Add `ExecutionResult#example_skipped?`. This helps us differentiate the reason an example got a pending result. --- lib/rspec/core/example.rb | 9 +++++++++ spec/support/matchers.rb | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 1778700cc3..b0aa2382d1 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -461,6 +461,15 @@ class ExecutionResult alias pending_fixed? pending_fixed + # @return [Boolean] Indicates if the example was completely skipped + # (typically done via `:skip` metadata or the `skip` method). Skipped examples + # will have a `:pending` result. A `:pending` result can also come from examples + # that were marked as `:pending`, which causes them to be run, and produces a + # `:failed` result if the example passes. + def example_skipped? + status == :pending && !pending_exception + end + # @api private # Records the finished status of the example. def record_finished(status, finished_at) diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index a8ddb670b8..b83b9789ca 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -22,6 +22,7 @@ def prepare(autotest) RSpec::Matchers.define :fail_with do |exception_klass| match do |example| + !example.execution_result.example_skipped? && failure_reason(example, exception_klass).nil? end @@ -42,6 +43,7 @@ def failure_reason(example, exception_klass) RSpec::Matchers.define :pass do match do |example| + !example.execution_result.example_skipped? && failure_reason(example).nil? end @@ -67,6 +69,7 @@ def failure_reason(example) RSpec::Matchers.define :be_pending_with do |message| match do |example| example.pending? && + !example.execution_result.example_skipped? && example.execution_result.pending_exception && example.execution_result.status == :pending && example.execution_result.pending_message == message @@ -83,6 +86,7 @@ def failure_reason(example) match do |example| example.skipped? && example.pending? && + example.execution_result.example_skipped? && example.execution_result.pending_message == message end From 949fa0c13dcb70b46097ee7d975f0efa608376a9 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 11 Jan 2015 12:46:11 -0800 Subject: [PATCH 242/275] Add new notification classes for other pending/skipped cases. --- lib/rspec/core/notifications.rb | 19 +++++++++++++++++-- spec/support/formatter_support.rb | 5 +++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/rspec/core/notifications.rb b/lib/rspec/core/notifications.rb index 6994c70665..02dc1fbe21 100644 --- a/lib/rspec/core/notifications.rb +++ b/lib/rspec/core/notifications.rb @@ -36,14 +36,21 @@ def wrap(line, _code_or_symbol) class ExampleNotification # @private def self.for(example) - if example.execution_result.pending_fixed? + execution_result = example.execution_result + + if execution_result.pending_fixed? PendingExampleFixedNotification.new(example) - elsif example.execution_result.status == :failed + elsif execution_result.example_skipped? + SkippedExampleNotification.new(example) + elsif execution_result.status == :pending + PendingExampleFailedAsExpectedNotification.new(example) + elsif execution_result.status == :failed FailedExampleNotification.new(example) else new(example) end end + private_class_method :new end @@ -299,6 +306,14 @@ def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes) end end + class PendingExampleFailedAsExpectedNotification < ExampleNotification + public_class_method :new + end + + class SkippedExampleNotification < ExampleNotification + public_class_method :new + end + # The `GroupNotification` represents notifications sent by the reporter # which contain information about the currently running (or soon to be) # example group. It is used by formatters to access information about that diff --git a/spec/support/formatter_support.rb b/spec/support/formatter_support.rb index d2b82624d9..2e56ba8d8d 100644 --- a/spec/support/formatter_support.rb +++ b/spec/support/formatter_support.rb @@ -185,8 +185,9 @@ def example @example ||= begin result = instance_double(RSpec::Core::Example::ExecutionResult, - :pending_fixed? => false, - :status => :passed + :pending_fixed? => false, + :example_skipped? => false, + :status => :passed ) allow(result).to receive(:exception) { exception } instance_double(RSpec::Core::Example, From 3694232d7b0fa5f4a8562df1a50a3f2cd561d3e0 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 11 Jan 2015 21:00:51 -0800 Subject: [PATCH 243/275] `RSpec`, not `Rspec`. --- lib/rspec/core/notifications.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rspec/core/notifications.rb b/lib/rspec/core/notifications.rb index 02dc1fbe21..308d2fff7b 100644 --- a/lib/rspec/core/notifications.rb +++ b/lib/rspec/core/notifications.rb @@ -82,13 +82,13 @@ def pending_examples @reporter.pending_examples end - # @return [Array(Rspec::Core::Notifications::ExampleNotification] + # @return [Array(RSpec::Core::Notifications::ExampleNotification] # returns examples as notifications def notifications @notifications ||= format_examples(examples) end - # @return [Array(Rspec::Core::Notifications::FailedExampleNotification] + # @return [Array(RSpec::Core::Notifications::FailedExampleNotification] # returns failed examples as notifications def failure_notifications @failed_notifications ||= format_examples(failed_examples) From 41c5e36a1b5b45c6bc0fa7057930316433e2ac52 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 11 Jan 2015 21:59:40 -0800 Subject: [PATCH 244/275] Use proper array type specifiers in YARD docs. According to http://yardoc.org/types.html, `Array(...)` is for tuples, where each element in the parens defines the type of element at that position in the tuple, whereas `Array<...>` is for lists, where each element in the angle brackets defines one of the allowed types of *any* element in the list. --- lib/rspec/core/notifications.rb | 34 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/rspec/core/notifications.rb b/lib/rspec/core/notifications.rb index 308d2fff7b..f3fdd305c7 100644 --- a/lib/rspec/core/notifications.rb +++ b/lib/rspec/core/notifications.rb @@ -67,28 +67,28 @@ def initialize(reporter) @reporter = reporter end - # @return [Array(RSpec::Core::Example)] list of examples + # @return [Array] list of examples def examples @reporter.examples end - # @return [Array(RSpec::Core::Example)] list of failed examples + # @return [Array] list of failed examples def failed_examples @reporter.failed_examples end - # @return [Array(RSpec::Core::Example)] list of pending examples + # @return [Array] list of pending examples def pending_examples @reporter.pending_examples end - # @return [Array(RSpec::Core::Notifications::ExampleNotification] + # @return [Array] # returns examples as notifications def notifications @notifications ||= format_examples(examples) end - # @return [Array(RSpec::Core::Notifications::FailedExampleNotification] + # @return [Array] # returns failed examples as notifications def failure_notifications @failed_notifications ||= format_examples(failed_examples) @@ -159,7 +159,7 @@ def description # Returns the message generated for this failure line by line. # - # @return [Array(String)] The example failure message + # @return [Array] The example failure message def message_lines add_shared_group_lines(failure_lines, NullColorizer) end @@ -167,7 +167,7 @@ def message_lines # Returns the message generated for this failure colorized line by line. # # @param colorizer [#wrap] An object to colorize the message_lines by - # @return [Array(String)] The example failure message colorized + # @return [Array] The example failure message colorized def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes) add_shared_group_lines(failure_lines, colorizer).map do |line| colorizer.wrap line, RSpec.configuration.failure_color @@ -176,7 +176,7 @@ def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes) # Returns the failures formatted backtrace. # - # @return [Array(String)] the examples backtrace lines + # @return [Array] the examples backtrace lines def formatted_backtrace backtrace_formatter.format_backtrace(exception.backtrace, example.metadata) end @@ -184,7 +184,7 @@ def formatted_backtrace # Returns the failures colorized formatted backtrace. # # @param colorizer [#wrap] An object to colorize the message_lines by - # @return [Array(String)] the examples colorized backtrace lines + # @return [Array] the examples colorized backtrace lines def colorized_formatted_backtrace(colorizer=::RSpec::Core::Formatters::ConsoleCodes) formatted_backtrace.map do |backtrace_info| colorizer.wrap "# #{backtrace_info}", RSpec.configuration.detail_color @@ -292,7 +292,7 @@ def description # Returns the message generated for this failure line by line. # - # @return [Array(String)] The example failure message + # @return [Array] The example failure message def message_lines ["Expected pending '#{example.execution_result.pending_message}' to fail. No Error was raised."] end @@ -300,7 +300,7 @@ def message_lines # Returns the message generated for this failure colorized line by line. # # @param colorizer [#wrap] An object to colorize the message_lines by - # @return [Array(String)] The example failure message colorized + # @return [Array] The example failure message colorized def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes) message_lines.map { |line| colorizer.wrap(line, RSpec.configuration.fixed_color) } end @@ -358,9 +358,9 @@ def fully_formatted # of the test run. # # @attr duration [Float] the time taken (in seconds) to run the suite - # @attr examples [Array(RSpec::Core::Example)] the examples run - # @attr failed_examples [Array(RSpec::Core::Example)] the failed examples - # @attr pending_examples [Array(RSpec::Core::Example)] the pending examples + # @attr examples [Array] the examples run + # @attr failed_examples [Array] the failed examples + # @attr pending_examples [Array] the pending examples # @attr load_time [Float] the number of seconds taken to boot RSpec # and load the spec files SummaryNotification = Struct.new(:duration, :examples, :failed_examples, :pending_examples, :load_time) @@ -458,11 +458,11 @@ def fully_formatted(colorizer=::RSpec::Core::Formatters::ConsoleCodes) # information at the end of the test run for profiling information. # # @attr duration [Float] the time taken (in seconds) to run the suite - # @attr examples [Array(RSpec::Core::Example)] the examples run + # @attr examples [Array] the examples run # @attr number_of_examples [Fixnum] the number of examples to profile ProfileNotification = Struct.new(:duration, :examples, :number_of_examples) class ProfileNotification - # @return [Array(RSpec::Core::Example)] the slowest examples + # @return [Array] the slowest examples def slowest_examples @slowest_examples ||= examples.sort_by do |example| @@ -487,7 +487,7 @@ def percentage end end - # @return [Array(RSpec::Core::Example)] the slowest example groups + # @return [Array] the slowest example groups def slowest_groups @slowest_groups ||= calculate_slowest_groups end From a549c2d65d7eedd809bf4283ae882be64f4345a1 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sun, 11 Jan 2015 22:41:22 -0800 Subject: [PATCH 245/275] Include failure details in pending example dumps. Fixes #1792. --- Changelog.md | 2 + features/hooks/around_hooks.feature | 14 ++- .../pending_examples.feature | 9 +- .../skipped_examples.feature | 50 +++++----- lib/rspec/core/notifications.rb | 98 +++++++++++++++---- spec/support/formatter_support.rb | 52 +++++++--- 6 files changed, 159 insertions(+), 66 deletions(-) diff --git a/Changelog.md b/Changelog.md index 2c296712c2..79c0f3501b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -38,6 +38,8 @@ Enhancements: `RSpec.world` state. (Tyler Ball, 1808) * Apply line-number filters only to the files they are scoped to, allowing you to mix filtered and unfiltered files. (Myron Marston, #1839) +* When dumping pending examples, include the failure details so that you + don't have to un-pend the example to see it. (Myron Marston, #1844) Bug Fixes: diff --git a/features/hooks/around_hooks.feature b/features/hooks/around_hooks.feature index 1ce45f1e40..99a76d9224 100644 --- a/features/hooks/around_hooks.feature +++ b/features/hooks/around_hooks.feature @@ -238,9 +238,11 @@ Feature: `around` hooks Then the output should contain "1 example, 0 failures, 1 pending" And the output should contain: """ - Pending: - implicit pending example should be detected as Not yet implemented - # Not yet implemented + Pending: (Failures listed here are expected and do not affect your suite's status) + + 1) implicit pending example should be detected as Not yet implemented + # Not yet implemented + # ./example_spec.rb:6 """ @@ -262,8 +264,10 @@ Feature: `around` hooks Then the output should contain "1 example, 0 failures, 1 pending" And the output should contain: """ - explicit pending example should be detected as pending - # No reason given + Pending: (Failures listed here are expected and do not affect your suite's status) + + 1) explicit pending example should be detected as pending + # No reason given """ Scenario: Multiple `around` hooks in the same scope diff --git a/features/pending_and_skipped_examples/pending_examples.feature b/features/pending_and_skipped_examples/pending_examples.feature index c8bf26b366..7a5da21b82 100644 --- a/features/pending_and_skipped_examples/pending_examples.feature +++ b/features/pending_and_skipped_examples/pending_examples.feature @@ -18,11 +18,12 @@ Feature: `pending` examples And the output should contain "1 example, 0 failures, 1 pending" And the output should contain: """ - Pending: - an example is implemented but waiting - # something else getting finished - # ./pending_without_block_spec.rb:2 + Pending: (Failures listed here are expected and do not affect your suite's status) + + 1) an example is implemented but waiting + # something else getting finished """ + Scenario: `pending` any arbitrary reason with a passing example Given a file named "pending_with_passing_example_spec.rb" with: """ruby diff --git a/features/pending_and_skipped_examples/skipped_examples.feature b/features/pending_and_skipped_examples/skipped_examples.feature index b2ceb43c25..4ff3c82b28 100644 --- a/features/pending_and_skipped_examples/skipped_examples.feature +++ b/features/pending_and_skipped_examples/skipped_examples.feature @@ -29,10 +29,11 @@ Feature: `skip` examples And the output should contain "1 example, 0 failures, 1 pending" And the output should contain: """ - Pending: - an example is skipped - # No reason given - # ./skipped_spec.rb:2 + Pending: (Failures listed here are expected and do not affect your suite's status) + + 1) an example is skipped + # No reason given + # ./skipped_spec.rb:2 """ Scenario: Skipping using `skip` inside an example @@ -49,10 +50,11 @@ Feature: `skip` examples And the output should contain "1 example, 0 failures, 1 pending" And the output should contain: """ - Pending: - an example is skipped - # No reason given - # ./skipped_spec.rb:2 + Pending: (Failures listed here are expected and do not affect your suite's status) + + 1) an example is skipped + # No reason given + # ./skipped_spec.rb:2 """ Scenario: Temporarily skipping by prefixing `it`, `specify`, or `example` with an x @@ -74,16 +76,19 @@ Feature: `skip` examples And the output should contain "3 examples, 0 failures, 3 pending" And the output should contain: """ - Pending: - an example is skipped using xit - # Temporarily skipped with xit - # ./temporarily_skipped_spec.rb:2 - an example is skipped using xspecify - # Temporarily skipped with xspecify - # ./temporarily_skipped_spec.rb:5 - an example is skipped using xexample - # Temporarily skipped with xexample - # ./temporarily_skipped_spec.rb:8 + Pending: (Failures listed here are expected and do not affect your suite's status) + + 1) an example is skipped using xit + # Temporarily skipped with xit + # ./temporarily_skipped_spec.rb:2 + + 2) an example is skipped using xspecify + # Temporarily skipped with xspecify + # ./temporarily_skipped_spec.rb:5 + + 3) an example is skipped using xexample + # Temporarily skipped with xexample + # ./temporarily_skipped_spec.rb:8 """ Scenario: Skipping using metadata @@ -99,8 +104,9 @@ Feature: `skip` examples And the output should contain "1 example, 0 failures, 1 pending" And the output should contain: """ - Pending: - an example is skipped - # No reason given - # ./skipped_spec.rb:2 + Pending: (Failures listed here are expected and do not affect your suite's status) + + 1) an example is skipped + # No reason given + # ./skipped_spec.rb:2 """ diff --git a/lib/rspec/core/notifications.rb b/lib/rspec/core/notifications.rb index f3fdd305c7..50355d2077 100644 --- a/lib/rspec/core/notifications.rb +++ b/lib/rspec/core/notifications.rb @@ -94,6 +94,13 @@ def failure_notifications @failed_notifications ||= format_examples(failed_examples) end + # @return [Array] + # returns pending examples as notifications + def pending_notifications + @pending_notifications ||= format_examples(pending_examples) + end + # @return [String] The list of failed examples, fully formatted in the way # that RSpec's built-in formatters emit. def fully_formatted_failed_examples(colorizer=::RSpec::Core::Formatters::ConsoleCodes) @@ -109,15 +116,10 @@ def fully_formatted_failed_examples(colorizer=::RSpec::Core::Formatters::Console # @return [String] The list of pending examples, fully formatted in the # way that RSpec's built-in formatters emit. def fully_formatted_pending_examples(colorizer=::RSpec::Core::Formatters::ConsoleCodes) - formatted = "\nPending:\n" + formatted = "\nPending: (Failures listed here are expected and do not affect your suite's status)\n" - pending_examples.each do |example| - formatted_caller = RSpec.configuration.backtrace_formatter.backtrace_line(example.location) - - formatted << - " #{colorizer.wrap(example.full_description, :pending)}\n" \ - " # #{colorizer.wrap(example.execution_result.pending_message, :detail)}\n" \ - " # #{colorizer.wrap(formatted_caller, :detail)}\n" + pending_notifications.each_with_index do |notification, index| + formatted << notification.fully_formatted(index.next, colorizer) end formatted @@ -170,7 +172,7 @@ def message_lines # @return [Array] The example failure message colorized def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes) add_shared_group_lines(failure_lines, colorizer).map do |line| - colorizer.wrap line, RSpec.configuration.failure_color + colorizer.wrap line, message_color end end @@ -194,17 +196,7 @@ def colorized_formatted_backtrace(colorizer=::RSpec::Core::Formatters::ConsoleCo # @return [String] The failure information fully formatted in the way that # RSpec's built-in formatters emit. def fully_formatted(failure_number, colorizer=::RSpec::Core::Formatters::ConsoleCodes) - formatted = "\n #{failure_number}) #{description}\n" - - colorized_message_lines(colorizer).each do |line| - formatted << RSpec::Support::EncodedString.new(" #{line}\n", encoding_of(formatted)) - end - - colorized_formatted_backtrace(colorizer).each do |line| - formatted << RSpec::Support::EncodedString.new(" #{line}\n", encoding_of(formatted)) - end - - formatted + "\n #{failure_number}) #{description}\n#{formatted_message_and_backtrace(colorizer)}" end private @@ -273,6 +265,24 @@ def find_failed_line File.expand_path(line_path).downcase == example_path end end + + def formatted_message_and_backtrace(colorizer) + formatted = "" + + colorized_message_lines(colorizer).each do |line| + formatted << RSpec::Support::EncodedString.new(" #{line}\n", encoding_of(formatted)) + end + + colorized_formatted_backtrace(colorizer).each do |line| + formatted << RSpec::Support::EncodedString.new(" #{line}\n", encoding_of(formatted)) + end + + formatted + end + + def message_color + RSpec.configuration.failure_color + end end # The `PendingExampleFixedNotification` extends `ExampleNotification` with @@ -306,12 +316,58 @@ def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes) end end - class PendingExampleFailedAsExpectedNotification < ExampleNotification + # @private + module PendingExampleNotificationMethods + private + + def fully_formatted_header(pending_number, colorizer=::RSpec::Core::Formatters::ConsoleCodes) + colorizer.wrap("\n #{pending_number}) #{example.full_description}\n", :pending) << + colorizer.wrap(" # #{example.execution_result.pending_message}\n", :detail) + end + end + + # The `PendingExampleFailedAsExpectedNotification` extends `FailedExampleNotification` with + # things useful for pending specs that fail as expected. + # + # @attr [RSpec::Core::Example] example the current example + # @see ExampleNotification + class PendingExampleFailedAsExpectedNotification < FailedExampleNotification + include PendingExampleNotificationMethods public_class_method :new + + # @return [Exception] The exception that occurred while the pending example was executed + def exception + example.execution_result.pending_exception + end + + # @return [String] The pending detail fully formatted in the way that + # RSpec's built-in formatters emit. + def fully_formatted(pending_number, colorizer=::RSpec::Core::Formatters::ConsoleCodes) + fully_formatted_header(pending_number, colorizer) << formatted_message_and_backtrace(colorizer) + end + + private + + def message_color + RSpec.configuration.pending_color + end end + # The `SkippedExampleNotification` extends `ExampleNotification` with + # things useful for specs that are skipped. + # + # @attr [RSpec::Core::Example] example the current example + # @see ExampleNotification class SkippedExampleNotification < ExampleNotification + include PendingExampleNotificationMethods public_class_method :new + + # @return [String] The pending detail fully formatted in the way that + # RSpec's built-in formatters emit. + def fully_formatted(pending_number, colorizer=::RSpec::Core::Formatters::ConsoleCodes) + formatted_caller = RSpec.configuration.backtrace_formatter.backtrace_line(example.location) + fully_formatted_header(pending_number, colorizer) << colorizer.wrap(" # #{formatted_caller}\n", :detail) + end end # The `GroupNotification` represents notifications sent by the reporter diff --git a/spec/support/formatter_support.rb b/spec/support/formatter_support.rb index 2e56ba8d8d..0d146ce0b9 100644 --- a/spec/support/formatter_support.rb +++ b/spec/support/formatter_support.rb @@ -32,13 +32,25 @@ def run_example_specs_with_formatter(formatter_option) if RUBY_VERSION.to_f < 1.9 def expected_summary_output_for_example_specs <<-EOS.gsub(/^\s+\|/, '').chomp - |Pending: - | pending spec with no implementation is pending - | # Not yet implemented - | # ./spec/rspec/core/resources/formatter_specs.rb:4 - | pending command with block format with content that would fail is pending - | # No reason given - | # ./spec/rspec/core/resources/formatter_specs.rb:9 + |Pending: (Failures listed here are expected and do not affect your suite's status) + | + | 1) pending spec with no implementation is pending + | # Not yet implemented + | # ./spec/rspec/core/resources/formatter_specs.rb:4 + | + | 2) pending command with block format with content that would fail is pending + | # No reason given + | Failure/Error: expect(1).to eq(2) + | + | expected: 2 + | got: 1 + | + | (compared using ==) + | # ./spec/rspec/core/resources/formatter_specs.rb:11 + | # ./spec/support/formatter_support.rb:13:in `run_example_specs_with_formatter' + | # ./spec/support/sandboxing.rb:16 + | # ./spec/support/sandboxing.rb:14 + | # ./spec/support/sandboxing.rb:8 | |Failures: | @@ -85,13 +97,25 @@ def expected_summary_output_for_example_specs else def expected_summary_output_for_example_specs <<-EOS.gsub(/^\s+\|/, '').chomp - |Pending: - | pending spec with no implementation is pending - | # Not yet implemented - | # ./spec/rspec/core/resources/formatter_specs.rb:4 - | pending command with block format with content that would fail is pending - | # No reason given - | # ./spec/rspec/core/resources/formatter_specs.rb:9 + |Pending: (Failures listed here are expected and do not affect your suite's status) + | + | 1) pending spec with no implementation is pending + | # Not yet implemented + | # ./spec/rspec/core/resources/formatter_specs.rb:4 + | + | 2) pending command with block format with content that would fail is pending + | # No reason given + | Failure/Error: expect(1).to eq(2) + | + | expected: 2 + | got: 1 + | + | (compared using ==) + | # ./spec/rspec/core/resources/formatter_specs.rb:11:in `block (3 levels) in ' + | # ./spec/support/formatter_support.rb:13:in `run_example_specs_with_formatter' + | # ./spec/support/sandboxing.rb:16:in `block (4 levels) in ' + | # ./spec/support/sandboxing.rb:14:in `block (3 levels) in ' + | # ./spec/support/sandboxing.rb:8:in `block (2 levels) in ' | |Failures: | From 294fc7fa59195a61269c034e2eb682921ab70994 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 14 Jan 2015 19:22:56 -0800 Subject: [PATCH 246/275] Exclude aruba 0.6.2 to get build to pass. See https://github.com/cucumber/aruba/commit/5b2c7b445bc80083e577b793e4411887ef295660#commitcomment-9284628 for background. --- rspec-core.gemspec | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rspec-core.gemspec b/rspec-core.gemspec index 44d4b4edb9..907f14836a 100644 --- a/rspec-core.gemspec +++ b/rspec-core.gemspec @@ -42,7 +42,11 @@ Gem::Specification.new do |s| s.add_development_dependency "rake", "~> 10.0.0" s.add_development_dependency "cucumber", "~> 1.3" s.add_development_dependency "minitest", "~> 5.3" - s.add_development_dependency "aruba", "~> 0.5" + + # Aruba 0.6.2 removed an API we rely on. For now we are excluding + # that version until we find out if they will restore it. See: + # https://github.com/cucumber/aruba/commit/5b2c7b445bc80083e577b793e4411887ef295660#commitcomment-9284628 + s.add_development_dependency "aruba", "~> 0.5", "!= 0.6.2" s.add_development_dependency "nokogiri", (RUBY_VERSION < '1.9.3' ? "1.5.2" : "~> 1.5") s.add_development_dependency "coderay", "~> 1.0.9" From 2257ad387637a53dd7fc85d0f7019870007802da Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 22 Dec 2014 14:14:20 -0800 Subject: [PATCH 247/275] Keep benchmark working. --- benchmarks/module_inclusion_filtering.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/module_inclusion_filtering.rb b/benchmarks/module_inclusion_filtering.rb index dc80c50ed7..eaeb5f01c6 100644 --- a/benchmarks/module_inclusion_filtering.rb +++ b/benchmarks/module_inclusion_filtering.rb @@ -21,7 +21,7 @@ def include(mod, *filters) def old_configure_group(group) @include_extend_or_prepend_modules.each do |include_extend_or_prepend, mod, filters| - next unless filters.empty? || group.apply?(:any?, filters) + next unless filters.empty? || RSpec::Core::MetadataFilter.apply?(:any?, filters, group.metadata) __send__("safe_#{include_extend_or_prepend}", mod, group) end end From 58d954358ed46fd71c5514b328a6558ae808df82 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 28 Oct 2014 14:14:18 -0700 Subject: [PATCH 248/275] Apply module inclusion to individual examples when filtered. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously it only applied to filtered groups. Luckily, ruby’s object model supports this very easily through the use of singleton classes. --- lib/rspec/core/configuration.rb | 10 +++++ lib/rspec/core/example.rb | 1 + spec/rspec/core/configuration_spec.rb | 63 +++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index 8553b34c69..2b4117e230 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1146,6 +1146,16 @@ def configure_group_with(group, module_list, application_method) end end + # @private + # + # Used internally to extend the singleton class of a single example's + # example group instance with modules using `include` and/or `extend`. + def configure_example(example) + @include_modules.items_for(example.metadata).each do |mod| + safe_include(mod, example.example_group_instance.singleton_class) + end + end + # @private def safe_include(mod, host) host.__send__(:include, mod) unless host < mod diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index b0aa2382d1..bcdddebbcc 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -160,6 +160,7 @@ def example_group # @param example_group_instance the instance of an ExampleGroup subclass def run(example_group_instance, reporter) @example_group_instance = example_group_instance + RSpec.configuration.configure_example(self) RSpec.current_example = self start(reporter) diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index eae31f8f0d..331d734ee3 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -862,6 +862,69 @@ def metadata_hash(*args) expect(group).not_to respond_to(:you_call_this_a_blt?) expect(group.new.you_call_this_a_blt?).to eq("egad man, where's the mayo?!?!?") end + + it "includes the given module into the singleton class of matching examples" do + RSpec.configure do |c| + c.include(InstanceLevelMethods, :magic_key => :include) + end + + value = ex1 = ex2 = nil + + RSpec.describe("Group") do + ex1 = example("ex", :magic_key => :include) do + value = you_call_this_a_blt? + end + + ex2 = example("ex") { you_call_this_a_blt? } + end.run + + expect(ex1.execution_result.exception).to be_nil + expect(value).to match(/egad/) + expect(ex2.execution_result.exception).to be_a(NameError) + end + + it "ensures that `before` hooks have access to the module methods, even when only included in the singleton class of one example" do + RSpec.configure do |c| + c.include(Module.new { def which_mod; :mod_1; end }, :mod_1) + c.include(Module.new { def which_mod; :mod_2; end }, :mod_2) + end + + ex1_value = ex2_value = ex3 = nil + + RSpec.describe("group") do + before { @value = which_mod } + example("ex", :mod_1) { ex1_value = @value } + example("ex", :mod_2) { ex2_value = @value } + ex3 = example("ex") { } + end.run + + expect(ex1_value).to eq(:mod_1) + expect(ex2_value).to eq(:mod_2) + expect(ex3.execution_result.exception).to be_a(NameError) + end + + it "does not include the module in an example's singleton class when it has already been included in the group" do + mod = Module.new do + def self.inclusions + @inclusions ||= [] + end + + def self.included(klass) + inclusions << klass + end + end + + RSpec.configure do |c| + c.include mod, :magic_key + end + + group = RSpec.describe("Group", :magic_key) do + example("ex", :magic_key) { } + end + + group.run + expect(mod.inclusions).to eq([group]) + end end end From 7bf36bbf1285a0de3eb2a9b019b4f66af4705778 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 28 Oct 2014 14:46:14 -0700 Subject: [PATCH 249/275] Ensure shared contexts applied to one example have their hooks run. --- lib/rspec/core/example.rb | 10 ++++-- lib/rspec/core/hooks.rb | 14 ++++++-- spec/rspec/core/shared_example_group_spec.rb | 35 ++++++++++++++++++++ 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index bcdddebbcc..733f58c44c 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -334,8 +334,12 @@ def instance_exec(*args, &block) private + def hooks + example_group_instance.singleton_class.hooks + end + def with_around_example_hooks - @example_group_class.hooks.run(:around, :example, self) { yield } + hooks.run(:around, :example, self) { yield } rescue Exception => e set_exception(e, "in an `around(:example)` hook") end @@ -371,12 +375,12 @@ def record_finished(status) def run_before_example @example_group_instance.setup_mocks_for_rspec - @example_group_class.hooks.run(:before, :example, self) + hooks.run(:before, :example, self) end def run_after_example assign_generated_description if defined?(::RSpec::Matchers) - @example_group_class.hooks.run(:after, :example, self) + hooks.run(:after, :example, self) verify_mocks ensure @example_group_instance.teardown_mocks_for_rspec diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index e5a66b3ac9..a37a70c003 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -582,13 +582,13 @@ def normalized_scope_for(scope) end def run_example_hooks_for(example, position, each_method) - @owner.parent_groups.__send__(each_method) do |group| + owner_parent_groups.__send__(each_method) do |group| group.hooks.run_owned_hooks_for(position, :example, example) end end def run_around_example_hooks_for(example) - hooks = FlatMap.flat_map(@owner.parent_groups) do |group| + hooks = FlatMap.flat_map(owner_parent_groups) do |group| group.hooks.matching_hooks_for(:around, :example, example) end @@ -599,6 +599,16 @@ def run_around_example_hooks_for(example) procsy.wrap { around_hook.execute_with(example, procsy) } end.call end + + if respond_to?(:singleton_class) && singleton_class.ancestors.include?(singleton_class) + def owner_parent_groups + @owner.parent_groups + end + else # Ruby < 2.1 (see https://bugs.ruby-lang.org/issues/8035) + def owner_parent_groups + @owner_parent_groups ||= [@owner] + @owner.parent_groups + end + end end end end diff --git a/spec/rspec/core/shared_example_group_spec.rb b/spec/rspec/core/shared_example_group_spec.rb index 65123b3a89..da59bc9c58 100644 --- a/spec/rspec/core/shared_example_group_spec.rb +++ b/spec/rspec/core/shared_example_group_spec.rb @@ -132,6 +132,41 @@ module Core expect(matching_group.bar).to eq("bar") expect(non_matching_group).not_to respond_to(:bar) end + + it 'runs hooks for individual examples that have matching metadata' do + sequence = [] + + define_shared_group("name", :include_it) do + # These two should not be run for individual examples... + before(:context) { sequence << :before_context } + after(:context) { sequence << :after_context } + + before(:example) { sequence << :before_example } + after(:example) { sequence << :after_example } + + around(:example) do |ex| + sequence << :around_example_before + ex.run + sequence << :around_example_after + end + end + + RSpec.describe "group" do + example("ex1") { sequence << :unmatched_example_1 } + example("ex2", :include_it) { sequence << :matched_example } + example("ex3") { sequence << :unmatched_example_2 } + end.run + + expect(sequence).to eq([ + :unmatched_example_1, + :around_example_before, + :before_example, + :matched_example, + :after_example, + :around_example_after, + :unmatched_example_2 + ]) + end end context "when called at the top level" do From ab2b613050d72327def1d840474e1cab647a7bc6 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 29 Oct 2014 10:51:26 -0700 Subject: [PATCH 250/275] Got things to work on 1.8.7. --- lib/rspec/core/configuration.rb | 15 ++++++++++----- lib/rspec/core/example_group.rb | 7 +++++++ spec/rspec/core/example_group_spec.rb | 3 ++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index 2b4117e230..106547ae57 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1156,11 +1156,6 @@ def configure_example(example) end end - # @private - def safe_include(mod, host) - host.__send__(:include, mod) unless host < mod - end - if RSpec::Support::RubyFeatures.module_prepends_supported? # @private def safe_prepend(mod, host) @@ -1178,11 +1173,21 @@ def requires=(paths) # @private if RUBY_VERSION.to_f >= 1.9 + # @private + def safe_include(mod, host) + host.__send__(:include, mod) unless host < mod + end + # @private def safe_extend(mod, host) host.extend(mod) unless host.singleton_class < mod end else + # @private + def safe_include(mod, host) + host.__send__(:include, mod) unless host.included_modules.include?(mod) + end + # @private def safe_extend(mod, host) host.extend(mod) unless (class << host; self; end).included_modules.include?(mod) diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index 5d022b519b..c9db7b030b 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -573,6 +573,13 @@ def inspect "#<#{self.class} #{@__inspect_output}>" end + unless method_defined?(:singleton_class) # for 1.8.7 + # @private + def singleton_class + class << self; self; end + end + end + # Raised when an RSpec API is called in the wrong scope, such as `before` # being called from within an example rather than from within an example # group block. diff --git a/spec/rspec/core/example_group_spec.rb b/spec/rspec/core/example_group_spec.rb index 2c64d31a06..f19175777f 100644 --- a/spec/rspec/core/example_group_spec.rb +++ b/spec/rspec/core/example_group_spec.rb @@ -1662,7 +1662,8 @@ def foo; end rspec_core_methods = ExampleGroup.instance_methods - RSpec::Matchers.instance_methods - RSpec::Mocks::ExampleMethods.instance_methods - - Object.instance_methods + Object.instance_methods - + ["singleton_class"] # Feel free to expand this list if you intend to add another public API # for users. RSpec internals should not add methods here, though. From 639db76e5fd0e845ae6c44527a0ba750cb934acd Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Thu, 6 Nov 2014 22:09:34 -0800 Subject: [PATCH 251/275] Run `:context` hooks from shared contexts that apply to one example. --- lib/rspec/core/example.rb | 10 ++- lib/rspec/core/example_group.rb | 24 ++++++- spec/rspec/core/shared_example_group_spec.rb | 75 +++++++++++++------- 3 files changed, 80 insertions(+), 29 deletions(-) diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 733f58c44c..b3b3419db5 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -170,7 +170,7 @@ def run(example_group_instance, reporter) if skipped? Pending.mark_pending! self, skip elsif !RSpec.configuration.dry_run? - with_around_example_hooks do + with_around_and_singleton_context_hooks do begin run_before_example @example_group_instance.instance_exec(self, &@example_block) @@ -378,6 +378,14 @@ def run_before_example hooks.run(:before, :example, self) end + def with_around_and_singleton_context_hooks + singleton_context_hooks_host = example_group_instance.singleton_class + singleton_context_hooks_host.run_before_context_hooks(example_group_instance) + with_around_example_hooks { yield } + ensure + singleton_context_hooks_host.run_after_context_hooks(example_group_instance) + end + def run_after_example assign_generated_description if defined?(::RSpec::Matchers) hooks.run(:after, :example, self) diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index c9db7b030b..ffe4f0c49f 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -442,7 +442,7 @@ def self.store_before_context_ivars(example_group_instance) # @private def self.run_before_context_hooks(example_group_instance) - set_ivars(example_group_instance, superclass.before_context_ivars) + set_ivars(example_group_instance, superclass_before_context_ivars) ContextHookMemoizedHash::Before.isolate_for_context_hook(example_group_instance) do hooks.run(:before, :context, example_group_instance) @@ -451,6 +451,28 @@ def self.run_before_context_hooks(example_group_instance) store_before_context_ivars(example_group_instance) end + if RUBY_VERSION.to_f >= 1.9 + # @private + def self.superclass_before_context_ivars + superclass.before_context_ivars + end + else # 1.8.7 + # @private + def self.superclass_before_context_ivars + if superclass.respond_to?(:before_context_ivars) + superclass.before_context_ivars + else + # `self` must be the singleton class of an ExampleGroup instance. + # On 1.8.7, the superclass of a singleton class of an instance of A + # is A's singleton class. On 1.9+, it's A. On 1.8.7, the first ancestor + # is A, so we can mirror 1.8.7's behavior here. Note that we have to + # search for the first that responds to `before_context_ivars` + # in case a module has been included in the singleton class. + ancestors.find { |a| a.respond_to?(:before_context_ivars) }.before_context_ivars + end + end + end + # @private def self.run_after_context_hooks(example_group_instance) set_ivars(example_group_instance, before_context_ivars) diff --git a/spec/rspec/core/shared_example_group_spec.rb b/spec/rspec/core/shared_example_group_spec.rb index da59bc9c58..bf7be3793f 100644 --- a/spec/rspec/core/shared_example_group_spec.rb +++ b/spec/rspec/core/shared_example_group_spec.rb @@ -133,39 +133,60 @@ module Core expect(non_matching_group).not_to respond_to(:bar) end - it 'runs hooks for individual examples that have matching metadata' do - sequence = [] + describe "hooks for individual examples that have matching metadata" do + it 'runs them' do + sequence = [] - define_shared_group("name", :include_it) do - # These two should not be run for individual examples... - before(:context) { sequence << :before_context } - after(:context) { sequence << :after_context } + define_shared_group("name", :include_it) do + before(:context) { sequence << :before_context } + after(:context) { sequence << :after_context } - before(:example) { sequence << :before_example } - after(:example) { sequence << :after_example } + before(:example) { sequence << :before_example } + after(:example) { sequence << :after_example } - around(:example) do |ex| - sequence << :around_example_before - ex.run - sequence << :around_example_after + around(:example) do |ex| + sequence << :around_example_before + ex.run + sequence << :around_example_after + end end + + RSpec.describe "group" do + example("ex1") { sequence << :unmatched_example_1 } + example("ex2", :include_it) { sequence << :matched_example } + example("ex3") { sequence << :unmatched_example_2 } + end.run + + expect(sequence).to eq([ + :unmatched_example_1, + :before_context, + :around_example_before, + :before_example, + :matched_example, + :after_example, + :around_example_after, + :after_context, + :unmatched_example_2 + ]) end - RSpec.describe "group" do - example("ex1") { sequence << :unmatched_example_1 } - example("ex2", :include_it) { sequence << :matched_example } - example("ex3") { sequence << :unmatched_example_2 } - end.run - - expect(sequence).to eq([ - :unmatched_example_1, - :around_example_before, - :before_example, - :matched_example, - :after_example, - :around_example_after, - :unmatched_example_2 - ]) + it 'runs the `after(:context)` hooks even if the `before(:context)` hook raises an error' do + sequence = [] + + define_shared_group("name", :include_it) do + before(:context) do + sequence << :before_context + raise "boom" + end + after(:context) { sequence << :after_context } + end + + RSpec.describe "group" do + example("ex", :include_it) { sequence << :example } + end.run + + expect(sequence).to eq([ :before_context, :after_context ]) + end end end From 3e1184ced381d087dadcb3eef34cd4dfad17f36e Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 19 Dec 2014 23:59:10 -0800 Subject: [PATCH 252/275] Make :context hooks in config apply to individual matching examples. --- lib/rspec/core/example.rb | 1 + lib/rspec/core/hooks.rb | 25 ++++++++++----- spec/rspec/core/hooks_filtering_spec.rb | 42 ++++++++++++++++++++++--- 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index b3b3419db5..8d02d6ef72 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -160,6 +160,7 @@ def example_group # @param example_group_instance the instance of an ExampleGroup subclass def run(example_group_instance, reporter) @example_group_instance = example_group_instance + hooks.register_global_singleton_context_hooks(self, RSpec.configuration.hooks) RSpec.configuration.configure_example(self) RSpec.current_example = self diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index a37a70c003..74165fcdb6 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -416,12 +416,21 @@ def initialize(owner) end def register_globals(host, globals) - process(host, globals, :before, :example) - process(host, globals, :after, :example) - process(host, globals, :around, :example) + parent_groups = host.parent_groups - process(host, globals, :before, :context) - process(host, globals, :after, :context) + process(host, parent_groups, globals, :before, :example, &:options) + process(host, parent_groups, globals, :after, :example, &:options) + process(host, parent_groups, globals, :around, :example, &:options) + + process(host, parent_groups, globals, :before, :context, &:options) + process(host, parent_groups, globals, :after, :context, &:options) + end + + def register_global_singleton_context_hooks(example, globals) + parent_groups = example.example_group.parent_groups + + process(example, parent_groups, globals, :before, :context) { {} } + process(example, parent_groups, globals, :after, :context) { {} } end def register(prepend_or_append, position, *args, &block) @@ -539,17 +548,17 @@ def ensure_hooks_initialized_for(position, scope) end end - def process(host, globals, position, scope) + def process(host, parent_groups, globals, position, scope) hooks_to_process = globals.processable_hooks_for(position, scope, host) return if hooks_to_process.empty? - hooks_to_process -= FlatMap.flat_map(host.parent_groups) do |group| + hooks_to_process -= FlatMap.flat_map(parent_groups) do |group| group.hooks.all_hooks_for(position, scope) end return if hooks_to_process.empty? repository = ensure_hooks_initialized_for(position, scope) - hooks_to_process.each { |hook| repository.append hook, hook.options } + hooks_to_process.each { |hook| repository.append hook, (yield hook) } end def scope_and_options_from(*args) diff --git a/spec/rspec/core/hooks_filtering_spec.rb b/spec/rspec/core/hooks_filtering_spec.rb index d35ef38272..2cf81960eb 100644 --- a/spec/rspec/core/hooks_filtering_spec.rb +++ b/spec/rspec/core/hooks_filtering_spec.rb @@ -168,10 +168,6 @@ def filters 'after each in config' ]) end - - it "does not run the `:all` hooks" do - expect(all_filters).to be_empty - end end describe 'an example without matching metadata' do @@ -224,10 +220,12 @@ def filters expect(sequence).to eq([ "ex1", + "before all in config", "around each in config", "before each in config", "ex2", "after each in config", + "after all in config" ]) end @@ -246,5 +244,41 @@ def filters expect(filters).to eq([:before_each, :matching_example, :nonmatching_example]) end end + + describe ":context hooks defined in configuration with metadata" do + it 'applies to individual matching examples' do + sequence = [] + + RSpec.configure do |config| + config.before(:context, :apply_it) { sequence << :before_context } + config.after(:context, :apply_it) { sequence << :after_context } + end + + RSpec.describe do + example("ex", :apply_it) { sequence << :example } + end.run + + expect(sequence).to eq([:before_context, :example, :after_context]) + end + + it 'does not apply to individual matching examples for which it also applies to a parent example group' do + sequence = [] + + RSpec.configure do |config| + config.before(:context, :apply_it) { sequence << :before_context } + config.after(:context, :apply_it) { sequence << :after_context } + end + + RSpec.describe "Group", :apply_it do + example("ex") { sequence << :outer_example } + + context "nested", :apply_it => false do + example("ex", :apply_it) { sequence << :inner_example } + end + end.run + + expect(sequence).to eq([:before_context, :outer_example, :inner_example, :after_context]) + end + end end end From 658e6e0a51563cc65f2d39398a46a0ecb2ce47a3 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 8 Dec 2014 08:46:39 -0800 Subject: [PATCH 253/275] Skip specs failing due to a JRuby 1.7 bug when run in ruby 2.0 mode. --- spec/rspec/core/shared_example_group_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/rspec/core/shared_example_group_spec.rb b/spec/rspec/core/shared_example_group_spec.rb index bf7be3793f..65b82b8576 100644 --- a/spec/rspec/core/shared_example_group_spec.rb +++ b/spec/rspec/core/shared_example_group_spec.rb @@ -134,6 +134,14 @@ module Core end describe "hooks for individual examples that have matching metadata" do + before do + skip "These specs pass in 2.0 mode on JRuby 1.7.8 but fail on " \ + "1.7.15 when the entire spec suite runs. They pass on " \ + "1.7.15 when this one spec file is run or if we filter to " \ + "just them. Given that 2.0 support on JRuby 1.7 is " \ + "experimental, we're just skipping these specs." + end if RUBY_VERSION == "2.0.0" && RSpec::Support::Ruby.jruby? + it 'runs them' do sequence = [] From ec88ce75209a7267f92cb7f079ea895fdc99d5e3 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 8 Dec 2014 16:04:35 -0800 Subject: [PATCH 254/275] =?UTF-8?q?Add=20benchmarks=20for=20treating=20an?= =?UTF-8?q?=20example=E2=80=99s=20group=20instance=E2=80=99s=20singleton?= =?UTF-8?q?=20class=20as=20a=20group.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- benchmarks/singleton_example_groups/helper.rb | 93 +++++++++++++++++++ .../with_config_hooks.rb | 29 ++++++ ...nclusions_and_shared_context_inclusions.rb | 35 +++++++ .../with_module_inclusions.rb | 28 ++++++ .../with_no_config_hooks_or_inclusions.rb | 22 +++++ .../with_shared_context_inclusions.rb | 27 ++++++ 6 files changed, 234 insertions(+) create mode 100644 benchmarks/singleton_example_groups/helper.rb create mode 100644 benchmarks/singleton_example_groups/with_config_hooks.rb create mode 100644 benchmarks/singleton_example_groups/with_config_hooks_module_inclusions_and_shared_context_inclusions.rb create mode 100644 benchmarks/singleton_example_groups/with_module_inclusions.rb create mode 100644 benchmarks/singleton_example_groups/with_no_config_hooks_or_inclusions.rb create mode 100644 benchmarks/singleton_example_groups/with_shared_context_inclusions.rb diff --git a/benchmarks/singleton_example_groups/helper.rb b/benchmarks/singleton_example_groups/helper.rb new file mode 100644 index 0000000000..4c643febd2 --- /dev/null +++ b/benchmarks/singleton_example_groups/helper.rb @@ -0,0 +1,93 @@ +require_relative "../../bundle/bundler/setup" # configures load paths +require 'rspec/core' + +class << RSpec + attr_writer :world +end + +RSpec::Core::Example.class_eval do + alias_method :new_with_around_and_singleton_context_hooks, :with_around_and_singleton_context_hooks + alias_method :old_with_around_and_singleton_context_hooks, :with_around_example_hooks +end + +RSpec::Core::Hooks::HookCollections.class_eval do + def old_register_global_singleton_context_hooks(*) + # no-op: this method didn't exist before + end + alias_method :new_register_global_singleton_context_hooks, :register_global_singleton_context_hooks +end + +RSpec::Core::Configuration.class_eval do + def old_configure_example(*) + # no-op: this method didn't exist before + end + alias_method :new_configure_example, :configure_example +end + +RSpec.configure do |c| + c.output_stream = StringIO.new +end + +require 'benchmark/ips' + +class BenchmarkHelpers + def self.prepare_implementation(prefix) + RSpec.world = RSpec::Core::World.new # clear our state + RSpec::Core::Example.__send__ :alias_method, :with_around_and_singleton_context_hooks, :"#{prefix}_with_around_and_singleton_context_hooks" + RSpec::Core::Hooks::HookCollections.__send__ :alias_method, :register_global_singleton_context_hooks, :"#{prefix}_register_global_singleton_context_hooks" + RSpec::Core::Configuration.__send__ :alias_method, :configure_example, :"#{prefix}_configure_example" + end + + @@runner = RSpec::Core::Runner.new(RSpec::Core::ConfigurationOptions.new([])) + def self.define_and_run_examples(desc, count, group_meta: {}, example_meta: {}) + groups = count.times.map do |i| + RSpec.describe "Group #{desc} #{i}", group_meta do + 10.times { |j| example("ex #{j}", example_meta) { } } + end + end + + @@runner.run_specs(groups) + end + + def self.run_benchmarks + Benchmark.ips do |x| + implementations = { :old => "without", :new => "with" } + # Historically, many of our benchmarks have initially been order-sensitive, + # where whichever implementation went first got favored because defining + # more groups (or whatever) would cause things to slow down. To easily + # check if we're having those problems, you can pass REVERSE=1 to try + # it out in the opposite order. + implementations = implementations.to_a.reverse.to_h if ENV['REVERSE'] + + implementations.each do |prefix, description| + x.report("No match -- #{description} singleton group support") do |times| + prepare_implementation(prefix) + define_and_run_examples("No match/#{description}", times) + end + end + + implementations.each do |prefix, description| + x.report("Example match -- #{description} singleton group support") do |times| + prepare_implementation(prefix) + define_and_run_examples("Example match/#{description}", times, example_meta: { apply_it: true }) + end + end + + implementations.each do |prefix, description| + x.report("Group match -- #{description} singleton group support") do |times| + prepare_implementation(prefix) + define_and_run_examples("Group match/#{description}", times, group_meta: { apply_it: true }) + end + end + + implementations.each do |prefix, description| + x.report("Both match -- #{description} singleton group support") do |times| + prepare_implementation(prefix) + define_and_run_examples("Both match/#{description}", times, + example_meta: { apply_it: true }, + group_meta: { apply_it: true }) + end + end + end + end +end diff --git a/benchmarks/singleton_example_groups/with_config_hooks.rb b/benchmarks/singleton_example_groups/with_config_hooks.rb new file mode 100644 index 0000000000..e988b1ecae --- /dev/null +++ b/benchmarks/singleton_example_groups/with_config_hooks.rb @@ -0,0 +1,29 @@ +require_relative "helper" + +RSpec.configure do |c| + 10.times do + c.before(:context, :apply_it) { } + c.after(:context, :apply_it) { } + end +end + +BenchmarkHelpers.run_benchmarks + +__END__ + +No match -- without singleton group support + 489.165 (±33.3%) i/s - 2.080k +No match -- with singleton group support + 451.019 (±29.3%) i/s - 1.890k +Example match -- without singleton group support + 465.178 (±35.3%) i/s - 1.820k +Example match -- with singleton group support + 244.273 (±23.3%) i/s - 1.064k +Group match -- without singleton group support + 406.979 (±27.0%) i/s - 1.700k +Group match -- with singleton group support + 327.455 (±22.6%) i/s - 1.421k +Both match -- without singleton group support + 423.859 (±32.1%) i/s - 1.763k +Both match -- with singleton group support + 293.873 (±23.5%) i/s - 1.333k diff --git a/benchmarks/singleton_example_groups/with_config_hooks_module_inclusions_and_shared_context_inclusions.rb b/benchmarks/singleton_example_groups/with_config_hooks_module_inclusions_and_shared_context_inclusions.rb new file mode 100644 index 0000000000..b76751d8ec --- /dev/null +++ b/benchmarks/singleton_example_groups/with_config_hooks_module_inclusions_and_shared_context_inclusions.rb @@ -0,0 +1,35 @@ +require_relative "helper" + +RSpec.configure do |c| + 10.times do + c.before(:context, :apply_it) { } + c.after(:context, :apply_it) { } + c.include Module.new, :apply_it + end +end + +1.upto(10) do |i| + RSpec.shared_context "context #{i}", :apply_it do + end +end + +BenchmarkHelpers.run_benchmarks + +__END__ + +No match -- without singleton group support + 452.015 (±33.8%) i/s - 1.900k +No match -- with singleton group support + 464.520 (±31.0%) i/s - 1.887k +Example match -- without singleton group support + 476.961 (±34.6%) i/s - 1.978k in 5.340615s +Example match -- with singleton group support + 76.177 (±34.1%) i/s - 266.000 +Group match -- without singleton group support + 364.554 (±28.3%) i/s - 1.372k +Group match -- with singleton group support + 281.761 (±24.1%) i/s - 1.200k +Both match -- without singleton group support + 281.521 (±27.4%) i/s - 1.188k +Both match -- with singleton group support + 297.886 (±18.1%) i/s - 1.288k diff --git a/benchmarks/singleton_example_groups/with_module_inclusions.rb b/benchmarks/singleton_example_groups/with_module_inclusions.rb new file mode 100644 index 0000000000..8d133c6e0d --- /dev/null +++ b/benchmarks/singleton_example_groups/with_module_inclusions.rb @@ -0,0 +1,28 @@ +require_relative "helper" + +RSpec.configure do |c| + 1.upto(10) do + c.include Module.new, :apply_it + end +end + +BenchmarkHelpers.run_benchmarks + +__END__ + +No match -- without singleton group support + 519.880 (±33.9%) i/s - 2.162k +No match -- with singleton group support + 481.334 (±28.5%) i/s - 2.028k +Example match -- without singleton group support + 491.348 (±29.9%) i/s - 2.068k +Example match -- with singleton group support + 407.257 (±22.3%) i/s - 1.782k +Group match -- without singleton group support + 483.403 (±36.4%) i/s - 1.815k +Group match -- with singleton group support + 424.932 (±29.4%) i/s - 1.804k +Both match -- without singleton group support + 397.831 (±31.9%) i/s - 1.720k +Both match -- with singleton group support + 424.233 (±25.5%) i/s - 1.720k diff --git a/benchmarks/singleton_example_groups/with_no_config_hooks_or_inclusions.rb b/benchmarks/singleton_example_groups/with_no_config_hooks_or_inclusions.rb new file mode 100644 index 0000000000..78529f3abb --- /dev/null +++ b/benchmarks/singleton_example_groups/with_no_config_hooks_or_inclusions.rb @@ -0,0 +1,22 @@ +require_relative "helper" + +BenchmarkHelpers.run_benchmarks + +__END__ + +No match -- without singleton group support + 504.865 (±31.9%) i/s - 2.128k +No match -- with singleton group support + 463.115 (±26.6%) i/s - 1.998k +Example match -- without singleton group support + 472.825 (±31.9%) i/s - 1.938k +Example match -- with singleton group support + 436.539 (±33.9%) i/s - 1.840k +Group match -- without singleton group support + 460.643 (±33.4%) i/s - 1.892k +Group match -- with singleton group support + 430.339 (±23.2%) i/s - 1.911k +Both match -- without singleton group support + 406.712 (±26.6%) i/s - 1.848k +Both match -- with singleton group support + 470.299 (±26.4%) i/s - 1.890k diff --git a/benchmarks/singleton_example_groups/with_shared_context_inclusions.rb b/benchmarks/singleton_example_groups/with_shared_context_inclusions.rb new file mode 100644 index 0000000000..74f30435cc --- /dev/null +++ b/benchmarks/singleton_example_groups/with_shared_context_inclusions.rb @@ -0,0 +1,27 @@ +require_relative "helper" + +1.upto(10) do |i| + RSpec.shared_context "context #{i}", :apply_it do + end +end + +BenchmarkHelpers.run_benchmarks + +__END__ + +No match -- without singleton group support + 503.700 (±33.2%) i/s - 2.184k +No match -- with singleton group support + 471.018 (±26.8%) i/s - 2.009k +Example match -- without singleton group support + 467.859 (±34.8%) i/s - 2.021k in 5.600106s +Example match -- with singleton group support + 84.138 (±34.5%) i/s - 296.000 in 5.515586s +Group match -- without singleton group support + 384.144 (±27.9%) i/s - 1.560k +Group match -- with singleton group support + 349.301 (±27.5%) i/s - 1.288k +Both match -- without singleton group support + 388.100 (±25.8%) i/s - 1.702k +Both match -- with singleton group support + 339.310 (±20.3%) i/s - 1.504k From f054a61b247fd6219ce6d2efb343855789345db2 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 24 Dec 2014 12:12:41 -0800 Subject: [PATCH 255/275] Add profiling support with stackprof. --- benchmarks/singleton_example_groups/helper.rb | 27 +++++++++++++++++++ .../with_shared_context_inclusions.rb | 1 + 2 files changed, 28 insertions(+) diff --git a/benchmarks/singleton_example_groups/helper.rb b/benchmarks/singleton_example_groups/helper.rb index 4c643febd2..8f543c1e25 100644 --- a/benchmarks/singleton_example_groups/helper.rb +++ b/benchmarks/singleton_example_groups/helper.rb @@ -1,5 +1,6 @@ require_relative "../../bundle/bundler/setup" # configures load paths require 'rspec/core' +require 'stackprof' class << RSpec attr_writer :world @@ -49,6 +50,32 @@ def self.define_and_run_examples(desc, count, group_meta: {}, example_meta: {}) @@runner.run_specs(groups) end + def self.profile(count, meta = { example_meta: { apply_it: true } }) + [:new, :old].map do |prefix| + prepare_implementation(prefix) + + results = StackProf.run(mode: :cpu) do + define_and_run_examples("No match/#{prefix}", count, meta) + end + + format_profile_results(results, prefix) + end + end + + def self.format_profile_results(results, prefix) + File.open("tmp/#{prefix}_stack_prof_results.txt", "w") do |f| + StackProf::Report.new(results).print_text(false, nil, f) + end + system "open tmp/#{prefix}_stack_prof_results.txt" + + File.open("tmp/#{prefix}_stack_prof_results.graphviz", "w") do |f| + StackProf::Report.new(results).print_graphviz(nil, f) + end + + system "dot tmp/#{prefix}_stack_prof_results.graphviz -Tpdf > tmp/#{prefix}_stack_prof_results.pdf" + system "open tmp/#{prefix}_stack_prof_results.pdf" + end + def self.run_benchmarks Benchmark.ips do |x| implementations = { :old => "without", :new => "with" } diff --git a/benchmarks/singleton_example_groups/with_shared_context_inclusions.rb b/benchmarks/singleton_example_groups/with_shared_context_inclusions.rb index 74f30435cc..28ab30e365 100644 --- a/benchmarks/singleton_example_groups/with_shared_context_inclusions.rb +++ b/benchmarks/singleton_example_groups/with_shared_context_inclusions.rb @@ -6,6 +6,7 @@ end BenchmarkHelpers.run_benchmarks +# BenchmarkHelpers.profile(1000) __END__ From 8a09786c0e35aa6c17c74f61328bbac7e24281ba Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 24 Dec 2014 15:10:50 -0800 Subject: [PATCH 256/275] Update docs to explain how singleton example groups work. --- .../example_groups/shared_context.feature | 20 +++++++++++++++++++ features/helper_methods/modules.feature | 6 ++++++ features/hooks/filtering.feature | 20 +++++++++++++++---- lib/rspec/core/configuration.rb | 9 ++++++++- lib/rspec/core/hooks.rb | 7 +++++++ lib/rspec/core/shared_example_group.rb | 8 ++++---- 6 files changed, 61 insertions(+), 9 deletions(-) diff --git a/features/example_groups/shared_context.feature b/features/example_groups/shared_context.feature index a156d320e0..c21e23861e 100644 --- a/features/example_groups/shared_context.feature +++ b/features/example_groups/shared_context.feature @@ -4,6 +4,8 @@ Feature: shared context of example groups either explicitly, using `include_context`, or implicitly by matching metadata. + When implicitly including shared contexts via matching metadata, the normal way is to define matching metadata on an example group (in which case the ontext is included in the entire group), but you can also include it in an individual example. RSpec treats every example as having a singleton example group (analogous to Ruby's singleton classes) containing just the one example. + Background: Given a file named "shared_stuff.rb" with: """ruby @@ -90,3 +92,21 @@ Feature: shared context """ When I run `rspec shared_context_example.rb` Then the examples should all pass + + Scenario: Declare a shared context and include it with metadata of an individual example + Given a file named "shared_context_example.rb" with: + """ruby + require "./shared_stuff.rb" + + RSpec.describe "group that does not include the shared context" do + it "does not have access to shared methods normally" do + expect(self).not_to respond_to(:shared_method) + end + + it "has access to shared methods from examples with matching metadata", :a => :b do + expect(shared_method).to eq("it works") + end + end + """ + When I run `rspec shared_context_example.rb` + Then the examples should all pass diff --git a/features/helper_methods/modules.feature b/features/helper_methods/modules.feature index 200765a1ff..b92070e497 100644 --- a/features/helper_methods/modules.feature +++ b/features/helper_methods/modules.feature @@ -11,6 +11,8 @@ Feature: Define helper methods in a module given metadata will `include` or `extend` the module. You can also specify metadata using only symbols. + Note that examples that match a `config.include` module's metadata will also have the module included. RSpec treats every example as having a singleton example group (analogous to Ruby's singleton classes) containing just the one example. + Background: Given a file named "helpers.rb" with: """ruby @@ -79,6 +81,10 @@ Feature: Define helper methods in a module it "does not have access to the helper methods defined in the module" do expect { help }.to raise_error(NameError) end + + it "does have access when the example has matching metadata", :foo => :bar do + expect(help).to be(:available) + end end """ When I run `rspec include_module_in_some_groups_spec.rb` diff --git a/features/hooks/filtering.feature b/features/hooks/filtering.feature index 30af1a4d8d..abdb12be69 100644 --- a/features/hooks/filtering.feature +++ b/features/hooks/filtering.feature @@ -14,6 +14,8 @@ Feature: filters end ``` + Note that filtered `:context` hooks will still be applied to individual examples with matching metadata -- in effect, every example has a singleton example group containing just the one example (analogous to Ruby's singleton classes). + You can also specify metadata using only symbols. Scenario: Filter `before(:example)` hooks using arbitrary metadata @@ -127,6 +129,10 @@ Feature: filters expect(@hook).to be_nil end + it "runs the hook for a single example with matching metadata", :foo => :bar do + expect(@hook).to eq(:before_context_foo_bar) + end + describe "a nested subgroup with matching metadata", :foo => :bar do it "runs the hook" do expect(@hook).to eq(:before_context_foo_bar) @@ -166,31 +172,37 @@ Feature: filters it "does not run the hook" do puts "unfiltered" end + + it "runs the hook for a single example with matching metadata", :foo => :bar do + puts "filtered 1" + end end describe "a group with matching metadata", :foo => :bar do it "runs the hook" do - puts "filtered 1" + puts "filtered 2" end end describe "another group without matching metadata" do describe "a nested subgroup with matching metadata", :foo => :bar do it "runs the hook" do - puts "filtered 2" + puts "filtered 3" end end end end """ - When I run `rspec --format progress filter_after_context_hooks_spec.rb` + When I run `rspec --format progress filter_after_context_hooks_spec.rb --order defined` Then the examples should all pass And the output should contain: """ unfiltered .filtered 1 + after :context + .filtered 2 .after :context - filtered 2 + filtered 3 .after :context """ diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index 106547ae57..ffd52b7273 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1019,7 +1019,7 @@ def exclusion_filter # Tells RSpec to include `mod` in example groups. Methods defined in # `mod` are exposed to examples (not example groups). Use `filters` to - # constrain the groups in which to include the module. + # constrain the groups or examples in which to include the module. # # @example # @@ -1048,6 +1048,13 @@ def exclusion_filter # end # end # + # @note Filtered module inclusions can also be applied to + # individual examples that have matching metadata. Just like + # Ruby's object model is that every object has a singleton class + # which has only a single instance, RSpec's model is that every + # example has a singleton example group containing just the one + # example. + # # @see #extend # @see #prepend def include(mod, *filters) diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index 74165fcdb6..1d3cd92d17 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -88,6 +88,13 @@ module Hooks # end # end # + # Note that filtered config `:context` hooks can still be applied + # to individual examples that have matching metadata. Just like + # Ruby's object model is that every object has a singleton class + # which has only a single instance, RSpec's model is that every + # example has a singleton example group containing just the one + # example. + # # ### Warning: `before(:suite, :with => :conditions)` # # The conditions hash is used to match against specific examples. Since diff --git a/lib/rspec/core/shared_example_group.rb b/lib/rspec/core/shared_example_group.rb index fde57bf59d..fcffb853e3 100644 --- a/lib/rspec/core/shared_example_group.rb +++ b/lib/rspec/core/shared_example_group.rb @@ -48,13 +48,13 @@ module SharedExampleGroup # @param name [String, Symbol, Module] identifer to use when looking up # this shared group # @param metadata [Array, Hash] metadata to attach to this - # group; any example group with matching metadata will automatically - # include this shared example group. + # group; any example group or example with matching metadata will + # automatically include this shared example group. # @param block The block to be eval'd # @overload shared_examples(metadata, &block) # @param metadata [Array, Hash] metadata to attach to this - # group; any example group with matching metadata will automatically - # include this shared example group. + # group; any example group or example with matching metadata will + # automatically include this shared example group. # @param block The block to be eval'd # # Stores the block for later use. The block will be evaluated From c2f04ed55765ff489905a9e913f28f1e8a7eee84 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 12 Jan 2015 16:37:43 -0800 Subject: [PATCH 257/275] Remove unnecessary module inclusion. --- lib/rspec/core/world.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/rspec/core/world.rb b/lib/rspec/core/world.rb index 94211edfa6..307538eecc 100644 --- a/lib/rspec/core/world.rb +++ b/lib/rspec/core/world.rb @@ -4,8 +4,6 @@ module Core # # Internal container for global non-configuration data. class World - include RSpec::Core::Hooks - # @private attr_reader :example_groups, :filtered_examples From 06c5d755f24994d0b1eae471b16ec942387948ad Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 12 Jan 2015 21:00:45 -0800 Subject: [PATCH 258/275] Split FilterableItemRepository into two implementations. They are optimized for different cases. --- lib/rspec/core/configuration.rb | 8 +- lib/rspec/core/hooks.rb | 10 +- lib/rspec/core/metadata_filter.rb | 179 ++++++----- .../core/filterable_item_repository_spec.rb | 282 +++++++++--------- 4 files changed, 268 insertions(+), 211 deletions(-) diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index ffd52b7273..01e1f1fd37 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -297,9 +297,9 @@ def initialize @start_time = $_rspec_core_load_started_at || ::RSpec::Core::Time.now # rubocop:enable Style/GlobalVars @expectation_frameworks = [] - @include_modules = FilterableItemRepository.new(:any?) - @extend_modules = FilterableItemRepository.new(:any?) - @prepend_modules = FilterableItemRepository.new(:any?) + @include_modules = FilterableItemRepository::QueryOptimized.new(:any?) + @extend_modules = FilterableItemRepository::QueryOptimized.new(:any?) + @prepend_modules = FilterableItemRepository::QueryOptimized.new(:any?) @before_suite_hooks = [] @after_suite_hooks = [] @@ -333,7 +333,7 @@ def initialize @profile_examples = false @requires = [] @libs = [] - @derived_metadata_blocks = FilterableItemRepository.new(:any?) + @derived_metadata_blocks = FilterableItemRepository::QueryOptimized.new(:any?) end # @private diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index 1d3cd92d17..decfb0a4f9 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -540,18 +540,18 @@ def hooks_for(position, scope) def ensure_hooks_initialized_for(position, scope) if position == :before if scope == :example - @before_example_hooks ||= FilterableItemRepository.new(:all?) + @before_example_hooks ||= FilterableItemRepository::QueryOptimized.new(:all?) else - @before_context_hooks ||= FilterableItemRepository.new(:all?) + @before_context_hooks ||= FilterableItemRepository::QueryOptimized.new(:all?) end elsif position == :after if scope == :example - @after_example_hooks ||= FilterableItemRepository.new(:all?) + @after_example_hooks ||= FilterableItemRepository::QueryOptimized.new(:all?) else - @after_context_hooks ||= FilterableItemRepository.new(:all?) + @after_context_hooks ||= FilterableItemRepository::QueryOptimized.new(:all?) end else # around - @around_example_hooks ||= FilterableItemRepository.new(:all?) + @around_example_hooks ||= FilterableItemRepository::QueryOptimized.new(:all?) end end diff --git a/lib/rspec/core/metadata_filter.rb b/lib/rspec/core/metadata_filter.rb index c4fcd919fb..ffe8c86258 100644 --- a/lib/rspec/core/metadata_filter.rb +++ b/lib/rspec/core/metadata_filter.rb @@ -80,95 +80,140 @@ def silence_metadata_example_group_deprecations # Tracks a collection of filterable items (e.g. modules, hooks, etc) # and provides an optimized API to get the applicable items for the # metadata of an example or example group. + # + # There are two implementations, optimized for different uses. # @private - class FilterableItemRepository - attr_reader :items_and_filters + module FilterableItemRepository + # This implementation is simple, and is optimized for frequent + # updates but rare queries. `append` and `prepend` do no extra + # processing, and no internal memoization is done, since this + # is not optimized for queries. + # + # This is ideal for use by a example or example group, which may + # be updated multiple times with globally configured hooks, etc, + # but will not be queried frequently by other examples or examle + # groups. + # @private + class UpdateOptimized + attr_reader :items_and_filters - def initialize(applies_predicate) - @applies_predicate = applies_predicate - @items_and_filters = [] - @applicable_keys = Set.new - @proc_keys = Set.new - @memoized_lookups = Hash.new do |hash, applicable_metadata| - hash[applicable_metadata] = find_items_for(applicable_metadata) + def initialize(applies_predicate) + @applies_predicate = applies_predicate + @items_and_filters = [] end - end - def append(item, metadata) - @items_and_filters << [item, metadata] - handle_mutation(metadata) - end + def append(item, metadata) + @items_and_filters << [item, metadata] + end - def prepend(item, metadata) - @items_and_filters.unshift [item, metadata] - handle_mutation(metadata) - end + def prepend(item, metadata) + @items_and_filters.unshift [item, metadata] + end - def items_for(metadata) - # The filtering of `metadata` to `applicable_metadata` is the key thing - # that makes the memoization actually useful in practice, since each - # example and example group have different metadata (e.g. location and - # description). By filtering to the metadata keys our items care about, - # we can ignore extra metadata keys that differ for each example/group. - # For example, given `config.include DBHelpers, :db`, example groups - # can be split into these two sets: those that are tagged with `:db` and those - # that are not. For each set, this method for the first group in the set is - # still an `O(N)` calculation, but all subsequent groups in the set will be - # constant time lookups when they call this method. - applicable_metadata = applicable_metadata_from(metadata) - - if applicable_metadata.any? { |k, _| @proc_keys.include?(k) } - # It's unsafe to memoize lookups involving procs (since they can - # be non-deterministic), so we skip the memoization in this case. - find_items_for(applicable_metadata) - else - @memoized_lookups[applicable_metadata] + def items_for(request_meta) + @items_and_filters.each_with_object([]) do |(item, item_meta), to_return| + to_return << item if item_meta.empty? || + MetadataFilter.apply?(@applies_predicate, item_meta, request_meta) + end + end + + unless [].respond_to?(:each_with_object) # For 1.8.7 + undef items_for + def items_for(request_meta) + @items_and_filters.inject([]) do |to_return, (item, item_meta)| + to_return << item if item_meta.empty? || + MetadataFilter.apply?(@applies_predicate, item_meta, request_meta) + to_return + end + end end end - private + # This implementation is much more complex, and is optimized for + # rare (or hopefully no) updates once the queries start. Updates + # incur a cost as it has to clear the memoization and keep track + # of applicable keys. Queries will be O(N) the first time an item + # is provided with a given set of applicable metadata; subsequent + # queries with items with the same set of applicable metadata will + # be O(1) due to internal memoization. + # + # This is ideal for use by config, where filterable items (e.g. hooks) + # are typically added at the start of the process (e.g. in `spec_helper`) + # and then repeatedly queried as example groups and examples are defined. + # @private + class QueryOptimized < UpdateOptimized + alias find_items_for items_for + private :find_items_for + + def initialize(applies_predicate) + super + @applicable_keys = Set.new + @proc_keys = Set.new + @memoized_lookups = Hash.new do |hash, applicable_metadata| + hash[applicable_metadata] = find_items_for(applicable_metadata) + end + end - def handle_mutation(metadata) - @applicable_keys.merge(metadata.keys) - @proc_keys.merge(proc_keys_from metadata) - @memoized_lookups.clear - end + def append(item, metadata) + super + handle_mutation(metadata) + end - def applicable_metadata_from(metadata) - @applicable_keys.inject({}) do |hash, key| - hash[key] = metadata[key] if metadata.key?(key) - hash + def prepend(item, metadata) + super + handle_mutation(metadata) end - end - def find_items_for(request_meta) - @items_and_filters.each_with_object([]) do |(item, item_meta), to_return| - to_return << item if item_meta.empty? || - MetadataFilter.apply?(@applies_predicate, item_meta, request_meta) + def items_for(metadata) + # The filtering of `metadata` to `applicable_metadata` is the key thing + # that makes the memoization actually useful in practice, since each + # example and example group have different metadata (e.g. location and + # description). By filtering to the metadata keys our items care about, + # we can ignore extra metadata keys that differ for each example/group. + # For example, given `config.include DBHelpers, :db`, example groups + # can be split into these two sets: those that are tagged with `:db` and those + # that are not. For each set, this method for the first group in the set is + # still an `O(N)` calculation, but all subsequent groups in the set will be + # constant time lookups when they call this method. + applicable_metadata = applicable_metadata_from(metadata) + + if applicable_metadata.any? { |k, _| @proc_keys.include?(k) } + # It's unsafe to memoize lookups involving procs (since they can + # be non-deterministic), so we skip the memoization in this case. + find_items_for(applicable_metadata) + else + @memoized_lookups[applicable_metadata] + end end - end - def proc_keys_from(metadata) - metadata.each_with_object([]) do |(key, value), to_return| - to_return << key if Proc === value + private + + def handle_mutation(metadata) + @applicable_keys.merge(metadata.keys) + @proc_keys.merge(proc_keys_from metadata) + @memoized_lookups.clear end - end - unless [].respond_to?(:each_with_object) # For 1.8.7 - undef find_items_for - def find_items_for(request_meta) - @items_and_filters.inject([]) do |to_return, (item, item_meta)| - to_return << item if item_meta.empty? || - MetadataFilter.apply?(@applies_predicate, item_meta, request_meta) - to_return + def applicable_metadata_from(metadata) + @applicable_keys.inject({}) do |hash, key| + hash[key] = metadata[key] if metadata.key?(key) + hash end end - undef proc_keys_from def proc_keys_from(metadata) - metadata.inject([]) do |to_return, (key, value)| + metadata.each_with_object([]) do |(key, value), to_return| to_return << key if Proc === value - to_return + end + end + + unless [].respond_to?(:each_with_object) # For 1.8.7 + undef proc_keys_from + def proc_keys_from(metadata) + metadata.inject([]) do |to_return, (key, value)| + to_return << key if Proc === value + to_return + end end end end diff --git a/spec/rspec/core/filterable_item_repository_spec.rb b/spec/rspec/core/filterable_item_repository_spec.rb index fb050d1c18..8171a43d1a 100644 --- a/spec/rspec/core/filterable_item_repository_spec.rb +++ b/spec/rspec/core/filterable_item_repository_spec.rb @@ -3,188 +3,200 @@ module Core RSpec.describe FilterableItemRepository, "#items_for" do FilterableItem = Struct.new(:name) - let(:repo) { FilterableItemRepository.new(:any?) } - let(:item_1) { FilterableItem.new("Item 1") } - let(:item_2) { FilterableItem.new("Item 2") } - let(:item_3) { FilterableItem.new("Item 3") } - let(:item_4) { FilterableItem.new("Item 4") } - - context "when the repository is empty" do - it 'returns an empty list' do - expect(repo.items_for(:foo => "bar")).to eq([]) - end - end - - shared_examples_for "adding items to the repository" do |add_method| - describe "adding items using `#{add_method}`" do - define_method :add_item do |*args| - repo.__send__ add_method, *args + def self.it_behaves_like_a_filterable_item_repo(&when_the_repo_has_items_with_metadata) + let(:repo) { described_class.new(:any?) } + let(:item_1) { FilterableItem.new("Item 1") } + let(:item_2) { FilterableItem.new("Item 2") } + let(:item_3) { FilterableItem.new("Item 3") } + let(:item_4) { FilterableItem.new("Item 4") } + + context "when the repository is empty" do + it 'returns an empty list' do + expect(repo.items_for(:foo => "bar")).to eq([]) end + end - context "when the repository has items that have no metadata" do - before do - add_item item_1, {} - add_item item_2, {} + shared_examples_for "adding items to the repository" do |add_method| + describe "adding items using `#{add_method}`" do + define_method :add_item do |*args| + repo.__send__ add_method, *args end - it "returns those items, regardless of the provided argument" do - expect(repo.items_for({})).to contain_exactly(item_1, item_2) - expect(repo.items_for(:foo => "bar")).to contain_exactly(item_1, item_2) - end - end + context "when the repository has items that have no metadata" do + before do + add_item item_1, {} + add_item item_2, {} + end - context "when the repository has items that have metadata" do - before do - add_item item_1, :foo => "bar" - add_item item_2, :slow => true - add_item item_3, :foo => "bar" + it "returns those items, regardless of the provided argument" do + expect(repo.items_for({})).to contain_exactly(item_1, item_2) + expect(repo.items_for(:foo => "bar")).to contain_exactly(item_1, item_2) + end end - it 'return an empty list when given empty metadata' do - expect(repo.items_for({})).to eq([]) - end + context "when the repository has items that have metadata" do + before do + add_item item_1, :foo => "bar" + add_item item_2, :slow => true + add_item item_3, :foo => "bar" + end - it 'return an empty list when given metadata that matches no items' do - expect(repo.items_for(:slow => false, :foo => "bazz")).to eq([]) - end + it 'return an empty list when given empty metadata' do + expect(repo.items_for({})).to eq([]) + end - it 'returns matching items for the provided metadata' do - expect(repo.items_for(:slow => true)).to contain_exactly(item_2) - expect(repo.items_for(:foo => "bar")).to contain_exactly(item_1, item_3) - expect(repo.items_for(:slow => true, :foo => "bar")).to contain_exactly(item_1, item_2, item_3) - end + it 'return an empty list when given metadata that matches no items' do + expect(repo.items_for(:slow => false, :foo => "bazz")).to eq([]) + end - it 'returns the matching items in the correct order' do - expect(repo.items_for(:slow => true, :foo => "bar")).to eq items_in_expected_order - end + it 'returns matching items for the provided metadata' do + expect(repo.items_for(:slow => true)).to contain_exactly(item_2) + expect(repo.items_for(:foo => "bar")).to contain_exactly(item_1, item_3) + expect(repo.items_for(:slow => true, :foo => "bar")).to contain_exactly(item_1, item_2, item_3) + end - it 'ignores other metadata keys that are not related to the appended items' do - expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) - end + it 'returns the matching items in the correct order' do + expect(repo.items_for(:slow => true, :foo => "bar")).to eq items_in_expected_order + end - it 'differentiates between an applicable key being missing and having an explicit `nil` value' do - add_item item_4, :bar => nil + it 'ignores other metadata keys that are not related to the appended items' do + expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) + end - expect(repo.items_for({})).to eq([]) - expect(repo.items_for(:bar => nil)).to contain_exactly(item_4) - end + it 'differentiates between an applicable key being missing and having an explicit `nil` value' do + add_item item_4, :bar => nil - it 'returns the correct items when they are appended after a memoized lookup' do - expect { - add_item item_4, :slow => true - }.to change { repo.items_for(:slow => true) }. - from(a_collection_containing_exactly(item_2)). - to(a_collection_containing_exactly(item_2, item_4)) - end + expect(repo.items_for({})).to eq([]) + expect(repo.items_for(:bar => nil)).to contain_exactly(item_4) + end - let(:flip_proc) do - return_val = true - Proc.new { return_val.tap { |v| return_val = !v } } - end + it 'returns the correct items when they are appended after a memoized lookup' do + expect { + add_item item_4, :slow => true + }.to change { repo.items_for(:slow => true) }. + from(a_collection_containing_exactly(item_2)). + to(a_collection_containing_exactly(item_2, item_4)) + end - context "with proc values" do - before do - add_item item_4, { :include_it => flip_proc } + let(:flip_proc) do + return_val = true + Proc.new { return_val.tap { |v| return_val = !v } } end - it 'evaluates the proc each time since the logic can return a different value each time' do - expect(repo.items_for(:include_it => nil)).to contain_exactly(item_4) - expect(repo.items_for(:include_it => nil)).to eq([]) - expect(repo.items_for(:include_it => nil)).to contain_exactly(item_4) - expect(repo.items_for(:include_it => nil)).to eq([]) + context "with proc values" do + before do + add_item item_4, { :include_it => flip_proc } + end + + it 'evaluates the proc each time since the logic can return a different value each time' do + expect(repo.items_for(:include_it => nil)).to contain_exactly(item_4) + expect(repo.items_for(:include_it => nil)).to eq([]) + expect(repo.items_for(:include_it => nil)).to contain_exactly(item_4) + expect(repo.items_for(:include_it => nil)).to eq([]) + end end - end - context "when initialized with the `:any?` predicate" do - let(:repo) { FilterableItemRepository.new(:any?) } + context "when initialized with the `:any?` predicate" do + let(:repo) { FilterableItemRepository::QueryOptimized.new(:any?) } - it 'matches against multi-entry items when any of the metadata entries match' do - add_item item_4, :key_1 => "val_1", :key_2 => "val_2" + it 'matches against multi-entry items when any of the metadata entries match' do + add_item item_4, :key_1 => "val_1", :key_2 => "val_2" - expect(repo.items_for(:key_1 => "val_1")).to contain_exactly(item_4) - expect(repo.items_for(:key_2 => "val_2")).to contain_exactly(item_4) - expect(repo.items_for(:key_1 => "val_1", :key_2 => "val_2")).to contain_exactly(item_4) + expect(repo.items_for(:key_1 => "val_1")).to contain_exactly(item_4) + expect(repo.items_for(:key_2 => "val_2")).to contain_exactly(item_4) + expect(repo.items_for(:key_1 => "val_1", :key_2 => "val_2")).to contain_exactly(item_4) + end end - end - context "when initialized with the `:all?` predicate" do - let(:repo) { FilterableItemRepository.new(:all?) } + context "when initialized with the `:all?` predicate" do + let(:repo) { FilterableItemRepository::QueryOptimized.new(:all?) } - it 'matches against multi-entry items when all of the metadata entries match' do - add_item item_4, :key_1 => "val_1", :key_2 => "val_2" + it 'matches against multi-entry items when all of the metadata entries match' do + add_item item_4, :key_1 => "val_1", :key_2 => "val_2" - expect(repo.items_for(:key_1 => "val_1")).to eq([]) - expect(repo.items_for(:key_2 => "val_2")).to eq([]) - expect(repo.items_for(:key_1 => "val_1", :key_2 => "val_2")).to contain_exactly(item_4) + expect(repo.items_for(:key_1 => "val_1")).to eq([]) + expect(repo.items_for(:key_2 => "val_2")).to eq([]) + expect(repo.items_for(:key_1 => "val_1", :key_2 => "val_2")).to contain_exactly(item_4) + end end + + module_eval(&when_the_repo_has_items_with_metadata) if when_the_repo_has_items_with_metadata end + end + end - describe "performance optimization" do - # NOTE: the specs in this context are potentially brittle because they are - # coupled to the implementation's usage of `MetadataFilter.apply?`. However, - # they demonstrate the perf optimization that was the reason we created - # this class, and thus have value in demonstrating the memoization is working - # properly and in documenting the reason the class exists in the first place. - # Still, if these prove to be brittle in the future, feel free to delete them since - # they are not concerned with externally visible behaviors. - - it 'is optimized to check metadata filter application for a given pair of metadata hashes only once' do - # TODO: use mock expectations for this once https://github.com/rspec/rspec-mocks/pull/841 is fixed. - call_counts = track_metadata_filter_apply_calls + it_behaves_like "adding items to the repository", :append do + let(:items_in_expected_order) { [item_1, item_2, item_3] } + end - 3.times do - expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) - end + it_behaves_like "adding items to the repository", :prepend do + let(:items_in_expected_order) { [item_3, item_2, item_1] } + end + end - expect(call_counts[:slow => true]).to eq(1) + describe FilterableItemRepository::UpdateOptimized do + it_behaves_like_a_filterable_item_repo + end + + describe FilterableItemRepository::QueryOptimized do + it_behaves_like_a_filterable_item_repo do + describe "performance optimization" do + # NOTE: the specs in this context are potentially brittle because they are + # coupled to the implementation's usage of `MetadataFilter.apply?`. However, + # they demonstrate the perf optimization that was the reason we created + # this class, and thus have value in demonstrating the memoization is working + # properly and in documenting the reason the class exists in the first place. + # Still, if these prove to be brittle in the future, feel free to delete them since + # they are not concerned with externally visible behaviors. + + it 'is optimized to check metadata filter application for a given pair of metadata hashes only once' do + # TODO: use mock expectations for this once https://github.com/rspec/rspec-mocks/pull/841 is fixed. + call_counts = track_metadata_filter_apply_calls + + 3.times do + expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) end - it 'ignores extraneous metadata keys when doing memoized lookups' do - # TODO: use mock expectations for this once https://github.com/rspec/rspec-mocks/pull/841 is fixed. - call_counts = track_metadata_filter_apply_calls + expect(call_counts[:slow => true]).to eq(1) + end - expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) - expect(repo.items_for(:slow => true, :other => "bar")).to contain_exactly(item_2) - expect(repo.items_for(:slow => true, :goo => "bazz")).to contain_exactly(item_2) + it 'ignores extraneous metadata keys when doing memoized lookups' do + # TODO: use mock expectations for this once https://github.com/rspec/rspec-mocks/pull/841 is fixed. + call_counts = track_metadata_filter_apply_calls - expect(call_counts[:slow => true]).to eq(1) - end + expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) + expect(repo.items_for(:slow => true, :other => "bar")).to contain_exactly(item_2) + expect(repo.items_for(:slow => true, :goo => "bazz")).to contain_exactly(item_2) - context "when there are some proc keys" do - before do - add_item item_4, { :include_it => flip_proc } - end + expect(call_counts[:slow => true]).to eq(1) + end - it 'still performs memoization for metadata hashes that lack those keys' do - call_counts = track_metadata_filter_apply_calls + context "when there are some proc keys" do + before do + add_item item_4, { :include_it => flip_proc } + end - expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) - expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) + it 'still performs memoization for metadata hashes that lack those keys' do + call_counts = track_metadata_filter_apply_calls - expect(call_counts[:slow => true]).to eq(1) - end + expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) + expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) + + expect(call_counts[:slow => true]).to eq(1) end + end - def track_metadata_filter_apply_calls - Hash.new(0).tap do |call_counts| - allow(MetadataFilter).to receive(:apply?).and_wrap_original do |original, predicate, item_meta, request_meta| - call_counts[item_meta] += 1 - original.call(predicate, item_meta, request_meta) - end + def track_metadata_filter_apply_calls + Hash.new(0).tap do |call_counts| + allow(MetadataFilter).to receive(:apply?).and_wrap_original do |original, predicate, item_meta, request_meta| + call_counts[item_meta] += 1 + original.call(predicate, item_meta, request_meta) end end end end end end - - it_behaves_like "adding items to the repository", :append do - let(:items_in_expected_order) { [item_1, item_2, item_3] } - end - - it_behaves_like "adding items to the repository", :prepend do - let(:items_in_expected_order) { [item_3, item_2, item_1] } - end end end end From 580bc76d7726418e35f918ac1844b9f02a006e31 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 12 Jan 2015 21:20:35 -0800 Subject: [PATCH 259/275] Use optimized filterable item repository in the appropriate places. - Config should use the QueryOptimized one because config usually sets up global hooks, inclusions, etc once at the start of the process (e.g. in `spec_helper`) and then is repeatedly queried as each example or group is defined. - Examples and groups should use the UpdateOptimized one as they are not queried by other examples or groups the way config is; instead, they can be updated multiple times based on having multiple global config hooks to process. Cutting out the additional update processing done by the QueryOptimized implementation greatly improves the performance of the `with_config_hooks` benchmark. --- .../with_config_hooks.rb | 16 +++++------ lib/rspec/core/configuration.rb | 8 ++++++ lib/rspec/core/hooks.rb | 27 ++++++++++--------- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/benchmarks/singleton_example_groups/with_config_hooks.rb b/benchmarks/singleton_example_groups/with_config_hooks.rb index e988b1ecae..cf3bd3ac1c 100644 --- a/benchmarks/singleton_example_groups/with_config_hooks.rb +++ b/benchmarks/singleton_example_groups/with_config_hooks.rb @@ -12,18 +12,18 @@ __END__ No match -- without singleton group support - 489.165 (±33.3%) i/s - 2.080k + 614.535 (±33.8%) i/s - 2.520k No match -- with singleton group support - 451.019 (±29.3%) i/s - 1.890k + 555.190 (±21.1%) i/s - 2.496k Example match -- without singleton group support - 465.178 (±35.3%) i/s - 1.820k + 574.821 (±31.5%) i/s - 2.491k Example match -- with singleton group support - 244.273 (±23.3%) i/s - 1.064k + 436.391 (±25.2%) i/s - 1.872k Group match -- without singleton group support - 406.979 (±27.0%) i/s - 1.700k + 544.063 (±31.4%) i/s - 2.112k Group match -- with singleton group support - 327.455 (±22.6%) i/s - 1.421k + 457.098 (±18.8%) i/s - 1.961k Both match -- without singleton group support - 423.859 (±32.1%) i/s - 1.763k + 554.004 (±30.1%) i/s - 2.255k Both match -- with singleton group support - 293.873 (±23.5%) i/s - 1.333k + 452.834 (±19.7%) i/s - 1.935k diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index 01e1f1fd37..a2a03d0481 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1525,6 +1525,14 @@ def with_suite_hooks end end + # @private + # Holds the various registered hooks. Here we use a FilterableItemRepository + # implementation that is specifically optimized for the read/write patterns + # of the config object. + def hooks + @hooks ||= HookCollections.new(self, FilterableItemRepository::QueryOptimized) + end + private def handle_suite_hook(args, collection, append_or_prepend, hook_type, block) diff --git a/lib/rspec/core/hooks.rb b/lib/rspec/core/hooks.rb index decfb0a4f9..9c5601777c 100644 --- a/lib/rspec/core/hooks.rb +++ b/lib/rspec/core/hooks.rb @@ -336,7 +336,7 @@ def around(*args, &block) # @private # Holds the various registered hooks. def hooks - @hooks ||= HookCollections.new(self) + @hooks ||= HookCollections.new(self, FilterableItemRepository::UpdateOptimized) end private @@ -413,13 +413,14 @@ def hook_description # API, so that callers _tell_ this class what to do with the hooks, rather than # asking this class for a list of hooks, and then doing something with them. class HookCollections - def initialize(owner) - @owner = owner - @before_example_hooks = nil - @after_example_hooks = nil - @before_context_hooks = nil - @after_context_hooks = nil - @around_example_hooks = nil + def initialize(owner, filterable_item_repo_class) + @owner = owner + @filterable_item_repo_class = filterable_item_repo_class + @before_example_hooks = nil + @after_example_hooks = nil + @before_context_hooks = nil + @after_context_hooks = nil + @around_example_hooks = nil end def register_globals(host, globals) @@ -540,18 +541,18 @@ def hooks_for(position, scope) def ensure_hooks_initialized_for(position, scope) if position == :before if scope == :example - @before_example_hooks ||= FilterableItemRepository::QueryOptimized.new(:all?) + @before_example_hooks ||= @filterable_item_repo_class.new(:all?) else - @before_context_hooks ||= FilterableItemRepository::QueryOptimized.new(:all?) + @before_context_hooks ||= @filterable_item_repo_class.new(:all?) end elsif position == :after if scope == :example - @after_example_hooks ||= FilterableItemRepository::QueryOptimized.new(:all?) + @after_example_hooks ||= @filterable_item_repo_class.new(:all?) else - @after_context_hooks ||= FilterableItemRepository::QueryOptimized.new(:all?) + @after_context_hooks ||= @filterable_item_repo_class.new(:all?) end else # around - @around_example_hooks ||= FilterableItemRepository::QueryOptimized.new(:all?) + @around_example_hooks ||= @filterable_item_repo_class.new(:all?) end end From a1a3dc411a15317b08222d4d97e9d0e71b4bd832 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Wed, 14 Jan 2015 10:00:16 -0800 Subject: [PATCH 260/275] Improve perf of metadata-based shared group inclusion. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The call to `RSpec::CallerFilter.first_non_rspec_line` took most of the time. I originally planned to leverage rspec/rspec-support#155 but realized we could avoid calling that entirely by re-using the location from the group’s metadata. I re-ran all the benchmarks and updated them. They look much, much better. --- .../with_config_hooks.rb | 17 ++++++++--------- ...inclusions_and_shared_context_inclusions.rb | 16 ++++++++-------- .../with_module_inclusions.rb | 16 ++++++++-------- .../with_no_config_hooks_or_inclusions.rb | 16 ++++++++-------- .../with_shared_context_inclusions.rb | 16 ++++++++-------- lib/rspec/core/configuration.rb | 8 ++++++-- lib/rspec/core/example_group.rb | 18 ++++++++++++++++-- lib/rspec/core/shared_example_group.rb | 3 ++- spec/rspec/core/metadata_spec.rb | 8 ++++---- 9 files changed, 68 insertions(+), 50 deletions(-) diff --git a/benchmarks/singleton_example_groups/with_config_hooks.rb b/benchmarks/singleton_example_groups/with_config_hooks.rb index cf3bd3ac1c..08f49265fd 100644 --- a/benchmarks/singleton_example_groups/with_config_hooks.rb +++ b/benchmarks/singleton_example_groups/with_config_hooks.rb @@ -10,20 +10,19 @@ BenchmarkHelpers.run_benchmarks __END__ - No match -- without singleton group support - 614.535 (±33.8%) i/s - 2.520k + 575.250 (±29.0%) i/s - 2.484k No match -- with singleton group support - 555.190 (±21.1%) i/s - 2.496k + 503.671 (±21.8%) i/s - 2.250k Example match -- without singleton group support - 574.821 (±31.5%) i/s - 2.491k + 544.191 (±25.7%) i/s - 2.160k Example match -- with singleton group support - 436.391 (±25.2%) i/s - 1.872k + 413.538 (±22.2%) i/s - 1.715k Group match -- without singleton group support - 544.063 (±31.4%) i/s - 2.112k + 517.998 (±28.2%) i/s - 2.058k Group match -- with singleton group support - 457.098 (±18.8%) i/s - 1.961k + 431.554 (±15.3%) i/s - 1.960k Both match -- without singleton group support - 554.004 (±30.1%) i/s - 2.255k + 525.306 (±25.1%) i/s - 2.107k in 5.556760s Both match -- with singleton group support - 452.834 (±19.7%) i/s - 1.935k + 440.288 (±16.6%) i/s - 1.848k diff --git a/benchmarks/singleton_example_groups/with_config_hooks_module_inclusions_and_shared_context_inclusions.rb b/benchmarks/singleton_example_groups/with_config_hooks_module_inclusions_and_shared_context_inclusions.rb index b76751d8ec..9eee773b14 100644 --- a/benchmarks/singleton_example_groups/with_config_hooks_module_inclusions_and_shared_context_inclusions.rb +++ b/benchmarks/singleton_example_groups/with_config_hooks_module_inclusions_and_shared_context_inclusions.rb @@ -18,18 +18,18 @@ __END__ No match -- without singleton group support - 452.015 (±33.8%) i/s - 1.900k + 544.396 (±34.0%) i/s - 2.340k No match -- with singleton group support - 464.520 (±31.0%) i/s - 1.887k + 451.635 (±31.0%) i/s - 1.935k Example match -- without singleton group support - 476.961 (±34.6%) i/s - 1.978k in 5.340615s + 538.788 (±23.8%) i/s - 2.450k Example match -- with singleton group support - 76.177 (±34.1%) i/s - 266.000 + 342.990 (±22.4%) i/s - 1.440k Group match -- without singleton group support - 364.554 (±28.3%) i/s - 1.372k + 509.969 (±26.7%) i/s - 2.070k Group match -- with singleton group support - 281.761 (±24.1%) i/s - 1.200k + 405.284 (±20.5%) i/s - 1.518k Both match -- without singleton group support - 281.521 (±27.4%) i/s - 1.188k + 513.344 (±24.0%) i/s - 1.927k Both match -- with singleton group support - 297.886 (±18.1%) i/s - 1.288k + 406.111 (±18.5%) i/s - 1.760k diff --git a/benchmarks/singleton_example_groups/with_module_inclusions.rb b/benchmarks/singleton_example_groups/with_module_inclusions.rb index 8d133c6e0d..e3a8c43fe2 100644 --- a/benchmarks/singleton_example_groups/with_module_inclusions.rb +++ b/benchmarks/singleton_example_groups/with_module_inclusions.rb @@ -11,18 +11,18 @@ __END__ No match -- without singleton group support - 519.880 (±33.9%) i/s - 2.162k + 555.498 (±27.0%) i/s - 2.496k No match -- with singleton group support - 481.334 (±28.5%) i/s - 2.028k + 529.826 (±23.0%) i/s - 2.397k in 5.402305s Example match -- without singleton group support - 491.348 (±29.9%) i/s - 2.068k + 541.845 (±29.0%) i/s - 2.208k Example match -- with singleton group support - 407.257 (±22.3%) i/s - 1.782k + 465.440 (±20.4%) i/s - 2.091k Group match -- without singleton group support - 483.403 (±36.4%) i/s - 1.815k + 530.976 (±24.1%) i/s - 2.303k Group match -- with singleton group support - 424.932 (±29.4%) i/s - 1.804k + 505.291 (±18.8%) i/s - 2.226k Both match -- without singleton group support - 397.831 (±31.9%) i/s - 1.720k + 542.168 (±28.4%) i/s - 2.067k in 5.414905s Both match -- with singleton group support - 424.233 (±25.5%) i/s - 1.720k + 503.226 (±27.2%) i/s - 1.880k in 5.621210s diff --git a/benchmarks/singleton_example_groups/with_no_config_hooks_or_inclusions.rb b/benchmarks/singleton_example_groups/with_no_config_hooks_or_inclusions.rb index 78529f3abb..ec7849d2b1 100644 --- a/benchmarks/singleton_example_groups/with_no_config_hooks_or_inclusions.rb +++ b/benchmarks/singleton_example_groups/with_no_config_hooks_or_inclusions.rb @@ -5,18 +5,18 @@ __END__ No match -- without singleton group support - 504.865 (±31.9%) i/s - 2.128k + 565.198 (±28.8%) i/s - 2.438k No match -- with singleton group support - 463.115 (±26.6%) i/s - 1.998k + 539.781 (±18.9%) i/s - 2.496k Example match -- without singleton group support - 472.825 (±31.9%) i/s - 1.938k + 539.287 (±28.2%) i/s - 2.450k in 5.555471s Example match -- with singleton group support - 436.539 (±33.9%) i/s - 1.840k + 511.576 (±28.1%) i/s - 2.058k Group match -- without singleton group support - 460.643 (±33.4%) i/s - 1.892k + 535.298 (±23.2%) i/s - 2.352k Group match -- with singleton group support - 430.339 (±23.2%) i/s - 1.911k + 539.454 (±19.1%) i/s - 2.350k Both match -- without singleton group support - 406.712 (±26.6%) i/s - 1.848k + 550.932 (±32.1%) i/s - 2.145k in 5.930432s Both match -- with singleton group support - 470.299 (±26.4%) i/s - 1.890k + 540.183 (±19.6%) i/s - 2.300k diff --git a/benchmarks/singleton_example_groups/with_shared_context_inclusions.rb b/benchmarks/singleton_example_groups/with_shared_context_inclusions.rb index 28ab30e365..2d99ec6d2a 100644 --- a/benchmarks/singleton_example_groups/with_shared_context_inclusions.rb +++ b/benchmarks/singleton_example_groups/with_shared_context_inclusions.rb @@ -11,18 +11,18 @@ __END__ No match -- without singleton group support - 503.700 (±33.2%) i/s - 2.184k + 563.304 (±29.6%) i/s - 2.385k No match -- with singleton group support - 471.018 (±26.8%) i/s - 2.009k + 538.738 (±22.3%) i/s - 2.209k Example match -- without singleton group support - 467.859 (±34.8%) i/s - 2.021k in 5.600106s + 546.605 (±25.6%) i/s - 2.450k Example match -- with singleton group support - 84.138 (±34.5%) i/s - 296.000 in 5.515586s + 421.111 (±23.5%) i/s - 1.845k Group match -- without singleton group support - 384.144 (±27.9%) i/s - 1.560k + 536.267 (±27.4%) i/s - 2.050k Group match -- with singleton group support - 349.301 (±27.5%) i/s - 1.288k + 508.644 (±17.7%) i/s - 2.268k Both match -- without singleton group support - 388.100 (±25.8%) i/s - 1.702k + 538.047 (±27.7%) i/s - 2.067k in 5.431649s Both match -- with singleton group support - 339.310 (±20.3%) i/s - 1.504k + 505.388 (±26.7%) i/s - 1.880k in 5.578614s diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index a2a03d0481..f5dd8f9543 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -1158,8 +1158,12 @@ def configure_group_with(group, module_list, application_method) # Used internally to extend the singleton class of a single example's # example group instance with modules using `include` and/or `extend`. def configure_example(example) - @include_modules.items_for(example.metadata).each do |mod| - safe_include(mod, example.example_group_instance.singleton_class) + # We replace the metadata so that SharedExampleGroupModule#included + # has access to the example's metadata[:location]. + example.example_group_instance.singleton_class.with_replaced_metadata(example.metadata) do + @include_modules.items_for(example.metadata).each do |mod| + safe_include(mod, example.example_group_instance.singleton_class) + end end end diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index ffe4f0c49f..184ee946da 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -45,7 +45,21 @@ def self.idempotently_define_singleton_method(name, &definition) # The [Metadata](Metadata) object associated with this group. # @see Metadata def self.metadata - @metadata if defined?(@metadata) + @metadata ||= nil + end + + # Temporarily replace the provided metadata. + # Intended primarily to allow an example group's singleton class + # to return the metadata of the example that it exists for. This + # is necessary for shared example group inclusion to work properly + # with singleton example groups. + # @private + def self.with_replaced_metadata(meta) + orig_metadata = metadata + @metadata = meta + yield + ensure + @metadata = orig_metadata end # @private @@ -333,7 +347,7 @@ def self.find_and_eval_shared(label, name, inclusion_location, *args, &customiza raise ArgumentError, "Could not find shared #{label} #{name.inspect}" end - SharedExampleGroupInclusionStackFrame.with_frame(name, inclusion_location) do + SharedExampleGroupInclusionStackFrame.with_frame(name, Metadata.relative_path(inclusion_location)) do module_exec(*args, &shared_block) module_exec(&customization_block) if customization_block end diff --git a/lib/rspec/core/shared_example_group.rb b/lib/rspec/core/shared_example_group.rb index fcffb853e3..4af32607cb 100644 --- a/lib/rspec/core/shared_example_group.rb +++ b/lib/rspec/core/shared_example_group.rb @@ -20,7 +20,8 @@ def inspect # Our definition evaluates the shared group block in the context of the # including example group. def included(klass) - SharedExampleGroupInclusionStackFrame.with_frame(@description, RSpec::CallerFilter.first_non_rspec_line) do + inclusion_line = klass.metadata[:location] + SharedExampleGroupInclusionStackFrame.with_frame(@description, inclusion_line) do klass.class_exec(&@definition) end end diff --git a/spec/rspec/core/metadata_spec.rb b/spec/rspec/core/metadata_spec.rb index f454e3eb9b..5203072dd9 100644 --- a/spec/rspec/core/metadata_spec.rb +++ b/spec/rspec/core/metadata_spec.rb @@ -189,7 +189,7 @@ def metadata_for(*args) expect(meta[:shared_group_inclusion_backtrace]).to match [ an_object_having_attributes( :shared_group_name => "some shared behavior", - :inclusion_location => a_string_including("#{__FILE__}:#{line}") + :inclusion_location => a_string_including("#{Metadata.relative_path __FILE__}:#{line}") ) ] end end @@ -213,7 +213,7 @@ def metadata_for(*args) expect(meta[:shared_group_inclusion_backtrace]).to match [ an_object_having_attributes( :shared_group_name => "some shared behavior", - :inclusion_location => a_string_including("#{__FILE__}:#{line}") + :inclusion_location => a_string_including("#{Metadata.relative_path __FILE__}:#{line}") ) ] end end @@ -239,11 +239,11 @@ def metadata_for(*args) expect(meta[:shared_group_inclusion_backtrace]).to match [ an_object_having_attributes( :shared_group_name => "inner", - :inclusion_location => a_string_including("#{__FILE__}:#{inner_line}") + :inclusion_location => a_string_including("#{Metadata.relative_path __FILE__}:#{inner_line}") ), an_object_having_attributes( :shared_group_name => "outer", - :inclusion_location => a_string_including("#{__FILE__}:#{outer_line}") + :inclusion_location => a_string_including("#{Metadata.relative_path __FILE__}:#{outer_line}") ), ] end From cd32c7c917b7ef2ae85f5e36d1f81b85e63ef0b2 Mon Sep 17 00:00:00 2001 From: Cezary Baginski Date: Wed, 14 Jan 2015 20:44:46 +0100 Subject: [PATCH 261/275] docs: emphasize gem backtrace exclusion patterns --- .../backtrace_exclusion_patterns.feature | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/features/configuration/backtrace_exclusion_patterns.feature b/features/configuration/backtrace_exclusion_patterns.feature index 95fee755b2..4a87469d0d 100644 --- a/features/configuration/backtrace_exclusion_patterns.feature +++ b/features/configuration/backtrace_exclusion_patterns.feature @@ -1,7 +1,21 @@ Feature: Excluding lines from the backtrace - To reduce the noise when diagnosing failures, RSpec excludes matching lines - from backtraces. The default exclusion patterns are: + To reduce the noise when diagnosing failures, RSpec can exclude lines belonging to certain gems or matching given patterns. + + If you want to filter out backtrace lines belonging to specific gems, you can use `config.filter_gems_from_backtrace` like so: + + ```ruby + config.filter_gems_from_backtrace "ignored_gem", "another_ignored_gem", + ``` + + For more control over which lines to ignore, you can use the the `backtrace_exclusion_patterns` option to either replace the default exclusion patterns, or append your own, e.g. + + ```ruby + config.backtrace_exclusion_patterns = [/first pattern/, /second pattern/] + config.backtrace_exclusion_patterns << /another pattern/ + ``` + + The default exclusion patterns are: ```ruby /\/lib\d*\/ruby\//, @@ -10,12 +24,8 @@ Feature: Excluding lines from the backtrace /lib\/rspec\/(core|expectations|matchers|mocks)/ ``` - This list can be modified or replaced with the `backtrace_exclusion_patterns` - option. Additionally, `rspec` can be run with the `--backtrace` option to skip - backtrace cleaning entirely. + Additionally, `rspec` can be run with the `--backtrace` option to skip backtrace cleaning entirely. - In addition, if you want to filter out backtrace lines from specific gems, you - can use `config.filter_gems_from_backtrace`. Scenario: Using default `backtrace_exclusion_patterns` Given a file named "spec/failing_spec.rb" with: From 70bb6b9d8ada16812dd129bffed17eab335f9dcb Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 16 Jan 2015 09:04:03 -0800 Subject: [PATCH 262/275] =?UTF-8?q?Stop=20using=20aruba=E2=80=99s=20remove?= =?UTF-8?q?d=20`regexp`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/cucumber/aruba/issues/227 for background. --- features/step_definitions/additional_cli_steps.rb | 6 +++--- rspec-core.gemspec | 6 +----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/features/step_definitions/additional_cli_steps.rb b/features/step_definitions/additional_cli_steps.rb index dc792608dd..5ecfcadea7 100644 --- a/features/step_definitions/additional_cli_steps.rb +++ b/features/step_definitions/additional_cli_steps.rb @@ -8,13 +8,13 @@ Then /^the output should not contain any of these:$/ do |table| table.raw.flatten.each do |string| - expect(all_output).not_to match(regexp(string)) + expect(all_output).not_to include(string) end end Then /^the output should contain one of the following:$/ do |table| matching_output = table.raw.flatten.select do |string| - all_output =~ regexp(string) + all_output.include?(string) end expect(matching_output.count).to eq(1) @@ -66,7 +66,7 @@ line =~ /(^\s+# [^:]+:\d+)/ ? $1 : line # http://rubular.com/r/zDD7DdWyzF end.join("\n") - expect(normalized_output).to match(regexp(partial_output)) + expect(normalized_output).to include(partial_output) end Then /^the output should not contain any error backtraces$/ do diff --git a/rspec-core.gemspec b/rspec-core.gemspec index 907f14836a..301dba5b3f 100644 --- a/rspec-core.gemspec +++ b/rspec-core.gemspec @@ -42,11 +42,7 @@ Gem::Specification.new do |s| s.add_development_dependency "rake", "~> 10.0.0" s.add_development_dependency "cucumber", "~> 1.3" s.add_development_dependency "minitest", "~> 5.3" - - # Aruba 0.6.2 removed an API we rely on. For now we are excluding - # that version until we find out if they will restore it. See: - # https://github.com/cucumber/aruba/commit/5b2c7b445bc80083e577b793e4411887ef295660#commitcomment-9284628 - s.add_development_dependency "aruba", "~> 0.5", "!= 0.6.2" + s.add_development_dependency "aruba", "~> 0.6" s.add_development_dependency "nokogiri", (RUBY_VERSION < '1.9.3' ? "1.5.2" : "~> 1.5") s.add_development_dependency "coderay", "~> 1.0.9" From 3b944b54c3b11aefd541f30a25d77afe939afe4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Mon, 12 Jan 2015 14:51:04 +0100 Subject: [PATCH 263/275] Escape rspec path otherwise things like bundle exec rake spec break in repos containing spaces in directory names --- lib/rspec/core/rake_task.rb | 2 +- spec/rspec/core/rake_task_spec.rb | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/rspec/core/rake_task.rb b/lib/rspec/core/rake_task.rb index 4d6ea9083e..baf1c441d6 100644 --- a/lib/rspec/core/rake_task.rb +++ b/lib/rspec/core/rake_task.rb @@ -147,7 +147,7 @@ def spec_command cmd_parts << RUBY cmd_parts << ruby_opts cmd_parts << rspec_load_path - cmd_parts << rspec_path + cmd_parts << escape(rspec_path) cmd_parts << file_inclusion_specification cmd_parts << file_exclusion_specification cmd_parts << rspec_opts diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index 71876e963a..772dea4162 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -36,14 +36,21 @@ def spec_command context "default" do it "renders rspec" do - expect(spec_command).to match(/^#{ruby} #{default_load_path_opts} #{task.rspec_path}/) + expect(spec_command).to match(/^#{ruby} #{default_load_path_opts} '?#{task.rspec_path}'?/) + end + end + + context "with space", :unless => RSpec::Support::OS.windows? do + it "renders rspec with space escaped" do + task.rspec_path = '/path with space/exe/rspec' + expect(spec_command).to match(/^#{ruby} #{default_load_path_opts} \/path\\ with\\ space\/exe\/rspec/) end end context "with ruby options" do it "renders them before the rspec path" do task.ruby_opts = "-w" - expect(spec_command).to match(/^#{ruby} -w #{default_load_path_opts} #{task.rspec_path}/) + expect(spec_command).to match(/^#{ruby} -w #{default_load_path_opts} '?#{task.rspec_path}'?/) end end From e5f20d2b5975fb815251b86970073ab5873471dd Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Sat, 17 Jan 2015 21:26:43 -0800 Subject: [PATCH 264/275] Changelog for #1845. [ci skip] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 79c0f3501b..fdfc4e6c21 100644 --- a/Changelog.md +++ b/Changelog.md @@ -64,6 +64,8 @@ Bug Fixes: * When a pending example fails (as expected) due to a mock expectation, set `RSpec::Core::Example::ExecutionResult#pending_exception` -- previously it was not being set but should have been. (Myron Marston, #1844) +* Fix rake task to work when `rspec-core` is installed in a directory + containing a space. (Guido Günther, #1845) ### 3.1.8 Development From d7f5e3e7f379848682b37eda39f9d850ba6548bd Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 22 Jan 2015 11:03:31 +0100 Subject: [PATCH 265/275] Allow Regexp class to be described --- lib/rspec/core/example_group.rb | 2 +- spec/integration/describe_spec.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 spec/integration/describe_spec.rb diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index 184ee946da..1738de33a9 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -729,7 +729,7 @@ def self.base_name_for(group) # Convert to CamelCase. name = ' ' << group.description name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) do - match = Regexp.last_match[1] + match = ::Regexp.last_match[1] match.upcase! match end diff --git a/spec/integration/describe_spec.rb b/spec/integration/describe_spec.rb new file mode 100644 index 0000000000..67d8994675 --- /dev/null +++ b/spec/integration/describe_spec.rb @@ -0,0 +1,6 @@ +RSpec.describe Regexp do + + it 'sets the core class as the described class' do + expect(described_class).to eq(Regexp) + end +end From b1f52e4ff0588fc73cb7dadcbdbdd6f498f911b7 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Fri, 23 Jan 2015 10:33:30 -0800 Subject: [PATCH 266/275] It should be `flatten(1)`. We changed it to `flatten(1)` in cd52601a7fe0ea4c3f4f3f290f4336921afa11f7 but then accidentally changed it to `flatten(2)` in 940b7abda7e3a02ddd4d0ba44c4f9c8b16761b35. Not sure how... --- lib/rspec/core/flat_map.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/core/flat_map.rb b/lib/rspec/core/flat_map.rb index 6e5e35385f..71093ac832 100644 --- a/lib/rspec/core/flat_map.rb +++ b/lib/rspec/core/flat_map.rb @@ -8,7 +8,7 @@ def flat_map(array, &block) end else # for 1.8.7 def flat_map(array, &block) - array.map(&block).flatten(2) + array.map(&block).flatten(1) end end From dcfb37b58380c3491e3ed99bae49264fa2008eb0 Mon Sep 17 00:00:00 2001 From: Fumiaki MATSUSHIMA Date: Sun, 25 Jan 2015 23:55:34 +0900 Subject: [PATCH 267/275] accept semicolon separated -I --- lib/rspec/core/option_parser.rb | 4 ++-- spec/rspec/core/option_parser_spec.rb | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/rspec/core/option_parser.rb b/lib/rspec/core/option_parser.rb index 1a10c8d458..8ee17163c1 100644 --- a/lib/rspec/core/option_parser.rb +++ b/lib/rspec/core/option_parser.rb @@ -25,9 +25,9 @@ def parser(options) OptionParser.new do |parser| parser.banner = "Usage: rspec [options] [files or directories]\n\n" - parser.on('-I PATH', 'Specify PATH to add to $LOAD_PATH (may be used more than once).') do |dir| + parser.on('-I PATH', 'Specify PATH to add to $LOAD_PATH (may be used more than once).') do |dirs| options[:libs] ||= [] - options[:libs] << dir + options[:libs].concat(dirs.split(File::PATH_SEPARATOR)) end parser.on('-r', '--require PATH', 'Require a file.') do |path| diff --git a/spec/rspec/core/option_parser_spec.rb b/spec/rspec/core/option_parser_spec.rb index e735d93fe7..c4a41b8972 100644 --- a/spec/rspec/core/option_parser_spec.rb +++ b/spec/rspec/core/option_parser_spec.rb @@ -59,6 +59,20 @@ def generate_help_text expect { generate_help_text }.to_not output(useless_lines).to_stdout end + describe "-I" do + it "sets the path" do + options = Parser.parse(%w[-I path/to/foo]) + expect(options[:libs]).to eq %w[path/to/foo] + end + + context "with multiple paths" do + it "sets the paths" do + options = Parser.parse(%W[-I path/to/foo -I path/to/bar#{File::PATH_SEPARATOR}path/to/baz]) + expect(options[:libs]).to eq %w[path/to/foo path/to/bar path/to/baz] + end + end + end + describe "--default-path" do it "sets the default path where RSpec looks for examples" do options = Parser.parse(%w[--default-path foo]) From b829a80c6898574efe4ef4e0e25c0635544e48cc Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Mon, 26 Jan 2015 08:44:22 -0800 Subject: [PATCH 268/275] Changelog for #1855. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also, add a bit more detail to the spec doc string, to mention this mirror’s Ruby’s -I behavior. --- Changelog.md | 3 +++ spec/rspec/core/option_parser_spec.rb | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index fdfc4e6c21..28a162492e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -40,6 +40,9 @@ Enhancements: allowing you to mix filtered and unfiltered files. (Myron Marston, #1839) * When dumping pending examples, include the failure details so that you don't have to un-pend the example to see it. (Myron Marston, #1844) +* Make `-I` option support multiple values when separated by + `File::PATH_SEPARATOR`, such as `rspec -I foo:bar`. This matches + the behavior of Ruby's `-I` option. (Fumiaki Matsushima, #1855). Bug Fixes: diff --git a/spec/rspec/core/option_parser_spec.rb b/spec/rspec/core/option_parser_spec.rb index c4a41b8972..f387dbb3f6 100644 --- a/spec/rspec/core/option_parser_spec.rb +++ b/spec/rspec/core/option_parser_spec.rb @@ -65,8 +65,8 @@ def generate_help_text expect(options[:libs]).to eq %w[path/to/foo] end - context "with multiple paths" do - it "sets the paths" do + context "with a string containing `#{File::PATH_SEPARATOR}`" do + it "splits into multiple paths, just like Ruby's `-I` option" do options = Parser.parse(%W[-I path/to/foo -I path/to/bar#{File::PATH_SEPARATOR}path/to/baz]) expect(options[:libs]).to eq %w[path/to/foo path/to/bar path/to/baz] end From f9f4534e9ecadcd7effcfbe6024b6313f9648ac6 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 27 Jan 2015 17:31:10 +0100 Subject: [PATCH 269/275] Move spec with described_class specs for Regexp failure --- spec/integration/describe_spec.rb | 6 ------ spec/rspec/core/metadata_spec.rb | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 spec/integration/describe_spec.rb diff --git a/spec/integration/describe_spec.rb b/spec/integration/describe_spec.rb deleted file mode 100644 index 67d8994675..0000000000 --- a/spec/integration/describe_spec.rb +++ /dev/null @@ -1,6 +0,0 @@ -RSpec.describe Regexp do - - it 'sets the core class as the described class' do - expect(described_class).to eq(Regexp) - end -end diff --git a/spec/rspec/core/metadata_spec.rb b/spec/rspec/core/metadata_spec.rb index 5203072dd9..b646a7dfc7 100644 --- a/spec/rspec/core/metadata_spec.rb +++ b/spec/rspec/core/metadata_spec.rb @@ -278,6 +278,12 @@ def metadata_for(*args) it "returns the class" do expect(value_for String).to be(String) end + + context "when the class is Regexp" do + it "returns the class" do + expect(value_for Regexp).to be(Regexp) + end + end end end From 2ed060aeb760f56bab3a93588f9c756efac5037d Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 27 Jan 2015 09:46:06 -0800 Subject: [PATCH 270/275] Add changelog entry for #1853. [ci skip] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 28a162492e..0f9b285202 100644 --- a/Changelog.md +++ b/Changelog.md @@ -69,6 +69,8 @@ Bug Fixes: previously it was not being set but should have been. (Myron Marston, #1844) * Fix rake task to work when `rspec-core` is installed in a directory containing a space. (Guido Günther, #1845) +* Fix regression in 3.1 that caused `describe Regexp` to raise errors. + (Durran Jordan, #1853) ### 3.1.8 Development From ca49430495f621c05c18d61a6ccb4e574c44ce5d Mon Sep 17 00:00:00 2001 From: Max Lincoln Date: Thu, 29 Jan 2015 22:33:49 -0500 Subject: [PATCH 271/275] Fixes #1856: make sure summary is last so it doesn't scroll off the screen --- lib/rspec/core/reporter.rb | 4 ++-- spec/rspec/core/reporter_spec.rb | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/rspec/core/reporter.rb b/lib/rspec/core/reporter.rb index 3671a6750f..0405889355 100644 --- a/lib/rspec/core/reporter.rb +++ b/lib/rspec/core/reporter.rb @@ -123,12 +123,12 @@ def finish notify :dump_pending, Notifications::ExamplesNotification.new(self) notify :dump_failures, Notifications::ExamplesNotification.new(self) notify :deprecation_summary, Notifications::NullNotification - notify :dump_summary, Notifications::SummaryNotification.new(@duration, @examples, @failed_examples, - @pending_examples, @load_time) unless mute_profile_output? notify :dump_profile, Notifications::ProfileNotification.new(@duration, @examples, @configuration.profile_examples) end + notify :dump_summary, Notifications::SummaryNotification.new(@duration, @examples, @failed_examples, + @pending_examples, @load_time) notify :seed, Notifications::SeedNotification.new(@configuration.seed, seed_used?) ensure notify :close, Notifications::NullNotification diff --git a/spec/rspec/core/reporter_spec.rb b/spec/rspec/core/reporter_spec.rb index 2b20eaa145..0c195a7db4 100644 --- a/spec/rspec/core/reporter_spec.rb +++ b/spec/rspec/core/reporter_spec.rb @@ -22,11 +22,13 @@ module RSpec::Core end end - it "dumps the failure summary after the deprecation summary so failures don't scroll off the screen and get missed" do + it "dumps the failure summary after the profile and deprecation summary so failures don't scroll off the screen and get missed" do + config.profile_examples = 10 formatter = instance_double("RSpec::Core::Formatter::ProgressFormatter") - reporter.register_listener(formatter, :dump_summary, :deprecation_summary) + reporter.register_listener(formatter, :dump_summary, :dump_profile, :deprecation_summary) expect(formatter).to receive(:deprecation_summary).ordered + expect(formatter).to receive(:dump_profile).ordered expect(formatter).to receive(:dump_summary).ordered reporter.finish From 655a52b03fd00c3d64554c4268e8da3ac0bd587c Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Fri, 30 Jan 2015 15:20:56 +1100 Subject: [PATCH 272/275] changelog for #1857 [skip ci] --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 0f9b285202..7133a5928b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -71,6 +71,8 @@ Bug Fixes: containing a space. (Guido Günther, #1845) * Fix regression in 3.1 that caused `describe Regexp` to raise errors. (Durran Jordan, #1853) +* Fix regression in 3.x that caused the profile information to be printed + after the summary. (Max Lincoln, #1857) ### 3.1.8 Development From 26a5cd95c8a645f480e7a194ef7ee768a9d1af08 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 3 Feb 2015 00:34:48 -0800 Subject: [PATCH 273/275] Update changelog. [ci skip] --- Changelog.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index 7133a5928b..95208974ae 100644 --- a/Changelog.md +++ b/Changelog.md @@ -73,11 +73,6 @@ Bug Fixes: (Durran Jordan, #1853) * Fix regression in 3.x that caused the profile information to be printed after the summary. (Max Lincoln, #1857) - -### 3.1.8 Development - -Bug Fixes: - * Apply `--seed` before loading `--require` files so that required files can access the provided seed. (Myron Marston, #1745) * Handle `RSpec::Core::Formatters::DeprecationFormatter::FileStream` being From 41abbacce6cbab307acb2b267b49b9cb2680700a Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 3 Feb 2015 07:28:35 -0800 Subject: [PATCH 274/275] Update changelog for v3.2.0 [ci skip] --- Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 95208974ae..68be1747e3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,5 @@ -### 3.2.0 Development -[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.7...master) +### 3.2.0 / 2015-02-03 +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.7...v3.2.0) Enhancements: From c13d083a3298e4a3191552927c88b7df9cc05b22 Mon Sep 17 00:00:00 2001 From: Myron Marston Date: Tue, 3 Feb 2015 07:31:44 -0800 Subject: [PATCH 275/275] Release 3.2.0 --- lib/rspec/core/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/core/version.rb b/lib/rspec/core/version.rb index f0cbac101f..5fa13d465d 100644 --- a/lib/rspec/core/version.rb +++ b/lib/rspec/core/version.rb @@ -3,7 +3,7 @@ module Core # Version information for RSpec Core. module Version # Current version of RSpec Core, in semantic versioning format. - STRING = '3.2.0.pre' + STRING = '3.2.0' end end end