Update test suite for compatibility with Ruby 3.4-dev

https://bugs.ruby-lang.org/issues/19117 and https://bugs.ruby-lang.org/issues/16495
slightly change how backtrace are rendered which makes a few tests fail.
This commit is contained in:
Jean Boussier 2024-02-16 09:53:45 +01:00
parent 9e01d93547
commit 50daadaa71
15 changed files with 149 additions and 70 deletions

@ -701,7 +701,7 @@ def self.build_app(app, *args)
# assert application trace refers to line that calls method_that_raises is first # assert application trace refers to line that calls method_that_raises is first
assert_select "#Application-Trace-0" do assert_select "#Application-Trace-0" do
assert_select "code a:first", %r{test/dispatch/debug_exceptions_test\.rb:\d+:in `call} assert_select "code a:first", %r{test/dispatch/debug_exceptions_test\.rb:\d+:in .*call}
end end
# assert framework trace that threw the error is first # assert framework trace that threw the error is first
@ -747,19 +747,32 @@ def self.build_app(app, *args)
assert_select "pre .line.active", /raise "Third error"/ assert_select "pre .line.active", /raise "Third error"/
end end
# assert application trace refers to line that raises the last exception if RUBY_VERSION >= "3.4"
assert_select "#Application-Trace-0" do # Possible Ruby 3.4-dev bug: https://bugs.ruby-lang.org/issues/19117#note-45
assert_select "code a:first", %r{in `rescue in rescue in raise_nested_exceptions'} # assert application trace refers to line that raises the last exception
end assert_select "#Application-Trace-0" do
assert_select "code a:first", %r{in '.*raise_nested_exceptions'}
end
# assert the second application trace refers to the line that raises the second exception # assert the second application trace refers to the line that raises the second exception
assert_select "#Application-Trace-1" do assert_select "#Application-Trace-1" do
assert_select "code a:first", %r{in `rescue in raise_nested_exceptions'} assert_select "code a:first", %r{in '.*raise_nested_exceptions'}
end
else
# assert application trace refers to line that raises the last exception
assert_select "#Application-Trace-0" do
assert_select "code a:first", %r{in [`']rescue in rescue in .*raise_nested_exceptions'}
end
# assert the second application trace refers to the line that raises the second exception
assert_select "#Application-Trace-1" do
assert_select "code a:first", %r{in [`']rescue in .*raise_nested_exceptions'}
end
end end
# assert the third application trace refers to the line that raises the first exception # assert the third application trace refers to the line that raises the first exception
assert_select "#Application-Trace-2" do assert_select "#Application-Trace-2" do
assert_select "code a:first", %r{in `raise_nested_exceptions'} assert_select "code a:first", %r{in [`'].*raise_nested_exceptions'}
end end
end end
end end
@ -810,6 +823,6 @@ def self.build_app(app, *args)
assert_response 500 assert_response 500
assert_select "#container p", /Showing #{__FILE__} where line #\d+ raised/ assert_select "#container p", /Showing #{__FILE__} where line #\d+ raised/
assert_select "#container code", /undefined local variable or method `string”'/ assert_select "#container code", /undefined local variable or method ['`]string”'/
end end
end end

@ -114,7 +114,11 @@ def backtrace
exception = begin index; rescue TestError => ex; ex; end exception = begin index; rescue TestError => ex; ex; end
wrapper = ExceptionWrapper.new(@cleaner, TopErrorProxy.new(exception, 1)) wrapper = ExceptionWrapper.new(@cleaner, TopErrorProxy.new(exception, 1))
assert_equal [ "lib/file.rb:42:in `index'" ], wrapper.application_trace.map(&:to_s) if RUBY_VERSION >= "3.4"
assert_equal [ "lib/file.rb:42:in 'ActionDispatch::ExceptionWrapperTest#index'" ], wrapper.application_trace.map(&:to_s)
else
assert_equal [ "lib/file.rb:42:in `index'" ], wrapper.application_trace.map(&:to_s)
end
end end
test "#status_code returns 400 for Rack::Utils::ParameterTypeError" do test "#status_code returns 400 for Rack::Utils::ParameterTypeError" do
@ -182,30 +186,57 @@ def backtrace
exception = begin in_rack; rescue TestError => ex; TopErrorProxy.new(ex, 2); end exception = begin in_rack; rescue TestError => ex; TopErrorProxy.new(ex, 2); end
wrapper = ExceptionWrapper.new(@cleaner, exception) wrapper = ExceptionWrapper.new(@cleaner, exception)
assert_equal({ if RUBY_VERSION >= "3.4"
"Application Trace" => [ assert_equal({
exception_object_id: exception.object_id, "Application Trace" => [
id: 0, exception_object_id: exception.object_id,
trace: "lib/file.rb:42:in `index'" id: 0,
], trace: "lib/file.rb:42:in 'ActionDispatch::ExceptionWrapperTest#index'"
"Framework Trace" => [ ],
exception_object_id: exception.object_id, "Framework Trace" => [
id: 1, exception_object_id: exception.object_id,
trace: "/gems/rack.rb:43:in `in_rack'" id: 1,
], trace: "/gems/rack.rb:43:in 'ActionDispatch::ExceptionWrapperTest#in_rack'"
"Full Trace" => [ ],
{ "Full Trace" => [
{
exception_object_id: exception.object_id,
id: 0,
trace: "lib/file.rb:42:in 'ActionDispatch::ExceptionWrapperTest#index'"
},
{
exception_object_id: exception.object_id,
id: 1,
trace: "/gems/rack.rb:43:in 'ActionDispatch::ExceptionWrapperTest#in_rack'"
}
]
}.inspect, wrapper.traces.inspect)
else
assert_equal({
"Application Trace" => [
exception_object_id: exception.object_id, exception_object_id: exception.object_id,
id: 0, id: 0,
trace: "lib/file.rb:42:in `index'" trace: "lib/file.rb:42:in `index'"
}, ],
{ "Framework Trace" => [
exception_object_id: exception.object_id, exception_object_id: exception.object_id,
id: 1, id: 1,
trace: "/gems/rack.rb:43:in `in_rack'" trace: "/gems/rack.rb:43:in `in_rack'"
} ],
] "Full Trace" => [
}.inspect, wrapper.traces.inspect) {
exception_object_id: exception.object_id,
id: 0,
trace: "lib/file.rb:42:in `index'"
},
{
exception_object_id: exception.object_id,
id: 1,
trace: "/gems/rack.rb:43:in `in_rack'"
}
]
}.inspect, wrapper.traces.inspect)
end
end end
test "#show? returns false when using :rescuable and the exceptions is not rescuable" do test "#show? returns false when using :rescuable and the exceptions is not rescuable" do

@ -213,7 +213,7 @@ def test_with_nil_in_list_does_not_generate_invalid_link
@series.save @series.save
polymorphic_url([nil, @series]) polymorphic_url([nil, @series])
end end
assert_match(/undefined method `series_url'/, exception.message) assert_match(/undefined method [`']series_url'/, exception.message)
end end
end end

@ -316,7 +316,7 @@ def test_render_renderable_does_not_mask_nomethoderror_from_within_render_in
renderable = Object.new renderable = Object.new
renderable.define_singleton_method(:render_in) { |*| nil.render_in } renderable.define_singleton_method(:render_in) { |*| nil.render_in }
assert_raises NoMethodError, match: "undefined method `render_in' for nil" do assert_raises NoMethodError, match: /undefined method [`']render_in' for nil/ do
@view.render renderable: renderable @view.render renderable: renderable
end end
end end
@ -390,7 +390,7 @@ def test_render_sub_template_with_errors
def test_undefined_method_error_references_named_class def test_undefined_method_error_references_named_class
e = assert_raises(ActionView::Template::Error) { @view.render(inline: "<%= undefined %>") } e = assert_raises(ActionView::Template::Error) { @view.render(inline: "<%= undefined %>") }
assert_match(/undefined local variable or method `undefined'/, e.message) assert_match(/undefined local variable or method [`']undefined'/, e.message)
end end
def test_render_renderable_object def test_render_renderable_object

@ -278,14 +278,14 @@ def test_job_error_logging
RescueJob.perform_later "other" RescueJob.perform_later "other"
rescue RescueJob::OtherError rescue RescueJob::OtherError
assert_match(/Performing RescueJob \(Job ID: .*?\) from .*? with arguments:.*other/, @logger.messages) assert_match(/Performing RescueJob \(Job ID: .*?\) from .*? with arguments:.*other/, @logger.messages)
assert_match(/Error performing RescueJob \(Job ID: .*?\) from .*? in .*ms: RescueJob::OtherError \(Bad hair\):\n.*\brescue_job\.rb:\d+:in `perform'/, @logger.messages) assert_match(/Error performing RescueJob \(Job ID: .*?\) from .*? in .*ms: RescueJob::OtherError \(Bad hair\):\n.*\brescue_job\.rb:\d+:in .*perform'/, @logger.messages)
end end
end end
def test_job_no_error_logging_on_rescuable_job def test_job_no_error_logging_on_rescuable_job
perform_enqueued_jobs { RescueJob.perform_later "david" } perform_enqueued_jobs { RescueJob.perform_later "david" }
assert_match(/Performing RescueJob \(Job ID: .*?\) from .*? with arguments:.*david/, @logger.messages) assert_match(/Performing RescueJob \(Job ID: .*?\) from .*? with arguments:.*david/, @logger.messages)
assert_no_match(/Error performing RescueJob \(Job ID: .*?\) from .*? in .*ms: ArgumentError \(Hair too good\):\n.*\brescue_job\.rb:\d+:in `perform'/, @logger.messages) assert_no_match(/Error performing RescueJob \(Job ID: .*?\) from .*? in .*ms: ArgumentError \(Hair too good\):\n.*\brescue_job\.rb:\d+:in .*perform'/, @logger.messages)
end end
def test_enqueue_retry_logging def test_enqueue_retry_logging

@ -262,7 +262,7 @@ def attribute(name)
assert_equal("Active Model Topic", topic_class.new.subject_to_be_undefined) assert_equal("Active Model Topic", topic_class.new.subject_to_be_undefined)
topic_class.undefine_attribute_methods topic_class.undefine_attribute_methods
assert_raises(NoMethodError, match: /undefined method `subject_to_be_undefined'/) do assert_raises(NoMethodError, match: /undefined method [`']subject_to_be_undefined'/) do
topic_class.new.subject_to_be_undefined topic_class.new.subject_to_be_undefined
end end
end end

@ -291,14 +291,14 @@ def test_overriding_default_journal_size_limit_pragma
conn.execute("PRAGMA journal_size_limit") conn.execute("PRAGMA journal_size_limit")
end end
end end
assert_match(/undefined method `to_i'/, error.message) assert_match(/undefined method [`']to_i'/, error.message)
error = assert_raises(ActiveRecord::StatementInvalid) do error = assert_raises(ActiveRecord::StatementInvalid) do
send(method_name, pragmas: { journal_size_limit: :false }) do |conn| send(method_name, pragmas: { journal_size_limit: :false }) do |conn|
conn.execute("PRAGMA journal_size_limit") conn.execute("PRAGMA journal_size_limit")
end end
end end
assert_match(/undefined method `to_i'/, error.message) assert_match(/undefined method [`']to_i'/, error.message)
end end
def test_overriding_default_mmap_size_pragma def test_overriding_default_mmap_size_pragma
@ -317,14 +317,14 @@ def test_overriding_default_mmap_size_pragma
conn.execute("PRAGMA mmap_size") conn.execute("PRAGMA mmap_size")
end end
end end
assert_match(/undefined method `to_i'/, error.message) assert_match(/undefined method [`']to_i'/, error.message)
error = assert_raises(ActiveRecord::StatementInvalid) do error = assert_raises(ActiveRecord::StatementInvalid) do
with_memory_connection(pragmas: { mmap_size: :false }) do |conn| with_memory_connection(pragmas: { mmap_size: :false }) do |conn|
conn.execute("PRAGMA mmap_size") conn.execute("PRAGMA mmap_size")
end end
end end
assert_match(/undefined method `to_i'/, error.message) assert_match(/undefined method [`']to_i'/, error.message)
else else
with_file_connection(pragmas: { mmap_size: 100 }) do |conn| with_file_connection(pragmas: { mmap_size: 100 }) do |conn|
assert_equal [{ "mmap_size" => 100 }], conn.execute("PRAGMA mmap_size") assert_equal [{ "mmap_size" => 100 }], conn.execute("PRAGMA mmap_size")
@ -339,14 +339,14 @@ def test_overriding_default_mmap_size_pragma
conn.execute("PRAGMA mmap_size") conn.execute("PRAGMA mmap_size")
end end
end end
assert_match(/undefined method `to_i'/, error.message) assert_match(/undefined method [`']to_i'/, error.message)
error = assert_raises(ActiveRecord::StatementInvalid) do error = assert_raises(ActiveRecord::StatementInvalid) do
with_file_connection(pragmas: { mmap_size: :false }) do |conn| with_file_connection(pragmas: { mmap_size: :false }) do |conn|
conn.execute("PRAGMA mmap_size") conn.execute("PRAGMA mmap_size")
end end
end end
assert_match(/undefined method `to_i'/, error.message) assert_match(/undefined method [`']to_i'/, error.message)
end end
end end
@ -366,14 +366,14 @@ def test_overriding_default_cache_size_pragma
conn.execute("PRAGMA cache_size") conn.execute("PRAGMA cache_size")
end end
end end
assert_match(/undefined method `to_i'/, error.message) assert_match(/undefined method [`']to_i'/, error.message)
error = assert_raises(ActiveRecord::StatementInvalid) do error = assert_raises(ActiveRecord::StatementInvalid) do
send(method_name, pragmas: { cache_size: :false }) do |conn| send(method_name, pragmas: { cache_size: :false }) do |conn|
conn.execute("PRAGMA cache_size") conn.execute("PRAGMA cache_size")
end end
end end
assert_match(/undefined method `to_i'/, error.message) assert_match(/undefined method [`']to_i'/, error.message)
end end
def test_setting_new_pragma def test_setting_new_pragma

@ -473,18 +473,33 @@ def test_tidy_call_stack
# callbacks that have been invoked, if there are any (plus # callbacks that have been invoked, if there are any (plus
# whatever the callbacks do themselves, of course). # whatever the callbacks do themselves, of course).
assert_equal [ if RUBY_VERSION >= "3.4"
"block in save", assert_equal [
"block in run_callbacks", "block in CallbacksTest::MySlate#save",
"tweedle_deedle", "block in ActiveSupport::Callbacks#run_callbacks",
"block in run_callbacks", "CallbacksTest::AroundPerson#tweedle_deedle",
"w0tyes", "block in ActiveSupport::Callbacks#run_callbacks",
"block in run_callbacks", "CallbacksTest::AroundPerson#w0tyes",
"tweedle_dum", "block in ActiveSupport::Callbacks#run_callbacks",
"block in run_callbacks", "CallbacksTest::AroundPerson#tweedle_dum",
"run_callbacks", "block in ActiveSupport::Callbacks#run_callbacks",
"save" "ActiveSupport::Callbacks#run_callbacks",
], call_stack.map(&:label) "CallbacksTest::MySlate#save",
].join("\n"), call_stack.map(&:label).join("\n")
else
assert_equal [
"block in save",
"block in run_callbacks",
"tweedle_deedle",
"block in run_callbacks",
"w0tyes",
"block in run_callbacks",
"tweedle_dum",
"block in run_callbacks",
"run_callbacks",
"save",
].join("\n"), call_stack.map(&:label).join("\n")
end
end end
def test_short_call_stack def test_short_call_stack
@ -503,11 +518,19 @@ def test_short_call_stack
# there should be just one line. run_callbacks yields directly # there should be just one line. run_callbacks yields directly
# back to its caller. # back to its caller.
assert_equal [ if RUBY_VERSION >= "3.4"
"block in save", assert_equal [
"run_callbacks", "block in CallbacksTest::Person#save",
"save" "ActiveSupport::Callbacks#run_callbacks",
], call_stack.map(&:label) "CallbacksTest::Person#save",
].join("\n"), call_stack.map(&:label).join("\n")
else
assert_equal [
"block in save",
"run_callbacks",
"save",
].join("\n"), call_stack.map(&:label).join("\n")
end
end end
end end

@ -426,7 +426,7 @@ def test_delegate_missing_to_does_not_delegate_to_private_methods
DecoratedReserved.new(@david).private_name DecoratedReserved.new(@david).private_name
end end
assert_match(/undefined method `private_name' for/, e.message) assert_match(/undefined method [`']private_name' for/, e.message)
end end
def test_delegate_missing_to_does_not_delegate_to_fake_methods def test_delegate_missing_to_does_not_delegate_to_fake_methods
@ -434,7 +434,7 @@ def test_delegate_missing_to_does_not_delegate_to_fake_methods
DecoratedReserved.new(@david).my_fake_method DecoratedReserved.new(@david).my_fake_method
end end
assert_match(/undefined method `my_fake_method' for/, e.message) assert_match(/undefined method [`']my_fake_method' for/, e.message)
end end
def test_delegate_missing_to_raises_delegation_error_if_target_nil def test_delegate_missing_to_raises_delegation_error_if_target_nil

@ -1080,7 +1080,7 @@ def test_no_method_error_has_proper_context
e = assert_raises(NoMethodError) { e = assert_raises(NoMethodError) {
@twz.this_method_does_not_exist @twz.this_method_does_not_exist
} }
assert_match(/undefined method `this_method_does_not_exist' for.*ActiveSupport::TimeWithZone/, e.message) assert_match(/undefined method [`']this_method_does_not_exist' for.*ActiveSupport::TimeWithZone/, e.message)
assert_no_match "rescue", e.backtrace.first assert_no_match "rescue", e.backtrace.first
end end
end end

@ -968,7 +968,11 @@ def generated_method_that_call_deprecation(deprecator)
test "warn deprecation can blame code generated with eval" do test "warn deprecation can blame code generated with eval" do
@deprecator.behavior = ->(message, *) { @message = message } @deprecator.behavior = ->(message, *) { @message = message }
generated_method_that_call_deprecation(@deprecator) generated_method_that_call_deprecation(@deprecator)
assert_equal "DEPRECATION WARNING: Here (called from generated_method_that_call_deprecation at /path/to/template.html.erb:2)", @message if RUBY_VERSION >= "3.4"
assert_equal "DEPRECATION WARNING: Here (called from DeprecationTest#generated_method_that_call_deprecation at /path/to/template.html.erb:2)", @message
else
assert_equal "DEPRECATION WARNING: Here (called from generated_method_that_call_deprecation at /path/to/template.html.erb:2)", @message
end
end end
private private

@ -416,8 +416,8 @@ def test_fails_and_warning_is_logged_if_wrong_error_caught
Other block based assertions (e.g. `assert_no_changes`) can be used, as long as `assert_raises` is inside their block. Other block based assertions (e.g. `assert_no_changes`) can be used, as long as `assert_raises` is inside their block.
MSG MSG
assert_includes @out.string, expected assert_includes @out.string, expected
assert error.message.include?("ArgumentError: ArgumentError") assert_includes error.message, "ArgumentError: ArgumentError"
assert error.message.include?("in `block (2 levels) in run_test_that_should_fail_confusingly'") assert_includes error.message, "run_test_that_should_fail_confusingly"
end end
private private

@ -19,7 +19,11 @@ def teardown
app("development") app("development")
get "/" get "/"
assert_includes last_response.body, "app/app/controllers/foo_controller.rb:4:in `index'" if RUBY_VERSION >= "3.4"
assert_includes last_response.body, "app/app/controllers/foo_controller.rb:4:in 'FooController#index'"
else
assert_includes last_response.body, "app/app/controllers/foo_controller.rb:4:in `index'"
end
assert_not_includes last_response.body, "rails/railties/test/env_helpers.rb" assert_not_includes last_response.body, "rails/railties/test/env_helpers.rb"
end end
@ -29,7 +33,11 @@ def teardown
app("development") app("development")
get "/" get "/"
assert_includes last_response.body, "app/app/controllers/foo_controller.rb:4:in `index'" if RUBY_VERSION >= "3.4"
assert_includes last_response.body, "app/app/controllers/foo_controller.rb:4:in 'FooController#index'"
else
assert_includes last_response.body, "app/app/controllers/foo_controller.rb:4:in `index'"
end
assert_includes last_response.body, "rails/railties/test/env_helpers.rb" assert_includes last_response.body, "rails/railties/test/env_helpers.rb"
end end
end end

@ -122,7 +122,7 @@ def test_runner_detects_syntax_errors
def test_runner_detects_bad_script_name def test_runner_detects_bad_script_name
output = rails("runner", "iuiqwiourowe", allow_failure: true) output = rails("runner", "iuiqwiourowe", allow_failure: true)
assert_not_predicate $?, :success? assert_not_predicate $?, :success?
assert_match "undefined local variable or method `iuiqwiourowe' for", output assert_match(/undefined local variable or method [`']iuiqwiourowe' for/, output)
end end
def test_environment_with_rails_env def test_environment_with_rails_env

@ -243,7 +243,7 @@ class Foo < Rails::Railtie
Foo.instance.abc Foo.instance.abc
end end
assert_match(/undefined method `abc' for.*RailtiesTest::RailtieTest::Foo/, error.original_message) assert_match(/undefined method [`']abc' for.*RailtiesTest::RailtieTest::Foo/, error.original_message)
end end
test "rake environment can be called in the ralitie" do test "rake environment can be called in the ralitie" do