From 4cdd52d3f1e335d4641a7cf07d723e4dce2908e0 Mon Sep 17 00:00:00 2001 From: Andrew Konchin Date: Wed, 18 Dec 2024 16:01:39 +0200 Subject: [PATCH] Add missing specs for NoMethodError#message --- spec/ruby/core/exception/fixtures/common.rb | 3 + .../core/exception/no_method_error_spec.rb | 174 ++++++++++++++---- .../core/exception/no_method_error_tags.txt | 2 + 3 files changed, 139 insertions(+), 40 deletions(-) create mode 100644 spec/tags/core/exception/no_method_error_tags.txt diff --git a/spec/ruby/core/exception/fixtures/common.rb b/spec/ruby/core/exception/fixtures/common.rb index 1e243098bd08..3d8a3c34308f 100644 --- a/spec/ruby/core/exception/fixtures/common.rb +++ b/spec/ruby/core/exception/fixtures/common.rb @@ -84,6 +84,9 @@ class NoMethodErrorD; end class InstanceException < Exception end + + class AClass; end + module AModule; end end class NameErrorSpecs diff --git a/spec/ruby/core/exception/no_method_error_spec.rb b/spec/ruby/core/exception/no_method_error_spec.rb index 26df3338e9da..78fafcaa28f3 100644 --- a/spec/ruby/core/exception/no_method_error_spec.rb +++ b/spec/ruby/core/exception/no_method_error_spec.rb @@ -67,7 +67,7 @@ end ruby_version_is ""..."3.3" do - it "calls receiver.inspect only when calling Exception#message" do + it "calls #inspect when calling Exception#message" do ScratchPad.record [] test_class = Class.new do def inspect @@ -76,68 +76,162 @@ def inspect end end instance = test_class.new + begin instance.bar - rescue Exception => e - e.name.should == :bar - ScratchPad.recorded.should == [] - e.message.should =~ /undefined method.+\bbar\b/ + rescue NoMethodError => error + error.message.should =~ /\Aundefined method [`']bar' for :#$/ ScratchPad.recorded.should == [:inspect_called] end end - end - ruby_version_is "3.3" do - it "does not call receiver.inspect even when calling Exception#message" do - ScratchPad.record [] + it "fallbacks to a simpler representation of the receiver when receiver.inspect raises an exception" do test_class = Class.new do def inspect - ScratchPad << :inspect_called - "" + raise NoMethodErrorSpecs::InstanceException end end instance = test_class.new + begin instance.bar - rescue Exception => e - e.name.should == :bar - ScratchPad.recorded.should == [] - e.message.should =~ /undefined method.+\bbar\b/ - ScratchPad.recorded.should == [] + rescue NoMethodError => error + message = error.message + message.should =~ /undefined method.+\bbar\b/ + message.should include test_class.inspect end end - end - it "fallbacks to a simpler representation of the receiver when receiver.inspect raises an exception" do - test_class = Class.new do - def inspect - raise NoMethodErrorSpecs::InstanceException + it "uses #name to display the receiver if it is a class" do + klass = Class.new { def self.name; "MyClass"; end } + + begin + klass.foo + rescue NoMethodError => error + error.message.should =~ /\Aundefined method [`']foo' for MyClass:Class$/ end end - instance = test_class.new - begin - instance.bar - rescue Exception => e - e.name.should == :bar - message = e.message - message.should =~ /undefined method.+\bbar\b/ - message.should include test_class.inspect + + it "uses #name to display the receiver if it is a module" do + mod = Module.new { def self.name; "MyModule"; end } + + begin + mod.foo + rescue NoMethodError => error + error.message.should =~ /\Aundefined method [`']foo' for MyModule:Module$/ + end end end - it "uses #name to display the receiver if it is a class or a module" do - klass = Class.new { def self.name; "MyClass"; end } - begin - klass.foo - rescue NoMethodError => error - error.message.lines.first.chomp.should =~ /^undefined method [`']foo' for / + ruby_version_is "3.3" do + it "uses a literal name when receiver is nil" do + begin + nil.foo + rescue NoMethodError => error + error.message.should =~ /\Aundefined method [`']foo' for nil\Z/ + end end - mod = Module.new { def self.name; "MyModule"; end } - begin - mod.foo - rescue NoMethodError => error - error.message.lines.first.chomp.should =~ /^undefined method [`']foo' for / + it "uses a literal name when receiver is true" do + begin + true.foo + rescue NoMethodError => error + error.message.should =~ /\Aundefined method [`']foo' for true\Z/ + end + end + + it "uses a literal name when receiver is false" do + begin + false.foo + rescue NoMethodError => error + error.message.should =~ /\Aundefined method [`']foo' for false\Z/ + end + end + + it "uses #name when receiver is a class" do + begin + NoMethodErrorSpecs::AClass.foo + rescue NoMethodError => error + error.message.should =~ /\Aundefined method [`']foo' for class NoMethodErrorSpecs::AClass\Z/ + end + end + + it "uses class' string representation when receiver is an anonymous class" do + klass = Class.new + + begin + klass.foo + rescue NoMethodError => error + error.message.should =~ /\Aundefined method [`']foo' for class #\Z/ + end + end + + it "uses #name when receiver is a module" do + begin + NoMethodErrorSpecs::AModule.foo + rescue NoMethodError => error + error.message.should =~ /\Aundefined method [`']foo' for module NoMethodErrorSpecs::AModule\Z/ + end + end + + it "uses module's string representation when receiver is an anonymous module" do + m = Module.new + + begin + m.foo + rescue NoMethodError => error + error.message.should =~ /\Aundefined method [`']foo' for module #\Z/ + end + end + + it "uses class name when receiver is an ordinary object" do + instance = NoMethodErrorSpecs::NoMethodErrorA.new + + begin + instance.bar + rescue NoMethodError => error + error.message.should =~ /\Aundefined method [`']bar' for an instance of NoMethodErrorSpecs::NoMethodErrorA\Z/ + end + end + + it "uses class string representation when receiver is an instance of anonymous class" do + klass = Class.new + instance = klass.new + + begin + instance.bar + rescue NoMethodError => error + error.message.should =~ /\Aundefined method [`']bar' for an instance of #\Z/ + end + end + + it "uses class name when receiver has a singleton class" do + instance = NoMethodErrorSpecs::NoMethodErrorA.new + def instance.foo; end + + begin + instance.bar + rescue NoMethodError => error + error.message.should =~ /\Aundefined method [`']bar' for #\Z/ + end + end + + it "does not call #inspect when calling Exception#message" do + ScratchPad.record [] + test_class = Class.new do + def inspect + ScratchPad << :inspect_called + "" + end + end + instance = test_class.new + + begin + instance.bar + rescue NoMethodError => error + error.message.should =~ /\Aundefined method [`']bar' for an instance of #\Z/ + ScratchPad.recorded.should == [] + end end end end diff --git a/spec/tags/core/exception/no_method_error_tags.txt b/spec/tags/core/exception/no_method_error_tags.txt new file mode 100644 index 000000000000..0a1d27b5d5d4 --- /dev/null +++ b/spec/tags/core/exception/no_method_error_tags.txt @@ -0,0 +1,2 @@ +fails:NoMethodError#message uses class string representation when receiver is an instance of anonymous class +fails:NoMethodError#message does not call #inspect when calling Exception#message