timed-out memory leak child
(https://github.com/ruby/test-unit-ruby-core/pull/37)
* Close stdin before terminating a timed-out memory leak child
With a sample range, assert_no_memory_leak keeps the child's stdin open,
and the child's ensure clause reads a "continue" line from it after each
sample. When the timeout fires while the child is running the sampled
code, EnvUtil.terminate sends SIGTERM, and the child's ensure clause
then blocks reading stdin, which was closed only after terminate
returned. SIGTERM cannot end the child, so terminate escalates to
SIGABRT and then waits for the aborted child without a time limit. On
ruby/ruby's YJIT macOS CI jobs the child sometimes did not exit after
the RUBY_ON_BUG core dump, and the test worker hung until the runner
killed it 20 minutes later. In other runs the test passed but left a
crash report for the child.
Close stdin first. The child's read then returns nil, and its ensure
clause breaks out of the sample loop. The break discards the
SignalException raised by SIGTERM, so the child exits with status 0. If
SIGTERM arrives before the child reaches the sample loop, the child
still dies from SIGTERM.
The existing timeout test now also checks that the child exited
normally or by SIGTERM.
https://github.com/ruby/test-unit-ruby-core/commit/ce7a92e724
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
busybox lsof ignores the options and prints no header line, so the
FD/TYPE/NODE columns cannot be found and check_fd_leak crashed with
NoMethodError, which killed the test worker. Treat such lsof as
unavailable; leaked fds are still reported.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
prism is no longer a default gem (the built-in parser is Ruby::Prism
now), so add the prism gem to gems/bundled_gems. `require "prism"`
keeps providing the gem, which can be any version. lib/bundled_gems.rb
warns when it is required under bundler without a Gemfile entry, like
other gems promoted to bundled gems.
The gem is pinned to the upstream revision that ruby/ruby is synced
with, because the tests of the released 1.9.0 compare with the parser
of the running Ruby, whose grammar has moved on in 4.1.
prism does not keep its templated files in git, so BundledGem.build
(used by both common.mk and defs/gmake.mk) generates them before
building the gem from the checkout. Since the gem built from the
revision has the same version as the released one, BundledGem.unpack
now clears the target directory first; otherwise files removed upstream
(ext/prism/api_pack.c) remain from the released gem and break the
build.
tool/test-bundled-gems.rb: several bundled gems (irb, rdoc, rbs,
typeprof, ...) require prism, and their tests spawn ruby with
--disable=gems, where the prism gem is not loadable anymore. Preset its
load paths in RUBYLIB, as is already done for debug.
[Bug #22379]
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Worker#kill and quit_workers both need the test process rather than the runruby.rb process on Windows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The KILL sent when Process.waitall times out in quit_workers also hit only the runruby.rb process on Windows, leaving the test process running. It now targets only the workers still running, since the pids of the test processes that have exited may already be reused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Windows, exec in runruby.rb spawns the worker as another process and waits for it, so killing the launched pid left the worker running with the inherited stderr open, and GitHub Actions waited on it until the step timed out. The worker now reports its own pid, which is also the process the debugger should dump.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Git for Windows 2.56.0 dies with "unable to access 'NUL': Invalid argument" when GIT_CONFIG_SYSTEM or GIT_CONFIG_GLOBAL is `NUL`, which stops the revision.h generation on mswin. `/dev/null` is the value git documents for them, and Git for Windows has accepted it since 2.32, the first version that reads them.
https://github.com/git-for-windows/git/issues/6449
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test-bundled-gems runs the tests with the test-unit gem, which loads EnvUtil through gems/lib but never goes through tool/lib/test/unit.rb, so the scale was ignored there. `--timeout-scale` still takes precedence.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Extended Support for Solaris 9 ended in 2014. Solaris 10 and later have setenv and unsetenv, and libumem is always available, so the version specific fallbacks are no longer needed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Timeout.timeout in Ruby 2.3 takes at most two arguments, so EnvUtil.timeout always raised ArgumentError there. EnvUtil.terminate reaches it through Debugger#dump on Windows when gdb is installed, which broke test_terminate_reaps_exited_child on windows-latest 2.3.
https://github.com/ruby/test-unit-ruby-core/commit/42d15562b2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Windows, Process.kill raises Errno::ESRCH for a child that has exited but has not been reaped, so terminate returned a stale $? and left the child to ZombieHunter. TestIO#test_write_32bit_boundary hit this on Windows 11-arm, where the child finished writing after its timeout expired and before it was killed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A subprocess that expires its timeout reports which assertion it was and
the output it did manage to produce. Dropping that left nothing to go on
for the crash report timeouts on macOS CI.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Compaction has been suppressed on s390x since 2021 because the tests
randomly stalled past the 1800s output timeout. That no longer happens:
on s390x.rubyci.org these all pass individually, inside a full serial
test-all, and across a 100-run soak under CPU saturation, with GC.compact
and GC.verify_compaction_references both behaving correctly.
https://github.com/ruby/ruby/pull/5077
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
assert_linear_performance() picks CLOCK_THREAD_CPUTIME_ID when it is
available. That clock belongs to the native thread that reads it, and an
M:N thread moves between native threads, so two reads around one
measurement can come from different native threads and the elapsed time
comes out negative:
Expected 0 to be <= -2.2150775009999997.
Reproduced with RUBY_MN_THREADS=2, 1500 fork/waitpid pairs per run: the
Ruby thread changed native thread 1, 9 and 10 times over three runs, and
the clock went backwards 1, 5 and 6 times, by as much as 0.38s. The
default, =1 and =-1 never moved the main thread and never went backwards.
CLOCK_PROCESS_CPUTIME_ID stayed monotonic across 15 such migrations.
Prefer process CPU time when the M:N scheduler is on. This only changes
which clock the assertion uses; it does not relax the assertion.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
With RUBY_BOX=1 inherited from the environment, a child that clears or
overrides RUBYOPT, or that passes its own warning flags, prints the
experimental warning into the captured output and fails the assertions
on it. Tests that hand RUBY_BOX to the child themselves still see the
warning.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The -w it hands every child turns all the categories back on, so the
RUBYOPT setting test-all passes down never reaches them and a Ruby::Box
startup warning lands in output the assertions compare verbatim. The
switch is 2.7 or later, and this file is copied verbatim into
ruby/test-unit-ruby-core, which still runs on older rubies.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`EnvUtil::Debugger.search` runs each debugger to test whether it is
usable, and the probe output goes straight to the test output. On
Windows, the lldb bundled with Visual Studio BuildTools fails to
delay-load python311.dll and dumps an LLVM crash report over the test
progress line. Also cache a nil result so the failing probe does not
repeat on every timeout.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The test runner split -Idirectory on a hardcoded ":", so on Windows an
absolute path such as -IV:/foo/lib was broken into "V" and "/foo/lib".
The tail only resolved by accident when the current drive matched.
File::PATH_SEPARATOR is the right delimiter, matching ruby -I itself.
This is the same class of bug as the test-syntax-suggest -I fix in
commit 3c7a4c174f, which replaced ":" with $(PATH_SEPARATOR) in
common.mk. Makefiles only pass relative paths, so this stayed latent.
* Group test-bundler-parallel workers by recorded runtime
turbo_tests balances workers by a recorded runtime log only when invoked
as a bare `spec`; this build passes an absolute spec/bundler path, so it
grouped by file size and let the single heaviest file dominate the
wall-clock. A tool/ patch loaded from the recipe records per-file
runtimes and reorders the size-based grouper to use the previous run's
data, leaving the synced spec/bin/parallel_rspec untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Fix stale reference in runtime recorder comment
The launcher script it pointed at was replaced by the recipe calling
BundlerRuntimeGrouping.install! directly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Rescue LoadError when installing runtime grouping
install! is meant to degrade to filesize grouping on any failure, but
LoadError from the parallel_tests require is a ScriptError and escaped
the StandardError rescue, killing the whole recipe instead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Set RUBY_TEST_GC_VERIFY to call GC.verify_internal_consistency after each
test. An integer >= 2 samples once every N tests; any other non-empty value
verifies every test. Useful for pinning down which test corrupts the heap.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
spec/mspec/lib/mspec/helpers/tmp.rb also tries to remove SPEC_TEMP_DIR
at exit, before this `END`. At that time, the "tmp" marker directory
causes an ENOTEMPTY.
The sandbox marker created at the temp root is ours, so the END block
must remove it before Dir.rmdir(tmpdir); otherwise that call always
fails with ENOTEMPTY and a clean run looks like it left files behind.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bundler's spec helpers walk up from the working directory looking for a
`.bundle` directory, stopping at the first ancestor that contains a `tmp`
directory (Bundler::SharedHelpers#search_up). On Windows the test temp dir
lives under the user home (%LOCALAPPDATA%\Temp), so the search escaped the
sandbox and picked up the real ~/.bundle, breaking config, cache and other
specs. Create a `tmp` marker at the temp root so the search stops there.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The GitHub Actions part particularly is for ZJIT and YJIT jobs which
have a separate "Dump crash logs" fold. There doesn't seem to be a way
to ask for the web UI to unfold on loading of the logs.
Do not execute `git` command which won't work when building from a
tarball, to avoid the error message "fatal: not a git repository".
The default gemspecs do not need these file lists anyway.