Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Commit 8cada17

Browse files
committed
Stop rescuing all exceptions.
We do want to generally rescue `Exception` and any subclasses but there are a few exceptions we should not rescue. For example, if we rescue `SystemExit`, it prevents `exit` from exiting. Fixes #2058.
1 parent fecfcca commit 8cada17

File tree

7 files changed

+44
-9
lines changed

7 files changed

+44
-9
lines changed

.rubocop.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,6 @@ Style/RaiseArgs:
5151
- lib/rspec/core/hooks.rb
5252
- lib/rspec/core/option_parser.rb
5353
- lib/rspec/core/pending.rb
54+
55+
RescueException:
56+
Enabled: true

lib/rspec/core.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,12 @@ def self.const_missing(name)
178178
require MODULES_TO_AUTOLOAD.fetch(name) { return super }
179179
::RSpec.const_get(name)
180180
end
181+
182+
module NonPassthroughExceptions
183+
PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, Interrupt, SystemExit]
184+
185+
def self.===(exception)
186+
PASSTHROUGH_EXCEPTIONS.none? { |pe| pe === exception }
187+
end
188+
end
181189
end

lib/rspec/core/configuration.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1735,7 +1735,7 @@ def define_built_in_hooks
17351735
around(:example, :aggregate_failures => true) do |procsy|
17361736
begin
17371737
aggregate_failures(nil, :hide_backtrace => true, &procsy)
1738-
rescue Exception => exception
1738+
rescue NonPassthroughExceptions => exception
17391739
procsy.example.set_aggregate_failures_exception(exception)
17401740
end
17411741
end

lib/rspec/core/example.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -227,14 +227,14 @@ def run(example_group_instance, reporter)
227227
rescue Pending::SkipDeclaredInExample
228228
# no-op, required metadata has already been set by the `skip`
229229
# method.
230-
rescue Exception => e
230+
rescue NonPassthroughExceptions => e
231231
set_exception(e)
232232
ensure
233233
run_after_example
234234
end
235235
end
236236
end
237-
rescue Exception => e
237+
rescue NonPassthroughExceptions => e
238238
set_exception(e)
239239
ensure
240240
@example_group_instance = nil # if you love something... let it go
@@ -400,7 +400,7 @@ def hooks
400400

401401
def with_around_example_hooks
402402
hooks.run(:around, :example, self) { yield }
403-
rescue Exception => e
403+
rescue NonPassthroughExceptions => e
404404
set_exception(e)
405405
end
406406

@@ -457,7 +457,7 @@ def run_after_example
457457

458458
def verify_mocks
459459
@example_group_instance.verify_mocks_for_rspec if mocks_need_verification?
460-
rescue Exception => e
460+
rescue NonPassthroughExceptions => e
461461
set_exception(e)
462462
end
463463

@@ -476,7 +476,7 @@ def assign_generated_description
476476

477477
def generate_description
478478
RSpec::Matchers.generated_description
479-
rescue Exception => e
479+
rescue NonPassthroughExceptions => e
480480
location_description + " (Got an error when generating description " \
481481
"from matcher: #{e.class}: #{e.message} -- #{e.backtrace.first})"
482482
end

lib/rspec/core/example_group.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,7 @@ def self.run(reporter=RSpec::Core::NullReporter)
524524
rescue Pending::SkipDeclaredInExample => ex
525525
for_filtered_examples(reporter) { |example| example.skip_with_exception(reporter, ex) }
526526
true
527-
rescue Exception => ex
527+
rescue NonPassthroughExceptions => ex
528528
RSpec.world.wants_to_quit = true if fail_fast?
529529
for_filtered_examples(reporter) { |example| example.fail_with_exception(reporter, ex) }
530530
false

lib/rspec/core/hooks.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ def run(example)
362362
class AfterHook < Hook
363363
def run(example)
364364
example.instance_exec(example, &block)
365-
rescue Exception => ex
365+
rescue NonPassthroughExceptions => ex
366366
example.set_exception(ex)
367367
end
368368
end
@@ -371,7 +371,7 @@ def run(example)
371371
class AfterContextHook < Hook
372372
def run(example)
373373
example.instance_exec(example, &block)
374-
rescue Exception => e
374+
rescue NonPassthroughExceptions => e
375375
# TODO: Come up with a better solution for this.
376376
RSpec.configuration.reporter.message <<-EOS
377377
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
require 'support/aruba_support'
2+
3+
RSpec.describe 'When an example forks and exits it the subprocess' do
4+
include_context "aruba support"
5+
before { clean_current_dir }
6+
7+
it 'prints the results only once' do
8+
write_file_formatted "spec/fork_spec.rb", "
9+
RSpec.describe do
10+
example { exit unless fork }
11+
end
12+
"
13+
14+
# Must pipe output to a file to see double output bug.
15+
# If used the `aruba_support` default of capturing the output
16+
# in a StringIO, we won't see the output from the subprocess
17+
# because it can't write to a StringIO in memory in the parent process.
18+
run_command "spec/fork_spec.rb --out rspec.out"
19+
in_current_dir do
20+
finished_lines = File.read("rspec.out").lines.grep(/Finished in/)
21+
expect(finished_lines.count).to eq(1)
22+
end
23+
end
24+
end

0 commit comments

Comments
 (0)