From af5c4efdc0ac85a02f4d9ec7ccb187f0ea0746e7 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 12 Aug 2026 18:55:34 +0900 Subject: [PATCH 01/10] Sync test-unit-ruby-core --- tool/sync_default_gems.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index f07d1b3840e5da..4e87e12bc8b19d 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -305,6 +305,10 @@ def lib((upstream, branch), gemspec_in_subdir: false) ["test/zlib", "test/zlib"], ["zlib.gemspec", "ext/zlib/zlib.gemspec"], ]), + "test-unit-ruby-core":repo("ruby/test-unit-ruby-core", [ + ["lib", "tool/lib"], + ["test", "tool/test"], + ]), }.transform_keys(&:to_s) def REPOSITORIES.[](gem) From 1ca45d55803363a242626c59b87909d5c251aa4a Mon Sep 17 00:00:00 2001 From: Taketo Takashima Date: Mon, 10 Aug 2026 22:06:51 +0900 Subject: [PATCH 02/10] [ruby/test-unit-ruby-core] Fix SyntaxError: unexpected keyword_rescue, expecting keyword_end in Ruby 2.4 and earlier https://github.com/ruby/test-unit-ruby-core/commit/15d209a002 --- tool/lib/core_assertions.rb | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/tool/lib/core_assertions.rb b/tool/lib/core_assertions.rb index 5ca318a5989753..e1f8dda5a6d8de 100644 --- a/tool/lib/core_assertions.rb +++ b/tool/lib/core_assertions.rb @@ -388,21 +388,23 @@ def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **o assert(!abort, FailDesc[status, nil, stderr]) res.scan(/^\n(.*?)\n(?=<\/error id="#{token_re}">$)/m) do self._assertions += $1.to_i - res = Marshal.load($2.unpack1("m")) or next - rescue => marshal_error - ignore_stderr = nil - res = nil - else - next if SystemExit === res - if bt = res.backtrace - bt.each do |l| - l.sub!(/\A-:(\d+)/){"#{file}:#{line + $1.to_i}"} - end - bt.concat(caller) + begin + res = Marshal.load($2.unpack1("m")) or next + rescue => marshal_error + ignore_stderr = nil + res = nil else - res.set_backtrace(caller) + next if SystemExit === res + if bt = res.backtrace + bt.each do |l| + l.sub!(/\A-:(\d+)/){"#{file}:#{line + $1.to_i}"} + end + bt.concat(caller) + else + res.set_backtrace(caller) + end + raise res end - raise res end # really did it succeed? From b02fceb08eef596aed4d9b7de3070b4acfb33a6b Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 20 Aug 2026 10:48:33 +0900 Subject: [PATCH 03/10] [ruby/test-unit-ruby-core] Add tests https://github.com/ruby/test-unit-ruby-core/commit/11ea26ebcc --- tool/test/test_core_assertions.rb | 46 +++++++++++++++++++++++++++++++ tool/test/test_envutil.rb | 30 ++++++++++++++++++++ tool/test/test_find_executable.rb | 26 +++++++++++++++++ tool/test/test_memory_status.rb | 23 ++++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 tool/test/test_core_assertions.rb create mode 100644 tool/test/test_envutil.rb create mode 100644 tool/test/test_find_executable.rb create mode 100644 tool/test/test_memory_status.rb diff --git a/tool/test/test_core_assertions.rb b/tool/test/test_core_assertions.rb new file mode 100644 index 00000000000000..8293c8168142dd --- /dev/null +++ b/tool/test/test_core_assertions.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require "test/unit" +require "core_assertions" + +class TestCoreAssertions < Test::Unit::TestCase + include Test::Unit::CoreAssertions + + def test_backtrace_filter_handles_missing_backtrace + assert_equal(["No backtrace"], Test.filter_backtrace(nil)) + end + + def test_backtrace_filter_removes_internal_entries + backtrace = [ + "/tmp/example.rb:1:in `run'", + "/tmp/lib/test/unit.rb:2:in `assert'", + ] + + assert_equal([backtrace.first], Test.filter_backtrace(backtrace)) + end + + def test_message_adds_sentence_endings + object = Object.new + object.extend(Test::Unit::Assertions) + + message = object.message("details") { "default" } + + assert_equal("details.\ndefault.", message.call) + end + + def test_assert_separately_runs_assertions_in_child_ruby + assert_separately([], <<~RUBY) + assert_equal(4, 2 + 2) + RUBY + end + + def test_assert_separately_propagates_child_failure + error = assert_raise(Test::Unit::AssertionFailedError) do + assert_separately([], <<~RUBY) + assert_equal(:expected, :actual) + RUBY + end + + assert_match(/expected/, error.message) + end +end diff --git a/tool/test/test_envutil.rb b/tool/test/test_envutil.rb new file mode 100644 index 00000000000000..806df6ab059e67 --- /dev/null +++ b/tool/test/test_envutil.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require "test/unit" +require "envutil" + +class TestEnvUtil < Test::Unit::TestCase + def test_rubybin_points_to_a_ruby_executable + assert(File.executable?(EnvUtil.rubybin)) + end + + def test_apply_timeout_scale + original_scale = EnvUtil.timeout_scale + EnvUtil.timeout_scale = 2.5 + + assert_equal(5.0, EnvUtil.apply_timeout_scale(2)) + ensure + EnvUtil.timeout_scale = original_scale + end + + def test_invoke_ruby_captures_output_and_status + stdout, stderr, status = EnvUtil.invoke_ruby( + ["-e", "STDOUT.print('out'); STDERR.print('err')"], + "", true, true + ) + + assert_equal("out", stdout) + assert_equal("err", stderr) + assert_predicate(status, :success?) + end +end diff --git a/tool/test/test_find_executable.rb b/tool/test/test_find_executable.rb new file mode 100644 index 00000000000000..277279aca595e2 --- /dev/null +++ b/tool/test/test_find_executable.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require "test/unit" +require "rbconfig" +require "find_executable" + +class TestFindExecutable < Test::Unit::TestCase + def test_find_executable_returns_command_and_arguments + ruby = RbConfig.ruby + command = File.basename(ruby, RbConfig::CONFIG["EXEEXT"]) + original_path = ENV["PATH"] + ENV["PATH"] = File.dirname(ruby) + + found = EnvUtil.find_executable(command, "--version") do |output| + output.start_with?("ruby ") + end + + assert_equal([ruby, "--version"], found) + ensure + ENV["PATH"] = original_path + end + + def test_find_executable_returns_nil_for_unknown_command + assert_nil(EnvUtil.find_executable("test-unit-ruby-core-missing") { true }) + end +end diff --git a/tool/test/test_memory_status.rb b/tool/test/test_memory_status.rb new file mode 100644 index 00000000000000..53830e9a238f35 --- /dev/null +++ b/tool/test/test_memory_status.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require "test/unit" +require "memory_status" + +class TestMemoryStatus < Test::Unit::TestCase + def setup + omit("memory status is unsupported") unless defined?(Memory::Status) + end + + def test_status_reports_numeric_values + status = Memory::Status.new + + assert(status.members.any? { |member| status[member].to_i > 0 }) + assert_match(/\A\{[^}]+:\d+(?:,[^}]+:\d+)*\}\z/, status.to_s) + end + + def test_parse_round_trips_status + status = Memory::Status.new + + assert_equal(status, Memory::Status.parse(status.to_s)) + end +end From d9ce2875561dab3a84161bc52f19ced57ea3c737 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 20 Aug 2026 10:49:16 +0900 Subject: [PATCH 04/10] [ruby/test-unit-ruby-core] Fix errors on ruby 2.6 or earliers https://github.com/ruby/test-unit-ruby-core/commit/434734d9bb --- tool/lib/core_assertions.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tool/lib/core_assertions.rb b/tool/lib/core_assertions.rb index e1f8dda5a6d8de..fed1e2f6e30d61 100644 --- a/tool/lib/core_assertions.rb +++ b/tool/lib/core_assertions.rb @@ -327,8 +327,8 @@ def separated_runner(token, out = nil) at_exit { assertions = assertions_ivar_get.call(:@_assertions) out_write.call <<~OUT - - #{array_pack.bind_call([marshal_dump.call($!)], 'm0')} + + #{array_pack.bind([marshal_dump.call($!)]).call('m0')} OUT } From 2951d70c900f7c6e9755b4d57c846ae77bcf184c Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 20 Aug 2026 12:17:41 +0900 Subject: [PATCH 05/10] [ruby/test-unit-ruby-core] Use `Regexp#=~` for ruby 2.3 `Regexp#match?` was introduced at ruby 2.4. https://github.com/ruby/test-unit-ruby-core/commit/11c19e865b --- tool/lib/core_assertions.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tool/lib/core_assertions.rb b/tool/lib/core_assertions.rb index fed1e2f6e30d61..30b25dde408abc 100644 --- a/tool/lib/core_assertions.rb +++ b/tool/lib/core_assertions.rb @@ -18,11 +18,11 @@ def filter bt unless $DEBUG then bt.each do |line| - break if pattern.match?(line) + break if pattern =~ line new_bt << line end - new_bt = bt.reject { |line| pattern.match?(line) } if new_bt.empty? + new_bt = bt.reject { |line| pattern =~ line } if new_bt.empty? new_bt = bt.dup if new_bt.empty? else new_bt = bt.dup From f76b9efac9d153bcd93a56f7b590873d03a14f50 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 20 Aug 2026 14:27:08 +0900 Subject: [PATCH 06/10] [ruby/test-unit-ruby-core] Fix for rubies older than 2.7 https://github.com/ruby/test-unit-ruby-core/commit/c53dddaa9c --- tool/lib/core_assertions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/lib/core_assertions.rb b/tool/lib/core_assertions.rb index 30b25dde408abc..cd6cd1d6a6d31e 100644 --- a/tool/lib/core_assertions.rb +++ b/tool/lib/core_assertions.rb @@ -365,7 +365,7 @@ def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **o args.insert((Hash === args.first ? 1 : 0), "-w", "--disable=gems", *$:.map {|l| "-I#{l}"}) args << "--debug" if RUBY_ENGINE == 'jruby' # warning: tracing (e.g. set_trace_func) will not capture all events without --debug flag # power_assert 3 requires ruby 3.1 or later - args << "-W:no-experimental" if RUBY_VERSION < "3.1." + args << "-W:no-experimental" if ("2.7."..."3.1.").cover?(RUBY_VERSION) stdout, stderr, status = EnvUtil.invoke_ruby(args, src, capture_stdout, true, **opt) if sanitizers&.lsan_enabled? From 2b0dfbe6902159f0feade1460e57ff1421b7d067 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 20 Aug 2026 11:40:32 +0900 Subject: [PATCH 07/10] Load bundler/setup explicitly in the bundle exec -r spec gem_prelude no longer consumes BUNDLER_SETUP in the main box once the decorator gems are autoloaded, and command-line -r features are required before RUBYOPT's, so the bundled gems warning no longer fires for a plain -ropenssl. Request bundler/setup ahead of it to keep exercising the warning for requires without a Ruby caller frame. Co-Authored-By: Claude Fable 5 --- spec/bundled_gems_spec.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spec/bundled_gems_spec.rb b/spec/bundled_gems_spec.rb index dc0e7dde6c7bfc..d56b0d2011d9f7 100644 --- a/spec/bundled_gems_spec.rb +++ b/spec/bundled_gems_spec.rb @@ -203,7 +203,11 @@ def script(code, options = {}) it "Show warning when bundle exec with -r option" do create_file("stub.rb", stub_code) create_file("Gemfile", "source 'https://rubygems.org'") - bundle "exec ruby -r./stub -ropenssl -e ''" + # Command-line -r features are required before RUBYOPT's -rbundler/setup, + # and gem_prelude no longer consumes BUNDLER_SETUP in the main box, so + # bundler/setup must be requested explicitly ahead of the bundled gem to + # exercise the warning for requires without a Ruby caller frame. + bundle "exec ruby -rbundler/setup -r./stub -ropenssl -e ''" expect(err).to include(/openssl used to be loaded from (.*) since Ruby #{RUBY_VERSION}/) end From f62baa9827b70cd5eec40f45cc277f9dc9dab2d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=84=9C=EC=9A=B8=EB=AF=BC=ED=8A=B8=EC=B4=88=EC=BD=94?= Date: Thu, 20 Aug 2026 15:50:08 +0900 Subject: [PATCH 08/10] [DOC] Correct wording and formatting issues in contributing documentation --- doc/contributing/building_ruby.md | 2 +- doc/contributing/documentation_guide.md | 2 +- doc/contributing/making_changes_to_ruby.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/contributing/building_ruby.md b/doc/contributing/building_ruby.md index a283a2f3dbd3ef..956ea5f325daa0 100644 --- a/doc/contributing/building_ruby.md +++ b/doc/contributing/building_ruby.md @@ -298,7 +298,7 @@ The compiled Ruby will now automatically crash with a report and a backtrace if ASAN detects a memory safety issue. To run Ruby's test suite under ASAN, issue the following command. Note that this will take quite a long time (over two hours on my laptop); the `RUBY_TEST_TIMEOUT_SCALE` and -`SYNTAX_SUGEST_TIMEOUT` variables are required to make sure tests don't +`SYNTAX_SUGGEST_TIMEOUT` variables are required to make sure tests don't spuriously fail with timeouts when in fact they're just slow. ```sh diff --git a/doc/contributing/documentation_guide.md b/doc/contributing/documentation_guide.md index d0c29e2e76d98e..7a28acdf4dc297 100644 --- a/doc/contributing/documentation_guide.md +++ b/doc/contributing/documentation_guide.md @@ -347,7 +347,7 @@ Alternatives: - Example {source}[https://github.com/ruby/ruby/blob/34d802f32f00df1ac0220b62f72605827c16bad8/file.c#L6570-L6596]. - Corresponding {output}[rdoc-ref:File@ReadWrite+Mode]. -- (Markdown format only): A {Github Flavored Markdown (GFM) table}[https://github.github.com/gfm/#tables-extension-], +- (Markdown format only): A {GitHub Flavored Markdown (GFM) table}[https://github.github.com/gfm/#tables-extension-], using special formatting for the text: - Example {source}[https://github.com/ruby/ruby/blob/34d802f32f00df1ac0220b62f72605827c16bad8/doc/contributing/glossary.md?plain=1]. diff --git a/doc/contributing/making_changes_to_ruby.md b/doc/contributing/making_changes_to_ruby.md index 260fadb7e34b96..4a7aecea0337ab 100644 --- a/doc/contributing/making_changes_to_ruby.md +++ b/doc/contributing/making_changes_to_ruby.md @@ -23,6 +23,6 @@ Use the following style for commit messages: ## CI -GitHub actions will run on each pull request. +GitHub Actions will run on each pull request. There is [a CI that runs on master](https://rubyci.org/). It has broad coverage of different systems and architectures, such as Solaris SPARC and macOS. From 43748e917fefa400b794af41c02101cf5b2af447 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 20 Aug 2026 18:27:11 +0900 Subject: [PATCH 09/10] Raise subprocess timeout in TestArgf#test_inplace_bug_17117 The test allocates one million strings in a subprocess, which normally finishes in under a second but can exceed the default 10s (scaled) limit on overloaded CI runners, as seen on the macOS repeat-count=2 job. https://github.com/ruby/ruby/actions/runs/32345364008/job/96352871123 Co-Authored-By: Claude Fable 5 --- test/ruby/test_argf.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ruby/test_argf.rb b/test/ruby/test_argf.rb index 55a06296aa7275..3c8959acb92446 100644 --- a/test/ruby/test_argf.rb +++ b/test/ruby/test_argf.rb @@ -383,7 +383,7 @@ def test_inplace_suffix_encoding end def test_inplace_bug_17117 - assert_in_out_err(["-", @t1.path], "#{<<~"{#"}#{<<~'};'}") + assert_in_out_err(["-", @t1.path], "#{<<~"{#"}#{<<~'};'}", timeout: 60) {# #!/usr/bin/ruby -pi.bak BEGIN { From 9272524d00e3171c30ad65a5e442024ddfd3824b Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 20 Aug 2026 19:34:20 +0900 Subject: [PATCH 10/10] Keep the original PATH in the EnvUtil.find_executable test Replacing PATH with only the build directory breaks the spawned ruby.exe on MSYS2 UCRT64, where the Windows DLL loader needs PATH to resolve libgmp-10.dll linked into the ruby DLL. Prepend the directory instead so the build ruby is still found first. Co-Authored-By: Claude Fable 5 --- tool/test/test_find_executable.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/test/test_find_executable.rb b/tool/test/test_find_executable.rb index 277279aca595e2..8ebaf85db24910 100644 --- a/tool/test/test_find_executable.rb +++ b/tool/test/test_find_executable.rb @@ -9,7 +9,7 @@ def test_find_executable_returns_command_and_arguments ruby = RbConfig.ruby command = File.basename(ruby, RbConfig::CONFIG["EXEEXT"]) original_path = ENV["PATH"] - ENV["PATH"] = File.dirname(ruby) + ENV["PATH"] = File.dirname(ruby) + File::PATH_SEPARATOR + original_path found = EnvUtil.find_executable(command, "--version") do |output| output.start_with?("ruby ")