diff --git a/Changelog.md b/Changelog.md index b833600d66..0ae9637513 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,8 @@ Bug Fixes: * Predicates for pending examples, (in `RSpec::Core::Example`, `#pending?`, `#skipped?` and `#pending_fixed?`) now return boolean values rather than truthy values. (Marc-André Lafortune, #2756, #2758) +* Exceptions which have a message which cannot be cast to a string will no longer + cause a crash. (Jon Rowe, #2761) ### 3.9.2 / 2020-05-02 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.9.1...v3.9.2) diff --git a/lib/rspec/core/formatters/exception_presenter.rb b/lib/rspec/core/formatters/exception_presenter.rb index db612e8858..811e512028 100644 --- a/lib/rspec/core/formatters/exception_presenter.rb +++ b/lib/rspec/core/formatters/exception_presenter.rb @@ -51,7 +51,7 @@ def formatted_cause(exception) cause << '--- Caused by: ---' cause << "#{exception_class_name(last_cause)}:" unless exception_class_name(last_cause) =~ /RSpec/ - encoded_string(last_cause.message.to_s).split("\n").each do |line| + encoded_string(exception_message_string(last_cause)).split("\n").each do |line| cause << " #{line}" end @@ -174,10 +174,18 @@ def failure_slash_error_lines lines end + # rubocop:disable Lint/RescueException + def exception_message_string(exception) + exception.message.to_s + rescue Exception => other + "A #{exception.class} for which `exception.message.to_s` raises #{other.class}." + end + # rubocop:enable Lint/RescueException + def exception_lines lines = [] lines << "#{exception_class_name}:" unless exception_class_name =~ /RSpec/ - encoded_string(exception.message.to_s).split("\n").each do |line| + encoded_string(exception_message_string(exception)).split("\n").each do |line| lines << (line.empty? ? line : " #{line}") end lines diff --git a/spec/rspec/core/formatters/exception_presenter_spec.rb b/spec/rspec/core/formatters/exception_presenter_spec.rb index 104673fa5b..a87815a68f 100644 --- a/spec/rspec/core/formatters/exception_presenter_spec.rb +++ b/spec/rspec/core/formatters/exception_presenter_spec.rb @@ -260,6 +260,32 @@ def initialize(message, backtrace = [], cause = nil) EOS end + it 'will work then the message to_s raises a looped exception' do + raising_to_s_klass = + Class.new do + def to_s + raise StandardError, self + end + end + + if RSpec::Support::Ruby.jruby? + expected_error = Java::JavaLang::StackOverflowError + else + expected_error = StandardError + end + + incorrect_message_exception = FakeException.new(raising_to_s_klass.new, []) + + the_presenter = Formatters::ExceptionPresenter.new(incorrect_message_exception, example) + + expect(the_presenter.fully_formatted(1)).to eq(<<-EOS.gsub(/^ +\|/, '')) + | + | 1) Example + | Failure/Error: Unable to find matching line from backtrace + | A #{FakeException} for which `exception.message.to_s` raises #{expected_error}. + EOS + end + it "adds extra failure lines from the example metadata" do extra_example = example.clone failure_line = 'http://www.example.com/job_details/123'