diff --git a/lib/rspec/core.rb b/lib/rspec/core.rb index fe9d9fd9b4..f492a21f0b 100644 --- a/lib/rspec/core.rb +++ b/lib/rspec/core.rb @@ -69,7 +69,7 @@ def self.reset # same process. def self.clear_examples world.reset - configuration.reporter.reset + configuration.reset_reporter configuration.start_time = ::RSpec::Core::Time.now configuration.reset_filters end diff --git a/lib/rspec/core/configuration.rb b/lib/rspec/core/configuration.rb index 6656e34ef5..edc6c19a57 100644 --- a/lib/rspec/core/configuration.rb +++ b/lib/rspec/core/configuration.rb @@ -477,6 +477,12 @@ def reset @formatter_loader = nil end + # @private + def reset_reporter + @reporter = nil + @formatter_loader = nil + end + # @private def reset_filters self.filter_manager = FilterManager.new diff --git a/lib/rspec/core/formatters/base_text_formatter.rb b/lib/rspec/core/formatters/base_text_formatter.rb index 262a8d3dda..49ff205d91 100644 --- a/lib/rspec/core/formatters/base_text_formatter.rb +++ b/lib/rspec/core/formatters/base_text_formatter.rb @@ -58,8 +58,9 @@ def seed(notification) # @api public # - # Invoked at the very end, `close` allows the formatter to clean - # up resources, e.g. open streams, etc. + # Invoked at the end of a suite run. Allows the formatter to do any + # tidying up, but be aware that formatter output streams may be used + # elsewhere so don't actually close them. # # @param _notification [NullNotification] (Ignored) def close(_notification) @@ -69,7 +70,6 @@ def close(_notification) output.puts output.flush - output.close unless output == $stdout end end end diff --git a/lib/rspec/core/formatters/json_formatter.rb b/lib/rspec/core/formatters/json_formatter.rb index 510a80c648..2f2208e304 100644 --- a/lib/rspec/core/formatters/json_formatter.rb +++ b/lib/rspec/core/formatters/json_formatter.rb @@ -48,7 +48,6 @@ def stop(notification) def close(_notification) output.write @output_hash.to_json - output.close if IO === output && output != $stdout end def dump_profile(profile) diff --git a/lib/rspec/core/formatters/protocol.rb b/lib/rspec/core/formatters/protocol.rb index e466cc802c..35e45429d3 100644 --- a/lib/rspec/core/formatters/protocol.rb +++ b/lib/rspec/core/formatters/protocol.rb @@ -171,8 +171,9 @@ class Protocol # @api public # @group Suite Notifications # - # Invoked at the very end, `close` allows the formatter to clean - # up resources, e.g. open streams, etc. + # Invoked at the end of a suite run. Allows the formatter to do any + # tidying up, but be aware that formatter output streams may be used + # elsewhere so don't actually close them. # # @param notification [NullNotification] end diff --git a/lib/rspec/core/reporter.rb b/lib/rspec/core/reporter.rb index 16e88b94cd..884fb699b4 100644 --- a/lib/rspec/core/reporter.rb +++ b/lib/rspec/core/reporter.rb @@ -25,14 +25,6 @@ def initialize(configuration) # @private attr_reader :examples, :failed_examples, :pending_examples - # @private - def reset - @examples = [] - @failed_examples = [] - @pending_examples = [] - @profiler = Profiler.new if defined?(@profiler) - end - # @private def setup_profiler @profiler = Profiler.new diff --git a/lib/rspec/core/world.rb b/lib/rspec/core/world.rb index 37b582d554..eafde40f5e 100644 --- a/lib/rspec/core/world.rb +++ b/lib/rspec/core/world.rb @@ -40,7 +40,6 @@ def ordered_example_groups def reset RSpec::ExampleGroups.remove_all_constants example_groups.clear - @shared_example_group_registry = nil end # @private diff --git a/spec/rspec/core/bisect/coordinator_spec.rb b/spec/rspec/core/bisect/coordinator_spec.rb index f2cff58ccc..cae6d8b5b3 100644 --- a/spec/rspec/core/bisect/coordinator_spec.rb +++ b/spec/rspec/core/bisect/coordinator_spec.rb @@ -26,24 +26,25 @@ def find_minimal_repro(output, formatter=Formatters::BisectProgressFormatter) it 'notifies the bisect progress formatter of progress and closes the output' do tempfile = Tempfile.new("bisect") - output_file = File.open(tempfile.path, "w") - expect { find_minimal_repro(output_file) }.to change(output_file, :closed?).from(false).to(true) - output = normalize_durations(File.read(tempfile.path)).chomp + File.open(tempfile.path, "w") do |output_file| + find_minimal_repro(output_file) + output = normalize_durations(File.read(tempfile.path)).chomp - expect(output).to eq(<<-EOS.gsub(/^\s+\|/, '')) - |Bisect started using options: "" - |Running suite to find failures... (n.nnnn seconds) - |Starting bisect with 2 failing examples and 6 non-failing examples. - |Checking that failure(s) are order-dependent... failure appears to be order-dependent - | - |Round 1: bisecting over non-failing examples 1-6 .. ignoring examples 4-6 (n.nnnn seconds) - |Round 2: bisecting over non-failing examples 1-3 .. multiple culprits detected - splitting candidates (n.nnnn seconds) - |Round 3: bisecting over non-failing examples 1-2 .. ignoring example 2 (n.nnnn seconds) - |Bisect complete! Reduced necessary non-failing examples from 6 to 2 in n.nnnn seconds. - | - |The minimal reproduction command is: - | rspec 1.rb[1:1] 2.rb[1:1] 4.rb[1:1] 5.rb[1:1] - EOS + expect(output).to eq(<<-EOS.gsub(/^\s+\|/, '')) + |Bisect started using options: "" + |Running suite to find failures... (n.nnnn seconds) + |Starting bisect with 2 failing examples and 6 non-failing examples. + |Checking that failure(s) are order-dependent... failure appears to be order-dependent + | + |Round 1: bisecting over non-failing examples 1-6 .. ignoring examples 4-6 (n.nnnn seconds) + |Round 2: bisecting over non-failing examples 1-3 .. multiple culprits detected - splitting candidates (n.nnnn seconds) + |Round 3: bisecting over non-failing examples 1-2 .. ignoring example 2 (n.nnnn seconds) + |Bisect complete! Reduced necessary non-failing examples from 6 to 2 in n.nnnn seconds. + | + |The minimal reproduction command is: + | rspec 1.rb[1:1] 2.rb[1:1] 4.rb[1:1] 5.rb[1:1] + EOS + end end it 'can use the bisect debug formatter to get detailed progress' do diff --git a/spec/rspec/core/formatters/base_text_formatter_spec.rb b/spec/rspec/core/formatters/base_text_formatter_spec.rb index f034fd5abb..c93fcf4139 100644 --- a/spec/rspec/core/formatters/base_text_formatter_spec.rb +++ b/spec/rspec/core/formatters/base_text_formatter_spec.rb @@ -8,7 +8,13 @@ let(:output_to_close) { File.new("./output_to_close", "w") } let(:formatter) { described_class.new(output_to_close) } - it 'does not close an already closed output stream' do + after do + # Windows appears to not let the `:isolated_directory` shared group + # cleanup if the file isn't closed. + output_to_close.close unless output_to_close.closed? + end + + it 'does not error on an already closed output stream' do output_to_close.close expect { formatter.close(RSpec::Core::Notifications::NullNotification) }.not_to raise_error @@ -16,12 +22,14 @@ it "flushes output before closing the stream so buffered bytes are not lost if we exit right away" do expect(output_to_close).to receive(:flush).ordered.and_call_original - # Windows appears to not let the `:isolated_directory` shared group cleanup if - # the file isn't closed, so we need to use `and_call_original` here. - expect(output_to_close).to receive(:close).ordered.and_call_original formatter.close(RSpec::Core::Notifications::NullNotification) end + + it "does not close the stream so that it can be reused within a process" do + formatter.close(RSpec::Core::Notifications::NullNotification) + expect(output_to_close.closed?).to be(false) + end end describe "#dump_summary" do diff --git a/spec/rspec/core/formatters/json_formatter_spec.rb b/spec/rspec/core/formatters/json_formatter_spec.rb index 4faefd64af..bf3d027887 100644 --- a/spec/rspec/core/formatters/json_formatter_spec.rb +++ b/spec/rspec/core/formatters/json_formatter_spec.rb @@ -103,6 +103,11 @@ :version => RSpec::Core::Version::STRING }.to_json) end + + it "does not close the stream so that it can be reused within a process" do + formatter.close(RSpec::Core::Notifications::NullNotification) + expect(formatter_output.closed?).to be(false) + end end describe "#message" do diff --git a/spec/rspec/core_spec.rb b/spec/rspec/core_spec.rb index ec80ea5025..18ec3a67ef 100644 --- a/spec/rspec/core_spec.rb +++ b/spec/rspec/core_spec.rb @@ -136,7 +136,10 @@ describe ".clear_examples" do let(:listener) { double("listener") } - let(:reporter) { RSpec.configuration.reporter } + + def reporter + RSpec.configuration.reporter + end before do RSpec.configuration.output_stream = StringIO.new @@ -175,6 +178,8 @@ reporter.example_pending(pending_ex) reporter.finish + RSpec.clear_examples + reporter.register_listener(listener, :dump_summary) expect(listener).to receive(:dump_summary) do |notification| @@ -183,7 +188,6 @@ expect(notification.pending_examples).to be_empty end - RSpec.clear_examples reporter.start(0) reporter.finish end @@ -224,6 +228,40 @@ RSpec.configuration.filter_manager.exclusions.rules ).to eq(:slow => true) end + + it 'clears the deprecation buffer' do + RSpec.configuration.deprecation_stream = StringIO.new + + group = RSpec.describe do + example { RSpec.deprecate("first deprecation") } + end.run + + reporter.start(1) + reporter.finish + + RSpec.clear_examples + + RSpec.configuration.deprecation_stream = StringIO.new(deprecations = "") + + group = RSpec.describe do + example { RSpec.deprecate("second deprecation") } + end.run + + reporter.start(1) + reporter.finish + + expect(deprecations).to include("second deprecation") + expect(deprecations).to_not include("first deprecation") + end + + it 'does not clear shared examples' do + RSpec.shared_examples_for("shared") { } + + RSpec.clear_examples + + registry = RSpec.world.shared_example_group_registry + expect(registry.find([:main], "shared")).to_not be_nil + end end it 'uses only one thread local variable', :run_last do