From 11c8bad64b31c125b903547bb3eed0ede8f0f8e2 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Fri, 15 Aug 2025 18:02:03 -0400 Subject: [PATCH 01/60] CI: Also show errors in rust-warnings.yml [ci skip] At under a minute, this check runs faster than a lot of the other CI checks, so we might as well show errors from `cargo check` to serve as a smoke check in addition to surfacing warnings. --- .github/workflows/rust-warnings.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust-warnings.yml b/.github/workflows/rust-warnings.yml index a8840c28e4c30c..8fd78ea04bc97d 100644 --- a/.github/workflows/rust-warnings.yml +++ b/.github/workflows/rust-warnings.yml @@ -42,10 +42,10 @@ jobs: run: rustup default beta - name: Rust warnings + shell: bash run: | - set -euo pipefail + set -eu cargo check --quiet --all-features --message-format=json \ - | jq -r 'select(.reason == "compiler-message" and .message.level == "warning") | .message.rendered' \ - > warnings.txt - cat warnings.txt - ! grep --quiet '[^[:space:]]' warnings.txt + | jq -r 'select(.message.level == "warning" or .message.level == "error") | .message.rendered' \ + | tee messages.txt + (exit "${PIPESTATUS[0]}") && ! grep --quiet '[^[:space:]]' messages.txt From 26776ee91ab6045cc43c11b8022651572ad01e4c Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Fri, 15 Aug 2025 17:07:18 -0700 Subject: [PATCH 02/60] ZJIT: Remove unused HIR test code (#14248) --- zjit/src/hir.rs | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index c88965f891ac59..20faf5398ce6f0 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -3798,40 +3798,6 @@ mod tests { use super::*; use expect_test::{expect, Expect}; - #[macro_export] - macro_rules! assert_matches { - ( $x:expr, $pat:pat ) => { - { - let val = $x; - if (!matches!(val, $pat)) { - eprintln!("{} ({:?}) does not match pattern {}", stringify!($x), val, stringify!($pat)); - assert!(false); - } - } - }; - } - - - #[track_caller] - fn assert_matches_value(insn: Option<&Insn>, val: VALUE) { - match insn { - Some(Insn::Const { val: Const::Value(spec) }) => { - assert_eq!(*spec, val); - } - _ => assert!(false, "Expected Const {val}, found {insn:?}"), - } - } - - #[track_caller] - fn assert_matches_const(insn: Option<&Insn>, expected: Const) { - match insn { - Some(Insn::Const { val }) => { - assert_eq!(*val, expected, "{val:?} does not match {expected:?}"); - } - _ => assert!(false, "Expected Const {expected:?}, found {insn:?}"), - } - } - #[track_caller] fn assert_method_hir(method: &str, hir: Expect) { let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("self", method)); From 7f398a38e37c109babde89475a75b51308044f4d Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Tue, 22 Jul 2025 19:11:18 -0400 Subject: [PATCH 03/60] ZJIT: Make `opnd!()` work on both `&InsnId` and `InsnId` --- zjit/src/codegen.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index cba25f9cdb85e7..50ca8fbdbd7fe6 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -313,7 +313,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio // Convert InsnId to lir::Opnd macro_rules! opnd { ($insn_id:ident) => { - jit.get_opnd(*$insn_id)? + jit.get_opnd($insn_id.clone())? }; } From db3d82bcd2f2c7ed41e3c2cb138d2b432b1a224e Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Tue, 22 Jul 2025 19:12:03 -0400 Subject: [PATCH 04/60] ZJIT: Guide WB skipping for Insn::SetLocal using HIR type info --- zjit/src/codegen.rs | 24 +++++++++++------------- zjit/src/hir.rs | 2 +- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 50ca8fbdbd7fe6..a58950ab9acb4b 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -370,7 +370,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::SetGlobal { id, val, state } => return gen_setglobal(jit, asm, *id, opnd!(val), &function.frame_state(*state)), Insn::GetGlobal { id, state: _ } => gen_getglobal(asm, *id), &Insn::GetLocal { ep_offset, level } => gen_getlocal_with_ep(asm, ep_offset, level)?, - Insn::SetLocal { val, ep_offset, level } => return gen_setlocal_with_ep(asm, opnd!(val), *ep_offset, *level), + &Insn::SetLocal { val, ep_offset, level } => return gen_setlocal_with_ep(asm, jit, function, val, ep_offset, level), Insn::GetConstantPath { ic, state } => gen_get_constant_path(jit, asm, *ic, &function.frame_state(*state))?, Insn::SetIvar { self_val, id, val, state: _ } => return gen_setivar(asm, opnd!(self_val), *id, opnd!(val)), Insn::SideExit { state, reason } => return gen_side_exit(jit, asm, reason, &function.frame_state(*state)), @@ -507,21 +507,19 @@ fn gen_getlocal_with_ep(asm: &mut Assembler, local_ep_offset: u32, level: u32) - /// Set a local variable from a higher scope or the heap. `local_ep_offset` is in number of VALUEs. /// We generate this instruction with level=0 only when the local variable is on the heap, so we /// can't optimize the level=0 case using the SP register. -fn gen_setlocal_with_ep(asm: &mut Assembler, val: Opnd, local_ep_offset: u32, level: u32) -> Option<()> { +fn gen_setlocal_with_ep(asm: &mut Assembler, jit: &JITState, function: &Function, val: InsnId, local_ep_offset: u32, level: u32) -> Option<()> { let ep = gen_get_ep(asm, level); - match val { - // If we're writing a constant, non-heap VALUE, do a raw memory write without - // running write barrier. - lir::Opnd::Value(const_val) if const_val.special_const_p() => { - let offset = -(SIZEOF_VALUE_I32 * i32::try_from(local_ep_offset).ok()?); - asm.mov(Opnd::mem(64, ep, offset), val); - } + + // When we've proved that we're writing an immediate, + // we can skip the write barrier. + if function.type_of(val).is_immediate() { + let offset = -(SIZEOF_VALUE_I32 * i32::try_from(local_ep_offset).ok()?); + asm.mov(Opnd::mem(64, ep, offset), jit.get_opnd(val)?); + } else { // We're potentially writing a reference to an IMEMO/env object, // so take care of the write barrier with a function. - _ => { - let local_index = c_int::try_from(local_ep_offset).ok().and_then(|idx| idx.checked_mul(-1))?; - asm_ccall!(asm, rb_vm_env_write, ep, local_index.into(), val); - } + let local_index = c_int::try_from(local_ep_offset).ok().and_then(|idx| idx.checked_mul(-1))?; + asm_ccall!(asm, rb_vm_env_write, ep, local_index.into(), jit.get_opnd(val)?); } Some(()) } diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 20faf5398ce6f0..c50c1ce985a69c 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -1264,7 +1264,7 @@ impl Function { self.union_find.borrow_mut().make_equal_to(insn, replacement); } - fn type_of(&self, insn: InsnId) -> Type { + pub fn type_of(&self, insn: InsnId) -> Type { assert!(self.insns[insn.0].has_output()); self.insn_types[self.union_find.borrow_mut().find(insn).0] } From 87c4ebd00115b9424f04813b4dd5f3d087a14eb9 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Fri, 15 Aug 2025 21:15:45 -0400 Subject: [PATCH 05/60] Fix tests using assert_raise_with_message on US-ASCII systems On systems where the Encoding.default_internal defaults to US-ASCII instead of UTF-8, some tests using assert_raise_with_message can fail since it no longer changes Encoding.default_internal in 79f5202. This tests explicitly uses EnvUtil.with_default_internal on systems where these tests fail. --- test/-ext-/symbol/test_type.rb | 16 ++++++++++------ test/ruby/test_class.rb | 8 +++++--- test/ruby/test_float.rb | 4 +++- test/ruby/test_integer.rb | 4 +++- test/ruby/test_method.rb | 13 +++++++++---- test/ruby/test_module.rb | 5 ++++- test/ruby/test_numeric.rb | 20 ++++++++++++-------- test/ruby/test_process.rb | 9 +++++++-- test/ruby/test_rational.rb | 10 +++++++--- 9 files changed, 60 insertions(+), 29 deletions(-) diff --git a/test/-ext-/symbol/test_type.rb b/test/-ext-/symbol/test_type.rb index 2b0fbe5b798b93..ed019062faf218 100644 --- a/test/-ext-/symbol/test_type.rb +++ b/test/-ext-/symbol/test_type.rb @@ -123,16 +123,20 @@ def test_attrset def test_check_id_invalid_type cx = EnvUtil.labeled_class("X\u{1f431}") - assert_raise_with_message(TypeError, /X\u{1F431}/) { - Bug::Symbol.pinneddown?(cx) - } + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(TypeError, /X\u{1F431}/) { + Bug::Symbol.pinneddown?(cx) + } + end end def test_check_symbol_invalid_type cx = EnvUtil.labeled_class("X\u{1f431}") - assert_raise_with_message(TypeError, /X\u{1F431}/) { - Bug::Symbol.find(cx) - } + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(TypeError, /X\u{1F431}/) { + Bug::Symbol.find(cx) + } + end end def test_const_name_type diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index 9a34a81334f5ba..74541bba3f7d87 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -696,9 +696,11 @@ def xyzzy def test_namescope_error_message m = Module.new o = m.module_eval "class A\u{3042}; self; end.new" - assert_raise_with_message(TypeError, /A\u{3042}/) { - o::Foo - } + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(TypeError, /A\u{3042}/) { + o::Foo + } + end end def test_redefinition_mismatch diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb index b865d339a96341..d0d180593ab272 100644 --- a/test/ruby/test_float.rb +++ b/test/ruby/test_float.rb @@ -861,7 +861,9 @@ def o.to_f; inf = Float::INFINITY; inf/inf; end assert_raise(Encoding::CompatibilityError) {Float("0".encode("utf-32le"))} assert_raise(Encoding::CompatibilityError) {Float("0".encode("iso-2022-jp"))} - assert_raise_with_message(ArgumentError, /\u{1f4a1}/) {Float("\u{1f4a1}")} + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(ArgumentError, /\u{1f4a1}/) {Float("\u{1f4a1}")} + end end def test_invalid_str diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb index fb7aabba358b21..f9bf4fa20c80c1 100644 --- a/test/ruby/test_integer.rb +++ b/test/ruby/test_integer.rb @@ -158,7 +158,9 @@ def obj.to_i; "str"; end assert_raise(Encoding::CompatibilityError, bug6192) {Integer("0".encode("utf-32le"))} assert_raise(Encoding::CompatibilityError, bug6192) {Integer("0".encode("iso-2022-jp"))} - assert_raise_with_message(ArgumentError, /\u{1f4a1}/) {Integer("\u{1f4a1}")} + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(ArgumentError, /\u{1f4a1}/) {Integer("\u{1f4a1}")} + end obj = Struct.new(:s).new(%w[42 not-an-integer]) def obj.to_str; s.shift; end diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb index 08f794fa0eab14..8561f841a8cda6 100644 --- a/test/ruby/test_method.rb +++ b/test/ruby/test_method.rb @@ -284,8 +284,10 @@ def o.bar; end assert_raise(TypeError) { m.bind(Object.new) } cx = EnvUtil.labeled_class("X\u{1f431}") - assert_raise_with_message(TypeError, /X\u{1f431}/) do - o.method(cx) + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(TypeError, /X\u{1f431}/) do + o.method(cx) + end end end @@ -315,9 +317,12 @@ def o.bar; :bar; end assert_raise(TypeError) do Class.new.class_eval { define_method(:bar, o.method(:bar)) } end + cx = EnvUtil.labeled_class("X\u{1f431}") - assert_raise_with_message(TypeError, /X\u{1F431}/) do - Class.new {define_method(cx) {}} + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(TypeError, /X\u{1F431}/) do + Class.new {define_method(cx) {}} + end end end diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index 9a21113fe06741..bc8583b475acfa 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -1291,8 +1291,11 @@ def test_const_set_invalid_name assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-16le"), :foo) } assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-32be"), :foo) } assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-32le"), :foo) } + cx = EnvUtil.labeled_class("X\u{3042}") - assert_raise_with_message(TypeError, /X\u{3042}/) { c1.const_set(cx, :foo) } + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(TypeError, /X\u{3042}/) { c1.const_set(cx, :foo) } + end end def test_const_get_invalid_name diff --git a/test/ruby/test_numeric.rb b/test/ruby/test_numeric.rb index cf408fac0af411..3bf93ef20dc0bd 100644 --- a/test/ruby/test_numeric.rb +++ b/test/ruby/test_numeric.rb @@ -18,20 +18,24 @@ def test_coerce assert_raise_with_message(TypeError, /can't be coerced into /) {1|:foo} assert_raise_with_message(TypeError, /can't be coerced into /) {1^:foo} - assert_raise_with_message(TypeError, /:\u{3042}/) {1+:"\u{3042}"} - assert_raise_with_message(TypeError, /:\u{3042}/) {1&:"\u{3042}"} - assert_raise_with_message(TypeError, /:\u{3042}/) {1|:"\u{3042}"} - assert_raise_with_message(TypeError, /:\u{3042}/) {1^:"\u{3042}"} + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(TypeError, /:\u{3042}/) {1+:"\u{3042}"} + assert_raise_with_message(TypeError, /:\u{3042}/) {1&:"\u{3042}"} + assert_raise_with_message(TypeError, /:\u{3042}/) {1|:"\u{3042}"} + assert_raise_with_message(TypeError, /:\u{3042}/) {1^:"\u{3042}"} + + assert_raise_with_message(TypeError, /:\u{3044}/) {1+"\u{3044}".to_sym} + assert_raise_with_message(TypeError, /:\u{3044}/) {1&"\u{3044}".to_sym} + assert_raise_with_message(TypeError, /:\u{3044}/) {1|"\u{3044}".to_sym} + assert_raise_with_message(TypeError, /:\u{3044}/) {1^"\u{3044}".to_sym} + end + EnvUtil.with_default_internal(Encoding::US_ASCII) do assert_raise_with_message(TypeError, /:"\\u3042"/) {1+:"\u{3042}"} assert_raise_with_message(TypeError, /:"\\u3042"/) {1&:"\u{3042}"} assert_raise_with_message(TypeError, /:"\\u3042"/) {1|:"\u{3042}"} assert_raise_with_message(TypeError, /:"\\u3042"/) {1^:"\u{3042}"} end - assert_raise_with_message(TypeError, /:\u{3044}/) {1+"\u{3044}".to_sym} - assert_raise_with_message(TypeError, /:\u{3044}/) {1&"\u{3044}".to_sym} - assert_raise_with_message(TypeError, /:\u{3044}/) {1|"\u{3044}".to_sym} - assert_raise_with_message(TypeError, /:\u{3044}/) {1^"\u{3044}".to_sym} bug10711 = '[ruby-core:67405] [Bug #10711]' exp = "1.2 can't be coerced into Integer" diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index f1894ab0c30f8a..221ff37c6b6946 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -114,14 +114,19 @@ def test_rlimit_name } assert_raise(ArgumentError) { Process.getrlimit(:FOO) } assert_raise(ArgumentError) { Process.getrlimit("FOO") } - assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { Process.getrlimit("\u{30eb 30d3 30fc}") } + + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { Process.getrlimit("\u{30eb 30d3 30fc}") } + end end def test_rlimit_value return unless rlimit_exist? assert_raise(ArgumentError) { Process.setrlimit(:FOO, 0) } assert_raise(ArgumentError) { Process.setrlimit(:CORE, :FOO) } - assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { Process.setrlimit("\u{30eb 30d3 30fc}", 0) } + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { Process.setrlimit("\u{30eb 30d3 30fc}", 0) } + end assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { Process.setrlimit(:CORE, "\u{30eb 30d3 30fc}") } with_tmpchdir do s = run_in_child(<<-'End') diff --git a/test/ruby/test_rational.rb b/test/ruby/test_rational.rb index 89bb7b20a8c696..e0edbde4637c44 100644 --- a/test/ruby/test_rational.rb +++ b/test/ruby/test_rational.rb @@ -117,9 +117,13 @@ def test_conv assert_equal(Rational(111, 1000), Rational('1.11e-1')) assert_raise(TypeError){Rational(nil)} assert_raise(ArgumentError){Rational('')} - assert_raise_with_message(ArgumentError, /\u{221a 2668}/) { - Rational("\u{221a 2668}") - } + + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(ArgumentError, /\u{221a 2668}/) { + Rational("\u{221a 2668}") + } + end + assert_warning('') { assert_predicate(Rational('1e-99999999999999999999'), :zero?) } From a79600db93820fdeec60400592e8f5a5bd1c9d85 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 16 Aug 2025 21:30:19 +0900 Subject: [PATCH 06/60] CI: windows: Use possibly faster device for TMP/TEMP --- .github/workflows/windows.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 9137e5298a9b1b..e853471b05a997 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -113,8 +113,8 @@ jobs: -arch=${{ matrix.target || 'amd64' }} ^ ${{ matrix.vcvars && '-vcvars_ver=' || '' }}${{ matrix.vcvars }} nmake -f nul - set TMP=%USERPROFILE%\AppData\Local\Temp - set TEMP=%USERPROFILE%\AppData\Local\Temp + set TMP=%RUNNER_TEMP% + set TEMP=%RUNNER_TEMP% set MAKEFLAGS=l set /a TEST_JOBS=(15 * %NUMBER_OF_PROCESSORS% / 10) > nul set RUBY_OPT_DIR=%GITHUB_WORKSPACE:\=/%/src/vcpkg_installed/%VCPKG_DEFAULT_TRIPLET% From 4d0836d928839ff13580f5e5b74a40b3914d9f59 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 16 Aug 2025 21:38:03 +0900 Subject: [PATCH 07/60] CI: windows: Skip rebuilding vcpkg packages when cache restored --- .github/workflows/windows.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index e853471b05a997..0b7bf25d95ff0b 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -85,6 +85,7 @@ jobs: shell: pwsh - name: Restore vcpkg artifact + id: restore-vcpkg uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: src\vcpkg_installed @@ -94,13 +95,14 @@ jobs: run: | vcpkg install --vcpkg-root=%USERPROFILE%\scoop\apps\vcpkg\current working-directory: src + if: ${{ ! steps.restore-vcpkg.outputs.cache-hit }} - name: Save vcpkg artifact uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: src\vcpkg_installed key: windows-${{ matrix.os }}-vcpkg-${{ hashFiles('src/vcpkg.json') }} - if: ${{ github.ref_name == 'master' || startsWith(github.ref_name, 'ruby_') }} + if: ${{ ! steps.restore-vcpkg.outputs.cache-hit && (github.ref_name == 'master' || startsWith(github.ref_name, 'ruby_')) }} - name: setup env # Available Ruby versions: https://github.com/actions/runner-images/blob/main/images/windows/Windows2019-Readme.md#ruby From d96991cc37b9c73372a8d0a61c5d83a2ce5b0cbe Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Sat, 16 Aug 2025 11:18:33 -0500 Subject: [PATCH 08/60] [DOC] Tweaks for String#hex --- string.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/string.c b/string.c index 96a9f96bd3432b..73b5bf83fcf283 100644 --- a/string.c +++ b/string.c @@ -10681,18 +10681,21 @@ rb_str_scan(VALUE str, VALUE pat) * call-seq: * hex -> integer * - * Interprets the leading substring of +self+ as a string of hexadecimal digits - * (with an optional sign and an optional 0x) and returns the - * corresponding number; - * returns zero if there is no such leading substring: + * Interprets the leading substring of +self+ as hexadecimal; + * returns its integer value: + * + * '0xFFFF'.hex # => 65535 + * 'FFzzzFF'.hex # => 255 # Hex ends at first non-hex character, 'z'. + * 'ffzzzFF'.hex # => 255 # Case does not matter. + * '-FFzzzFF'.hex # => -255 # May have leading '-'. + * '0xFFzzzFF'.hex # => 255 # May have leading '0x'. + * '-0xFFzzzFF'.hex # => -255 # May have leading '-0x'. * - * '0x0a'.hex # => 10 - * '-1234'.hex # => -4660 - * '0'.hex # => 0 - * 'non-numeric'.hex # => 0 + * Returns zero if there is no such leading substring: * - * Related: String#oct. + * 'zzz'.hex # => 0 * + * Related: See {Converting to Non-String}[rdoc-ref:String@Converting+to+Non--5CString]. */ static VALUE From 9b40837bb086c69b98d33457a967434fd36586fe Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Sat, 16 Aug 2025 11:43:49 -0500 Subject: [PATCH 09/60] [DOC] Tweaks for String#include? --- string.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/string.c b/string.c index 73b5bf83fcf283..0ed7b74c44cd9f 100644 --- a/string.c +++ b/string.c @@ -7083,13 +7083,17 @@ rb_str_reverse_bang(VALUE str) * call-seq: * include?(other_string) -> true or false * - * Returns +true+ if +self+ contains +other_string+, +false+ otherwise: + * Returns whether +self+ contains +other_string+: * - * s = 'foo' - * s.include?('f') # => true - * s.include?('fo') # => true - * s.include?('food') # => false + * s = 'bar' + * s.include?('ba') # => true + * s.include?('ar') # => true + * s.include?('bar') # => true + * s.include?('a') # => true + * s.include?('') # => true + * s.include?('foo') # => false * + * Related: see {Querying}[rdoc-ref:String@Querying]. */ VALUE From e0b2e2faf730ddfa83b7405f11f2ffad9a6972b4 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Sat, 16 Aug 2025 12:38:38 -0500 Subject: [PATCH 10/60] [DOC] Tweaks for String#index --- doc/string/index.rdoc | 32 ++++++++++++++++++-------------- string.c | 3 +-- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/doc/string/index.rdoc b/doc/string/index.rdoc index ce09a37bdfae27..cc34bc68e6b6bd 100644 --- a/doc/string/index.rdoc +++ b/doc/string/index.rdoc @@ -1,31 +1,35 @@ -Returns the integer index of the first match for the given argument, -or +nil+ if none found; -the search of +self+ is forward, and begins at position +offset+ (in characters). +Returns the integer position of the first substring that matches the given argument +pattern+, +or +nil+ if none found. -With string argument +substring+, +When +pattern+ is a string, returns the index of the first matching substring in +self+: 'foo'.index('f') # => 0 'foo'.index('o') # => 1 'foo'.index('oo') # => 1 'foo'.index('ooo') # => nil - 'тест'.index('с') # => 2 - 'こんにちは'.index('ち') # => 3 + 'тест'.index('с') # => 2 # Characters, not bytes. + 'こんにちは'.index('ち') # => 3 -With Regexp argument +regexp+, returns the index of the first match in +self+: +When +pattern is a Regexp, returns the index of the first match in +self+: 'foo'.index(/o./) # => 1 'foo'.index(/.o/) # => 0 -With positive integer +offset+, begins the search at position +offset+: +When +offset+ is non-negative, begins the search at position +offset+; +the returned index is relative to the beginning of +self+: - 'foo'.index('o', 1) # => 1 - 'foo'.index('o', 2) # => 2 - 'foo'.index('o', 3) # => nil + 'bar'.index('r', 0) # => 2 + 'bar'.index('r', 1) # => 2 + 'bar'.index('r', 2) # => 2 + 'bar'.index('r', 3) # => nil + 'bar'.index(/[r-z]/, 0) # => 2 'тест'.index('с', 1) # => 2 - 'こんにちは'.index('ち', 2) # => 3 + 'тест'.index('с', 2) # => 2 + 'тест'.index('с', 3) # => nil # Offset in characters, not bytes. + 'こんにちは'.index('ち', 2) # => 3 -With negative integer +offset+, selects the search position by counting backward +With negative integer argument +offset+, selects the search position by counting backward from the end of +self+: 'foo'.index('o', -1) # => 2 @@ -35,4 +39,4 @@ from the end of +self+: 'foo'.index(/o./, -2) # => 1 'foo'.index(/.o/, -2) # => 1 -Related: String#rindex. +Related: see {Querying}[rdoc-ref:String@Querying]. diff --git a/string.c b/string.c index 0ed7b74c44cd9f..d9d8678c71a748 100644 --- a/string.c +++ b/string.c @@ -4501,8 +4501,7 @@ rb_strseq_index(VALUE str, VALUE sub, long offset, int in_byte) /* * call-seq: - * index(substring, offset = 0) -> integer or nil - * index(regexp, offset = 0) -> integer or nil + * index(pattern, offset = 0) -> integer or nil * * :include: doc/string/index.rdoc * From ad047459c195d63190b2d6edee826573a74ed91e Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Sat, 16 Aug 2025 16:08:11 -0500 Subject: [PATCH 11/60] [DOC] Tweaks for GC.stat_heap --- gc.rb | 145 +++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 113 insertions(+), 32 deletions(-) diff --git a/gc.rb b/gc.rb index 883206dde85323..f5e62eed6058fa 100644 --- a/gc.rb +++ b/gc.rb @@ -252,61 +252,142 @@ def self.stat hash_or_key = nil end # call-seq: - # GC.stat_heap -> Hash - # GC.stat_heap(nil, hash) -> Hash - # GC.stat_heap(heap_name) -> Hash - # GC.stat_heap(heap_name, hash) -> Hash - # GC.stat_heap(heap_name, :key) -> Numeric + # GC.stat_heap -> new_hash + # GC.stat_heap(heap_id) -> new_hash + # GC.stat_heap(heap_id, key) -> value + # GC.stat_heap(nil, hash) -> hash + # GC.stat_heap(heap_id, hash) -> hash # - # Returns information for heaps in the \GC. + # This method is implementation-specific to CRuby. # - # If the first optional argument, +heap_name+, is passed in and not +nil+, it - # returns a +Hash+ containing information about the particular heap. - # Otherwise, it will return a +Hash+ with heap names as keys and - # a +Hash+ containing information about the heap as values. + # Returns statistics for \GC heaps. + # The particular statistics are implementation-specific + # and may change in the future without notice. # - # If the second optional argument, +hash_or_key+, is given as a +Hash+, it will - # be overwritten and returned. This is intended to avoid the probe effect. + # With no argument given, returns statistics for all heaps: # - # If both optional arguments are passed in and the second optional argument is - # a symbol, it will return a +Numeric+ value for the particular heap. + # GC.stat_heap + # # => + # {0 => + # {slot_size: 40, + # heap_eden_pages: 246, + # heap_eden_slots: 402802, + # total_allocated_pages: 246, + # force_major_gc_count: 2, + # force_incremental_marking_finish_count: 1, + # total_allocated_objects: 33867152, + # total_freed_objects: 33520523}, + # 1 => + # {slot_size: 80, + # heap_eden_pages: 84, + # heap_eden_slots: 68746, + # total_allocated_pages: 84, + # force_major_gc_count: 1, + # force_incremental_marking_finish_count: 4, + # total_allocated_objects: 147491, + # total_freed_objects: 90699}, + # 2 => + # {slot_size: 160, + # heap_eden_pages: 157, + # heap_eden_slots: 64182, + # total_allocated_pages: 157, + # force_major_gc_count: 0, + # force_incremental_marking_finish_count: 0, + # total_allocated_objects: 211460, + # total_freed_objects: 190075}, + # 3 => + # {slot_size: 320, + # heap_eden_pages: 8, + # heap_eden_slots: 1631, + # total_allocated_pages: 8, + # force_major_gc_count: 0, + # force_incremental_marking_finish_count: 0, + # total_allocated_objects: 1422, + # total_freed_objects: 700}, + # 4 => + # {slot_size: 640, + # heap_eden_pages: 16, + # heap_eden_slots: 1628, + # total_allocated_pages: 16, + # force_major_gc_count: 0, + # force_incremental_marking_finish_count: 0, + # total_allocated_objects: 1230, + # total_freed_objects: 309}} + # + # In the example above, the keys in the outer hash are the heap identifiers: + # + # GC.stat_heap.keys # => [0, 1, 2, 3, 4] + # + # On CRuby, each heap identifier is an integer; + # on other implementations, a heap identifier may be a string. + # + # With only argument +heap_id+ given, + # returns statistics for the given heap identifier: + # + # GC.stat_heap(2) + # # => + # {slot_size: 160, + # heap_eden_pages: 157, + # heap_eden_slots: 64182, + # total_allocated_pages: 157, + # force_major_gc_count: 0, + # force_incremental_marking_finish_count: 0, + # total_allocated_objects: 225018, + # total_freed_objects: 206647} # - # On CRuby, +heap_name+ is of the type +Integer+ but may be of type +String+ - # on other implementations. + # With arguments +heap_id+ and +key+ given, + # returns the value for the given key in the given heap: # - # The contents of the hash are implementation-specific and may change in - # the future without notice. + # GC.stat_heap(2, :slot_size) # => 160 # - # If the optional argument, hash, is given, it is overwritten and returned. + # With arguments +nil+ and +hash+ given, + # merges the statistics for all heaps into the given hash: # - # This method is only expected to work on CRuby. + # h = {foo: 0, bar: 1} + # GC.stat_heap(nil, h).keys # => [:foo, :bar, 0, 1, 2, 3, 4] # - # The hash includes the following keys about the internal information in - # the \GC: + # With arguments +heap_id+ and +hash+ given, + # merges the statistics for the given heap into the given hash: # - # [slot_size] + # h = {foo: 0, bar: 1} + # GC.stat_heap(2, h).keys + # # => + # [:foo, + # :bar, + # :slot_size, + # :heap_eden_pages, + # :heap_eden_slots, + # :total_allocated_pages, + # :force_major_gc_count, + # :force_incremental_marking_finish_count, + # :total_allocated_objects, + # :total_freed_objects] + # + # The statistics for a heap may include: + # + # - +:slot_size+: # The slot size of the heap in bytes. - # [heap_allocatable_pages] + # - +:heap_allocatable_pages+: # The number of pages that can be allocated without triggering a new # garbage collection cycle. - # [heap_eden_pages] + # - +:heap_eden_pages+: # The number of pages in the eden heap. - # [heap_eden_slots] + # - +:heap_eden_slots+: # The total number of slots in all of the pages in the eden heap. - # [heap_tomb_pages] + # - +:heap_tomb_pages+: # The number of pages in the tomb heap. The tomb heap only contains pages # that do not have any live objects. - # [heap_tomb_slots] + # - +:heap_tomb_slots+: # The total number of slots in all of the pages in the tomb heap. - # [total_allocated_pages] + # - +:total_allocated_pages+: # The total number of pages that have been allocated in the heap. - # [total_freed_pages] + # - +:total_freed_pages+: # The total number of pages that have been freed and released back to the # system in the heap. - # [force_major_gc_count] + # - +:force_major_gc_count+: # The number of times this heap has forced major garbage collection cycles # to start due to running out of free slots. - # [force_incremental_marking_finish_count] + # - +:force_incremental_marking_finish_count+: # The number of times this heap has forced incremental marking to complete # due to running out of pooled slots. # From 7c28fb2fb2544e5fed75ef216c4dd08084b38671 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 17 Aug 2025 17:13:15 +0900 Subject: [PATCH 12/60] [Bug #21546] Make the generated pc file relocatable --- .github/workflows/ubuntu.yml | 5 +++++ template/Makefile.in | 31 +++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 27cd43bf9cd880..8955ea91d4c558 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -191,6 +191,11 @@ jobs: if: ${{ matrix.test_task == 'check' && matrix.skipped_tests }} continue-on-error: ${{ matrix.continue-on-skipped_tests || false }} + - name: test-pc + run: | + DESTDIR=${RUNNER_TEMP-${TMPDIR-/tmp}}/installed + $SETARCH make test-pc "DESTDIR=$DESTDIR" + - uses: ./.github/actions/slack with: label: ${{ matrix.test_task }} ${{ matrix.configure }}${{ matrix.arch }} diff --git a/template/Makefile.in b/template/Makefile.in index 66ac10de1b4578..e1749bf3eb1ba5 100644 --- a/template/Makefile.in +++ b/template/Makefile.in @@ -350,13 +350,40 @@ $(LIBRUBY_$(LIBRUBY_WITH_EXT)): $(LIBRUBY_SO_UPDATE) PKG_CONFIG = @PKG_CONFIG@ ruby_pc = @ruby_pc@ $(ruby_pc): config.status Makefile - $(Q)./config.status --file=-:$(srcdir)/template/ruby.pc.in | \ + $(Q) \ + pkg="$(@libdirname@)/pkgconfig" prefix="$(prefix)"; \ + if [ "$(LIBRUBY_RELATIVE)" = yes ]; then \ + case "$$pkg" in "$$prefix"/?*) \ + pkg="$${pkg#$$prefix/}"; \ + prefix='$${pcfiledir}'`echo "/$${pkg}" | sed -e 's|/[^/][^/]*|/..|g'`; \ + esac; \ + fi; \ + ./config.status --file=-:$(srcdir)/template/ruby.pc.in | \ sed -e 's/\$$(\([A-Za-z_][A-Za-z0-9_]*\))/$${\1}/g' \ - -e 's|^prefix=.*|prefix=$(prefix)|' \ + -e "s|^prefix=.*|prefix=$$prefix|" \ > ruby.tmp.pc $(Q)pkg_config=${PKG_CONFIG} && PKG_CONFIG_PATH=. $${pkg_config:-:} --print-errors ruby.tmp $(Q)$(MV) -f ruby.tmp.pc $(ruby_pc) +test-pc: install-data + set -ex; \ + [ -z "$${pkg_config=$(PKG_CONFIG)}" ] && exit; \ + export PKG_CONFIG_PATH=$(DESTDIR)/$(libdir)/pkgconfig$${PKG_CONFIG_PATH:+:$$PKG_CONFIG_PATH}; \ + $${pkg_config} --exists $(ruby_pc:.pc=); \ + path=`$${pkg_config} --variable=prefix $(ruby_pc:.pc=)`; \ + if [ "$(LIBRUBY_RELATIVE)" = yes ]; then \ + test "$$path" -ef "$(DESTDIR)$(prefix)"; \ + else \ + test "$$path" = "$(prefix)"; \ + fi + +install-data: pkgconfig-data pre-install-data do-install-data post-install-data +pre-install-data:: install-prereq +do-install-data: $(PREP) pre-install-data + $(INSTRUBY) --make="$(MAKE)" $(INSTRUBY_ARGS) --install=data +post-install-data:: + @$(NULLCMD) + modular-gc-precheck: $(Q) if test -z $(modular_gc_dir); then \ echo "You must configure with --with-modular-gc to use modular GC"; \ From 504b963762c1633c4da1141b8785c119551e63d2 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 24 Jul 2025 00:41:36 +0900 Subject: [PATCH 13/60] Fix dependencies of generated prism sources for NMake The VPATH rule of NMake is different from others. Abandon using them in the rules for the generated source, locate them in the top source directory, as well as the generated library files of prism. --- common.mk | 34 ++++---- depend | 200 +++++++++++++++++++++++------------------------ tool/update-deps | 19 +++-- 3 files changed, 126 insertions(+), 127 deletions(-) diff --git a/common.mk b/common.mk index 9e5098ef4b1300..61290e54f0e55c 100644 --- a/common.mk +++ b/common.mk @@ -250,36 +250,36 @@ srcs: $(srcdir)/lib/prism/visitor.rb $(srcdir)/lib/prism/visitor.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/visitor.rb.erb $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/visitor.rb $(srcdir)/lib/prism/visitor.rb -srcs: prism/api_node.c -prism/api_node.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/ext/prism/api_node.c.erb +srcs: $(top_srcdir)/prism/api_node.c +$(top_srcdir)/prism/api_node.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/ext/prism/api_node.c.erb $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb ext/prism/api_node.c $@ -srcs: prism/ast.h -prism/ast.h: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/include/prism/ast.h.erb +srcs: $(top_srcdir)/prism/ast.h +$(top_srcdir)/prism/ast.h: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/include/prism/ast.h.erb $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb include/prism/ast.h $@ -srcs: prism/diagnostic.c -prism/diagnostic.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/diagnostic.c.erb +srcs: $(top_srcdir)/prism/diagnostic.c +$(top_srcdir)/prism/diagnostic.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/diagnostic.c.erb $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb src/diagnostic.c $@ -srcs: prism/diagnostic.h -prism/diagnostic.h: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/include/prism/diagnostic.h.erb +srcs: $(top_srcdir)/prism/diagnostic.h +$(top_srcdir)/prism/diagnostic.h: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/include/prism/diagnostic.h.erb $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb include/prism/diagnostic.h $@ -srcs: prism/node.c -prism/node.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/node.c.erb +srcs: $(top_srcdir)/prism/node.c +$(top_srcdir)/prism/node.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/node.c.erb $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb src/node.c $@ -srcs: prism/prettyprint.c -prism/prettyprint.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/prettyprint.c.erb +srcs: $(top_srcdir)/prism/prettyprint.c +$(top_srcdir)/prism/prettyprint.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/prettyprint.c.erb $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb src/prettyprint.c $@ -srcs: prism/serialize.c -prism/serialize.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/serialize.c.erb +srcs: $(top_srcdir)/prism/serialize.c +$(top_srcdir)/prism/serialize.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/serialize.c.erb $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb src/serialize.c $@ -srcs: prism/token_type.c -prism/token_type.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/token_type.c.erb +srcs: $(top_srcdir)/prism/token_type.c +$(top_srcdir)/prism/token_type.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/token_type.c.erb $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb src/token_type.c $@ EXPORTOBJS = $(DLNOBJ) \ @@ -1290,7 +1290,7 @@ incs: $(INSNS) {$(VPATH)}node_name.inc {$(VPATH)}known_errors.inc \ {$(VPATH)}vm_call_iseq_optimized.inc $(srcdir)/revision.h \ $(REVISION_H) \ $(UNICODE_DATA_HEADERS) $(ENC_HEADERS) \ - $(srcs_vpath)prism/ast.h $(srcs_vpath)prism/diagnostic.h \ + $(top_srcdir)/prism/ast.h $(top_srcdir)/prism/diagnostic.h \ {$(VPATH)}id.h {$(VPATH)}probes.dmyh insns: $(INSNS) diff --git a/depend b/depend index ea2486e9e8da1d..0fcf5ce652cd06 100644 --- a/depend +++ b/depend @@ -303,7 +303,9 @@ ast.$(OBJEXT): $(top_srcdir)/internal/symbol.h ast.$(OBJEXT): $(top_srcdir)/internal/variable.h ast.$(OBJEXT): $(top_srcdir)/internal/vm.h ast.$(OBJEXT): $(top_srcdir)/internal/warnings.h +ast.$(OBJEXT): $(top_srcdir)/prism/ast.h ast.$(OBJEXT): $(top_srcdir)/prism/defines.h +ast.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h ast.$(OBJEXT): $(top_srcdir)/prism/encoding.h ast.$(OBJEXT): $(top_srcdir)/prism/node.h ast.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -323,6 +325,7 @@ ast.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h ast.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h ast.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h ast.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +ast.$(OBJEXT): $(top_srcdir)/prism/version.h ast.$(OBJEXT): {$(VPATH)}assert.h ast.$(OBJEXT): {$(VPATH)}ast.c ast.$(OBJEXT): {$(VPATH)}ast.rbinc @@ -501,9 +504,6 @@ ast.$(OBJEXT): {$(VPATH)}missing.h ast.$(OBJEXT): {$(VPATH)}node.h ast.$(OBJEXT): {$(VPATH)}onigmo.h ast.$(OBJEXT): {$(VPATH)}oniguruma.h -ast.$(OBJEXT): {$(VPATH)}prism/ast.h -ast.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -ast.$(OBJEXT): {$(VPATH)}prism/version.h ast.$(OBJEXT): {$(VPATH)}prism_compile.h ast.$(OBJEXT): {$(VPATH)}ruby_assert.h ast.$(OBJEXT): {$(VPATH)}ruby_atomic.h @@ -746,7 +746,9 @@ builtin.$(OBJEXT): $(top_srcdir)/internal/static_assert.h builtin.$(OBJEXT): $(top_srcdir)/internal/variable.h builtin.$(OBJEXT): $(top_srcdir)/internal/vm.h builtin.$(OBJEXT): $(top_srcdir)/internal/warnings.h +builtin.$(OBJEXT): $(top_srcdir)/prism/ast.h builtin.$(OBJEXT): $(top_srcdir)/prism/defines.h +builtin.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h builtin.$(OBJEXT): $(top_srcdir)/prism/encoding.h builtin.$(OBJEXT): $(top_srcdir)/prism/node.h builtin.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -766,6 +768,7 @@ builtin.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h builtin.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h builtin.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h builtin.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +builtin.$(OBJEXT): $(top_srcdir)/prism/version.h builtin.$(OBJEXT): {$(VPATH)}assert.h builtin.$(OBJEXT): {$(VPATH)}atomic.h builtin.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -944,9 +947,6 @@ builtin.$(OBJEXT): {$(VPATH)}missing.h builtin.$(OBJEXT): {$(VPATH)}node.h builtin.$(OBJEXT): {$(VPATH)}onigmo.h builtin.$(OBJEXT): {$(VPATH)}oniguruma.h -builtin.$(OBJEXT): {$(VPATH)}prism/ast.h -builtin.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -builtin.$(OBJEXT): {$(VPATH)}prism/version.h builtin.$(OBJEXT): {$(VPATH)}prism_compile.h builtin.$(OBJEXT): {$(VPATH)}ruby_assert.h builtin.$(OBJEXT): {$(VPATH)}ruby_atomic.h @@ -1391,7 +1391,9 @@ compile.$(OBJEXT): $(top_srcdir)/internal/thread.h compile.$(OBJEXT): $(top_srcdir)/internal/variable.h compile.$(OBJEXT): $(top_srcdir)/internal/vm.h compile.$(OBJEXT): $(top_srcdir)/internal/warnings.h +compile.$(OBJEXT): $(top_srcdir)/prism/ast.h compile.$(OBJEXT): $(top_srcdir)/prism/defines.h +compile.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h compile.$(OBJEXT): $(top_srcdir)/prism/encoding.h compile.$(OBJEXT): $(top_srcdir)/prism/node.h compile.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -1399,6 +1401,7 @@ compile.$(OBJEXT): $(top_srcdir)/prism/pack.h compile.$(OBJEXT): $(top_srcdir)/prism/parser.h compile.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h compile.$(OBJEXT): $(top_srcdir)/prism/prism.h +compile.$(OBJEXT): $(top_srcdir)/prism/prism.h compile.$(OBJEXT): $(top_srcdir)/prism/regexp.h compile.$(OBJEXT): $(top_srcdir)/prism/static_literals.h compile.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h @@ -1411,6 +1414,7 @@ compile.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h compile.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h compile.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h compile.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +compile.$(OBJEXT): $(top_srcdir)/prism/version.h compile.$(OBJEXT): $(top_srcdir)/prism_compile.c compile.$(OBJEXT): {$(VPATH)}assert.h compile.$(OBJEXT): {$(VPATH)}atomic.h @@ -1597,10 +1601,6 @@ compile.$(OBJEXT): {$(VPATH)}node.h compile.$(OBJEXT): {$(VPATH)}onigmo.h compile.$(OBJEXT): {$(VPATH)}oniguruma.h compile.$(OBJEXT): {$(VPATH)}optinsn.inc -compile.$(OBJEXT): {$(VPATH)}prism/ast.h -compile.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -compile.$(OBJEXT): {$(VPATH)}prism/prism.h -compile.$(OBJEXT): {$(VPATH)}prism/version.h compile.$(OBJEXT): {$(VPATH)}prism_compile.c compile.$(OBJEXT): {$(VPATH)}prism_compile.h compile.$(OBJEXT): {$(VPATH)}ractor.h @@ -2067,7 +2067,9 @@ cont.$(OBJEXT): $(top_srcdir)/internal/thread.h cont.$(OBJEXT): $(top_srcdir)/internal/variable.h cont.$(OBJEXT): $(top_srcdir)/internal/vm.h cont.$(OBJEXT): $(top_srcdir)/internal/warnings.h +cont.$(OBJEXT): $(top_srcdir)/prism/ast.h cont.$(OBJEXT): $(top_srcdir)/prism/defines.h +cont.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h cont.$(OBJEXT): $(top_srcdir)/prism/encoding.h cont.$(OBJEXT): $(top_srcdir)/prism/node.h cont.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -2087,6 +2089,7 @@ cont.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h cont.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h cont.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h cont.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +cont.$(OBJEXT): $(top_srcdir)/prism/version.h cont.$(OBJEXT): {$(VPATH)}$(COROUTINE_H) cont.$(OBJEXT): {$(VPATH)}assert.h cont.$(OBJEXT): {$(VPATH)}atomic.h @@ -2267,9 +2270,6 @@ cont.$(OBJEXT): {$(VPATH)}missing.h cont.$(OBJEXT): {$(VPATH)}node.h cont.$(OBJEXT): {$(VPATH)}onigmo.h cont.$(OBJEXT): {$(VPATH)}oniguruma.h -cont.$(OBJEXT): {$(VPATH)}prism/ast.h -cont.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -cont.$(OBJEXT): {$(VPATH)}prism/version.h cont.$(OBJEXT): {$(VPATH)}prism_compile.h cont.$(OBJEXT): {$(VPATH)}ractor.h cont.$(OBJEXT): {$(VPATH)}ractor_core.h @@ -5074,7 +5074,9 @@ eval.$(OBJEXT): $(top_srcdir)/internal/thread.h eval.$(OBJEXT): $(top_srcdir)/internal/variable.h eval.$(OBJEXT): $(top_srcdir)/internal/vm.h eval.$(OBJEXT): $(top_srcdir)/internal/warnings.h +eval.$(OBJEXT): $(top_srcdir)/prism/ast.h eval.$(OBJEXT): $(top_srcdir)/prism/defines.h +eval.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h eval.$(OBJEXT): $(top_srcdir)/prism/encoding.h eval.$(OBJEXT): $(top_srcdir)/prism/node.h eval.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -5094,6 +5096,7 @@ eval.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h eval.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h eval.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h eval.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +eval.$(OBJEXT): $(top_srcdir)/prism/version.h eval.$(OBJEXT): {$(VPATH)}assert.h eval.$(OBJEXT): {$(VPATH)}atomic.h eval.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -5276,9 +5279,6 @@ eval.$(OBJEXT): {$(VPATH)}missing.h eval.$(OBJEXT): {$(VPATH)}node.h eval.$(OBJEXT): {$(VPATH)}onigmo.h eval.$(OBJEXT): {$(VPATH)}oniguruma.h -eval.$(OBJEXT): {$(VPATH)}prism/ast.h -eval.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -eval.$(OBJEXT): {$(VPATH)}prism/version.h eval.$(OBJEXT): {$(VPATH)}prism_compile.h eval.$(OBJEXT): {$(VPATH)}probes.dmyh eval.$(OBJEXT): {$(VPATH)}probes.h @@ -5563,7 +5563,9 @@ gc.$(OBJEXT): $(top_srcdir)/internal/thread.h gc.$(OBJEXT): $(top_srcdir)/internal/variable.h gc.$(OBJEXT): $(top_srcdir)/internal/vm.h gc.$(OBJEXT): $(top_srcdir)/internal/warnings.h +gc.$(OBJEXT): $(top_srcdir)/prism/ast.h gc.$(OBJEXT): $(top_srcdir)/prism/defines.h +gc.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h gc.$(OBJEXT): $(top_srcdir)/prism/encoding.h gc.$(OBJEXT): $(top_srcdir)/prism/node.h gc.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -5583,6 +5585,7 @@ gc.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h gc.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h gc.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h gc.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +gc.$(OBJEXT): $(top_srcdir)/prism/version.h gc.$(OBJEXT): {$(VPATH)}assert.h gc.$(OBJEXT): {$(VPATH)}atomic.h gc.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -5767,9 +5770,6 @@ gc.$(OBJEXT): {$(VPATH)}missing.h gc.$(OBJEXT): {$(VPATH)}node.h gc.$(OBJEXT): {$(VPATH)}onigmo.h gc.$(OBJEXT): {$(VPATH)}oniguruma.h -gc.$(OBJEXT): {$(VPATH)}prism/ast.h -gc.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -gc.$(OBJEXT): {$(VPATH)}prism/version.h gc.$(OBJEXT): {$(VPATH)}prism_compile.h gc.$(OBJEXT): {$(VPATH)}probes.dmyh gc.$(OBJEXT): {$(VPATH)}probes.h @@ -5825,7 +5825,9 @@ goruby.$(OBJEXT): $(top_srcdir)/internal/static_assert.h goruby.$(OBJEXT): $(top_srcdir)/internal/variable.h goruby.$(OBJEXT): $(top_srcdir)/internal/vm.h goruby.$(OBJEXT): $(top_srcdir)/internal/warnings.h +goruby.$(OBJEXT): $(top_srcdir)/prism/ast.h goruby.$(OBJEXT): $(top_srcdir)/prism/defines.h +goruby.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h goruby.$(OBJEXT): $(top_srcdir)/prism/encoding.h goruby.$(OBJEXT): $(top_srcdir)/prism/node.h goruby.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -5845,6 +5847,7 @@ goruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h goruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h goruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h goruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +goruby.$(OBJEXT): $(top_srcdir)/prism/version.h goruby.$(OBJEXT): {$(VPATH)}assert.h goruby.$(OBJEXT): {$(VPATH)}atomic.h goruby.$(OBJEXT): {$(VPATH)}backward.h @@ -6024,9 +6027,6 @@ goruby.$(OBJEXT): {$(VPATH)}missing.h goruby.$(OBJEXT): {$(VPATH)}node.h goruby.$(OBJEXT): {$(VPATH)}onigmo.h goruby.$(OBJEXT): {$(VPATH)}oniguruma.h -goruby.$(OBJEXT): {$(VPATH)}prism/ast.h -goruby.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -goruby.$(OBJEXT): {$(VPATH)}prism/version.h goruby.$(OBJEXT): {$(VPATH)}prism_compile.h goruby.$(OBJEXT): {$(VPATH)}ruby_assert.h goruby.$(OBJEXT): {$(VPATH)}ruby_atomic.h @@ -6072,7 +6072,9 @@ hash.$(OBJEXT): $(top_srcdir)/internal/time.h hash.$(OBJEXT): $(top_srcdir)/internal/variable.h hash.$(OBJEXT): $(top_srcdir)/internal/vm.h hash.$(OBJEXT): $(top_srcdir)/internal/warnings.h +hash.$(OBJEXT): $(top_srcdir)/prism/ast.h hash.$(OBJEXT): $(top_srcdir)/prism/defines.h +hash.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h hash.$(OBJEXT): $(top_srcdir)/prism/encoding.h hash.$(OBJEXT): $(top_srcdir)/prism/node.h hash.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -6092,6 +6094,7 @@ hash.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h hash.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h hash.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h hash.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +hash.$(OBJEXT): $(top_srcdir)/prism/version.h hash.$(OBJEXT): {$(VPATH)}assert.h hash.$(OBJEXT): {$(VPATH)}atomic.h hash.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -6272,9 +6275,6 @@ hash.$(OBJEXT): {$(VPATH)}missing.h hash.$(OBJEXT): {$(VPATH)}node.h hash.$(OBJEXT): {$(VPATH)}onigmo.h hash.$(OBJEXT): {$(VPATH)}oniguruma.h -hash.$(OBJEXT): {$(VPATH)}prism/ast.h -hash.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -hash.$(OBJEXT): {$(VPATH)}prism/version.h hash.$(OBJEXT): {$(VPATH)}prism_compile.h hash.$(OBJEXT): {$(VPATH)}probes.dmyh hash.$(OBJEXT): {$(VPATH)}probes.h @@ -7149,7 +7149,9 @@ iseq.$(OBJEXT): $(top_srcdir)/internal/thread.h iseq.$(OBJEXT): $(top_srcdir)/internal/variable.h iseq.$(OBJEXT): $(top_srcdir)/internal/vm.h iseq.$(OBJEXT): $(top_srcdir)/internal/warnings.h +iseq.$(OBJEXT): $(top_srcdir)/prism/ast.h iseq.$(OBJEXT): $(top_srcdir)/prism/defines.h +iseq.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h iseq.$(OBJEXT): $(top_srcdir)/prism/encoding.h iseq.$(OBJEXT): $(top_srcdir)/prism/node.h iseq.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -7157,6 +7159,7 @@ iseq.$(OBJEXT): $(top_srcdir)/prism/pack.h iseq.$(OBJEXT): $(top_srcdir)/prism/parser.h iseq.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h iseq.$(OBJEXT): $(top_srcdir)/prism/prism.h +iseq.$(OBJEXT): $(top_srcdir)/prism/prism.h iseq.$(OBJEXT): $(top_srcdir)/prism/regexp.h iseq.$(OBJEXT): $(top_srcdir)/prism/static_literals.h iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h @@ -7169,6 +7172,7 @@ iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +iseq.$(OBJEXT): $(top_srcdir)/prism/version.h iseq.$(OBJEXT): {$(VPATH)}assert.h iseq.$(OBJEXT): {$(VPATH)}atomic.h iseq.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -7352,10 +7356,6 @@ iseq.$(OBJEXT): {$(VPATH)}missing.h iseq.$(OBJEXT): {$(VPATH)}node.h iseq.$(OBJEXT): {$(VPATH)}onigmo.h iseq.$(OBJEXT): {$(VPATH)}oniguruma.h -iseq.$(OBJEXT): {$(VPATH)}prism/ast.h -iseq.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -iseq.$(OBJEXT): {$(VPATH)}prism/prism.h -iseq.$(OBJEXT): {$(VPATH)}prism/version.h iseq.$(OBJEXT): {$(VPATH)}prism_compile.h iseq.$(OBJEXT): {$(VPATH)}ractor.h iseq.$(OBJEXT): {$(VPATH)}ruby_assert.h @@ -7393,7 +7393,9 @@ jit.$(OBJEXT): $(top_srcdir)/internal/static_assert.h jit.$(OBJEXT): $(top_srcdir)/internal/variable.h jit.$(OBJEXT): $(top_srcdir)/internal/vm.h jit.$(OBJEXT): $(top_srcdir)/internal/warnings.h +jit.$(OBJEXT): $(top_srcdir)/prism/ast.h jit.$(OBJEXT): $(top_srcdir)/prism/defines.h +jit.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h jit.$(OBJEXT): $(top_srcdir)/prism/encoding.h jit.$(OBJEXT): $(top_srcdir)/prism/node.h jit.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -7413,6 +7415,7 @@ jit.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h jit.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h jit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h jit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +jit.$(OBJEXT): $(top_srcdir)/prism/version.h jit.$(OBJEXT): {$(VPATH)}assert.h jit.$(OBJEXT): {$(VPATH)}atomic.h jit.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -7594,9 +7597,6 @@ jit.$(OBJEXT): {$(VPATH)}missing.h jit.$(OBJEXT): {$(VPATH)}node.h jit.$(OBJEXT): {$(VPATH)}onigmo.h jit.$(OBJEXT): {$(VPATH)}oniguruma.h -jit.$(OBJEXT): {$(VPATH)}prism/ast.h -jit.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -jit.$(OBJEXT): {$(VPATH)}prism/version.h jit.$(OBJEXT): {$(VPATH)}prism_compile.h jit.$(OBJEXT): {$(VPATH)}ruby_assert.h jit.$(OBJEXT): {$(VPATH)}ruby_atomic.h @@ -7646,7 +7646,9 @@ load.$(OBJEXT): $(top_srcdir)/internal/thread.h load.$(OBJEXT): $(top_srcdir)/internal/variable.h load.$(OBJEXT): $(top_srcdir)/internal/vm.h load.$(OBJEXT): $(top_srcdir)/internal/warnings.h +load.$(OBJEXT): $(top_srcdir)/prism/ast.h load.$(OBJEXT): $(top_srcdir)/prism/defines.h +load.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h load.$(OBJEXT): $(top_srcdir)/prism/encoding.h load.$(OBJEXT): $(top_srcdir)/prism/node.h load.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -7666,6 +7668,7 @@ load.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h load.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h load.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h load.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +load.$(OBJEXT): $(top_srcdir)/prism/version.h load.$(OBJEXT): {$(VPATH)}assert.h load.$(OBJEXT): {$(VPATH)}atomic.h load.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -7845,9 +7848,6 @@ load.$(OBJEXT): {$(VPATH)}missing.h load.$(OBJEXT): {$(VPATH)}node.h load.$(OBJEXT): {$(VPATH)}onigmo.h load.$(OBJEXT): {$(VPATH)}oniguruma.h -load.$(OBJEXT): {$(VPATH)}prism/ast.h -load.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -load.$(OBJEXT): {$(VPATH)}prism/version.h load.$(OBJEXT): {$(VPATH)}prism_compile.h load.$(OBJEXT): {$(VPATH)}probes.dmyh load.$(OBJEXT): {$(VPATH)}probes.h @@ -9000,7 +9000,9 @@ miniinit.$(OBJEXT): $(top_srcdir)/internal/static_assert.h miniinit.$(OBJEXT): $(top_srcdir)/internal/variable.h miniinit.$(OBJEXT): $(top_srcdir)/internal/vm.h miniinit.$(OBJEXT): $(top_srcdir)/internal/warnings.h +miniinit.$(OBJEXT): $(top_srcdir)/prism/ast.h miniinit.$(OBJEXT): $(top_srcdir)/prism/defines.h +miniinit.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h miniinit.$(OBJEXT): $(top_srcdir)/prism/encoding.h miniinit.$(OBJEXT): $(top_srcdir)/prism/node.h miniinit.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -9020,6 +9022,7 @@ miniinit.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h miniinit.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h miniinit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h miniinit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +miniinit.$(OBJEXT): $(top_srcdir)/prism/version.h miniinit.$(OBJEXT): {$(VPATH)}array.rb miniinit.$(OBJEXT): {$(VPATH)}assert.h miniinit.$(OBJEXT): {$(VPATH)}ast.rb @@ -9215,9 +9218,6 @@ miniinit.$(OBJEXT): {$(VPATH)}oniguruma.h miniinit.$(OBJEXT): {$(VPATH)}pack.rb miniinit.$(OBJEXT): {$(VPATH)}pathname_builtin.rb miniinit.$(OBJEXT): {$(VPATH)}prelude.rb -miniinit.$(OBJEXT): {$(VPATH)}prism/ast.h -miniinit.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -miniinit.$(OBJEXT): {$(VPATH)}prism/version.h miniinit.$(OBJEXT): {$(VPATH)}prism_compile.h miniinit.$(OBJEXT): {$(VPATH)}ractor.rb miniinit.$(OBJEXT): {$(VPATH)}ruby_assert.h @@ -11008,7 +11008,10 @@ pathname.$(OBJEXT): {$(VPATH)}st.h pathname.$(OBJEXT): {$(VPATH)}subst.h prism/api_node.$(OBJEXT): $(hdrdir)/ruby.h prism/api_node.$(OBJEXT): $(hdrdir)/ruby/ruby.h +prism/api_node.$(OBJEXT): $(top_srcdir)/prism/api_node.c +prism/api_node.$(OBJEXT): $(top_srcdir)/prism/ast.h prism/api_node.$(OBJEXT): $(top_srcdir)/prism/defines.h +prism/api_node.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h prism/api_node.$(OBJEXT): $(top_srcdir)/prism/encoding.h prism/api_node.$(OBJEXT): $(top_srcdir)/prism/extension.h prism/api_node.$(OBJEXT): $(top_srcdir)/prism/node.h @@ -11029,6 +11032,7 @@ prism/api_node.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h prism/api_node.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h prism/api_node.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h prism/api_node.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +prism/api_node.$(OBJEXT): $(top_srcdir)/prism/version.h prism/api_node.$(OBJEXT): {$(VPATH)}assert.h prism/api_node.$(OBJEXT): {$(VPATH)}backward/2/assume.h prism/api_node.$(OBJEXT): {$(VPATH)}backward/2/attributes.h @@ -11195,16 +11199,14 @@ prism/api_node.$(OBJEXT): {$(VPATH)}internal/xmalloc.h prism/api_node.$(OBJEXT): {$(VPATH)}missing.h prism/api_node.$(OBJEXT): {$(VPATH)}onigmo.h prism/api_node.$(OBJEXT): {$(VPATH)}oniguruma.h -prism/api_node.$(OBJEXT): {$(VPATH)}prism/api_node.c -prism/api_node.$(OBJEXT): {$(VPATH)}prism/ast.h -prism/api_node.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -prism/api_node.$(OBJEXT): {$(VPATH)}prism/version.h prism/api_node.$(OBJEXT): {$(VPATH)}st.h prism/api_node.$(OBJEXT): {$(VPATH)}subst.h prism/api_pack.$(OBJEXT): $(hdrdir)/ruby.h prism/api_pack.$(OBJEXT): $(hdrdir)/ruby/ruby.h prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/api_pack.c +prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/ast.h prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/defines.h +prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/encoding.h prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/extension.h prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/node.h @@ -11225,6 +11227,7 @@ prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +prism/api_pack.$(OBJEXT): $(top_srcdir)/prism/version.h prism/api_pack.$(OBJEXT): {$(VPATH)}assert.h prism/api_pack.$(OBJEXT): {$(VPATH)}backward/2/assume.h prism/api_pack.$(OBJEXT): {$(VPATH)}backward/2/attributes.h @@ -11391,12 +11394,12 @@ prism/api_pack.$(OBJEXT): {$(VPATH)}internal/xmalloc.h prism/api_pack.$(OBJEXT): {$(VPATH)}missing.h prism/api_pack.$(OBJEXT): {$(VPATH)}onigmo.h prism/api_pack.$(OBJEXT): {$(VPATH)}oniguruma.h -prism/api_pack.$(OBJEXT): {$(VPATH)}prism/ast.h -prism/api_pack.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -prism/api_pack.$(OBJEXT): {$(VPATH)}prism/version.h prism/api_pack.$(OBJEXT): {$(VPATH)}st.h prism/api_pack.$(OBJEXT): {$(VPATH)}subst.h +prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/ast.h prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/defines.h +prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/diagnostic.c +prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h @@ -11404,16 +11407,15 @@ prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h -prism/diagnostic.$(OBJEXT): {$(VPATH)}prism/ast.h -prism/diagnostic.$(OBJEXT): {$(VPATH)}prism/diagnostic.c -prism/diagnostic.$(OBJEXT): {$(VPATH)}prism/diagnostic.h prism/encoding.$(OBJEXT): $(top_srcdir)/prism/defines.h prism/encoding.$(OBJEXT): $(top_srcdir)/prism/encoding.c prism/encoding.$(OBJEXT): $(top_srcdir)/prism/encoding.h prism/encoding.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h prism/extension.$(OBJEXT): $(hdrdir)/ruby.h prism/extension.$(OBJEXT): $(hdrdir)/ruby/ruby.h +prism/extension.$(OBJEXT): $(top_srcdir)/prism/ast.h prism/extension.$(OBJEXT): $(top_srcdir)/prism/defines.h +prism/extension.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h prism/extension.$(OBJEXT): $(top_srcdir)/prism/encoding.h prism/extension.$(OBJEXT): $(top_srcdir)/prism/extension.c prism/extension.$(OBJEXT): $(top_srcdir)/prism/extension.h @@ -11435,6 +11437,7 @@ prism/extension.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h prism/extension.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h prism/extension.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h prism/extension.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +prism/extension.$(OBJEXT): $(top_srcdir)/prism/version.h prism/extension.$(OBJEXT): {$(VPATH)}assert.h prism/extension.$(OBJEXT): {$(VPATH)}backward/2/assume.h prism/extension.$(OBJEXT): {$(VPATH)}backward/2/attributes.h @@ -11601,13 +11604,13 @@ prism/extension.$(OBJEXT): {$(VPATH)}internal/xmalloc.h prism/extension.$(OBJEXT): {$(VPATH)}missing.h prism/extension.$(OBJEXT): {$(VPATH)}onigmo.h prism/extension.$(OBJEXT): {$(VPATH)}oniguruma.h -prism/extension.$(OBJEXT): {$(VPATH)}prism/ast.h -prism/extension.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -prism/extension.$(OBJEXT): {$(VPATH)}prism/version.h prism/extension.$(OBJEXT): {$(VPATH)}st.h prism/extension.$(OBJEXT): {$(VPATH)}subst.h +prism/node.$(OBJEXT): $(top_srcdir)/prism/ast.h prism/node.$(OBJEXT): $(top_srcdir)/prism/defines.h +prism/node.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h prism/node.$(OBJEXT): $(top_srcdir)/prism/encoding.h +prism/node.$(OBJEXT): $(top_srcdir)/prism/node.c prism/node.$(OBJEXT): $(top_srcdir)/prism/node.h prism/node.$(OBJEXT): $(top_srcdir)/prism/options.h prism/node.$(OBJEXT): $(top_srcdir)/prism/pack.h @@ -11624,9 +11627,6 @@ prism/node.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h prism/node.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h prism/node.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h prism/node.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h -prism/node.$(OBJEXT): {$(VPATH)}prism/ast.h -prism/node.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -prism/node.$(OBJEXT): {$(VPATH)}prism/node.c prism/options.$(OBJEXT): $(top_srcdir)/prism/defines.h prism/options.$(OBJEXT): $(top_srcdir)/prism/options.c prism/options.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -11636,10 +11636,12 @@ prism/options.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h prism/pack.$(OBJEXT): $(top_srcdir)/prism/defines.h prism/pack.$(OBJEXT): $(top_srcdir)/prism/pack.c prism/pack.$(OBJEXT): $(top_srcdir)/prism/pack.h +prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/ast.h prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/defines.h prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/encoding.h prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/options.h prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/parser.h +prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/prettyprint.c prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/static_literals.h prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h @@ -11650,9 +11652,9 @@ prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h prism/prettyprint.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h -prism/prettyprint.$(OBJEXT): {$(VPATH)}prism/ast.h -prism/prettyprint.$(OBJEXT): {$(VPATH)}prism/prettyprint.c +prism/prism.$(OBJEXT): $(top_srcdir)/prism/ast.h prism/prism.$(OBJEXT): $(top_srcdir)/prism/defines.h +prism/prism.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h prism/prism.$(OBJEXT): $(top_srcdir)/prism/encoding.h prism/prism.$(OBJEXT): $(top_srcdir)/prism/node.h prism/prism.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -11674,9 +11676,8 @@ prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h prism/prism.$(OBJEXT): $(top_srcdir)/prism/version.h -prism/prism.$(OBJEXT): {$(VPATH)}prism/ast.h -prism/prism.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -prism/prism.$(OBJEXT): {$(VPATH)}prism/version.h +prism/prism.$(OBJEXT): $(top_srcdir)/prism/version.h +prism/regexp.$(OBJEXT): $(top_srcdir)/prism/ast.h prism/regexp.$(OBJEXT): $(top_srcdir)/prism/defines.h prism/regexp.$(OBJEXT): $(top_srcdir)/prism/encoding.h prism/regexp.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -11693,8 +11694,9 @@ prism/regexp.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h prism/regexp.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h prism/regexp.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h prism/regexp.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h -prism/regexp.$(OBJEXT): {$(VPATH)}prism/ast.h +prism/serialize.$(OBJEXT): $(top_srcdir)/prism/ast.h prism/serialize.$(OBJEXT): $(top_srcdir)/prism/defines.h +prism/serialize.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h prism/serialize.$(OBJEXT): $(top_srcdir)/prism/encoding.h prism/serialize.$(OBJEXT): $(top_srcdir)/prism/node.h prism/serialize.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -11703,6 +11705,7 @@ prism/serialize.$(OBJEXT): $(top_srcdir)/prism/parser.h prism/serialize.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h prism/serialize.$(OBJEXT): $(top_srcdir)/prism/prism.h prism/serialize.$(OBJEXT): $(top_srcdir)/prism/regexp.h +prism/serialize.$(OBJEXT): $(top_srcdir)/prism/serialize.c prism/serialize.$(OBJEXT): $(top_srcdir)/prism/static_literals.h prism/serialize.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h prism/serialize.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h @@ -11714,10 +11717,8 @@ prism/serialize.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h prism/serialize.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h prism/serialize.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h prism/serialize.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h -prism/serialize.$(OBJEXT): {$(VPATH)}prism/ast.h -prism/serialize.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -prism/serialize.$(OBJEXT): {$(VPATH)}prism/serialize.c -prism/serialize.$(OBJEXT): {$(VPATH)}prism/version.h +prism/serialize.$(OBJEXT): $(top_srcdir)/prism/version.h +prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/ast.h prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/defines.h prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/encoding.h prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/node.h @@ -11733,16 +11734,15 @@ prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h prism/static_literals.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h -prism/static_literals.$(OBJEXT): {$(VPATH)}prism/ast.h +prism/token_type.$(OBJEXT): $(top_srcdir)/prism/ast.h prism/token_type.$(OBJEXT): $(top_srcdir)/prism/defines.h +prism/token_type.$(OBJEXT): $(top_srcdir)/prism/token_type.c prism/token_type.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h prism/token_type.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h prism/token_type.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h prism/token_type.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h prism/token_type.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h prism/token_type.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h -prism/token_type.$(OBJEXT): {$(VPATH)}prism/ast.h -prism/token_type.$(OBJEXT): {$(VPATH)}prism/token_type.c prism/util/pm_buffer.$(OBJEXT): $(top_srcdir)/prism/defines.h prism/util/pm_buffer.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.c prism/util/pm_buffer.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h @@ -11764,6 +11764,7 @@ prism/util/pm_integer.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h prism/util/pm_list.$(OBJEXT): $(top_srcdir)/prism/defines.h prism/util/pm_list.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.c prism/util/pm_list.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h +prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/ast.h prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/defines.h prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/encoding.h prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/parser.h @@ -11774,7 +11775,6 @@ prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h prism/util/pm_memchr.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h -prism/util/pm_memchr.$(OBJEXT): {$(VPATH)}prism/ast.h prism/util/pm_newline_list.$(OBJEXT): $(top_srcdir)/prism/defines.h prism/util/pm_newline_list.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.c prism/util/pm_newline_list.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h @@ -11784,7 +11784,9 @@ prism/util/pm_string.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h prism/util/pm_strncasecmp.$(OBJEXT): $(top_srcdir)/prism/defines.h prism/util/pm_strncasecmp.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.c prism/util/pm_strncasecmp.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h +prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/ast.h prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/defines.h +prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/encoding.h prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/options.h prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/parser.h @@ -11799,11 +11801,11 @@ prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.c prism/util/pm_strpbrk.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h -prism/util/pm_strpbrk.$(OBJEXT): {$(VPATH)}prism/ast.h -prism/util/pm_strpbrk.$(OBJEXT): {$(VPATH)}prism/diagnostic.h prism_init.$(OBJEXT): $(hdrdir)/ruby.h prism_init.$(OBJEXT): $(hdrdir)/ruby/ruby.h +prism_init.$(OBJEXT): $(top_srcdir)/prism/ast.h prism_init.$(OBJEXT): $(top_srcdir)/prism/defines.h +prism_init.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h prism_init.$(OBJEXT): $(top_srcdir)/prism/encoding.h prism_init.$(OBJEXT): $(top_srcdir)/prism/extension.h prism_init.$(OBJEXT): $(top_srcdir)/prism/node.h @@ -11824,6 +11826,7 @@ prism_init.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h prism_init.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h prism_init.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h prism_init.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +prism_init.$(OBJEXT): $(top_srcdir)/prism/version.h prism_init.$(OBJEXT): $(top_srcdir)/prism_init.c prism_init.$(OBJEXT): {$(VPATH)}assert.h prism_init.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -11991,9 +11994,6 @@ prism_init.$(OBJEXT): {$(VPATH)}internal/xmalloc.h prism_init.$(OBJEXT): {$(VPATH)}missing.h prism_init.$(OBJEXT): {$(VPATH)}onigmo.h prism_init.$(OBJEXT): {$(VPATH)}oniguruma.h -prism_init.$(OBJEXT): {$(VPATH)}prism/ast.h -prism_init.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -prism_init.$(OBJEXT): {$(VPATH)}prism/version.h prism_init.$(OBJEXT): {$(VPATH)}prism_init.c prism_init.$(OBJEXT): {$(VPATH)}st.h prism_init.$(OBJEXT): {$(VPATH)}subst.h @@ -12024,7 +12024,9 @@ proc.$(OBJEXT): $(top_srcdir)/internal/symbol.h proc.$(OBJEXT): $(top_srcdir)/internal/variable.h proc.$(OBJEXT): $(top_srcdir)/internal/vm.h proc.$(OBJEXT): $(top_srcdir)/internal/warnings.h +proc.$(OBJEXT): $(top_srcdir)/prism/ast.h proc.$(OBJEXT): $(top_srcdir)/prism/defines.h +proc.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h proc.$(OBJEXT): $(top_srcdir)/prism/encoding.h proc.$(OBJEXT): $(top_srcdir)/prism/node.h proc.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -12044,6 +12046,7 @@ proc.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h proc.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h proc.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h proc.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +proc.$(OBJEXT): $(top_srcdir)/prism/version.h proc.$(OBJEXT): {$(VPATH)}assert.h proc.$(OBJEXT): {$(VPATH)}atomic.h proc.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -12221,9 +12224,6 @@ proc.$(OBJEXT): {$(VPATH)}missing.h proc.$(OBJEXT): {$(VPATH)}node.h proc.$(OBJEXT): {$(VPATH)}onigmo.h proc.$(OBJEXT): {$(VPATH)}oniguruma.h -proc.$(OBJEXT): {$(VPATH)}prism/ast.h -proc.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -proc.$(OBJEXT): {$(VPATH)}prism/version.h proc.$(OBJEXT): {$(VPATH)}prism_compile.h proc.$(OBJEXT): {$(VPATH)}proc.c proc.$(OBJEXT): {$(VPATH)}ractor.h @@ -14578,7 +14578,9 @@ ruby.$(OBJEXT): $(top_srcdir)/internal/thread.h ruby.$(OBJEXT): $(top_srcdir)/internal/variable.h ruby.$(OBJEXT): $(top_srcdir)/internal/vm.h ruby.$(OBJEXT): $(top_srcdir)/internal/warnings.h +ruby.$(OBJEXT): $(top_srcdir)/prism/ast.h ruby.$(OBJEXT): $(top_srcdir)/prism/defines.h +ruby.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h ruby.$(OBJEXT): $(top_srcdir)/prism/encoding.h ruby.$(OBJEXT): $(top_srcdir)/prism/node.h ruby.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -14598,6 +14600,7 @@ ruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h ruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h ruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h ruby.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +ruby.$(OBJEXT): $(top_srcdir)/prism/version.h ruby.$(OBJEXT): {$(VPATH)}assert.h ruby.$(OBJEXT): {$(VPATH)}atomic.h ruby.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -14777,9 +14780,6 @@ ruby.$(OBJEXT): {$(VPATH)}missing.h ruby.$(OBJEXT): {$(VPATH)}node.h ruby.$(OBJEXT): {$(VPATH)}onigmo.h ruby.$(OBJEXT): {$(VPATH)}oniguruma.h -ruby.$(OBJEXT): {$(VPATH)}prism/ast.h -ruby.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -ruby.$(OBJEXT): {$(VPATH)}prism/version.h ruby.$(OBJEXT): {$(VPATH)}prism_compile.h ruby.$(OBJEXT): {$(VPATH)}ruby.c ruby.$(OBJEXT): {$(VPATH)}ruby_assert.h @@ -17280,7 +17280,9 @@ thread.$(OBJEXT): $(top_srcdir)/internal/time.h thread.$(OBJEXT): $(top_srcdir)/internal/variable.h thread.$(OBJEXT): $(top_srcdir)/internal/vm.h thread.$(OBJEXT): $(top_srcdir)/internal/warnings.h +thread.$(OBJEXT): $(top_srcdir)/prism/ast.h thread.$(OBJEXT): $(top_srcdir)/prism/defines.h +thread.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h thread.$(OBJEXT): $(top_srcdir)/prism/encoding.h thread.$(OBJEXT): $(top_srcdir)/prism/node.h thread.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -17300,6 +17302,7 @@ thread.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h thread.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h thread.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h thread.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +thread.$(OBJEXT): $(top_srcdir)/prism/version.h thread.$(OBJEXT): {$(VPATH)}$(COROUTINE_H) thread.$(OBJEXT): {$(VPATH)}assert.h thread.$(OBJEXT): {$(VPATH)}atomic.h @@ -17483,9 +17486,6 @@ thread.$(OBJEXT): {$(VPATH)}missing.h thread.$(OBJEXT): {$(VPATH)}node.h thread.$(OBJEXT): {$(VPATH)}onigmo.h thread.$(OBJEXT): {$(VPATH)}oniguruma.h -thread.$(OBJEXT): {$(VPATH)}prism/ast.h -thread.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -thread.$(OBJEXT): {$(VPATH)}prism/version.h thread.$(OBJEXT): {$(VPATH)}prism_compile.h thread.$(OBJEXT): {$(VPATH)}ractor.h thread.$(OBJEXT): {$(VPATH)}ractor_core.h @@ -18568,7 +18568,9 @@ vm.$(OBJEXT): $(top_srcdir)/internal/transcode.h vm.$(OBJEXT): $(top_srcdir)/internal/variable.h vm.$(OBJEXT): $(top_srcdir)/internal/vm.h vm.$(OBJEXT): $(top_srcdir)/internal/warnings.h +vm.$(OBJEXT): $(top_srcdir)/prism/ast.h vm.$(OBJEXT): $(top_srcdir)/prism/defines.h +vm.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h vm.$(OBJEXT): $(top_srcdir)/prism/encoding.h vm.$(OBJEXT): $(top_srcdir)/prism/node.h vm.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -18588,6 +18590,7 @@ vm.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h vm.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h vm.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h vm.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +vm.$(OBJEXT): $(top_srcdir)/prism/version.h vm.$(OBJEXT): {$(VPATH)}assert.h vm.$(OBJEXT): {$(VPATH)}atomic.h vm.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -18772,9 +18775,6 @@ vm.$(OBJEXT): {$(VPATH)}missing.h vm.$(OBJEXT): {$(VPATH)}node.h vm.$(OBJEXT): {$(VPATH)}onigmo.h vm.$(OBJEXT): {$(VPATH)}oniguruma.h -vm.$(OBJEXT): {$(VPATH)}prism/ast.h -vm.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -vm.$(OBJEXT): {$(VPATH)}prism/version.h vm.$(OBJEXT): {$(VPATH)}prism_compile.h vm.$(OBJEXT): {$(VPATH)}probes.dmyh vm.$(OBJEXT): {$(VPATH)}probes.h @@ -18832,7 +18832,9 @@ vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/string.h vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/variable.h vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/vm.h vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/warnings.h +vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/ast.h vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/defines.h +vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/encoding.h vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/node.h vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -18852,6 +18854,7 @@ vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +vm_backtrace.$(OBJEXT): $(top_srcdir)/prism/version.h vm_backtrace.$(OBJEXT): {$(VPATH)}assert.h vm_backtrace.$(OBJEXT): {$(VPATH)}atomic.h vm_backtrace.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -19030,9 +19033,6 @@ vm_backtrace.$(OBJEXT): {$(VPATH)}missing.h vm_backtrace.$(OBJEXT): {$(VPATH)}node.h vm_backtrace.$(OBJEXT): {$(VPATH)}onigmo.h vm_backtrace.$(OBJEXT): {$(VPATH)}oniguruma.h -vm_backtrace.$(OBJEXT): {$(VPATH)}prism/ast.h -vm_backtrace.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -vm_backtrace.$(OBJEXT): {$(VPATH)}prism/version.h vm_backtrace.$(OBJEXT): {$(VPATH)}prism_compile.h vm_backtrace.$(OBJEXT): {$(VPATH)}ruby_assert.h vm_backtrace.$(OBJEXT): {$(VPATH)}ruby_atomic.h @@ -19065,7 +19065,9 @@ vm_dump.$(OBJEXT): $(top_srcdir)/internal/static_assert.h vm_dump.$(OBJEXT): $(top_srcdir)/internal/variable.h vm_dump.$(OBJEXT): $(top_srcdir)/internal/vm.h vm_dump.$(OBJEXT): $(top_srcdir)/internal/warnings.h +vm_dump.$(OBJEXT): $(top_srcdir)/prism/ast.h vm_dump.$(OBJEXT): $(top_srcdir)/prism/defines.h +vm_dump.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h vm_dump.$(OBJEXT): $(top_srcdir)/prism/encoding.h vm_dump.$(OBJEXT): $(top_srcdir)/prism/node.h vm_dump.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -19085,6 +19087,7 @@ vm_dump.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h vm_dump.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h vm_dump.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h vm_dump.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +vm_dump.$(OBJEXT): $(top_srcdir)/prism/version.h vm_dump.$(OBJEXT): {$(VPATH)}addr2line.h vm_dump.$(OBJEXT): {$(VPATH)}assert.h vm_dump.$(OBJEXT): {$(VPATH)}atomic.h @@ -19262,9 +19265,6 @@ vm_dump.$(OBJEXT): {$(VPATH)}missing.h vm_dump.$(OBJEXT): {$(VPATH)}node.h vm_dump.$(OBJEXT): {$(VPATH)}onigmo.h vm_dump.$(OBJEXT): {$(VPATH)}oniguruma.h -vm_dump.$(OBJEXT): {$(VPATH)}prism/ast.h -vm_dump.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -vm_dump.$(OBJEXT): {$(VPATH)}prism/version.h vm_dump.$(OBJEXT): {$(VPATH)}prism_compile.h vm_dump.$(OBJEXT): {$(VPATH)}procstat_vm.c vm_dump.$(OBJEXT): {$(VPATH)}ractor.h @@ -19514,7 +19514,9 @@ vm_trace.$(OBJEXT): $(top_srcdir)/internal/thread.h vm_trace.$(OBJEXT): $(top_srcdir)/internal/variable.h vm_trace.$(OBJEXT): $(top_srcdir)/internal/vm.h vm_trace.$(OBJEXT): $(top_srcdir)/internal/warnings.h +vm_trace.$(OBJEXT): $(top_srcdir)/prism/ast.h vm_trace.$(OBJEXT): $(top_srcdir)/prism/defines.h +vm_trace.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h vm_trace.$(OBJEXT): $(top_srcdir)/prism/encoding.h vm_trace.$(OBJEXT): $(top_srcdir)/prism/node.h vm_trace.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -19534,6 +19536,7 @@ vm_trace.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h vm_trace.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h vm_trace.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h vm_trace.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +vm_trace.$(OBJEXT): $(top_srcdir)/prism/version.h vm_trace.$(OBJEXT): {$(VPATH)}assert.h vm_trace.$(OBJEXT): {$(VPATH)}atomic.h vm_trace.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -19713,9 +19716,6 @@ vm_trace.$(OBJEXT): {$(VPATH)}missing.h vm_trace.$(OBJEXT): {$(VPATH)}node.h vm_trace.$(OBJEXT): {$(VPATH)}onigmo.h vm_trace.$(OBJEXT): {$(VPATH)}oniguruma.h -vm_trace.$(OBJEXT): {$(VPATH)}prism/ast.h -vm_trace.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -vm_trace.$(OBJEXT): {$(VPATH)}prism/version.h vm_trace.$(OBJEXT): {$(VPATH)}prism_compile.h vm_trace.$(OBJEXT): {$(VPATH)}ractor.h vm_trace.$(OBJEXT): {$(VPATH)}ruby_assert.h @@ -19962,7 +19962,9 @@ yjit.$(OBJEXT): $(top_srcdir)/internal/string.h yjit.$(OBJEXT): $(top_srcdir)/internal/variable.h yjit.$(OBJEXT): $(top_srcdir)/internal/vm.h yjit.$(OBJEXT): $(top_srcdir)/internal/warnings.h +yjit.$(OBJEXT): $(top_srcdir)/prism/ast.h yjit.$(OBJEXT): $(top_srcdir)/prism/defines.h +yjit.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h yjit.$(OBJEXT): $(top_srcdir)/prism/encoding.h yjit.$(OBJEXT): $(top_srcdir)/prism/node.h yjit.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -19982,6 +19984,7 @@ yjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h yjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h yjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h yjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +yjit.$(OBJEXT): $(top_srcdir)/prism/version.h yjit.$(OBJEXT): {$(VPATH)}assert.h yjit.$(OBJEXT): {$(VPATH)}atomic.h yjit.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -20164,9 +20167,6 @@ yjit.$(OBJEXT): {$(VPATH)}missing.h yjit.$(OBJEXT): {$(VPATH)}node.h yjit.$(OBJEXT): {$(VPATH)}onigmo.h yjit.$(OBJEXT): {$(VPATH)}oniguruma.h -yjit.$(OBJEXT): {$(VPATH)}prism/ast.h -yjit.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -yjit.$(OBJEXT): {$(VPATH)}prism/version.h yjit.$(OBJEXT): {$(VPATH)}prism_compile.h yjit.$(OBJEXT): {$(VPATH)}probes.dmyh yjit.$(OBJEXT): {$(VPATH)}probes.h @@ -20214,7 +20214,9 @@ zjit.$(OBJEXT): $(top_srcdir)/internal/string.h zjit.$(OBJEXT): $(top_srcdir)/internal/variable.h zjit.$(OBJEXT): $(top_srcdir)/internal/vm.h zjit.$(OBJEXT): $(top_srcdir)/internal/warnings.h +zjit.$(OBJEXT): $(top_srcdir)/prism/ast.h zjit.$(OBJEXT): $(top_srcdir)/prism/defines.h +zjit.$(OBJEXT): $(top_srcdir)/prism/diagnostic.h zjit.$(OBJEXT): $(top_srcdir)/prism/encoding.h zjit.$(OBJEXT): $(top_srcdir)/prism/node.h zjit.$(OBJEXT): $(top_srcdir)/prism/options.h @@ -20234,6 +20236,7 @@ zjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h zjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h zjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h zjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +zjit.$(OBJEXT): $(top_srcdir)/prism/version.h zjit.$(OBJEXT): {$(VPATH)}assert.h zjit.$(OBJEXT): {$(VPATH)}atomic.h zjit.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -20415,9 +20418,6 @@ zjit.$(OBJEXT): {$(VPATH)}missing.h zjit.$(OBJEXT): {$(VPATH)}node.h zjit.$(OBJEXT): {$(VPATH)}onigmo.h zjit.$(OBJEXT): {$(VPATH)}oniguruma.h -zjit.$(OBJEXT): {$(VPATH)}prism/ast.h -zjit.$(OBJEXT): {$(VPATH)}prism/diagnostic.h -zjit.$(OBJEXT): {$(VPATH)}prism/version.h zjit.$(OBJEXT): {$(VPATH)}prism_compile.h zjit.$(OBJEXT): {$(VPATH)}probes.dmyh zjit.$(OBJEXT): {$(VPATH)}probes.h diff --git a/tool/update-deps b/tool/update-deps index 375986a915ab46..c927d2483e9a7a 100755 --- a/tool/update-deps +++ b/tool/update-deps @@ -96,6 +96,15 @@ result.each {|k,v| # They can be referenced as $(top_srcdir)/filename. # % ruby -e 'def g(d) Dir.chdir(d) { Dir["**/*.{c,h,inc,dmyh}"] } end; puts((g("repo_source_dir_after_build") - g("repo_source_dir_original")).sort)' FILES_IN_SOURCE_DIRECTORY = %w[ + prism/api_node.c + prism/ast.h + prism/diagnostic.c + prism/diagnostic.h + prism/node.c + prism/prettyprint.c + prism/serialize.c + prism/token_type.c + prism/version.h ] # Files built in the build directory (except extconf.h). @@ -157,16 +166,6 @@ FILES_NEED_VPATH = %w[ enc/trans/single_byte.c enc/trans/utf8_mac.c enc/trans/utf_16_32.c - - prism/api_node.c - prism/ast.h - prism/diagnostic.c - prism/diagnostic.h - prism/node.c - prism/prettyprint.c - prism/serialize.c - prism/token_type.c - prism/version.h ] # Multiple files with same filename. From 90cb2bb871ff5d6d4d0dfbed2ee352c7ff818a87 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 30 Jul 2025 16:59:01 +0900 Subject: [PATCH 14/60] Gererate prism source files dependencies from template.rb --- .gitignore | 1 + common.mk | 87 +++++---------------------------------- configure.ac | 5 ++- prism/generate-srcs.mk.rb | 17 ++++++++ template/Makefile.in | 2 +- tool/make-snapshot | 2 +- tool/prereq.status | 3 +- win32/setup.mak | 10 +++++ 8 files changed, 45 insertions(+), 82 deletions(-) create mode 100644 prism/generate-srcs.mk.rb diff --git a/.gitignore b/.gitignore index ddf8e9a99adae3..6cf5fb5f32d63e 100644 --- a/.gitignore +++ b/.gitignore @@ -272,6 +272,7 @@ lcov*.info /prism/prettyprint.c /prism/serialize.c /prism/token_type.c +/prism/srcs.mk # tool/update-NEWS-gemlist.rb /bundled_gems.json diff --git a/common.mk b/common.mk index 61290e54f0e55c..e2b6a2f685b979 100644 --- a/common.mk +++ b/common.mk @@ -205,82 +205,12 @@ $(PRISM_BUILD_DIR)/.time $(PRISM_BUILD_DIR)/util/.time: $(Q) $(MAKEDIRS) $(@D) @$(NULLCMD) > $@ -main: $(srcdir)/lib/prism/compiler.rb -srcs: $(srcdir)/lib/prism/compiler.rb -$(srcdir)/lib/prism/compiler.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/compiler.rb.erb - $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/compiler.rb $(srcdir)/lib/prism/compiler.rb - -main: $(srcdir)/lib/prism/dispatcher.rb -srcs: $(srcdir)/lib/prism/dispatcher.rb -$(srcdir)/lib/prism/dispatcher.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/dispatcher.rb.erb - $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/dispatcher.rb $(srcdir)/lib/prism/dispatcher.rb - -main: $(srcdir)/lib/prism/dsl.rb -srcs: $(srcdir)/lib/prism/dsl.rb -$(srcdir)/lib/prism/dsl.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/dsl.rb.erb - $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/dsl.rb $(srcdir)/lib/prism/dsl.rb - -main: $(srcdir)/lib/prism/inspect_visitor.rb -srcs: $(srcdir)/lib/prism/inspect_visitor.rb -$(srcdir)/lib/prism/inspect_visitor.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/inspect_visitor.rb.erb - $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/inspect_visitor.rb $(srcdir)/lib/prism/inspect_visitor.rb - -main: $(srcdir)/lib/prism/mutation_compiler.rb -srcs: $(srcdir)/lib/prism/mutation_compiler.rb -$(srcdir)/lib/prism/mutation_compiler.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/mutation_compiler.rb.erb - $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/mutation_compiler.rb $(srcdir)/lib/prism/mutation_compiler.rb - -main: $(srcdir)/lib/prism/node.rb -srcs: $(srcdir)/lib/prism/node.rb -$(srcdir)/lib/prism/node.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/node.rb.erb - $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/node.rb $(srcdir)/lib/prism/node.rb - -main: $(srcdir)/lib/prism/reflection.rb -srcs: $(srcdir)/lib/prism/reflection.rb -$(srcdir)/lib/prism/reflection.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/reflection.rb.erb - $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/reflection.rb $(srcdir)/lib/prism/reflection.rb - -main: $(srcdir)/lib/prism/serialize.rb -srcs: $(srcdir)/lib/prism/serialize.rb -$(srcdir)/lib/prism/serialize.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/serialize.rb.erb - $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/serialize.rb $(srcdir)/lib/prism/serialize.rb - -main: $(srcdir)/lib/prism/visitor.rb -srcs: $(srcdir)/lib/prism/visitor.rb -$(srcdir)/lib/prism/visitor.rb: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/lib/prism/visitor.rb.erb - $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb lib/prism/visitor.rb $(srcdir)/lib/prism/visitor.rb - -srcs: $(top_srcdir)/prism/api_node.c -$(top_srcdir)/prism/api_node.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/ext/prism/api_node.c.erb - $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb ext/prism/api_node.c $@ - -srcs: $(top_srcdir)/prism/ast.h -$(top_srcdir)/prism/ast.h: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/include/prism/ast.h.erb - $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb include/prism/ast.h $@ - -srcs: $(top_srcdir)/prism/diagnostic.c -$(top_srcdir)/prism/diagnostic.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/diagnostic.c.erb - $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb src/diagnostic.c $@ - -srcs: $(top_srcdir)/prism/diagnostic.h -$(top_srcdir)/prism/diagnostic.h: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/include/prism/diagnostic.h.erb - $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb include/prism/diagnostic.h $@ - -srcs: $(top_srcdir)/prism/node.c -$(top_srcdir)/prism/node.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/node.c.erb - $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb src/node.c $@ - -srcs: $(top_srcdir)/prism/prettyprint.c -$(top_srcdir)/prism/prettyprint.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/prettyprint.c.erb - $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb src/prettyprint.c $@ - -srcs: $(top_srcdir)/prism/serialize.c -$(top_srcdir)/prism/serialize.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/serialize.c.erb - $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb src/serialize.c $@ - -srcs: $(top_srcdir)/prism/token_type.c -$(top_srcdir)/prism/token_type.c: $(PRISM_SRCDIR)/config.yml $(PRISM_SRCDIR)/templates/template.rb $(PRISM_SRCDIR)/templates/src/token_type.c.erb - $(Q) $(BASERUBY) $(PRISM_SRCDIR)/templates/template.rb src/token_type.c $@ +$(PRISM_SRCDIR)/srcs.mk: $(HAVE_BASERUBY:yes=$(PRISM_SRCDIR)/templates/template.rb) \ + $(HAVE_BASERUBY:yes=$(PRISM_SRCDIR)/generate-srcs.mk.rb) + $(ECHO) Updating prism/srcs.mk + $(BASERUBY) $(PRISM_SRCDIR)/generate-srcs.mk.rb > $@ + +srcs: $(PRISM_SRCDIR)/srcs.mk EXPORTOBJS = $(DLNOBJ) \ localeinit.$(OBJEXT) \ @@ -797,7 +727,8 @@ clean-srcs-local:: realclean-srcs-local:: clean-srcs-local $(Q)$(CHDIR) $(srcdir) && $(RM) \ parse.c parse.h lex.c enc/trans/newline.c $(PRELUDES) revision.h \ - id.c id.h probes.dmyh configure aclocal.m4 tool/config.guess tool/config.sub gems/*.gem \ + id.c id.h probes.dmyh configure aclocal.m4 tool/config.guess tool/config.sub \ + $(PRISM_SRCDIR)/srcs.mk gems/*.gem \ || $(NULLCMD) clean-srcs-ext:: @@ -2046,3 +1977,5 @@ help: PHONY $(CROSS_COMPILING:yes=)builtin.$(OBJEXT): {$(VPATH)}mini_builtin.c $(CROSS_COMPILING:yes=)builtin.$(OBJEXT): {$(VPATH)}miniprelude.c + +!include $(srcdir)/prism/srcs.mk diff --git a/configure.ac b/configure.ac index c8018cdabb490a..366ffe1e05a88c 100644 --- a/configure.ac +++ b/configure.ac @@ -4698,8 +4698,9 @@ AC_CONFIG_FILES(Makefile:template/Makefile.in, [ sed '/^MISSING/s/\$U\././g;/^VCS *=/s#@VCS@#'"$VCS"'#;/^VCSUP *=/s#@VCSUP@#'"$VCSUP"'#' Makefile echo; test x"$EXEEXT" = x || echo 'miniruby: miniruby$(EXEEXT)' AS_IF([test "$gnumake" != yes], [ - echo ['$(MKFILES): $(srcdir)/common.mk $(srcdir)/depend'] - sed ['s/{\$([^(){}]*)[^{}]*}//g'] ${srcdir}/common.mk ${srcdir}/depend + echo ['$(MKFILES): $(srcdir)/common.mk $(srcdir)/depend $(srcdir)/prism/srcs.mk'] + sed ['s/{\$([^(){}]*)[^{}]*}//g;/^!/d'] ${srcdir}/common.mk ${srcdir}/depend + cat ${srcdir}/prism/srcs.mk AS_IF([test "$YJIT_SUPPORT" = yes], [ cat ${srcdir}/yjit/not_gmake.mk echo ['$(MKFILES): ${srcdir}/yjit/not_gmake.mk'] diff --git a/prism/generate-srcs.mk.rb b/prism/generate-srcs.mk.rb new file mode 100644 index 00000000000000..af031ef2e4a71a --- /dev/null +++ b/prism/generate-srcs.mk.rb @@ -0,0 +1,17 @@ +require_relative 'templates/template' + +puts %[ +PRISM_TEMPLATES_DIR = $(PRISM_SRCDIR)/templates +PRISM_TEMPLATE = $(PRISM_TEMPLATES_DIR)/template.rb +PRISM_CONFIG = $(PRISM_SRCDIR)/config.yml +] + +Prism::Template::TEMPLATES.map do |t| + /\.(?:[ch]|rb)\z/ =~ t or next + s = t.sub(%r[\A(?:(src)|ext|include)/]) {$1 && 'prism/'} + puts %[ +main srcs: $(srcdir)/#{s} +$(srcdir)/#{s}: $(PRISM_CONFIG) $(PRISM_TEMPLATE) $(PRISM_TEMPLATES_DIR)/#{t}.erb +\t$(Q) $(BASERUBY) $(PRISM_TEMPLATE) #{t} $@ +] +end diff --git a/template/Makefile.in b/template/Makefile.in index e1749bf3eb1ba5..daecd1debe9eb2 100644 --- a/template/Makefile.in +++ b/template/Makefile.in @@ -427,7 +427,7 @@ $(MKFILES): config.status $(srcdir)/version.h $(ABI_VERSION_HDR) { echo "$@ updated, restart."; exit 1; } uncommon.mk: $(srcdir)/common.mk $(srcdir)/depend - sed 's/{\$$([^(){}]*)[^{}]*}//g' $(srcdir)/common.mk $(srcdir)/depend > $@ + sed -f $(srcdir)/tool/prereq.status $(srcdir)/common.mk $(srcdir)/depend > $@ .PHONY: reconfig reconfig-args = $(srcdir)/$(CONFIGURE) $(yes_silence:yes=--silent) $(configure_args) diff --git a/tool/make-snapshot b/tool/make-snapshot index c7ccc468d4c256..99609a8977f19c 100755 --- a/tool/make-snapshot +++ b/tool/make-snapshot @@ -480,7 +480,7 @@ def package(vcs, rev, destdir, tmp = nil) vars["UNICODE_VERSION"] = $unicode_version if $unicode_version args = vars.dup mk.gsub!(/@([A-Za-z_]\w*)@/) {args.delete($1); vars[$1] || ENV[$1]} - mk << commonmk.gsub(/\{\$([^(){}]*)[^{}]*\}/, "").sub(/^revision\.tmp::$/, '\& Makefile') + mk << commonmk.gsub(/\{\$([^(){}]*)[^{}]*\}/, "").gsub(/^!/, '-').sub(/^revision\.tmp::$/, '\& Makefile') mk << <<-'APPEND' update-download:: touch-unicode-files diff --git a/tool/prereq.status b/tool/prereq.status index 6de00c8a92bee8..da92460c8d66a1 100644 --- a/tool/prereq.status +++ b/tool/prereq.status @@ -41,4 +41,5 @@ s,@rubylibprefix@,,g s,@srcdir@,.,g s/@[A-Za-z][A-Za-z0-9_]*@//g -s/{\$([A-Za-z]*)}//g +s/{\$([^(){}]*)}//g +s/^!/-/ diff --git a/win32/setup.mak b/win32/setup.mak index 275ccda3bb6578..6af70d58300561 100644 --- a/win32/setup.mak +++ b/win32/setup.mak @@ -238,6 +238,16 @@ MACHINE = x86 @echo # ENCODING>>$(MAKEFILE) @$(MAKE) -l -f $(srcdir)/win32/enc-setup.mak srcdir="$(srcdir)" MAKEFILE=$(MAKEFILE) +!ifdef BASERUBY +ruby = $(BASERUBY) +!else ifndef ruby +ruby = ruby +!endif +$(srcdir)/prism/srcs.mk: + $(ruby:/=\) $(srcdir)/prism/generate-srcs.mk.rb > $@ + +-epilogue-: $(srcdir)/prism/srcs.mk + -epilogue-: nul @type << >>$(MAKEFILE) From 449cc2501ec453c5d1511e7e5353d55793cad5ab Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 1 Aug 2025 21:30:34 +0900 Subject: [PATCH 15/60] Use `File` instead of `IO`, for read/write singleton methods --- tool/make-snapshot | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tool/make-snapshot b/tool/make-snapshot index 99609a8977f19c..7a9797d13d2d9f 100755 --- a/tool/make-snapshot +++ b/tool/make-snapshot @@ -334,7 +334,7 @@ def package(vcs, rev, destdir, tmp = nil) FileUtils.rm(file, verbose: $VERBOSE) end - status = IO.read(File.dirname(__FILE__) + "/prereq.status") + status = File.read(File.dirname(__FILE__) + "/prereq.status") Dir.chdir(tmp) if tmp if !File.directory?(v) @@ -346,10 +346,10 @@ def package(vcs, rev, destdir, tmp = nil) File.open("#{v}/revision.h", "wb") {|f| f.puts vcs.revision_header(revision, modified) } - version ||= (versionhdr = IO.read("#{v}/version.h"))[RUBY_VERSION_PATTERN, 1] + version ||= (versionhdr = File.read("#{v}/version.h"))[RUBY_VERSION_PATTERN, 1] version ||= begin - include_ruby_versionhdr = IO.read("#{v}/include/ruby/version.h") + include_ruby_versionhdr = File.read("#{v}/include/ruby/version.h") api_major_version = include_ruby_versionhdr[/^\#define\s+RUBY_API_VERSION_MAJOR\s+([\d.]+)/, 1] api_minor_version = include_ruby_versionhdr[/^\#define\s+RUBY_API_VERSION_MINOR\s+([\d.]+)/, 1] version_teeny = versionhdr[/^\#define\s+RUBY_VERSION_TEENY\s+(\d+)/, 1] @@ -358,14 +358,14 @@ def package(vcs, rev, destdir, tmp = nil) version or return if patchlevel unless tag.empty? - versionhdr ||= IO.read("#{v}/version.h") + versionhdr ||= File.read("#{v}/version.h") patchlevel = versionhdr[/^\#define\s+RUBY_PATCHLEVEL\s+(\d+)/, 1] tag = (patchlevel ? "p#{patchlevel}" : vcs.revision_name(revision)) end elsif prerelease - versionhdr ||= IO.read("#{v}/version.h") + versionhdr ||= File.read("#{v}/version.h") versionhdr.sub!(/^\#\s*define\s+RUBY_PATCHLEVEL_STR\s+"\K.+?(?=")/, tag) or raise "no match of RUBY_PATCHLEVEL_STR to replace" - IO.write("#{v}/version.h", versionhdr) + File.write("#{v}/version.h", versionhdr) else tag ||= vcs.revision_name(revision) end @@ -439,11 +439,11 @@ def package(vcs, rev, destdir, tmp = nil) clean.add("autom4te.cache") clean.add("enc/unicode/data") print "creating prerequisites..." - if File.file?("common.mk") && /^prereq/ =~ commonmk = IO.read("common.mk") + if File.file?("common.mk") && /^prereq/ =~ commonmk = File.read("common.mk") puts extout = clean.add('tmp') begin - status = IO.read("tool/prereq.status") + status = File.read("tool/prereq.status") rescue Errno::ENOENT # use fallback file end @@ -456,7 +456,7 @@ def package(vcs, rev, destdir, tmp = nil) File.binwrite("#{defaults}/ruby.rb", "") miniruby = ENV['MINIRUBY'] + " -I. -I#{extout} -rcross" baseruby = ENV["BASERUBY"] - mk = (IO.read("template/Makefile.in") rescue IO.read("Makefile.in")). + mk = (File.read("template/Makefile.in") rescue File.read("Makefile.in")). gsub(/^@.*\n/, '') vars = { "EXTOUT"=>extout, From ddbfe353c2100400f60d6bd012cc175ca4b518ab Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 1 Aug 2025 21:32:17 +0900 Subject: [PATCH 16/60] Use autogen.sh if exists --- tool/make-snapshot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/make-snapshot b/tool/make-snapshot index 7a9797d13d2d9f..7d4fce4f153172 100755 --- a/tool/make-snapshot +++ b/tool/make-snapshot @@ -430,7 +430,7 @@ def package(vcs, rev, destdir, tmp = nil) puts "cross.rb:", File.read("cross.rb").gsub(/^/, "> "), "" if $VERBOSE unless File.exist?("configure") print "creating configure..." - unless system([ENV["AUTOCONF"]]*2) + unless system(File.exist?(gen = "./autogen.sh") ? gen : [ENV["AUTOCONF"]]*2) puts $colorize.fail(" failed") return end From 3c1244ab836226c324519d3d77bf712ec9168de0 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 1 Aug 2025 22:42:23 +0900 Subject: [PATCH 17/60] Define targets for packages also in common.mk --- common.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common.mk b/common.mk index e2b6a2f685b979..f4ea06c2631449 100644 --- a/common.mk +++ b/common.mk @@ -1846,6 +1846,9 @@ clean-gems: CLEAN_CACHE = clean-extlibs +prepare-package: prereq after-update +clean-cache: $(CLEAN_CACHE) + info: info-program info-libruby_a info-libruby_so info-arch info-program: PHONY @echo PROGRAM=$(PROGRAM) From 75a968d88af62dda468b5fb0481279c638483eab Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 1 Aug 2025 21:42:36 +0900 Subject: [PATCH 18/60] Add the recipe to fix/update depend files --- .github/workflows/check_dependencies.yml | 6 +----- defs/gmake.mk | 3 +++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml index 6d85cf8e9c043a..db93e90efb7c10 100644 --- a/.github/workflows/check_dependencies.yml +++ b/.github/workflows/check_dependencies.yml @@ -48,11 +48,7 @@ jobs: - name: Run configure run: ./configure -C --disable-install-doc --disable-rubygems --with-gcc 'optflags=-O0' 'debugflags=-save-temps=obj -g' - - run: make all golf - - - run: ./goruby -veh - - - run: ruby tool/update-deps --fix + - run: make fix-depends - run: git diff --no-ext-diff --ignore-submodules --exit-code diff --git a/defs/gmake.mk b/defs/gmake.mk index 6382e3d0031832..bd4b8b8e39f201 100644 --- a/defs/gmake.mk +++ b/defs/gmake.mk @@ -505,6 +505,9 @@ update-deps: $(GIT) --git-dir=$(GIT_DIR) merge --no-edit --ff-only $(update_deps) $(GIT) --git-dir=$(GIT_DIR) branch --delete $(update_deps) +fix-depends check-depends: all hello + $(BASERUBY) -C $(srcdir) tool/update-deps $(if $(filter fix-%,$@),--fix) + # order-only-prerequisites doesn't work for $(RUBYSPEC_CAPIEXT) # because the same named directory exists in the source tree. $(RUBYSPEC_CAPIEXT)/%.$(DLEXT): $(srcdir)/$(RUBYSPEC_CAPIEXT)/%.c $(RUBYSPEC_CAPIEXT_DEPS) \ From 6e10267714817424049049dadd14e931aa25bf01 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 6 Aug 2025 10:14:48 +0900 Subject: [PATCH 19/60] [rubygems/rubygems] Removed to workaround for Bundler 2.2. The current oldest support Ruby version is 3.2. And Ruby 3.2 bundled Bundler 2.5. It means RG 4.0 can drop to support Bundler 2.2. https://github.com/rubygems/rubygems/commit/592ac09b5c --- lib/rubygems/installer.rb | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb index 2b3200223a5db7..d0092899a40263 100644 --- a/lib/rubygems/installer.rb +++ b/lib/rubygems/installer.rb @@ -67,23 +67,6 @@ class Gem::Installer attr_reader :package class << self - # - # Changes in rubygems to lazily loading `rubygems/command` (in order to - # lazily load `optparse` as a side effect) affect bundler's custom installer - # which uses `Gem::Command` without requiring it (up until bundler 2.2.29). - # This hook is to compensate for that missing require. - # - # TODO: Remove when rubygems no longer supports running on bundler older - # than 2.2.29. - - def inherited(klass) - if klass.name == "Bundler::RubyGemsGemInstaller" - require "rubygems/command" - end - - super(klass) - end - ## # Overrides the executable format. # From d4bf58b56e7c31b9e838d9ff86e2b13027cb54ea Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 28 Jul 2025 11:08:21 +1200 Subject: [PATCH 20/60] [rubygems/rubygems] Don't worry about missing Makefile. https://github.com/rubygems/rubygems/commit/0e92346d88 --- lib/rubygems/ext/builder.rb | 6 +++- lib/rubygems/ext/ext_conf_builder.rb | 4 +++ .../rubygems/test_gem_ext_ext_conf_builder.rb | 33 ++++++++----------- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb index 05cd735bd90d4f..b47996d0920bb3 100644 --- a/lib/rubygems/ext/builder.rb +++ b/lib/rubygems/ext/builder.rb @@ -11,6 +11,9 @@ class Gem::Ext::Builder include Gem::UserInteraction + class NoMakefileError < Gem::InstallError + end + attr_accessor :build_args # :nodoc: def self.class_name @@ -21,7 +24,8 @@ def self.class_name def self.make(dest_path, results, make_dir = Dir.pwd, sitedir = nil, targets = ["clean", "", "install"], target_rbconfig: Gem.target_rbconfig) unless File.exist? File.join(make_dir, "Makefile") - raise Gem::InstallError, "Makefile not found" + # No makefile exists, nothing to do. + raise NoMakefileError, "No Makefile found in #{make_dir}" end # try to find make program from Ruby configure arguments first diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb index e652a221f83b32..8aa15962a0f307 100644 --- a/lib/rubygems/ext/ext_conf_builder.rb +++ b/lib/rubygems/ext/ext_conf_builder.rb @@ -66,6 +66,10 @@ def self.build(extension, dest_path, results, args=[], lib_dir=nil, extension_di end results + rescue Gem::Ext::Builder::NoMakefileError => error + results << error.message + results << "Skipping make for #{extension} as no Makefile was found." + # We are good, do not re-raise the error. ensure FileUtils.rm_rf tmp_dest if tmp_dest end diff --git a/test/rubygems/test_gem_ext_ext_conf_builder.rb b/test/rubygems/test_gem_ext_ext_conf_builder.rb index 218c6f3d5e3678..bc383e5540a9e3 100644 --- a/test/rubygems/test_gem_ext_ext_conf_builder.rb +++ b/test/rubygems/test_gem_ext_ext_conf_builder.rb @@ -15,15 +15,12 @@ def setup end def test_class_build - if Gem.java_platform? - pend("failing on jruby") - end - if vc_windows? && !nmake_found? pend("test_class_build skipped - nmake not found") end File.open File.join(@ext, "extconf.rb"), "w" do |extconf| + extconf.puts "return if Gem.java_platform?" extconf.puts "require 'mkmf'\ncreate_makefile 'foo'" end @@ -35,20 +32,22 @@ def test_class_build assert_match(/^current directory:/, output[0]) assert_match(/^#{Regexp.quote(Gem.ruby)}.* extconf.rb/, output[1]) - assert_equal "creating Makefile\n", output[2] - assert_match(/^current directory:/, output[3]) - assert_contains_make_command "clean", output[4] - assert_contains_make_command "", output[7] - assert_contains_make_command "install", output[10] + + if Gem.java_platform? + assert_includes(output, "Skipping make for extconf.rb as no Makefile was found.") + else + assert_equal "creating Makefile\n", output[2] + assert_match(/^current directory:/, output[3]) + assert_contains_make_command "clean", output[4] + assert_contains_make_command "", output[7] + assert_contains_make_command "install", output[10] + end + assert_empty Dir.glob(File.join(@ext, "siteconf*.rb")) assert_empty Dir.glob(File.join(@ext, ".gem.*")) end def test_class_build_rbconfig_make_prog - if Gem.java_platform? - pend("failing on jruby") - end - configure_args do File.open File.join(@ext, "extconf.rb"), "w" do |extconf| extconf.puts "require 'mkmf'\ncreate_makefile 'foo'" @@ -72,10 +71,6 @@ def test_class_build_env_make env_large_make = ENV.delete "MAKE" ENV["MAKE"] = "anothermake" - if Gem.java_platform? - pend("failing on jruby") - end - configure_args "" do File.open File.join(@ext, "extconf.rb"), "w" do |extconf| extconf.puts "require 'mkmf'\ncreate_makefile 'foo'" @@ -206,11 +201,11 @@ def test_class_make end def test_class_make_no_Makefile - error = assert_raise Gem::InstallError do + error = assert_raise Gem::Ext::Builder::NoMakefileError do Gem::Ext::ExtConfBuilder.make @ext, ["output"], @ext end - assert_equal "Makefile not found", error.message + assert_match(/No Makefile found/, error.message) end def configure_args(args = nil) From 3c669e2d411d06e0458804f1e1d8bd5e9352172c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Fri, 8 Aug 2025 10:56:59 +0200 Subject: [PATCH 21/60] [rubygems/rubygems] Remove unnecessary rubygems filters Since the lowest supported version is now 3.4.1. https://github.com/rubygems/rubygems/commit/d00e03c52e --- spec/bundler/install/gemfile/specific_platform_spec.rb | 4 ++-- spec/bundler/resolver/platform_spec.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb index 71065c36f33507..62540f0488de62 100644 --- a/spec/bundler/install/gemfile/specific_platform_spec.rb +++ b/spec/bundler/install/gemfile/specific_platform_spec.rb @@ -1432,7 +1432,7 @@ end end - it "does not fail when a platform variant is incompatible with the current ruby and another equivalent platform specific variant is part of the resolution", rubygems: ">= 3.3.21" do + it "does not fail when a platform variant is incompatible with the current ruby and another equivalent platform specific variant is part of the resolution" do build_repo4 do build_gem "nokogiri", "1.15.5" @@ -1578,7 +1578,7 @@ end end - it "adds current musl platform, when there are also gnu variants", rubygems: ">= 3.3.21" do + it "adds current musl platform, when there are also gnu variants" do build_repo4 do build_gem "rcee_precompiled", "0.5.0" do |s| s.platform = "x86_64-linux-gnu" diff --git a/spec/bundler/resolver/platform_spec.rb b/spec/bundler/resolver/platform_spec.rb index 13f3e152828e16..a1d095d024de10 100644 --- a/spec/bundler/resolver/platform_spec.rb +++ b/spec/bundler/resolver/platform_spec.rb @@ -387,7 +387,7 @@ should_resolve_as %w[thin-1.2.7-x64-mingw-ucrt] end - it "finds universal-mingw gems on x64-mingw-ucrt", rubygems: ">= 3.3.18" do + it "finds universal-mingw gems on x64-mingw-ucrt" do platform "x64-mingw-ucrt" dep "win32-api" should_resolve_as %w[win32-api-1.5.1-universal-mingw32] From a6aa8e67f16cafb74cd86d0ba2467755e56bab20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Fri, 8 Aug 2025 10:58:13 +0200 Subject: [PATCH 22/60] [rubygems/rubygems] Fix spec no longer run since rubygems timeout was renamed https://github.com/rubygems/rubygems/commit/4d0c058e6a --- spec/bundler/runtime/inline_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/bundler/runtime/inline_spec.rb b/spec/bundler/runtime/inline_spec.rb index 0467d8b14abdcb..0d4c4df08cb237 100644 --- a/spec/bundler/runtime/inline_spec.rb +++ b/spec/bundler/runtime/inline_spec.rb @@ -595,7 +595,7 @@ def confirm(msg, newline = nil) skip "timeout isn't a default gem" if default_timeout_version.empty? # This only works on RubyGems 3.5.0 or higher - ruby "require 'rubygems/timeout'", raise_on_error: false + ruby "require 'rubygems/vendored_timeout'", raise_on_error: false skip "rubygems under test does not yet vendor timeout" unless last_command.success? build_repo4 do From 2e983280e27c6e076546c0444a9489c832e9b32b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Fri, 8 Aug 2025 11:00:54 +0200 Subject: [PATCH 23/60] [rubygems/rubygems] Use a rubygems filter for the timeout spec For consistency. https://github.com/rubygems/rubygems/commit/3e3364e19f --- spec/bundler/runtime/inline_spec.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/spec/bundler/runtime/inline_spec.rb b/spec/bundler/runtime/inline_spec.rb index 0d4c4df08cb237..cffaab35799aca 100644 --- a/spec/bundler/runtime/inline_spec.rb +++ b/spec/bundler/runtime/inline_spec.rb @@ -590,14 +590,10 @@ def confirm(msg, newline = nil) expect(err).to be_empty end - it "does not load default timeout" do + it "does not load default timeout", rubygems: ">= 3.5.0" do default_timeout_version = ruby "gem 'timeout', '< 999999'; require 'timeout'; puts Timeout::VERSION", raise_on_error: false skip "timeout isn't a default gem" if default_timeout_version.empty? - # This only works on RubyGems 3.5.0 or higher - ruby "require 'rubygems/vendored_timeout'", raise_on_error: false - skip "rubygems under test does not yet vendor timeout" unless last_command.success? - build_repo4 do build_gem "timeout", "999" end From f074f8260aae2cb541d13f87a990404038c1b6e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Fri, 8 Aug 2025 11:07:04 +0200 Subject: [PATCH 24/60] [rubygems/rubygems] Fix `bundle cache --no-all` not printing a deprecation warning Like others, it's a remembered option which we are deprecating in favor of configuration. https://github.com/rubygems/rubygems/commit/9ea55e0df2 --- lib/bundler/cli.rb | 1 + spec/bundler/other/major_deprecation_spec.rb | 22 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index ea85f9af22ab24..23c20fc7b3c307 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -412,6 +412,7 @@ def fund D def cache print_remembered_flag_deprecation("--all", "cache_all", "true") if ARGV.include?("--all") + print_remembered_flag_deprecation("--no-all", "cache_all", "false") if ARGV.include?("--no-all") if flag_passed?("--path") message = diff --git a/spec/bundler/other/major_deprecation_spec.rb b/spec/bundler/other/major_deprecation_spec.rb index 51d490ea7247ac..55a30ad157c1e7 100644 --- a/spec/bundler/other/major_deprecation_spec.rb +++ b/spec/bundler/other/major_deprecation_spec.rb @@ -199,6 +199,28 @@ pending "fails with a helpful error", bundler: "4" end + context "bundle cache --no-all" do + before do + install_gemfile <<-G + source "https://gem.repo1" + gem "myrack" + G + + bundle "cache --no-all", raise_on_error: false + end + + it "should print a deprecation warning" do + expect(deprecations).to include( + "The `--no-all` flag is deprecated because it relies on being " \ + "remembered across bundler invocations, which bundler will no " \ + "longer do in future versions. Instead please use `bundle config set " \ + "cache_all false`, and stop using this flag" + ) + end + + pending "fails with a helpful error", bundler: "4" + end + context "bundle cache --path" do before do install_gemfile <<-G From 8d5f00c5371ff71f33ef57b1419fd8d4b1aa9074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Fri, 8 Aug 2025 12:42:16 +0200 Subject: [PATCH 25/60] [rubygems/rubygems] Fix Bundler printing more flags than actually passed in verbose mode This reverts commit https://github.com/rubygems/rubygems/commit/bea87eab0b17 and adds a regression spec for it. https://github.com/rubygems/rubygems/commit/ac98107864 --- lib/bundler/cli.rb | 7 ++++++- spec/bundler/commands/list_spec.rb | 12 ++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 23c20fc7b3c307..8c4e3c36a7e059 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -714,7 +714,12 @@ def print_command command_name = cmd.name return if PARSEABLE_COMMANDS.include?(command_name) command = ["bundle", command_name] + args - command << Thor::Options.to_switches(options.sort_by(&:first)).strip + options_to_print = options.dup + options_to_print.delete_if do |k, v| + next unless o = cmd.options[k] + o.default == v + end + command << Thor::Options.to_switches(options_to_print.sort_by(&:first)).strip command.reject!(&:empty?) Bundler.ui.info "Running `#{command * " "}` with bundler #{Bundler.verbose_version}" end diff --git a/spec/bundler/commands/list_spec.rb b/spec/bundler/commands/list_spec.rb index cc0db9169d64ad..e8ed863310aa73 100644 --- a/spec/bundler/commands/list_spec.rb +++ b/spec/bundler/commands/list_spec.rb @@ -1,6 +1,18 @@ # frozen_string_literal: true RSpec.describe "bundle list" do + context "in verbose mode" do + it "logs the actual flags passed to the command" do + install_gemfile <<-G + source "https://gem.repo1" + G + + bundle "list --verbose" + + expect(out).to include("Running `bundle list --verbose`") + end + end + context "with name-only and paths option" do it "raises an error" do bundle "list --name-only --paths", raise_on_error: false From 813603994a388ad8f22ab831d2b554671dfd23b6 Mon Sep 17 00:00:00 2001 From: Schneems Date: Thu, 29 May 2025 11:11:48 -0500 Subject: [PATCH 26/60] [rubygems/rubygems] Introduce `bundle list --format=json` The `bundle list` command is a convenient way for human to know what gems and versions are available. By introducing a `--format=json` option, we can provide the same information to machines in a stable format that is robust to UI additions or modifications. It indirectly supports `Gemfile.lock` modifications by discouraging external tools from attempting to parse that format. This addition allows for the scripting of installation tools, such as buildpacks, that wish to branch logic based on gem versions. For example: ```ruby require "json" command = "bundle list --format=json" output = `#{command}` raise "Command `#{command}` errored: #{output}" unless $?.success? railties = JSON.parse(output).find {|gem| gem["name"] == railties } if railties && Gem::Version.new(railties["version"]) >= Gem::Version.new("7") puts "Using Rails greater than 7!" end ``` The top level is an object with a single key, "gems", this structure allows us to add other information in the future (should we desire) without having to change the json schema. https://github.com/rubygems/rubygems/commit/9e081b0689 --- lib/bundler/cli.rb | 1 + lib/bundler/cli/list.rb | 35 +++++++++- lib/bundler/man/bundle-list.1 | 5 ++ lib/bundler/man/bundle-list.1.ronn | 5 ++ spec/bundler/commands/list_spec.rb | 108 +++++++++++++++++++++++++++++ 5 files changed, 152 insertions(+), 2 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 8c4e3c36a7e059..65bf8eee836f67 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -299,6 +299,7 @@ def show(gem_name = nil) method_option "name-only", type: :boolean, banner: "print only the gem names" method_option "only-group", type: :array, default: [], banner: "print gems from a given set of groups" method_option "without-group", type: :array, default: [], banner: "print all gems except from a given set of groups" + method_option "format", type: :string, banner: "format output ('json' is the only supported format)" method_option "paths", type: :boolean, banner: "print the path to each gem in the bundle" def list require_relative "cli/list" diff --git a/lib/bundler/cli/list.rb b/lib/bundler/cli/list.rb index f56bf5b86a426a..6a467f45a94ea2 100644 --- a/lib/bundler/cli/list.rb +++ b/lib/bundler/cli/list.rb @@ -1,11 +1,14 @@ # frozen_string_literal: true +require "json" + module Bundler class CLI::List def initialize(options) @options = options @without_group = options["without-group"].map(&:to_sym) @only_group = options["only-group"].map(&:to_sym) + @format = options["format"] end def run @@ -25,6 +28,36 @@ def run end end.reject {|s| s.name == "bundler" }.sort_by(&:name) + case @format + when "json" + print_json(specs: specs) + when nil + print_human(specs: specs) + else + raise InvalidOption, "Unknown option`--format=#{@format}`. Supported formats: `json`" + end + end + + private + + def print_json(specs:) + gems = if @options["name-only"] + specs.map {|s| { name: s.name } } + else + specs.map do |s| + { + name: s.name, + version: s.version.to_s, + git_version: s.git_version&.strip, + }.tap do |h| + h[:path] = s.full_gem_path if @options["paths"] + end + end + end + Bundler.ui.info({ gems: gems }.to_json) + end + + def print_human(specs:) return Bundler.ui.info "No gems in the Gemfile" if specs.empty? return specs.each {|s| Bundler.ui.info s.name } if @options["name-only"] @@ -37,8 +70,6 @@ def run Bundler.ui.info "Use `bundle info` to print more detailed information about a gem" end - private - def verify_group_exists(groups) (@without_group + @only_group).each do |group| raise InvalidOption, "`#{group}` group could not be found." unless groups.include?(group) diff --git a/lib/bundler/man/bundle-list.1 b/lib/bundler/man/bundle-list.1 index 26c2833218093a..7698fe16cc0ea1 100644 --- a/lib/bundler/man/bundle-list.1 +++ b/lib/bundler/man/bundle-list.1 @@ -19,6 +19,8 @@ bundle list \-\-without\-group test bundle list \-\-only\-group dev .P bundle list \-\-only\-group dev test \-\-paths +.P +bundle list \-\-format json .SH "OPTIONS" .TP \fB\-\-name\-only\fR @@ -32,4 +34,7 @@ A space\-separated list of groups of gems to skip during printing\. .TP \fB\-\-only\-group=\fR A space\-separated list of groups of gems to print\. +.TP +\fB\-\-format=FORMAT\fR +Format output ('json' is the only supported format) diff --git a/lib/bundler/man/bundle-list.1.ronn b/lib/bundler/man/bundle-list.1.ronn index 81bee0ac332047..9ec2b132828958 100644 --- a/lib/bundler/man/bundle-list.1.ronn +++ b/lib/bundler/man/bundle-list.1.ronn @@ -21,6 +21,8 @@ bundle list --only-group dev bundle list --only-group dev test --paths +bundle list --format json + ## OPTIONS * `--name-only`: @@ -34,3 +36,6 @@ bundle list --only-group dev test --paths * `--only-group=`: A space-separated list of groups of gems to print. + +* `--format=FORMAT`: + Format output ('json' is the only supported format) diff --git a/spec/bundler/commands/list_spec.rb b/spec/bundler/commands/list_spec.rb index e8ed863310aa73..c890646a816cc4 100644 --- a/spec/bundler/commands/list_spec.rb +++ b/spec/bundler/commands/list_spec.rb @@ -1,6 +1,16 @@ # frozen_string_literal: true +require "json" + RSpec.describe "bundle list" do + def find_gem_name(json:, name:) + parse_json(json)["gems"].detect {|h| h["name"] == name } + end + + def parse_json(json) + JSON.parse(json) + end + context "in verbose mode" do it "logs the actual flags passed to the command" do install_gemfile <<-G @@ -29,6 +39,20 @@ end end + context "with invalid format option" do + before do + install_gemfile <<-G + source "https://gem.repo1" + G + end + + it "raises an error" do + bundle "list --format=nope", raise_on_error: false + + expect(err).to eq "Unknown option`--format=nope`. Supported formats: `json`" + end + end + describe "with without-group option" do before do install_gemfile <<-G @@ -48,6 +72,17 @@ expect(out).to include(" * rails (2.3.2)") expect(out).not_to include(" * rspec (1.2.7)") end + + it "prints the gems not in the specified group with json" do + bundle "list --without-group test --format=json" + + gem = find_gem_name(json: out, name: "myrack") + expect(gem["version"]).to eq("1.0.0") + gem = find_gem_name(json: out, name: "rails") + expect(gem["version"]).to eq("2.3.2") + gem = find_gem_name(json: out, name: "rspec") + expect(gem).to be_nil + end end context "when group is not found" do @@ -66,6 +101,17 @@ expect(out).not_to include(" * rails (2.3.2)") expect(out).not_to include(" * rspec (1.2.7)") end + + it "prints the gems not in the specified groups with json" do + bundle "list --without-group test production --format=json" + + gem = find_gem_name(json: out, name: "myrack") + expect(gem["version"]).to eq("1.0.0") + gem = find_gem_name(json: out, name: "rails") + expect(gem).to be_nil + gem = find_gem_name(json: out, name: "rspec") + expect(gem).to be_nil + end end end @@ -87,6 +133,15 @@ expect(out).to include(" * myrack (1.0.0)") expect(out).not_to include(" * rspec (1.2.7)") end + + it "prints the gems in the specified group with json" do + bundle "list --only-group default --format=json" + + gem = find_gem_name(json: out, name: "myrack") + expect(gem["version"]).to eq("1.0.0") + gem = find_gem_name(json: out, name: "rspec") + expect(gem).to be_nil + end end context "when group is not found" do @@ -105,6 +160,17 @@ expect(out).to include(" * rails (2.3.2)") expect(out).not_to include(" * rspec (1.2.7)") end + + it "prints the gems in the specified groups with json" do + bundle "list --only-group default production --format=json" + + gem = find_gem_name(json: out, name: "myrack") + expect(gem["version"]).to eq("1.0.0") + gem = find_gem_name(json: out, name: "rails") + expect(gem["version"]).to eq("2.3.2") + gem = find_gem_name(json: out, name: "rspec") + expect(gem).to be_nil + end end end @@ -124,6 +190,15 @@ expect(out).to include("myrack") expect(out).to include("rspec") end + + it "prints only the name of the gems in the bundle with json" do + bundle "list --name-only --format=json" + + gem = find_gem_name(json: out, name: "myrack") + expect(gem.keys).to eq(["name"]) + gem = find_gem_name(json: out, name: "rspec") + expect(gem.keys).to eq(["name"]) + end end context "with paths option" do @@ -158,6 +233,27 @@ expect(out).to match(%r{.*\/git_test\-\w}) expect(out).to match(%r{.*\/gemspec_test}) end + + it "prints the path of each gem in the bundle with json" do + bundle "list --paths --format=json" + + gem = find_gem_name(json: out, name: "rails") + expect(gem["path"]).to match(%r{.*\/rails\-2\.3\.2}) + expect(gem["git_version"]).to be_nil + + gem = find_gem_name(json: out, name: "myrack") + expect(gem["path"]).to match(%r{.*\/myrack\-1\.2}) + expect(gem["git_version"]).to be_nil + + gem = find_gem_name(json: out, name: "git_test") + expect(gem["path"]).to match(%r{.*\/git_test\-\w}) + expect(gem["git_version"]).to be_truthy + expect(gem["git_version"].strip).to eq(gem["git_version"]) + + gem = find_gem_name(json: out, name: "gemspec_test") + expect(gem["path"]).to match(%r{.*\/gemspec_test}) + expect(gem["git_version"]).to be_nil + end end context "when no gems are in the gemfile" do @@ -171,6 +267,11 @@ bundle "list" expect(out).to include("No gems in the Gemfile") end + + it "prints empty json" do + bundle "list --format=json" + expect(parse_json(out)["gems"]).to eq([]) + end end context "without options" do @@ -187,6 +288,13 @@ bundle "list" expect(out).to include(" * myrack (1.0.0)") end + + it "lists gems installed in the bundle with json" do + bundle "list --format=json" + + gem = find_gem_name(json: out, name: "myrack") + expect(gem["version"]).to eq("1.0.0") + end end context "when using the ls alias" do From bcc287fc6f33e5680ece7dd737e46fd7894d6ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Fri, 1 Aug 2025 15:19:39 +0200 Subject: [PATCH 27/60] [rubygems/rubygems] Fix `bundle update foo` not upgrading foo to latest in a specific case If upgrading `foo` needs an indirect dependency to be downgraded, Bundler would not be able to upgrade foo. This is because when calculating the latest resolvable version of foo, Bundler was still adding lower bound requirements on the locked versions of all dependencies to avoid downgrades, effectively pinning foo to a version older than the latest. To fix this, instead of creating a second "unlocked" definition to figure out the latest resolvable version, create a second unlocked resolver, and DO NOT add lower bound requirements to it. https://github.com/rubygems/rubygems/commit/00cc0ecc69 --- lib/bundler/definition.rb | 37 +++++++++++++----------- spec/bundler/commands/update_spec.rb | 43 ++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 6c7a3e9c38a613..7d00c234d671d3 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -492,8 +492,6 @@ def unlocking? @unlocking end - attr_writer :source_requirements - def add_checksums @locked_checksums = true @@ -614,7 +612,7 @@ def write_lock(file, preserve_unknown_sections) end def resolver - @resolver ||= Resolver.new(resolution_base, gem_version_promoter, @most_specific_locked_platform) + @resolver ||= new_resolver(resolution_base) end def expanded_dependencies @@ -632,8 +630,7 @@ def resolution_base @resolution_base ||= begin last_resolve = converge_locked_specs remove_invalid_platforms! - new_resolution_platforms = @current_platform_missing ? @new_platforms + [Bundler.local_platform] : @new_platforms - base = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @unlocking_all || @gems_to_unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: new_resolution_platforms) + base = new_resolution_base(last_resolve: last_resolve, unlock: @unlocking_all || @gems_to_unlock) base = additional_base_requirements_to_prevent_downgrades(base) base = additional_base_requirements_to_force_updates(base) base @@ -1136,7 +1133,7 @@ def additional_base_requirements_to_prevent_downgrades(resolution_base) def additional_base_requirements_to_force_updates(resolution_base) return resolution_base if @explicit_unlocks.empty? - full_update = dup_for_full_unlock.resolve + full_update = SpecSet.new(new_resolver_for_full_update.start) @explicit_unlocks.each do |name| version = full_update.version_for(name) resolution_base.base_requirements[name] = Gem::Requirement.new("= #{version}") if version @@ -1144,17 +1141,6 @@ def additional_base_requirements_to_force_updates(resolution_base) resolution_base end - def dup_for_full_unlock - unlocked_definition = self.class.new(@lockfile, @dependencies, @sources, true, @ruby_version, @optional_groups, @gemfiles) - unlocked_definition.source_requirements = source_requirements - unlocked_definition.gem_version_promoter.tap do |gvp| - gvp.level = gem_version_promoter.level - gvp.strict = gem_version_promoter.strict - gvp.pre = gem_version_promoter.pre - end - unlocked_definition - end - def remove_invalid_platforms! return if Bundler.frozen_bundle? @@ -1173,5 +1159,22 @@ def remove_invalid_platforms! def source_map @source_map ||= SourceMap.new(sources, dependencies, @locked_specs) end + + def new_resolver_for_full_update + new_resolver(unlocked_resolution_base) + end + + def unlocked_resolution_base + new_resolution_base(last_resolve: SpecSet.new([]), unlock: true) + end + + def new_resolution_base(last_resolve:, unlock:) + new_resolution_platforms = @current_platform_missing ? @new_platforms + [Bundler.local_platform] : @new_platforms + Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: new_resolution_platforms) + end + + def new_resolver(base) + Resolver.new(base, gem_version_promoter, @most_specific_locked_platform) + end end end diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb index 22bb1662e6571c..8b0f28ae6e366e 100644 --- a/spec/bundler/commands/update_spec.rb +++ b/spec/bundler/commands/update_spec.rb @@ -445,6 +445,49 @@ expect(out).to include("Installing sneakers 2.11.0").and include("Installing rake 13.0.6") end + it "downgrades indirect dependencies if required to fulfill an explicit upgrade request" do + build_repo4 do + build_gem "rbs", "3.6.1" + build_gem "rbs", "3.9.4" + + build_gem "solargraph", "0.56.0" do |s| + s.add_dependency "rbs", "~> 3.3" + end + + build_gem "solargraph", "0.56.2" do |s| + s.add_dependency "rbs", "~> 3.6.1" + end + end + + gemfile <<~G + source "https://gem.repo4" + + gem 'solargraph', '~> 0.56.0' + G + + lockfile <<~L + GEM + remote: https://gem.repo4/ + specs: + rbs (3.9.4) + solargraph (0.56.0) + rbs (~> 3.3) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + solargraph (~> 0.56.0) + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "lock --update solargraph" + + expect(lockfile).to include("solargraph (0.56.2)") + end + it "does not downgrade direct dependencies unnecessarily" do build_repo4 do build_gem "redis", "4.8.1" From 679e8b1be42b8af2b675815d956eb86479cd1d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Fri, 8 Aug 2025 14:00:22 +0200 Subject: [PATCH 28/60] [rubygems/rubygems] Fix `bundle show --verbose` and recommend it as an alternative to `bundle show --outdated` https://github.com/rubygems/rubygems/commit/7cad1e4947 --- lib/bundler/cli.rb | 4 ++-- lib/bundler/cli/show.rb | 8 ++----- spec/bundler/commands/show_spec.rb | 22 +++++++++++++++++++- spec/bundler/other/major_deprecation_spec.rb | 2 +- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 65bf8eee836f67..84e8ca1c535b12 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -287,8 +287,8 @@ def update(*gems) method_option "outdated", type: :boolean, banner: "Show verbose output including whether gems are outdated." def show(gem_name = nil) if ARGV.include?("--outdated") - message = "the `--outdated` flag to `bundle show` was undocumented and will be removed without replacement" - removed_message = "the `--outdated` flag to `bundle show` was undocumented and has been removed without replacement" + message = "the `--outdated` flag to `bundle show` will be removed in favor of `bundle show --verbose`" + removed_message = "the `--outdated` flag to `bundle show` has been removed in favor of `bundle show --verbose`" SharedHelpers.major_deprecation(2, message, removed_message: removed_message) end require_relative "cli/show" diff --git a/lib/bundler/cli/show.rb b/lib/bundler/cli/show.rb index 13bb8b774b670d..b55eb7bad5aecc 100644 --- a/lib/bundler/cli/show.rb +++ b/lib/bundler/cli/show.rb @@ -57,12 +57,8 @@ def run def fetch_latest_specs definition = Bundler.definition(true) - if options[:outdated] - Bundler.ui.info "Fetching remote specs for outdated check...\n\n" - Bundler.ui.silence { definition.remotely! } - else - definition.with_cache! - end + Bundler.ui.info "Fetching remote specs for outdated check...\n\n" + Bundler.ui.silence { definition.remotely! } Bundler.reset! definition.specs end diff --git a/spec/bundler/commands/show_spec.rb b/spec/bundler/commands/show_spec.rb index 82ec6e51f2e74e..e5440cdf7fa7c5 100644 --- a/spec/bundler/commands/show_spec.rb +++ b/spec/bundler/commands/show_spec.rb @@ -3,8 +3,10 @@ RSpec.describe "bundle show" do context "with a standard Gemfile" do before :each do + build_repo2 + install_gemfile <<-G - source "https://gem.repo1" + source "https://gem.repo2" gem "rails" G end @@ -86,6 +88,24 @@ \tStatus: Up to date MSG end + + it "includes up to date status in summary of gems" do + update_repo2 do + build_gem "rails", "3.0.0" + end + + bundle "show --verbose" + + expect(out).to include <<~MSG + * rails (2.3.2) + \tSummary: This is just a fake gem for testing + \tHomepage: http://example.com + \tStatus: Outdated - 2.3.2 < 3.0.0 + MSG + + # check lockfile is not accidentally updated + expect(lockfile).to include("actionmailer (2.3.2)") + end end context "with a git repo in the Gemfile" do diff --git a/spec/bundler/other/major_deprecation_spec.rb b/spec/bundler/other/major_deprecation_spec.rb index 55a30ad157c1e7..d57abe45f35259 100644 --- a/spec/bundler/other/major_deprecation_spec.rb +++ b/spec/bundler/other/major_deprecation_spec.rb @@ -614,7 +614,7 @@ end it "prints a deprecation warning informing about its removal" do - expect(deprecations).to include("the `--outdated` flag to `bundle show` was undocumented and will be removed without replacement") + expect(deprecations).to include("the `--outdated` flag to `bundle show` will be removed in favor of `bundle show --verbose`") end pending "fails with a helpful message", bundler: "4" From 9983d2bceed3d50196bc755733c15561821b1409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Fri, 8 Aug 2025 14:00:51 +0200 Subject: [PATCH 29/60] [rubygems/rubygems] Delay pending spec We had actually cancelled this change. After a second look though, it does seem like a reasonable plan since `bundle list` makes more sense for listing gems, and `bundle info` makes more sense for showing info about a particular gem. `bundle show` is a strange mix of both. Let's schedule this for Bundler 5. https://github.com/rubygems/rubygems/commit/7071a1e82e --- spec/bundler/commands/show_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/bundler/commands/show_spec.rb b/spec/bundler/commands/show_spec.rb index e5440cdf7fa7c5..ba903ac4957a5a 100644 --- a/spec/bundler/commands/show_spec.rb +++ b/spec/bundler/commands/show_spec.rb @@ -239,6 +239,6 @@ end end -RSpec.describe "bundle show", bundler: "4" do +RSpec.describe "bundle show", bundler: "5" do pending "shows a friendly error about the command removal" end From 302c5ae28f5627f856a99c2fef9b0bb8f695bbef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Mon, 11 Aug 2025 19:40:07 +0200 Subject: [PATCH 30/60] [rubygems/rubygems] Consistently use banner of desc when defining CLI flags https://github.com/rubygems/rubygems/commit/2cbe7ea0a5 --- lib/bundler/cli.rb | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 84e8ca1c535b12..47a39069cc1b56 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -520,11 +520,11 @@ def licenses Viz requires the ruby-graphviz gem (and its dependencies). The associated gems must also be installed via 'bundle install'. D - method_option :file, type: :string, default: "gem_graph", aliases: "-f", desc: "The name to use for the generated file. see format option" - method_option :format, type: :string, default: "png", aliases: "-F", desc: "This is output format option. Supported format is png, jpg, svg, dot ..." - method_option :requirements, type: :boolean, default: false, aliases: "-R", desc: "Set to show the version of each required dependency." - method_option :version, type: :boolean, default: false, aliases: "-v", desc: "Set to show each gem version." - method_option :without, type: :array, default: [], aliases: "-W", banner: "GROUP[ GROUP...]", desc: "Exclude gems that are part of the specified named group." + method_option :file, type: :string, default: "gem_graph", aliases: "-f", banner: "The name to use for the generated file. see format option" + method_option :format, type: :string, default: "png", aliases: "-F", banner: "This is output format option. Supported format is png, jpg, svg, dot ..." + method_option :requirements, type: :boolean, default: false, aliases: "-R", banner: "Set to show the version of each required dependency." + method_option :version, type: :boolean, default: false, aliases: "-v", banner: "Set to show each gem version." + method_option :without, type: :array, default: [], aliases: "-W", banner: "Exclude gems that are part of the specified named group." def viz SharedHelpers.major_deprecation 2, "The `viz` command has been renamed to `graph` and moved to a plugin. See https://github.com/rubygems/bundler-graph" require_relative "cli/viz" @@ -533,19 +533,19 @@ def viz end desc "gem NAME [OPTIONS]", "Creates a skeleton for creating a rubygem" - method_option :exe, type: :boolean, default: false, aliases: ["--bin", "-b"], desc: "Generate a binary executable for your library." - method_option :coc, type: :boolean, desc: "Generate a code of conduct file. Set a default with `bundle config set --global gem.coc true`." - method_option :edit, type: :string, aliases: "-e", required: false, banner: "EDITOR", lazy_default: [ENV["BUNDLER_EDITOR"], ENV["VISUAL"], ENV["EDITOR"]].find {|e| !e.nil? && !e.empty? }, desc: "Open generated gemspec in the specified editor (defaults to $EDITOR or $BUNDLER_EDITOR)" - method_option :ext, type: :string, desc: "Generate the boilerplate for C extension code.", enum: EXTENSIONS - method_option :git, type: :boolean, default: true, desc: "Initialize a git repo inside your library." - method_option :mit, type: :boolean, desc: "Generate an MIT license file. Set a default with `bundle config set --global gem.mit true`." - method_option :rubocop, type: :boolean, desc: "Add rubocop to the generated Rakefile and gemspec. Set a default with `bundle config set --global gem.rubocop true`." - method_option :changelog, type: :boolean, desc: "Generate changelog file. Set a default with `bundle config set --global gem.changelog true`." + method_option :exe, type: :boolean, default: false, aliases: ["--bin", "-b"], banner: "Generate a binary executable for your library." + method_option :coc, type: :boolean, banner: "Generate a code of conduct file. Set a default with `bundle config set --global gem.coc true`." + method_option :edit, type: :string, aliases: "-e", required: false, lazy_default: [ENV["BUNDLER_EDITOR"], ENV["VISUAL"], ENV["EDITOR"]].find {|e| !e.nil? && !e.empty? }, banner: "Open generated gemspec in the specified editor (defaults to $EDITOR or $BUNDLER_EDITOR)" + method_option :ext, type: :string, banner: "Generate the boilerplate for C extension code.", enum: EXTENSIONS + method_option :git, type: :boolean, default: true, banner: "Initialize a git repo inside your library." + method_option :mit, type: :boolean, banner: "Generate an MIT license file. Set a default with `bundle config set --global gem.mit true`." + method_option :rubocop, type: :boolean, banner: "Add rubocop to the generated Rakefile and gemspec. Set a default with `bundle config set --global gem.rubocop true`." + method_option :changelog, type: :boolean, banner: "Generate changelog file. Set a default with `bundle config set --global gem.changelog true`." method_option :test, type: :string, lazy_default: Bundler.settings["gem.test"] || "", aliases: "-t", banner: "Use the specified test framework for your library", enum: %w[rspec minitest test-unit], desc: "Generate a test directory for your library, either rspec, minitest or test-unit. Set a default with `bundle config set --global gem.test (rspec|minitest|test-unit)`." - method_option :ci, type: :string, lazy_default: Bundler.settings["gem.ci"] || "", enum: %w[github gitlab circle], desc: "Generate CI configuration, either GitHub Actions, GitLab CI or CircleCI. Set a default with `bundle config set --global gem.ci (github|gitlab|circle)`" - method_option :linter, type: :string, lazy_default: Bundler.settings["gem.linter"] || "", enum: %w[rubocop standard], desc: "Add a linter and code formatter, either RuboCop or Standard. Set a default with `bundle config set --global gem.linter (rubocop|standard)`" + method_option :ci, type: :string, lazy_default: Bundler.settings["gem.ci"] || "", enum: %w[github gitlab circle], banner: "Generate CI configuration, either GitHub Actions, GitLab CI or CircleCI. Set a default with `bundle config set --global gem.ci (github|gitlab|circle)`" + method_option :linter, type: :string, lazy_default: Bundler.settings["gem.linter"] || "", enum: %w[rubocop standard], banner: "Add a linter and code formatter, either RuboCop or Standard. Set a default with `bundle config set --global gem.linter (rubocop|standard)`" method_option :github_username, type: :string, default: Bundler.settings["gem.github_username"], banner: "Set your username on GitHub", desc: "Fill in GitHub username on README so that you don't have to do it manually. Set a default with `bundle config set --global gem.github_username `." - method_option :bundle, type: :boolean, default: Bundler.settings["gem.bundle"], desc: "Automatically run `bundle install` after creation. Set a default with `bundle config set --global gem.bundle true`" + method_option :bundle, type: :boolean, default: Bundler.settings["gem.bundle"], banner: "Automatically run `bundle install` after creation. Set a default with `bundle config set --global gem.bundle true`" def gem(name) require_relative "cli/gem" From 12c200a78fa525375b3695c7cad97e3013a7de66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Mon, 11 Aug 2025 19:56:37 +0200 Subject: [PATCH 31/60] [rubygems/rubygems] Improve `bundle config` documentation For synopsis, subcommand and flag documentation like other commands do. https://github.com/rubygems/rubygems/commit/f528029756 --- lib/bundler/man/bundle-add.1 | 2 +- lib/bundler/man/bundle-binstubs.1 | 2 +- lib/bundler/man/bundle-cache.1 | 2 +- lib/bundler/man/bundle-check.1 | 2 +- lib/bundler/man/bundle-clean.1 | 2 +- lib/bundler/man/bundle-config.1 | 45 ++++++++++++------ lib/bundler/man/bundle-config.1.ronn | 69 ++++++++++++++++++++-------- lib/bundler/man/bundle-console.1 | 2 +- lib/bundler/man/bundle-doctor.1 | 2 +- lib/bundler/man/bundle-env.1 | 2 +- lib/bundler/man/bundle-exec.1 | 2 +- lib/bundler/man/bundle-fund.1 | 2 +- lib/bundler/man/bundle-gem.1 | 2 +- lib/bundler/man/bundle-help.1 | 2 +- lib/bundler/man/bundle-info.1 | 2 +- lib/bundler/man/bundle-init.1 | 2 +- lib/bundler/man/bundle-inject.1 | 2 +- lib/bundler/man/bundle-install.1 | 2 +- lib/bundler/man/bundle-issue.1 | 2 +- lib/bundler/man/bundle-licenses.1 | 2 +- lib/bundler/man/bundle-list.1 | 2 +- lib/bundler/man/bundle-lock.1 | 2 +- lib/bundler/man/bundle-open.1 | 2 +- lib/bundler/man/bundle-outdated.1 | 2 +- lib/bundler/man/bundle-platform.1 | 2 +- lib/bundler/man/bundle-plugin.1 | 2 +- lib/bundler/man/bundle-pristine.1 | 2 +- lib/bundler/man/bundle-remove.1 | 2 +- lib/bundler/man/bundle-show.1 | 2 +- lib/bundler/man/bundle-update.1 | 2 +- lib/bundler/man/bundle-version.1 | 2 +- lib/bundler/man/bundle-viz.1 | 2 +- lib/bundler/man/bundle.1 | 2 +- lib/bundler/man/gemfile.5 | 2 +- 34 files changed, 112 insertions(+), 66 deletions(-) diff --git a/lib/bundler/man/bundle-add.1 b/lib/bundler/man/bundle-add.1 index baa4a376c83b4c..74422cbd313ddf 100644 --- a/lib/bundler/man/bundle-add.1 +++ b/lib/bundler/man/bundle-add.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-ADD" "1" "July 2025" "" +.TH "BUNDLE\-ADD" "1" "August 2025" "" .SH "NAME" \fBbundle\-add\fR \- Add gem to the Gemfile and run bundle install .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-binstubs.1 b/lib/bundler/man/bundle-binstubs.1 index 0131dd663ea38e..be9c6d0d097390 100644 --- a/lib/bundler/man/bundle-binstubs.1 +++ b/lib/bundler/man/bundle-binstubs.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-BINSTUBS" "1" "July 2025" "" +.TH "BUNDLE\-BINSTUBS" "1" "August 2025" "" .SH "NAME" \fBbundle\-binstubs\fR \- Install the binstubs of the listed gems .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-cache.1 b/lib/bundler/man/bundle-cache.1 index 4c5dcff052bc42..1d16b164fa2806 100644 --- a/lib/bundler/man/bundle-cache.1 +++ b/lib/bundler/man/bundle-cache.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-CACHE" "1" "July 2025" "" +.TH "BUNDLE\-CACHE" "1" "August 2025" "" .SH "NAME" \fBbundle\-cache\fR \- Package your needed \fB\.gem\fR files into your application .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-check.1 b/lib/bundler/man/bundle-check.1 index 376becdbe47d5b..4f0d51bb71472e 100644 --- a/lib/bundler/man/bundle-check.1 +++ b/lib/bundler/man/bundle-check.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-CHECK" "1" "July 2025" "" +.TH "BUNDLE\-CHECK" "1" "August 2025" "" .SH "NAME" \fBbundle\-check\fR \- Verifies if dependencies are satisfied by installed gems .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-clean.1 b/lib/bundler/man/bundle-clean.1 index 85e6186f49cf2e..df66523829d35e 100644 --- a/lib/bundler/man/bundle-clean.1 +++ b/lib/bundler/man/bundle-clean.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-CLEAN" "1" "July 2025" "" +.TH "BUNDLE\-CLEAN" "1" "August 2025" "" .SH "NAME" \fBbundle\-clean\fR \- Cleans up unused gems in your bundler directory .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-config.1 b/lib/bundler/man/bundle-config.1 index 6f12696ab6961f..5157e514531238 100644 --- a/lib/bundler/man/bundle-config.1 +++ b/lib/bundler/man/bundle-config.1 @@ -1,16 +1,16 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-CONFIG" "1" "July 2025" "" +.TH "BUNDLE\-CONFIG" "1" "August 2025" "" .SH "NAME" \fBbundle\-config\fR \- Set bundler configuration options .SH "SYNOPSIS" -\fBbundle config\fR list +\fBbundle config\fR [list] .br -\fBbundle config\fR [get] NAME +\fBbundle config\fR [get [\-\-local|\-\-global]] NAME .br -\fBbundle config\fR [set] NAME VALUE +\fBbundle config\fR [set [\-\-local|\-\-global]] NAME VALUE .br -\fBbundle config\fR unset NAME +\fBbundle config\fR unset [\-\-local|\-\-global] NAME .SH "DESCRIPTION" This command allows you to interact with Bundler's configuration system\. .P @@ -25,23 +25,40 @@ Global config (\fB~/\.bundle/config\fR) Bundler default config .IP "" 0 .P +Executing \fBbundle\fR with the \fBBUNDLE_IGNORE_CONFIG\fR environment variable set will cause it to ignore all configuration\. +.SH "SUB\-COMMANDS" +.SS "list (default command)" Executing \fBbundle config list\fR will print a list of all bundler configuration for the current bundle, and where that configuration was set\. +.SS "get" +Executing \fBbundle config get \fR will print the value of that configuration setting, and all locations where it was set\. .P -Executing \fBbundle config get \fR will print the value of that configuration setting, and where it was set\. -.P -Executing \fBbundle config set \fR defaults to setting \fBlocal\fR configuration if executing from within a local application, otherwise it will set \fBglobal\fR configuration\. See \fB\-\-local\fR and \fB\-\-global\fR options below\. +\fBOPTIONS\fR +.TP +\fB\-\-local\fR +Get configuration from configuration file for the local application, namely, \fB/\.bundle/config\fR, or \fB$BUNDLE_APP_CONFIG/config\fR if \fBBUNDLE_APP_CONFIG\fR is set\. +.TP +\fB\-\-global\fR +Get configuration from configuration file global to all bundles executed as the current user, namely, from \fB~/\.bundle/config\fR\. +.SS "set" +Executing \fBbundle config set \fR defaults to setting \fBlocal\fR configuration if executing from within a local application, otherwise it will set \fBglobal\fR configuration\. .P +\fBOPTIONS\fR +.TP +\fB\-\-local\fR Executing \fBbundle config set \-\-local \fR will set that configuration in the directory for the local application\. The configuration will be stored in \fB/\.bundle/config\fR\. If \fBBUNDLE_APP_CONFIG\fR is set, the configuration will be stored in \fB$BUNDLE_APP_CONFIG/config\fR\. -.P +.TP +\fB\-\-global\fR Executing \fBbundle config set \-\-global \fR will set that configuration to the value specified for all bundles executed as the current user\. The configuration will be stored in \fB~/\.bundle/config\fR\. If \fIname\fR already is set, \fIname\fR will be overridden and user will be warned\. -.P +.SS "unset" Executing \fBbundle config unset \fR will delete the configuration in both local and global sources\. .P -Executing \fBbundle config unset \-\-global \fR will delete the configuration only from the user configuration\. -.P +\fBOPTIONS\fR +.TP +\fB\-\-local\fR Executing \fBbundle config unset \-\-local \fR will delete the configuration only from the local application\. -.P -Executing bundle with the \fBBUNDLE_IGNORE_CONFIG\fR environment variable set will cause it to ignore all configuration\. +.TP +\fB\-\-global\fR +Executing \fBbundle config unset \-\-global \fR will delete the configuration only from the user configuration\. .SH "CONFIGURATION KEYS" Configuration keys in bundler have two forms: the canonical form and the environment variable form\. .P diff --git a/lib/bundler/man/bundle-config.1.ronn b/lib/bundler/man/bundle-config.1.ronn index 7f31eb4c39a0ad..2b2e39f03eb7f0 100644 --- a/lib/bundler/man/bundle-config.1.ronn +++ b/lib/bundler/man/bundle-config.1.ronn @@ -3,10 +3,10 @@ bundle-config(1) -- Set bundler configuration options ## SYNOPSIS -`bundle config` list
-`bundle config` [get] NAME
-`bundle config` [set] NAME VALUE
-`bundle config` unset NAME +`bundle config` [list]
+`bundle config` [get [--local|--global]] NAME
+`bundle config` [set [--local|--global]] NAME VALUE
+`bundle config` unset [--local|--global] NAME ## DESCRIPTION @@ -19,38 +19,67 @@ Bundler loads configuration settings in this order: 3. Global config (`~/.bundle/config`) 4. Bundler default config +Executing `bundle` with the `BUNDLE_IGNORE_CONFIG` environment variable set will +cause it to ignore all configuration. + +## SUB-COMMANDS + +### list (default command) + Executing `bundle config list` will print a list of all bundler configuration for the current bundle, and where that configuration was set. +### get + Executing `bundle config get ` will print the value of that configuration -setting, and where it was set. +setting, and all locations where it was set. + +**OPTIONS** + +* `--local`: + Get configuration from configuration file for the local application, namely, + `/.bundle/config`, or `$BUNDLE_APP_CONFIG/config` if + `BUNDLE_APP_CONFIG` is set. + +* `--global`: + Get configuration from configuration file global to all bundles executed as + the current user, namely, from `~/.bundle/config`. + +### set Executing `bundle config set ` defaults to setting `local` configuration if executing from within a local application, otherwise it will -set `global` configuration. See `--local` and `--global` options below. +set `global` configuration. -Executing `bundle config set --local ` will set that configuration -in the directory for the local application. The configuration will be stored in -`/.bundle/config`. If `BUNDLE_APP_CONFIG` is set, the configuration -will be stored in `$BUNDLE_APP_CONFIG/config`. +**OPTIONS** -Executing `bundle config set --global ` will set that -configuration to the value specified for all bundles executed as the current -user. The configuration will be stored in `~/.bundle/config`. If already -is set, will be overridden and user will be warned. +* `--local`: + Executing `bundle config set --local ` will set that configuration + in the directory for the local application. The configuration will be stored in + `/.bundle/config`. If `BUNDLE_APP_CONFIG` is set, the configuration + will be stored in `$BUNDLE_APP_CONFIG/config`. + +* `--global`: + Executing `bundle config set --global ` will set that + configuration to the value specified for all bundles executed as the current + user. The configuration will be stored in `~/.bundle/config`. If already + is set, will be overridden and user will be warned. + +### unset Executing `bundle config unset ` will delete the configuration in both local and global sources. -Executing `bundle config unset --global ` will delete the configuration -only from the user configuration. +**OPTIONS** -Executing `bundle config unset --local ` will delete the configuration -only from the local application. +* `--local`: + Executing `bundle config unset --local ` will delete the configuration + only from the local application. -Executing bundle with the `BUNDLE_IGNORE_CONFIG` environment variable set will -cause it to ignore all configuration. +* `--global`: + Executing `bundle config unset --global ` will delete the configuration + only from the user configuration. ## CONFIGURATION KEYS diff --git a/lib/bundler/man/bundle-console.1 b/lib/bundler/man/bundle-console.1 index a263fef37640ae..18c765372b4230 100644 --- a/lib/bundler/man/bundle-console.1 +++ b/lib/bundler/man/bundle-console.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-CONSOLE" "1" "July 2025" "" +.TH "BUNDLE\-CONSOLE" "1" "August 2025" "" .SH "NAME" \fBbundle\-console\fR \- Open an IRB session with the bundle pre\-loaded .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-doctor.1 b/lib/bundler/man/bundle-doctor.1 index 2b695dc2eef2ec..aa5e8ea2e336bf 100644 --- a/lib/bundler/man/bundle-doctor.1 +++ b/lib/bundler/man/bundle-doctor.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-DOCTOR" "1" "July 2025" "" +.TH "BUNDLE\-DOCTOR" "1" "August 2025" "" .SH "NAME" \fBbundle\-doctor\fR \- Checks the bundle for common problems .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-env.1 b/lib/bundler/man/bundle-env.1 index 3e6c9f6e171de8..28ccc17f03aa9f 100644 --- a/lib/bundler/man/bundle-env.1 +++ b/lib/bundler/man/bundle-env.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-ENV" "1" "July 2025" "" +.TH "BUNDLE\-ENV" "1" "August 2025" "" .SH "NAME" \fBbundle\-env\fR \- Print information about the environment Bundler is running under .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-exec.1 b/lib/bundler/man/bundle-exec.1 index 79cdad0288f498..cd53916cffcc34 100644 --- a/lib/bundler/man/bundle-exec.1 +++ b/lib/bundler/man/bundle-exec.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-EXEC" "1" "July 2025" "" +.TH "BUNDLE\-EXEC" "1" "August 2025" "" .SH "NAME" \fBbundle\-exec\fR \- Execute a command in the context of the bundle .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-fund.1 b/lib/bundler/man/bundle-fund.1 index 3f6e3a46dfec80..a91c1809514aee 100644 --- a/lib/bundler/man/bundle-fund.1 +++ b/lib/bundler/man/bundle-fund.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-FUND" "1" "July 2025" "" +.TH "BUNDLE\-FUND" "1" "August 2025" "" .SH "NAME" \fBbundle\-fund\fR \- Lists information about gems seeking funding assistance .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-gem.1 b/lib/bundler/man/bundle-gem.1 index 44a02c033c70af..5fe27772306930 100644 --- a/lib/bundler/man/bundle-gem.1 +++ b/lib/bundler/man/bundle-gem.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-GEM" "1" "July 2025" "" +.TH "BUNDLE\-GEM" "1" "August 2025" "" .SH "NAME" \fBbundle\-gem\fR \- Generate a project skeleton for creating a rubygem .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-help.1 b/lib/bundler/man/bundle-help.1 index e6bbea6dadbf39..9ea28bef14d082 100644 --- a/lib/bundler/man/bundle-help.1 +++ b/lib/bundler/man/bundle-help.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-HELP" "1" "July 2025" "" +.TH "BUNDLE\-HELP" "1" "August 2025" "" .SH "NAME" \fBbundle\-help\fR \- Displays detailed help for each subcommand .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-info.1 b/lib/bundler/man/bundle-info.1 index 435518e120fe73..29d649d342cc42 100644 --- a/lib/bundler/man/bundle-info.1 +++ b/lib/bundler/man/bundle-info.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-INFO" "1" "July 2025" "" +.TH "BUNDLE\-INFO" "1" "August 2025" "" .SH "NAME" \fBbundle\-info\fR \- Show information for the given gem in your bundle .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-init.1 b/lib/bundler/man/bundle-init.1 index 48b3232b8cba2a..be9399c20f2367 100644 --- a/lib/bundler/man/bundle-init.1 +++ b/lib/bundler/man/bundle-init.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-INIT" "1" "July 2025" "" +.TH "BUNDLE\-INIT" "1" "August 2025" "" .SH "NAME" \fBbundle\-init\fR \- Generates a Gemfile into the current working directory .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-inject.1 b/lib/bundler/man/bundle-inject.1 index abc63c392e525c..7e30e26a4e4637 100644 --- a/lib/bundler/man/bundle-inject.1 +++ b/lib/bundler/man/bundle-inject.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-INJECT" "1" "July 2025" "" +.TH "BUNDLE\-INJECT" "1" "August 2025" "" .SH "NAME" \fBbundle\-inject\fR \- Add named gem(s) with version requirements to Gemfile .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-install.1 b/lib/bundler/man/bundle-install.1 index bf067475584a03..f9bbade2fd59fb 100644 --- a/lib/bundler/man/bundle-install.1 +++ b/lib/bundler/man/bundle-install.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-INSTALL" "1" "July 2025" "" +.TH "BUNDLE\-INSTALL" "1" "August 2025" "" .SH "NAME" \fBbundle\-install\fR \- Install the dependencies specified in your Gemfile .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-issue.1 b/lib/bundler/man/bundle-issue.1 index 668da5712f7cd9..3c7e5305f3721a 100644 --- a/lib/bundler/man/bundle-issue.1 +++ b/lib/bundler/man/bundle-issue.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-ISSUE" "1" "July 2025" "" +.TH "BUNDLE\-ISSUE" "1" "August 2025" "" .SH "NAME" \fBbundle\-issue\fR \- Get help reporting Bundler issues .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-licenses.1 b/lib/bundler/man/bundle-licenses.1 index cccb860854819b..eb0ad5ae405cd3 100644 --- a/lib/bundler/man/bundle-licenses.1 +++ b/lib/bundler/man/bundle-licenses.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-LICENSES" "1" "July 2025" "" +.TH "BUNDLE\-LICENSES" "1" "August 2025" "" .SH "NAME" \fBbundle\-licenses\fR \- Print the license of all gems in the bundle .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-list.1 b/lib/bundler/man/bundle-list.1 index 7698fe16cc0ea1..a345787a5e9ee0 100644 --- a/lib/bundler/man/bundle-list.1 +++ b/lib/bundler/man/bundle-list.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-LIST" "1" "July 2025" "" +.TH "BUNDLE\-LIST" "1" "August 2025" "" .SH "NAME" \fBbundle\-list\fR \- List all the gems in the bundle .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-lock.1 b/lib/bundler/man/bundle-lock.1 index 5faa46da180afb..79aa1e2452f6d3 100644 --- a/lib/bundler/man/bundle-lock.1 +++ b/lib/bundler/man/bundle-lock.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-LOCK" "1" "July 2025" "" +.TH "BUNDLE\-LOCK" "1" "August 2025" "" .SH "NAME" \fBbundle\-lock\fR \- Creates / Updates a lockfile without installing .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-open.1 b/lib/bundler/man/bundle-open.1 index e8a24f35414207..b1c2022f40195b 100644 --- a/lib/bundler/man/bundle-open.1 +++ b/lib/bundler/man/bundle-open.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-OPEN" "1" "July 2025" "" +.TH "BUNDLE\-OPEN" "1" "August 2025" "" .SH "NAME" \fBbundle\-open\fR \- Opens the source directory for a gem in your bundle .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-outdated.1 b/lib/bundler/man/bundle-outdated.1 index 3259b0f0233fa3..d4790f8876a54d 100644 --- a/lib/bundler/man/bundle-outdated.1 +++ b/lib/bundler/man/bundle-outdated.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-OUTDATED" "1" "July 2025" "" +.TH "BUNDLE\-OUTDATED" "1" "August 2025" "" .SH "NAME" \fBbundle\-outdated\fR \- List installed gems with newer versions available .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-platform.1 b/lib/bundler/man/bundle-platform.1 index 1032acc4e6405c..78de506b57b838 100644 --- a/lib/bundler/man/bundle-platform.1 +++ b/lib/bundler/man/bundle-platform.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-PLATFORM" "1" "July 2025" "" +.TH "BUNDLE\-PLATFORM" "1" "August 2025" "" .SH "NAME" \fBbundle\-platform\fR \- Displays platform compatibility information .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-plugin.1 b/lib/bundler/man/bundle-plugin.1 index 5803b7a5549917..9285f128c1b10b 100644 --- a/lib/bundler/man/bundle-plugin.1 +++ b/lib/bundler/man/bundle-plugin.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-PLUGIN" "1" "July 2025" "" +.TH "BUNDLE\-PLUGIN" "1" "August 2025" "" .SH "NAME" \fBbundle\-plugin\fR \- Manage Bundler plugins .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-pristine.1 b/lib/bundler/man/bundle-pristine.1 index 5c7871069c0940..e39f264ceff298 100644 --- a/lib/bundler/man/bundle-pristine.1 +++ b/lib/bundler/man/bundle-pristine.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-PRISTINE" "1" "July 2025" "" +.TH "BUNDLE\-PRISTINE" "1" "August 2025" "" .SH "NAME" \fBbundle\-pristine\fR \- Restores installed gems to their pristine condition .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-remove.1 b/lib/bundler/man/bundle-remove.1 index df8ce3232ab8dc..67508176802bf4 100644 --- a/lib/bundler/man/bundle-remove.1 +++ b/lib/bundler/man/bundle-remove.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-REMOVE" "1" "July 2025" "" +.TH "BUNDLE\-REMOVE" "1" "August 2025" "" .SH "NAME" \fBbundle\-remove\fR \- Removes gems from the Gemfile .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-show.1 b/lib/bundler/man/bundle-show.1 index ca10c00701e85d..67b387559d7ca5 100644 --- a/lib/bundler/man/bundle-show.1 +++ b/lib/bundler/man/bundle-show.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-SHOW" "1" "July 2025" "" +.TH "BUNDLE\-SHOW" "1" "August 2025" "" .SH "NAME" \fBbundle\-show\fR \- Shows all the gems in your bundle, or the path to a gem .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-update.1 b/lib/bundler/man/bundle-update.1 index 8655aa5cd3545e..fbcabb69a8d61f 100644 --- a/lib/bundler/man/bundle-update.1 +++ b/lib/bundler/man/bundle-update.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-UPDATE" "1" "July 2025" "" +.TH "BUNDLE\-UPDATE" "1" "August 2025" "" .SH "NAME" \fBbundle\-update\fR \- Update your gems to the latest available versions .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-version.1 b/lib/bundler/man/bundle-version.1 index e591f597664636..261140ff3816f5 100644 --- a/lib/bundler/man/bundle-version.1 +++ b/lib/bundler/man/bundle-version.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-VERSION" "1" "July 2025" "" +.TH "BUNDLE\-VERSION" "1" "August 2025" "" .SH "NAME" \fBbundle\-version\fR \- Prints Bundler version information .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle-viz.1 b/lib/bundler/man/bundle-viz.1 index b5dd1db7b7f05a..f2570103dcc546 100644 --- a/lib/bundler/man/bundle-viz.1 +++ b/lib/bundler/man/bundle-viz.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE\-VIZ" "1" "July 2025" "" +.TH "BUNDLE\-VIZ" "1" "August 2025" "" .SH "NAME" \fBbundle\-viz\fR \- Generates a visual dependency graph for your Gemfile .SH "SYNOPSIS" diff --git a/lib/bundler/man/bundle.1 b/lib/bundler/man/bundle.1 index 269e28141d3101..f87a98150da609 100644 --- a/lib/bundler/man/bundle.1 +++ b/lib/bundler/man/bundle.1 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "BUNDLE" "1" "July 2025" "" +.TH "BUNDLE" "1" "August 2025" "" .SH "NAME" \fBbundle\fR \- Ruby Dependency Management .SH "SYNOPSIS" diff --git a/lib/bundler/man/gemfile.5 b/lib/bundler/man/gemfile.5 index c926e1ff2d30da..cd04d3d1983d37 100644 --- a/lib/bundler/man/gemfile.5 +++ b/lib/bundler/man/gemfile.5 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 -.TH "GEMFILE" "5" "July 2025" "" +.TH "GEMFILE" "5" "August 2025" "" .SH "NAME" \fBGemfile\fR \- A format for describing gem dependencies for Ruby programs .SH "SYNOPSIS" From f3d62db05a36e4182a6ac9a0d6b1a88477275d08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Mon, 11 Aug 2025 19:57:44 +0200 Subject: [PATCH 32/60] [rubygems/rubygems] Improve `doctor` CLI flag documentation Name default value placeholders in a more standard way. That's what our specs check, but they don't yet work for subcommand flags. https://github.com/rubygems/rubygems/commit/c589899cb8 --- lib/bundler/man/bundle-doctor.1 | 6 +++--- lib/bundler/man/bundle-doctor.1.ronn | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/bundler/man/bundle-doctor.1 b/lib/bundler/man/bundle-doctor.1 index aa5e8ea2e336bf..6d4b0376a68ad8 100644 --- a/lib/bundler/man/bundle-doctor.1 +++ b/lib/bundler/man/bundle-doctor.1 @@ -6,7 +6,7 @@ .SH "SYNOPSIS" \fBbundle doctor [diagnose]\fR [\-\-quiet] [\-\-gemfile=GEMFILE] [\-\-ssl] .br -\fBbundle doctor ssl\fR [\-\-host=HOST] [\-\-tls\-version=VERSION] [\-\-verify\-mode=MODE] +\fBbundle doctor ssl\fR [\-\-host=HOST] [\-\-tls\-version=TLS\-VERSION] [\-\-verify\-mode=VERIFY\-MODE] .br \fBbundle doctor\fR help [COMMAND] .SH "DESCRIPTION" @@ -57,12 +57,12 @@ Open a TLS connection and verify the outcome\. \fB\-\-host=HOST\fR Perform the diagnostic on HOST\. Defaults to \fBrubygems\.org\fR\. .TP -\fB\-\-tls\-version=VERSION\fR +\fB\-\-tls\-version=TLS\-VERSION\fR Specify the TLS version when opening the connection to HOST\. .IP Accepted values are: \fB1\.1\fR or \fB1\.2\fR\. .TP -\fB\-\-verify\-mode=MODE\fR +\fB\-\-verify\-mode=VERIFY\-MODE\fR Specify the TLS verify mode when opening the connection to HOST\. Defaults to \fBSSL_VERIFY_PEER\fR\. .IP Accepted values are: \fBCLIENT_ONCE\fR, \fBFAIL_IF_NO_PEER_CERT\fR, \fBNONE\fR, \fBPEER\fR\. diff --git a/lib/bundler/man/bundle-doctor.1.ronn b/lib/bundler/man/bundle-doctor.1.ronn index 7e8a21b1c58968..7495099ff546bc 100644 --- a/lib/bundler/man/bundle-doctor.1.ronn +++ b/lib/bundler/man/bundle-doctor.1.ronn @@ -7,8 +7,8 @@ bundle-doctor(1) -- Checks the bundle for common problems [--gemfile=GEMFILE] [--ssl]
`bundle doctor ssl` [--host=HOST] - [--tls-version=VERSION] - [--verify-mode=MODE]
+ [--tls-version=TLS-VERSION] + [--verify-mode=VERIFY-MODE]
`bundle doctor` help [COMMAND] ## DESCRIPTION @@ -65,12 +65,12 @@ The diagnostic will perform a few checks such as: * `--host=HOST`: Perform the diagnostic on HOST. Defaults to `rubygems.org`. -* `--tls-version=VERSION`: +* `--tls-version=TLS-VERSION`: Specify the TLS version when opening the connection to HOST. Accepted values are: `1.1` or `1.2`. -* `--verify-mode=MODE`: +* `--verify-mode=VERIFY-MODE`: Specify the TLS verify mode when opening the connection to HOST. Defaults to `SSL_VERIFY_PEER`. From 4a3a544d53a4058339787b7e11138d29d3c900a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Mon, 11 Aug 2025 20:00:49 +0200 Subject: [PATCH 33/60] [rubygems/rubygems] Improve `bundle plugin` documentation Make synopsis, subcommands, and CLI flags use a format consistent with the other docs, and also reword some sentences for clarify. https://github.com/rubygems/rubygems/commit/9272169ad0 --- lib/bundler/man/bundle-plugin.1 | 45 ++++++++++++++++-------- lib/bundler/man/bundle-plugin.1.ronn | 51 ++++++++++++++++++++-------- 2 files changed, 67 insertions(+), 29 deletions(-) diff --git a/lib/bundler/man/bundle-plugin.1 b/lib/bundler/man/bundle-plugin.1 index 9285f128c1b10b..5fcc88b50df308 100644 --- a/lib/bundler/man/bundle-plugin.1 +++ b/lib/bundler/man/bundle-plugin.1 @@ -4,7 +4,7 @@ .SH "NAME" \fBbundle\-plugin\fR \- Manage Bundler plugins .SH "SYNOPSIS" -\fBbundle plugin\fR install PLUGINS [\-\-source=\fISOURCE\fR] [\-\-version=\fIversion\fR] [\-\-git=\fIgit\-url\fR] [\-\-branch=\fIbranch\fR|\-\-ref=\fIrev\fR] [\-\-path=\fIpath\fR] +\fBbundle plugin\fR install PLUGINS [\-\-source=SOURCE] [\-\-version=VERSION] [\-\-git=GIT] [\-\-branch=BRANCH|\-\-ref=REF] [\-\-local\-git=LOCAL_GIT] [\-\-path=PATH] .br \fBbundle plugin\fR uninstall PLUGINS [\-\-all] .br @@ -16,18 +16,23 @@ You can install, uninstall, and list plugin(s) with this command to extend funct .SH "SUB\-COMMANDS" .SS "install" Install the given plugin(s)\. +.P +For example, \fBbundle plugin install bundler\-graph\fR will install bundler\-graph gem from globally configured sources (defaults to RubyGems\.org)\. Note that the global source specified in Gemfile is ignored\. +.P +\fBOPTIONS\fR .TP -\fBbundle plugin install bundler\-graph\fR -Install bundler\-graph gem from globally configured sources (defaults to RubyGems\.org)\. The global source, specified in source in Gemfile is ignored\. -.TP -\fBbundle plugin install bundler\-graph \-\-source https://example\.com\fR -Install bundler\-graph gem from example\.com\. The global source, specified in source in Gemfile is not considered\. +\fB\-\-source=SOURCE\fR +Install the plugin gem from a specific source, rather than from globally configured sources\. +.IP +Example: \fBbundle plugin install bundler\-graph \-\-source https://example\.com\fR .TP -\fBbundle plugin install bundler\-graph \-\-version 0\.2\.1\fR -You can specify the version of the gem via \fB\-\-version\fR\. +\fB\-\-version=VERSION\fR +Specify a version of the plugin gem to install via \fB\-\-version\fR\. +.IP +Example: \fBbundle plugin install bundler\-graph \-\-version 0\.2\.1\fR .TP -\fBbundle plugin install bundler\-graph \-\-git https://github\.com/rubygems/bundler\-graph\fR -Install bundler\-graph gem from Git repository\. You can use standard Git URLs like: +\fB\-\-git=GIT\fR +Install the plugin gem from a Git repository\. You can use standard Git URLs like: .IP \fBssh://[user@]host\.xz[:port]/path/to/repo\.git\fR .br @@ -37,12 +42,24 @@ Install bundler\-graph gem from Git repository\. You can use standard Git URLs l .br \fBfile:///path/to/repo\fR .IP -When you specify \fB\-\-git\fR, you can use \fB\-\-branch\fR or \fB\-\-ref\fR to specify any branch, tag, or commit hash (revision) to use\. +Example: \fBbundle plugin install bundler\-graph \-\-git https://github\.com/rubygems/bundler\-graph\fR .TP -\fBbundle plugin install bundler\-graph \-\-path \.\./bundler\-graph\fR -Install bundler\-graph gem from a local path\. +\fB\-\-branch=BRANCH\fR +When you specify \fB\-\-git\fR, you can use \fB\-\-branch\fR to use\. .TP -\fBbundle plugin install bundler\-graph \-\-local\-git \.\./bundler\-graph\fR +\fB\-\-ref=REF\fR +When you specify \fB\-\-git\fR, you can use \fB\-\-ref\fR to specify any tag, or commit hash (revision) to use\. +.TP +\fB\-\-path=PATH\fR +Install the plugin gem from a local path\. +.IP +Example: \fBbundle plugin install bundler\-graph \-\-path \.\./bundler\-graph\fR +.TP +\fB\-\-local\-git=LOCAL_GIT\fR +Install the plugin gem from a local Git repository\. +.IP +Example: \fBbundle plugin install bundler\-graph \-\-local\-git \.\./bundler\-graph\fR\. +.IP This option is deprecated in favor of \fB\-\-git\fR\. .SS "uninstall" Uninstall the plugin(s) specified in PLUGINS\. diff --git a/lib/bundler/man/bundle-plugin.1.ronn b/lib/bundler/man/bundle-plugin.1.ronn index 74879aa6812d63..efb42407611836 100644 --- a/lib/bundler/man/bundle-plugin.1.ronn +++ b/lib/bundler/man/bundle-plugin.1.ronn @@ -3,9 +3,10 @@ bundle-plugin(1) -- Manage Bundler plugins ## SYNOPSIS -`bundle plugin` install PLUGINS [--source=] [--version=] - [--git=] [--branch=|--ref=] - [--path=]
+`bundle plugin` install PLUGINS [--source=SOURCE] [--version=VERSION] + [--git=GIT] [--branch=BRANCH|--ref=REF] + [--local-git=LOCAL_GIT] + [--path=PATH]
`bundle plugin` uninstall PLUGINS [--all]
`bundle plugin` list
`bundle plugin` help [COMMAND] @@ -20,29 +21,49 @@ You can install, uninstall, and list plugin(s) with this command to extend funct Install the given plugin(s). -* `bundle plugin install bundler-graph`: - Install bundler-graph gem from globally configured sources (defaults to RubyGems.org). The global source, specified in source in Gemfile is ignored. +For example, `bundle plugin install bundler-graph` will install bundler-graph +gem from globally configured sources (defaults to RubyGems.org). Note that the +global source specified in Gemfile is ignored. -* `bundle plugin install bundler-graph --source https://example.com`: - Install bundler-graph gem from example.com. The global source, specified in source in Gemfile is not considered. +**OPTIONS** + +* `--source=SOURCE`: + Install the plugin gem from a specific source, rather than from globally configured sources. + + Example: `bundle plugin install bundler-graph --source https://example.com` -* `bundle plugin install bundler-graph --version 0.2.1`: - You can specify the version of the gem via `--version`. +* `--version=VERSION`: + Specify a version of the plugin gem to install via `--version`. -* `bundle plugin install bundler-graph --git https://github.com/rubygems/bundler-graph`: - Install bundler-graph gem from Git repository. You can use standard Git URLs like: + Example: `bundle plugin install bundler-graph --version 0.2.1` + +* `--git=GIT`: + Install the plugin gem from a Git repository. You can use standard Git URLs like: `ssh://[user@]host.xz[:port]/path/to/repo.git`
`http[s]://host.xz[:port]/path/to/repo.git`
`/path/to/repo`
`file:///path/to/repo` - When you specify `--git`, you can use `--branch` or `--ref` to specify any branch, tag, or commit hash (revision) to use. + Example: `bundle plugin install bundler-graph --git https://github.com/rubygems/bundler-graph` + +* `--branch=BRANCH`: + When you specify `--git`, you can use `--branch` to use. + +* `--ref=REF`: + When you specify `--git`, you can use `--ref` to specify any tag, or commit + hash (revision) to use. + +* `--path=PATH`: + Install the plugin gem from a local path. + + Example: `bundle plugin install bundler-graph --path ../bundler-graph` + +* `--local-git=LOCAL_GIT`: + Install the plugin gem from a local Git repository. -* `bundle plugin install bundler-graph --path ../bundler-graph`: - Install bundler-graph gem from a local path. + Example: `bundle plugin install bundler-graph --local-git ../bundler-graph`. -* `bundle plugin install bundler-graph --local-git ../bundler-graph`: This option is deprecated in favor of `--git`. ### uninstall From d04f1874692b7d731533a9f65d037b2880c30f5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 30 Jul 2025 11:25:26 +0200 Subject: [PATCH 34/60] [rubygems/rubygems] Refactor git specs To save some unnecessary `bundle install` commands. https://github.com/rubygems/rubygems/commit/61e7d9d09a --- spec/bundler/install/gemfile/git_spec.rb | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb index cd83ad71c07e63..4658dce9c001ed 100644 --- a/spec/bundler/install/gemfile/git_spec.rb +++ b/spec/bundler/install/gemfile/git_spec.rb @@ -2,7 +2,7 @@ RSpec.describe "bundle install with git sources" do describe "when floating on main" do - before :each do + let(:install_base_gemfile) do build_git "foo" do |s| s.executables = "foobar" end @@ -16,6 +16,7 @@ end it "fetches gems" do + install_base_gemfile expect(the_bundle).to include_gems("foo 1.0") run <<-RUBY @@ -27,16 +28,19 @@ end it "caches the git repo" do + install_base_gemfile expect(Dir["#{default_bundle_path}/cache/bundler/git/foo-1.0-*"]).to have_attributes size: 1 end it "does not write to cache on bundler/setup" do + install_base_gemfile FileUtils.rm_r(default_cache_path) ruby "require 'bundler/setup'" expect(default_cache_path).not_to exist end it "caches the git repo globally and properly uses the cached repo on the next invocation" do + install_base_gemfile pristine_system_gems bundle "config set global_gem_cache true" bundle :install @@ -48,6 +52,7 @@ end it "caches the evaluated gemspec" do + install_base_gemfile git = update_git "foo" do |s| s.executables = ["foobar"] # we added this the first time, so keep it now s.files = ["bin/foobar"] # updating git nukes the files list @@ -66,6 +71,7 @@ end it "does not update the git source implicitly" do + install_base_gemfile update_git "foo" install_gemfile bundled_app2("Gemfile"), <<-G, dir: bundled_app2 @@ -84,6 +90,7 @@ end it "sets up git gem executables on the path" do + install_base_gemfile bundle "exec foobar" expect(out).to eq("1.0") end @@ -136,7 +143,7 @@ it "still works after moving the application directory" do bundle "config set --local path vendor/bundle" - bundle "install" + install_base_gemfile FileUtils.mv bundled_app, tmp("bundled_app.bck") @@ -145,7 +152,7 @@ it "can still install after moving the application directory" do bundle "config set --local path vendor/bundle" - bundle "install" + install_base_gemfile FileUtils.mv bundled_app, tmp("bundled_app.bck") From 50c1faedebf6b8d779e05811398f25bbdf769f0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 30 Jul 2025 11:26:01 +0200 Subject: [PATCH 35/60] [rubygems/rubygems] Fix incorrect error message capitalization https://github.com/rubygems/rubygems/commit/d41b8d303c --- lib/bundler/definition.rb | 2 +- spec/bundler/commands/update_spec.rb | 2 +- spec/bundler/lock/lockfile_spec.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 7d00c234d671d3..2313e467bc00b2 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -531,7 +531,7 @@ def lockfile_changes_summary(update_refused_reason) return unless added.any? || deleted.any? || changed.any? || resolve_needed? - msg = String.new("#{change_reason.capitalize.strip}, but ") + msg = String.new("#{change_reason[0].upcase}#{change_reason[1..-1].strip}, but ") msg << "the lockfile " unless msg.start_with?("Your lockfile") msg << "can't be updated because #{update_refused_reason}" msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any? diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb index 8b0f28ae6e366e..7702c723977559 100644 --- a/spec/bundler/commands/update_spec.rb +++ b/spec/bundler/commands/update_spec.rb @@ -1852,7 +1852,7 @@ system_gems "bundler-9.9.9", path: local_gem_path bundle "update --bundler=9.9.9", env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false - expect(err).to include("An update to the version of bundler itself was requested, but the lockfile can't be updated because frozen mode is set") + expect(err).to include("An update to the version of Bundler itself was requested, but the lockfile can't be updated because frozen mode is set") end end diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb index e1fbe6934a1b6e..d0bee11e7fc82c 100644 --- a/spec/bundler/lock/lockfile_spec.rb +++ b/spec/bundler/lock/lockfile_spec.rb @@ -1638,7 +1638,7 @@ G expect(err).to eq <<~L.strip - Your lockfile is missing a checksums entry for \"myrack_middleware\", but can't be updated because frozen mode is set + Your lockfile is missing a CHECKSUMS entry for \"myrack_middleware\", but can't be updated because frozen mode is set Run `bundle install` elsewhere and add the updated Gemfile.lock to version control. L From 8576da4eb4d7dc7a1afb26126192971e787d7c5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 30 Jul 2025 11:48:10 +0200 Subject: [PATCH 36/60] [rubygems/rubygems] Enforce checksums strictly for registry gems https://github.com/rubygems/rubygems/commit/05199ae0c1 --- lib/bundler/checksum.rb | 6 +++ lib/bundler/definition.rb | 15 ++++++- spec/bundler/install/gemfile/git_spec.rb | 53 +++++++++++++++++++++--- spec/bundler/lock/lockfile_spec.rb | 34 +++++++++++++++ 4 files changed, 101 insertions(+), 7 deletions(-) diff --git a/lib/bundler/checksum.rb b/lib/bundler/checksum.rb index 356f4a48bcd11a..ce05818bb07992 100644 --- a/lib/bundler/checksum.rb +++ b/lib/bundler/checksum.rb @@ -205,6 +205,12 @@ def missing?(spec) @store[spec.lock_name].nil? end + def empty?(spec) + return false unless spec.source.is_a?(Bundler::Source::Rubygems) + + @store[spec.lock_name].empty? + end + def register(spec, checksum) register_checksum(spec.lock_name, checksum) end diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 2313e467bc00b2..52f9c6e1255c36 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -557,6 +557,7 @@ def something_changed? @missing_lockfile_dep || @unlocking_bundler || @locked_spec_with_missing_checksums || + @locked_spec_with_empty_checksums || @locked_spec_with_missing_deps || @locked_spec_with_invalid_deps end @@ -836,6 +837,7 @@ def lockfile_changed_reason [@missing_lockfile_dep, "your lockfile is missing \"#{@missing_lockfile_dep}\""], [@unlocking_bundler, "an update to the version of Bundler itself was requested"], [@locked_spec_with_missing_checksums, "your lockfile is missing a CHECKSUMS entry for \"#{@locked_spec_with_missing_checksums}\""], + [@locked_spec_with_empty_checksums, "your lockfile has an empty CHECKSUMS entry for \"#{@locked_spec_with_empty_checksums}\""], [@locked_spec_with_missing_deps, "your lockfile includes \"#{@locked_spec_with_missing_deps}\" but not some of its dependencies"], [@locked_spec_with_invalid_deps, "your lockfile does not satisfy dependencies of \"#{@locked_spec_with_invalid_deps}\""], ].select(&:first).map(&:last).join(", ") @@ -895,13 +897,23 @@ def check_lockfile @locked_spec_with_invalid_deps = nil @locked_spec_with_missing_deps = nil @locked_spec_with_missing_checksums = nil + @locked_spec_with_empty_checksums = nil missing_deps = [] missing_checksums = [] + empty_checksums = [] invalid = [] @locked_specs.each do |s| - missing_checksums << s if @locked_checksums && s.source.checksum_store.missing?(s) + if @locked_checksums + checksum_store = s.source.checksum_store + + if checksum_store.missing?(s) + missing_checksums << s + elsif checksum_store.empty?(s) + empty_checksums << s + end + end validation = @locked_specs.validate_deps(s) @@ -910,6 +922,7 @@ def check_lockfile end @locked_spec_with_missing_checksums = missing_checksums.first.name if missing_checksums.any? + @locked_spec_with_empty_checksums = empty_checksums.first.name if empty_checksums.any? if missing_deps.any? @locked_specs.delete(missing_deps) diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb index 4658dce9c001ed..216548cf27ccee 100644 --- a/spec/bundler/install/gemfile/git_spec.rb +++ b/spec/bundler/install/gemfile/git_spec.rb @@ -2,12 +2,8 @@ RSpec.describe "bundle install with git sources" do describe "when floating on main" do - let(:install_base_gemfile) do - build_git "foo" do |s| - s.executables = "foobar" - end - - install_gemfile <<-G + let(:base_gemfile) do + <<-G source "https://gem.repo1" git "#{lib_path("foo-1.0")}" do gem 'foo' @@ -15,6 +11,14 @@ G end + let(:install_base_gemfile) do + build_git "foo" do |s| + s.executables = "foobar" + end + + install_gemfile base_gemfile + end + it "fetches gems" do install_base_gemfile expect(the_bundle).to include_gems("foo 1.0") @@ -27,6 +31,43 @@ expect(out).to eq("WIN") end + it "does not (yet?) enforce CHECKSUMS" do + build_git "foo" + revision = revision_for(lib_path("foo-1.0")) + + bundle "config set lockfile_checksums true" + gemfile base_gemfile + + lockfile <<~L + GIT + remote: #{lib_path("foo-1.0")} + revision: #{revision} + specs: + foo (1.0) + + GEM + remote: https://gem.repo1/ + specs: + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + foo! + + CHECKSUMS + foo (1.0) + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "config set frozen true" + + bundle "install" + expect(the_bundle).to include_gems("foo 1.0") + end + it "caches the git repo" do install_base_gemfile expect(Dir["#{default_bundle_path}/cache/bundler/git/foo-1.0-*"]).to have_attributes size: 1 diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb index d0bee11e7fc82c..6b98e0924e41d8 100644 --- a/spec/bundler/lock/lockfile_spec.rb +++ b/spec/bundler/lock/lockfile_spec.rb @@ -1646,6 +1646,40 @@ expect(the_bundle).not_to include_gems "myrack_middleware 1.0" end + it "raises a clear error when frozen mode is set and lockfile has empty checksums in CHECKSUMS section, and does not install any gems" do + lockfile <<-L + GEM + remote: https://gem.repo2/ + specs: + myrack (0.9.1) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + myrack + + CHECKSUMS + myrack (0.9.1) + + BUNDLED WITH + #{Bundler::VERSION} + L + + install_gemfile <<-G, env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false + source "https://gem.repo2" + gem "myrack" + G + + expect(err).to eq <<~L.strip + Your lockfile has an empty CHECKSUMS entry for \"myrack\", but can't be updated because frozen mode is set + + Run `bundle install` elsewhere and add the updated Gemfile.lock to version control. + L + + expect(the_bundle).not_to include_gems "myrack 0.9.1" + end + it "automatically fixes the lockfile when it's missing deps, they conflict with other locked deps, but conflicts are fixable" do build_repo4 do build_gem "other_dep", "0.9" From 7125f7635d43f67b1664bf85e72a34d868259822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Mon, 11 Aug 2025 22:02:10 +0200 Subject: [PATCH 37/60] [rubygems/rubygems] Use spaces around optional parameter values https://github.com/rubygems/rubygems/commit/b58829a868 --- lib/bundler/resolver.rb | 2 +- lib/bundler/rubygems_ext.rb | 2 +- lib/rubygems.rb | 10 +++++----- lib/rubygems/command.rb | 2 +- lib/rubygems/command_manager.rb | 6 +++--- lib/rubygems/dependency.rb | 2 +- lib/rubygems/dependency_installer.rb | 2 +- lib/rubygems/dependency_list.rb | 2 +- lib/rubygems/deprecate.rb | 2 +- lib/rubygems/errors.rb | 2 +- lib/rubygems/exceptions.rb | 4 ++-- lib/rubygems/ext/cargo_builder.rb | 2 +- lib/rubygems/ext/cmake_builder.rb | 4 ++-- lib/rubygems/ext/configure_builder.rb | 4 ++-- lib/rubygems/ext/ext_conf_builder.rb | 4 ++-- lib/rubygems/ext/rake_builder.rb | 4 ++-- lib/rubygems/installer.rb | 2 +- lib/rubygems/name_tuple.rb | 2 +- lib/rubygems/package/tar_writer.rb | 2 +- lib/rubygems/remote_fetcher.rb | 2 +- lib/rubygems/resolver.rb | 2 +- lib/rubygems/resolver/conflict.rb | 2 +- lib/rubygems/source.rb | 4 ++-- lib/rubygems/spec_fetcher.rb | 8 ++++---- lib/rubygems/specification.rb | 2 +- lib/rubygems/text.rb | 2 +- lib/rubygems/user_interaction.rb | 12 ++++++------ lib/rubygems/validator.rb | 2 +- test/rubygems/helper.rb | 6 +++--- test/rubygems/installer_test_case.rb | 2 +- test/rubygems/mock_gem_ui.rb | 2 +- test/rubygems/test_gem_remote_fetcher.rb | 2 +- 32 files changed, 54 insertions(+), 54 deletions(-) diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index fba9badec727ba..1dbf565d4676e8 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -165,7 +165,7 @@ def parse_dependency(package, dependency) PubGrub::VersionConstraint.new(package, range: range) end - def versions_for(package, range=VersionRange.any) + def versions_for(package, range = VersionRange.any) range.select_versions(@sorted_versions[package]) end diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb index 8cf3b56b831f0f..fedf44b0e69b14 100644 --- a/lib/bundler/rubygems_ext.rb +++ b/lib/bundler/rubygems_ext.rb @@ -417,7 +417,7 @@ class NameTuple unless Gem::NameTuple.new("a", Gem::Version.new("1"), Gem::Platform.new("x86_64-linux")).platform.is_a?(String) alias_method :initialize_with_platform, :initialize - def initialize(name, version, platform=Gem::Platform::RUBY) + def initialize(name, version, platform = Gem::Platform::RUBY) if Gem::Platform === platform initialize_with_platform(name, version, platform.to_s) else diff --git a/lib/rubygems.rb b/lib/rubygems.rb index d4e88579e823db..f8f1451ee661a7 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -224,7 +224,7 @@ def self.needs finish_resolve rs end - def self.finish_resolve(request_set=Gem::RequestSet.new) + def self.finish_resolve(request_set = Gem::RequestSet.new) request_set.import Gem::Specification.unresolved_deps.values request_set.import Gem.loaded_specs.values.map {|s| Gem::Dependency.new(s.name, s.version) } @@ -341,7 +341,7 @@ def self.binary_mode ## # The path where gem executables are to be installed. - def self.bindir(install_dir=Gem.dir) + def self.bindir(install_dir = Gem.dir) return File.join install_dir, "bin" unless install_dir.to_s == Gem.default_dir.to_s Gem.default_bindir @@ -350,7 +350,7 @@ def self.bindir(install_dir=Gem.dir) ## # The path were rubygems plugins are to be installed. - def self.plugindir(install_dir=Gem.dir) + def self.plugindir(install_dir = Gem.dir) File.join install_dir, "plugins" end @@ -534,7 +534,7 @@ def self.extension_api_version # :nodoc: # Note that find_files will return all files even if they are from different # versions of the same gem. See also find_latest_files - def self.find_files(glob, check_load_path=true) + def self.find_files(glob, check_load_path = true) files = [] files = find_files_from_load_path glob if check_load_path @@ -571,7 +571,7 @@ def self.find_files_from_load_path(glob) # :nodoc: # Unlike find_files, find_latest_files will return only files from the # latest version of a gem. - def self.find_latest_files(glob, check_load_path=true) + def self.find_latest_files(glob, check_load_path = true) files = [] files = find_files_from_load_path glob if check_load_path diff --git a/lib/rubygems/command.rb b/lib/rubygems/command.rb index 3a149ea38e23e2..d38363f293c79c 100644 --- a/lib/rubygems/command.rb +++ b/lib/rubygems/command.rb @@ -117,7 +117,7 @@ def self.specific_extra_args_hash # Unhandled arguments (gem names, files, etc.) are left in # options[:args]. - def initialize(command, summary=nil, defaults={}) + def initialize(command, summary = nil, defaults = {}) @command = command @summary = summary @program_name = "gem #{command}" diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb index 15834ce4dd0c09..8521517a24ef93 100644 --- a/lib/rubygems/command_manager.rb +++ b/lib/rubygems/command_manager.rb @@ -118,7 +118,7 @@ def initialize ## # Register the Symbol +command+ as a gem command. - def register_command(command, obj=false) + def register_command(command, obj = false) @commands[command] = obj end @@ -148,7 +148,7 @@ def command_names ## # Run the command specified by +args+. - def run(args, build_args=nil) + def run(args, build_args = nil) process_args(args, build_args) rescue StandardError, Gem::Timeout::Error => ex if ex.respond_to?(:detailed_message) @@ -165,7 +165,7 @@ def run(args, build_args=nil) terminate_interaction(1) end - def process_args(args, build_args=nil) + def process_args(args, build_args = nil) if args.empty? say Gem::Command::HELP terminate_interaction 1 diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb index d1ec9222af1a89..1e91f493a64eb5 100644 --- a/lib/rubygems/dependency.rb +++ b/lib/rubygems/dependency.rb @@ -217,7 +217,7 @@ def =~(other) # NOTE: Unlike #matches_spec? this method does not return true when the # version is a prerelease version unless this is a prerelease dependency. - def match?(obj, version=nil, allow_prerelease=false) + def match?(obj, version = nil, allow_prerelease = false) if !version name = obj.name version = obj.version diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb index b119dca1cf2912..b4152e83e913b4 100644 --- a/lib/rubygems/dependency_installer.rb +++ b/lib/rubygems/dependency_installer.rb @@ -127,7 +127,7 @@ def consider_remote? # sources. Gems are sorted with newer gems preferred over older gems, and # local gems preferred over remote gems. - def find_gems_with_sources(dep, best_only=false) # :nodoc: + def find_gems_with_sources(dep, best_only = false) # :nodoc: set = Gem::AvailableSet.new if consider_local? diff --git a/lib/rubygems/dependency_list.rb b/lib/rubygems/dependency_list.rb index ad5e59e8c173f1..99643a426d423a 100644 --- a/lib/rubygems/dependency_list.rb +++ b/lib/rubygems/dependency_list.rb @@ -140,7 +140,7 @@ def why_not_ok?(quick = false) # If removing the gemspec creates breaks a currently ok dependency, then it # is NOT ok to remove the gemspec. - def ok_to_remove?(full_name, check_dev=true) + def ok_to_remove?(full_name, check_dev = true) gem_to_remove = find_name full_name # If the state is inconsistent, at least don't crash diff --git a/lib/rubygems/deprecate.rb b/lib/rubygems/deprecate.rb index a20649cbdab166..303b344d42c838 100644 --- a/lib/rubygems/deprecate.rb +++ b/lib/rubygems/deprecate.rb @@ -126,7 +126,7 @@ def deprecate(name, repl, year, month) # telling the user of +repl+ (unless +repl+ is :none) and the # Rubygems version that it is planned to go away. - def rubygems_deprecate(name, replacement=:none, version=nil) + def rubygems_deprecate(name, replacement = :none, version = nil) class_eval do old = "_deprecated_#{name}" alias_method old, name diff --git a/lib/rubygems/errors.rb b/lib/rubygems/errors.rb index 57fb3eb12008a1..4bbc5217e0e639 100644 --- a/lib/rubygems/errors.rb +++ b/lib/rubygems/errors.rb @@ -26,7 +26,7 @@ class LoadError < ::LoadError # system. Instead of rescuing from this class, make sure to rescue from the # superclass Gem::LoadError to catch all types of load errors. class MissingSpecError < Gem::LoadError - def initialize(name, requirement, extra_message=nil) + def initialize(name, requirement, extra_message = nil) @name = name @requirement = requirement @extra_message = extra_message diff --git a/lib/rubygems/exceptions.rb b/lib/rubygems/exceptions.rb index 793324b875c754..adf7ad6d7d05ac 100644 --- a/lib/rubygems/exceptions.rb +++ b/lib/rubygems/exceptions.rb @@ -110,7 +110,7 @@ class Gem::SpecificGemNotFoundException < Gem::GemNotFoundException # and +version+. Any +errors+ encountered when attempting to find the gem # are also stored. - def initialize(name, version, errors=nil) + def initialize(name, version, errors = nil) super "Could not find a valid gem '#{name}' (#{version}) locally or in a repository" @name = name @@ -261,7 +261,7 @@ class Gem::UnsatisfiableDependencyError < Gem::DependencyError # Creates a new UnsatisfiableDependencyError for the unsatisfiable # Gem::Resolver::DependencyRequest +dep+ - def initialize(dep, platform_mismatch=nil) + def initialize(dep, platform_mismatch = nil) if platform_mismatch && !platform_mismatch.empty? plats = platform_mismatch.map {|x| x.platform.to_s }.sort.uniq super "Unable to resolve dependency: No match for '#{dep}' on this platform. Found: #{plats.join(", ")}" diff --git a/lib/rubygems/ext/cargo_builder.rb b/lib/rubygems/ext/cargo_builder.rb index 21b50f394d552d..e58d0bb75ccb9d 100644 --- a/lib/rubygems/ext/cargo_builder.rb +++ b/lib/rubygems/ext/cargo_builder.rb @@ -15,7 +15,7 @@ def initialize end def build(extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd, - target_rbconfig=Gem.target_rbconfig) + target_rbconfig = Gem.target_rbconfig) require "tempfile" require "fileutils" diff --git a/lib/rubygems/ext/cmake_builder.rb b/lib/rubygems/ext/cmake_builder.rb index 34564f668da87d..c7bfbb8a57ad27 100644 --- a/lib/rubygems/ext/cmake_builder.rb +++ b/lib/rubygems/ext/cmake_builder.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true class Gem::Ext::CmakeBuilder < Gem::Ext::Builder - def self.build(extension, dest_path, results, args=[], lib_dir=nil, cmake_dir=Dir.pwd, - target_rbconfig=Gem.target_rbconfig) + def self.build(extension, dest_path, results, args = [], lib_dir = nil, cmake_dir = Dir.pwd, + target_rbconfig = Gem.target_rbconfig) if target_rbconfig.path warn "--target-rbconfig is not yet supported for CMake extensions. Ignoring" end diff --git a/lib/rubygems/ext/configure_builder.rb b/lib/rubygems/ext/configure_builder.rb index d91b1ec5e82a17..76c1cd8b197548 100644 --- a/lib/rubygems/ext/configure_builder.rb +++ b/lib/rubygems/ext/configure_builder.rb @@ -7,8 +7,8 @@ #++ class Gem::Ext::ConfigureBuilder < Gem::Ext::Builder - def self.build(extension, dest_path, results, args=[], lib_dir=nil, configure_dir=Dir.pwd, - target_rbconfig=Gem.target_rbconfig) + def self.build(extension, dest_path, results, args = [], lib_dir = nil, configure_dir = Dir.pwd, + target_rbconfig = Gem.target_rbconfig) if target_rbconfig.path warn "--target-rbconfig is not yet supported for configure-based extensions. Ignoring" end diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb index 8aa15962a0f307..ec2fa59412df64 100644 --- a/lib/rubygems/ext/ext_conf_builder.rb +++ b/lib/rubygems/ext/ext_conf_builder.rb @@ -7,8 +7,8 @@ #++ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder - def self.build(extension, dest_path, results, args=[], lib_dir=nil, extension_dir=Dir.pwd, - target_rbconfig=Gem.target_rbconfig) + def self.build(extension, dest_path, results, args = [], lib_dir = nil, extension_dir = Dir.pwd, + target_rbconfig = Gem.target_rbconfig) require "fileutils" require "tempfile" diff --git a/lib/rubygems/ext/rake_builder.rb b/lib/rubygems/ext/rake_builder.rb index 8edd8d1373768f..0eac5a180ca3ca 100644 --- a/lib/rubygems/ext/rake_builder.rb +++ b/lib/rubygems/ext/rake_builder.rb @@ -7,8 +7,8 @@ #++ class Gem::Ext::RakeBuilder < Gem::Ext::Builder - def self.build(extension, dest_path, results, args=[], lib_dir=nil, extension_dir=Dir.pwd, - target_rbconfig=Gem.target_rbconfig) + def self.build(extension, dest_path, results, args = [], lib_dir = nil, extension_dir = Dir.pwd, + target_rbconfig = Gem.target_rbconfig) if target_rbconfig.path warn "--target-rbconfig is not yet supported for Rake extensions. Ignoring" end diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb index d0092899a40263..0cfe59b5bb5be3 100644 --- a/lib/rubygems/installer.rb +++ b/lib/rubygems/installer.rb @@ -153,7 +153,7 @@ def self.for_spec(spec, options = {}) # process. If not set, then Gem::Command.build_args is used # :post_install_message:: Print gem post install message if true - def initialize(package, options={}) + def initialize(package, options = {}) require "fileutils" @options = options diff --git a/lib/rubygems/name_tuple.rb b/lib/rubygems/name_tuple.rb index 3f4a6fcf3dfbb1..67c6f30a3dfff4 100644 --- a/lib/rubygems/name_tuple.rb +++ b/lib/rubygems/name_tuple.rb @@ -6,7 +6,7 @@ # wrap the data returned from the indexes. class Gem::NameTuple - def initialize(name, version, platform=Gem::Platform::RUBY) + def initialize(name, version, platform = Gem::Platform::RUBY) @name = name @version = version diff --git a/lib/rubygems/package/tar_writer.rb b/lib/rubygems/package/tar_writer.rb index 7dcb9737c06620..39fed9e2afe74b 100644 --- a/lib/rubygems/package/tar_writer.rb +++ b/lib/rubygems/package/tar_writer.rb @@ -99,7 +99,7 @@ def initialize(io) # Gem.source_date_epoch if not specified), and yields an IO for # writing the file to - def add_file(name, mode, mtime=nil) # :yields: io + def add_file(name, mode, mtime = nil) # :yields: io check_closed name, prefix = split_name name diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb index 355a668b39f81e..6ed0842963e6f0 100644 --- a/lib/rubygems/remote_fetcher.rb +++ b/lib/rubygems/remote_fetcher.rb @@ -72,7 +72,7 @@ def self.fetcher # +headers+: A set of additional HTTP headers to be sent to the server when # fetching the gem. - def initialize(proxy=nil, dns=nil, headers={}) + def initialize(proxy = nil, dns = nil, headers = {}) require_relative "core_ext/tcpsocket_init" if Gem.configuration.ipv4_fallback_enabled require_relative "vendored_net_http" require_relative "vendor/uri/lib/uri" diff --git a/lib/rubygems/resolver.rb b/lib/rubygems/resolver.rb index 9bf5f8093041d7..ed4cbde3bab12c 100644 --- a/lib/rubygems/resolver.rb +++ b/lib/rubygems/resolver.rb @@ -144,7 +144,7 @@ def activation_request(dep, possible) # :nodoc: [spec, activation_request] end - def requests(s, act, reqs=[]) # :nodoc: + def requests(s, act, reqs = []) # :nodoc: return reqs if @ignore_dependencies s.fetch_development_dependencies if @development diff --git a/lib/rubygems/resolver/conflict.rb b/lib/rubygems/resolver/conflict.rb index 367a36b43d7fe5..77c3add4b32d21 100644 --- a/lib/rubygems/resolver/conflict.rb +++ b/lib/rubygems/resolver/conflict.rb @@ -21,7 +21,7 @@ class Gem::Resolver::Conflict # Creates a new resolver conflict when +dependency+ is in conflict with an # already +activated+ specification. - def initialize(dependency, activated, failed_dep=dependency) + def initialize(dependency, activated, failed_dep = dependency) @dependency = dependency @activated = activated @failed_dep = failed_dep diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb index 772ad04bc92575..8b031e27a8d611 100644 --- a/lib/rubygems/source.rb +++ b/lib/rubygems/source.rb @@ -190,7 +190,7 @@ def load_specs(type) # Downloads +spec+ and writes it to +dir+. See also # Gem::RemoteFetcher#download. - def download(spec, dir=Dir.pwd) + def download(spec, dir = Dir.pwd) fetcher = Gem::RemoteFetcher.fetcher fetcher.download spec, uri.to_s, dir end @@ -210,7 +210,7 @@ def pretty_print(q) # :nodoc: end end - def typo_squatting?(host, distance_threshold=4) + def typo_squatting?(host, distance_threshold = 4) return if @uri.host.nil? levenshtein_distance(@uri.host, host).between? 1, distance_threshold end diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb index 285f117b393269..835dedf9489a2c 100644 --- a/lib/rubygems/spec_fetcher.rb +++ b/lib/rubygems/spec_fetcher.rb @@ -83,7 +83,7 @@ def initialize(sources = nil) # # If +matching_platform+ is false, gems for all platforms are returned. - def search_for_dependency(dependency, matching_platform=true) + def search_for_dependency(dependency, matching_platform = true) found = {} rejected_specs = {} @@ -130,7 +130,7 @@ def search_for_dependency(dependency, matching_platform=true) ## # Return all gem name tuples who's names match +obj+ - def detect(type=:complete) + def detect(type = :complete) tuples = [] list, _ = available_specs(type) @@ -150,7 +150,7 @@ def detect(type=:complete) # # If +matching_platform+ is false, gems for all platforms are returned. - def spec_for_dependency(dependency, matching_platform=true) + def spec_for_dependency(dependency, matching_platform = true) tuples, errors = search_for_dependency(dependency, matching_platform) specs = [] @@ -280,7 +280,7 @@ def available_specs(type) # Retrieves NameTuples from +source+ of the given +type+ (:prerelease, # etc.). If +gracefully_ignore+ is true, errors are ignored. - def tuples_for(source, type, gracefully_ignore=false) # :nodoc: + def tuples_for(source, type, gracefully_ignore = false) # :nodoc: @caches[type][source.uri] ||= source.load_specs(type).sort_by(&:name) rescue Gem::RemoteFetcher::FetchError diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index 68ebbf8bc31454..1d351f8aff9746 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -1757,7 +1757,7 @@ def dependencies # # [depending_gem, dependency, [list_of_gems_that_satisfy_dependency]] - def dependent_gems(check_dev=true) + def dependent_gems(check_dev = true) out = [] Gem::Specification.each do |spec| deps = check_dev ? spec.dependencies : spec.runtime_dependencies diff --git a/lib/rubygems/text.rb b/lib/rubygems/text.rb index da0795b771b350..88d4ce59b4b9ae 100644 --- a/lib/rubygems/text.rb +++ b/lib/rubygems/text.rb @@ -21,7 +21,7 @@ def truncate_text(text, description, max_length = 100_000) # Wraps +text+ to +wrap+ characters and optionally indents by +indent+ # characters - def format_text(text, wrap, indent=0) + def format_text(text, wrap, indent = 0) result = [] work = clean_text(text) diff --git a/lib/rubygems/user_interaction.rb b/lib/rubygems/user_interaction.rb index 0172c4ee897a40..571137629406fb 100644 --- a/lib/rubygems/user_interaction.rb +++ b/lib/rubygems/user_interaction.rb @@ -193,7 +193,7 @@ class Gem::StreamUI # then special operations (like asking for passwords) will use the TTY # commands to disable character echo. - def initialize(in_stream, out_stream, err_stream=$stderr, usetty=true) + def initialize(in_stream, out_stream, err_stream = $stderr, usetty = true) @ins = in_stream @outs = out_stream @errs = err_stream @@ -246,7 +246,7 @@ def choose_from_list(question, list) # to a tty, raises an exception if default is nil, otherwise returns # default. - def ask_yes_no(question, default=nil) + def ask_yes_no(question, default = nil) unless tty? if default.nil? raise Gem::OperationNotSupportedError, @@ -325,14 +325,14 @@ def _gets_noecho ## # Display a statement. - def say(statement="") + def say(statement = "") @outs.puts statement end ## # Display an informational alert. Will ask +question+ if it is not nil. - def alert(statement, question=nil) + def alert(statement, question = nil) @outs.puts "INFO: #{statement}" ask(question) if question end @@ -340,7 +340,7 @@ def alert(statement, question=nil) ## # Display a warning on stderr. Will ask +question+ if it is not nil. - def alert_warning(statement, question=nil) + def alert_warning(statement, question = nil) @errs.puts "WARNING: #{statement}" ask(question) if question end @@ -349,7 +349,7 @@ def alert_warning(statement, question=nil) # Display an error message in a location expected to get error messages. # Will ask +question+ if it is not nil. - def alert_error(statement, question=nil) + def alert_error(statement, question = nil) @errs.puts "ERROR: #{statement}" ask(question) if question end diff --git a/lib/rubygems/validator.rb b/lib/rubygems/validator.rb index 57e0747eb4a7d5..eb5b513570778e 100644 --- a/lib/rubygems/validator.rb +++ b/lib/rubygems/validator.rb @@ -59,7 +59,7 @@ def <=>(other) # :nodoc: #-- # TODO needs further cleanup - def alien(gems=[]) + def alien(gems = []) errors = Hash.new {|h,k| h[k] = {} } Gem::Specification.each do |spec| diff --git a/test/rubygems/helper.rb b/test/rubygems/helper.rb index 2f4abff1e84ca9..51c99a1bc5e211 100644 --- a/test/rubygems/helper.rb +++ b/test/rubygems/helper.rb @@ -724,7 +724,7 @@ def all_spec_names # # Use this with #write_file to build an installed gem. - def quick_gem(name, version="2") + def quick_gem(name, version = "2") require "rubygems/specification" spec = Gem::Specification.new do |s| @@ -1033,7 +1033,7 @@ def util_set_arch(arch) # Add +spec+ to +@fetcher+ serving the data in the file +path+. # +repo+ indicates which repo to make +spec+ appear to be in. - def add_to_fetcher(spec, path=nil, repo=@gem_repo) + def add_to_fetcher(spec, path = nil, repo = @gem_repo) path ||= spec.cache_file @fetcher.data["#{@gem_repo}gems/#{spec.file_name}"] = read_binary(path) end @@ -1206,7 +1206,7 @@ def wait_for_child_process_to_exit ## # Allows the proper version of +rake+ to be used for the test. - def build_rake_in(good=true) + def build_rake_in(good = true) gem_ruby = Gem.ruby Gem.ruby = self.class.rubybin env_rake = ENV["rake"] diff --git a/test/rubygems/installer_test_case.rb b/test/rubygems/installer_test_case.rb index 7a719843207c82..ded205c5f56273 100644 --- a/test/rubygems/installer_test_case.rb +++ b/test/rubygems/installer_test_case.rb @@ -215,7 +215,7 @@ def util_setup_gem(ui = @ui, force = true) ## # Creates an installer for +spec+ that will install into +gem_home+. - def util_installer(spec, gem_home, force=true) + def util_installer(spec, gem_home, force = true) Gem::Installer.at(spec.cache_file, install_dir: gem_home, force: force) diff --git a/test/rubygems/mock_gem_ui.rb b/test/rubygems/mock_gem_ui.rb index 218d4b6965d1eb..fb804c5555cf6d 100644 --- a/test/rubygems/mock_gem_ui.rb +++ b/test/rubygems/mock_gem_ui.rb @@ -77,7 +77,7 @@ def terminated? @terminated end - def terminate_interaction(status=0) + def terminate_interaction(status = 0) @terminated = true raise TermError, status if status != 0 diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb index ca858cfda5d33d..5c1d89fad6934a 100644 --- a/test/rubygems/test_gem_remote_fetcher.rb +++ b/test/rubygems/test_gem_remote_fetcher.rb @@ -592,7 +592,7 @@ def test_yaml_error_on_size end end - def assert_error(exception_class=Exception) + def assert_error(exception_class = Exception) got_exception = false begin From f0ee7630ed8993ef80cd11b12330c78616819ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Mon, 11 Aug 2025 20:01:45 +0200 Subject: [PATCH 38/60] Merge specs checking CLI flags and subcommands are documented We had them duplicated, but with slightly different features: * The ones in `other/cli_man_pages.rb` enforced a specific structure to document CLI options, so were less likely to have false positives. * The ones in `quality_spec.rb` were able to check subcommands and their flags. This commit merges both and preserves the best of both. --- spec/bundler/other/cli_man_pages_spec.rb | 85 ++++++++++++++++-------- spec/bundler/quality_spec.rb | 49 -------------- 2 files changed, 56 insertions(+), 78 deletions(-) diff --git a/spec/bundler/other/cli_man_pages_spec.rb b/spec/bundler/other/cli_man_pages_spec.rb index 84ffca14e62379..6efd2904d6497a 100644 --- a/spec/bundler/other/cli_man_pages_spec.rb +++ b/spec/bundler/other/cli_man_pages_spec.rb @@ -1,49 +1,72 @@ # frozen_string_literal: true RSpec.describe "bundle commands" do - it "expects all commands to have a man page" do - Bundler::CLI.all_commands.each_key do |command_name| - next if command_name == "cli_help" + it "expects all commands to have all options and subcommands documented" do + check_commands!(Bundler::CLI) - expect(man_page(command_name)).to exist + Bundler::CLI.subcommand_classes.each_value do |klass| + check_commands!(klass) end end - it "expects all commands to have all options documented" do - Bundler::CLI.all_commands.each do |command_name, command| - next if command_name == "cli_help" + private + + def check_commands!(command_class) + command_class.commands.each do |command_name, command| + next if command.is_a?(Bundler::Thor::HiddenCommand) + + if command_class == Bundler::CLI + man_page = man_page(command_name) + expect(man_page).to exist + + check_options!(command, man_page) + else + man_page = man_page(command.ancestor_name) + expect(man_page).to exist - man_page_content = man_page(command_name).read + check_options!(command, man_page) + check_subcommand!(command_name, man_page) + end + end + end - command.options.each do |_, option| - aliases = option.aliases - formatted_aliases = aliases.sort.map {|name| "`#{name}`" }.join(", ") if aliases + def check_options!(command, man_page) + command.options.each do |_, option| + check_option!(option, man_page) + end + end - help = if option.type == :boolean - "* #{append_aliases("`#{option.switch_name}`", formatted_aliases)}:" - elsif option.enum - formatted_aliases = "`#{option.switch_name}`" if aliases.empty? && option.lazy_default - "* #{prepend_aliases(option.enum.sort.map {|enum| "`#{option.switch_name}=#{enum}`" }.join(", "), formatted_aliases)}:" - else - names = [option.switch_name, *aliases] - value = - case option.type - when :array then "" - when :numeric then "" - else option.name.upcase - end + def check_option!(option, man_page) + man_page_content = man_page.read - value = option.type != :numeric && option.lazy_default ? "[=#{value}]" : "=#{value}" + aliases = option.aliases + formatted_aliases = aliases.sort.map {|name| "`#{name}`" }.join(", ") if aliases - "* #{names.map {|name| "`#{name}#{value}`" }.join(", ")}:" + help = if option.type == :boolean + "* #{append_aliases("`#{option.switch_name}`", formatted_aliases)}:" + elsif option.enum + formatted_aliases = "`#{option.switch_name}`" if aliases.empty? && option.lazy_default + "* #{prepend_aliases(option.enum.sort.map {|enum| "`#{option.switch_name}=#{enum}`" }.join(", "), formatted_aliases)}:" + else + names = [option.switch_name, *aliases] + value = + case option.type + when :array then "" + when :numeric then "" + else option.name.upcase end - expect(man_page_content).to include(help) - end + value = option.type != :numeric && option.lazy_default ? "[=#{value}]" : "=#{value}" + + "* #{names.map {|name| "`#{name}#{value}`" }.join(", ")}:" end + + expect(man_page_content).to include(help) end - private + def check_subcommand!(name, man_page) + expect(man_page.read).to match(name) + end def append_aliases(text, aliases) return text if aliases.empty? @@ -57,6 +80,10 @@ def prepend_aliases(text, aliases) "#{aliases}, #{text}" end + def man_page_content(command_name) + man_page(command_name).read + end + def man_page(command_name) source_root.join("lib/bundler/man/bundle-#{command_name}.1.ronn") end diff --git a/spec/bundler/quality_spec.rb b/spec/bundler/quality_spec.rb index 18d8e20030ce1a..3e5a960a960f2e 100644 --- a/spec/bundler/quality_spec.rb +++ b/spec/bundler/quality_spec.rb @@ -251,58 +251,9 @@ def check_for_specific_pronouns(filename) expect(lib_code).to eq(spec_code) end - it "documents all cli command options in their associated man pages" do - commands = normalize_commands_and_options(Bundler::CLI) - cli_and_man_pages_in_sync!(commands) - - Bundler::CLI.subcommand_classes.each do |_, klass| - subcommands = normalize_commands_and_options(klass) - - cli_and_man_pages_in_sync!(subcommands) - end - end - private def each_line(filename, &block) File.readlines(filename, encoding: "UTF-8").each_with_index(&block) end - - def normalize_commands_and_options(command_class) - commands = {} - - command_class.commands.each do |command_name, command| - next if command.is_a?(Bundler::Thor::HiddenCommand) - - key = command.ancestor_name || command_name - commands[key] ||= [] - # Verify that all subcommands are documented in the main command's man page. - commands[key] << command_name unless command_class == Bundler::CLI - - command.options.each do |_, option| - commands[key] << option.switch_name - end - end - - commands - end - - def cli_and_man_pages_in_sync!(commands) - commands.each do |command_name, opts| - man_page_path = man_tracked_files.find {|f| File.basename(f) == "bundle-#{command_name}.1.ronn" } - expect(man_page_path).to_not be_nil, "The command #{command_name} has no associated man page." - - next if opts.empty? - - man_page_content = File.read(man_page_path) - opts.each do |option_name| - error_msg = <<~EOM - The command #{command_name} has no mention of the option or subcommand `#{option_name}` in its man page. - Document the `#{option_name}` in the man page to discard this error. - EOM - - expect(man_page_content).to match(option_name), error_msg - end - end - end end From 5be3ebb0fdc67d0340c84af7f1086333f23b4240 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 7 Aug 2025 16:30:48 +0900 Subject: [PATCH 39/60] [ruby/json] Remove trailing spaces [ci skip] https://github.com/ruby/json/commit/2d2e0d403d --- ext/json/lib/json/add/string.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/json/lib/json/add/string.rb b/ext/json/lib/json/add/string.rb index 46f07967cd624d..64b1c217f86be2 100644 --- a/ext/json/lib/json/add/string.rb +++ b/ext/json/lib/json/add/string.rb @@ -5,7 +5,7 @@ class String # call-seq: json_create(o) - # + # # Raw Strings are JSON Objects (the raw bytes are stored in an array for the # key "raw"). The Ruby String can be created by this class method. def self.json_create(object) @@ -13,7 +13,7 @@ def self.json_create(object) end # call-seq: to_json_raw_object() - # + # # This method creates a raw object hash, that can be nested into # other data structures and will be generated as a raw string. This # method should be used, if you want to convert raw strings to JSON @@ -26,7 +26,7 @@ def to_json_raw_object end # call-seq: to_json_raw(*args) - # + # # This method creates a JSON text from the result of a call to # to_json_raw_object of this String. def to_json_raw(...) From 0de695e23c9fbfe5d8d363282a592787254def30 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 8 Aug 2025 19:48:29 +0900 Subject: [PATCH 40/60] [ruby/json] Append newline at EOF [ci skip] https://github.com/ruby/json/commit/72e231f929 --- ext/json/lib/json/add/string.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/json/lib/json/add/string.rb b/ext/json/lib/json/add/string.rb index 64b1c217f86be2..9c3bde27fbcf37 100644 --- a/ext/json/lib/json/add/string.rb +++ b/ext/json/lib/json/add/string.rb @@ -32,4 +32,4 @@ def to_json_raw_object def to_json_raw(...) to_json_raw_object.to_json(...) end -end \ No newline at end of file +end From e3ce56c9dc40d0eeb6d9de453e94621503afd9e6 Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Wed, 13 Aug 2025 13:52:40 -0700 Subject: [PATCH 41/60] Fix typos --- ext/json/generator/generator.c | 4 ++-- ext/json/lib/json.rb | 2 +- ext/json/lib/json/common.rb | 2 +- ext/json/parser/parser.c | 2 +- test/json/ractor_test.rb | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c index 5248a9e26aae3e..9c6ed930495a51 100644 --- a/ext/json/generator/generator.c +++ b/ext/json/generator/generator.c @@ -137,8 +137,8 @@ static inline FORCE_INLINE void search_flush(search_state *search) { // Do not remove this conditional without profiling, specifically escape-heavy text. // escape_UTF8_char_basic will advance search->ptr and search->cursor (effectively a search_flush). - // For back-to-back characters that need to be escaped, specifcally for the SIMD code paths, this method - // will be called just before calling escape_UTF8_char_basic. There will be no characers to append for the + // For back-to-back characters that need to be escaped, specifically for the SIMD code paths, this method + // will be called just before calling escape_UTF8_char_basic. There will be no characters to append for the // consecutive characters that need to be escaped. While the fbuffer_append is a no-op if // nothing needs to be flushed, we can save a few memory references with this conditional. if (search->ptr > search->cursor) { diff --git a/ext/json/lib/json.rb b/ext/json/lib/json.rb index 735f2380667daa..0ebff2f948af56 100644 --- a/ext/json/lib/json.rb +++ b/ext/json/lib/json.rb @@ -133,7 +133,7 @@ # When not specified: # # The last value is used and a deprecation warning emitted. # JSON.parse('{"a": 1, "a":2}') => {"a" => 2} -# # waring: detected duplicate keys in JSON object. +# # warning: detected duplicate keys in JSON object. # # This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true` # # When set to `+true+` diff --git a/ext/json/lib/json/common.rb b/ext/json/lib/json/common.rb index 9a878cead9f6a3..e99d152a884898 100644 --- a/ext/json/lib/json/common.rb +++ b/ext/json/lib/json/common.rb @@ -1002,7 +1002,7 @@ class Coder # See {Parsing Options}[#module-JSON-label-Parsing+Options], and {Generating Options}[#module-JSON-label-Generating+Options]. # # For generation, the strict: true option is always set. When a Ruby object with no native \JSON counterpart is - # encoutered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native + # encountered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native # \JSON counterpart: # # module MyApp diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index ab9d6c205e69b2..1e6ee753f0cf4b 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -1265,7 +1265,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config) break; } - raise_parse_error("unreacheable: %s", state); + raise_parse_error("unreachable: %s", state); } static void json_ensure_eof(JSON_ParserState *state) diff --git a/test/json/ractor_test.rb b/test/json/ractor_test.rb index dda34c64c08520..0ebdb0e91a1cfc 100644 --- a/test/json/ractor_test.rb +++ b/test/json/ractor_test.rb @@ -42,7 +42,7 @@ def test_generate else puts "Expected:" puts expected_json - puts "Acutual:" + puts "Actual:" puts actual_json puts exit 1 From 7ddc53bc0c57b1c079e40a74d233f537111b3e92 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Mon, 18 Aug 2025 11:39:30 +0900 Subject: [PATCH 42/60] Exclude prism/generate-srcs.mk.rb from sync targets of prism --- tool/sync_default_gems.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index 5794edaa83145a..029a27c8292b79 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -330,6 +330,7 @@ def sync_default_gems(gem) rm_rf("test/prism/snapshots") rm("prism/extconf.rb") + `git checkout prism/generate-srcs.mk.rb` when "resolv" rm_rf(%w[lib/resolv.* ext/win32/resolv test/resolv ext/win32/lib/win32/resolv.rb]) cp_r("#{upstream}/lib/resolv.rb", "lib") From 70378db2fff0b1bc39a7d2b011da90486579fb0e Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Fri, 15 Aug 2025 11:09:13 -0400 Subject: [PATCH 43/60] Remove impossible case in rb_raw_obj_info_buitin_type for array Since we handle embedded arrays in the if statement above, we don't need to handle it here. --- gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gc.c b/gc.c index 160398f9a6a661..aa3140ed473462 100644 --- a/gc.c +++ b/gc.c @@ -4709,7 +4709,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU C(ARY_EMBED_P(obj), "E"), C(ARY_SHARED_P(obj), "S"), RARRAY_LEN(obj), - ARY_EMBED_P(obj) ? -1L : RARRAY(obj)->as.heap.aux.capa, + RARRAY(obj)->as.heap.aux.capa, (void *)RARRAY_CONST_PTR(obj)); } break; From ef3fdb04d2be6e0337bea2ca84c7158d32b89719 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Fri, 15 Aug 2025 11:19:11 -0400 Subject: [PATCH 44/60] Move flags for arrays out of if statements in rb_raw_obj_info_buitin_type --- gc.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/gc.c b/gc.c index aa3140ed473462..c78ef4de113ef5 100644 --- a/gc.c +++ b/gc.c @@ -4698,19 +4698,21 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU APPEND_S("shared -> "); rb_raw_obj_info(BUFF_ARGS, ARY_SHARED_ROOT(obj)); } - else if (ARY_EMBED_P(obj)) { - APPEND_F("[%s%s] len: %ld (embed)", - C(ARY_EMBED_P(obj), "E"), - C(ARY_SHARED_P(obj), "S"), - RARRAY_LEN(obj)); - } else { - APPEND_F("[%s%s] len: %ld, capa:%ld ptr:%p", + APPEND_F("[%s%s] ", C(ARY_EMBED_P(obj), "E"), - C(ARY_SHARED_P(obj), "S"), - RARRAY_LEN(obj), - RARRAY(obj)->as.heap.aux.capa, - (void *)RARRAY_CONST_PTR(obj)); + C(ARY_SHARED_P(obj), "S")); + + if (ARY_EMBED_P(obj)) { + APPEND_F("len: %ld (embed)", + RARRAY_LEN(obj)); + } + else { + APPEND_F("len: %ld, capa:%ld ptr:%p", + RARRAY_LEN(obj), + RARRAY(obj)->as.heap.aux.capa, + (void *)RARRAY_CONST_PTR(obj)); + } } break; case T_STRING: { From 094fa3ed0922901b5771fc0e1651ecb161b5cdb6 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Fri, 15 Aug 2025 11:21:09 -0400 Subject: [PATCH 45/60] Output array shared root flag in rb_raw_obj_info_buitin_type --- gc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gc.c b/gc.c index c78ef4de113ef5..c2fc681253aa36 100644 --- a/gc.c +++ b/gc.c @@ -4699,9 +4699,10 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU rb_raw_obj_info(BUFF_ARGS, ARY_SHARED_ROOT(obj)); } else { - APPEND_F("[%s%s] ", - C(ARY_EMBED_P(obj), "E"), - C(ARY_SHARED_P(obj), "S")); + APPEND_F("[%s%s%s] ", + C(ARY_EMBED_P(obj), "E"), + C(ARY_SHARED_P(obj), "S"), + C(ARY_SHARED_ROOT_P(obj), "R")); if (ARY_EMBED_P(obj)) { APPEND_F("len: %ld (embed)", From 5a5efb12d0a4c9f9a22e276d337f41ed45dc59e9 Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Tue, 12 Aug 2025 12:45:20 -0400 Subject: [PATCH 46/60] Change test-all suite to enable testing each test in a separate ractor. This tests for ractor safety issues as well as issues with GC and the ractor scheduler. It also tests move/copy logic because it uses these internally for running the tests. You can enable these tests with RUBY_TESTS_WITH_RACTORS=1 when running `make test-all TESTS=test/ruby`. These ractor tests are currently only working for tests under the "test/ruby" directory, so don't run any others with this ENV var set or you will get errors. Currently there are GC issues with ractors so in tool/lib/test/unit.rb, I disable the GC before running any of the tests (for now). If you uncomment this, you will get errors and you can debug the GC issues. --- ext/-test-/file/fs.c | 3 + ext/-test-/integer/init.c | 3 + ext/-test-/iter/break.c | 4 + ext/-test-/iter/init.c | 5 + ext/-test-/iter/yield.c | 5 + .../rb_call_super_kw/rb_call_super_kw.c | 4 + test/fileutils/test_fileutils.rb | 9 +- test/ruby/enc/test_case_comprehensive.rb | 6 +- test/ruby/enc/test_emoji_breaks.rb | 5 +- test/ruby/enc/test_grapheme_breaks.rb | 14 +- test/ruby/enc/test_iso_8859.rb | 4 +- test/ruby/enc/test_koi8.rb | 4 +- test/ruby/enc/test_regex_casefold.rb | 22 ++-- test/ruby/marshaltestlib.rb | 1 + test/ruby/test_alias.rb | 2 + test/ruby/test_allocation.rb | 1 + test/ruby/test_argf.rb | 2 + test/ruby/test_array.rb | 66 ++++++---- test/ruby/test_assignment.rb | 2 + test/ruby/test_ast.rb | 74 ++++++----- test/ruby/test_autoload.rb | 15 ++- test/ruby/test_basicinstructions.rb | 4 + test/ruby/test_beginendblock.rb | 4 +- test/ruby/test_bignum.rb | 24 ++-- test/ruby/test_class.rb | 30 +++-- test/ruby/test_compile_prism.rb | 115 ++++++++++------ test/ruby/test_condition.rb | 10 +- test/ruby/test_const.rb | 1 + test/ruby/test_default_gems.rb | 1 + test/ruby/test_defined.rb | 6 +- test/ruby/test_dir_m17n.rb | 2 + test/ruby/test_env.rb | 12 +- test/ruby/test_eval.rb | 105 ++++++++++----- test/ruby/test_exception.rb | 5 +- test/ruby/test_file.rb | 15 ++- test/ruby/test_file_exhaustive.rb | 3 +- test/ruby/test_flip.rb | 1 + test/ruby/test_float.rb | 2 +- test/ruby/test_gc.rb | 1 + test/ruby/test_hash.rb | 18 ++- test/ruby/test_integer_comb.rb | 2 +- test/ruby/test_io.rb | 89 ++++++++----- test/ruby/test_io_buffer.rb | 24 ++-- test/ruby/test_io_m17n.rb | 72 +++++----- test/ruby/test_iseq.rb | 5 +- test/ruby/test_keyword.rb | 8 +- test/ruby/test_literal.rb | 2 +- test/ruby/test_m17n_comb.rb | 12 +- test/ruby/test_marshal.rb | 11 +- test/ruby/test_method.rb | 16 ++- test/ruby/test_module.rb | 43 ++++-- test/ruby/test_namespace.rb | 3 +- test/ruby/test_object.rb | 11 +- test/ruby/test_object_id.rb | 7 + test/ruby/test_objectspace.rb | 12 +- test/ruby/test_optimization.rb | 2 + test/ruby/test_parse.rb | 9 +- test/ruby/test_pattern_matching.rb | 26 ++-- test/ruby/test_primitive.rb | 37 ++++-- test/ruby/test_proc.rb | 22 ++-- test/ruby/test_process.rb | 123 ++++++++++-------- test/ruby/test_rand.rb | 3 +- test/ruby/test_range.rb | 1 + test/ruby/test_readpartial.rb | 2 + test/ruby/test_refinement.rb | 36 ++++- test/ruby/test_regexp.rb | 16 ++- test/ruby/test_require.rb | 56 ++++++-- test/ruby/test_require_lib.rb | 4 + test/ruby/test_rubyoptions.rb | 4 + test/ruby/test_settracefunc.rb | 37 +++--- test/ruby/test_shapes.rb | 7 + test/ruby/test_signal.rb | 10 +- test/ruby/test_sprintf_comb.rb | 4 + test/ruby/test_string.rb | 37 ++++-- test/ruby/test_string_memory.rb | 4 + test/ruby/test_stringchar.rb | 8 +- test/ruby/test_struct.rb | 1 + test/ruby/test_super.rb | 17 ++- test/ruby/test_symbol.rb | 7 +- test/ruby/test_syntax.rb | 13 +- test/ruby/test_thread.rb | 35 +++-- test/ruby/test_thread_cv.rb | 6 +- test/ruby/test_thread_queue.rb | 1 + test/ruby/test_time.rb | 5 +- test/ruby/test_time_tz.rb | 102 ++++++++++----- test/ruby/test_trace.rb | 7 +- test/ruby/test_variable.rb | 13 ++ test/ruby/test_whileuntil.rb | 16 ++- test/ruby/test_yield.rb | 4 +- test/ruby/test_yjit.rb | 2 +- test/test_open3.rb | 10 +- test/test_singleton.rb | 4 + test/test_time.rb | 12 +- test/test_tmpdir.rb | 4 +- tool/lib/core_assertions.rb | 82 +++++++----- tool/lib/envutil.rb | 11 +- tool/lib/leakchecker.rb | 4 + tool/lib/test/unit.rb | 70 +++++++++- tool/lib/test/unit/assertions.rb | 6 +- tool/lib/test/unit/testcase.rb | 26 +++- tool/lib/tracepointchecker.rb | 2 +- 101 files changed, 1224 insertions(+), 611 deletions(-) diff --git a/ext/-test-/file/fs.c b/ext/-test-/file/fs.c index eb17e9768ef6f1..bc5bac407df4fb 100644 --- a/ext/-test-/file/fs.c +++ b/ext/-test-/file/fs.c @@ -105,6 +105,9 @@ get_noatime_p(VALUE self, VALUE str) void Init_fs(VALUE module) { +#ifdef HAVE_RB_EXT_RACTOR_SAFE + rb_ext_ractor_safe(true); +#endif VALUE fs = rb_define_module_under(module, "Fs"); rb_define_module_function(fs, "fsname", get_fsname, 1); rb_define_module_function(fs, "noatime?", get_noatime_p, 1); diff --git a/ext/-test-/integer/init.c b/ext/-test-/integer/init.c index fc256ea16bf46e..483af73177eec3 100644 --- a/ext/-test-/integer/init.c +++ b/ext/-test-/integer/init.c @@ -5,6 +5,9 @@ void Init_integer(void) { +#ifdef HAVE_RB_EXT_RACTOR_SAFE + rb_ext_ractor_safe(true); +#endif VALUE mBug = rb_define_module("Bug"); VALUE klass = rb_define_class_under(mBug, "Integer", rb_cObject); TEST_INIT_FUNCS(init); diff --git a/ext/-test-/iter/break.c b/ext/-test-/iter/break.c index 4d43c5d0cf4782..b0b240a47537a2 100644 --- a/ext/-test-/iter/break.c +++ b/ext/-test-/iter/break.c @@ -19,6 +19,10 @@ iter_break_value(VALUE self, VALUE val) void Init_break(VALUE klass) { +#ifdef HAVE_RB_EXT_RACTOR_SAFE + // Mark this extension as Ractor-safe. + rb_ext_ractor_safe(true); +#endif VALUE breakable = rb_define_module_under(klass, "Breakable"); rb_define_module_function(breakable, "iter_break", iter_break, 0); rb_define_module_function(breakable, "iter_break_value", iter_break_value, 1); diff --git a/ext/-test-/iter/init.c b/ext/-test-/iter/init.c index a074ec46a982d4..2eea461ddec71e 100644 --- a/ext/-test-/iter/init.c +++ b/ext/-test-/iter/init.c @@ -5,6 +5,11 @@ void Init_iter(void) { + +#ifdef HAVE_RB_EXT_RACTOR_SAFE + // Mark this extension as Ractor-safe. + rb_ext_ractor_safe(true); +#endif VALUE mBug = rb_define_module("Bug"); VALUE klass = rb_define_module_under(mBug, "Iter"); TEST_INIT_FUNCS(init); diff --git a/ext/-test-/iter/yield.c b/ext/-test-/iter/yield.c index 0f6f3e87eb7731..e47166be1db9b6 100644 --- a/ext/-test-/iter/yield.c +++ b/ext/-test-/iter/yield.c @@ -10,6 +10,11 @@ yield_block(int argc, VALUE *argv, VALUE self) void Init_yield(VALUE klass) { + +#ifdef HAVE_RB_EXT_RACTOR_SAFE + // Mark this extension as Ractor-safe. + rb_ext_ractor_safe(true); +#endif VALUE yield = rb_define_module_under(klass, "Yield"); rb_define_method(yield, "yield_block", yield_block, -1); diff --git a/ext/-test-/rb_call_super_kw/rb_call_super_kw.c b/ext/-test-/rb_call_super_kw/rb_call_super_kw.c index 61681ed7334498..3607e008c3dccf 100644 --- a/ext/-test-/rb_call_super_kw/rb_call_super_kw.c +++ b/ext/-test-/rb_call_super_kw/rb_call_super_kw.c @@ -9,6 +9,10 @@ rb_call_super_kw_m(int argc, VALUE *argv, VALUE self) void Init_rb_call_super_kw(void) { +#ifdef HAVE_RB_EXT_RACTOR_SAFE + // Mark this extension as Ractor-safe. + rb_ext_ractor_safe(true); +#endif VALUE module = rb_define_module("Bug"); module = rb_define_module_under(module, "RbCallSuperKw"); rb_define_method(module, "m", rb_call_super_kw_m, -1); diff --git a/test/fileutils/test_fileutils.rb b/test/fileutils/test_fileutils.rb index 92308d95573206..2e145b4ee93f83 100644 --- a/test/fileutils/test_fileutils.rb +++ b/test/fileutils/test_fileutils.rb @@ -84,13 +84,10 @@ def check_have_hardlink? return true end - @@no_broken_symlink = false - if /cygwin/ =~ RUBY_PLATFORM and /\bwinsymlinks:native(?:strict)?\b/ =~ ENV["CYGWIN"] - @@no_broken_symlink = true - end + NO_BROKEN_SYMLINK = /cygwin/ =~ RUBY_PLATFORM and /\bwinsymlinks:native(?:strict)?\b/ =~ ENV["CYGWIN"] def no_broken_symlink? - @@no_broken_symlink + NO_BROKEN_SYMLINK end def has_capsh? @@ -196,7 +193,7 @@ def teardown end - TARGETS = %w( data/a data/all data/random data/zero ) + TARGETS = %w( data/a data/all data/random data/zero ).freeze def prepare_data_file File.open('data/a', 'w') {|f| diff --git a/test/ruby/enc/test_case_comprehensive.rb b/test/ruby/enc/test_case_comprehensive.rb index b812b88b832be2..0692391ea89cf7 100644 --- a/test/ruby/enc/test_case_comprehensive.rb +++ b/test/ruby/enc/test_case_comprehensive.rb @@ -5,8 +5,8 @@ class TestComprehensiveCaseMapping < Test::Unit::TestCase UNICODE_VERSION = RbConfig::CONFIG['UNICODE_VERSION'] - path = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}", __dir__) - UNICODE_DATA_PATH = File.directory?("#{path}/ucd") ? "#{path}/ucd" : path + path = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}", __dir__).freeze + UNICODE_DATA_PATH = File.directory?("#{path}/ucd") ? "#{path}/ucd".freeze : path def self.hex2utf8(s) s.split(' ').map { |c| c.to_i(16) }.pack('U*') @@ -29,7 +29,7 @@ def test_data_files_available end end -TestComprehensiveCaseMapping.data_files_available? and class TestComprehensiveCaseMapping +TestComprehensiveCaseMapping.data_files_available? && will_run_in_main_ractor? && class TestComprehensiveCaseMapping (CaseTest = Struct.new(:method_name, :attributes, :first_data, :follow_data)).class_eval do def initialize(method_name, attributes, first_data, follow_data=first_data) super diff --git a/test/ruby/enc/test_emoji_breaks.rb b/test/ruby/enc/test_emoji_breaks.rb index bb5114680e686f..7789c8eed2bb4d 100644 --- a/test/ruby/enc/test_emoji_breaks.rb +++ b/test/ruby/enc/test_emoji_breaks.rb @@ -46,15 +46,16 @@ def self.files end UNICODE_VERSION = RbConfig::CONFIG['UNICODE_VERSION'] - UNICODE_DATA_PATH = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}/ucd/emoji", __dir__) + UNICODE_DATA_PATH = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}/ucd/emoji", __dir__).freeze EMOJI_VERSION = RbConfig::CONFIG['UNICODE_EMOJI_VERSION'] - EMOJI_DATA_PATH = File.expand_path("../../../enc/unicode/data/emoji/#{EMOJI_VERSION}", __dir__) + EMOJI_DATA_PATH = File.expand_path("../../../enc/unicode/data/emoji/#{EMOJI_VERSION}", __dir__).freeze EMOJI_DATA_FILES = %w[emoji-sequences emoji-test emoji-zwj-sequences].map do |basename| BreakFile.new(basename, EMOJI_DATA_PATH, EMOJI_VERSION) end UNICODE_DATA_FILE = BreakFile.new('emoji-variation-sequences', UNICODE_DATA_PATH, UNICODE_VERSION) EMOJI_DATA_FILES << UNICODE_DATA_FILE + Ractor.make_shareable(EMOJI_DATA_FILES) def self.data_files_available? EMOJI_DATA_FILES.all? do |f| diff --git a/test/ruby/enc/test_grapheme_breaks.rb b/test/ruby/enc/test_grapheme_breaks.rb index 7e6d722d4076e1..88eeaf6cf8ab89 100644 --- a/test/ruby/enc/test_grapheme_breaks.rb +++ b/test/ruby/enc/test_grapheme_breaks.rb @@ -27,9 +27,9 @@ def initialize(line_number, data, comment) end UNICODE_VERSION = RbConfig::CONFIG['UNICODE_VERSION'] - path = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}", __dir__) - UNICODE_DATA_PATH = File.directory?("#{path}/ucd/auxiliary") ? "#{path}/ucd/auxiliary" : path - GRAPHEME_BREAK_TEST_FILE = File.expand_path("#{UNICODE_DATA_PATH}/GraphemeBreakTest.txt", __dir__) + path = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}", __dir__).freeze + UNICODE_DATA_PATH = File.directory?("#{path}/ucd/auxiliary") ? "#{path}/ucd/auxiliary".freeze : path + GRAPHEME_BREAK_TEST_FILE = File.expand_path("#{UNICODE_DATA_PATH}/GraphemeBreakTest.txt", __dir__).freeze def self.file_available? File.exist? GRAPHEME_BREAK_TEST_FILE @@ -44,10 +44,12 @@ def test_data_files_available if file_available? def read_data tests = [] + lineno = 1 File.foreach(GRAPHEME_BREAK_TEST_FILE, encoding: Encoding::UTF_8) do |line| - if $. == 1 and not line.start_with?("# GraphemeBreakTest-#{UNICODE_VERSION}.txt") + if lineno == 1 and not line.start_with?("# GraphemeBreakTest-#{UNICODE_VERSION}.txt") raise "File Version Mismatch" end + lineno += 1 next if /\A#/.match? line tests << BreakTest.new($., *line.chomp.split('#')) rescue 'whatever' end @@ -55,9 +57,9 @@ def read_data end def all_tests - @@tests ||= read_data + @grapheme_breaks_tests ||= read_data rescue Errno::ENOENT - @@tests ||= [] + @grapheme_breaks_tests ||= [] end def test_each_grapheme_cluster diff --git a/test/ruby/enc/test_iso_8859.rb b/test/ruby/enc/test_iso_8859.rb index ed663be24307fb..3b0a830bafeba7 100644 --- a/test/ruby/enc/test_iso_8859.rb +++ b/test/ruby/enc/test_iso_8859.rb @@ -2,7 +2,7 @@ require 'test/unit' class TestISO8859 < Test::Unit::TestCase - ASSERTS = %q( + ASSERTS = Ractor.make_shareable(%q( assert_match(/^(\xdf)\1$/i, "\xdf\xdf") assert_match(/^(\xdf)\1$/i, "ssss") # assert_match(/^(\xdf)\1$/i, "\xdfss") # this must be bug... @@ -18,7 +18,7 @@ class TestISO8859 < Test::Unit::TestCase assert_match(/^[#{ c2 }]+$/i, c1 + c2) end assert_match(/^\xff$/i, "\xff") - ) + )) def test_iso_8859_1 eval("# encoding: iso8859-1\n" + ASSERTS.gsub(/ENCODING/m, "iso8859-1")) diff --git a/test/ruby/enc/test_koi8.rb b/test/ruby/enc/test_koi8.rb index 4a4d233e8d686d..8557ff29590912 100644 --- a/test/ruby/enc/test_koi8.rb +++ b/test/ruby/enc/test_koi8.rb @@ -2,7 +2,7 @@ require "test/unit" class TestKOI8 < Test::Unit::TestCase - ASSERTS = %q( + ASSERTS = Ractor.make_shareable(%q( (0xc0..0xdf).each do |c| c1 = c.chr("ENCODING") c2 = (c + 0x20).chr("ENCODING") @@ -11,7 +11,7 @@ class TestKOI8 < Test::Unit::TestCase assert_match(/^[#{ c1 }]+$/i, c2 + c1) assert_match(/^[#{ c2 }]+$/i, c1 + c2) end - ) + )) def test_koi8_r eval("# encoding: koi8-r\n" + ASSERTS.gsub("ENCODING", "koi8-r")) diff --git a/test/ruby/enc/test_regex_casefold.rb b/test/ruby/enc/test_regex_casefold.rb index b5d5c6e33740f5..a006453e856f23 100644 --- a/test/ruby/enc/test_regex_casefold.rb +++ b/test/ruby/enc/test_regex_casefold.rb @@ -5,8 +5,8 @@ class TestCaseFold < Test::Unit::TestCase UNICODE_VERSION = RbConfig::CONFIG['UNICODE_VERSION'] - path = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}", __dir__) - UNICODE_DATA_PATH = File.directory?("#{path}/ucd") ? "#{path}/ucd" : path + path = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}", __dir__).freeze + UNICODE_DATA_PATH = File.directory?("#{path}/ucd") ? "#{path}/ucd".freeze : path CaseTest = Struct.new :source, :target, :kind, :line def check_downcase_properties(expected, start, *flags) @@ -36,15 +36,21 @@ def to_codepoints(string) end def setup - @@tests ||= read_tests + pend "TODO: tests use define_method" if non_main_ractor? + @regex_casefold_tests ||= read_tests rescue Errno::ENOENT => e - @@tests ||= [] + @regex_casefold_tests ||= [] omit e.message end + def tests + setup + @regex_casefold_tests + end + def self.generate_test_casefold(encoding) define_method "test_mbc_case_fold_#{encoding}" do - @@tests.each do |test| + tests.each do |test| begin source = test.source.encode encoding target = test.target.encode encoding @@ -57,7 +63,7 @@ def self.generate_test_casefold(encoding) end define_method "test_get_case_fold_codes_by_str_#{encoding}" do - @@tests.each do |test| + tests.each do |test| begin source = test.source.encode encoding target = test.target.encode encoding @@ -71,7 +77,7 @@ def self.generate_test_casefold(encoding) end define_method "test_apply_all_case_fold_#{encoding}" do - @@tests.each do |test| + tests.each do |test| begin source = test.source.encode encoding target = test.target.encode encoding @@ -90,7 +96,7 @@ def self.generate_test_casefold(encoding) end def test_downcase_fold - @@tests.each do |test| + tests.each do |test| check_downcase_properties test.target, test.source, :fold end end diff --git a/test/ruby/marshaltestlib.rb b/test/ruby/marshaltestlib.rb index 7f100b787362b4..93a0c6d25606ef 100644 --- a/test/ruby/marshaltestlib.rb +++ b/test/ruby/marshaltestlib.rb @@ -365,6 +365,7 @@ def test_range_cyclic end def test_singleton + omit "ivar of singleton class" if non_main_ractor? o = Object.new def o.m() end assert_raise(TypeError) { marshaltest(o) } diff --git a/test/ruby/test_alias.rb b/test/ruby/test_alias.rb index 539cd4948878b3..31eaa465556a90 100644 --- a/test/ruby/test_alias.rb +++ b/test/ruby/test_alias.rb @@ -227,6 +227,7 @@ def test_alias_method_equation end def test_alias_class_method_added + omit "class ivars" if non_main_ractor? name = nil k = Class.new { def foo;end @@ -241,6 +242,7 @@ def self.method_added(mid) end def test_alias_module_method_added + omit "module ivars" if non_main_ractor? name = nil k = Module.new { def foo;end diff --git a/test/ruby/test_allocation.rb b/test/ruby/test_allocation.rb index a2ccd7bd659d0e..d7a8f9302bc647 100644 --- a/test/ruby/test_allocation.rb +++ b/test/ruby/test_allocation.rb @@ -6,6 +6,7 @@ def setup # The namespace changes on i686 platform triggers a bug to allocate objects unexpectedly. # For now, skip these tests only on i686 pend if RUBY_PLATFORM =~ /^i686/ + pend if non_main_ractor? end def munge_checks(checks) diff --git a/test/ruby/test_argf.rb b/test/ruby/test_argf.rb index 55a06296aa7275..f868b56f32c5c0 100644 --- a/test/ruby/test_argf.rb +++ b/test/ruby/test_argf.rb @@ -7,6 +7,7 @@ class TestArgf < Test::Unit::TestCase def setup + omit "ARGF is not shareable" if non_main_ractor? @tmpdir = Dir.mktmpdir @tmp_count = 0 @t1 = make_tempfile("argf-foo", %w"1 2", binmode: true) @@ -15,6 +16,7 @@ def setup end def teardown + return if non_main_ractor? FileUtils.rmtree(@tmpdir) end diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb index a3ac0a6a0b420d..fd293c8d276cad 100644 --- a/test/ruby/test_array.rb +++ b/test/ruby/test_array.rb @@ -1173,44 +1173,52 @@ def test_values_at end def test_join - assert_deprecated_warning {$, = ""} + if main_ractor? + assert_deprecated_warn = method(:assert_deprecated_warn) + assert_deprecated_warning {$, = ""} + else + assert_deprecated_warn = lambda { |*, &blk| blk.call } + end + a = @cls[] - assert_equal("", assert_deprecated_warn(/non-nil value/) {a.join}) + assert_equal("", assert_deprecated_warn.(/non-nil value/) {a.join}) assert_equal("", a.join(',')) - assert_equal(Encoding::US_ASCII, assert_deprecated_warn(/non-nil value/) {a.join}.encoding) + assert_equal(Encoding::US_ASCII, assert_deprecated_warn.(/non-nil value/) {a.join}.encoding) - assert_deprecated_warning {$, = ""} + assert_deprecated_warning {$, = ""} if main_ractor? a = @cls[1, 2] - assert_equal("12", assert_deprecated_warn(/non-nil value/) {a.join}) - assert_equal("12", assert_deprecated_warn(/non-nil value/) {a.join(nil)}) + assert_equal("12", assert_deprecated_warn.(/non-nil value/) {a.join}) + assert_equal("12", assert_deprecated_warn.(/non-nil value/) {a.join(nil)}) assert_equal("1,2", a.join(',')) - assert_deprecated_warning {$, = ""} + assert_deprecated_warning {$, = ""} if main_ractor? a = @cls[1, 2, 3] - assert_equal("123", assert_deprecated_warn(/non-nil value/) {a.join}) - assert_equal("123", assert_deprecated_warn(/non-nil value/) {a.join(nil)}) + assert_equal("123", assert_deprecated_warn.(/non-nil value/) {a.join}) + assert_equal("123", assert_deprecated_warn.(/non-nil value/) {a.join(nil)}) assert_equal("1,2,3", a.join(',')) - assert_deprecated_warning {$, = ":"} + assert_deprecated_warning {$, = ":"} if main_ractor? a = @cls[1, 2, 3] - assert_equal("1:2:3", assert_deprecated_warn(/non-nil value/) {a.join}) - assert_equal("1:2:3", assert_deprecated_warn(/non-nil value/) {a.join(nil)}) + if main_ractor? + assert_equal("1:2:3", assert_deprecated_warn.(/non-nil value/) {a.join}) + assert_equal("1:2:3", assert_deprecated_warn.(/non-nil value/) {a.join(nil)}) + end assert_equal("1,2,3", a.join(',')) - assert_deprecated_warning {$, = ""} + assert_deprecated_warning {$, = ""} if main_ractor? e = ''.force_encoding('EUC-JP') u = ''.force_encoding('UTF-8') - assert_equal(Encoding::US_ASCII, assert_deprecated_warn(/non-nil value/) {[[]].join}.encoding) - assert_equal(Encoding::US_ASCII, assert_deprecated_warn(/non-nil value/) {[1, [u]].join}.encoding) - assert_equal(Encoding::UTF_8, assert_deprecated_warn(/non-nil value/) {[u, [e]].join}.encoding) - assert_equal(Encoding::UTF_8, assert_deprecated_warn(/non-nil value/) {[u, [1]].join}.encoding) - assert_equal(Encoding::UTF_8, assert_deprecated_warn(/non-nil value/) {[Struct.new(:to_str).new(u)].join}.encoding) + assert_equal(Encoding::US_ASCII, assert_deprecated_warn.(/non-nil value/) {[[]].join}.encoding) + assert_equal(Encoding::US_ASCII, assert_deprecated_warn.(/non-nil value/) {[1, [u]].join}.encoding) + assert_equal(Encoding::UTF_8, assert_deprecated_warn.(/non-nil value/) {[u, [e]].join}.encoding) + assert_equal(Encoding::UTF_8, assert_deprecated_warn.(/non-nil value/) {[u, [1]].join}.encoding) + assert_equal(Encoding::UTF_8, assert_deprecated_warn.(/non-nil value/) {[Struct.new(:to_str).new(u)].join}.encoding) bug5379 = '[ruby-core:39776]' - assert_equal(Encoding::US_ASCII, assert_deprecated_warn(/non-nil value/) {[[], u, nil].join}.encoding, bug5379) - assert_equal(Encoding::UTF_8, assert_deprecated_warn(/non-nil value/) {[[], "\u3042", nil].join}.encoding, bug5379) + assert_equal(Encoding::US_ASCII, assert_deprecated_warn.(/non-nil value/) {[[], u, nil].join}.encoding, bug5379) + assert_equal(Encoding::UTF_8, assert_deprecated_warn.(/non-nil value/) {[[], "\u3042", nil].join}.encoding, bug5379) ensure - $, = nil + $, = nil if main_ractor? end def test_last @@ -1956,33 +1964,33 @@ def o.to_ary end def test_to_s - assert_deprecated_warning {$, = ""} + assert_deprecated_warning {$, = ""} if main_ractor? a = @cls[] assert_equal("[]", a.to_s) - assert_deprecated_warning {$, = ""} + assert_deprecated_warning {$, = ""} if main_ractor? a = @cls[1, 2] assert_equal("[1, 2]", a.to_s) - assert_deprecated_warning {$, = ""} + assert_deprecated_warning {$, = ""} if main_ractor? a = @cls[1, 2, 3] assert_equal("[1, 2, 3]", a.to_s) - assert_deprecated_warning {$, = ""} + assert_deprecated_warning {$, = ""} if main_ractor? a = @cls[1, 2, 3] assert_equal("[1, 2, 3]", a.to_s) ensure - $, = nil + $, = nil if main_ractor? end - StubToH = [ + StubToH = Ractor.make_shareable([ [:key, :value], Object.new.tap do |kvp| def kvp.to_ary [:obtained, :via_to_ary] end end, - ] + ]) def test_to_h array = StubToH @@ -2977,6 +2985,7 @@ def o.==(x); :foo; end end def test_equal_resize + omit "global variable access" if non_main_ractor? $test_equal_resize_a = Array.new(3, &:to_s) $test_equal_resize_b = $test_equal_resize_a.dup o = Object.new @@ -3590,6 +3599,7 @@ def need_continuation EnvUtil.suppress_warning {require 'continuation'} end omit 'requires callcc support' unless respond_to?(:callcc, true) + omit "not ractor safe" if non_main_ractor? end def random_generator(&block) diff --git a/test/ruby/test_assignment.rb b/test/ruby/test_assignment.rb index 3d0e773c825664..6e9452340fea83 100644 --- a/test/ruby/test_assignment.rb +++ b/test/ruby/test_assignment.rb @@ -140,6 +140,7 @@ def test_massign_order end def test_massign_const_order + pend "mutated constants" if non_main_ractor? order = [] test_mod_class = Class.new(Module) do @@ -928,6 +929,7 @@ def check(assign) end def test_assignment + pend "errors with ractors" if non_main_ractor? syntax = Sentence.expand_syntax(Syntax) Sentence.each(syntax, :xassign, 4) {|assign| check(assign) diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb index 9a7d75c270b661..19495265f756b6 100644 --- a/test/ruby/test_ast.rb +++ b/test/ruby/test_ast.rb @@ -116,49 +116,53 @@ def validate_not_cared0(node) SRCDIR = File.expand_path("../../..", __FILE__) Dir.glob("test/**/*.rb", base: SRCDIR).each do |path| - define_method("test_ranges:#{path}") do - helper = Helper.new("#{SRCDIR}/#{path}") - helper.validate_range - - assert_equal([], helper.errors) - end + class_eval <<-RUBY + def #{"test_ranges_#{path}".gsub(/[^\w]/, '_')} + helper = Helper.new("#{SRCDIR}/#{path}") + helper.validate_range + assert_equal([], helper.errors) + end + RUBY end Dir.glob("test/**/*.rb", base: SRCDIR).each do |path| - define_method("test_not_cared:#{path}") do - helper = Helper.new("#{SRCDIR}/#{path}") - helper.validate_not_cared - - assert_equal([], helper.errors) - end + class_eval <<-RUBY + def #{"test_not_cared:#{path}".gsub(/[^\w]/, '_')} + helper = Helper.new("#{SRCDIR}/#{path}") + helper.validate_not_cared + assert_equal([], helper.errors) + end + RUBY end Dir.glob("test/**/*.rb", base: SRCDIR).each do |path| - define_method("test_all_tokens:#{path}") do - node = RubyVM::AbstractSyntaxTree.parse_file("#{SRCDIR}/#{path}", keep_tokens: true) - tokens = node.all_tokens.sort_by { [_1.last[0], _1.last[1]] } - tokens_bytes = tokens.map { _1[2]}.join.bytes - source_bytes = File.read("#{SRCDIR}/#{path}").bytes - - assert_equal(source_bytes, tokens_bytes) - - (tokens.count - 1).times do |i| - token_0 = tokens[i] - token_1 = tokens[i + 1] - end_pos = token_0.last[2..3] - beg_pos = token_1.last[0..1] - - if end_pos[0] == beg_pos[0] - # When both tokens are same line, column should be consecutives - assert_equal(beg_pos[1], end_pos[1], "#{token_0}. #{token_1}") - else - # Line should be next - assert_equal(beg_pos[0], end_pos[0] + 1, "#{token_0}. #{token_1}") - # It should be on the beginning of the line - assert_equal(0, beg_pos[1], "#{token_0}. #{token_1}") + class_eval <<-RUBY + def #{"test_all_tokens:#{path}".gsub(/[^\w]/, '_')} + node = RubyVM::AbstractSyntaxTree.parse_file("#{SRCDIR}/#{path}", keep_tokens: true) + tokens = node.all_tokens.sort_by { [_1.last[0], _1.last[1]] } + tokens_bytes = tokens.map { _1[2]}.join.bytes + source_bytes = File.read("#{SRCDIR}/#{path}").bytes + + assert_equal(source_bytes, tokens_bytes) + + (tokens.count - 1).times do |i| + token_0 = tokens[i] + token_1 = tokens[i + 1] + end_pos = token_0.last[2..3] + beg_pos = token_1.last[0..1] + + if end_pos[0] == beg_pos[0] + # When both tokens are same line, column should be consecutives + assert_equal(beg_pos[1], end_pos[1], "\#{token_0}. \#{token_1}") + else + # Line should be next + assert_equal(beg_pos[0], end_pos[0] + 1, "\#{token_0}. \#{token_1}") + # It should be on the beginning of the line + assert_equal(0, beg_pos[1], "\#{token_0}. \#{token_1}") + end end end - end + RUBY end private def parse(src) diff --git a/test/ruby/test_autoload.rb b/test/ruby/test_autoload.rb index 607f0e3355efe9..3672ce1df9a30c 100644 --- a/test/ruby/test_autoload.rb +++ b/test/ruby/test_autoload.rb @@ -66,6 +66,7 @@ def test_autoload_p end def test_autoload_p_with_static_extensions + pend "belonging issue" if non_main_ractor? require 'rbconfig' omit unless RbConfig::CONFIG['EXTSTATIC'] == 'static' begin @@ -84,6 +85,7 @@ def test_autoload_p_with_static_extensions end def test_autoload_with_unqualified_file_name # [ruby-core:69206] + omit "global variable access" if non_main_ractor? Object.send(:remove_const, :A) if Object.const_defined?(:A) lp = $LOAD_PATH.dup @@ -106,12 +108,13 @@ module A end } ensure - $LOAD_PATH.replace lp - $LOADED_FEATURES.replace lf + $LOAD_PATH.replace lp if lp + $LOADED_FEATURES.replace lf if lf Object.send(:remove_const, :A) if Object.const_defined?(:A) end def test_require_explicit + pend "Tempfile" if non_main_ractor? Tempfile.create(['autoload', '.rb']) {|file| file.puts 'class Object; AutoloadTest = 1; end' file.close @@ -128,6 +131,7 @@ def test_require_explicit end def test_threaded_accessing_constant + pend "Tempfile" if non_main_ractor? # Suppress "warning: loading in progress, circular require considered harmful" EnvUtil.default_warning { Tempfile.create(['autoload', '.rb']) {|file| @@ -148,6 +152,7 @@ def test_threaded_accessing_constant end def test_threaded_accessing_inner_constant + pend "Tempfile" if non_main_ractor? # Suppress "warning: loading in progress, circular require considered harmful" EnvUtil.default_warning { Tempfile.create(['autoload', '.rb']) {|file| @@ -168,6 +173,7 @@ def test_threaded_accessing_inner_constant end def test_nameerror_when_autoload_did_not_define_the_constant + pend "Tempfile" if non_main_ractor? verbose_bak, $VERBOSE = $VERBOSE, nil Tempfile.create(['autoload', '.rb']) {|file| file.puts '' @@ -186,6 +192,7 @@ def test_nameerror_when_autoload_did_not_define_the_constant end def test_override_autoload + pend "Tempfile" if non_main_ractor? Tempfile.create(['autoload', '.rb']) {|file| file.puts '' file.close @@ -200,6 +207,7 @@ def test_override_autoload end def test_override_while_autoloading + pend "Tempfile" if non_main_ractor? Tempfile.create(['autoload', '.rb']) {|file| file.puts 'class AutoloadTest; sleep 0.5; end' file.close @@ -251,6 +259,7 @@ def ruby_impl_require end def test_require_implemented_in_ruby_is_called + pend "Tempfile" if non_main_ractor? ruby_impl_require do |called_with| Tempfile.create(['autoload', '.rb']) {|file| file.puts 'class AutoloadTest; end' @@ -268,6 +277,7 @@ def test_require_implemented_in_ruby_is_called end def test_autoload_while_autoloading + pend "Tempfile" if non_main_ractor? ruby_impl_require do |called_with| Tempfile.create(%w(a .rb)) do |a| Tempfile.create(%w(b .rb)) do |b| @@ -397,6 +407,7 @@ class AutoloadTest end def test_autoload_fork + pend "Tempfile" if non_main_ractor? EnvUtil.default_warning do Tempfile.create(['autoload', '.rb']) {|file| file.puts 'sleep 0.3; class AutoloadTest; end' diff --git a/test/ruby/test_basicinstructions.rb b/test/ruby/test_basicinstructions.rb index f6b69cc1e57e58..6acf200a4dbf16 100644 --- a/test/ruby/test_basicinstructions.rb +++ b/test/ruby/test_basicinstructions.rb @@ -9,6 +9,10 @@ class Class class TestBasicInstructions < Test::Unit::TestCase + def setup + omit "Lots of unfrozen strings in constants" if non_main_ractor? + end + def test_immediates assert_equal((1==1), true) assert_equal((1==2), false) diff --git a/test/ruby/test_beginendblock.rb b/test/ruby/test_beginendblock.rb index 3706efab52dd0b..c86797aa6d4e80 100644 --- a/test/ruby/test_beginendblock.rb +++ b/test/ruby/test_beginendblock.rb @@ -3,7 +3,7 @@ EnvUtil.suppress_warning {require 'continuation'} class TestBeginEndBlock < Test::Unit::TestCase - DIR = File.dirname(File.expand_path(__FILE__)) + DIR = File.dirname(File.expand_path(__FILE__)).freeze def test_beginendblock target = File.join(DIR, 'beginmainend.rb') @@ -170,7 +170,7 @@ def test_internal_errinfo_at_exit error, pid, status = IO.pipe do |r, w| pid = fork do r.close - STDERR.reopen(w) + $stderr.reopen(w) at_exit do $!.class end diff --git a/test/ruby/test_bignum.rb b/test/ruby/test_bignum.rb index dd6f4baa4c292b..122130819934ba 100644 --- a/test/ruby/test_bignum.rb +++ b/test/ruby/test_bignum.rb @@ -70,17 +70,18 @@ def test_prepare end def test_bignum - $x = fact(40) - assert_equal($x, $x) - assert_equal($x, fact(40)) - assert_operator($x, :<, $x+2) - assert_operator($x, :>, $x-2) - assert_equal(815915283247897734345611269596115894272000000000, $x) - assert_not_equal(815915283247897734345611269596115894272000000001, $x) - assert_equal(815915283247897734345611269596115894272000000001, $x+1) - assert_equal(335367096786357081410764800000, $x/fact(20)) - $x = -$x - assert_equal(-815915283247897734345611269596115894272000000000, $x) + omit "global var access" if non_main_ractor? + x = fact(40) + assert_equal(x, x) + assert_equal(x, fact(40)) + assert_operator(x, :<, $x+2) + assert_operator(x, :>, $x-2) + assert_equal(815915283247897734345611269596115894272000000000, x) + assert_not_equal(815915283247897734345611269596115894272000000001, x) + assert_equal(815915283247897734345611269596115894272000000001, x+1) + assert_equal(335367096786357081410764800000, x/fact(20)) + x = -x + assert_equal(-815915283247897734345611269596115894272000000000, x) b = 2*BIGNUM_MIN assert_equal(2-b, -(b-2)) @@ -705,6 +706,7 @@ def test_interrupt_during_bigdivrem return # GMP doesn't support interrupt during an operation. end return unless Process.respond_to?(:kill) + omit "Signal.trap lambda accesses outers" if non_main_ractor? begin trace = [] oldtrap = Signal.trap(:INT) {|sig| trace << :int } diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index 74541bba3f7d87..0d517d72de7a19 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -38,6 +38,7 @@ class ClassFour < ClassThree # ------------------ def test_s_inherited + pend "accesses class variables" if non_main_ractor? assert_equal([ClassTwo, ClassThree, ClassFour], ClassOne.new.subs) end @@ -465,20 +466,26 @@ class C < c } end - define_method :test_invalid_reset_superclass do - class A; end - class SuperclassCannotBeReset < A - end + def test_invalid_reset_superclass + self.class.class_eval <<-RUBY + class A; end + class SuperclassCannotBeReset < A + end + RUBY assert_equal A, SuperclassCannotBeReset.superclass assert_raise_with_message(TypeError, /superclass mismatch/) { - class SuperclassCannotBeReset < String - end + self.class.class_eval <<-RUBY + class SuperclassCannotBeReset < String + end + RUBY } assert_raise_with_message(TypeError, /superclass mismatch/, "[ruby-core:75446]") { - class SuperclassCannotBeReset < Object - end + self.class.class_eval <<-RUBY + class SuperclassCannotBeReset < Object + end + RUBY } assert_equal A, SuperclassCannotBeReset.superclass @@ -574,6 +581,7 @@ def c.f; end end def test_singleton_class_should_has_own_namespace + pend "Accesses global" if non_main_ractor? # CONST in singleton class objs = [] $i = 0 @@ -765,7 +773,11 @@ def test_subclasses ssc = Class.new(sc) [c, sc, ssc].each do |k| k.include Module.new - k.new.define_singleton_method(:force_singleton_class){} + k.new.define_singleton_method(:force_singleton_class, &Ractor.make_shareable( + nil.instance_eval do + proc { } + end + )) end assert_equal([sc], c.subclasses) assert_equal([ssc], sc.subclasses) diff --git a/test/ruby/test_compile_prism.rb b/test/ruby/test_compile_prism.rb index b95add5bd45fe4..41bb1995a6d1df 100644 --- a/test/ruby/test_compile_prism.rb +++ b/test/ruby/test_compile_prism.rb @@ -81,6 +81,7 @@ def test_BackReferenceReadNode end def test_ClassVariableReadNode + omit "class variables" if non_main_ractor? assert_prism_eval("class Prism::TestCompilePrism; @@pit = 1; @@pit; end") end @@ -134,9 +135,11 @@ def test_DefinedNode assert_prism_eval("defined? [a: [:b, :c]]") assert_prism_eval("defined? 1 in 1") - assert_prism_eval("defined? @a") - assert_prism_eval("defined? $a") - assert_prism_eval("defined? @@a") + assert_prism_eval("defined? @a", raw: true) + if main_ractor? + assert_prism_eval("defined? $a") + assert_prism_eval("defined? @@a") + end assert_prism_eval("defined? A") assert_prism_eval("defined? ::A") assert_prism_eval("defined? A::B") @@ -151,29 +154,31 @@ def test_DefinedNode assert_prism_eval("defined? X &= 1") assert_prism_eval("defined? X ||= 1") - assert_prism_eval("defined? $1") - assert_prism_eval("defined? $2") - assert_prism_eval("defined? $`") - assert_prism_eval("defined? $'") - assert_prism_eval("defined? $+") - - assert_prism_eval("defined? $X = 1") - assert_prism_eval("defined? $X *= 1") - assert_prism_eval("defined? $X /= 1") - assert_prism_eval("defined? $X &= 1") - assert_prism_eval("defined? $X ||= 1") - - assert_prism_eval("defined? @@X = 1") - assert_prism_eval("defined? @@X *= 1") - assert_prism_eval("defined? @@X /= 1") - assert_prism_eval("defined? @@X &= 1") - assert_prism_eval("defined? @@X ||= 1") - - assert_prism_eval("defined? @X = 1") - assert_prism_eval("defined? @X *= 1") - assert_prism_eval("defined? @X /= 1") - assert_prism_eval("defined? @X &= 1") - assert_prism_eval("defined? @X ||= 1") + if main_ractor? + assert_prism_eval("defined? $1") + assert_prism_eval("defined? $2") + assert_prism_eval("defined? $`") + assert_prism_eval("defined? $'") + assert_prism_eval("defined? $+") + + assert_prism_eval("defined? $X = 1") + assert_prism_eval("defined? $X *= 1") + assert_prism_eval("defined? $X /= 1") + assert_prism_eval("defined? $X &= 1") + assert_prism_eval("defined? $X ||= 1") + + assert_prism_eval("defined? @@X = 1") + assert_prism_eval("defined? @@X *= 1") + assert_prism_eval("defined? @@X /= 1") + assert_prism_eval("defined? @@X &= 1") + assert_prism_eval("defined? @@X ||= 1") + end + + assert_prism_eval("defined? @X = 1", raw: true) + assert_prism_eval("defined? @X *= 1", raw: true) + assert_prism_eval("defined? @X /= 1", raw: true) + assert_prism_eval("defined? @X &= 1", raw: true) + assert_prism_eval("defined? @X ||= 1", raw: true) assert_prism_eval("x = 1; defined? x = 1") assert_prism_eval("x = 1; defined? x *= 1") @@ -275,10 +280,12 @@ def self.m1; defined?(return) end end def test_GlobalVariableReadNode + omit "global variable access" if non_main_ractor? assert_prism_eval("$pit = 1; $pit") end def test_InstanceVariableReadNode + omit "class ivars" if non_main_ractor? assert_prism_eval("class Prism::TestCompilePrism; @pit = 1; @pit; end") end @@ -296,19 +303,23 @@ def test_NumberedReferenceReadNode ############################################################################ def test_ClassVariableAndWriteNode + omit "class variables" if non_main_ractor? assert_prism_eval("class Prism::TestCompilePrism; @@pit = 0; @@pit &&= 1; end") end def test_ClassVariableOperatorWriteNode + omit "class variables" if non_main_ractor? assert_prism_eval("class Prism::TestCompilePrism; @@pit = 0; @@pit += 1; end") end def test_ClassVariableOrWriteNode + omit "class variables" if non_main_ractor? assert_prism_eval("class Prism::TestCompilePrism; @@pit = 1; @@pit ||= 0; end") assert_prism_eval("class Prism::TestCompilePrism; @@pit = nil; @@pit ||= 1; end") end def test_ClassVariableWriteNode + omit "class variables" if non_main_ractor? assert_prism_eval("class Prism::TestCompilePrism; @@pit = 1; end") end @@ -359,34 +370,39 @@ def test_ConstantPathOperatorWriteNode end def test_GlobalVariableAndWriteNode + omit "global variable access" if non_main_ractor? assert_prism_eval("$pit = 0; $pit &&= 1") end def test_GlobalVariableOperatorWriteNode + omit "global variable access" if non_main_ractor? assert_prism_eval("$pit = 0; $pit += 1") end def test_GlobalVariableOrWriteNode + omit "global variable access" if non_main_ractor? assert_prism_eval("$pit ||= 1") end def test_GlobalVariableWriteNode + omit "global variable access" if non_main_ractor? assert_prism_eval("$pit = 1") end def test_InstanceVariableAndWriteNode - assert_prism_eval("@pit = 0; @pit &&= 1") + assert_prism_eval("@pit = 0; @pit &&= 1", raw: non_main_ractor?) end def test_InstanceVariableOperatorWriteNode - assert_prism_eval("@pit = 0; @pit += 1") + assert_prism_eval("@pit = 0; @pit += 1", raw: non_main_ractor?) end def test_InstanceVariableOrWriteNode - assert_prism_eval("@pit ||= 1") + assert_prism_eval("@pit ||= 1", raw: non_main_ractor?) end def test_InstanceVariableWriteNode + omit "class ivars" if non_main_ractor? assert_prism_eval("class Prism::TestCompilePrism; @pit = 1; end") end @@ -438,6 +454,7 @@ def test_MatchWriteNode ############################################################################ def test_ClassVariableTargetNode + omit "class variables" if non_main_ractor? assert_prism_eval("class Prism::TestCompilePrism; @@pit, @@pit1 = 1; end") end @@ -473,10 +490,12 @@ def test_ConstantPathTargetNode end def test_GlobalVariableTargetNode + omit "global variable access" if non_main_ractor? assert_prism_eval("$pit, $pit1 = 1") end def test_InstanceVariableTargetNode + omit "class ivars" if non_main_ractor? assert_prism_eval("class Prism::TestCompilePrism; @pit, @pit1 = 1; end") end @@ -584,16 +603,19 @@ def test_EmbeddedStatementsNode end def test_EmbeddedVariableNode + omit "class ivars" if non_main_ractor? assert_prism_eval('class Prism::TestCompilePrism; @pit = 1; "#@pit"; end') assert_prism_eval('class Prism::TestCompilePrism; @@pit = 1; "#@@pit"; end') assert_prism_eval('$pit = 1; "#$pit"') end def test_InterpolatedMatchLastLineNode + omit "global variables access" if non_main_ractor? assert_prism_eval('$pit = ".oo"; if /"#{$pit}"/mix; end') end def test_InterpolatedRegularExpressionNode + omit "global variables access" if non_main_ractor? assert_prism_eval('$pit = 1; /1 #$pit 1/') assert_prism_eval('$pit = 1; /#$pit/i') assert_prism_eval('/1 #{1 + 2} 1/') @@ -601,6 +623,7 @@ def test_InterpolatedRegularExpressionNode end def test_InterpolatedStringNode + omit "global variables access" if non_main_ractor? assert_prism_eval('$pit = 1; "1 #$pit 1"') assert_prism_eval('"1 #{1 + 2} 1"') assert_prism_eval('"Prism" "::" "TestCompilePrism"') @@ -636,6 +659,7 @@ def test_concatenated_StringNode end def test_InterpolatedSymbolNode + omit "global variable access" if non_main_ractor? assert_prism_eval('$pit = 1; :"1 #$pit 1"') assert_prism_eval(':"1 #{1 + 2} 1"') end @@ -992,8 +1016,10 @@ def test_IfNode assert_prism_eval('if "a"..; end') assert_prism_eval('if .."b"; end') assert_prism_eval('if ..1; end') - assert_prism_eval('if 1..; end') - assert_prism_eval('if 1..2; end') + if main_ractor? + assert_prism_eval('if 1..; end') # accesses $. implicitly + assert_prism_eval('if 1..2; end') + end assert_prism_eval('if true or true; end'); end @@ -1047,8 +1073,10 @@ def o.bar = @ret.length < 3 def test_ForNode assert_prism_eval("for i in [1,2] do; i; end") - assert_prism_eval("for @i in [1,2] do; @i; end") - assert_prism_eval("for $i in [1,2] do; $i; end") + assert_prism_eval("for @i in [1,2] do; @i; end", raw: non_main_ractor?) + if main_ractor? + assert_prism_eval("for $i in [1,2] do; $i; end") + end assert_prism_eval("for foo, in [1,2,3] do end") @@ -1207,9 +1235,9 @@ def self.prism_test_ensure_node # Bug #21001 assert_prism_eval(<<~RUBY) - RUN_ARRAY = [1,2] + RUN_ARRAY = [1,2].freeze - MAP_PROC = Proc.new do |&blk| + MAP_PROC = Ractor.make_shareable(Proc.new do |&blk| block_results = [] RUN_ARRAY.each do |value| block_value = blk.call(value) @@ -1218,7 +1246,7 @@ def self.prism_test_ensure_node block_results ensure next block_results - end + end) MAP_PROC.call do |value| break if value > 1 @@ -1848,8 +1876,10 @@ def test_PreExecutionNode def test_PostExecutionNode assert_prism_eval("END { 1 }") - assert_prism_eval("END { @b }; @b = 1") - assert_prism_eval("END { @b; 0 }; @b = 1") + if main_ractor? + assert_prism_eval("END { @b }; @b = 1") + assert_prism_eval("END { @b; 0 }; @b = 1") + end assert_prism_eval("foo = 1; END { foo.nil? }") assert_prism_eval("foo = 1; END { END { foo.nil? }}") end @@ -2270,6 +2300,7 @@ def test_SuperNode ############################################################################ def test_AliasGlobalVariableNode + omit "global variable access" if non_main_ractor? assert_prism_eval("alias $prism_foo $prism_bar") end @@ -2612,9 +2643,11 @@ def test_PinnedExpressionNode end def test_PinnedVariableNode - assert_prism_eval("module Prism; @@prism = 1; 1 in ^@@prism; end") - assert_prism_eval("module Prism; @prism = 1; 1 in ^@prism; end") - assert_prism_eval("$prism = 1; 1 in ^$prism") + if main_ractor? + assert_prism_eval("module Prism; @@prism = 1; 1 in ^@@prism; end") + assert_prism_eval("module Prism; @prism = 1; 1 in ^@prism; end") + assert_prism_eval("$prism = 1; 1 in ^$prism") + end assert_prism_eval("prism = 1; 1 in ^prism") assert_prism_eval("[1].each { 1 => ^it }") end diff --git a/test/ruby/test_condition.rb b/test/ruby/test_condition.rb index ab0ffc4b6a2559..a6b955fe592340 100644 --- a/test/ruby/test_condition.rb +++ b/test/ruby/test_condition.rb @@ -6,12 +6,12 @@ class TestCondition < Test::Unit::TestCase # [should] first test to see if we can run the tests. def test_condition - $x = '0'; + x = '0'; - $x == $x && assert(true) - $x != $x && assert(false) - $x == $x || assert(false) - $x != $x || assert(true) + x == x && assert(true) + x != x && assert(false) + x == x || assert(false) + x != x || assert(true) end end diff --git a/test/ruby/test_const.rb b/test/ruby/test_const.rb index f6b9ea83d3c02d..f07ffa44455496 100644 --- a/test/ruby/test_const.rb +++ b/test/ruby/test_const.rb @@ -25,6 +25,7 @@ module Const2 end def test_const + omit "not ractor safe" if non_main_ractor? Constants_Setup.call assert defined?(TEST1) diff --git a/test/ruby/test_default_gems.rb b/test/ruby/test_default_gems.rb index b82e304cbdb1c6..d21c8b9c597cda 100644 --- a/test/ruby/test_default_gems.rb +++ b/test/ruby/test_default_gems.rb @@ -15,6 +15,7 @@ def self.load(file) end def test_validate_gemspec + omit "loading gemspecs accesses load path" if non_main_ractor? srcdir = File.expand_path('../../..', __FILE__) specs = 0 Dir.chdir(srcdir) do diff --git a/test/ruby/test_defined.rb b/test/ruby/test_defined.rb index db1fdc8e25a3c5..a0df27ed2324e1 100644 --- a/test/ruby/test_defined.rb +++ b/test/ruby/test_defined.rb @@ -24,6 +24,7 @@ def defined_test end def test_defined_global_variable + omit "global var" if non_main_ractor? $x = nil assert(defined?($x)) # global variable @@ -302,6 +303,7 @@ def b? end def test_autoloaded_noload + omit "accesses load path" if non_main_ractor? loaded = $".dup $".clear loadpath = $:.dup @@ -311,8 +313,8 @@ def test_autoloaded_noload assert_nil(x.b?) assert_equal([], $") ensure - $".replace(loaded) - $:.replace(loadpath) + $".replace(loaded) if loaded + $:.replace(loadpath) if loadpath end def test_exception diff --git a/test/ruby/test_dir_m17n.rb b/test/ruby/test_dir_m17n.rb index cdf8b44ef2705c..af5f517cf5e85e 100644 --- a/test/ruby/test_dir_m17n.rb +++ b/test/ruby/test_dir_m17n.rb @@ -395,6 +395,7 @@ def PP.mu_pp(ary) #:nodoc: end def test_entries_compose + omit "accesses non-shareable object PP" if non_main_ractor? bug7267 = '[ruby-core:48745] [Bug #7267]' with_tmpdir {|d| @@ -410,6 +411,7 @@ def test_entries_compose end def test_pwd + omit "accesses non-shareable object PP" if non_main_ractor? orig = %W"d\u{e9}tente x\u{304c 304e 3050 3052 3054}" expected = [] results = [] diff --git a/test/ruby/test_env.rb b/test/ruby/test_env.rb index 2727620c198522..06a8b544add704 100644 --- a/test/ruby/test_env.rb +++ b/test/ruby/test_env.rb @@ -5,12 +5,12 @@ class TestEnv < Test::Unit::TestCase windows = /bccwin|mswin|mingw/ =~ RUBY_PLATFORM IGNORE_CASE = windows ENCODING = windows ? Encoding::UTF_8 : Encoding.find("locale") - PATH_ENV = "PATH" + PATH_ENV = "PATH".freeze INVALID_ENVVARS = [ - "foo\0bar", - "\xa1\xa1".force_encoding(Encoding::UTF_16LE), - "foo".force_encoding(Encoding::ISO_2022_JP), - ] + "foo\0bar".freeze, + "\xa1\xa1".force_encoding(Encoding::UTF_16LE).freeze, + "foo".force_encoding(Encoding::ISO_2022_JP).freeze, + ].freeze def assert_invalid_env(msg = nil) all_assertions(msg) do |a| @@ -645,7 +645,7 @@ def check(as, bs) end assert_equal(as.sort, bs.sort) end - } + }.freeze def test_bracket_in_ractor assert_ractor(<<-"end;") diff --git a/test/ruby/test_eval.rb b/test/ruby/test_eval.rb index d2145bec5d9f13..5b707679c06d64 100644 --- a/test/ruby/test_eval.rb +++ b/test/ruby/test_eval.rb @@ -28,8 +28,10 @@ def test_eval_basic assert_equal 11, eval("11") @ivar = 12 assert_equal 12, eval("@ivar") - assert_equal 13, eval("@@cvar") - assert_equal 14, eval("$gvar__eval") + if main_ractor? + assert_equal 13, eval("@@cvar") + assert_equal 14, eval("$gvar__eval") + end assert_equal 15, eval("Const") assert_equal 16, eval("7 + 9") @@ -39,8 +41,10 @@ def test_eval_basic 1.times { assert_equal 12, eval("@ivar") - assert_equal 13, eval("@@cvar") - assert_equal 14, eval("$gvar__eval") + if main_ractor? + assert_equal 13, eval("@@cvar") + assert_equal 14, eval("$gvar__eval") + end assert_equal 15, eval("Const") } end @@ -56,8 +60,10 @@ def test_eval_binding_basic assert_equal 11, eval("11", binding()) @ivar = 12 assert_equal 12, eval("@ivar", binding()) - assert_equal 13, eval("@@cvar", binding()) - assert_equal 14, eval("$gvar__eval", binding()) + if main_ractor? + assert_equal 13, eval("@@cvar", binding()) + assert_equal 14, eval("$gvar__eval", binding()) + end assert_equal 15, eval("Const", binding()) assert_equal 16, eval("7 + 9", binding()) @@ -67,8 +73,10 @@ def test_eval_binding_basic 1.times { assert_equal 12, eval("@ivar") - assert_equal 13, eval("@@cvar") - assert_equal 14, eval("$gvar__eval") + if main_ractor? + assert_equal 13, eval("@@cvar") + assert_equal 14, eval("$gvar__eval") + end assert_equal 15, eval("Const") } end @@ -83,8 +91,10 @@ def test_module_eval_string_basic assert_equal 11, c.module_eval("11") @ivar = 12 assert_equal 12, c.module_eval("@ivar") - assert_equal 13, c.module_eval("@@cvar") - assert_equal 14, c.module_eval("$gvar__eval") + if main_ractor? + assert_equal 13, c.module_eval("@@cvar") + assert_equal 14, c.module_eval("$gvar__eval") + end assert_equal 15, c.module_eval("Const") assert_equal 16, c.module_eval("7 + 9") assert_equal 17, c.module_eval("17.to_i") @@ -94,8 +104,10 @@ def test_module_eval_string_basic @ivar = 12 1.times { assert_equal 12, c.module_eval("@ivar") - assert_equal 13, c.module_eval("@@cvar") - assert_equal 14, c.module_eval("$gvar__eval") + if main_ractor? + assert_equal 13, c.module_eval("@@cvar") + assert_equal 14, c.module_eval("$gvar__eval") + end assert_equal 15, c.module_eval("Const") } end @@ -110,8 +122,10 @@ def test_module_eval_block_basic assert_equal 11, c.module_eval { 11 } @ivar = 12 assert_equal 12, c.module_eval { @ivar } - assert_equal 13, c.module_eval { @@cvar } - assert_equal 14, c.module_eval { $gvar__eval } + if main_ractor? + assert_equal 13, c.module_eval { @@cvar } + assert_equal 14, c.module_eval { $gvar__eval } + end assert_equal 15, c.module_eval { Const } assert_equal 16, c.module_eval { 7 + 9 } assert_equal 17, c.module_eval { "17".to_i } @@ -121,8 +135,10 @@ def test_module_eval_block_basic @ivar = 12 1.times { assert_equal 12, c.module_eval { @ivar } - assert_equal 13, c.module_eval { @@cvar } - assert_equal 14, c.module_eval { $gvar__eval } + if main_ractor? + assert_equal 13, c.module_eval { @@cvar } + assert_equal 14, c.module_eval { $gvar__eval } + end assert_equal 15, c.module_eval { Const } } end @@ -150,8 +166,10 @@ def test_instance_eval_string_basic assert_equal 11, o.instance_eval("11") assert_equal 12, o.instance_eval("@ivar") unless o.frozen? - assert_equal 13, o.instance_eval("@@cvar") - assert_equal 14, o.instance_eval("$gvar__eval") + if main_ractor? + assert_equal 13, o.instance_eval("@@cvar") + assert_equal 14, o.instance_eval("$gvar__eval") + end assert_equal 15, o.instance_eval("Const") assert_equal 16, o.instance_eval("7 + 9") assert_equal 17, o.instance_eval("17.to_i") @@ -160,8 +178,10 @@ def test_instance_eval_string_basic 1.times { assert_equal 12, o.instance_eval("@ivar") unless o.frozen? - assert_equal 13, o.instance_eval("@@cvar") - assert_equal 14, o.instance_eval("$gvar__eval") + if main_ractor? + assert_equal 13, o.instance_eval("@@cvar") + assert_equal 14, o.instance_eval("$gvar__eval") + end assert_equal 15, o.instance_eval("Const") } end @@ -178,8 +198,10 @@ def test_instance_eval_block_basic assert_equal 11, o.instance_eval { 11 } assert_equal 12, o.instance_eval { @ivar } unless o.frozen? - assert_equal 13, o.instance_eval { @@cvar } - assert_equal 14, o.instance_eval { $gvar__eval } + if main_ractor? + assert_equal 13, o.instance_eval { @@cvar } + assert_equal 14, o.instance_eval { $gvar__eval } + end assert_equal 15, o.instance_eval { Const } assert_equal 16, o.instance_eval { 7 + 9 } assert_equal 17, o.instance_eval { 17.to_i } @@ -188,8 +210,10 @@ def test_instance_eval_block_basic 1.times { assert_equal 12, o.instance_eval { @ivar } unless o.frozen? - assert_equal 13, o.instance_eval { @@cvar } - assert_equal 14, o.instance_eval { $gvar__eval } + if main_ractor? + assert_equal 13, o.instance_eval { @@cvar } + assert_equal 14, o.instance_eval { $gvar__eval } + end assert_equal 15, o.instance_eval { Const } } end @@ -211,6 +235,7 @@ def test_instance_eval_block_symbol end def test_instance_eval_cvar + omit "class variable access" if non_main_ractor? [Object.new, [], 7, :sym, true, false, nil].each do |obj| assert_equal(13, obj.instance_eval("@@cvar")) assert_equal(13, obj.instance_eval{@@cvar}) @@ -220,6 +245,7 @@ def test_instance_eval_cvar end def test_instance_exec_cvar + omit "class variable access" if non_main_ractor? [Object.new, [], 7, :sym, true, false, nil].each do |obj| assert_equal(13, obj.instance_exec{@@cvar}) end @@ -241,6 +267,7 @@ class << o end def test_instance_eval_on_argf_singleton_class + omit "ARGF access" if non_main_ractor? bug8188 = '[ruby-core:53839] [Bug #8188]' assert_warning('', bug8188) do ARGF.singleton_class.instance_eval{} @@ -270,8 +297,10 @@ def test_instance_exec_block_basic assert_equal 11, o.instance_exec { 11 } assert_equal 12, o.instance_exec { @ivar } unless o.frozen? - assert_equal 13, o.instance_exec { @@cvar } - assert_equal 14, o.instance_exec { $gvar__eval } + if main_ractor? + assert_equal 13, o.instance_exec { @@cvar } + assert_equal 14, o.instance_exec { $gvar__eval } + end assert_equal 15, o.instance_exec { Const } assert_equal 16, o.instance_exec { 7 + 9 } assert_equal 17, o.instance_exec { 17.to_i } @@ -280,8 +309,10 @@ def test_instance_exec_block_basic 1.times { assert_equal 12, o.instance_exec { @ivar } unless o.frozen? - assert_equal 13, o.instance_exec { @@cvar } - assert_equal 14, o.instance_exec { $gvar__eval } + if main_ractor? + assert_equal 13, o.instance_exec { @@cvar } + assert_equal 14, o.instance_exec { $gvar__eval } + end assert_equal 15, o.instance_exec { Const } } end @@ -346,14 +377,16 @@ def test_eval_orig assert(!eval('nil')) assert(!eval('false')) - $foo = 'assert(true)' - begin - eval $foo - rescue - assert(false) + if main_ractor? + $foo = 'assert(true)' + begin + eval $foo + rescue + assert(false) + end + assert_equal('assert(true)', eval("$foo")) end - assert_equal('assert(true)', eval("$foo")) assert_equal(true, eval("true")) i = i = 5 assert(eval("i == 5")) @@ -424,6 +457,7 @@ module EvTest end def test_nil_instance_eval_cvar + omit "class variable access" if non_main_ractor? def nil.test_binding binding end @@ -435,10 +469,12 @@ class << nil end def test_fixnum_instance_eval_cvar + omit "class variable access" if non_main_ractor? assert_raise(NameError, "[ruby-dev:24213]") { 1.instance_eval "@@a" } end def test_cvar_scope_with_instance_eval + omit "class variable read/write" if non_main_ractor? # TODO: check Integer.class_eval "@@test_cvar_scope_with_instance_eval = 1" # depends on [ruby-dev:24229] @@test_cvar_scope_with_instance_eval = 4 @@ -473,6 +509,7 @@ def test_define_method_block end def test_define_method_toplevel + omit "access TOPLEVEL_BINDING" if non_main_ractor? feature6609 = '[ruby-core:45715]' main = eval("self", TOPLEVEL_BINDING) assert_nothing_raised(NoMethodError, feature6609) do diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb index 84581180b60958..637a645834195f 100644 --- a/test/ruby/test_exception.rb +++ b/test/ruby/test_exception.rb @@ -665,7 +665,7 @@ def test_backtrace_by_exception assert_equal([__FILE__, line], [loc.path, loc.lineno]) end - Bug4438 = '[ruby-core:35364]' + Bug4438 = '[ruby-core:35364]'.freeze def test_rescue_single_argument assert_raise(TypeError, Bug4438) do @@ -1099,6 +1099,7 @@ def capture_warning_warn(category: false) end def test_warning_warn + omit "global variable access" if non_main_ractor? warning = capture_warning_warn {$asdfasdsda_test_warning_warn} assert_match(/global variable '\$asdfasdsda_test_warning_warn' not initialized/, warning[0]) @@ -1108,6 +1109,7 @@ def test_warning_warn end def test_warn_deprecated_backwards_compatibility_category + omit "accesses global variable" if non_main_ractor? (message, category), = capture_warning_warn(category: true) do $; = "www" $; = nil @@ -1149,6 +1151,7 @@ def test_warning_warn_invalid_argument end def test_warning_warn_circular_require_backtrace + omit "accesses $LOAD_PATH and $LOADED_FEATURES" if non_main_ractor? warning = nil path = nil Tempfile.create(%w[circular .rb]) do |t| diff --git a/test/ruby/test_file.rb b/test/ruby/test_file.rb index a3d6221c0f924b..59a72157711358 100644 --- a/test/ruby/test_file.rb +++ b/test/ruby/test_file.rb @@ -5,7 +5,6 @@ require_relative 'ut_eof' class TestFile < Test::Unit::TestCase - # I don't know Ruby's spec about "unlink-before-close" exactly. # This test asserts current behaviour. def test_unlink_before_close @@ -41,6 +40,7 @@ def open_file(content) include TestEOF::Seek def test_empty_file_bom + pend "Tempfile" unless main_ractor? bug6487 = '[ruby-core:45203]' Tempfile.create(__method__.to_s) {|f| assert_file.exist?(f.path) @@ -50,6 +50,7 @@ def test_empty_file_bom end def assert_bom(bytes, name) + pend "Tempfile" unless main_ractor? bug6487 = '[ruby-core:45203]' Tempfile.create(name.to_s) {|f| @@ -91,6 +92,7 @@ def test_bom_32le end def test_truncate_wbuf + pend "Tempfile" unless main_ractor? Tempfile.create("test-truncate") {|f| f.print "abc" f.truncate(0) @@ -101,6 +103,7 @@ def test_truncate_wbuf end def test_truncate_rbuf + pend "Tempfile" unless main_ractor? Tempfile.create("test-truncate") {|f| f.puts "abc" f.puts "def" @@ -112,6 +115,7 @@ def test_truncate_rbuf end def test_truncate_beyond_eof + pend "Tempfile" unless main_ractor? Tempfile.create("test-truncate") {|f| f.print "abc" f.truncate 10 @@ -120,6 +124,7 @@ def test_truncate_beyond_eof end def test_truncate_size + pend "Tempfile" unless main_ractor? Tempfile.create("test-truncate") do |f| q1 = Thread::Queue.new q2 = Thread::Queue.new @@ -147,6 +152,7 @@ def test_truncate_size end def test_read_all_extended_file + pend "Tempfile" unless main_ractor? [{}, {:textmode=>true}, {:binmode=>true}].each do |mode| Tempfile.create("test-extended-file", **mode) {|f| assert_nil(f.getc) @@ -158,6 +164,7 @@ def test_read_all_extended_file end def test_gets_extended_file + pend "Tempfile" unless main_ractor? [{}, {:textmode=>true}, {:binmode=>true}].each do |mode| Tempfile.create("test-extended-file", **mode) {|f| assert_nil(f.getc) @@ -169,6 +176,7 @@ def test_gets_extended_file end def test_gets_para_extended_file + pend "Tempfile" unless main_ractor? [{}, {:textmode=>true}, {:binmode=>true}].each do |mode| Tempfile.create("test-extended-file", **mode) {|f| assert_nil(f.getc) @@ -193,6 +201,7 @@ def test_each_char_extended_file end def test_each_byte_extended_file + pend "Tempfile" unless main_ractor? [{}, {:textmode=>true}, {:binmode=>true}].each do |mode| Tempfile.create("test-extended-file", **mode) {|f| assert_nil(f.getc) @@ -217,6 +226,7 @@ def test_getc_extended_file end def test_getbyte_extended_file + pend "Tempfile" unless main_ractor? [{}, {:textmode=>true}, {:binmode=>true}].each do |mode| Tempfile.create("test-extended-file", **mode) {|f| assert_nil(f.getc) @@ -233,6 +243,7 @@ def test_s_chown end def test_chown + pend "Tempfile" unless main_ractor? Tempfile.create("test-chown") {|f| assert_nothing_raised {f.chown(-1, -1)} assert_nothing_raised("[ruby-dev:27140]") {f.chown(nil, nil)} @@ -339,6 +350,7 @@ def test_utime_with_minus_time_segv end def test_utime + pend "Tempfile" unless main_ractor? bug6385 = '[ruby-core:44776]' mod_time_contents = Time.at 1306527039 @@ -372,6 +384,7 @@ def measure_time end def test_stat + pend "Tempfile" unless main_ractor? btime = Process.clock_gettime(Process::CLOCK_REALTIME) Tempfile.create("stat") {|file| btime = (btime + Process.clock_gettime(Process::CLOCK_REALTIME)) / 2 diff --git a/test/ruby/test_file_exhaustive.rb b/test/ruby/test_file_exhaustive.rb index b20b597256a125..13b8970c11075b 100644 --- a/test/ruby/test_file_exhaustive.rb +++ b/test/ruby/test_file_exhaustive.rb @@ -7,7 +7,7 @@ class TestFileExhaustive < Test::Unit::TestCase ROOT_REGEXP = %r'\A(?:[a-z]:(?=(/))|//[^/]+/[^/]+)'i - DRIVE = Dir.pwd[ROOT_REGEXP] + DRIVE = Dir.pwd[ROOT_REGEXP].freeze POSIX = /cygwin|mswin|bccwin|mingw|emx/ !~ RUBY_PLATFORM NTFS = !(/mingw|mswin|bccwin/ !~ RUBY_PLATFORM) @@ -874,6 +874,7 @@ def test_expand_path_absolute end def test_expand_path_memsize + pend "ObjectSpace.memsize_of not yet ractor safe" if non_main_ractor? bug9934 = '[ruby-core:63114] [Bug #9934]' require "objspace" path = File.expand_path("/foo") diff --git a/test/ruby/test_flip.rb b/test/ruby/test_flip.rb index 9c58f5f497c0a9..42d063d39659c5 100644 --- a/test/ruby/test_flip.rb +++ b/test/ruby/test_flip.rb @@ -53,6 +53,7 @@ def test_shared_thread end def test_input_line_number_range + omit "Cannot access $." if non_main_ractor? bug12947 = '[ruby-core:78162] [Bug #12947]' ary = b1 = b2 = nil EnvUtil.suppress_warning do diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb index d0d180593ab272..f01a78f66e591d 100644 --- a/test/ruby/test_float.rb +++ b/test/ruby/test_float.rb @@ -657,7 +657,7 @@ def (prec = Object.new).to_int; 2; end -18446744073709551616.8, -18446744073709551617.0, -18446744073709551618.0, - ] + ].freeze def test_truncate VS.each {|f| diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb index 5fc9ea508c7142..fd08332213065b 100644 --- a/test/ruby/test_gc.rb +++ b/test/ruby/test_gc.rb @@ -887,6 +887,7 @@ def test_ast_node_buffer end def test_old_to_young_reference + pend "ObjectSpace.dump not yet ractor safe" if non_main_ractor? EnvUtil.without_gc do require "objspace" diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index dbf041a7321252..c1edb3d10c6e41 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -62,17 +62,18 @@ def test_hash x.default = 5 assert_equal(5, x[23]) + z = nil x = Hash.new - def x.default(k) - $z = k + x.singleton_class.define_method(:default) do |k| + z = k self[k] = k*2 end - $z = 0 + z = 0 assert_equal(44, x[22]) - assert_equal(22, $z) - $z = 0 + assert_equal(22, z) + z = 0 assert_equal(44, x[22]) - assert_equal(0, $z) + assert_equal(0, z) end # From rubicon @@ -859,6 +860,7 @@ def test_to_h_block end def test_to_s + omit "Accesses global variable" if non_main_ractor? h = @cls[ 1 => 2, "cat" => "dog", 1.5 => :fred ] assert_equal(h.inspect, h.to_s) assert_deprecated_warning { $, = ":" } @@ -866,7 +868,7 @@ def test_to_s h = @cls[] assert_equal(h.inspect, h.to_s) ensure - $, = nil + $, = nil if main_ractor? end def test_inspect @@ -2137,6 +2139,7 @@ def hash_iter_recursion(h, level) end def test_iterlevel_in_ivar_bug19589 + pend "Ractor bug!" if non_main_ractor? # NOTE: we get stack level too deep within a ractor h = { a: nil } # Recursion level should be over 127 to actually test iterlevel being set in an instance variable, # but it should be under 131 not to overflow the stack under MN threads/ractors. @@ -2173,6 +2176,7 @@ def hash end def test_memory_size_after_delete + pend "ObjectSpace.memsize_of not yet ractor safe" if non_main_ractor? require 'objspace' h = {} 1000.times {|i| h[i] = true} diff --git a/test/ruby/test_integer_comb.rb b/test/ruby/test_integer_comb.rb index 150f45cfd7ddb7..e293fb3dc1f54f 100644 --- a/test/ruby/test_integer_comb.rb +++ b/test/ruby/test_integer_comb.rb @@ -104,7 +104,7 @@ class TestIntegerComb < Test::Unit::TestCase 0xffffffffffffffffffffffffffffffffffffffffffffffff, 0x1000000000000000000000000000000000000000000000000, 0x1000000000000000000000000000000000000000000000001 - ] + ].freeze #VS.map! {|v| 0x4000000000000000.coerce(v)[0] } #VS.concat VS.find_all {|v| Fixnum === v }.map {|v| 0x4000000000000000.coerce(v)[0] } diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index 3ec8726b5e45ce..49a1b0efd734f8 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -999,6 +999,7 @@ def test_copy_stream_socket6 end def test_copy_stream_socket7 + omit "Signal.trap not ractor safe" if non_main_ractor? if RUBY_PLATFORM =~ /mingw|mswin/ omit "pread(2) is not implemented." end @@ -1014,7 +1015,7 @@ def test_copy_stream_socket7 rescue Errno::EBADF omit "nonblocking IO for pipe is not implemented" end - trapping_usr2 do |rd| + trapping_usr2 do |rd| # not ractor safe nr = 30 begin pid = fork do @@ -1116,6 +1117,7 @@ def test_copy_stream_strio_to_io end def test_copy_stream_strio_to_tempfile + pend "Tempfile" if non_main_ractor? bug11015 = '[ruby-core:68676] [Bug #11015]' # StringIO to Tempfile src = StringIO.new("abcd") @@ -1127,7 +1129,7 @@ def test_copy_stream_strio_to_tempfile assert_equal("abcd", dst.read) assert_equal(4, pos, bug11015) ensure - dst.close! + dst&.close! end def test_copy_stream_pathname_to_pathname @@ -1142,6 +1144,7 @@ def test_copy_stream_pathname_to_pathname end def test_copy_stream_dup_buffer + pend "Tempfile" if non_main_ractor? bug21131 = '[ruby-core:120961] [Bug #21131]' mkcdtmpdir do dst_class = Class.new do @@ -1391,6 +1394,7 @@ def ruby(*args) end def test_try_convert + omit "non-shareable value" if non_main_ractor? assert_equal(STDOUT, IO.try_convert(STDOUT)) assert_equal(nil, IO.try_convert("STDOUT")) end @@ -1898,6 +1902,7 @@ def test_pid_after_close_read end def make_tempfile + pend "Tempfile" if non_main_ractor? t = Tempfile.new("test_io") t.binmode t.puts "foo" @@ -1936,6 +1941,7 @@ def test_set_lineno end def test_set_lineno_gets + omit "global variable access" if non_main_ractor? pipe(proc do |w| w.puts "foo" w.puts "bar" @@ -1986,6 +1992,7 @@ def test_readline_separators end def test_readline_separators_limits + pend "Tempfile" if non_main_ractor? t = Tempfile.open("readline_limit") str = "#" * 50 sep = "def" @@ -2017,6 +2024,7 @@ def test_readline_separators_limits end def test_readline_limit_without_separator + pend "Tempfile" if non_main_ractor? t = Tempfile.open("readline_limit") str = "#" * 50 sep = "\n" @@ -2093,6 +2101,7 @@ def test_readline_limit_nonascii end def test_set_lineno_readline + omit "global variable access" if non_main_ractor? pipe(proc do |w| w.puts "foo" w.puts "bar" @@ -2541,6 +2550,7 @@ def test_autoclose end def test_autoclose_true_closed_by_finalizer + pend "Tempfile" if non_main_ractor? feature2250 = '[ruby-core:26222]' pre = 'ft2250' t = Tempfile.new(pre) @@ -2560,6 +2570,7 @@ def test_autoclose_true_closed_by_finalizer end def test_autoclose_false_closed_by_finalizer + pend "Tempfile" if non_main_ractor? feature2250 = '[ruby-core:26222]' pre = 'ft2250' t = Tempfile.new(pre) @@ -2572,7 +2583,7 @@ def test_autoclose_false_closed_by_finalizer assert_nothing_raised(Errno::EBADF, feature2250) {t.close} end ensure - t.close! + t&.close! end def test_open_redirect @@ -2805,22 +2816,25 @@ def test_reopen_opt_encoding bug11320 = '[ruby-core:69780] [Bug #11320]' ["UTF-8", "EUC-JP", "Shift_JIS"].each do |enc| - define_method("test_reopen_nonascii(#{enc})") do - mkcdtmpdir do - fname = "\u{30eb 30d3 30fc}".encode(enc) - File.write(fname, '') - assert_file.exist?(fname) - stdin = $stdin.dup - begin - assert_nothing_raised(Errno::ENOENT, "#{bug11320}: #{enc}") { - $stdin.reopen(fname, 'r') - } - ensure - $stdin.reopen(stdin) - stdin.close + class_eval <<-RUBY + def test_reopen_nonascii_#{enc.sub('-', '_')} + mkcdtmpdir do + enc = #{enc.inspect} + fname = "#{'\u{30eb 30d3 30fc}'}".encode(enc) + File.write(fname, '') + assert_file.exist?(fname) + stdin = $stdin.dup + begin + assert_nothing_raised(Errno::ENOENT, "#{bug11320}: #{enc}") { + $stdin.reopen(fname, 'r') + } + ensure + $stdin.reopen(stdin) + stdin.close + end end end - end + RUBY end def test_reopen_ivar @@ -2923,6 +2937,7 @@ def test_print end def test_print_separators + omit "global variable access" if non_main_ractor? EnvUtil.suppress_warning { $, = ':' $\ = "\n" @@ -2938,8 +2953,10 @@ def test_print_separators r.close end) ensure - $, = nil - $\ = nil + if main_ractor? + $, = nil + $\ = nil + end end def test_putc @@ -3139,6 +3156,7 @@ def test_DATA_binmode end def test_threaded_flush + omit "separate process" if non_main_ractor? bug3585 = '[ruby-core:31348]' src = "#{<<~"begin;"}\n#{<<~'end;'}" begin; @@ -3156,6 +3174,7 @@ def test_threaded_flush end def test_flush_in_finalizer1 + pend "Tempfile" if non_main_ractor? bug3910 = '[ruby-dev:42341]' tmp = Tempfile.open("bug3910") {|t| path = t.path @@ -3172,15 +3191,18 @@ def test_flush_in_finalizer1 t } ensure - ObjectSpace.each_object(File) {|f| - if f.instance_variables.include?(:@test_flush_in_finalizer1) - f.close - end - } - tmp.close! + if main_ractor? + ObjectSpace.each_object(File) {|f| + if f.instance_variables.include?(:@test_flush_in_finalizer1) + f.close + end + } + tmp.close! + end end def test_flush_in_finalizer2 + pend "Tempfile" if non_main_ractor? bug3910 = '[ruby-dev:42341]' Tempfile.create("bug3910") {|t| path = t.path @@ -3313,6 +3335,7 @@ def test_invalid_advise end def test_fcntl_lock_linux + pend "Tempfile" if non_main_ractor? pad = 0 Tempfile.create(self.class.name) do |f| r, w = IO.pipe @@ -3345,6 +3368,7 @@ def test_fcntl_lock_linux [nil].pack("p").bytesize == 8 # unless x32 platform. def test_fcntl_lock_freebsd + pend "Tempfile" if non_main_ractor? start = 12 len = 34 sysid = 0 @@ -3378,6 +3402,7 @@ def test_fcntl_lock_freebsd end if /freebsd/ =~ RUBY_PLATFORM # A binary form of struct flock depend on platform def test_fcntl_dupfd + pend "Tempfile" if non_main_ractor? Tempfile.create(self.class.name) do |f| fd = f.fcntl(Fcntl::F_DUPFD, 63) begin @@ -3529,6 +3554,7 @@ def test_s_binwrite end def test_race_between_read + pend "Tempfile" if non_main_ractor? Tempfile.create("test") {|file| begin path = file.path @@ -3611,7 +3637,7 @@ def test_ioctl_linux end if /^(?:i.?86|x86_64)-linux/ =~ RUBY_PLATFORM def test_ioctl_linux2 - return unless STDIN.tty? # stdin is not a terminal + return unless $stdin.tty? # stdin is not a terminal begin f = File.open('/dev/tty') rescue Errno::ENOENT, Errno::ENXIO => e @@ -3668,9 +3694,11 @@ def test_setpos end def test_std_fileno - assert_equal(0, STDIN.fileno) - assert_equal(1, STDOUT.fileno) - assert_equal(2, STDERR.fileno) + if main_ractor? + assert_equal(0, STDIN.fileno) + assert_equal(1, STDOUT.fileno) + assert_equal(2, STDERR.fileno) + end assert_equal(0, $stdin.fileno) assert_equal(1, $stdout.fileno) assert_equal(2, $stderr.fileno) @@ -3750,6 +3778,7 @@ def test_advise_pipe end if /linux/ =~ RUBY_PLATFORM def assert_buffer_not_raise_shared_string_error + pend "Tempfile" if non_main_ractor? bug6764 = '[ruby-core:46586]' bug9847 = '[ruby-core:62643] [Bug #9847]' size = 28 @@ -3768,7 +3797,7 @@ def assert_buffer_not_raise_shared_string_error end assert_equal(data, w.join(""), bug9847) ensure - t.close! + t&.close! end def test_read_buffer_not_raise_shared_string_error diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb index 62c46678882a10..e3cd5be46ba47d 100644 --- a/test/ruby/test_io_buffer.rb +++ b/test/ruby/test_io_buffer.rb @@ -334,7 +334,7 @@ def test_zero_length_get_string end # We check that values are correctly round tripped. - RANGES = { + Ractor.make_shareable(RANGES = { :U8 => [0, 2**8-1], :S8 => [-2**7, 0, 2**7-1], @@ -355,7 +355,7 @@ def test_zero_length_get_string :F32 => [-1.0, 0.0, 0.5, 1.0, 128.0], :F64 => [-1.0, 0.0, 0.5, 1.0, 128.0], - } + }) def test_get_set_value buffer = IO::Buffer.new(128) @@ -461,6 +461,7 @@ def test_invalidation end def hello_world_tempfile(repeats = 1) + pend "Tempfile" if non_main_ractor? io = Tempfile.new repeats.times do io.write("Hello World") @@ -506,6 +507,7 @@ def test_read_with_length_and_offset end def test_write + pend "Tempfile" if non_main_ractor? io = Tempfile.new buffer = IO::Buffer.new(128) @@ -515,10 +517,11 @@ def test_write io.seek(0) assert_equal "Hello", io.read(5) ensure - io.close! + io&.close! end def test_write_with_length_and_offset + pend "Tempfile" if non_main_ractor? io = Tempfile.new buffer = IO::Buffer.new(5) @@ -528,10 +531,11 @@ def test_write_with_length_and_offset io.seek(0) assert_equal "ello", io.read(4) ensure - io.close! + io&.close! end def test_pread + pend "Tempfile" if non_main_ractor? io = Tempfile.new io.write("Hello World") io.seek(0) @@ -542,10 +546,11 @@ def test_pread assert_equal "World", buffer.get_string(0, 5) assert_equal 0, io.tell ensure - io.close! + io&.close! end def test_pread_offset + pend "Tempfile" if non_main_ractor? io = Tempfile.new io.write("Hello World") io.seek(0) @@ -556,10 +561,11 @@ def test_pread_offset assert_equal "World", buffer.get_string(6, 5) assert_equal 0, io.tell ensure - io.close! + io&.close! end def test_pwrite + pend "Tempfile" if non_main_ractor? io = Tempfile.new buffer = IO::Buffer.new(128) @@ -571,10 +577,11 @@ def test_pwrite io.seek(6) assert_equal "World", io.read(5) ensure - io.close! + io&.close! end def test_pwrite_offset + pend "Tempfile" if non_main_ractor? io = Tempfile.new buffer = IO::Buffer.new(128) @@ -586,7 +593,7 @@ def test_pwrite_offset io.seek(6) assert_equal "World", io.read(5) ensure - io.close! + io&.close! end def test_operators @@ -625,6 +632,7 @@ def test_shared end def test_private + pend "Tempfile" if non_main_ractor? Tempfile.create(%w"buffer .txt") do |file| file.write("Hello World") diff --git a/test/ruby/test_io_m17n.rb b/test/ruby/test_io_m17n.rb index b01d627d920d7e..45f8f687b76fa6 100644 --- a/test/ruby/test_io_m17n.rb +++ b/test/ruby/test_io_m17n.rb @@ -11,7 +11,7 @@ class TestIO_M17N < Test::Unit::TestCase Encoding::EUC_JP, Encoding::Shift_JIS, Encoding::UTF_8 - ] + ].freeze def with_tmpdir Dir.mktmpdir {|dir| @@ -404,18 +404,18 @@ def test_dup_undef end def test_stdin - assert_equal(Encoding.default_external, STDIN.external_encoding) - assert_equal(nil, STDIN.internal_encoding) + assert_equal(Encoding.default_external, $stdin.external_encoding) + assert_equal(nil, $stdin.internal_encoding) end def test_stdout - assert_equal(nil, STDOUT.external_encoding) - assert_equal(nil, STDOUT.internal_encoding) + assert_equal(nil, $stdout.external_encoding) + assert_equal(nil, $stdout.internal_encoding) end def test_stderr - assert_equal(nil, STDERR.external_encoding) - assert_equal(nil, STDERR.internal_encoding) + assert_equal(nil, $stderr.external_encoding) + assert_equal(nil, $stderr.internal_encoding) end def test_terminator_conversion @@ -464,6 +464,7 @@ def test_nonascii_terminator end def test_pipe_terminator_conversion + pend "Timeout" if non_main_ractor? rs = "\xA2\xA2".encode("utf-8", "euc-jp") pipe("euc-jp:utf-8", proc do |w| @@ -1657,18 +1658,17 @@ def test_textmode_decode_universal_newline_utf16 } end - SYSTEM_NEWLINE = [] def system_newline - return SYSTEM_NEWLINE.first if !SYSTEM_NEWLINE.empty? + return @_system_newline if defined?(@_system_newline) with_tmpdir { open("newline", "wt") {|f| f.print "\n" } open("newline", "rb") {|f| - SYSTEM_NEWLINE << f.read + @_system_newline = f.read } } - SYSTEM_NEWLINE.first + @_system_newline end def test_textmode_encode_newline @@ -2168,31 +2168,34 @@ def test_w_xml_attr end %w/UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE/.each do |name| - define_method("test_strip_bom:#{name}") do - path = "#{name}-bom.txt" - with_tmpdir { - text = "\uFEFF\u0100a" - stripped = "\u0100a" - content = text.encode(name) - generate_file(path, content) - result = File.read(path, mode: 'rb:BOM|UTF-8') - assert_equal(Encoding.find(name), result.encoding, name) - assert_equal(content[1..-1].b, result.b, name) - %w[rb rt r].each do |mode| - message = "#{name}, mode: #{mode.dump}" - result = File.read(path, mode: "#{mode}:BOM|UTF-8:UTF-8") - assert_equal(Encoding::UTF_8, result.encoding, message) - assert_equal(stripped, result, message) - end + class_eval <<-RUBY + def test_strip_bom_#{name.sub('-', '_')} + name = #{name.inspect} + path = "#{name}-bom.txt" + with_tmpdir { + text = "#{'\uFEFF\u0100a'}" + stripped = "#{'\u0100a'}" + content = text.encode(name) + generate_file(path, content) + result = File.read(path, mode: 'rb:BOM|UTF-8') + assert_equal(Encoding.find(name), result.encoding, name) + assert_equal(content[1..-1].b, result.b, name) + %w[rb rt r].each do |mode| + message = #{name.inspect} + ", mode: \#{mode.dump}" + result = File.read(path, mode: "\#{mode}:BOM|UTF-8:UTF-8") + assert_equal(Encoding::UTF_8, result.encoding, message) + assert_equal(stripped, result, message) + end - File.open(path, "rb") {|f| - assert_equal(Encoding.find(name), f.set_encoding_by_bom) - } - File.open(path, "rb", encoding: "iso-8859-1") {|f| - assert_raise(ArgumentError) {f.set_encoding_by_bom} + File.open(path, "rb") {|f| + assert_equal(Encoding.find(name), f.set_encoding_by_bom) + } + File.open(path, "rb", encoding: "iso-8859-1") {|f| + assert_raise(ArgumentError) {f.set_encoding_by_bom} + } } - } - end + end + RUBY end def test_strip_bom_no_conv @@ -2794,6 +2797,7 @@ def test_read_with_buf_broken_ascii_only end def test_each_codepoint_need_more + pend "Tempfile" if non_main_ractor? bug11444 = '[ruby-core:70379] [Bug #11444]' tests = [ ["incomplete multibyte", "\u{1f376}".b[0,3], [], ["invalid byte sequence in UTF-8"]], diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb index 45223c89da5927..3c265f183b64ad 100644 --- a/test/ruby/test_iseq.rb +++ b/test/ruby/test_iseq.rb @@ -348,6 +348,7 @@ def initialize_dup(_) end def test_frozen_string_literal_compile_option + omit "global variable access" if non_main_ractor? $f = 'f' line = __LINE__ + 2 code = <<-'EOS' @@ -693,8 +694,8 @@ def test_to_binary_line_info class P def p; end def q; end - E = "" - N = "#{E}" + E = "".freeze + N = "#{E}".freeze attr_reader :i end end; diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb index 4563308fa2ec97..7d951ef9e534c2 100644 --- a/test/ruby/test_keyword.rb +++ b/test/ruby/test_keyword.rb @@ -42,7 +42,7 @@ def test_f3 end - define_method(:f4) {|str: "foo", num: 424242| [str, num] } + define_method(:f4, &Ractor.make_shareable(proc {|str: "foo", num: 424242| [str, num] })) def test_f4 assert_equal(["foo", 424242], f4) @@ -54,7 +54,7 @@ def test_f4 end - define_method(:f5) {|str: "foo", num: 424242, **h| [str, num, h] } + define_method(:f5, &Ractor.make_shareable(proc {|str: "foo", num: 424242, **h| [str, num, h] })) def test_f5 assert_equal(["foo", 424242, {}], f5) @@ -98,9 +98,9 @@ def test_f7 # [ruby-core:41772] assert_equal([[1, 2, 3], "bar", 424242, {}], f7(1, 2, 3, str: "bar")) end - define_method(:f8) { |opt = :ion, *rest, key: :word| + define_method(:f8, &Ractor.make_shareable(proc { |opt = :ion, *rest, key: :word| [opt, rest, key] - } + })) def test_f8 assert_equal([:ion, [], :word], f8) diff --git a/test/ruby/test_literal.rb b/test/ruby/test_literal.rb index dbff3c4734275d..15de867ea1d9ca 100644 --- a/test/ruby/test_literal.rb +++ b/test/ruby/test_literal.rb @@ -537,7 +537,7 @@ def test_hash_key_tampering assert_equal(100, h['a']) end - FOO = "foo" + FOO = "foo".freeze def test_hash_value_omission x = 1 diff --git a/test/ruby/test_m17n_comb.rb b/test/ruby/test_m17n_comb.rb index e48a1948beaa1c..2a554457874b0f 100644 --- a/test/ruby/test_m17n_comb.rb +++ b/test/ruby/test_m17n_comb.rb @@ -25,7 +25,7 @@ def assert_strenc(bytes, enc, actual, message=nil) assert_equal(b(bytes), b(actual), message) end - STRINGS = [ + STRINGS = Ractor.make_shareable([ b(""), e(""), s(""), u(""), b("a"), e("a"), s("a"), u("a"), b("."), e("."), s("."), u("."), @@ -53,13 +53,13 @@ def assert_strenc(bytes, enc, actual, message=nil) # for transitivity test u("\xe0\xa0\xa1"), e("\xe0\xa0\xa1"), s("\xe0\xa0\xa1"), # [ruby-dev:32693] e("\xa1\xa1"), b("\xa1\xa1"), s("\xa1\xa1"), # [ruby-dev:36484] - ] + ]) - WSTRINGS = [ + WSTRINGS = Ractor.make_shareable([ "aa".force_encoding("utf-16be"), "aaaa".force_encoding("utf-32be"), "aaa".force_encoding("utf-32be"), - ] + ]) def combination(*args, &b) AllPairs.each(*args, &b) @@ -177,12 +177,12 @@ def each_slice_call } end - ASCII_INCOMPATIBLE_ENCODINGS = %w[ + ASCII_INCOMPATIBLE_ENCODINGS = Ractor.make_shareable(%w[ UTF-16BE UTF-16LE UTF-32BE UTF-32LE - ] + ]) def str_enc_compatible?(*strs) encs = [] ascii_incompatible_encodings = {} diff --git a/test/ruby/test_marshal.rb b/test/ruby/test_marshal.rb index eb669948017d98..6c79913d76dcb9 100644 --- a/test/ruby/test_marshal.rb +++ b/test/ruby/test_marshal.rb @@ -334,11 +334,11 @@ def test_regexp2 class DumpTest def marshal_dump - @@block.call(:marshal_dump) + @block.call(:marshal_dump) end def dump_each(&block) - @@block = block + @block = block Marshal.dump(self) end end @@ -357,6 +357,7 @@ def self.load_each(m, &block) end def test_context_switch + omit "access class variables" if non_main_ractor? o = DumpTest.new e = o.enum_for(:dump_each) assert_equal(:marshal_dump, e.next) @@ -404,14 +405,14 @@ def test_marshal_dump class C6 def initialize - @stdin = STDIN + @stdin = $stdin end attr_reader :stdin def marshal_dump 1 end def marshal_load(x) - @stdin = STDIN + @stdin = $stdin end end def test_marshal_dump_extra_iv @@ -421,7 +422,7 @@ def test_marshal_dump_extra_iv m = Marshal.dump(o) } o2 = Marshal.load(m) - assert_equal(STDIN, o2.stdin) + assert_equal($stdin, o2.stdin) end def test_marshal_string_encoding diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb index 8561f841a8cda6..4ceb5c50bc50bb 100644 --- a/test/ruby/test_method.rb +++ b/test/ruby/test_method.rb @@ -347,9 +347,10 @@ def test_define_singleton_method PUBLIC_SINGLETON_TEST = Object.new class << PUBLIC_SINGLETON_TEST private - PUBLIC_SINGLETON_TEST.define_singleton_method(:dsm){} + PUBLIC_SINGLETON_TEST.define_singleton_method(:dsm, &Ractor.make_shareable(proc{})) def PUBLIC_SINGLETON_TEST.def; end end + PUBLIC_SINGLETON_TEST.freeze def test_define_singleton_method_public assert_nil(PUBLIC_SINGLETON_TEST.dsm) assert_nil(PUBLIC_SINGLETON_TEST.def) @@ -435,6 +436,7 @@ def test_define_method_visibility end def test_define_method_in_private_scope + omit "TOPLEVEL_BINDING" if non_main_ractor? bug9005 = '[ruby-core:57747] [Bug #9005]' c = Class.new class << c @@ -446,6 +448,7 @@ class << c end def test_singleton_define_method_in_private_scope + omit "TOPLEVEL_BINDING" if non_main_ractor? bug9141 = '[ruby-core:58497] [Bug #9141]' o = Object.new class << o @@ -998,7 +1001,7 @@ def self.baz = :baz assert_raise(NameError) {c2.singleton_method(:quux)} end - Feature9783 = '[ruby-core:62212] [Feature #9783]' + Feature9783 = '[ruby-core:62212] [Feature #9783]'.freeze def assert_curry_three_args(m) curried = m.curry @@ -1049,7 +1052,7 @@ def test_curry_from_proc_var_args assert_curry_var_args(c.new.method(:var_args)) end - Feature9781 = '[ruby-core:62202] [Feature #9781]' + Feature9781 = '[ruby-core:62202] [Feature #9781]'.freeze def test_super_method o = Derived.new @@ -1440,7 +1443,7 @@ def test_splat_long_array end class C - D = "Const_D" + D = "Const_D".freeze def foo a = b = c = a = b = c = 12345 end @@ -1562,7 +1565,7 @@ def test_method_binding assert_equal(987, b.local_variable_get(:x)) end - MethodInMethodClass_Setup = -> do + MethodInMethodClass_Setup = Ractor.make_shareable(proc do remove_const :MethodInMethodClass if defined? MethodInMethodClass class MethodInMethodClass @@ -1573,7 +1576,7 @@ def m2 end private end - end + end) def test_method_in_method_visibility_should_be_public MethodInMethodClass_Setup.call @@ -1729,6 +1732,7 @@ def test_umethod_bind_call end def test_method_list + pend "ObjectSpace.each_object doesn't work with ractors right now" if non_main_ractor? # chkbuild lists all methods. # The following code emulate this listing. diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index bc8583b475acfa..4518bf3b6c464c 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -87,13 +87,13 @@ def user3 private :user3 end - OtherSetup = -> do + OtherSetup = Ractor.make_shareable(proc do remove_const :Other if defined? ::TestModule::Other module Other def other end end - end + end) class AClass def AClass.cm1 @@ -225,6 +225,7 @@ def test_ancestors @@class_eval = 'b' def test_class_eval + omit "class variable access" if non_main_ractor? OtherSetup.call Other.class_eval("CLASS_EVAL = 1") @@ -383,6 +384,7 @@ def test_nested_defined_bad_class end def test_const_set + omit "class ivars" if non_main_ractor? OtherSetup.call assert_not_operator(Other, :const_defined?, :KOALA) @@ -426,6 +428,7 @@ def test_initialize_copy end def test_initialize_copy_empty + omit "module instance variable access" if non_main_ractor? m = Module.new do def x end @@ -471,6 +474,7 @@ def initialize(key:) end def test_module_subclass_initialize + omit "module instance variable access" if non_main_ractor? mod = Bug18185.new c = Class.new(Bug18185::Foo) do include mod @@ -1164,6 +1168,7 @@ module M2 end def test_s_nesting + omit "global variable access" if non_main_ractor? assert_equal([], $m0) assert_equal([TestModule::M1, TestModule], $m1) assert_equal([TestModule::M1::M2, @@ -1195,6 +1200,7 @@ def foo; end end def test_attr_obsoleted_flag + omit "class ivars" if non_main_ractor? c = Class.new do extend Test::Unit::Assertions extend Test::Unit::CoreAssertions @@ -1217,6 +1223,7 @@ def initialize end def test_attr_public_at_toplevel + omit "TOPLEVEL_BINDING" if non_main_ractor? s = Object.new TOPLEVEL_BINDING.eval(<<-END).call(s.singleton_class) proc do |c| @@ -1366,6 +1373,7 @@ module X; end end def test_class_variable_get + omit "class variables" if non_main_ractor? c = Class.new c.class_eval('@@foo = :foo') assert_equal(:foo, c.class_variable_get(:@@foo)) @@ -1384,6 +1392,7 @@ def n.count; @count; end end def test_class_variable_set + omit "class variables" if non_main_ractor? c = Class.new c.class_variable_set(:@@foo, :foo) assert_equal(:foo, c.class_eval('@@foo')) @@ -1402,6 +1411,7 @@ def n.count; @count; end end def test_class_variable_defined + omit "class variables" if non_main_ractor? c = Class.new c.class_eval('@@foo = :foo') assert_equal(true, c.class_variable_defined?(:@@foo)) @@ -1419,6 +1429,7 @@ def n.count; @count; end end def test_remove_class_variable + omit "class variables" if non_main_ractor? c = Class.new c.class_eval('@@foo = :foo') c.class_eval { remove_class_variable(:@@foo) } @@ -1757,6 +1768,7 @@ def test_nonascii_name def test_const_added + omit "class ivars" if non_main_ractor? eval(<<~RUBY) module TestConstAdded @memo = [] @@ -1996,6 +2008,7 @@ def foo end def test_uninitialized_toplevel_constant + omit "TOPLEVEL_BINDING" if non_main_ractor? bug3123 = '[ruby-dev:40951]' e = assert_raise(NameError) {eval("Bug3123", TOPLEVEL_BINDING)} assert_not_match(/Object::/, e.message, bug3123) @@ -2024,7 +2037,7 @@ def test_attr_writer_with_no_arguments def test_private_constant_in_class c = Class.new - c.const_set(:FOO, "foo") + c.const_set(:FOO, "foo".freeze) assert_equal("foo", c::FOO) c.private_constant(:FOO) e = assert_raise(NameError) {c::FOO} @@ -2033,7 +2046,7 @@ def test_private_constant_in_class assert_equal("foo", c.class_eval("FOO")) assert_equal("foo", c.const_get("FOO")) $VERBOSE, verbose = nil, $VERBOSE - c.const_set(:FOO, "foo") + c.const_set(:FOO, "foo".freeze) $VERBOSE = verbose e = assert_raise(NameError) {c::FOO} assert_equal(c, e.receiver) @@ -2047,7 +2060,7 @@ def test_private_constant_in_class def test_private_constant_in_module m = Module.new - m.const_set(:FOO, "foo") + m.const_set(:FOO, "foo".freeze) assert_equal("foo", m::FOO) m.private_constant(:FOO) e = assert_raise(NameError) {m::FOO} @@ -2056,7 +2069,7 @@ def test_private_constant_in_module assert_equal("foo", m.class_eval("FOO")) assert_equal("foo", m.const_get("FOO")) $VERBOSE, verbose = nil, $VERBOSE - m.const_set(:FOO, "foo") + m.const_set(:FOO, "foo".freeze) $VERBOSE = verbose e = assert_raise(NameError) {m::FOO} assert_equal(m, e.receiver) @@ -2075,8 +2088,8 @@ def test_private_constant_in_module def test_private_constant2 c = Class.new - c.const_set(:FOO, "foo") - c.const_set(:BAR, "bar") + c.const_set(:FOO, "foo".freeze) + c.const_set(:BAR, "bar".freeze) assert_equal("foo", c::FOO) assert_equal("bar", c::BAR) c.private_constant(:FOO, :BAR) @@ -2096,6 +2109,7 @@ class X end def test_private_constant_const_missing + omit "can't access ivars of singleton class" if non_main_ractor? c = Class.new c.const_set(:FOO, "foo") c.private_constant(:FOO) @@ -2129,7 +2143,7 @@ def test_define_module_under_private_constant def test_public_constant c = Class.new - c.const_set(:FOO, "foo") + c.const_set(:FOO, "foo".freeze) assert_equal("foo", c::FOO) c.private_constant(:FOO) assert_raise(NameError) { c::FOO } @@ -2140,7 +2154,7 @@ def test_public_constant def test_deprecate_constant c = Class.new - c.const_set(:FOO, "foo") + c.const_set(:FOO, "foo".freeze) c.deprecate_constant(:FOO) assert_warn(/deprecated/) do Warning[:deprecated] = true @@ -2805,6 +2819,7 @@ def wrapped end def test_class_variables + omit "class variable access" if non_main_ractor? m = Module.new m.class_variable_set(:@@foo, 1) m2 = Module.new @@ -2817,6 +2832,7 @@ def test_class_variables end def test_class_variable_in_dup_class + omit "class variable access" if non_main_ractor? a = Class.new do @@a = 'A' def a=(x) @@ -2832,9 +2848,10 @@ def a assert_equal 'A', a.new.a, '[ruby-core:17019]' end - Bug6891 = '[ruby-core:47241]' + Bug6891 = '[ruby-core:47241]'.freeze def test_extend_module_with_protected_method + omit "class ivars" if non_main_ractor? list = [] x = Class.new { @@ -2947,6 +2964,7 @@ def test_uninitialized_attr end def test_uninitialized_attr_class + omit "class ivars" if non_main_ractor? assert_warning '' do assert_nil(AttrTest.cattr) end @@ -3005,6 +3023,7 @@ module PrivateConstantReopen end def test_private_constant_reopen + omit "TOPLEVEL_BINDING" if non_main_ractor? assert_raise(NameError) do eval <<-EOS, TOPLEVEL_BINDING module TestModule::PrivateConstantReopen::PRIVATE_CONSTANT @@ -3249,7 +3268,7 @@ def foo; bar; end } end - ConstLocation = [__FILE__, __LINE__] + ConstLocation = Ractor.make_shareable([__FILE__, __LINE__]) def test_const_source_location assert_equal(ConstLocation, self.class.const_source_location(:ConstLocation)) diff --git a/test/ruby/test_namespace.rb b/test/ruby/test_namespace.rb index cd593068675de6..1bfef49dd8ca30 100644 --- a/test/ruby/test_namespace.rb +++ b/test/ruby/test_namespace.rb @@ -472,6 +472,7 @@ def test_add_constants_in_namespace end def test_global_variables + omit "global variable access" if non_main_ractor? default_l = $-0 default_f = $, @@ -509,7 +510,7 @@ def test_global_variables EnvUtil.suppress_warning do $-0 = default_l $, = default_f - end + end if main_ractor? end def test_load_path_and_loaded_features diff --git a/test/ruby/test_object.rb b/test/ruby/test_object.rb index 9074e54df5ed50..f9c7c006e93738 100644 --- a/test/ruby/test_object.rb +++ b/test/ruby/test_object.rb @@ -339,6 +339,9 @@ def test_remove_instance_variable 'T_CLASS,T_MODULE' => Class.new(Object), 'generic ivar' => '', }.each do |desc, o| + if o.is_a?(Class) && non_main_ractor? + next + end e = assert_raise(NameError, "#{desc} iv removal raises before set") do o.remove_instance_variable(:@foo) end @@ -373,14 +376,14 @@ def c = @c o1.instance_variable_set(:@a, 0) o1.instance_variable_set(:@b, 1) o1.instance_variable_set(:@c, 2) - refute_includes ObjectSpace.dump(o1), '"embedded":true' + refute_includes ObjectSpace.dump(o1), '"embedded":true' if main_ractor? o1.remove_instance_variable(:@foo) - assert_includes ObjectSpace.dump(o1), '"embedded":true' + assert_includes ObjectSpace.dump(o1), '"embedded":true' if main_ractor? o2.instance_variable_set(:@a, 0) o2.instance_variable_set(:@b, 1) o2.instance_variable_set(:@c, 2) - assert_includes ObjectSpace.dump(o2), '"embedded":true' + assert_includes ObjectSpace.dump(o2), '"embedded":true' if main_ractor? assert_equal(0, o1.a) assert_equal(1, o1.b) @@ -1056,7 +1059,7 @@ def test_bad_initialize_copy assert_not_initialize_copy {Enumerator::Yielder.new {}} assert_not_initialize_copy {File.stat(__FILE__)} assert_not_initialize_copy {open(__FILE__)}.each(&:close) - assert_not_initialize_copy {ARGF.class.new} + assert_not_initialize_copy {ARGF.class.new} if main_ractor? assert_not_initialize_copy {Random.new} assert_not_initialize_copy {//} assert_not_initialize_copy {/.*/.match("foo")} diff --git a/test/ruby/test_object_id.rb b/test/ruby/test_object_id.rb index adb819febce57c..3783f6d1fda871 100644 --- a/test/ruby/test_object_id.rb +++ b/test/ruby/test_object_id.rb @@ -12,6 +12,7 @@ def test_dup_new_id end def test_dup_with_ivar_and_id + omit "class ivars" if @obj.is_a?(Module) && non_main_ractor? id = @obj.object_id @obj.instance_variable_set(:@foo, 42) @@ -21,6 +22,7 @@ def test_dup_with_ivar_and_id end def test_dup_with_id_and_ivar + omit "class ivars" if @obj.is_a?(Module) && non_main_ractor? @obj.instance_variable_set(:@foo, 42) id = @obj.object_id @@ -30,6 +32,7 @@ def test_dup_with_id_and_ivar end def test_dup_with_id_and_ivar_and_frozen + omit "class ivars" if @obj.is_a?(Module) && non_main_ractor? @obj.instance_variable_set(:@foo, 42) @obj.freeze id = @obj.object_id @@ -46,6 +49,7 @@ def test_clone_new_id end def test_clone_with_ivar_and_id + omit "class ivars" if @obj.is_a?(Module) && non_main_ractor? id = @obj.object_id @obj.instance_variable_set(:@foo, 42) @@ -55,6 +59,7 @@ def test_clone_with_ivar_and_id end def test_clone_with_id_and_ivar + omit "class ivars" if @obj.is_a?(Module) && non_main_ractor? @obj.instance_variable_set(:@foo, 42) id = @obj.object_id @@ -64,6 +69,7 @@ def test_clone_with_id_and_ivar end def test_clone_with_id_and_ivar_and_frozen + omit "class ivars" if @obj.is_a?(Module) && non_main_ractor? @obj.instance_variable_set(:@foo, 42) @obj.freeze id = @obj.object_id @@ -165,6 +171,7 @@ class TooComplex < Module end def setup + omit "class ivars" if non_main_ractor? if defined?(RubyVM::Shape::SHAPE_MAX_VARIATIONS) assert_equal 8, RubyVM::Shape::SHAPE_MAX_VARIATIONS end diff --git a/test/ruby/test_objectspace.rb b/test/ruby/test_objectspace.rb index a479547599a03a..be8c6138a14682 100644 --- a/test/ruby/test_objectspace.rb +++ b/test/ruby/test_objectspace.rb @@ -7,10 +7,11 @@ def self.deftest_id2ref(obj) file = $` line = $1.to_i code = <<"End" - define_method("test_id2ref_#{line}") {\ + define_method("test_id2ref_#{line}", &Ractor.make_shareable(proc {\ + omit "id2ref" if non_main_ractor? o = EnvUtil.suppress_warning { ObjectSpace._id2ref(obj.object_id) } assert_same(obj, o, "didn't round trip: \#{obj.inspect}");\ - } + })) End eval code, binding, file, line end @@ -29,7 +30,7 @@ def self.deftest_id2ref(obj) deftest_id2ref(:a) deftest_id2ref(:abcdefghijilkjl) deftest_id2ref(:==) - deftest_id2ref(Object.new) + deftest_id2ref(Object.new.freeze) deftest_id2ref(self) deftest_id2ref(true) deftest_id2ref(false) @@ -56,6 +57,7 @@ def test_id2ref_liveness end def test_id2ref_invalid_argument + omit "id2ref" if non_main_ractor? msg = /no implicit conversion/ assert_raise_with_message(TypeError, msg) { EnvUtil.suppress_warning { ObjectSpace._id2ref(nil) } } assert_raise_with_message(TypeError, msg) { EnvUtil.suppress_warning { ObjectSpace._id2ref(false) } } @@ -66,6 +68,7 @@ def test_id2ref_invalid_argument end def test_id2ref_invalid_symbol_id + omit "id2ref" if non_main_ractor? # RB_STATIC_SYM_P checks for static symbols by checking that the bottom # 8 bits of the object is equal to RUBY_SYMBOL_FLAG, so we need to make # sure that the bottom 8 bits remain unchanged. @@ -74,6 +77,7 @@ def test_id2ref_invalid_symbol_id end def test_count_objects + omit "count_objects" if non_main_ractor? h = {} ObjectSpace.count_objects(h) assert_kind_of(Hash, h) @@ -235,6 +239,7 @@ def test_finalizer_thread_raise end def test_each_object + omit "each_object" if non_main_ractor? klass = Class.new new_obj = klass.new @@ -248,6 +253,7 @@ def test_each_object end def test_each_object_enumerator + omit "each_object" if non_main_ractor? klass = Class.new new_obj = klass.new diff --git a/test/ruby/test_optimization.rb b/test/ruby/test_optimization.rb index e39eafa5e50bb9..df4cad6ba97e17 100644 --- a/test/ruby/test_optimization.rb +++ b/test/ruby/test_optimization.rb @@ -252,6 +252,7 @@ def test_trace_optimized_methods end def test_string_freeze_saves_memory + pend "ObjectSpace.memsize_of" if non_main_ractor? # TODO: memsize_of should be safe n = 16384 data = '.'.freeze r, w = IO.pipe @@ -519,6 +520,7 @@ def post_arg(_a = 1, _b) = ret_const end def test_tailcall_interrupted_by_sigint + omit "in a subprocess" if non_main_ractor? bug12576 = 'ruby-core:76327' script = "#{<<-"begin;"}\n#{<<~'end;'}" begin; diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb index 98e95b98afaf77..891e2f1a85fddb 100644 --- a/test/ruby/test_parse.rb +++ b/test/ruby/test_parse.rb @@ -135,6 +135,7 @@ def t.baz(*r) end def test_mlhs_node + omit "class ivars" if non_main_ractor? c = Class.new class << c attr_accessor :foo, :bar, :Foo, :Bar @@ -364,6 +365,7 @@ def test_words end def test_dstr + omit "class variables" if non_main_ractor? @@foo = 1 assert_equal("foo 1 bar", "foo #@@foo bar") "1" =~ /(.)/ @@ -897,6 +899,7 @@ def test_float end def test_global_variable + omit "global variable access" if non_main_ractor? assert_equal(nil, assert_warning(/not initialized/) {eval('$-x')}) assert_equal(nil, eval('alias $preserve_last_match $&')) assert_equal(nil, eval('alias $& $test_parse_foobarbazqux')) @@ -1449,11 +1452,11 @@ def test_command_def_cmdarg end; end - NONASCII_CONSTANTS = [ + NONASCII_CONSTANTS = Ractor.make_shareable([ *%W"\u{00de} \u{00C0}".flat_map {|c| [c, c.encode("iso-8859-15")]}, "\u{1c4}", "\u{1f2}", "\u{1f88}", "\u{370}", *%W"\u{391} \u{ff21}".flat_map {|c| [c, c.encode("cp932"), c.encode("euc-jp")]}, - ] + ]) def assert_nonascii_const assert_all_assertions_foreach("NONASCII_CONSTANTS", *NONASCII_CONSTANTS) do |n| @@ -1544,6 +1547,7 @@ def test_shareable_constant_value_ignored end def test_shareable_constant_value_simple + omit "can't access unshareables" if non_main_ractor? obj = [['unsharable_value']] a, b, c = eval_separately("#{<<~"begin;"}\n#{<<~'end;'}") begin; @@ -1610,6 +1614,7 @@ def test_shareable_constant_value_literal_const_refs end def test_shareable_constant_value_nested + omit "can't access unshareables" if non_main_ractor? a, b = eval_separately("#{<<~"begin;"}\n#{<<~'end;'}") begin; # shareable_constant_value: none diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb index 92a3244fc2ae88..5d7efa76e2282e 100644 --- a/test/ruby/test_pattern_matching.rb +++ b/test/ruby/test_pattern_matching.rb @@ -430,19 +430,21 @@ def test_pin_operator_value_pattern end end - assert_block do - @@TestPatternMatching = /a/ - case 'abc' - in ^@@TestPatternMatching - true + if main_ractor? + assert_block do + @@TestPatternMatching = /a/ + case 'abc' + in ^@@TestPatternMatching + true + end end - end - assert_block do - $TestPatternMatching = /a/ - case 'abc' - in ^$TestPatternMatching - true + assert_block do + $TestPatternMatching = /a/ + case 'abc' + in ^$TestPatternMatching + true + end end end end @@ -883,6 +885,7 @@ def test_find_pattern end def test_hash_pattern + omit "class ivars" if non_main_ractor? assert_block do [{}, C.new({})].all? do |i| case i @@ -1324,6 +1327,7 @@ def test_deconstruct end def test_deconstruct_keys + omit "can't access class ivars" if non_main_ractor? assert_raise(TypeError) do case CTypeError.new in {} diff --git a/test/ruby/test_primitive.rb b/test/ruby/test_primitive.rb index f1db934000dc23..038d404d6e152f 100644 --- a/test/ruby/test_primitive.rb +++ b/test/ruby/test_primitive.rb @@ -26,7 +26,7 @@ def test_lvar assert_equal 4, c end - C_Setup = -> do + C_Setup = Ractor.make_shareable(proc do remove_const :C if defined? ::TestRubyPrimitive::C remove_const :A if defined? ::TestRubyPrimitive::A @@ -46,7 +46,7 @@ def const (1..2).map { A::B::C::Const } - end + end) def test_constant C_Setup.call @@ -109,6 +109,7 @@ class A4 end def test_constant_cache3 + omit "global variable access" if non_main_ractor? assert_equal 7, $test_ruby_primitive_constant_cache3 end @@ -120,6 +121,7 @@ class A5 end def test_constatant_cache4 + omit "global variable access" if non_main_ractor? assert_equal 8, $test_ruby_primitive_constant_cache4 end @@ -138,10 +140,12 @@ class C6 < B6 $test_ruby_primitive_constant_cache5 = [A6.foo, B6.foo, C6.foo] def test_constant_cache5 + omit "global variable access" if non_main_ractor? assert_equal [0, 1, 2], $test_ruby_primitive_constant_cache5 end def test_gvar + omit "global variable access" if non_main_ractor? $test_ruby_primitive_gvar = 7 assert_equal 7, $test_ruby_primitive_gvar assert_equal 7, $test_ruby_primitive_gvar @@ -164,6 +168,7 @@ def m end def test_cvar_from_instance_method + omit "class variables" if non_main_ractor? A7_Setup.call assert_equal 2, A7.new.m @@ -185,6 +190,7 @@ def m end def test_cvar_from_singleton_method + omit "class variables" if non_main_ractor? A8_Setup.call assert_equal 2, A8.m @@ -204,6 +210,7 @@ def self.m end def test_cvar_from_singleton_method2 + omit "class variables" if non_main_ractor? A9_Setup.call assert_equal 2, A9.m @@ -225,18 +232,20 @@ def test_opassign assert_equal 4, @iv # init @@cv - @@cv = nil - - @@cv ||= 1 - assert_equal 1, @@cv - @@cv &&= 2 - assert_equal 2, @@cv - @@cv ||= 99 - assert_equal 2, @@cv - - $gv = 3 - $gv += 4 - assert_equal 7, $gv + if main_ractor? + @@cv = nil + + @@cv ||= 1 + assert_equal 1, @@cv + @@cv &&= 2 + assert_equal 2, @@cv + @@cv ||= 99 + assert_equal 2, @@cv + + $gv = 3 + $gv += 4 + assert_equal 7, $gv + end obj = A10.new obj.a = 9 diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb index 2cd97ca32464ab..8b5fb57176af71 100644 --- a/test/ruby/test_proc.rb +++ b/test/ruby/test_proc.rb @@ -513,7 +513,7 @@ def test_binding_source_location file, lineno = method(:source_location_test).to_proc.binding.source_location assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) - assert_equal(@@line_of_source_location_test[0], lineno, 'Bug #2427') + assert_equal(LINE_OF_SOURCE_LOCATION_TEST[0], lineno, 'Bug #2427') end def test_binding_error_unless_ruby_frame @@ -1499,7 +1499,7 @@ def test_to_s assert_include(EnvUtil.labeled_class(name, Proc).new {}.to_s, name) end - @@line_of_source_location_test = [__LINE__ + 1, 2, __LINE__ + 3, 5] + LINE_OF_SOURCE_LOCATION_TEST = [__LINE__ + 1, 2, __LINE__ + 3, 5].freeze def source_location_test a=1, b=2 end @@ -1507,16 +1507,16 @@ def source_location_test a=1, def test_source_location file, *loc = method(:source_location_test).source_location assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) - assert_equal(@@line_of_source_location_test, loc, 'Bug #2427') + assert_equal(LINE_OF_SOURCE_LOCATION_TEST, loc, 'Bug #2427') file, *loc = self.class.instance_method(:source_location_test).source_location assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) - assert_equal(@@line_of_source_location_test, loc, 'Bug #2427') + assert_equal(LINE_OF_SOURCE_LOCATION_TEST, loc, 'Bug #2427') end - @@line_of_attr_reader_source_location_test = __LINE__ + 3 - @@line_of_attr_writer_source_location_test = __LINE__ + 3 - @@line_of_attr_accessor_source_location_test = __LINE__ + 3 + LINE_OF_ATTR_READER_SOURCE_LOCATION_TEST = __LINE__ + 3 + LINE_OF_ATTR_WRITER_SOURCE_LOCATION_TEST = __LINE__ + 3 + LINE_OF_ATTR_ACCESSOR_SOURCE_LOCATION_TEST = __LINE__ + 3 attr_reader :attr_reader_source_location_test attr_writer :attr_writer_source_location_test attr_accessor :attr_accessor_source_location_test @@ -1524,19 +1524,19 @@ def test_source_location def test_attr_source_location file, lineno = method(:attr_reader_source_location_test).source_location assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) - assert_equal(@@line_of_attr_reader_source_location_test, lineno) + assert_equal(LINE_OF_ATTR_READER_SOURCE_LOCATION_TEST, lineno) file, lineno = method(:attr_writer_source_location_test=).source_location assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) - assert_equal(@@line_of_attr_writer_source_location_test, lineno) + assert_equal(LINE_OF_ATTR_WRITER_SOURCE_LOCATION_TEST, lineno) file, lineno = method(:attr_accessor_source_location_test).source_location assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) - assert_equal(@@line_of_attr_accessor_source_location_test, lineno) + assert_equal(LINE_OF_ATTR_ACCESSOR_SOURCE_LOCATION_TEST, lineno) file, lineno = method(:attr_accessor_source_location_test=).source_location assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) - assert_equal(@@line_of_attr_accessor_source_location_test, lineno) + assert_equal(LINE_OF_ATTR_ACCESSOR_SOURCE_LOCATION_TEST, lineno) end def block_source_location_test(*args, &block) diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index 221ff37c6b6946..94d2dcdbc02708 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -6,7 +6,7 @@ require 'rbconfig' class TestProcess < Test::Unit::TestCase - RUBY = EnvUtil.rubybin + RUBY = EnvUtil.rubybin.freeze def setup Process.waitall @@ -152,7 +152,7 @@ def test_rlimit_value end end - TRUECOMMAND = [RUBY, '-e', ''] + TRUECOMMAND = Ractor.make_shareable([RUBY, '-e', '']) def test_execopts_opts assert_nothing_raised { @@ -295,9 +295,10 @@ def test_overwrite_ENV if e = RbConfig::CONFIG['PRELOADENV'] and !e.empty? MANDATORY_ENVS << e end - PREENVARG = ['-e', "%w[#{MANDATORY_ENVS.join(' ')}].each{|e|ENV.delete(e)}"] - ENVARG = ['-e', 'ENV.each {|k,v| puts "#{k}=#{v}" }'] - ENVCOMMAND = [RUBY].concat(PREENVARG).concat(ENVARG) + Ractor.make_shareable(MANDATORY_ENVS) + PREENVARG = Ractor.make_shareable(['-e', "%w[#{MANDATORY_ENVS.join(' ')}].each{|e|ENV.delete(e)}"]) + ENVARG = Ractor.make_shareable(['-e', 'ENV.each {|k,v| puts "#{k}=#{v}" }']) + ENVCOMMAND = Ractor.make_shareable([RUBY].concat(PREENVARG).concat(ENVARG)) def test_execopts_env assert_raise(ArgumentError) { @@ -336,7 +337,7 @@ def test_execopts_env } with_tmpchdir {|d| - system({"fofo"=>"haha"}, *ENVCOMMAND, STDOUT=>"out") + system({"fofo"=>"haha"}, *ENVCOMMAND, $stdout=>"out") assert_match(/^fofo=haha$/, File.read("out").chomp) } @@ -455,7 +456,7 @@ def test_execopts_unsetenv_others } end - PWD = [RUBY, '-e', 'puts Dir.pwd'] + PWD = Ractor.make_shareable([RUBY, '-e', 'puts Dir.pwd']) def test_execopts_chdir with_tmpchdir {|d| @@ -511,7 +512,7 @@ def test_execopts_open_failure } end - UMASK = [RUBY, '-e', 'printf "%04o\n", File.umask'] + UMASK = Ractor.make_shareable([RUBY, '-e', 'printf "%04o\n", File.umask']) def test_execopts_umask omit "umask is not supported" if windows? @@ -548,49 +549,49 @@ def with_pipes(n) end end - ECHO = lambda {|arg| [RUBY, '-e', "puts #{arg.dump}; STDOUT.flush"] } - SORT = [RUBY, '-e', "puts ARGF.readlines.sort"] - CAT = [RUBY, '-e', "IO.copy_stream STDIN, STDOUT"] + ECHO = Ractor.make_shareable(lambda {|arg| [RUBY, '-e', "puts #{arg.dump}; $stdout.flush"] }) + SORT = Ractor.make_shareable([RUBY, '-e', "puts ARGF.readlines.sort"]) + CAT = Ractor.make_shareable([RUBY, '-e', "IO.copy_stream $stdin, $stdout"]) def test_execopts_redirect_fd with_tmpchdir {|d| - Process.wait Process.spawn(*ECHO["a"], STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) + Process.wait Process.spawn(*ECHO["a"], $stdout=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) assert_equal("a", File.read("out").chomp) if windows? # currently telling to child the file modes is not supported. File.write("out", "0\n", mode: "a") else - Process.wait Process.spawn(*ECHO["0"], STDOUT=>["out", File::WRONLY|File::CREAT|File::APPEND, 0644]) + Process.wait Process.spawn(*ECHO["0"], $stdout=>["out", File::WRONLY|File::CREAT|File::APPEND, 0644]) assert_equal("a\n0\n", File.read("out")) end - Process.wait Process.spawn(*SORT, STDIN=>["out", File::RDONLY, 0644], - STDOUT=>["out2", File::WRONLY|File::CREAT|File::TRUNC, 0644]) + Process.wait Process.spawn(*SORT, $stdin=>["out", File::RDONLY, 0644], + $stdout=>["out2", File::WRONLY|File::CREAT|File::TRUNC, 0644]) assert_equal("0\na\n", File.read("out2")) - Process.wait Process.spawn(*ECHO["b"], [STDOUT, STDERR]=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) + Process.wait Process.spawn(*ECHO["b"], [$stdout, $stderr]=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) assert_equal("b", File.read("out").chomp) # problem occur with valgrind - #Process.wait Process.spawn(*ECHO["a"], STDOUT=>:close, STDERR=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) + #Process.wait Process.spawn(*ECHO["a"], $stdout=>:close, $stderr=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) #p File.read("out") #assert_not_empty(File.read("out")) # error message such as "-e:1:in `flush': Bad file descriptor (Errno::EBADF)" - Process.wait Process.spawn(*ECHO["c"], STDERR=>STDOUT, STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) + Process.wait Process.spawn(*ECHO["c"], $stderr=>$stdout, $stdout=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) assert_equal("c", File.read("out").chomp) File.open("out", "w") {|f| - Process.wait Process.spawn(*ECHO["d"], STDOUT=>f) + Process.wait Process.spawn(*ECHO["d"], $stdout=>f) assert_equal("d", File.read("out").chomp) } - opts = {STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]} - opts.merge(3=>STDOUT, 4=>STDOUT, 5=>STDOUT, 6=>STDOUT, 7=>STDOUT) unless windows? + opts = {$stdout=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]} + opts.merge(3=>$stdout, 4=>$stdout, 5=>$stdout, 6=>$stdout, 7=>$stdout) unless windows? Process.wait Process.spawn(*ECHO["e"], opts) assert_equal("e", File.read("out").chomp) - opts = {STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]} - opts.merge(3=>0, 4=>:in, 5=>STDIN, 6=>1, 7=>:out, 8=>STDOUT, 9=>2, 10=>:err, 11=>STDERR) unless windows? + opts = {$stdout=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]} + opts.merge(3=>0, 4=>:in, 5=>$stdin, 6=>1, 7=>:out, 8=>$stdout, 9=>2, 10=>:err, 11=>$stderr) unless windows? Process.wait Process.spawn(*ECHO["ee"], opts) assert_equal("ee", File.read("out").chomp) unless windows? # passing non-stdio fds is not supported on Windows File.open("out", "w") {|f| - h = {STDOUT=>f, f=>STDOUT} - 3.upto(30) {|i| h[i] = STDOUT if f.fileno != i } + h = {$stdout=>f, f=>$stdout} + 3.upto(30) {|i| h[i] = $stdout if f.fileno != i } Process.wait Process.spawn(*ECHO["f"], h) assert_equal("f", File.read("out").chomp) } @@ -602,14 +603,14 @@ def test_execopts_redirect_fd Process.wait Process.spawn(*ECHO["f"], [Process]=>1) } assert_raise(ArgumentError) { - Process.wait Process.spawn(*ECHO["f"], [1, STDOUT]=>2) + Process.wait Process.spawn(*ECHO["f"], [1, $stdout]=>2) } assert_raise(ArgumentError) { Process.wait Process.spawn(*ECHO["f"], -1=>2) } - Process.wait Process.spawn(*ECHO["hhh\nggg\n"], STDOUT=>"out") + Process.wait Process.spawn(*ECHO["hhh\nggg\n"], $stdout=>"out") assert_equal("hhh\nggg\n", File.read("out")) - Process.wait Process.spawn(*SORT, STDIN=>"out", STDOUT=>"out2") + Process.wait Process.spawn(*SORT, $stdin=>"out", $stdout=>"out2") assert_equal("ggg\nhhh\n", File.read("out2")) unless windows? @@ -620,9 +621,9 @@ def test_execopts_redirect_fd assert_equal("", File.read("err")) end - system(*ECHO["bb\naa\n"], STDOUT=>["out", "w"]) + system(*ECHO["bb\naa\n"], $stdout=>["out", "w"]) assert_equal("bb\naa\n", File.read("out")) - system(*SORT, STDIN=>["out"], STDOUT=>"out2") + system(*SORT, $stdin=>["out"], $stdout=>"out2") assert_equal("aa\nbb\n", File.read("out2")) } end @@ -738,7 +739,7 @@ def test_execopts_redirect_open_fifo_interrupt_print def test_execopts_redirect_pipe with_pipe {|r1, w1| with_pipe {|r2, w2| - opts = {STDIN=>r1, STDOUT=>w2} + opts = {$stdin=>r1, $stdout=>w2} opts.merge(w1=>:close, r2=>:close) unless windows? pid = spawn(*SORT, opts) r1.close @@ -851,30 +852,30 @@ def test_execopts_redirect_to_out_and_err def test_execopts_redirect_dup2_child with_tmpchdir {|d| Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", - STDOUT=>"out", STDERR=>[:child, STDOUT]) + $stdout=>"out", $stderr=>[:child, $stdout]) assert_equal("errout", File.read("out")) Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", - STDERR=>"out", STDOUT=>[:child, STDERR]) + $stderr=>"out", $stdout=>[:child, $stderr]) assert_equal("errout", File.read("out")) omit "inheritance of fd other than stdin,stdout and stderr is not supported" if windows? Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", - STDOUT=>"out", - STDERR=>[:child, 3], + $stdout=>"out", + $stderr=>[:child, 3], 3=>[:child, 4], - 4=>[:child, STDOUT] + 4=>[:child, $stdout] ) assert_equal("errout", File.read("out")) - IO.popen([RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", STDERR=>[:child, STDOUT]]) {|io| + IO.popen([RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", $stderr=>[:child, $stdout]]) {|io| assert_equal("errout", io.read) } - assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, STDOUT=>[:child, STDOUT]) } + assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, $stdout=>[:child, $stdout]) } assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, 3=>[:child, 4], 4=>[:child, 3]) } assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, 3=>[:child, 4], 4=>[:child, 5], 5=>[:child, 3]) } - assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, STDOUT=>[:child, 3]) } + assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, $stdout=>[:child, 3]) } } end @@ -898,13 +899,13 @@ def test_execopts_popen def test_execopts_popen_stdio with_tmpchdir {|d| assert_raise(ArgumentError) { - IO.popen([*ECHO["qux"], STDOUT=>STDOUT]) {|io| } + IO.popen([*ECHO["qux"], $stdout=>$stdout]) {|io| } } - IO.popen([*ECHO["hoge"], STDERR=>STDOUT]) {|io| + IO.popen([*ECHO["hoge"], $stderr=>$stdout]) {|io| assert_equal("hoge\n", io.read) } assert_raise(ArgumentError) { - IO.popen([*ECHO["fuga"], STDOUT=>"out"]) {|io| } + IO.popen([*ECHO["fuga"], $stdout=>"out"]) {|io| } } } end @@ -939,9 +940,10 @@ def test_popen_fork end def test_popen_fork_ensure + pend "ractor_confirm_belonging issue on fork" if non_main_ractor? IO.popen("-") do |io| if !io - STDERR.reopen(STDOUT) + $stderr.reopen($stdout) # issue is here raise "fooo" else assert_empty io.read @@ -1098,6 +1100,7 @@ def test_execopts_redirect_self end unless windows? # passing non-stdio fds is not supported on Windows def test_execopts_redirect_tempfile + pend "Tempfile" if non_main_ractor? bug6269 = '[ruby-core:44181]' Tempfile.create("execopts") do |tmp| pid = assert_nothing_raised(ArgumentError, bug6269) do @@ -1111,8 +1114,8 @@ def test_execopts_redirect_tempfile def test_execopts_duplex_io IO.popen("#{RUBY} -e ''", "r+") {|duplex| - assert_raise(ArgumentError) { system("#{RUBY} -e ''", duplex=>STDOUT) } - assert_raise(ArgumentError) { system("#{RUBY} -e ''", STDOUT=>duplex) } + assert_raise(ArgumentError) { system("#{RUBY} -e ''", duplex=>$stdout) } + assert_raise(ArgumentError) { system("#{RUBY} -e ''", $stdout=>duplex) } } end @@ -1414,12 +1417,12 @@ def test_argv0 def with_stdin(filename) File.open(filename) {|f| begin - old = STDIN.dup + old = $stdin.dup begin - STDIN.reopen(filename) + $stdin.reopen(filename) yield ensure - STDIN.reopen(old) + $stdin.reopen(old) end ensure old.close @@ -1461,6 +1464,7 @@ def test_argv0_keep_alive end def test_argv0_frozen + omit "global variable access" if non_main_ractor? assert_predicate Process.argv0, :frozen? assert_predicate $0, :frozen? end @@ -1667,6 +1671,7 @@ def test_seteuid end def test_seteuid_name + pend "unsafe Etc method" if non_main_ractor? user = (Etc.getpwuid(Process.euid).name rescue ENV["USER"]) or return assert_nothing_raised(TypeError) {Process.euid = user} rescue NotImplementedError @@ -1684,8 +1689,10 @@ def test_setegid if Process::UID.respond_to?(:from_name) def test_uid_from_name - if u = Etc.getpwuid(Process.uid) - assert_equal(Process.uid, Process::UID.from_name(u.name), u.name) + if main_ractor? # ractor unsafe Etc method + if u = Etc.getpwuid(Process.uid) + assert_equal(Process.uid, Process::UID.from_name(u.name), u.name) + end end exc = assert_raise_kind_of(ArgumentError, SystemCallError) { Process::UID.from_name("\u{4e0d 5b58 5728}") @@ -1696,8 +1703,10 @@ def test_uid_from_name if Process::GID.respond_to?(:from_name) && !RUBY_PLATFORM.include?("android") def test_gid_from_name - if g = Etc.getgrgid(Process.gid) - assert_equal(Process.gid, Process::GID.from_name(g.name), g.name) + if main_ractor? # ractor unsafe Etc method + if g = Etc.getgrgid(Process.gid) + assert_equal(Process.gid, Process::GID.from_name(g.name), g.name) + end end exc = assert_raise_kind_of(ArgumentError, SystemCallError) do Process::GID.from_name("\u{4e0d 5b58 5728}") # fu son zai ("absent" in Kanji) @@ -1814,7 +1823,7 @@ def assert_fail_too_long_path((cmd, sep), mesg) exs = [Errno::ENOENT] exs << Errno::EINVAL if windows? exs << Errno::E2BIG if defined?(Errno::E2BIG) - opts = {[STDOUT, STDERR]=>File::NULL} + opts = {[$stdout, $stderr]=>File::NULL} if defined?(Process::RLIMIT_NPROC) opts[:rlimit_nproc] = /openbsd/i =~ RUBY_PLATFORM ? 64 : 128 end @@ -1844,6 +1853,7 @@ def assert_fail_too_long_path((cmd, sep), mesg) def test_system_sigpipe return if windows? + pend "Timeout" if non_main_ractor? pid = 0 @@ -1894,7 +1904,7 @@ def test_daemon_readwrite break f.read end Process.daemon(true, true) - puts STDIN.gets + puts $stdin.gets end assert_equal("ok?\n", data) end @@ -1943,6 +1953,7 @@ def test_daemon_no_threads end else # darwin def test_daemon_no_threads + pend "Timeout" if non_main_ractor? data = EnvUtil.timeout(3) do IO.popen("-") do |f| break f.readlines.map(&:chomp) if f @@ -2462,6 +2473,7 @@ def test_exec_close_reserved_fd end def test_signals_work_after_exec_fail + pend "Timeout" if non_main_ractor? r, w = IO.pipe pid = status = nil EnvUtil.timeout(30) do @@ -2496,6 +2508,7 @@ def test_signals_work_after_exec_fail end if defined?(fork) def test_threading_works_after_exec_fail + pend "Timeout" if non_main_ractor? r, w = IO.pipe pid = status = nil EnvUtil.timeout(90) do @@ -2551,6 +2564,7 @@ def test_many_args end def test_to_hash_on_arguments + omit "separate process" if non_main_ractor? all_assertions do |a| %w[Array String].each do |type| a.for(type) do @@ -2784,6 +2798,7 @@ def test_warmup_frees_pages end def test_concurrent_group_and_pid_wait + pend "Timeout" if non_main_ractor? # Use a pair of pipes that will make long_pid exit when this test exits, to avoid # leaking temp processes. long_rpipe, long_wpipe = IO.pipe diff --git a/test/ruby/test_rand.rb b/test/ruby/test_rand.rb index f177664943ae9e..d72d237154ac46 100644 --- a/test/ruby/test_rand.rb +++ b/test/ruby/test_rand.rb @@ -278,13 +278,14 @@ def test_fork_shuffle end def assert_fork_status(n, mesg, &block) + pend "ractor_confirm_belonging issue with fork" if non_main_ractor? IO.pipe do |r, w| (1..n).map do st = desc = nil IO.pipe do |re, we| p1 = fork { re.close - STDERR.reopen(we) + $stderr.reopen(we) w.puts(block.call.to_s) } we.close diff --git a/test/ruby/test_range.rb b/test/ruby/test_range.rb index f875c0ab40c5e4..9449ff3f4f8464 100644 --- a/test/ruby/test_range.rb +++ b/test/ruby/test_range.rb @@ -1173,6 +1173,7 @@ def test_cyclic_range_inspect end def test_comparison_when_recursive + pend "Timeout" if non_main_ractor? x = CyclicRange.allocate; x.send(:initialize, x, 1) y = CyclicRange.allocate; y.send(:initialize, y, 1) Timeout.timeout(1) { diff --git a/test/ruby/test_readpartial.rb b/test/ruby/test_readpartial.rb index bc22556cd4e9e3..ff3b9bb65ec007 100644 --- a/test/ruby/test_readpartial.rb +++ b/test/ruby/test_readpartial.rb @@ -47,6 +47,7 @@ def test_closed_pipe end def test_open_pipe + pend "Timeout" if non_main_ractor? pipe {|r, w| w << 'abc' assert_equal('ab', r.readpartial(2)) @@ -58,6 +59,7 @@ def test_open_pipe end def test_with_stdio + pend "Timeout" if non_main_ractor? pipe {|r, w| w << "abc\ndef\n" assert_equal("abc\n", r.gets) diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb index 6ce434790be13b..1e118a1c193f3b 100644 --- a/test/ruby/test_refinement.rb +++ b/test/ruby/test_refinement.rb @@ -225,6 +225,7 @@ def pow(*) end end def test_method_should_use_refinements + omit "binding" if non_main_ractor? omit if Test::Unit::Runner.current_repeat_count > 0 foo = Foo.new @@ -248,6 +249,7 @@ def abs end end def test_instance_method_should_use_refinements + omit "binding" if non_main_ractor? omit if Test::Unit::Runner.current_repeat_count > 0 foo = Foo.new @@ -314,6 +316,7 @@ def /(other) quo(other) end end def test_override_builtin_method + omit "binding" if non_main_ractor? assert_equal(0, 1 / 2) assert_equal(Rational(1, 2), eval_using(IntegerSlashExt, "1 / 2")) assert_equal(0, 1 / 2) @@ -327,6 +330,7 @@ def +(other) "overridden" end end def test_override_builtin_method_with_method_added + omit "binding" if non_main_ractor? assert_equal(3, 1 + 2) assert_equal("overridden", eval_using(IntegerPlusExt, "1 + 2")) assert_equal(3, 1 + 2) @@ -356,6 +360,7 @@ def baz; return "baz" end end def test_refine_same_class_twice + omit "binding" if non_main_ractor? assert_equal("foo", eval_using(RefineSameClass, "1.foo")) assert_equal("bar", eval_using(RefineSameClass, "1.bar")) assert_equal(RefineSameClass::REFINEMENT1, RefineSameClass::REFINEMENT2) @@ -369,6 +374,7 @@ def foo; "foo"; end end def test_respond_to_should_use_refinements + omit "binding" if non_main_ractor? assert_equal(false, 1.respond_to?(:foo)) assert_equal(true, eval_using(IntegerFooExt, "1.respond_to?(:foo)")) end @@ -390,6 +396,7 @@ def each end def test_builtin_method_no_local_rebinding + omit "binding" if non_main_ractor? assert_equal(false, eval_using(StringCmpExt, '"1" >= "2"')) assert_equal(1, eval_using(ArrayEachExt, "[1, 2, 3].min")) end @@ -419,6 +426,7 @@ def foo end def test_refine_prepended_class + omit "binding" if non_main_ractor? x = eval_using(RefinePrependedClass::M2, "TestRefinement::RefinePrependedClass::C.new.foo") assert_equal([:c, :m1, :m2], x) @@ -541,6 +549,7 @@ def foo end def test_main_using_is_private + omit "binding" if non_main_ractor? assert_raise(NoMethodError) do eval("recv = self; recv.using Module.new", Sandbox::BINDING) end @@ -556,6 +565,7 @@ class UsingClass end def test_module_using_class + omit "binding" if non_main_ractor? assert_raise(TypeError) do eval("using TestRefinement::UsingClass", Sandbox::BINDING) end @@ -638,6 +648,7 @@ def foo end def test_redefine_refined_method + omit "binding" if non_main_ractor? x = eval_using(RedefineRefinedMethod::M, "TestRefinement::RedefineRefinedMethod::C.new.foo") assert_equal("refined", x) @@ -687,6 +698,7 @@ def recursive_length end def test_refine_recursion + omit "binding" if non_main_ractor? x = eval_using(StringRecursiveLength, "'foo'.recursive_length") assert_equal(3, x) end @@ -706,6 +718,7 @@ def to_json; "{" + map { |k, v| k.to_s.dump + ":" + v.to_json }.join(",") + "}" end def test_refine_mutual_recursion + omit "binding" if non_main_ractor? x = eval_using(ToJSON, "[{1=>2}, {3=>4}].to_json") assert_equal('[{"1":2},{"3":4}]', x) end @@ -719,6 +732,7 @@ def test_refine_with_proc end def test_using_in_module + omit "binding" if non_main_ractor? assert_raise(RuntimeError) do eval(<<-EOF, Sandbox::BINDING) $main = self @@ -732,6 +746,7 @@ module M2 end def test_using_in_method + omit "binding" if non_main_ractor? assert_raise(RuntimeError) do eval(<<-EOF, Sandbox::BINDING) $main = self @@ -831,6 +846,7 @@ def test_prepend_after_refine_wb_miss end def test_prepend_after_refine + omit "binding" if non_main_ractor? x = eval_using(PrependAfterRefine::M, "TestRefinement::PrependAfterRefine::C.new.foo") assert_equal("refined", x) @@ -860,6 +876,7 @@ def foo(*args) end def test_super_in_block + omit "binding" if non_main_ractor? bug7925 = '[ruby-core:52750] [Bug #7925]' x = eval_using(SuperInBlock::R, "TestRefinement:: SuperInBlock::C.new.foo(#{bug7925.dump})") @@ -918,6 +935,7 @@ def test_module_using_in_method end def test_module_using_invalid_self + omit "binding" if non_main_ractor? assert_raise(RuntimeError) do eval <<-EOF, Sandbox::BINDING module TestRefinement::TestModuleUsingInvalidSelf @@ -1766,21 +1784,21 @@ def in_ref_c module Foo using RefB - USED_MODS = Module.used_modules - USED_REFS = Module.used_refinements + USED_MODS = Ractor.make_shareable(Module.used_modules) + USED_REFS = Ractor.make_shareable(Module.used_refinements) end module Bar using RefC - USED_MODS = Module.used_modules - USED_REFS = Module.used_refinements + USED_MODS = Ractor.make_shareable(Module.used_modules) + USED_REFS = Ractor.make_shareable(Module.used_refinements) end module Combined using RefA using RefB - USED_MODS = Module.used_modules - USED_REFS = Module.used_refinements + USED_MODS = Ractor.make_shareable(Module.used_modules) + USED_REFS = Ractor.make_shareable(Module.used_refinements) end end @@ -1894,6 +1912,7 @@ def bar end def test_refine_alias_in_subclass + omit "binding" if non_main_ractor? assert_equal(:refined, eval_using(AliasInSubclass::M, "AliasInSubclass::D.new.bar")) end @@ -2409,6 +2428,7 @@ def foo end def test_refine_frozen_class + omit "class ivar" if non_main_ractor? verbose_bak, $VERBOSE = $VERBOSE, nil singleton_class.instance_variable_set(:@x, self) class << self @@ -2601,7 +2621,7 @@ def foo end module B - BAR = "bar" + BAR = "bar".freeze def bar "#{foo}:#{BAR}" @@ -2662,6 +2682,8 @@ def to_s = :R end def test_inline_cache_invalidation + # class has no constant associated and is belonging to this ractor, in theory it could be allowed + pend "class ivar" if non_main_ractor? klass = Class.new do def cached_foo_callsite = foo diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb index 9f1e03e6499782..6f1a45f82d12e1 100644 --- a/test/ruby/test_regexp.rb +++ b/test/ruby/test_regexp.rb @@ -1012,6 +1012,7 @@ def test_regsub_no_memory_leak end def test_ignorecase + omit "global variable access" if non_main_ractor? v = assert_deprecated_warning(/variable \$= is no longer effective/) { $= } assert_equal(false, v) assert_deprecated_warning(/variable \$= is no longer effective; ignored/) { $= = nil } @@ -1068,6 +1069,7 @@ def test_last_match end def test_getter + omit "global variable access" if non_main_ractor? alias $__REGEXP_TEST_LASTMATCH__ $& alias $__REGEXP_TEST_PREMATCH__ $` alias $__REGEXP_TEST_POSTMATCH__ $' @@ -1630,8 +1632,12 @@ def test_unicode_age_16_0 "WHITE CROSS MARK..TOP LEFT JUSTIFIED LOWER RIGHT QUARTER BLACK CIRCLE") end - UnicodeAgeRegexps = Hash.new do |h, age| - h[age] = [/\A\p{age=#{age}}+\z/u, /\A\P{age=#{age}}+\z/u].freeze + def unicode_age_regexps + @unicode_age_regexps ||= begin + Hash.new do |h, age| + h[age] = [/\A\p{age=#{age}}+\z/u, /\A\P{age=#{age}}+\z/u] + end + end end def assert_unicode_age(char, mesg = nil, matches: @matches, unmatches: @unmatches) @@ -1640,13 +1646,13 @@ def assert_unicode_age(char, mesg = nil, matches: @matches, unmatches: @unmatche end matches.each do |age| - pos, neg = UnicodeAgeRegexps[age] + pos, neg = unicode_age_regexps[age] assert_match(pos, char, mesg) assert_not_match(neg, char, mesg) end unmatches.each do |age| - pos, neg = UnicodeAgeRegexps[age] + pos, neg = unicode_age_regexps[age] assert_not_match(pos, char, mesg) assert_match(neg, char, mesg) end @@ -1975,6 +1981,7 @@ def test_s_timeout_memory_leak end def test_bug_20453 + pend "Timeout" if non_main_ractor? re = Regexp.new("^(a*)x$", timeout: 0.001) assert_raise(Regexp::TimeoutError) do @@ -1983,6 +1990,7 @@ def test_bug_20453 end def test_bug_20886 + pend "Timeout" if non_main_ractor? re = Regexp.new("d()*+|a*a*bc", timeout: 0.02) assert_raise(Regexp::TimeoutError) do re === "b" + "a" * 1000 diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb index 13e707639123e9..ac18d7bb67425e 100644 --- a/test/ruby/test_require.rb +++ b/test/ruby/test_require.rb @@ -6,6 +6,7 @@ class TestRequire < Test::Unit::TestCase def test_load_error_path + pend "Tempfile" if non_main_ractor? Tempfile.create(["should_not_exist", ".rb"]) {|t| filename = t.path t.close @@ -30,6 +31,7 @@ def test_load_error_path end def test_require_invalid_shared_object + pend "Tempfile" if non_main_ractor? Tempfile.create(["test_ruby_test_require", ".so"]) {|t| t.puts "dummy" t.close @@ -105,6 +107,7 @@ def self.ospath_encoding(path) end def prepare_require_path(dir, encoding) + omit "global variable access" if non_main_ractor? require 'enc/trans/single_byte' Dir.mktmpdir {|tmp| begin @@ -168,6 +171,7 @@ def test_require_path_home_2 end def test_require_path_home_3 + pend "Tempfile" if non_main_ractor? env_rubypath, env_home = ENV["RUBYPATH"], ENV["HOME"] Tempfile.create(["test_ruby_test_require", ".rb"]) {|t| @@ -187,6 +191,7 @@ def test_require_path_home_3 end def test_require_with_unc + pend "Tempfile" if non_main_ractor? Tempfile.create(["test_ruby_test_require", ".rb"]) {|t| t.puts "puts __FILE__" t.close @@ -217,6 +222,7 @@ def test_require_twice end def assert_syntax_error_backtrace + omit "global variable access" if non_main_ractor? loaded_features = $LOADED_FEATURES.dup Dir.mktmpdir do |tmp| req = File.join(tmp, "test.rb") @@ -228,7 +234,7 @@ def assert_syntax_error_backtrace assert_not_empty(bt.find_all {|b| b.start_with? __FILE__}, proc {bt.inspect}) end ensure - $LOADED_FEATURES.replace loaded_features + $LOADED_FEATURES.replace loaded_features if main_ractor? end def test_require_syntax_error @@ -340,6 +346,7 @@ class Socket < BasicSocket; end end def test_load + pend "Tempfile" if non_main_ractor? Tempfile.create(["test_ruby_test_require", ".rb"]) {|t| t.puts "module Foo; end" t.puts "at_exit { p :wrap_end }" @@ -371,6 +378,7 @@ def test_require_in_wrapped_load end def test_public_in_wrapped_load + pend "Tempfile" if non_main_ractor? Tempfile.create(["test_public_in_wrapped_load", ".rb"]) do |t| t.puts "def foo; end", "public :foo" t.close @@ -381,6 +389,7 @@ def test_public_in_wrapped_load end def test_private_in_wrapped_load + pend "Tempfile" if non_main_ractor? Tempfile.create(["test_private_in_wrapped_load", ".rb"]) do |t| t.puts "def foo; end", "private :foo" t.close @@ -391,6 +400,7 @@ def test_private_in_wrapped_load end def test_load_scope + pend "Tempfile" if non_main_ractor? bug1982 = '[ruby-core:25039] [Bug #1982]' Tempfile.create(["test_ruby_test_require", ".rb"]) {|t| t.puts "Hello = 'hello'" @@ -406,6 +416,7 @@ def test_load_scope end def test_load_into_module + pend "Tempfile" if non_main_ractor? Tempfile.create(["test_ruby_test_require", ".rb"]) {|t| t.puts "def b; 1 end" t.puts "class Foo" @@ -460,6 +471,7 @@ def test_load_ospath end def test_relative + omit "global variable access" if non_main_ractor? load_path = $:.dup loaded_featrures = $LOADED_FEATURES.dup @@ -481,8 +493,10 @@ def test_relative end end ensure - $:.replace(load_path) if load_path - $LOADED_FEATURES.replace loaded_featrures + if main_ractor? + $:.replace(load_path) if load_path + $LOADED_FEATURES.replace loaded_featrures + end end def test_relative_symlink @@ -538,6 +552,7 @@ def test_frozen_loaded_features end def test_race_exception + pend "Tempfile" if non_main_ractor? bug5754 = '[ruby-core:41618]' path = nil stderr = $stderr @@ -602,12 +617,15 @@ class << (output = "") assert_equal([:pre, :post], scratch, bug5754) } ensure - $VERBOSE = verbose - $stderr = stderr - $".delete(path) + if main_ractor? + $VERBOSE = verbose + $stderr = stderr + $".delete(path) + end end def test_loaded_features_encoding + omit "global variable access" if non_main_ractor? bug6377 = '[ruby-core:44750]' loadpath = $:.dup features = $".dup @@ -620,8 +638,10 @@ def test_loaded_features_encoding assert_send([Encoding, :compatible?, tmp, $"[0]], bug6377) } ensure - $:.replace(loadpath) - $".replace(features) + if main_ractor? + $:.replace(loadpath) + $".replace(features) + end end def test_default_loaded_features_encoding @@ -820,6 +840,7 @@ def test_require_local_var_on_toplevel end def test_require_with_loaded_features_pop + pend "Tempfile" if non_main_ractor? bug7530 = '[ruby-core:50645]' Tempfile.create(%w'bug-7530- .rb') {|script| script.close @@ -843,6 +864,7 @@ def test_require_with_loaded_features_pop end def test_loading_fifo_threading_raise + pend "Tempfile" if non_main_ractor? Tempfile.create(%w'fifo .rb') {|f| f.close File.unlink(f.path) @@ -860,6 +882,7 @@ def test_loading_fifo_threading_raise def test_loading_fifo_threading_success omit "[Bug #18613]" if /freebsd/=~ RUBY_PLATFORM + pend "Tempfile" if non_main_ractor? Tempfile.create(%w'fifo .rb') {|f| f.close @@ -888,6 +911,7 @@ def test_loading_fifo_threading_success def test_loading_fifo_fd_leak omit if RUBY_PLATFORM =~ /android/ # https://rubyci.org/logs/rubyci.s3.amazonaws.com/android29-x86_64/ruby-master/log/20200419T124100Z.fail.html.gz + pend "Tempfile" if non_main_ractor? Tempfile.create(%w'fifo .rb') {|f| f.close @@ -913,6 +937,7 @@ def test_loading_fifo_fd_leak end if File.respond_to?(:mkfifo) and defined?(Process::RLIMIT_NOFILE) def test_throw_while_loading + pend "Tempfile" if non_main_ractor? Tempfile.create(%w'bug-11404 .rb') do |f| f.puts 'sleep' f.close @@ -957,6 +982,7 @@ def test_symlink_load_path end def test_provide_in_required_file + omit "global variable access" if non_main_ractor? paths, loaded = $:.dup, $".dup Dir.mktmpdir do |tmp| provide = File.realdirpath("provide.rb", tmp) @@ -969,12 +995,15 @@ def test_provide_in_required_file assert_equal($".pop, "target.rb") end ensure - $:.replace(paths) - $".replace(loaded) + if main_ractor? + $:.replace(paths) + $".replace(loaded) + end end if defined?($LOAD_PATH.resolve_feature_path) def test_resolve_feature_path + pend "Tempfile" if non_main_ractor? paths, loaded = $:.dup, $".dup Dir.mktmpdir do |tmp| Tempfile.create(%w[feature .rb], tmp) do |file| @@ -988,11 +1017,14 @@ def test_resolve_feature_path end end ensure - $:.replace(paths) - $".replace(loaded) + if main_ractor? + $:.replace(paths) + $".replace(loaded) + end end def test_resolve_feature_path_with_missing_feature + omit "global variable access" if non_main_ractor? assert_nil($LOAD_PATH.resolve_feature_path("superkalifragilisticoespialidoso")) end end diff --git a/test/ruby/test_require_lib.rb b/test/ruby/test_require_lib.rb index 44dfbcf9ec27a5..522444b2a30ab6 100644 --- a/test/ruby/test_require_lib.rb +++ b/test/ruby/test_require_lib.rb @@ -15,6 +15,10 @@ class TestRequireLib < Test::Unit::TestCase # skip some problems scripts -= %w[bundler bundled_gems rubygems mkmf set/sorted_set] + def setup + omit "separate process" if non_main_ractor? + end + scripts.each do |lib| define_method "test_thread_size:#{lib}" do assert_separately(['-W0'], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 60) diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb index b6c76ac73a662e..4982229cd15c2d 100644 --- a/test/ruby/test_rubyoptions.rb +++ b/test/ruby/test_rubyoptions.rb @@ -43,6 +43,10 @@ def with_tmpchdir } end + def setup + omit "separate process" if non_main_ractor? + end + def test_source_file assert_in_out_err([], "", [], []) end diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index fac6dd818533d0..6c7719839cf526 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -1535,6 +1535,7 @@ class C9759 end def test_define_method_on_exception + pend "ractor bug?" if non_main_ractor? events = [] obj = C9759.new TracePoint.new(:call, :return){|tp| @@ -1563,12 +1564,12 @@ def test_define_method_on_exception end class C11492 - define_method(:foo_return){ + define_method(:foo_return, &Ractor.make_shareable(proc{ return true - } - define_method(:foo_break){ + })) + define_method(:foo_break, &Ractor.make_shareable(proc{ break true - } + })) end def test_define_method_on_return @@ -2103,29 +2104,29 @@ def test_return_value_with_rescue '[Bug #13369]' end - define_method(:f_last_defined) do + define_method(:f_last_defined, &Ractor.make_shareable(proc do :f_last_defined - end + end)) - define_method(:f_return_defined) do + define_method(:f_return_defined, &Ractor.make_shareable(proc do return :f_return_defined - end + end)) - define_method(:f_break_defined) do + define_method(:f_break_defined, &Ractor.make_shareable(proc do break :f_break_defined - end + end)) - define_method(:f_raise_defined) do + define_method(:f_raise_defined, &Ractor.make_shareable(proc do raise rescue return :f_raise_defined - end + end)) - define_method(:f_break_in_rescue_defined) do + define_method(:f_break_in_rescue_defined, &Ractor.make_shareable(proc do raise rescue break :f_break_in_rescue_defined - end + end)) def test_return_value_with_rescue_and_defined_methods assert_equal [[:b_return, :f_last_defined, :f_last_defined], @@ -2154,13 +2155,13 @@ def test_return_value_with_rescue_and_defined_methods '[Bug #13369]' end - define_method(:just_yield) do |&block| + define_method(:just_yield, &Ractor.make_shareable(proc do |&block| block.call - end + end)) - define_method(:unwind_multiple_bmethods) do + define_method(:unwind_multiple_bmethods, &Ractor.make_shareable(proc do just_yield { return :unwind_multiple_bmethods } - end + end)) def test_non_local_return_across_multiple_define_methods assert_equal [[:b_return, :unwind_multiple_bmethods, nil], diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb index 77bba6421baee0..de484a9e576425 100644 --- a/test/ruby/test_shapes.rb +++ b/test/ruby/test_shapes.rb @@ -125,6 +125,7 @@ def test_too_complex end def test_ordered_alloc_is_not_complex + pend "ObjectSpace.dump" if non_main_ractor? 5.times { OrderedAlloc.new.add_ivars } obj = JSON.parse(ObjectSpace.dump(OrderedAlloc)) assert_operator obj["variation_count"], :<, RubyVM::Shape::SHAPE_MAX_VARIATIONS @@ -147,6 +148,8 @@ class Hi; end end def test_too_many_ivs_on_class + # could be safe due to not having a constant attached + pend "class ivars" obj = Class.new obj.instance_variable_set(:@test_too_many_ivs_on_class, 1) @@ -160,6 +163,8 @@ def test_too_many_ivs_on_class end def test_removing_when_too_many_ivs_on_class + # could be safe due to not having a constant attached + pend "class ivars" obj = Class.new (MANY_IVS + 2).times do @@ -173,6 +178,8 @@ def test_removing_when_too_many_ivs_on_class end def test_removing_when_too_many_ivs_on_module + # could be safe due to not having a constant attached + pend "module ivars" if non_main_ractor? obj = Module.new (MANY_IVS + 2).times do diff --git a/test/ruby/test_signal.rb b/test/ruby/test_signal.rb index a2bdf02b88522f..456cd8476ec34e 100644 --- a/test/ruby/test_signal.rb +++ b/test/ruby/test_signal.rb @@ -5,6 +5,7 @@ class TestSignal < Test::Unit::TestCase def test_signal + omit "signals run in main ractor" if non_main_ractor? begin x = 0 oldtrap = Signal.trap(:INT) {|sig| x = 2 } @@ -39,6 +40,7 @@ def test_signal_process_group Process.respond_to?(:pgroup) # for mswin32 def test_exit_action + pend "Timeout" if non_main_ractor? if Signal.list[sig = "USR1"] term = :TERM else @@ -97,6 +99,7 @@ def test_interrupt end def test_signal2 + pend "Timeout" if non_main_ractor? begin x = false oldtrap = Signal.trap(:INT) {|sig| x = true } @@ -169,11 +172,11 @@ def o.to_str; "SIGINT"; end end end if Process.respond_to?(:kill) - %w"KILL STOP".each do |sig| + Ractor.make_shareable(%w"KILL STOP").each do |sig| if Signal.list.key?(sig) - define_method("test_trap_uncatchable_#{sig}") do + define_method("test_trap_uncatchable_#{sig}", &Ractor.make_shareable(proc do assert_raise(Errno::EINVAL, "SIG#{sig} is not allowed to be caught") { Signal.trap(sig) {} } - end + end)) end end @@ -260,6 +263,7 @@ def test_trap_puts end if Process.respond_to?(:kill) def test_hup_me + pend "Timeout" if non_main_ractor? # [Bug #7951] [ruby-core:52864] # This is MRI specific spec. ruby has no guarantee # that signal will be deliverd synchronously. diff --git a/test/ruby/test_sprintf_comb.rb b/test/ruby/test_sprintf_comb.rb index 41131130309fdd..715cb414af2b9d 100644 --- a/test/ruby/test_sprintf_comb.rb +++ b/test/ruby/test_sprintf_comb.rb @@ -115,6 +115,10 @@ def self.combination(*args, &b) AllPairs.each(*args, &b) end + def setup + omit "unshareable procs" if non_main_ractor? + end + def emu_int(format, v) /\A%( )?(\#)?(\+)?(-)?(0)?(\d+)?(?:\.(\d*))?(.)\z/ =~ format sp = $1 diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index c7e4b0c1ec7fee..f287637892fa25 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -5,7 +5,7 @@ class TestString < Test::Unit::TestCase WIDE_ENCODINGS = [ Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE, - ] + ].freeze def initialize(*args) @cls = String @@ -399,7 +399,7 @@ def test_capitalize! end - Bug2463 = '[ruby-dev:39856]' + Bug2463 = '[ruby-dev:39856]'.freeze def test_center assert_equal(S("hello"), S("hello").center(4)) assert_equal(S(" hello "), S("hello").center(11)) @@ -673,6 +673,7 @@ def test_concat_literals def test_string_interpolations_across_heaps_get_embedded omit if GC::INTERNAL_CONSTANTS[:HEAP_COUNT] == 1 + omit "ObjectSpace.dump" if non_main_ractor? require 'objspace' base_slot_size = GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] @@ -738,12 +739,15 @@ def test_crypt assert_raise(ArgumentError) {S("mypassword".encode(enc)).crypt(S("aa"))} end - @cls == String and + #pend "String#crypt unsafe?" + if main_ractor? + @cls == String and assert_no_memory_leak([], "s = ''; salt_proc = proc{#{(crypt_supports_des_crypt? ? '..' : good_salt).inspect}}", "#{<<~"begin;"}\n#{<<~'end;'}") - begin; - 1000.times { s.crypt(-salt_proc.call).clear } - end; + begin; + 1000.times { s.crypt(-salt_proc.call).clear } + end; + end end def test_delete @@ -1804,6 +1808,7 @@ def test_slice! end def test_split + omit "global variable access" if non_main_ractor? fs, $; = $;, nil assert_equal([S("a"), S("b"), S("c")], S(" a b\t c ").split) assert_equal([S("a"), S("b"), S("c")], S(" a b\t c ").split(S(" "))) @@ -1827,10 +1832,11 @@ def test_split assert_equal([], S("").split(//, 1)) ensure - EnvUtil.suppress_warning {$; = fs} + EnvUtil.suppress_warning {$; = fs} if main_ractor? end def test_split_with_block + omit "global variable access" if non_main_ractor? fs, $; = $;, nil result = []; S(" a b\t c ").split {|s| result << s} assert_equal([S("a"), S("b"), S("c")], result) @@ -1877,10 +1883,11 @@ def test_split_with_block end } ensure - EnvUtil.suppress_warning {$; = fs} + EnvUtil.suppress_warning {$; = fs} if main_ractor? end def test_fs + omit "global variable access" if non_main_ractor? return unless @cls == String assert_raise_with_message(TypeError, /\$;/) { @@ -3391,10 +3398,12 @@ def test_uplus_minus assert_same(str, +str) assert_not_same(str, -str) - require 'objspace' - str = "test_uplus_minus_str".freeze - assert_includes ObjectSpace.dump(str), '"fstring":true' + if main_ractor? + require 'objspace' + + assert_includes ObjectSpace.dump(str), '"fstring":true' + end assert_predicate(str, :frozen?) assert_not_predicate(+str, :frozen?) @@ -3403,8 +3412,10 @@ def test_uplus_minus assert_not_same(str, +str) assert_same(str, -str) - bar = -%w(test uplus minus str).join('_') - assert_same(str, bar, "uminus deduplicates [Feature #13077] str: #{ObjectSpace.dump(str)} bar: #{ObjectSpace.dump(bar)}") + if main_ractor? + bar = -%w(test uplus minus str).join('_') + assert_same(str, bar, "uminus deduplicates [Feature #13077] str: #{ObjectSpace.dump(str)} bar: #{ObjectSpace.dump(bar)}") + end end def test_uminus_dedup_in_place diff --git a/test/ruby/test_string_memory.rb b/test/ruby/test_string_memory.rb index a93a3bd54afcea..ac260a818b55b1 100644 --- a/test/ruby/test_string_memory.rb +++ b/test/ruby/test_string_memory.rb @@ -3,6 +3,10 @@ require 'objspace' class TestStringMemory < Test::Unit::TestCase + def setup + omit "ObjectSpace.trace_object_allocations" if non_main_ractor? + end + def capture_allocations(klass) allocations = [] diff --git a/test/ruby/test_stringchar.rb b/test/ruby/test_stringchar.rb index e13beef69c1883..aa8983f31dafb0 100644 --- a/test/ruby/test_stringchar.rb +++ b/test/ruby/test_stringchar.rb @@ -41,9 +41,11 @@ def test_string assert_match(/foo(?=(bar)|(baz))/, "foobar") assert_match(/foo(?=(bar)|(baz))/, "foobaz") - $foo = "abc" - assert_equal("abc = abc", "#$foo = abc") - assert_equal("abc = abc", "#{$foo} = abc") + if main_ractor? + $foo = "abc" + assert_equal("abc = abc", "#$foo = abc") + assert_equal("abc = abc", "#{$foo} = abc") + end foo = "abc" assert_equal("abc = abc", "#{foo} = abc") diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb index db591c306e556e..6a659276076059 100644 --- a/test/ruby/test_struct.rb +++ b/test/ruby/test_struct.rb @@ -406,6 +406,7 @@ def test_junk end def test_comparison_when_recursive + pend "Timeout" if non_main_ractor? klass1 = @Struct.new(:a, :b, :c) x = klass1.new(1, 2, nil); x.c = x diff --git a/test/ruby/test_super.rb b/test/ruby/test_super.rb index 8e973b0f7f4a08..b6abea1c54d162 100644 --- a/test/ruby/test_super.rb +++ b/test/ruby/test_super.rb @@ -214,11 +214,13 @@ def obj.reverse str.reverse end - assert_nothing_raised('[ruby-core:27230]') do - mid=Indexed.new - mid.instance_eval(&Overlaid) - mid.subseq - mid.subseq + if main_ractor? + assert_nothing_raised('[ruby-core:27230]') do + mid=Indexed.new + mid.instance_eval(&Overlaid) + mid.subseq + mid.subseq + end end end @@ -427,9 +429,9 @@ def foo(*args) end class Y < X - define_method(:foo) do |*args| + define_method(:foo, &Ractor.make_shareable(proc do |*args| super(*args) - end + end)) end def test_super_splat @@ -620,6 +622,7 @@ def foo end def test_public_zsuper_with_prepend + pend "Timeout" if non_main_ractor? bug12876 = '[ruby-core:77784] [Bug #12876]' m = EnvUtil.labeled_module("M") c = EnvUtil.labeled_class("C") {prepend m; public :initialize} diff --git a/test/ruby/test_symbol.rb b/test/ruby/test_symbol.rb index c50febf5d1c6fa..c24b8049648bf8 100644 --- a/test/ruby/test_symbol.rb +++ b/test/ruby/test_symbol.rb @@ -214,14 +214,17 @@ def hoge end } def test_to_proc_arg_with_refinements + omit "ractor_confirm_belonging issue" if non_main_ractor? assert_equal(:hoge, _test_to_proc_arg_with_refinements_call(&:hoge)) end def test_to_proc_lambda_with_refinements + omit "ractor_confirm_belonging issue" if non_main_ractor? assert_predicate(_test_to_proc_with_refinements_call(&:hoge), :lambda?) end def test_to_proc_arity_with_refinements + omit "ractor_confirm_belonging issue" if non_main_ractor? assert_equal(-2, _test_to_proc_with_refinements_call(&:hoge).arity) end @@ -492,10 +495,10 @@ def test_singleton_method assert_raise(TypeError) { a = :foo; def a.foo; end } end - SymbolsForEval = [ + SymbolsForEval = Ractor.make_shareable([ :foo, "dynsym_#{Random.rand(10000)}_#{Time.now}".to_sym - ] + ]) def test_instance_eval bug11086 = '[ruby-core:68961] [Bug #11086]' diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index b7e021a4ff4976..1e9176103e2eac 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -32,6 +32,7 @@ def test_defined_empty_argument end def test_must_ascii_compatible + pend "Tempfile" if non_main_ractor? require 'tempfile' f = Tempfile.new("must_ac_") Encoding.list.each do |enc| @@ -49,6 +50,7 @@ def test_must_ascii_compatible end def test_script_lines + pend "Tempfile" if non_main_ractor? require 'tempfile' f = Tempfile.new("bug4361_") bug4361 = '[ruby-dev:43168]' @@ -220,7 +222,11 @@ def test_newline_in_block_parameters m = m.tr_s('()', ' ').strip if n3 == 'do' name = "test_#{n3}_block_after_blockcall_#{n1}_#{n2}_arg" code = "#{blockcall}#{c}#{m} #{b}" - define_method(name) {assert_valid_syntax(code, bug6115)} + class_eval <<-RUBY + def #{name} + assert_valid_syntax(#{code.inspect}, #{bug6115.inspect}) + end + RUBY end end @@ -830,6 +836,7 @@ def test_reserved_method_no_args end def test_unassignable + omit "global variable access" if non_main_ractor? gvar = global_variables %w[self nil true false __FILE__ __LINE__ __ENCODING__].each do |kwd| assert_syntax_error("#{kwd} = nil", /Can't .* #{kwd}$/) @@ -837,7 +844,7 @@ def test_unassignable end end - Bug7559 = '[ruby-dev:46737]' + Bug7559 = '[ruby-dev:46737]'.freeze def test_lineno_command_call_quote expected = __LINE__ + 1 @@ -1481,6 +1488,7 @@ def test_brace_after_literal_argument end def test_return_toplevel + pend "Tempfile" if non_main_ractor? feature4840 = '[ruby-core:36785] [Feature #4840]' line = __LINE__+2 code = "#{<<~"begin;"}#{<<~'end;'}" @@ -1530,6 +1538,7 @@ def test_return_toplevel end def test_eval_return_toplevel + pend "Tempfile" if non_main_ractor? feature4840 = '[ruby-core:36785] [Feature #4840]' line = __LINE__+2 code = "#{<<~"begin;"}#{<<~'end;'}" diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb index 12ba1165ed8817..a3b74aee186a38 100644 --- a/test/ruby/test_thread.rb +++ b/test/ruby/test_thread.rb @@ -6,20 +6,22 @@ class TestThread < Test::Unit::TestCase class Thread < ::Thread - Threads = [] + def self.Threads + Ractor.current[:__THREADS] ||= [] + end def self.new(*) th = super - Threads << th + (Ractor.current[:__THREADS] ||= []) << th th end end def setup - Thread::Threads.clear + (Ractor.current[:__THREADS] ||= []).clear end def teardown - Thread::Threads.each do |t| + Thread.Threads.each do |t| t.kill if t.alive? begin t.join @@ -148,6 +150,7 @@ def test_mutex_synchronize_yields_no_block_params end def test_local_barrier + omit "global variable access" if non_main_ractor? dir = File.dirname(__FILE__) lbtest = File.join(dir, "lbtest.rb") $:.unshift File.join(File.dirname(dir), 'ruby') @@ -255,12 +258,14 @@ def test_join_argument_conversion { 'FIXNUM_MAX' => RbConfig::LIMITS['FIXNUM_MAX'], 'UINT64_MAX' => RbConfig::LIMITS['UINT64_MAX'], - 'INFINITY' => Float::INFINITY + 'INFINITY' => 'Float::INFINITY' }.each do |name, limit| - define_method("test_join_limit_#{name}") do - t = Thread.new {} - assert_same t, t.join(limit), "limit=#{limit.inspect}" - end + class_eval <<-RUBY + def test_join_limit_#{name} + t = Thread.new {} + assert_same t, t.join(#{limit}), %q(limit=#{limit.inspect}) + end + RUBY end { 'minus_1' => -1, @@ -269,7 +274,8 @@ def test_join_argument_conversion 'INT64_MIN' => RbConfig::LIMITS['INT64_MIN'], 'minus_INFINITY' => -Float::INFINITY }.each do |name, limit| - define_method("test_join_limit_negative_#{name}") do + define_method("test_join_limit_negative_#{name}", &Ractor.make_shareable(proc do + pend "Timeout" if non_main_ractor? t = Thread.new { sleep } begin assert_nothing_raised(Timeout::Error) do @@ -280,7 +286,7 @@ def test_join_argument_conversion ensure t.kill end - end + end)) end def test_kill_main_thread @@ -924,6 +930,7 @@ def test_handle_interrupted? end def test_thread_timer_and_ensure + pend "Timeout" if non_main_ractor? assert_normal_exit(<<_eom, 'r36492', timeout: 10) flag = false t = Thread.new do @@ -976,6 +983,7 @@ def test_backtrace def test_thread_timer_and_interrupt omit "[Bug #18613]" if /freebsd/ =~ RUBY_PLATFORM + pend "Timeout" if non_main_ractor? bug5757 = '[ruby-dev:44985]' pid = nil @@ -1235,11 +1243,12 @@ def test_blocking_mutex_unlocked_on_fork end if Process.respond_to?(:fork) def test_fork_in_thread + pend "fork ractor_confirm_belonging issue" if non_main_ractor? bug9751 = '[ruby-core:62070] [Bug #9751]' f = nil th = Thread.start do unless f = IO.popen("-") - STDERR.reopen(STDOUT) + $stderr.reopen($stdout) exit end Process.wait2(f.pid) @@ -1304,6 +1313,7 @@ def test_fork_while_parent_locked def test_fork_while_mutex_locked_by_forker omit 'needs fork' unless Process.respond_to?(:fork) + pend "Timeout" if non_main_ractor? m = Thread::Mutex.new m.synchronize do pid = fork do @@ -1480,6 +1490,7 @@ def test_thread_native_thread_id_across_fork_on_linux end def test_thread_interrupt_for_killed_thread + pend "Timeout" if non_main_ractor? opts = { timeout: 5, timeout_error: nil } assert_normal_exit(<<-_end, '[Bug #8996]', **opts) diff --git a/test/ruby/test_thread_cv.rb b/test/ruby/test_thread_cv.rb index eb88b9606cebf1..9c825079909d1d 100644 --- a/test/ruby/test_thread_cv.rb +++ b/test/ruby/test_thread_cv.rb @@ -55,6 +55,7 @@ def test_condvar_wait_exception_handling end def test_condvar_wait_and_broadcast + pend "Timeout" if non_main_ractor? nr_threads = 3 threads = Array.new mutex = Thread::Mutex.new @@ -84,8 +85,8 @@ def test_condvar_wait_and_broadcast assert_equal ["C1", "C1", "C1", "P1", "P2", "C2", "C2", "C2"], result ensure - threads.each(&:kill) - threads.each(&:join) + threads&.each(&:kill) + threads&.each(&:join) end def test_condvar_wait_deadlock @@ -109,6 +110,7 @@ def test_condvar_wait_deadlock end def test_condvar_wait_deadlock_2 + pend "Timeout" if non_main_ractor? nr_threads = 3 threads = Array.new mutex = Thread::Mutex.new diff --git a/test/ruby/test_thread_queue.rb b/test/ruby/test_thread_queue.rb index 9485528977e599..756dfcd6da5a31 100644 --- a/test/ruby/test_thread_queue.rb +++ b/test/ruby/test_thread_queue.rb @@ -317,6 +317,7 @@ def test_sized_queue_throttle end def test_queue_thread_raise + pend "Timeout" if non_main_ractor? q = Thread::Queue.new th1 = Thread.new do begin diff --git a/test/ruby/test_time.rb b/test/ruby/test_time.rb index 333edb80218a64..9cb52a6d92423d 100644 --- a/test/ruby/test_time.rb +++ b/test/ruby/test_time.rb @@ -209,6 +209,7 @@ def negative_time_t? end def test_timegm + pend "accesses current_repeat_count" if non_main_ractor? if negative_time_t? assert_equal(-0x80000000, Time.utc(1901, 12, 13, 20, 45, 52).tv_sec) assert_equal(-2, Time.utc(1969, 12, 31, 23, 59, 58).tv_sec) @@ -472,7 +473,7 @@ def test_marshal_to_s "[ruby-dev:44827] [Bug #5586]") end - Bug8795 = '[ruby-core:56648] [Bug #8795]' + Bug8795 = '[ruby-core:56648] [Bug #8795]'.freeze def test_marshal_broken_offset data = "\x04\bIu:\tTime\r\xEFF\x1C\x80\x00\x00\x00\x00\x06:\voffset" @@ -570,6 +571,7 @@ def o.to_int; 10; end end def test_time_interval + pend "Timeout" if non_main_ractor? m = Thread::Mutex.new.lock assert_nothing_raised { Timeout.timeout(10) { @@ -1293,6 +1295,7 @@ def test_1970 end def test_2038 + pend "accesses current_repeat_count" if non_main_ractor? # Giveup to try 2nd test because some state is changed. omit if Test::Unit::Runner.current_repeat_count > 0 diff --git a/test/ruby/test_time_tz.rb b/test/ruby/test_time_tz.rb index f66cd9bec2eedc..3bb018d80370c4 100644 --- a/test/ruby/test_time_tz.rb +++ b/test/ruby/test_time_tz.rb @@ -88,7 +88,7 @@ def group_by(e, &block) CORRECT_TOKYO_DST_1951 = with_tz("Asia/Tokyo") { if Time.local(1951, 5, 6, 12, 0, 0).dst? # noon, DST if Time.local(1951, 5, 6, 1, 0, 0).dst? # DST with fixed tzdata - Time.local(1951, 9, 8, 23, 0, 0).dst? ? "2018f" : "2018e" + Time.local(1951, 9, 8, 23, 0, 0).dst? ? "2018f".freeze : "2018e".freeze end end } @@ -96,7 +96,7 @@ def group_by(e, &block) Time.local(1994, 12, 31, 0, 0, 0).year == 1995 } CORRECT_SINGAPORE_1982 = with_tz("Asia/Singapore") { - "2022g" if Time.local(1981, 12, 31, 23, 59, 59).utc_offset == 8*3600 + "2022g".freeze if Time.local(1981, 12, 31, 23, 59, 59).utc_offset == 8*3600 } def time_to_s(t) @@ -379,20 +379,23 @@ def self.gen_zdump_test(data) expected = "%04d-%02d-%02d %02d:%02d:%02d %s" % (l+[format_gmtoff(gmtoff)]) mesg_utc = "TZ=#{tz} Time.utc(#{u.map {|arg| arg.inspect }.join(', ')})" mesg = "#{mesg_utc}.localtime" - define_method(gen_test_name(tz)) { - with_tz(tz) { - t = nil - assert_nothing_raised(mesg) { t = Time.utc(*u) } - assert_equal(expected_utc, time_to_s(t), mesg_utc) - assert_nothing_raised(mesg) { t.localtime } - assert_equal(expected, time_to_s(t), mesg) - assert_equal(gmtoff, t.gmtoff) - assert_equal(format_gmtoff(gmtoff), t.strftime("%z")) - assert_equal(format_gmtoff(gmtoff, true), t.strftime("%:z")) - assert_equal(format_gmtoff2(gmtoff), t.strftime("%::z")) - assert_equal(Encoding::US_ASCII, t.zone.encoding) - } - } + class_eval <<-RUBY, __FILE__, __LINE__+1 + def #{gen_test_name(tz).gsub(/[@\/]/, '_')} + tz = #{tz.inspect} + with_tz(tz) { + t = nil + assert_nothing_raised(#{mesg.inspect}) { t = Time.utc(*#{u.inspect}) } + assert_equal(#{expected_utc.inspect}, time_to_s(t), #{mesg_utc.inspect}) + assert_nothing_raised(#{mesg.inspect}) { t.localtime } + assert_equal(#{expected.inspect}, time_to_s(t), #{mesg.inspect}) + assert_equal(#{gmtoff.inspect}, t.gmtoff) + assert_equal(format_gmtoff(#{gmtoff.inspect}), t.strftime("%z")) + assert_equal(format_gmtoff(#{gmtoff.inspect}, true), t.strftime("%:z")) + assert_equal(format_gmtoff2(#{gmtoff.inspect}), t.strftime("%::z")) + assert_equal(Encoding::US_ASCII, t.zone.encoding) + } + end + RUBY } group_by(sample) {|tz, _, _, _| tz }.each {|tz, a| @@ -401,7 +404,11 @@ def self.gen_zdump_test(data) monotonic_to_past = i == 0 || (a[i-1][2] <=> l) < 0 monotonic_to_future = i == a.length-1 || (l <=> a[i+1][2]) < 0 if monotonic_to_past && monotonic_to_future - define_method(gen_test_name(tz)) { + class_eval <<-RUBY, __FILE__, __LINE__+1 + def #{gen_test_name(tz).gsub(/[@\/]/, '_')} + expected = #{expected.inspect} + tz = #{tz.inspect} + l = #{l.inspect} with_tz(tz) { assert_time_constructor(tz, expected, :local, l) assert_time_constructor(tz, expected, :local, l.reverse+[nil, nil, false, nil]) @@ -410,25 +417,40 @@ def self.gen_zdump_test(data) assert_time_constructor(tz, expected, :new, l+[:std]) assert_time_constructor(tz, expected, :new, l+[:dst]) } - } + end + RUBY elsif monotonic_to_past && !monotonic_to_future - define_method(gen_test_name(tz)) { + class_eval <<-RUBY, __FILE__, __LINE__+1 + def #{gen_test_name(tz).gsub(/[@\/]/, '_')} + expected = #{expected.inspect} + tz = #{tz.inspect} + l = #{l.inspect} with_tz(tz) { assert_time_constructor(tz, expected, :local, l.reverse+[nil, nil, true, nil]) assert_time_constructor(tz, expected, :new, l+[:dst]) } - } + end + RUBY elsif !monotonic_to_past && monotonic_to_future - define_method(gen_test_name(tz)) { + class_eval <<-RUBY, __FILE__, __LINE__+1 + def #{gen_test_name(tz).gsub(/[@\/]/, '_')} + expected = #{expected.inspect} + tz = #{tz.inspect} + l = #{l.inspect} with_tz(tz) { assert_time_constructor(tz, expected, :local, l.reverse+[nil, nil, false, nil]) assert_time_constructor(tz, expected, :new, l+[:std]) } - } + end + RUBY else - define_method(gen_test_name(tz)) { - flunk("time in reverse order: TZ=#{tz} #{expected}") - } + class_eval <<-RUBY, __FILE__, __LINE__+1 + def #{gen_test_name(tz).gsub(/[@\/]/, '_')} + expected = #{expected.inspect} + tz = #{tz.inspect} + flunk("time in reverse order: TZ=\#{tz} \#{expected}") + end + RUBY end } } @@ -548,13 +570,15 @@ def self.gen_variational_zdump_test(hint, data) } end - # tzdata-2014g fixed the offset for lisbon from -0:36:32 to -0:36:45. - # [ruby-core:65058] [Bug #10245] - gen_variational_zdump_test "lisbon", <<'End' if has_lisbon_tz + unless will_run_in_non_main_ractor? + # tzdata-2014g fixed the offset for lisbon from -0:36:32 to -0:36:45. + # [ruby-core:65058] [Bug #10245] + gen_variational_zdump_test "lisbon", <<'End' if has_lisbon_tz Europe/Lisbon Mon Jan 1 00:36:31 1912 UTC = Sun Dec 31 23:59:59 1911 LMT isdst=0 gmtoff=-2192 Europe/Lisbon Mon Jan 1 00:36:44 1912 UT = Sun Dec 31 23:59:59 1911 LMT isdst=0 gmtoff=-2205 Europe/Lisbon Sun Dec 31 23:59:59 1911 UT = Sun Dec 31 23:23:14 1911 LMT isdst=0 gmtoff=-2205 End + end class TZ attr_reader :name @@ -722,12 +746,12 @@ def nametest_marshal_compatibility(time_class, tzname, abbr, utc_offset) # t.zone may be a mere String or timezone object. end - ZONES = { + ZONES = Ractor.make_shareable({ "Asia/Tokyo" => ["JST", +9*3600], "America/Los_Angeles" => ["PST", -8*3600, "PDT", -7*3600], "Africa/Ndjamena" => ["WAT", +1*3600], "Etc/UTC" => ["UTC", 0], - } + }) def make_timezone(tzname, abbr, utc_offset, abbr2 = nil, utc_offset2 = nil) self.class::TIME_CLASS.find_timezone(tzname) @@ -744,22 +768,36 @@ def subtest_dst?(time_class, tz, tzarg, tzname, abbr, utc_offset) instance_methods(false).grep(/\Asub(?=test_)/) do |subtest| test = $' ZONES.each_pair do |tzname, (abbr, utc_offset, abbr2, utc_offset2)| - define_method("#{test}@#{tzname}") do + class_eval <<-RUBY, __FILE__, __LINE__+1 + def #{(test + '_' + tzname).gsub(/[^\w]/, '_')} + subtest = #{subtest.inspect} + tzname = #{tzname.inspect} + abbr = #{abbr.inspect} + utc_offset = #{utc_offset.inspect} + abbr2 = #{abbr2.inspect} + utc_offset2 = #{utc_offset2.inspect} tz = make_timezone(tzname, abbr, utc_offset, abbr2, utc_offset2) time_class = self.class::TIME_CLASS __send__(subtest, time_class, tz, tz, tzname, [abbr, abbr2], [utc_offset, utc_offset2]) __send__(subtest, time_class, tz, tzname, tzname, [abbr, abbr2], [utc_offset, utc_offset2]) end + RUBY end end instance_methods(false).grep(/\Aname(?=test_)/) do |subtest| test = $' ZONES.each_pair do |tzname, (abbr, utc_offset)| - define_method("#{test}@#{tzname}") do + class_eval <<-RUBY, __FILE__, __LINE__+1 + def #{(test + '_' + tzname).gsub(/[^\w]/, '_')} + subtest = #{subtest.inspect} + tzname = #{tzname.inspect} + abbr = #{abbr.inspect} + utc_offset = #{utc_offset.inspect} time_class = self.class::TIME_CLASS __send__(subtest, time_class, tzname, abbr, utc_offset) end + RUBY end end end diff --git a/test/ruby/test_trace.rb b/test/ruby/test_trace.rb index 5842f11aee6007..9b987d90432c80 100644 --- a/test/ruby/test_trace.rb +++ b/test/ruby/test_trace.rb @@ -3,6 +3,7 @@ class TestTrace < Test::Unit::TestCase def test_trace + omit "global variable access" if non_main_ractor? $x = 1234 $y = 0 trace_var :$x, proc{$y = $x} @@ -21,20 +22,22 @@ def test_trace end def test_trace_proc_that_raises_exception + omit "global variable access" if non_main_ractor? $x = 1234 trace_var :$x, proc { raise } assert_raise(RuntimeError) { $x = 42 } ensure - untrace_var :$x + untrace_var :$x if main_ractor? end def test_trace_string + omit "global variable access" if non_main_ractor? $x = 1234 trace_var :$x, "$y = :bar" $x = 42 assert_equal(:bar, $y) ensure - untrace_var :$x + untrace_var :$x if main_ractor? end def test_trace_break diff --git a/test/ruby/test_variable.rb b/test/ruby/test_variable.rb index cc784e7644fdf3..e865149b528962 100644 --- a/test/ruby/test_variable.rb +++ b/test/ruby/test_variable.rb @@ -36,10 +36,12 @@ def ruler4 Athena = Gods.clone def test_cloned_classes_copy_cvar_cache + omit "class variables" if non_main_ractor? assert_equal "Cronus", Athena.new.ruler0 end def test_setting_class_variable_on_module_through_inheritance + omit "class variables" if non_main_ractor? mod = Module.new mod.class_variable_set(:@@foo, 1) mod.freeze @@ -52,6 +54,7 @@ def test_setting_class_variable_on_module_through_inheritance Zeus = Gods.clone def test_cloned_allows_setting_cvar + omit "class variables" if non_main_ractor? Zeus.class_variable_set(:@@rule, "Athena") god = Gods.new.ruler0 @@ -63,6 +66,7 @@ def test_cloned_allows_setting_cvar end def test_singleton_class_included_class_variable + omit "class variables" if non_main_ractor? c = Class.new c.extend(Olympians) assert_empty(c.singleton_class.class_variables) @@ -91,6 +95,7 @@ def test_singleton_class_included_class_variable end def test_cvar_overtaken_by_parent_class + omit "class variables" if non_main_ractor? error = eval <<~EORB class Parent end @@ -121,6 +126,7 @@ class Parent end def test_cvar_overtaken_by_module + omit "class variables" if non_main_ractor? error = eval <<~EORB class ParentForModule @@cvar = 1 @@ -169,12 +175,14 @@ def t end def test_include_refined_module_class_variable + omit "class variables" if non_main_ractor? assert_warning('') do IncludeRefinedModuleClassVariableNoWarning.new.t end end def test_set_class_variable_on_frozen_object + omit "class variables" if non_main_ractor? set_cvar = EnvUtil.labeled_class("SetCVar") set_cvar.class_eval "#{<<~"begin;"}\n#{<<~'end;'}" begin; @@ -190,6 +198,7 @@ def self.set(val) end def test_variable + omit "class variables" if non_main_ractor? assert_instance_of(Integer, $$) # read-only variable @@ -253,6 +262,7 @@ def test_shadowing_block_local_variables end def test_global_variables + omit "global variables" if non_main_ractor? gv = global_variables assert_empty(gv.grep(/\A(?!\$)/)) assert_nil($~) @@ -349,6 +359,7 @@ def test_global_variable_0 end def test_global_variable_popped + omit "global variables" if non_main_ractor? assert_nothing_raised { EnvUtil.suppress_warning { eval("$foo; 1") @@ -432,9 +443,11 @@ def test_many_instance_variables objects = [Object.new, Hash.new, Module.new] objects.each do |obj| 1000.times do |i| + next if obj.is_a?(Module) && non_main_ractor? obj.instance_variable_set("@var#{i}", i) end 1000.times do |i| + next if obj.is_a?(Module) && non_main_ractor? assert_equal(i, obj.instance_variable_get("@var#{i}")) end end diff --git a/test/ruby/test_whileuntil.rb b/test/ruby/test_whileuntil.rb index ff6d29ac4a9f89..79a4b60ebcd845 100644 --- a/test/ruby/test_whileuntil.rb +++ b/test/ruby/test_whileuntil.rb @@ -59,14 +59,16 @@ def test_while end assert_equal(220, sum) - tmp = open(tmpfilename, "r") - while line = tmp.gets() - break if $. == 3 - assert_no_match(/vt100/, line) - assert_no_match(/Amiga/, line) - assert_no_match(/paper/, line) + if main_ractor? + tmp = open(tmpfilename, "r") + while line = tmp.gets() + break if $. == 3 + assert_no_match(/vt100/, line) + assert_no_match(/Amiga/, line) + assert_no_match(/paper/, line) + end + tmp.close end - tmp.close File.unlink tmpfilename or `/bin/rm -f "#{tmpfilename}"` assert_file.not_exist?(tmpfilename) diff --git a/test/ruby/test_yield.rb b/test/ruby/test_yield.rb index 9b2b2f37e06e04..f1658b1211c5b9 100644 --- a/test/ruby/test_yield.rb +++ b/test/ruby/test_yield.rb @@ -89,7 +89,7 @@ def test_block_args_unleashed require_relative 'sentence' class TestRubyYieldGen < Test::Unit::TestCase - Syntax = { + Syntax = Ractor.make_shareable({ :exp => [["0"], ["nil"], ["false"], @@ -178,7 +178,7 @@ class TestRubyYieldGen < Test::Unit::TestCase :test_proc => [['def m() yield', :command_args_noblock, ' end; r = m {', :block_param_def, 'vars', '}; undef m; r']], :test_lambda => [['def m() yield', :command_args_noblock, ' end; r = m(&lambda {', :block_param_def, 'vars', '}); undef m; r']], :test_enum => [['o = Object.new; def o.each() yield', :command_args_noblock, ' end; r1 = r2 = nil; o.each {|*x| r1 = x }; o.to_enum.each {|*x| r2 = x }; [r1, r2]']] - } + }) def rename_var(obj) vars = [] diff --git a/test/ruby/test_yjit.rb b/test/ruby/test_yjit.rb index 25399d1e628f33..91aa8dd8099eb3 100644 --- a/test/ruby/test_yjit.rb +++ b/test/ruby/test_yjit.rb @@ -1802,7 +1802,7 @@ def assert_no_exits(script) assert_compiles(script) end - ANY = Object.new + ANY = Object.new.freeze def assert_compiles( test_script, insns: [], call_threshold: 1, diff --git a/test/test_open3.rb b/test/test_open3.rb index 19277c8a66eca6..59eb95ad2c6cb4 100644 --- a/test/test_open3.rb +++ b/test/test_open3.rb @@ -27,12 +27,14 @@ def test_stdin end def test_stdout + omit if non_main_ractor? Open3.popen3(RUBY, '-e', 'STDOUT.print "foo"') {|i,o,e,t| assert_equal("foo", o.read) } end def test_stderr + omit if non_main_ractor? Open3.popen3(RUBY, '-e', 'STDERR.print "bar"') {|i,o,e,t| assert_equal("bar", e.read) } @@ -118,13 +120,13 @@ def with_reopen(io, arg) def test_popen2 with_pipe {|r, w| - with_reopen(STDERR, w) {|old| + with_reopen($stderr, w) {|old| w.close Open3.popen2(RUBY, '-e', 's=STDIN.read; STDOUT.print s+"o"; STDERR.print s+"e"') {|i,o,t| assert_kind_of(Thread, t) i.print "z" i.close - STDERR.reopen(old) + $stderr.reopen(old) assert_equal("zo", o.read) assert_equal("ze", r.read) } @@ -134,13 +136,13 @@ def test_popen2 def test_popen2e with_pipe {|r, w| - with_reopen(STDERR, w) {|old| + with_reopen($stderr, w) {|old| w.close Open3.popen2e(RUBY, '-e', 's=STDIN.read; STDOUT.print s+"o"; STDOUT.flush; STDERR.print s+"e"') {|i,o,t| assert_kind_of(Thread, t) i.print "y" i.close - STDERR.reopen(old) + $stderr.reopen(old) assert_equal("yoye", o.read) assert_equal("", r.read) } diff --git a/test/test_singleton.rb b/test/test_singleton.rb index e474a0ccc596f2..2c5125cf0ca4ca 100644 --- a/test/test_singleton.rb +++ b/test/test_singleton.rb @@ -7,6 +7,10 @@ class SingletonTest include Singleton end + def setup + omit if non_main_ractor? + end + def test_marshal o1 = SingletonTest.instance m = Marshal.dump(o1) diff --git a/test/test_time.rb b/test/test_time.rb index 55964d02fc1e78..d0c1111a4c11a5 100644 --- a/test/test_time.rb +++ b/test/test_time.rb @@ -572,8 +572,16 @@ def test_huge_precision instance_methods(false).grep(/\Asub(test_xmlschema.*)/) do |sub| test = $1 - define_method(test) {__send__(sub, :xmlschema)} - define_method(test.sub(/xmlschema/, 'iso8601')) {__send__(sub, :iso8601)} + class_eval <<-RUBY + def #{test} + __send__(#{sub.inspect}, :xmlschema) + end + def #{test.sub(/xmlschema/, 'iso8601')} + __send__(#{sub.inspect}, :iso8601) + end + RUBY + #define_method(test) {__send__(sub, :xmlschema)} + #define_method(test.sub(/xmlschema/, 'iso8601')) {__send__(sub, :iso8601)} end def test_parse_with_various_object diff --git a/test/test_tmpdir.rb b/test/test_tmpdir.rb index c91fc334ed73db..9c09c66710f3a6 100644 --- a/test/test_tmpdir.rb +++ b/test/test_tmpdir.rb @@ -154,10 +154,10 @@ def test_ractor Ractor.receive end end - dir = r.take + dir = r.value assert_file.directory? dir r.send true - r.take + r.join assert_file.not_exist? dir end end; diff --git a/tool/lib/core_assertions.rb b/tool/lib/core_assertions.rb index 47cc6574c878d1..1436843d0bbb28 100644 --- a/tool/lib/core_assertions.rb +++ b/tool/lib/core_assertions.rb @@ -32,7 +32,7 @@ def filter bt end end - self.backtrace_filter = BacktraceFilter.new + self.backtrace_filter = BacktraceFilter.new.freeze def self.filter_backtrace bt # :nodoc: backtrace_filter.filter bt @@ -86,18 +86,21 @@ def mu_pp(obj) #:nodoc: end def assert_file - AssertFile + Ractor.current[:__AssertFile] ||= CoreAssertions.new_AssertFile end - FailDesc = proc do |status, message = "", out = ""| - now = Time.now - proc do - EnvUtil.failure_description(status, now, message, out) + FailDesc = Ractor.make_shareable(Ractor.current.instance_eval do + proc do |status, message = "", out = ""| + now = Time.now + proc do + EnvUtil.failure_description(status, now, message, out) + end end - end + end) def assert_in_out_err(args, test_stdin = "", test_stdout = [], test_stderr = [], message = nil, success: nil, failed: nil, **opt) + pend "#{__method__}" unless main_ractor? args = Array(args).dup args.insert((Hash === args[0] ? 1 : 0), '--disable=gems') stdout, stderr, status = EnvUtil.invoke_ruby(args, test_stdin, true, true, **opt) @@ -154,6 +157,7 @@ def syntax_check(code, fname, line) end def assert_no_memory_leak(args, prepare, code, message=nil, limit: 2.0, rss: false, **opt) + omit "separate process" if non_main_ractor? # TODO: consider choosing some appropriate limit for RJIT and stop skipping this once it does not randomly fail pend 'assert_no_memory_leak may consider RJIT memory usage as leak' if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? # For previous versions which implemented MJIT @@ -288,7 +292,7 @@ def assert_ruby_status(args, test_stdin="", message=nil, **opt) assert(status.success?, desc) end - ABORT_SIGNALS = Signal.list.values_at(*%w"ILL ABRT BUS SEGV TERM") + ABORT_SIGNALS = Signal.list.values_at(*%w"ILL ABRT BUS SEGV TERM").freeze def separated_runner(token, out = nil) include(*Test::Unit::TestCase.ancestors.select {|c| !c.is_a?(Class) }) @@ -304,6 +308,7 @@ def separated_runner(token, out = nil) end def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **opt) + pend "#{__method__}" if non_main_ractor? unless file and line loc, = caller_locations(1,1) file ||= loc.path @@ -389,7 +394,7 @@ def assert_ractor(src, args: [], require: nil, require_relative: nil, file: nil, #{require} previous_verbose = $VERBOSE $VERBOSE = nil - Ractor.new {} # trigger initial warning + Ractor.new {}.join # trigger initial warning $VERBOSE = previous_verbose #{src} RUBY @@ -554,7 +559,7 @@ def assert_raise_kind_of(*exp, &b) e end - TEST_DIR = File.join(__dir__, "test/unit") #:nodoc: + TEST_DIR = File.join(__dir__, "test/unit").freeze #:nodoc: # :call-seq: # assert(test, [failure_message]) @@ -699,27 +704,32 @@ def assert_deprecated_warn(mesg = /deprecated/, &block) end end - class << (AssertFile = Struct.new(:failure_message).new) - include Assertions - include CoreAssertions - def assert_file_predicate(predicate, *args) - if /\Anot_/ =~ predicate - predicate = $' - neg = " not" + def self.new_AssertFile + struct = Struct.new(:failure_message).new + # Uses method_missing, so contain it within its own object + class << struct + include Assertions + include CoreAssertions + def assert_file_predicate(predicate, *args) + if /\Anot_/ =~ predicate + predicate = $' + neg = " not" + end + result = File.__send__(predicate, *args) + result = !result if neg + mesg = "Expected file ".dup << args.shift.inspect + mesg << "#{neg} to be #{predicate}" + mesg << mu_pp(args).sub(/\A\[(.*)\]\z/m, '(\1)') unless args.empty? + mesg << " #{failure_message}" if failure_message + assert(result, mesg) end - result = File.__send__(predicate, *args) - result = !result if neg - mesg = "Expected file ".dup << args.shift.inspect - mesg << "#{neg} to be #{predicate}" - mesg << mu_pp(args).sub(/\A\[(.*)\]\z/m, '(\1)') unless args.empty? - mesg << " #{failure_message}" if failure_message - assert(result, mesg) - end - alias method_missing assert_file_predicate + alias method_missing assert_file_predicate - def for(message) - clone.tap {|a| a.failure_message = message} + def for(message) + clone.tap {|a| a.failure_message = message} + end end + struct end class AllFailures @@ -845,6 +855,7 @@ def assert_all_assertions_foreach(msg = nil, *keys, &block) # :yield: each elements of +seq+. def assert_linear_performance(seq, rehearsal: nil, pre: ->(n) {n}) pend "No PERFORMANCE_CLOCK found" unless defined?(PERFORMANCE_CLOCK) + pend "Timeout" if non_main_ractor? # Timeout testing generally doesn't work when RJIT compilation happens. rjit_enabled = defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? @@ -967,11 +978,20 @@ def self.glibc?(*ver) end def self.macos?(*ver) - unless defined?(@macos) - @macos = RUBY_PLATFORM.include?('darwin') && `sw_vers -productVersion`.scan(/\d+/).map(&:to_i) + is_main_ractor = Test::Unit::TestCase.main_ractor? + # Don't cache @macos as class ivar if we're running in a ractor. That means we shell out + # each time if we're not in main ractor, but this guard isn't used often so it's fine for now. + if !is_main_ractor || !defined?(@macos) + macos = RUBY_PLATFORM.include?('darwin') && `sw_vers -productVersion`.scan(/\d+/).map(&:to_i) + end + if macos && is_main_ractor + @macos = macos + elsif is_main_ractor + macos = @macos end - version_match? ver, @macos + version_match? ver, macos end + private def macos?(*ver) CoreAssertions.macos?(*ver) end diff --git a/tool/lib/envutil.rb b/tool/lib/envutil.rb index ab5e8d84e9c371..062145952d3c26 100644 --- a/tool/lib/envutil.rb +++ b/tool/lib/envutil.rb @@ -36,12 +36,13 @@ def rubybin end module_function :rubybin - LANG_ENVS = %w"LANG LC_ALL LC_CTYPE" + LANG_ENVS = %w"LANG LC_ALL LC_CTYPE".freeze DEFAULT_SIGNALS = Signal.list DEFAULT_SIGNALS.delete("TERM") if /mswin|mingw/ =~ RUBY_PLATFORM + DEFAULT_SIGNALS.freeze - RUBYLIB = ENV["RUBYLIB"] + RUBYLIB = ENV["RUBYLIB"].to_s.freeze class << self attr_accessor :timeout_scale @@ -54,7 +55,7 @@ def capture_global_values @original_verbose = $VERBOSE @original_warning = if defined?(Warning.categories) - Warning.categories.to_h {|i| [i, Warning[i]]} + Warning.categories.to_h {|i| [i, Warning[i]]}.freeze elsif defined?(Warning.[]) # 2.7+ %i[deprecated experimental performance].to_h do |i| [i, begin Warning[i]; rescue ArgumentError; end] @@ -401,8 +402,8 @@ def labeled_class(name, superclass = Object, &block) module_function :labeled_class if /darwin/ =~ RUBY_PLATFORM - DIAGNOSTIC_REPORTS_PATH = File.expand_path("~/Library/Logs/DiagnosticReports") - DIAGNOSTIC_REPORTS_TIMEFORMAT = '%Y-%m-%d-%H%M%S' + DIAGNOSTIC_REPORTS_PATH = File.expand_path("~/Library/Logs/DiagnosticReports").freeze + DIAGNOSTIC_REPORTS_TIMEFORMAT = '%Y-%m-%d-%H%M%S'.freeze @ruby_install_name = RbConfig::CONFIG['RUBY_INSTALL_NAME'] def self.diagnostic_reports(signame, pid, now) diff --git a/tool/lib/leakchecker.rb b/tool/lib/leakchecker.rb index 69aeb2c254cbfd..f572706d4abc26 100644 --- a/tool/lib/leakchecker.rb +++ b/tool/lib/leakchecker.rb @@ -14,6 +14,7 @@ def initialize end def check(test_name) + return if ENV["RUBY_TESTS_WITH_RACTORS"] if /i386-solaris/ =~ RUBY_PLATFORM && /TestGem/ =~ test_name GC.verify_internal_consistency end @@ -130,6 +131,7 @@ def check_fd_leak(test_name) def extend_tempfile_counter return if defined? LeakChecker::TempfileCounter + return if ENV["RUBY_TESTS_WITH_RACTORS"] m = Module.new { @count = 0 class << self @@ -150,6 +152,7 @@ class << Tempfile def find_tempfiles(prev_count=-1) return [prev_count, []] unless defined? Tempfile + return [prev_count,[]] if ENV["RUBY_TESTS_WITH_RACTORS"] extend_tempfile_counter count = TempfileCounter.count if prev_count == count @@ -164,6 +167,7 @@ def find_tempfiles(prev_count=-1) def check_tempfile_leak(test_name) return false unless defined? Tempfile + return false if ENV["RUBY_TESTS_WITH_RACTORS"] count1, initial_tempfiles = @tempfile_info count2, current_tempfiles = find_tempfiles(count1) leaked = false diff --git a/tool/lib/test/unit.rb b/tool/lib/test/unit.rb index 7d43e825e179eb..0cbd01627bc830 100644 --- a/tool/lib/test/unit.rb +++ b/tool/lib/test/unit.rb @@ -171,8 +171,10 @@ def process_args(args = []) opts = option_parser setup_options(opts, options) opts.parse!(args) + @option_parser = nil orig_args -= args args = @init_hook.call(args, options) if @init_hook + @init_hook = nil non_options(args, options) @run_options = orig_args @@ -818,6 +820,10 @@ def _run_parallel suites, type, result end def _run_suites suites, type + if ENV["RUBY_TESTS_WITH_RACTORS"] + Ractor.make_shareable(RbConfig::CONFIG) + Ractor.make_shareable(RbConfig::MAKEFILE_CONFIG) + end _prepare_run(suites, type) @interrupt = nil result = [] @@ -1270,6 +1276,7 @@ def non_options(files, options) result = false files.each {|f| d = File.dirname(path = File.realpath(f)) + # TODO: get enc tests working with ractors unless $:.include? d $: << d end @@ -1513,7 +1520,7 @@ def options end @@installed_at_exit ||= false - @@out = $stdout + OUT = "$stdout" @@after_tests = [] @@current_repeat_count = 0 @@ -1531,15 +1538,29 @@ def self.after_tests &block # Returns the stream to use for output. def self.output - @@out + if String === OUT + eval OUT # due to Ractors + else + OUT + end end ## # Sets Test::Unit::Runner to write output to +stream+. $stdout is the default - # output + # output. NOTE: if not $stdout or $stderr, may not be ractor safe! def self.output= stream - @@out = stream + old_verbose = $VERBOSE + $VERBOSE = nil + fd_num = stream.to_i + if [1,2].include?(fd_num) + stream = fd_num == 1 ? "$stdout" : "$stderr" # best guess + const_set(:OUT, stream) + else + const_set(:OUT, stream) + end + ensure + $VERBOSE = old_verbose end ## @@ -1667,6 +1688,9 @@ def _run_suite suite, type trace = true end + run_tests_in_ractors = ENV["RUBY_TESTS_WITH_RACTORS"] + + tests_run = 0 assertions = all_test_methods.map { |method| inst = suite.new method @@ -1680,8 +1704,32 @@ def _run_suite suite, type if trace ObjectSpace.trace_object_allocations {inst.run self} else - inst.run self + if run_tests_in_ractors + port = Ractor::Port.new + r = Ractor.new(port) do |p| + instance = Ractor.receive + runner = Ractor.receive + instance.run runner + movable_ivars = {:@_assertions => true, :@__passed__ => true, :@__name__ => true} + instance.instance_variables.each do |ivar| + unless movable_ivars[ivar] + instance.remove_instance_variable(ivar) + end + end + p.send(instance, move: true) + runner + end + r.send(inst, move: true) + r.send(self, move: false) + inst = port.receive + runner = r.value # done + _merge_results_from_ractor(runner) + port.close + else + inst.run self + end end + tests_run += 1 print "%.2f s = " % (Time.now - start_time) if @verbose print result @@ -1697,6 +1745,15 @@ def _run_suite suite, type return assertions.size, assertions.inject(0) { |sum, n| sum + n } end + def _merge_results_from_ractor(runner_cpy) + @report = runner_cpy.report + @failures = runner_cpy.failures + @errors = runner_cpy.errors + @skips = runner_cpy.skips + @assertion_count = runner_cpy.assertion_count + @test_count = runner_cpy.test_count + end + def _start_method(inst) end def _end_method(inst) @@ -1734,8 +1791,6 @@ def initialize # :nodoc: @report = [] @errors = @failures = @skips = 0 @verbose = false - @mutex = Thread::Mutex.new - @info_signal = Signal.list['INFO'] @repeat_count = nil end @@ -1763,6 +1818,7 @@ def _run args = [] self.options.merge! args puts "Run options: #{help}" + puts "\nNOTE: Running tests inside ractors" if ENV["RUBY_TESTS_WITH_RACTORS"] self.class.plugins.each do |plugin| send plugin diff --git a/tool/lib/test/unit/assertions.rb b/tool/lib/test/unit/assertions.rb index 19581fc3ab3ec0..782f3850629a4b 100644 --- a/tool/lib/test/unit/assertions.rb +++ b/tool/lib/test/unit/assertions.rb @@ -544,8 +544,12 @@ def skipped? # Takes a block and wraps it with the runner's shared mutex. def synchronize - Test::Unit::Runner.runner.synchronize do + if non_main_ractor? yield + else + Test::Unit::Runner.runner.synchronize do + yield + end end end diff --git a/tool/lib/test/unit/testcase.rb b/tool/lib/test/unit/testcase.rb index 51ffff37ebc376..a95faae17cfd7e 100644 --- a/tool/lib/test/unit/testcase.rb +++ b/tool/lib/test/unit/testcase.rb @@ -50,6 +50,24 @@ def mingw? platform = RUBY_PLATFORM /mingw/ =~ platform end + def main_ractor? + return true if !defined?(Ractor) + Ractor.current == Ractor.main + end + + def non_main_ractor? + not main_ractor? + end + + # In order to guard generating methods dynamically that will run inside a ractor + def will_run_in_non_main_ractor? + ENV["RUBY_TESTS_WITH_RACTORS"] + end + + def will_run_in_main_ractor? + not will_run_in_non_main_ractor? + end + end ## @@ -141,7 +159,7 @@ class TestCase alias method_name __name__ PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, - Interrupt, SystemExit] # :nodoc: + Interrupt, SystemExit].freeze # :nodoc: ## # Runs the tests reporting the status to +runner+ @@ -198,11 +216,13 @@ def run runner RUN_TEST_TRACE = "#{__FILE__}:#{__LINE__+3}:in `run_test'".freeze def run_test(name) - progname, $0 = $0, "#{$0}: #{self.class}##{name}" + progname, $0 = $0, "#{$0}: #{self.class}##{name}" if main_ractor? self.__send__(name) ensure $@.delete(RUN_TEST_TRACE) if $@ - $0 = progname + if main_ractor? + $0 = progname + end end def initialize name # :nodoc: diff --git a/tool/lib/tracepointchecker.rb b/tool/lib/tracepointchecker.rb index 3254e59357d493..7ff155227a2cb3 100644 --- a/tool/lib/tracepointchecker.rb +++ b/tool/lib/tracepointchecker.rb @@ -120,7 +120,7 @@ def self.check end if defined?(TracePoint.stat) class ::Test::Unit::TestCase - include TracePointChecker::ZombieTraceHunter + include TracePointChecker::ZombieTraceHunter unless ENV["RUBY_TESTS_WITH_RACTORS"] end if defined?(TracePointChecker) # TracePointChecker.start verbose: false From a13cf2703b3031579cf138fc4abb9d03a7ace0ae Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Wed, 21 May 2025 10:33:54 -0400 Subject: [PATCH 47/60] test_all_ractors: show errors immediately --- tool/lib/test/unit.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tool/lib/test/unit.rb b/tool/lib/test/unit.rb index 0cbd01627bc830..dba8cae66311e9 100644 --- a/tool/lib/test/unit.rb +++ b/tool/lib/test/unit.rb @@ -1709,7 +1709,7 @@ def _run_suite suite, type r = Ractor.new(port) do |p| instance = Ractor.receive runner = Ractor.receive - instance.run runner + res = instance.run runner movable_ivars = {:@_assertions => true, :@__passed__ => true, :@__name__ => true} instance.instance_variables.each do |ivar| unless movable_ivars[ivar] @@ -1717,14 +1717,17 @@ def _run_suite suite, type end end p.send(instance, move: true) - runner + p.send(runner) + res end r.send(inst, move: true) r.send(self, move: false) inst = port.receive - runner = r.value # done + runner = port.receive + result = r.value _merge_results_from_ractor(runner) port.close + result else inst.run self end @@ -1734,7 +1737,6 @@ def _run_suite suite, type print "%.2f s = " % (Time.now - start_time) if @verbose print result puts if @verbose - $stdout.flush leakchecker.check("#{inst.class}\##{inst.__name__}") From 986fd3281e8fa591a9ca711e1886ffc7e2b0a6c2 Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Wed, 21 May 2025 11:31:24 -0400 Subject: [PATCH 48/60] test_all_ractors: ignore some more tests (pend) --- test/ruby/test_optimization.rb | 1 + tool/lib/core_assertions.rb | 1 + tool/lib/envutil.rb | 6 ++++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/test/ruby/test_optimization.rb b/test/ruby/test_optimization.rb index df4cad6ba97e17..cf4214b421bae4 100644 --- a/test/ruby/test_optimization.rb +++ b/test/ruby/test_optimization.rb @@ -397,6 +397,7 @@ def self.tailcall(klass, src, file = nil, path = nil, line = nil, tailcall: true end def tailcall(*args) + pend "tailcall" if non_main_ractor? self.class.tailcall(singleton_class, *args) end diff --git a/tool/lib/core_assertions.rb b/tool/lib/core_assertions.rb index 1436843d0bbb28..cdd204bd9b331f 100644 --- a/tool/lib/core_assertions.rb +++ b/tool/lib/core_assertions.rb @@ -274,6 +274,7 @@ def assert_valid_syntax(code, *args, **opt) end def assert_normal_exit(testsrc, message = '', child_env: nil, **opt) + pend "#{__method__}" if non_main_ractor? assert_valid_syntax(testsrc, caller_locations(1, 1)[0]) if child_env child_env = [child_env] diff --git a/tool/lib/envutil.rb b/tool/lib/envutil.rb index 062145952d3c26..285deef13f5de3 100644 --- a/tool/lib/envutil.rb +++ b/tool/lib/envutil.rb @@ -482,10 +482,12 @@ def self.gc_stress_to_class? if defined?(RbConfig) module RbConfig - @ruby = EnvUtil.rubybin + RUBY__ = EnvUtil.rubybin.freeze class << self undef ruby if method_defined?(:ruby) - attr_reader :ruby + def ruby + RUBY__ + end end dir = File.dirname(ruby) CONFIG['bindir'] = dir From 643364e7730444203d1122c7ae1b0a3be61b29f0 Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Wed, 21 May 2025 16:35:55 -0400 Subject: [PATCH 49/60] test_all_ractors: fix some JSON tests --- test/json/json_common_interface_test.rb | 3 ++- test/json/json_fixtures_test.rb | 21 ++++++++++++--------- test/ruby/test_beginendblock.rb | 1 + test/ruby/test_exception.rb | 2 ++ 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/test/json/json_common_interface_test.rb b/test/json/json_common_interface_test.rb index 745400faa14d3a..f656dba5d456e5 100644 --- a/test/json/json_common_interface_test.rb +++ b/test/json/json_common_interface_test.rb @@ -118,6 +118,7 @@ def o.to_s end def test_load + pend "Tempfile" if non_main_ractor? assert_equal @hash, JSON.load(@json) tempfile = Tempfile.open('@json') tempfile.write @json @@ -129,7 +130,7 @@ def test_load assert_equal nil, JSON.load(nil) assert_equal nil, JSON.load('') ensure - tempfile.close! + tempfile&.close! end def test_load_with_proc diff --git a/test/json/json_fixtures_test.rb b/test/json/json_fixtures_test.rb index c153ebef7cbed1..bdeeefff447ca7 100644 --- a/test/json/json_fixtures_test.rb +++ b/test/json/json_fixtures_test.rb @@ -7,20 +7,23 @@ class JSONFixturesTest < Test::Unit::TestCase passed.each do |f| name = File.basename(f).gsub(".", "_") - source = File.read(f) - define_method("test_#{name}") do - assert JSON.parse(source), "Did not pass for fixture '#{File.basename(f)}': #{source.inspect}" + class_eval <<-RUBY, __FILE__, __LINE__+1 + def test_#{name} + assert JSON.parse(File.read(#{f.inspect})), "Did not pass for fixture '#{File.basename(f)}': \#{File.read(#{f.inspect})}" end + RUBY end failed.each do |f| name = File.basename(f).gsub(".", "_") - source = File.read(f) - define_method("test_#{name}") do - assert_raise(JSON::ParserError, JSON::NestingError, - "Did not fail for fixture '#{name}': #{source.inspect}") do - JSON.parse(source) + class_eval <<-RUBY, __FILE__, __LINE__+1 + def test_#{name} + source = File.read(#{f.inspect}) + assert_raise(JSON::ParserError, JSON::NestingError, + "Did not fail for fixture '#{name}': \#{source.inspect}") do + JSON.parse(source) + end end - end + RUBY end end diff --git a/test/ruby/test_beginendblock.rb b/test/ruby/test_beginendblock.rb index c86797aa6d4e80..64d5d1a496466f 100644 --- a/test/ruby/test_beginendblock.rb +++ b/test/ruby/test_beginendblock.rb @@ -165,6 +165,7 @@ def test_errinfo_at_exit if defined?(fork) def test_internal_errinfo_at_exit + omit "at_exit handlers cannot use ractor-local objects" if non_main_ractor? # TODO: use other than break-in-fork to throw an internal # error info. error, pid, status = IO.pipe do |r, w| diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb index 637a645834195f..3ded9d4d6a98f5 100644 --- a/test/ruby/test_exception.rb +++ b/test/ruby/test_exception.rb @@ -244,6 +244,7 @@ def test_uncaught_throw end def test_catch_throw_in_require + pend "Tempfile" bug7185 = '[ruby-dev:46234]' Tempfile.create(["dep", ".rb"]) {|t| t.puts("throw :extdep, 42") @@ -253,6 +254,7 @@ def test_catch_throw_in_require end def test_catch_throw_in_require_cant_be_rescued + pend "Tempfile" bug18562 = '[ruby-core:107403]' Tempfile.create(["dep", ".rb"]) {|t| t.puts("throw :extdep, 42") From e089a26ae66862f8883cb938fc3b2e207faeb70d Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Thu, 22 May 2025 14:35:56 -0400 Subject: [PATCH 50/60] test_all_ractors_multi_ractor: tmp commit --- tool/lib/core_assertions.rb | 1 + tool/lib/test/unit.rb | 91 ++++++++++++++++++++++------------ tool/lib/test/unit/testcase.rb | 4 +- 3 files changed, 63 insertions(+), 33 deletions(-) diff --git a/tool/lib/core_assertions.rb b/tool/lib/core_assertions.rb index cdd204bd9b331f..51142d133b8927 100644 --- a/tool/lib/core_assertions.rb +++ b/tool/lib/core_assertions.rb @@ -286,6 +286,7 @@ def assert_normal_exit(testsrc, message = '', child_env: nil, **opt) end def assert_ruby_status(args, test_stdin="", message=nil, **opt) + pend "#{__method__}" if non_main_ractor? out, _, status = EnvUtil.invoke_ruby(args, test_stdin, true, :merge_to_stdout, **opt) desc = FailDesc[status, message, out] assert(!status.signaled?, desc) diff --git a/tool/lib/test/unit.rb b/tool/lib/test/unit.rb index dba8cae66311e9..2521b72ff02d8b 100644 --- a/tool/lib/test/unit.rb +++ b/tool/lib/test/unit.rb @@ -1688,12 +1688,10 @@ def _run_suite suite, type trace = true end - run_tests_in_ractors = ENV["RUBY_TESTS_WITH_RACTORS"] - + run_tests_inside_ractors_num = ENV["RUBY_TESTS_WITH_RACTORS"].to_i tests_run = 0 assertions = all_test_methods.map { |method| - - inst = suite.new method + inst = suite.new method.to_s _start_method(inst) inst._assertions = 0 @@ -1704,34 +1702,62 @@ def _run_suite suite, type if trace ObjectSpace.trace_object_allocations {inst.run self} else - if run_tests_in_ractors - port = Ractor::Port.new - r = Ractor.new(port) do |p| - instance = Ractor.receive - runner = Ractor.receive - res = instance.run runner - movable_ivars = {:@_assertions => true, :@__passed__ => true, :@__name__ => true} - instance.instance_variables.each do |ivar| - unless movable_ivars[ivar] - instance.remove_instance_variable(ivar) + if run_tests_inside_ractors_num > 0 + old_report = self.report + old_failures = self.failures + old_errors = self.errors + old_skips = self.skips + old_assertion_count = self.assertion_count + old_test_count = self.test_count + self.report = [] + self.failures = 0 + self.errors = 0 + self.skips = 0 + self.assertion_count = 0 + self.test_count = 0 + rs = run_tests_inside_ractors_num.times.map do + r = Ractor.new(inst, self) do |instance, runner| + res = instance.run runner + testcase_copyable_ivars = {:@_assertions => true, :@__passed__ => true, :@__name__ => true} + runner_copyable_ivars = {:@report => true, :@failures => true, :@errors => true, :@skips => true, :@assertion_count => true, :@test_count => true} + instance.instance_variables.each do |ivar| + unless testcase_copyable_ivars[ivar] + instance.remove_instance_variable(ivar) + end end + runner.instance_variables.each do |ivar| + unless runner_copyable_ivars[ivar] + runner.remove_instance_variable(ivar) + end + end + [instance, runner, res] end - p.send(instance, move: true) - p.send(runner) - res end - r.send(inst, move: true) - r.send(self, move: false) - inst = port.receive - runner = port.receive - result = r.value - _merge_results_from_ractor(runner) - port.close - result + ractor_results = [] + while rs.any? + r, obj = Ractor.select(*rs) + _inst, runner, res = *obj + ractor_results << [res, runner] + rs.delete(r) + end + # ractors done + self.report = old_report + self.failures = old_failures + self.errors = old_errors + self.skips = old_skips + self.assertion_count = old_assertion_count + self.test_count = old_test_count + res = "" + ractor_results.each do |(res0, runner)| + res += res0 + _merge_results_from_ractor(runner) + end + res else inst.run self end end + tests_run += 1 print "%.2f s = " % (Time.now - start_time) if @verbose @@ -1747,13 +1773,16 @@ def _run_suite suite, type return assertions.size, assertions.inject(0) { |sum, n| sum + n } end + def __init_runner(runner) + end + def _merge_results_from_ractor(runner_cpy) - @report = runner_cpy.report - @failures = runner_cpy.failures - @errors = runner_cpy.errors - @skips = runner_cpy.skips - @assertion_count = runner_cpy.assertion_count - @test_count = runner_cpy.test_count + @report += runner_cpy.report + @failures += runner_cpy.failures + @errors += runner_cpy.errors + @skips += runner_cpy.skips + @assertion_count += runner_cpy.assertion_count + @test_count += runner_cpy.test_count end def _start_method(inst) diff --git a/tool/lib/test/unit/testcase.rb b/tool/lib/test/unit/testcase.rb index a95faae17cfd7e..8c13326d22357e 100644 --- a/tool/lib/test/unit/testcase.rb +++ b/tool/lib/test/unit/testcase.rb @@ -229,11 +229,11 @@ def initialize name # :nodoc: @__name__ = name @__io__ = nil @__passed__ = nil - @@__current__ = self # FIX: make thread local + Ractor.current[:__test_current__] = self end def self.current # :nodoc: - @@__current__ # FIX: make thread local + Ractor.current[:__test_current__] end ## From f099af083d0098ad29cdf16f36c3ec019ec3aa7b Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Thu, 29 May 2025 11:37:13 -0400 Subject: [PATCH 51/60] tmp commit for test_all_ractors_multi_ractor --- test/ruby/test_assignment.rb | 3 +++ test/ruby/test_bignum.rb | 4 ++-- test/ruby/test_dir.rb | 23 +++++++++++++++++++---- test/ruby/test_fiber.rb | 1 + test/ruby/test_proc.rb | 11 ++++++----- tool/lib/test/unit.rb | 8 ++++++-- 6 files changed, 37 insertions(+), 13 deletions(-) diff --git a/test/ruby/test_assignment.rb b/test/ruby/test_assignment.rb index 6e9452340fea83..05c50c10a0a1f6 100644 --- a/test/ruby/test_assignment.rb +++ b/test/ruby/test_assignment.rb @@ -316,6 +316,7 @@ def []=(i, a); 42; end end def test_yield + omit "lots of undefs" unless main_ractor? def f; yield(nil); end; f {|a| assert_nil(a)}; undef f def f; yield(1); end; f {|a| assert_equal(1, a)}; undef f def f; yield([]); end; f {|a| assert_equal([], a)}; undef f @@ -372,6 +373,7 @@ def f; yield(*[*[1,2]]); end; f {|a,b,*c| assert_equal([1,2,[]], [a,b,c])}; unde end def test_return + omit "lots of undefs" unless main_ractor? def r; return; end; a = r(); assert_nil(a); undef r def r; return nil; end; a = r(); assert_nil(a); undef r def r; return 1; end; a = r(); assert_equal(1, a); undef r @@ -556,6 +558,7 @@ def test_break end def test_next + omit "lots of undefs" unless main_ractor? def r(val); a = yield(); assert_equal(val, a); end r(nil){next} r(nil){next nil} diff --git a/test/ruby/test_bignum.rb b/test/ruby/test_bignum.rb index 122130819934ba..3807a83f1c303e 100644 --- a/test/ruby/test_bignum.rb +++ b/test/ruby/test_bignum.rb @@ -74,8 +74,8 @@ def test_bignum x = fact(40) assert_equal(x, x) assert_equal(x, fact(40)) - assert_operator(x, :<, $x+2) - assert_operator(x, :>, $x-2) + assert_operator(x, :<, x+2) + assert_operator(x, :>, x-2) assert_equal(815915283247897734345611269596115894272000000000, x) assert_not_equal(815915283247897734345611269596115894272000000001, x) assert_equal(815915283247897734345611269596115894272000000001, x+1) diff --git a/test/ruby/test_dir.rb b/test/ruby/test_dir.rb index edb5210af16313..7c2eac5d970133 100644 --- a/test/ruby/test_dir.rb +++ b/test/ruby/test_dir.rb @@ -97,6 +97,7 @@ def test_rewind end def test_class_chdir + omit "not ractor safe" unless main_ractor? pwd = Dir.pwd setup_envs @@ -129,13 +130,14 @@ def test_class_chdir ensure begin - Dir.chdir(pwd) + Dir.chdir(pwd) if pwd rescue abort("cannot return the original directory: #{ pwd }") end end def test_instance_chdir + omit "not ractor safe" unless main_ractor? pwd = Dir.pwd dir = Dir.new(pwd) root_dir = Dir.new(@root) @@ -194,15 +196,16 @@ def Warning.warn(message) assert_equal(42, ret) ensure begin - assert_equal(0, dir.chdir) + assert_equal(0, dir.chdir) if dir rescue abort("cannot return the original directory: #{ pwd }") end - dir.close - root_dir.close + dir&.close + root_dir&.close end def test_chdir_conflict + omit "not ractor safe" unless main_ractor? pwd = Dir.pwd q = Thread::Queue.new t = Thread.new do @@ -279,6 +282,7 @@ def test_glob end def test_glob_recursive + omit "not ractor safe" unless main_ractor? bug6977 = '[ruby-core:47418]' bug8006 = '[ruby-core:53108] [Bug #8006]' Dir.chdir(@root) do @@ -308,6 +312,7 @@ def test_glob_recursive end def test_glob_recursive_directory + omit "not ractor safe" unless main_ractor? Dir.chdir(@root) do ['d', 'e'].each do |path| FileUtils.mkdir_p("c/#{path}/a/b/c") @@ -325,6 +330,7 @@ def test_glob_recursive_directory end def test_glob_starts_with_brace + omit "not ractor safe" unless main_ractor? Dir.chdir(@root) do bug15649 = '[ruby-core:91728] [Bug #15649]' assert_equal(["#{@root}/a", "#{@root}/b"], @@ -333,6 +339,7 @@ def test_glob_starts_with_brace end def test_glob_recursive_with_brace + omit "not ractor safe" unless main_ractor? Dir.chdir(@root) do bug19042 = '[ruby-core:110220] [Bug #19042]' %w"c/dir_a c/dir_b c/dir_b/dir".each do |d| @@ -347,6 +354,7 @@ def test_glob_recursive_with_brace end def test_glob_order + omit "not ractor safe" unless main_ractor? Dir.chdir(@root) do assert_equal(["#{@root}/a", "#{@root}/b"], Dir.glob("#{@root}/[ba]")) assert_equal(["#{@root}/b", "#{@root}/a"], Dir.glob(%W"#{@root}/b #{@root}/a")) @@ -376,6 +384,7 @@ def test_glob_too_may_open_files end def test_glob_base + omit "not ractor safe (Dir.chdir)" unless main_ractor? files = %w[a/foo.c c/bar.c] files.each {|n| File.write(File.join(@root, n), "")} Dir.mkdir(File.join(@root, "a/dir")) @@ -416,6 +425,7 @@ def test_glob_base end def test_glob_base_dir + omit "not ractor safe" unless main_ractor? files = %w[a/foo.c c/bar.c] files.each {|n| File.write(File.join(@root, n), "")} Dir.mkdir(File.join(@root, "a/dir")) @@ -438,6 +448,7 @@ def test_glob_base_dir end def test_glob_ignore_casefold_invalid_encoding + omit "not ractor safe" unless main_ractor? bug14456 = "[ruby-core:85448]" filename = "\u00AAa123".encode('ISO-8859-1') File.write(File.join(@root, filename), "") @@ -555,6 +566,7 @@ def test_glob_metachar end def test_glob_cases + omit "not ractor safe (Dir.chdir)" unless main_ractor? feature5994 = "[ruby-core:42469] [Feature #5994]" feature5994 << "\nDir.glob should return the filename with actual cases on the filesystem" Dir.chdir(File.join(@root, "a")) do @@ -659,6 +671,7 @@ def test_children_long_name end def test_home + omit "not ractor safe" unless main_ractor? setup_envs ENV["HOME"] = @nodir @@ -690,6 +703,7 @@ def test_home_utf8 end def test_symlinks_not_resolved + omit "not ractors safe (Dir.chdir)" unless main_ractor? Dir.mktmpdir do |dirname| Dir.chdir(dirname) do begin @@ -718,6 +732,7 @@ def test_fileno end def test_for_fd + omit "not ractor safe" unless main_ractor? if Dir.respond_to? :for_fd begin new_dir = Dir.new('..') diff --git a/test/ruby/test_fiber.rb b/test/ruby/test_fiber.rb index b7d2b71c196c38..d265c192122dd5 100644 --- a/test/ruby/test_fiber.rb +++ b/test/ruby/test_fiber.rb @@ -395,6 +395,7 @@ def test_prohibit_resume_to_transferring_fiber def test_fork_from_fiber omit 'fork not supported' unless Process.respond_to?(:fork) + omit "maybe fork not supported unless main ractor" unless main_ractor? pid = nil bug5700 = '[ruby-core:41456]' assert_nothing_raised(bug5700) do diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb index 8b5fb57176af71..f1d47195e6306e 100644 --- a/test/ruby/test_proc.rb +++ b/test/ruby/test_proc.rb @@ -172,11 +172,12 @@ def test_hash_equal assert_equal p1.hash, p2.hash - # symbol backed proc - p1 = :hello.to_proc - p2 = :hello.to_proc - - assert_equal p1.hash, p2.hash + if main_ractor? + # symbol backed proc + p1 = :hello.to_proc + p2 = :hello.to_proc + assert_equal p1.hash, p2.hash + end end def test_hash_uniqueness diff --git a/tool/lib/test/unit.rb b/tool/lib/test/unit.rb index 2521b72ff02d8b..70c58d026b4531 100644 --- a/tool/lib/test/unit.rb +++ b/tool/lib/test/unit.rb @@ -1716,7 +1716,7 @@ def _run_suite suite, type self.assertion_count = 0 self.test_count = 0 rs = run_tests_inside_ractors_num.times.map do - r = Ractor.new(inst, self) do |instance, runner| + Ractor.new(inst, self) do |instance, runner| res = instance.run runner testcase_copyable_ivars = {:@_assertions => true, :@__passed__ => true, :@__name__ => true} runner_copyable_ivars = {:@report => true, :@failures => true, :@errors => true, :@skips => true, :@assertion_count => true, :@test_count => true} @@ -1849,7 +1849,11 @@ def _run args = [] self.options.merge! args puts "Run options: #{help}" - puts "\nNOTE: Running tests inside ractors" if ENV["RUBY_TESTS_WITH_RACTORS"] + ractors_num = ENV["RUBY_TESTS_WITH_RACTORS"].to_i + + if ractors_num > 0 + puts "\nNOTE: Running tests inside ractors (each test method inside #{ractors_num} ractors)" + end self.class.plugins.each do |plugin| send plugin From b326b7eed1f7e933e783c630d583503112904e54 Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Fri, 6 Jun 2025 18:17:46 -0400 Subject: [PATCH 52/60] test_all_ractors_multi_ractor: WIP ruby tests passing --- test/ruby/test_array.rb | 29 +++++++++++++++++------------ test/ruby/test_ast.rb | 2 ++ test/ruby/test_optimization.rb | 1 + tool/lib/test/unit.rb | 30 ++++++++++++++++-------------- tool/lib/test/unit/testcase.rb | 9 ++++++--- 5 files changed, 42 insertions(+), 29 deletions(-) diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb index fd293c8d276cad..457140d6f47cbb 100644 --- a/test/ruby/test_array.rb +++ b/test/ruby/test_array.rb @@ -660,14 +660,16 @@ def test_concat assert_raise(TypeError) { @cls[0].concat(:foo) } assert_raise(FrozenError) { @cls[0].freeze.concat(:foo) } - a = @cls[nil] - def (x = Object.new).to_ary - ary = Array.new(2) - ary << [] << [] << :ok + unless multiple_ractors? + a = @cls[nil] + def (x = Object.new).to_ary + ary = Array.new(2) + ary << [] << [] << :ok + end + EnvUtil.under_gc_stress {a.concat(x)} + GC.start + assert_equal(:ok, a.last) end - EnvUtil.under_gc_stress {a.concat(x)} - GC.start - assert_equal(:ok, a.last) end def test_count @@ -1476,6 +1478,7 @@ def test_replace end def test_replace_wb_variable_width_alloc + omit "not working properly across ractors" if multiple_ractors? small_embed = [] 4.times { GC.start } # age small_embed large_embed = [1, 2, 3, 4, 5, Array.new] # new young object @@ -2444,11 +2447,13 @@ def test_product assert_equal(@cls[], @cls[1,2].product([])) bug3394 = '[ruby-dev:41540]' - acc = [] - EnvUtil.under_gc_stress {[1,2].product([3,4,5],[6,8]){|array| acc << array}} - assert_equal([[1, 3, 6], [1, 3, 8], [1, 4, 6], [1, 4, 8], [1, 5, 6], [1, 5, 8], - [2, 3, 6], [2, 3, 8], [2, 4, 6], [2, 4, 8], [2, 5, 6], [2, 5, 8]], - acc, bug3394) + unless multiple_ractors? + acc = [] + EnvUtil.under_gc_stress {[1,2].product([3,4,5],[6,8]){|array| acc << array}} + assert_equal([[1, 3, 6], [1, 3, 8], [1, 4, 6], [1, 4, 8], [1, 5, 6], [1, 5, 8], + [2, 3, 6], [2, 3, 8], [2, 4, 6], [2, 4, 8], [2, 5, 6], [2, 5, 8]], + acc, bug3394) + end def (o = Object.new).to_ary; GC.start; [3,4] end acc = [1,2].product(*[o]*10) diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb index 19495265f756b6..b9aeaff613e529 100644 --- a/test/ruby/test_ast.rb +++ b/test/ruby/test_ast.rb @@ -210,6 +210,7 @@ def test_parse_raises_syntax_error end def test_parse_file_raises_syntax_error + omit "Tempfile" unless main_ractor? Tempfile.create(%w"test_ast .rb") do |f| f.puts "end" f.close @@ -377,6 +378,7 @@ def test_node_id_for_backtrace_location_raises_argument_error def test_of_proc_and_method omit if ParserSupport.prism_enabled? || ParserSupport.prism_enabled_in_subprocess? + omit "Tempfile" unless main_ractor? proc = Proc.new { 1 + 2 } method = self.method(__method__) diff --git a/test/ruby/test_optimization.rb b/test/ruby/test_optimization.rb index cf4214b421bae4..98732e5e9c6977 100644 --- a/test/ruby/test_optimization.rb +++ b/test/ruby/test_optimization.rb @@ -1225,6 +1225,7 @@ def test_opt_new_with_safe_navigation end def test_opt_new + omit "RubyVM::Iseq.compile/eval not working across multiple ractors" if multiple_ractors? pos_initialize = " def initialize a, b @a = a diff --git a/tool/lib/test/unit.rb b/tool/lib/test/unit.rb index 70c58d026b4531..c7bf85eb9f286c 100644 --- a/tool/lib/test/unit.rb +++ b/tool/lib/test/unit.rb @@ -1736,8 +1736,8 @@ def _run_suite suite, type ractor_results = [] while rs.any? r, obj = Ractor.select(*rs) - _inst, runner, res = *obj - ractor_results << [res, runner] + inst, runner, res = *obj + ractor_results << [res, inst, runner] rs.delete(r) end # ractors done @@ -1747,11 +1747,12 @@ def _run_suite suite, type self.skips = old_skips self.assertion_count = old_assertion_count self.test_count = old_test_count - res = "" - ractor_results.each do |(res0, runner)| - res += res0 - _merge_results_from_ractor(runner) + res = +"" + ractor_results.each do |(res0, inst, runner)| + res << res0 + _merge_results_from_ractor(inst, runner) end + inst._assertions = self.assertion_count - old_assertion_count res else inst.run self @@ -1776,13 +1777,13 @@ def _run_suite suite, type def __init_runner(runner) end - def _merge_results_from_ractor(runner_cpy) - @report += runner_cpy.report - @failures += runner_cpy.failures - @errors += runner_cpy.errors - @skips += runner_cpy.skips - @assertion_count += runner_cpy.assertion_count - @test_count += runner_cpy.test_count + def _merge_results_from_ractor(inst, runner_cpy) + self.report += runner_cpy.report + self.failures += runner_cpy.failures + self.errors += runner_cpy.errors + self.skips += runner_cpy.skips + self.assertion_count += inst._assertions + self.test_count += runner_cpy.test_count end def _start_method(inst) @@ -1852,7 +1853,8 @@ def _run args = [] ractors_num = ENV["RUBY_TESTS_WITH_RACTORS"].to_i if ractors_num > 0 - puts "\nNOTE: Running tests inside ractors (each test method inside #{ractors_num} ractors)" + puts "\nNOTE: Running tests inside ractors (each test method inside #{ractors_num} " \ + "ractor#{ractors_num > 1 ? 's' : ''})" end self.class.plugins.each do |plugin| diff --git a/tool/lib/test/unit/testcase.rb b/tool/lib/test/unit/testcase.rb index 8c13326d22357e..c381cd1b767d78 100644 --- a/tool/lib/test/unit/testcase.rb +++ b/tool/lib/test/unit/testcase.rb @@ -59,15 +59,18 @@ def non_main_ractor? not main_ractor? end + def multiple_ractors? + ENV["RUBY_TESTS_WITH_RACTORS"].to_i > 1 + end + # In order to guard generating methods dynamically that will run inside a ractor def will_run_in_non_main_ractor? - ENV["RUBY_TESTS_WITH_RACTORS"] + ENV["RUBY_TESTS_WITH_RACTORS"].to_i > 0 end def will_run_in_main_ractor? not will_run_in_non_main_ractor? end - end ## @@ -219,8 +222,8 @@ def run_test(name) progname, $0 = $0, "#{$0}: #{self.class}##{name}" if main_ractor? self.__send__(name) ensure - $@.delete(RUN_TEST_TRACE) if $@ if main_ractor? + $@.delete(RUN_TEST_TRACE) if $@ $0 = progname end end From b6d096189b18ca4fd9097b501f6c3f4cb23d2ed1 Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Mon, 9 Jun 2025 17:18:28 -0400 Subject: [PATCH 53/60] test_all_ractors_multi_ractor: another WIP --- test/ruby/test_backtrace.rb | 3 +++ test/ruby/test_beginendblock.rb | 2 ++ test/ruby/test_class.rb | 8 +++--- test/ruby/test_compile_prism.rb | 4 +++ test/ruby/test_defined.rb | 1 + test/ruby/test_dir_m17n.rb | 4 +++ test/ruby/test_enumerator.rb | 2 ++ test/ruby/test_env.rb | 9 ++++--- test/ruby/test_eval.rb | 37 +++++++++++++++------------- test/ruby/test_exception.rb | 34 ++++++++++++++++--------- test/ruby/test_file.rb | 15 ++++++++--- test/ruby/test_file_exhaustive.rb | 41 ++++++++++++++++++++----------- test/ruby/test_float.rb | 1 + test/ruby/test_gc.rb | 17 +++++++++++++ test/ruby/test_gc_compact.rb | 1 + test/ruby/test_hash.rb | 2 ++ test/ruby/test_io.rb | 4 ++- test/ruby/test_io_m17n.rb | 24 +++++++++++++----- test/ruby/test_iseq.rb | 4 +++ test/ruby/test_keyword.rb | 1 + test/ruby/test_m17n.rb | 2 ++ test/ruby/test_module.rb | 2 +- test/ruby/test_syntax.rb | 2 +- tool/lib/core_assertions.rb | 1 + tool/lib/envutil.rb | 5 ++++ tool/lib/test/unit.rb | 2 +- tool/lib/zombie_hunter.rb | 2 +- 27 files changed, 166 insertions(+), 64 deletions(-) diff --git a/test/ruby/test_backtrace.rb b/test/ruby/test_backtrace.rb index dad7dfcb558471..db5c7e6591f297 100644 --- a/test/ruby/test_backtrace.rb +++ b/test/ruby/test_backtrace.rb @@ -258,6 +258,7 @@ def test_caller_locations_to_s_inspect end def test_caller_locations_path + omit "Tempfile" unless main_ractor? loc, = caller_locations(0, 1) assert_equal(__FILE__, loc.path) Tempfile.create(%w"caller_locations .rb") do |f| @@ -269,6 +270,7 @@ def test_caller_locations_path end def test_caller_locations_absolute_path + omit "Tempfile" unless main_ractor? loc, = caller_locations(0, 1) assert_equal(__FILE__, loc.absolute_path) Tempfile.create(%w"caller_locations .rb") do |f| @@ -279,6 +281,7 @@ def test_caller_locations_absolute_path end def test_caller_locations_lineno + omit "Tempfile" unless main_ractor? loc, = caller_locations(0, 1) assert_equal(__LINE__-1, loc.lineno) Tempfile.create(%w"caller_locations .rb") do |f| diff --git a/test/ruby/test_beginendblock.rb b/test/ruby/test_beginendblock.rb index 64d5d1a496466f..d2fa95af08b274 100644 --- a/test/ruby/test_beginendblock.rb +++ b/test/ruby/test_beginendblock.rb @@ -73,6 +73,7 @@ def test_exitcode_in_at_exit end def test_propagate_exit_code + omit "TODO: look into this. Getting unexpected values but can't reproduce it in non-test environment" ruby = EnvUtil.rubybin assert_equal false, system(ruby, '-e', 'at_exit{exit 2}') assert_equal 2, $?.exitstatus @@ -119,6 +120,7 @@ def test_nested_at_exit end def test_rescue_at_exit + omit "subprocess" unless main_ractor? bug5218 = '[ruby-core:43173][Bug #5218]' cmd = [ "raise 'X' rescue nil", diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index 0d517d72de7a19..fc361f36308b6e 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -244,9 +244,11 @@ def test_check_inheritable assert_raise(TypeError) { Class.new(c) } assert_raise(TypeError) { Class.new(Class) } assert_raise(TypeError) { eval("class Foo < Class; end") } - m = "M\u{1f5ff}" - o = Class.new {break eval("class #{m}; self; end.new")} - assert_raise_with_message(TypeError, /#{m}/) {Class.new(o)} + if main_ractor? + m = "M\u{1f5ff}" + o = Class.new {break eval("class #{m}; self; end.new")} + assert_raise_with_message(TypeError, /#{m}/) {Class.new(o)} + end end def test_initialize_copy diff --git a/test/ruby/test_compile_prism.rb b/test/ruby/test_compile_prism.rb index 41bb1995a6d1df..e643cecaa42374 100644 --- a/test/ruby/test_compile_prism.rb +++ b/test/ruby/test_compile_prism.rb @@ -3,6 +3,10 @@ # This file is organized to match itemization in https://github.com/ruby/prism/issues/1335 module Prism class TestCompilePrism < Test::Unit::TestCase + def setup + omit "Not ractor safe" if multiple_ractors? + end + def test_iseq_has_node_id code = "proc { < e; puts e.full_message; end"], '', true, true) - assert_predicate(status1, :success?) - assert_empty(err1, "expected nothing wrote to $stdout by #full_message") + if main_ractor? + out1, err1, status1 = EnvUtil.invoke_ruby(['-e', "#{test_method}; begin; foo; rescue => e; puts e.full_message; end"], '', true, true) + assert_predicate(status1, :success?) + assert_empty(err1, "expected nothing wrote to $stdout by #full_message") - _, err2, status1 = EnvUtil.invoke_ruby(['-e', "#{test_method}; begin; foo; end"], '', true, true) - assert_equal(err2, out1) + _, err2, status1 = EnvUtil.invoke_ruby(['-e', "#{test_method}; begin; foo; end"], '', true, true) + assert_equal(err2, out1) + end e = RuntimeError.new("a\n") message = assert_nothing_raised(ArgumentError, proc {e.pretty_inspect}) do @@ -1447,6 +1456,7 @@ def test_detailed_message def test_detailed_message_under_gc_compact_stress omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077 + omit "gc_compact_stress" if multiple_ractors? EnvUtil.under_gc_compact_stress do e = RuntimeError.new("foo\nbar\nbaz") assert_equal("foo (RuntimeError)\nbar\nbaz", e.detailed_message) diff --git a/test/ruby/test_file.rb b/test/ruby/test_file.rb index 59a72157711358..ba87278050da50 100644 --- a/test/ruby/test_file.rb +++ b/test/ruby/test_file.rb @@ -261,10 +261,14 @@ def test_realpath tst = realdir + (File::SEPARATOR*3 + ".") assert_equal(realdir, File.realpath(tst)) assert_equal(realdir, File.realpath(".", tst)) - assert_equal(realdir, Dir.chdir(realdir) {File.realpath(".")}) + if main_ractor? + assert_equal(realdir, Dir.chdir(realdir) {File.realpath(".")}) + end realpath = File.join(realdir, "test") File.write(realpath, "") - assert_equal(realpath, Dir.chdir(realdir) {File.realpath("test")}) + if main_ractor? + assert_equal(realpath, Dir.chdir(realdir) {File.realpath("test")}) + end if File::ALT_SEPARATOR bug2961 = '[ruby-core:28653]' assert_equal(realdir, File.realpath(realdir.tr(File::SEPARATOR, File::ALT_SEPARATOR)), bug2961) @@ -311,8 +315,10 @@ def test_realdirpath assert_equal(realdir, File.realdirpath(tst)) assert_equal(realdir, File.realdirpath(".", tst)) assert_equal(File.join(realdir, "foo"), File.realdirpath("foo", tst)) - assert_equal(realdir, Dir.chdir(realdir) {File.realdirpath(".")}) - assert_equal(File.join(realdir, "foo"), Dir.chdir(realdir) {File.realdirpath("foo")}) + if main_ractor? + assert_equal(realdir, Dir.chdir(realdir) {File.realdirpath(".")}) + assert_equal(File.join(realdir, "foo"), Dir.chdir(realdir) {File.realdirpath("foo")}) if main + end } begin result = File.realdirpath("bar", "//:/foo") @@ -325,6 +331,7 @@ def test_realdirpath end def test_realdirpath_junction + omit "Dir.chdir" unless main_ractor? Dir.mktmpdir('rubytest-realpath') {|tmpdir| Dir.chdir(tmpdir) do Dir.mkdir('foo') diff --git a/test/ruby/test_file_exhaustive.rb b/test/ruby/test_file_exhaustive.rb index 13b8970c11075b..7c905a037d62c1 100644 --- a/test/ruby/test_file_exhaustive.rb +++ b/test/ruby/test_file_exhaustive.rb @@ -19,7 +19,7 @@ def assert_incompatible_encoding end def setup - @dir = Dir.mktmpdir("ruby-test") + @dir = Dir.mktmpdir("#{Ractor.current.object_id}") File.chown(-1, Process.gid, @dir) end @@ -268,7 +268,7 @@ def test_stat_drive_root end if DRIVE def test_stat_dotted_prefix - Dir.mktmpdir do |dir| + Dir.mktmpdir("#{Ractor.current.object_id}") do |dir| prefix = File.join(dir, "...a") Dir.mkdir(prefix) assert_file.exist?(prefix) @@ -277,7 +277,7 @@ def test_stat_dotted_prefix Dir.chdir(dir) do assert_nothing_raised { File.stat(File.basename(prefix)) } - end + end if main_ractor? end end if NTFS @@ -706,7 +706,7 @@ def test_utime File.write(path, "") rescue next assert_equal(1, File.utime(nil, nil, path)) end - end + end if main_ractor? end def test_utime_symlinkfile @@ -813,6 +813,7 @@ def test_rename end def test_umask + omit "global side effects" if multiple_ractors? prev = File.umask(0777) assert_equal(0777, File.umask) open(nofile, "w") { } @@ -915,6 +916,7 @@ def test_expand_path_encoding end def test_expand_path_encoding_filesystem + omit "global side effects" if multiple_ractors? home = ENV["HOME"] ENV["HOME"] = "#{DRIVE}/UserHome" @@ -925,12 +927,13 @@ def test_expand_path_encoding_filesystem assert_equal fs, File.expand_path(path).encoding assert_equal fs, File.expand_path(path, dir).encoding ensure - ENV["HOME"] = home + ENV["HOME"] = home if home end UnknownUserHome = "~foo_bar_baz_unknown_user_wahaha".freeze def test_expand_path_home + omit "global side effects" if multiple_ractors? assert_kind_of(String, File.expand_path("~")) if ENV["HOME"] assert_raise(ArgumentError) { File.expand_path(UnknownUserHome) } assert_raise(ArgumentError) { File.expand_path(UnknownUserHome, "/") } @@ -949,14 +952,17 @@ def test_expand_path_home ENV["HOME"] = "." assert_raise(ArgumentError, bug3630) { File.expand_path("~") } ensure - ENV["HOME"] = home - ENV["HOMEDRIVE"] = home_drive - ENV["HOMEPATH"] = home_path - ENV["USERPROFILE"] = user_profile + if home + ENV["HOME"] = home + ENV["HOMEDRIVE"] = home_drive + ENV["HOMEPATH"] = home_path + ENV["USERPROFILE"] = user_profile + end end end def test_expand_path_home_dir_string + omit "global side effects" if multiple_ractors? home = ENV["HOME"] new_home = "#{DRIVE}/UserHome" ENV["HOME"] = new_home @@ -970,7 +976,7 @@ def test_expand_path_home_dir_string ENV["HOME"] = "#{DRIVE}UserHome" assert_raise(ArgumentError) { File.expand_path("~") } ensure - ENV["HOME"] = home + ENV["HOME"] = home if home end if /mswin|mingw/ =~ RUBY_PLATFORM @@ -1092,31 +1098,34 @@ def test_expand_path_converts_a_pathname_which_starts_with_a_slash_using_a_curre end def test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_home_as_base + omit "global side effects" if multiple_ractors? old_home = ENV["HOME"] home = ENV["HOME"] = "#{DRIVE}/UserHome" assert_equal(home, File.expand_path("~")) assert_equal(home, File.expand_path("~", "C:/FooBar")) assert_equal(File.join(home, "a"), File.expand_path("~/a", "C:/FooBar")) ensure - ENV["HOME"] = old_home + ENV["HOME"] = old_home if old_home end def test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_unc_home + omit "global side effects" if multiple_ractors? old_home = ENV["HOME"] unc_home = ENV["HOME"] = "//UserHome" assert_equal(unc_home, File.expand_path("~")) ensure - ENV["HOME"] = old_home + ENV["HOME"] = old_home if old_home end if DRIVE def test_expand_path_does_not_modify_a_home_string_argument + omit "global side effects" if multiple_ractors? old_home = ENV["HOME"] home = ENV["HOME"] = "#{DRIVE}/UserHome" str = "~/a" assert_equal("#{home}/a", File.expand_path(str)) assert_equal("~/a", str) ensure - ENV["HOME"] = old_home + ENV["HOME"] = old_home if old_home end def test_expand_path_raises_argument_error_for_any_supplied_username @@ -1136,11 +1145,12 @@ def test_expand_path_error_for_nonexistent_username end unless DRIVE def test_expand_path_error_for_non_absolute_home + omit "global side effects" if multiple_ractors? old_home = ENV["HOME"] ENV["HOME"] = "./UserHome" assert_raise_with_message(ArgumentError, /non-absolute home/) {File.expand_path("~")} ensure - ENV["HOME"] = old_home + ENV["HOME"] = old_home if old_home end def test_expand_path_raises_a_type_error_if_not_passed_a_string_type @@ -1188,6 +1198,7 @@ def test_expand_path_with_drive_letter if /darwin/ =~ RUBY_PLATFORM and Encoding.find("filesystem") == Encoding::UTF_8 def test_expand_path_compose + omit "Dir.chdir" unless main_ractor? pp = Object.new.extend(Test::Unit::Assertions) def pp.mu_pp(str) #:nodoc: str.dump @@ -1417,6 +1428,7 @@ def test_truncate def test_flock_exclusive omit "[Bug #18613]" if /freebsd/ =~ RUBY_PLATFORM + omit "subprocess" unless main_ractor? timeout = EnvUtil.apply_timeout_scale(1).to_s File.open(regular_file, "r+") do |f| @@ -1448,6 +1460,7 @@ def test_flock_exclusive def test_flock_shared omit "[Bug #18613]" if /freebsd/ =~ RUBY_PLATFORM + omit "subprocess" unless main_ractor? timeout = EnvUtil.apply_timeout_scale(1).to_s File.open(regular_file, "r+") do |f| diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb index f01a78f66e591d..c4487313ee8341 100644 --- a/test/ruby/test_float.rb +++ b/test/ruby/test_float.rb @@ -867,6 +867,7 @@ def o.to_f; inf = Float::INFINITY; inf/inf; end end def test_invalid_str + omit "under_gc_stress" if multiple_ractors? bug4310 = '[ruby-core:34820]' assert_raise(ArgumentError, bug4310) {under_gc_stress {Float('a'*10000)}} end diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb index fd08332213065b..7d0065ee7e7f08 100644 --- a/test/ruby/test_gc.rb +++ b/test/ruby/test_gc.rb @@ -9,6 +9,7 @@ def initialize(a) end def test_gc + omit "global side effects" if multiple_ractors? prev_stress = GC.stress GC.stress = false @@ -40,6 +41,7 @@ def use_rgengc? end def test_enable_disable + omit "global side effects" if multiple_ractors? EnvUtil.without_gc do GC.enable assert_equal(false, GC.enable) @@ -53,6 +55,7 @@ def test_enable_disable end def test_gc_config_full_mark_by_default + omit "races with other ractors" if multiple_ractors? config = GC.config assert_not_empty(config) assert_true(config[:rgengc_allow_full_mark]) @@ -63,6 +66,7 @@ def test_gc_config_invalid_args end def test_gc_config_setting_returns_updated_config_hash + omit "global side effects" if multiple_ractors? old_value = GC.config[:rgengc_allow_full_mark] assert_true(old_value) @@ -81,6 +85,7 @@ def test_gc_config_setting_returns_config_hash end def test_gc_config_disable_major + omit "global side effects" if multiple_ractors? GC.enable GC.start @@ -103,6 +108,7 @@ def test_gc_config_disable_major end def test_gc_config_disable_major_gc_start_always_works + omit "global side effects" if multiple_ractors? GC.config(full_mark: false) major_count = GC.stat[:major_gc_count] @@ -129,6 +135,7 @@ def test_gc_config_implementation_is_readonly def test_start_full_mark return unless use_rgengc? + omit "latest_gc_info could race" if multiple_ractors? omit 'stress' if GC.stress 3.times { GC.start } # full mark and next time it should be minor mark @@ -140,6 +147,7 @@ def test_start_full_mark end def test_start_immediate_sweep + omit "latest_gc_info could race" if multiple_ractors? omit 'stress' if GC.stress GC.start(immediate_sweep: false) @@ -156,6 +164,7 @@ def test_count end def test_stat + omit "global side effects" if multiple_ractors? res = GC.stat assert_equal(false, res.empty?) assert_kind_of(Integer, res[:count]) @@ -201,6 +210,7 @@ def test_stat_single def test_stat_constraints omit 'stress' if GC.stress + omit "racy" if multiple_ractors? stat = GC.stat # marking_time + sweeping_time could differ from time by 1 because they're stored in nanoseconds @@ -217,6 +227,7 @@ def test_stat_constraints def test_stat_heap omit 'stress' if GC.stress + omit "global side effects" if multiple_ractors? stat_heap = {} stat = {} @@ -273,6 +284,7 @@ def test_stat_heap_all def test_stat_heap_constraints omit 'stress' if GC.stress + omit "races with other ractors" if multiple_ractors? stat = GC.stat stat_heap = GC.stat_heap @@ -349,6 +361,7 @@ def test_latest_gc_info_argument def test_latest_gc_info_need_major_by return unless use_rgengc? omit 'stress' if GC.stress + omit "global side effects" if multiple_ractors? 3.times { GC.start } assert_nil GC.latest_gc_info(:need_major_by) @@ -596,6 +609,7 @@ def test_profiler_clear end def test_profiler_raw_data + omit "racy" if multiple_ractors? GC::Profiler.enable GC.start assert GC::Profiler.raw_data @@ -604,6 +618,7 @@ def test_profiler_raw_data end def test_profiler_total_time + omit "racy" if multiple_ractors? GC::Profiler.enable GC::Profiler.clear @@ -802,6 +817,7 @@ def test_gc_stress_at_startup end def test_gc_disabled_start + omit "global side effects" if multiple_ractors? EnvUtil.without_gc do c = GC.count GC.start @@ -881,6 +897,7 @@ def test_object_ids_never_repeat end def test_ast_node_buffer + omit "TODO: freezes process" if multiple_ractors? # https://github.com/ruby/ruby/pull/4416 Module.new.class_eval( (["# shareable_constant_value: literal"] + (0..100000).map {|i| "M#{ i } = {}" }).join("\n")) diff --git a/test/ruby/test_gc_compact.rb b/test/ruby/test_gc_compact.rb index 3eaa93dfaea0bb..1c6127b649077a 100644 --- a/test/ruby/test_gc_compact.rb +++ b/test/ruby/test_gc_compact.rb @@ -18,6 +18,7 @@ module OmitUnlessCompactSupported def setup omit "GC compaction not supported on this platform" unless supports_compact? + omit "GC.auto_compact = true then setting back to prev is racy" if multiple_ractors? super end end diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index c1edb3d10c6e41..9613d3596d65bb 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -872,6 +872,7 @@ def test_to_s end def test_inspect + omit "global side effects" if multiple_ractors? no_quote = '{a: 1, a!: 1, a?: 1}' quote0 = '{"": 1}' quote1 = '{"0": 1, "!": 1, "%": 1, "&": 1, "*": 1, "+": 1, "-": 1, "/": 1, "<": 1, ">": 1, "^": 1, "`": 1, "|": 1, "~": 1}' @@ -2003,6 +2004,7 @@ def o.to_hash; {3=>4} end end def test_AREF_fstring_key + omit "EnvUtil.without_gc" unless main_ractor? # warmup ObjectSpace.count_objects ObjectSpace.count_objects diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index 49a1b0efd734f8..00ef8f275f8cfa 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -77,6 +77,7 @@ def with_read_pipe(content) end def mkcdtmpdir + omit "Dir.chdir" unless main_ractor? Dir.mktmpdir {|d| Dir.chdir(d) { yield @@ -114,6 +115,7 @@ def test_pipe end def test_binmode_pipe + omit "global side effects" if multiple_ractors? EnvUtil.with_default_internal(Encoding::UTF_8) do EnvUtil.with_default_external(Encoding::UTF_8) do begin @@ -2530,7 +2532,7 @@ def test_autoclose feature2250 = '[ruby-core:26222]' pre = 'ft2250' - Dir.mktmpdir {|d| + Dir.mktmpdir("#{Ractor.current.object_id}") {|d| t = open("#{d}/#{pre}", "w") f = IO.for_fd(t.fileno) assert_equal(true, f.autoclose?) diff --git a/test/ruby/test_io_m17n.rb b/test/ruby/test_io_m17n.rb index 45f8f687b76fa6..72bd8d6c75181a 100644 --- a/test/ruby/test_io_m17n.rb +++ b/test/ruby/test_io_m17n.rb @@ -14,6 +14,7 @@ class TestIO_M17N < Test::Unit::TestCase ].freeze def with_tmpdir + omit "Dir.chdir" unless main_ractor? Dir.mktmpdir {|dir| Dir.chdir(dir) { yield dir @@ -1327,6 +1328,7 @@ def test_stdin_external_encoding_with_reopen end unless /mswin|mingw/ =~ RUBY_PLATFORM # passing non-stdio fds is not supported def test_popen_r_enc + omit "subprocess" unless main_ractor? IO.popen("#{EnvUtil.rubybin} -e 'putc 255'", "r:ascii-8bit") {|f| assert_equal(Encoding::ASCII_8BIT, f.external_encoding) assert_equal(nil, f.internal_encoding) @@ -1337,6 +1339,7 @@ def test_popen_r_enc end def test_popen_r_enc_in_opt + omit "subprocess" unless main_ractor? IO.popen("#{EnvUtil.rubybin} -e 'putc 255'", "r", encoding: "ascii-8bit") {|f| assert_equal(Encoding::ASCII_8BIT, f.external_encoding) assert_equal(nil, f.internal_encoding) @@ -1347,6 +1350,7 @@ def test_popen_r_enc_in_opt end def test_popen_r_enc_in_opt2 + omit "subprocess" unless main_ractor? IO.popen("#{EnvUtil.rubybin} -e 'putc 255'", "r", external_encoding: "ascii-8bit") {|f| assert_equal(Encoding::ASCII_8BIT, f.external_encoding) assert_equal(nil, f.internal_encoding) @@ -1357,6 +1361,7 @@ def test_popen_r_enc_in_opt2 end def test_popen_r_enc_enc + omit "subprocess" unless main_ractor? IO.popen("#{EnvUtil.rubybin} -e 'putc 0xa1'", "r:shift_jis:euc-jp") {|f| assert_equal(Encoding::Shift_JIS, f.external_encoding) assert_equal(Encoding::EUC_JP, f.internal_encoding) @@ -1367,6 +1372,7 @@ def test_popen_r_enc_enc end def test_popen_r_enc_enc_in_opt + omit "subprocess" unless main_ractor? IO.popen("#{EnvUtil.rubybin} -e 'putc 0xa1'", "r", encoding: "shift_jis:euc-jp") {|f| assert_equal(Encoding::Shift_JIS, f.external_encoding) assert_equal(Encoding::EUC_JP, f.internal_encoding) @@ -1377,6 +1383,7 @@ def test_popen_r_enc_enc_in_opt end def test_popen_r_enc_enc_in_opt2 + omit "subprocess" unless main_ractor? IO.popen("#{EnvUtil.rubybin} -e 'putc 0xa1'", "r", external_encoding: "shift_jis", internal_encoding: "euc-jp") {|f| assert_equal(Encoding::Shift_JIS, f.external_encoding) assert_equal(Encoding::EUC_JP, f.internal_encoding) @@ -1387,6 +1394,7 @@ def test_popen_r_enc_enc_in_opt2 end def test_popenv_r_enc_enc_in_opt2 + omit "subprocess" unless main_ractor? IO.popen([EnvUtil.rubybin, "-e", "putc 0xa1"], "r", external_encoding: "shift_jis", internal_encoding: "euc-jp") {|f| assert_equal(Encoding::Shift_JIS, f.external_encoding) assert_equal(Encoding::EUC_JP, f.internal_encoding) @@ -1397,6 +1405,7 @@ def test_popenv_r_enc_enc_in_opt2 end def test_open_pipe_r_enc + omit "racy" if multiple_ractors? EnvUtil.suppress_warning do # https://bugs.ruby-lang.org/issues/19630 open("|#{EnvUtil.rubybin} -e 'putc 255'", "r:ascii-8bit") {|f| assert_equal(Encoding::ASCII_8BIT, f.external_encoding) @@ -1409,6 +1418,7 @@ def test_open_pipe_r_enc end def test_open_pipe_r_enc2 + omit "racy" if multiple_ractors? EnvUtil.suppress_warning do # https://bugs.ruby-lang.org/issues/19630 open("|#{EnvUtil.rubybin} -e 'putc \"\\u3042\"'", "r:UTF-8") {|f| assert_equal(Encoding::UTF_8, f.external_encoding) @@ -2292,12 +2302,14 @@ def test_bom_non_utf assert_equal(Encoding::US_ASCII, enc) tlhInganHol = "\u{f8e4 f8d9 f8d7 f8dc f8d0 f8db} \u{f8d6 f8dd f8d9}" - assert_warn(/#{tlhInganHol}/) { - EnvUtil.with_default_internal(nil) { - open(IO::NULL, "w:bom|#{tlhInganHol}") {|f| enc = f.external_encoding} + if main_ractor? + assert_warn(/#{tlhInganHol}/) { + EnvUtil.with_default_internal(nil) { + open(IO::NULL, "w:bom|#{tlhInganHol}") {|f| enc = f.external_encoding} + } } - } - assert_nil(enc) + assert_nil(enc) + end end def test_bom_non_reading @@ -2797,7 +2809,7 @@ def test_read_with_buf_broken_ascii_only end def test_each_codepoint_need_more - pend "Tempfile" if non_main_ractor? + pend "Tempfile" unless main_ractor? bug11444 = '[ruby-core:70379] [Bug #11444]' tests = [ ["incomplete multibyte", "\u{1f376}".b[0,3], [], ["invalid byte sequence in UTF-8"]], diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb index 3c265f183b64ad..9a72617d33d67b 100644 --- a/test/ruby/test_iseq.rb +++ b/test/ruby/test_iseq.rb @@ -4,6 +4,10 @@ class TestISeq < Test::Unit::TestCase ISeq = RubyVM::InstructionSequence + def setup + omit "not ractor safe" unless main_ractor? + end + def test_no_linenum bug5894 = '[ruby-dev:45130]' assert_normal_exit('p RubyVM::InstructionSequence.compile("1", "mac", "", 0).to_a', bug5894) diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb index 7d951ef9e534c2..2dfa46b084edfc 100644 --- a/test/ruby/test_keyword.rb +++ b/test/ruby/test_keyword.rb @@ -912,6 +912,7 @@ def test_lambda_method_kwsplat_call end def test_Thread_new_kwsplat + omit "global side effects" if multiple_ractors? Thread.report_on_exception = false kw = {} h = {:a=>1} diff --git a/test/ruby/test_m17n.rb b/test/ruby/test_m17n.rb index b0e2e9f849eeeb..9cc9b94dc31d1f 100644 --- a/test/ruby/test_m17n.rb +++ b/test/ruby/test_m17n.rb @@ -186,6 +186,7 @@ def test_string_inspect_invalid end def test_string_inspect_encoding + omit "suppress_warning is racy" if multiple_ractors? EnvUtil.suppress_warning do begin orig_int = Encoding.default_internal @@ -246,6 +247,7 @@ def test_utf_without_bom_valid end def test_object_utf16_32_inspect + omit "suppress_warning is racy" if multiple_ractors? EnvUtil.suppress_warning do begin orig_int = Encoding.default_internal diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index 4518bf3b6c464c..c88a7d54ad3046 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -261,7 +261,7 @@ def test_const_defined? assert_raise(EncodingError) do Math.const_defined?("\xC3") end - end + end unless multiple_ractors? end def each_bad_constants(m, &b) diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index 1e9176103e2eac..c4a96d03ccd559 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -320,7 +320,7 @@ def o.kw(**a) a end assert_equal({foo: 1, bar: 2}, o.kw(foo: 1, bar: 2), bug5989) EnvUtil.under_gc_stress do eval("def o.m(k: 0) k end") - end + end unless multiple_ractors? assert_equal(42, o.m(k: 42), '[ruby-core:45744]') bug7922 = '[ruby-core:52744] [Bug #7922]' def o.bug7922(**) end diff --git a/tool/lib/core_assertions.rb b/tool/lib/core_assertions.rb index 51142d133b8927..14499b05c0421b 100644 --- a/tool/lib/core_assertions.rb +++ b/tool/lib/core_assertions.rb @@ -679,6 +679,7 @@ def assert_pattern_list(pattern_list, actual, message=nil) end def assert_warning(pat, msg = nil) + return if multiple_ractors? # These envutil methods with blocks are racy across ractors result = nil stderr = EnvUtil.with_default_internal(of: pat) { EnvUtil.verbose_warning { diff --git a/tool/lib/envutil.rb b/tool/lib/envutil.rb index 285deef13f5de3..bc1093f7df79ec 100644 --- a/tool/lib/envutil.rb +++ b/tool/lib/envutil.rb @@ -329,6 +329,7 @@ def suppress_warning end module_function :suppress_warning + # NOTE: not safe to use when testing under multiple ractors. def under_gc_stress(stress = true) stress, GC.stress = GC.stress, stress yield @@ -337,6 +338,7 @@ def under_gc_stress(stress = true) end module_function :under_gc_stress + # NOTE: not safe to use when testing under multiple ractors. def under_gc_compact_stress(val = :empty, &block) raise "compaction doesn't work well on s390x. Omit the test in the caller." if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077 @@ -351,6 +353,7 @@ def under_gc_compact_stress(val = :empty, &block) end module_function :under_gc_compact_stress + # NOTE: not safe to use when testing under multiple ractors. def without_gc prev_disabled = GC.disable yield @@ -359,6 +362,7 @@ def without_gc end module_function :without_gc + # NOTE: not safe to use when testing under multiple ractors. def with_default_external(enc = nil, of: nil) enc = of.encoding if defined?(of.encoding) suppress_warning { Encoding.default_external = enc } @@ -368,6 +372,7 @@ def with_default_external(enc = nil, of: nil) end module_function :with_default_external + # NOTE: not safe to use when testing under multiple ractors. def with_default_internal(enc = nil, of: nil) enc = of.encoding if defined?(of.encoding) suppress_warning { Encoding.default_internal = enc } diff --git a/tool/lib/test/unit.rb b/tool/lib/test/unit.rb index c7bf85eb9f286c..9a1f86ae31b5e5 100644 --- a/tool/lib/test/unit.rb +++ b/tool/lib/test/unit.rb @@ -1718,7 +1718,7 @@ def _run_suite suite, type rs = run_tests_inside_ractors_num.times.map do Ractor.new(inst, self) do |instance, runner| res = instance.run runner - testcase_copyable_ivars = {:@_assertions => true, :@__passed__ => true, :@__name__ => true} + testcase_copyable_ivars = {:@_assertions => true, :@__passed__ => true, :@__name__ => true, :@__after_all_ractors_block => true} runner_copyable_ivars = {:@report => true, :@failures => true, :@errors => true, :@skips => true, :@assertion_count => true, :@test_count => true} instance.instance_variables.each do |ivar| unless testcase_copyable_ivars[ivar] diff --git a/tool/lib/zombie_hunter.rb b/tool/lib/zombie_hunter.rb index 33bc46794127f6..b1d3c9eaa5841b 100644 --- a/tool/lib/zombie_hunter.rb +++ b/tool/lib/zombie_hunter.rb @@ -3,7 +3,7 @@ module ZombieHunter def after_teardown super - assert_empty(Process.waitall) + assert_empty(Process.waitall) unless multiple_ractors? end end From 1bb15bdff7efc32d2e969666324dc58b0a21e930 Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Thu, 26 Jun 2025 12:11:51 -0400 Subject: [PATCH 54/60] add VM Lock around `rb_const_remove` operations (Module#remove_const) Without a VM Lock, there's an unlocked `rb_id_table_delete` for the class's const_tbl which can cause problems. Example: ```ruby class C CONSTANT = 3 end $VERBOSE = nil rs = [] 100.times do rs << Ractor.new do 10_000.times do if defined?(C::CONSTANT) C.send(:remove_const, :CONSTANT) rescue NameError else C.send(:const_set, :CONSTANT, 3) end end end end while rs.any? r, obj = Ractor.select(*rs) rs.delete(r) end ``` Without lock: ../ruby-release/test.rb:14: [BUG] Segmentation fault at 0x0000000000000001 -- Control frame information ----------------------------------------------- miniruby(82790,0x16f49f000) malloc: *** error for object 0x600000f880a0: pointer being freed was not allocated miniruby(82790,0x16f49f000) malloc: *** set a breakpoint in malloc_error_break to debug --- variable.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/variable.c b/variable.c index 4f0f83d20343fb..b395057c84bc3b 100644 --- a/variable.c +++ b/variable.c @@ -3717,29 +3717,31 @@ rb_const_remove(VALUE mod, ID id) rb_check_frozen(mod); - ce = rb_const_lookup(mod, id); - if (!ce || !rb_id_table_delete(RCLASS_WRITABLE_CONST_TBL(mod), id)) { - if (rb_const_defined_at(mod, id)) { - rb_name_err_raise("cannot remove %2$s::%1$s", mod, ID2SYM(id)); - } + RB_VM_LOCKING() { + ce = rb_const_lookup(mod, id); + if (!ce || !rb_id_table_delete(RCLASS_WRITABLE_CONST_TBL(mod), id)) { + if (rb_const_defined_at(mod, id)) { + rb_name_err_raise("cannot remove %2$s::%1$s", mod, ID2SYM(id)); + } - undefined_constant(mod, ID2SYM(id)); - } + undefined_constant(mod, ID2SYM(id)); + } - rb_const_warn_if_deprecated(ce, mod, id); - rb_clear_constant_cache_for_id(id); + rb_const_warn_if_deprecated(ce, mod, id); + rb_clear_constant_cache_for_id(id); - val = ce->value; + val = ce->value; - if (UNDEF_P(val)) { - autoload_delete(mod, id); - val = Qnil; - } + if (UNDEF_P(val)) { + autoload_delete(mod, id); + val = Qnil; + } - if (ce != const_lookup(RCLASS_PRIME_CONST_TBL(mod), id)) { - ruby_xfree(ce); + if (ce != const_lookup(RCLASS_PRIME_CONST_TBL(mod), id)) { + ruby_xfree(ce); + } + // else - skip free'ing the ce because it still exists in the prime classext } - // else - skip free'ing the ce because it still exists in the prime classext return val; } From d964b35962a1e4fe516f1f5db9ab8f3a2b33f02e Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Tue, 12 Aug 2025 12:53:17 -0400 Subject: [PATCH 55/60] Ractors: fixes for parallel const_set/remove_const and const_get --- constant.h | 2 +- variable.c | 133 ++++++++++++++++++++++++++++-------------------- vm_insnhelper.c | 7 +-- 3 files changed, 83 insertions(+), 59 deletions(-) diff --git a/constant.h b/constant.h index 90a68d447a6208..3d4982c936da0a 100644 --- a/constant.h +++ b/constant.h @@ -44,7 +44,7 @@ void rb_free_const_table(struct rb_id_table *tbl); VALUE rb_const_source_location(VALUE, ID); int rb_autoloading_value(VALUE mod, ID id, VALUE *value, rb_const_flag_t *flag); -rb_const_entry_t *rb_const_lookup(VALUE klass, ID id); +rb_const_entry_t *rb_const_lookup(VALUE klass, ID id, rb_const_entry_t *entry_out); VALUE rb_public_const_get_at(VALUE klass, ID id); VALUE rb_public_const_get_from(VALUE klass, ID id); int rb_public_const_defined_from(VALUE klass, ID id); diff --git a/variable.c b/variable.c index b395057c84bc3b..f9788b980df716 100644 --- a/variable.c +++ b/variable.c @@ -2995,9 +2995,10 @@ static VALUE autoload_synchronized(VALUE _arguments) { struct autoload_arguments *arguments = (struct autoload_arguments *)_arguments; + rb_const_entry_t constant_entry = {0}; - rb_const_entry_t *constant_entry = rb_const_lookup(arguments->module, arguments->name); - if (constant_entry && !UNDEF_P(constant_entry->value)) { + rb_const_entry_t *ce = rb_const_lookup(arguments->module, arguments->name, &constant_entry); + if (ce && !UNDEF_P(constant_entry.value)) { return Qfalse; } @@ -3198,13 +3199,15 @@ autoloading_const_entry(VALUE mod, ID id) return 0; } + static int autoload_defined_p(VALUE mod, ID id) { - rb_const_entry_t *ce = rb_const_lookup(mod, id); + rb_const_entry_t ce_out = {0}; + rb_const_entry_t *ce = rb_const_lookup(mod, id, &ce_out); // If there is no constant or the constant is not undefined (special marker for autoloading): - if (!ce || !UNDEF_P(ce->value)) { + if (!ce || !UNDEF_P(ce_out.value)) { // We are not autoloading: return 0; } @@ -3393,11 +3396,12 @@ autoload_try_load(VALUE _arguments) struct autoload_load_arguments *arguments = (struct autoload_load_arguments*)_arguments; VALUE result = autoload_feature_require(_arguments); + rb_const_entry_t ce_out = {0}; // After we loaded the feature, if the constant is not defined, we remove it completely: - rb_const_entry_t *ce = rb_const_lookup(arguments->module, arguments->name); + rb_const_entry_t *ce = rb_const_lookup(arguments->module, arguments->name, &ce_out); - if (!ce || UNDEF_P(ce->value)) { + if (!ce || UNDEF_P(ce_out.value)) { result = Qfalse; rb_const_remove(arguments->module, arguments->name); @@ -3429,10 +3433,11 @@ autoload_try_load(VALUE _arguments) VALUE rb_autoload_load(VALUE module, ID name) { - rb_const_entry_t *ce = rb_const_lookup(module, name); + rb_const_entry_t ce_out = {0}; + rb_const_entry_t *ce = rb_const_lookup(module, name, &ce_out); // We bail out as early as possible without any synchronisation: - if (!ce || !UNDEF_P(ce->value)) { + if (!ce || !UNDEF_P(ce_out.value)) { return Qfalse; } @@ -3450,7 +3455,7 @@ rb_autoload_load(VALUE module, ID name) // This confirms whether autoloading is required or not: if (autoload_const_value == Qfalse) return autoload_const_value; - arguments.flag = ce->flag & (CONST_DEPRECATED | CONST_VISIBILITY_MASK); + arguments.flag = ce_out.flag & (CONST_DEPRECATED | CONST_VISIBILITY_MASK); // Only one thread will enter here at a time: VALUE result = rb_mutex_synchronize(arguments.mutex, autoload_try_load, (VALUE)&arguments); @@ -3521,6 +3526,7 @@ rb_const_search_from(VALUE klass, ID id, int exclude, int recurse, int visibilit { VALUE value, current; bool first_iteration = true; + rb_const_entry_t ce_out = {0}; for (current = klass; RTEST(current); @@ -3542,13 +3548,13 @@ rb_const_search_from(VALUE klass, ID id, int exclude, int recurse, int visibilit if (BUILTIN_TYPE(tmp) == T_ICLASS) tmp = RBASIC(tmp)->klass; // Do the lookup. Loop in case of autoload. - while ((ce = rb_const_lookup(tmp, id))) { - if (visibility && RB_CONST_PRIVATE_P(ce)) { + while ((ce = rb_const_lookup(tmp, id, &ce_out))) { + if (visibility && RB_CONST_PRIVATE_P(&ce_out)) { GET_EC()->private_const_reference = tmp; return Qundef; } - rb_const_warn_if_deprecated(ce, tmp, id); - value = ce->value; + rb_const_warn_if_deprecated(&ce_out, tmp, id); + value = ce_out.value; if (UNDEF_P(value)) { struct autoload_const *ac; if (am == tmp) break; @@ -3628,16 +3634,17 @@ rb_const_location_from(VALUE klass, ID id, int exclude, int recurse, int visibil { while (RTEST(klass)) { rb_const_entry_t *ce; + rb_const_entry_t ce_out = {0}; - while ((ce = rb_const_lookup(klass, id))) { - if (visibility && RB_CONST_PRIVATE_P(ce)) { + while ((ce = rb_const_lookup(klass, id, &ce_out))) { + if (visibility && RB_CONST_PRIVATE_P(&ce_out)) { return Qnil; } if (exclude && klass == rb_cObject) { goto not_found; } - if (UNDEF_P(ce->value)) { // autoload + if (UNDEF_P(ce_out.value)) { // autoload VALUE autoload_const_value = autoload_data(klass, id); if (RTEST(autoload_const_value)) { struct autoload_const *autoload_const; @@ -3649,8 +3656,8 @@ rb_const_location_from(VALUE klass, ID id, int exclude, int recurse, int visibil } } - if (NIL_P(ce->file)) return rb_ary_new(); - return rb_assoc_new(ce->file, INT2NUM(ce->line)); + if (NIL_P(ce_out.file)) return rb_ary_new(); + return rb_assoc_new(ce_out.file, INT2NUM(ce_out.line)); } if (!recurse) break; klass = RCLASS_SUPER(klass); @@ -3707,18 +3714,19 @@ rb_mod_remove_const(VALUE mod, VALUE name) return rb_const_remove(mod, id); } -static rb_const_entry_t * const_lookup(struct rb_id_table *tbl, ID id); +static rb_const_entry_t * const_lookup(struct rb_id_table *tbl, ID id, rb_const_entry_t *ce_out); VALUE rb_const_remove(VALUE mod, ID id) { VALUE val; rb_const_entry_t *ce; + rb_const_entry_t ce_out = {0}; rb_check_frozen(mod); RB_VM_LOCKING() { - ce = rb_const_lookup(mod, id); + ce = rb_const_lookup(mod, id, &ce_out); if (!ce || !rb_id_table_delete(RCLASS_WRITABLE_CONST_TBL(mod), id)) { if (rb_const_defined_at(mod, id)) { rb_name_err_raise("cannot remove %2$s::%1$s", mod, ID2SYM(id)); @@ -3730,14 +3738,14 @@ rb_const_remove(VALUE mod, ID id) rb_const_warn_if_deprecated(ce, mod, id); rb_clear_constant_cache_for_id(id); - val = ce->value; + val = ce_out.value; if (UNDEF_P(val)) { autoload_delete(mod, id); val = Qnil; } - if (ce != const_lookup(RCLASS_PRIME_CONST_TBL(mod), id)) { + if (ce != const_lookup(RCLASS_PRIME_CONST_TBL(mod), id, NULL)) { ruby_xfree(ce); } // else - skip free'ing the ce because it still exists in the prime classext @@ -3881,15 +3889,16 @@ rb_const_defined_0(VALUE klass, ID id, int exclude, int recurse, int visibility) VALUE tmp; int mod_retry = 0; rb_const_entry_t *ce; + rb_const_entry_t ce_out = {0}; tmp = klass; retry: while (tmp) { - if ((ce = rb_const_lookup(tmp, id))) { - if (visibility && RB_CONST_PRIVATE_P(ce)) { + if ((ce = rb_const_lookup(tmp, id, &ce_out))) { + if (visibility && RB_CONST_PRIVATE_P(&ce_out)) { return (int)Qfalse; } - if (UNDEF_P(ce->value) && !check_autoload_required(tmp, id, 0) && + if (UNDEF_P(ce_out.value) && !check_autoload_required(tmp, id, 0) && !rb_autoloading_value(tmp, id, NULL, NULL)) return (int)Qfalse; @@ -4017,8 +4026,8 @@ const_set(VALUE klass, ID id, VALUE val) RCLASS_WRITE_CONST_TBL(klass, tbl, false); rb_clear_constant_cache_for_id(id); ce = ZALLOC(rb_const_entry_t); - rb_id_table_insert(tbl, id, (VALUE)ce); setup_const_entry(ce, klass, val, CONST_PUBLIC); + rb_id_table_insert(tbl, id, (VALUE)ce); } else { struct autoload_const ac = { @@ -4031,6 +4040,7 @@ const_set(VALUE klass, ID id, VALUE val) } } + // TODO: I think this needs to be locked /* * Resolve and cache class name immediately to resolve ambiguity * and avoid order-dependency on const_tbl @@ -4095,6 +4105,8 @@ const_tbl_update(struct autoload_const *ac, int autoload_force) rb_const_flag_t visibility = ac->flag; rb_const_entry_t *ce; + ASSERT_vm_locking(); + if (rb_id_table_lookup(tbl, id, &value)) { ce = (rb_const_entry_t *)value; if (UNDEF_P(ce->value)) { @@ -4141,8 +4153,8 @@ const_tbl_update(struct autoload_const *ac, int autoload_force) rb_clear_constant_cache_for_id(id); ce = ZALLOC(rb_const_entry_t); - rb_id_table_insert(tbl, id, (VALUE)ce); setup_const_entry(ce, klass, val, visibility); + rb_id_table_insert(tbl, id, (VALUE)ce); } } @@ -4190,29 +4202,31 @@ set_const_visibility(VALUE mod, int argc, const VALUE *argv, return; } - for (i = 0; i < argc; i++) { - struct autoload_const *ac; - VALUE val = argv[i]; - id = rb_check_id(&val); - if (!id) { - undefined_constant(mod, val); - } - if ((ce = rb_const_lookup(mod, id))) { - ce->flag &= ~mask; - ce->flag |= flag; - if (UNDEF_P(ce->value)) { - struct autoload_data *ele; - - ele = autoload_data_for_named_constant(mod, id, &ac); - if (ele) { - ac->flag &= ~mask; - ac->flag |= flag; + RB_VM_LOCKING() { + for (i = 0; i < argc; i++) { + struct autoload_const *ac; + VALUE val = argv[i]; + id = rb_check_id(&val); + if (!id) { + undefined_constant(mod, val); + } + if ((ce = rb_const_lookup(mod, id, NULL))) { + ce->flag &= ~mask; + ce->flag |= flag; + if (UNDEF_P(ce->value)) { + struct autoload_data *ele; + + ele = autoload_data_for_named_constant(mod, id, &ac); + if (ele) { + ac->flag &= ~mask; + ac->flag |= flag; + } } + rb_clear_constant_cache_for_id(id); + } + else { + undefined_constant(mod, ID2SYM(id)); } - rb_clear_constant_cache_for_id(id); - } - else { - undefined_constant(mod, ID2SYM(id)); } } } @@ -4228,10 +4242,12 @@ rb_deprecate_constant(VALUE mod, const char *name) if (!(id = rb_check_id_cstr(name, len, NULL))) { undefined_constant(mod, rb_fstring_new(name, len)); } - if (!(ce = rb_const_lookup(mod, id))) { - undefined_constant(mod, ID2SYM(id)); + RB_VM_LOCKING() { + if (!(ce = rb_const_lookup(mod, id, NULL))) { + undefined_constant(mod, ID2SYM(id)); + } + ce->flag |= CONST_DEPRECATED; } - ce->flag |= CONST_DEPRECATED; } /* @@ -4788,23 +4804,30 @@ rb_fields_tbl_copy(VALUE dst, VALUE src) } static rb_const_entry_t * -const_lookup(struct rb_id_table *tbl, ID id) +const_lookup(struct rb_id_table *tbl, ID id, rb_const_entry_t *entry_out) { if (tbl) { + rb_const_entry_t *ce; VALUE val; bool r; RB_VM_LOCKING() { r = rb_id_table_lookup(tbl, id, &val); + if (r) { + ce = (rb_const_entry_t*)val; + if (entry_out) *entry_out = *ce; + } } - if (r) return (rb_const_entry_t *)val; + if (r) { + return ce; + } } return NULL; } rb_const_entry_t * -rb_const_lookup(VALUE klass, ID id) +rb_const_lookup(VALUE klass, ID id, rb_const_entry_t *entry_out) { - return const_lookup(RCLASS_CONST_TBL(klass), id); + return const_lookup(RCLASS_CONST_TBL(klass), id, entry_out); } diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 3aca1bc24f35eb..fd10921cd0fe1d 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1092,10 +1092,11 @@ vm_get_ev_const(rb_execution_context_t *ec, VALUE orig_klass, ID id, bool allow_ if (!NIL_P(klass)) { VALUE av, am = 0; rb_const_entry_t *ce; + rb_const_entry_t ce_out = {0}; search_continue: - if ((ce = rb_const_lookup(klass, id))) { - rb_const_warn_if_deprecated(ce, klass, id); - val = ce->value; + if ((ce = rb_const_lookup(klass, id, &ce_out))) { + rb_const_warn_if_deprecated(&ce_out, klass, id); + val = ce_out.value; if (UNDEF_P(val)) { if (am == klass) break; am = klass; From 3f916ebd517bc0a980f77e21484842153a33deed Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Thu, 3 Jul 2025 09:06:10 -0400 Subject: [PATCH 56/60] More ractor test-all fixes --- test/ruby/test_argf.rb | 4 +-- test/ruby/test_autoload.rb | 26 ++++++++------- test/ruby/test_class.rb | 1 + test/ruby/test_defined.rb | 4 ++- test/ruby/test_dir.rb | 28 ++++++++-------- test/ruby/test_gc.rb | 13 +++++--- test/ruby/test_hash.rb | 1 + test/ruby/test_integer.rb | 2 +- test/ruby/test_io.rb | 18 ++++++++++ test/ruby/test_io_buffer.rb | 2 ++ test/ruby/test_marshal.rb | 60 ++++++++++++++++++++-------------- test/ruby/test_math.rb | 18 ++++++++-- test/ruby/test_metaclass.rb | 1 + test/ruby/test_method.rb | 6 ++-- test/ruby/test_module.rb | 41 +++++++++++++++-------- test/ruby/test_object_id.rb | 16 +++++---- test/ruby/test_parse.rb | 1 + test/ruby/test_primitive.rb | 15 +++++---- test/ruby/test_process.rb | 38 ++++++++++++++++++--- test/ruby/test_rand.rb | 1 + test/ruby/test_regexp.rb | 8 ++--- test/ruby/test_require.rb | 9 +++++ test/ruby/test_rubyoptions.rb | 2 +- test/ruby/test_settracefunc.rb | 1 + test/ruby/test_signal.rb | 3 ++ test/ruby/test_string.rb | 34 ++++++++++++++----- test/ruby/test_struct.rb | 16 ++++++--- test/ruby/test_super.rb | 1 + test/ruby/test_syntax.rb | 12 +++---- test/ruby/test_system.rb | 1 + test/ruby/test_thread.rb | 7 +++- test/ruby/test_thread_cv.rb | 1 + test/ruby/test_thread_queue.rb | 1 + test/ruby/test_time.rb | 17 ++++++---- test/ruby/test_time_tz.rb | 2 ++ test/ruby/test_variable.rb | 4 +-- tool/lib/core_assertions.rb | 23 ++++++++----- tool/lib/envutil.rb | 40 +++++++++++++++++++---- tool/lib/test/unit.rb | 7 +++- 39 files changed, 341 insertions(+), 144 deletions(-) diff --git a/test/ruby/test_argf.rb b/test/ruby/test_argf.rb index f868b56f32c5c0..6d209e25cd9bde 100644 --- a/test/ruby/test_argf.rb +++ b/test/ruby/test_argf.rb @@ -7,7 +7,7 @@ class TestArgf < Test::Unit::TestCase def setup - omit "ARGF is not shareable" if non_main_ractor? + omit "ARGF is not shareable" unless main_ractor? @tmpdir = Dir.mktmpdir @tmp_count = 0 @t1 = make_tempfile("argf-foo", %w"1 2", binmode: true) @@ -16,7 +16,7 @@ def setup end def teardown - return if non_main_ractor? + return unless main_ractor? FileUtils.rmtree(@tmpdir) end diff --git a/test/ruby/test_autoload.rb b/test/ruby/test_autoload.rb index 3672ce1df9a30c..f5a208caf155f7 100644 --- a/test/ruby/test_autoload.rb +++ b/test/ruby/test_autoload.rb @@ -3,6 +3,10 @@ require 'tempfile' class TestAutoload < Test::Unit::TestCase + def setup + pend "autoload" unless main_ractor? + end + def test_autoload_so # Date is always available, unless excluded intentionally. assert_in_out_err([], <<-INPUT, [], []) @@ -66,7 +70,7 @@ def test_autoload_p end def test_autoload_p_with_static_extensions - pend "belonging issue" if non_main_ractor? + pend "belonging issue" unless main_ractor? require 'rbconfig' omit unless RbConfig::CONFIG['EXTSTATIC'] == 'static' begin @@ -85,7 +89,7 @@ def test_autoload_p_with_static_extensions end def test_autoload_with_unqualified_file_name # [ruby-core:69206] - omit "global variable access" if non_main_ractor? + omit "global variable access" unless main_ractor? Object.send(:remove_const, :A) if Object.const_defined?(:A) lp = $LOAD_PATH.dup @@ -114,7 +118,7 @@ module A end def test_require_explicit - pend "Tempfile" if non_main_ractor? + pend "Tempfile" unless main_ractor? Tempfile.create(['autoload', '.rb']) {|file| file.puts 'class Object; AutoloadTest = 1; end' file.close @@ -131,7 +135,7 @@ def test_require_explicit end def test_threaded_accessing_constant - pend "Tempfile" if non_main_ractor? + pend "Tempfile" unless main_ractor? # Suppress "warning: loading in progress, circular require considered harmful" EnvUtil.default_warning { Tempfile.create(['autoload', '.rb']) {|file| @@ -152,7 +156,7 @@ def test_threaded_accessing_constant end def test_threaded_accessing_inner_constant - pend "Tempfile" if non_main_ractor? + pend "Tempfile" unless main_ractor? # Suppress "warning: loading in progress, circular require considered harmful" EnvUtil.default_warning { Tempfile.create(['autoload', '.rb']) {|file| @@ -173,7 +177,7 @@ def test_threaded_accessing_inner_constant end def test_nameerror_when_autoload_did_not_define_the_constant - pend "Tempfile" if non_main_ractor? + pend "Tempfile" unless main_ractor? verbose_bak, $VERBOSE = $VERBOSE, nil Tempfile.create(['autoload', '.rb']) {|file| file.puts '' @@ -192,7 +196,7 @@ def test_nameerror_when_autoload_did_not_define_the_constant end def test_override_autoload - pend "Tempfile" if non_main_ractor? + pend "Tempfile" unless main_ractor? Tempfile.create(['autoload', '.rb']) {|file| file.puts '' file.close @@ -207,7 +211,7 @@ def test_override_autoload end def test_override_while_autoloading - pend "Tempfile" if non_main_ractor? + pend "Tempfile" unless main_ractor? Tempfile.create(['autoload', '.rb']) {|file| file.puts 'class AutoloadTest; sleep 0.5; end' file.close @@ -259,7 +263,7 @@ def ruby_impl_require end def test_require_implemented_in_ruby_is_called - pend "Tempfile" if non_main_ractor? + pend "Tempfile" unless main_ractor? ruby_impl_require do |called_with| Tempfile.create(['autoload', '.rb']) {|file| file.puts 'class AutoloadTest; end' @@ -277,7 +281,7 @@ def test_require_implemented_in_ruby_is_called end def test_autoload_while_autoloading - pend "Tempfile" if non_main_ractor? + pend "Tempfile" unless main_ractor? ruby_impl_require do |called_with| Tempfile.create(%w(a .rb)) do |a| Tempfile.create(%w(b .rb)) do |b| @@ -407,7 +411,7 @@ class AutoloadTest end def test_autoload_fork - pend "Tempfile" if non_main_ractor? + pend "Tempfile" unless main_ractor? EnvUtil.default_warning do Tempfile.create(['autoload', '.rb']) {|file| file.puts 'sleep 0.3; class AutoloadTest; end' diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index fc361f36308b6e..57a5075e55120c 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -291,6 +291,7 @@ def test_uninitialized end def test_nonascii_name + omit "global side effects" if multiple_ractors? c = eval("class ::C\u{df}; self; end") assert_equal("C\u{df}", c.name, '[ruby-core:24600]') c = eval("class C\u{df}; self; end") diff --git a/test/ruby/test_defined.rb b/test/ruby/test_defined.rb index d3d1ed55455778..a7e02fb2b7c0bf 100644 --- a/test/ruby/test_defined.rb +++ b/test/ruby/test_defined.rb @@ -373,6 +373,8 @@ def x; b {return defined?(super)}; end end def test_super_in_basic_object + omit "global side effects" if multiple_ractors? + run_ensure = true BasicObject.class_eval do def a defined?(super) @@ -383,7 +385,7 @@ def a ensure BasicObject.class_eval do undef_method :a if defined?(a) - end + end if run_ensure end def test_super_toplevel diff --git a/test/ruby/test_dir.rb b/test/ruby/test_dir.rb index 7c2eac5d970133..5ab46ae1c6d16d 100644 --- a/test/ruby/test_dir.rb +++ b/test/ruby/test_dir.rb @@ -7,6 +7,7 @@ class TestDir < Test::Unit::TestCase def setup + omit "lots of Dir.chdir" if multiple_ractors? @verbose = $VERBOSE @root = File.realpath(Dir.mktmpdir('__test_dir__')) @nodir = File.join(@root, "dummy") @@ -23,6 +24,7 @@ def setup end def teardown + return if multiple_ractors? $VERBOSE = @verbose FileUtils.remove_entry_secure @root if File.directory?(@root) ENV.update(@envs) if @envs @@ -97,7 +99,7 @@ def test_rewind end def test_class_chdir - omit "not ractor safe" unless main_ractor? + omit "Dir.chdir" unless main_ractor? pwd = Dir.pwd setup_envs @@ -137,7 +139,7 @@ def test_class_chdir end def test_instance_chdir - omit "not ractor safe" unless main_ractor? + omit "Dir.chdir" unless main_ractor? pwd = Dir.pwd dir = Dir.new(pwd) root_dir = Dir.new(@root) @@ -205,7 +207,7 @@ def Warning.warn(message) end def test_chdir_conflict - omit "not ractor safe" unless main_ractor? + omit "Dir.chdir" unless main_ractor? pwd = Dir.pwd q = Thread::Queue.new t = Thread.new do @@ -282,7 +284,7 @@ def test_glob end def test_glob_recursive - omit "not ractor safe" unless main_ractor? + omit "Dir.chdir" unless main_ractor? bug6977 = '[ruby-core:47418]' bug8006 = '[ruby-core:53108] [Bug #8006]' Dir.chdir(@root) do @@ -312,7 +314,7 @@ def test_glob_recursive end def test_glob_recursive_directory - omit "not ractor safe" unless main_ractor? + omit "Dir.chdir" unless main_ractor? Dir.chdir(@root) do ['d', 'e'].each do |path| FileUtils.mkdir_p("c/#{path}/a/b/c") @@ -330,7 +332,7 @@ def test_glob_recursive_directory end def test_glob_starts_with_brace - omit "not ractor safe" unless main_ractor? + omit "Dir.chdir" unless main_ractor? Dir.chdir(@root) do bug15649 = '[ruby-core:91728] [Bug #15649]' assert_equal(["#{@root}/a", "#{@root}/b"], @@ -339,7 +341,7 @@ def test_glob_starts_with_brace end def test_glob_recursive_with_brace - omit "not ractor safe" unless main_ractor? + omit "Dir.chdir" unless main_ractor? Dir.chdir(@root) do bug19042 = '[ruby-core:110220] [Bug #19042]' %w"c/dir_a c/dir_b c/dir_b/dir".each do |d| @@ -354,7 +356,7 @@ def test_glob_recursive_with_brace end def test_glob_order - omit "not ractor safe" unless main_ractor? + omit "Dir.chdir" unless main_ractor? Dir.chdir(@root) do assert_equal(["#{@root}/a", "#{@root}/b"], Dir.glob("#{@root}/[ba]")) assert_equal(["#{@root}/b", "#{@root}/a"], Dir.glob(%W"#{@root}/b #{@root}/a")) @@ -384,7 +386,7 @@ def test_glob_too_may_open_files end def test_glob_base - omit "not ractor safe (Dir.chdir)" unless main_ractor? + omit "Dir.chdir" unless main_ractor? files = %w[a/foo.c c/bar.c] files.each {|n| File.write(File.join(@root, n), "")} Dir.mkdir(File.join(@root, "a/dir")) @@ -425,7 +427,7 @@ def test_glob_base end def test_glob_base_dir - omit "not ractor safe" unless main_ractor? + omit "Dir.chdir" unless main_ractor? files = %w[a/foo.c c/bar.c] files.each {|n| File.write(File.join(@root, n), "")} Dir.mkdir(File.join(@root, "a/dir")) @@ -448,7 +450,7 @@ def test_glob_base_dir end def test_glob_ignore_casefold_invalid_encoding - omit "not ractor safe" unless main_ractor? + omit "Dir.chdir" unless main_ractor? bug14456 = "[ruby-core:85448]" filename = "\u00AAa123".encode('ISO-8859-1') File.write(File.join(@root, filename), "") @@ -566,7 +568,7 @@ def test_glob_metachar end def test_glob_cases - omit "not ractor safe (Dir.chdir)" unless main_ractor? + omit "Dir.chdir" unless main_ractor? feature5994 = "[ruby-core:42469] [Feature #5994]" feature5994 << "\nDir.glob should return the filename with actual cases on the filesystem" Dir.chdir(File.join(@root, "a")) do @@ -732,7 +734,7 @@ def test_fileno end def test_for_fd - omit "not ractor safe" unless main_ractor? + omit "Dir.chdir" unless main_ractor? if Dir.respond_to? :for_fd begin new_dir = Dir.new('..') diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb index 7d0065ee7e7f08..ffc5888fd8d1dc 100644 --- a/test/ruby/test_gc.rb +++ b/test/ruby/test_gc.rb @@ -323,6 +323,7 @@ def test_measure_total_time def test_latest_gc_info omit 'stress' if GC.stress + omit "GC.stress=" if multiple_ractors? assert_separately([], __FILE__, __LINE__, <<-'RUBY') GC.start @@ -342,10 +343,12 @@ def test_latest_gc_info assert_equal true, h[:immediate_sweep] assert_equal true, h.key?(:need_major_by) - GC.stress = true - assert_equal :force, GC.latest_gc_info[:major_by] - ensure - GC.stress = false + begin + GC.stress = true + assert_equal :force, GC.latest_gc_info[:major_by] + ensure + GC.stress = false + end end def test_latest_gc_info_argument @@ -904,7 +907,7 @@ def test_ast_node_buffer end def test_old_to_young_reference - pend "ObjectSpace.dump not yet ractor safe" if non_main_ractor? + pend "ObjectSpace.dump not yet ractor safe" unless main_ractor? EnvUtil.without_gc do require "objspace" diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index 9613d3596d65bb..85c9dfafe7dc88 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -2265,6 +2265,7 @@ def test_label_syntax end def test_broken_hash_value + omit "too many objects" if multiple_ractors? bug14218 = '[ruby-core:84395] [Bug #14218]' assert_equal(0, 1_000_000.times.count{a=Object.new.hash; b=Object.new.hash; a < 0 && b < 0 && a + b > 0}, bug14218) diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb index f9bf4fa20c80c1..a6b93724389246 100644 --- a/test/ruby/test_integer.rb +++ b/test/ruby/test_integer.rb @@ -7,7 +7,7 @@ class TestInteger < Test::Unit::TestCase LONG_MAX = RbConfig::LIMITS['LONG_MAX'] def test_aref - + pend "TODO: why taking too long" if multiple_ractors? [ *-16..16, *(FIXNUM_MIN-2)..(FIXNUM_MIN+2), diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index 00ef8f275f8cfa..feb1ee3f21ac7f 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -1860,6 +1860,7 @@ def test_close_write_non_readable end def test_close_read_write_separately + pend "TODO: buggy with ractors" if multiple_ractors? bug = '[ruby-list:49598]' (1..10).each do |i| assert_nothing_raised(IOError, "#{bug} trying ##{i}") do @@ -2529,6 +2530,7 @@ def try_fdopen(fd, autoclose = true, level = 50) end def test_autoclose + pend "TODO: assert_raise fails sometimes under multiple ractors" if multiple_ractors? feature2250 = '[ruby-core:26222]' pre = 'ft2250' @@ -2615,6 +2617,7 @@ def o.to_open(kw); kw; end end def test_open_pipe + omit "fork" unless main_ractor? assert_deprecated_warning(/Kernel#open with a leading '\|'/) do # https://bugs.ruby-lang.org/issues/19630 open("|" + EnvUtil.rubybin, "r+") do |f| f.puts "puts 'foo'" @@ -3993,6 +3996,7 @@ def test_sysread_unlocktmp_ensure end if /cygwin/ !~ RUBY_PLATFORM def test_exception_at_close + pend "TODO: assert_raise fails sometimes under multiple ractors" if multiple_ractors? bug10153 = '[ruby-core:64463] [Bug #10153] exception in close at the end of block' assert_raise(Errno::EBADF, bug10153) do IO.pipe do |r, w| @@ -4472,4 +4476,18 @@ def test_fork_close assert_predicate(status, :success?) RUBY end + + def test_no_fork_in_ractor + omit "fork is not supported" unless Process.respond_to?(:fork) + + assert_ractor(<<~'RUBY') + r = Ractor.new do + fork { } + end + end + assert_raise Ractor::IsolationError do + r.value + end + RUBY + end end diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb index e3cd5be46ba47d..afb57f01312c68 100644 --- a/test/ruby/test_io_buffer.rb +++ b/test/ruby/test_io_buffer.rb @@ -617,6 +617,7 @@ def test_inplace_operators end def test_shared + omit "fork" unless main_ractor? message = "Hello World" buffer = IO::Buffer.new(64, IO::Buffer::MAPPED | IO::Buffer::SHARED) @@ -705,6 +706,7 @@ def test_set_string_null_destination # https://bugs.ruby-lang.org/issues/21210 def test_bug_21210 omit "compaction is not supported on this platform" unless GC.respond_to?(:compact) + omit "verify_compaction_references" unless main_ractor? str = +"hello" buf = IO::Buffer.for(str) diff --git a/test/ruby/test_marshal.rb b/test/ruby/test_marshal.rb index 6c79913d76dcb9..3155cda8097016 100644 --- a/test/ruby/test_marshal.rb +++ b/test/ruby/test_marshal.rb @@ -72,23 +72,27 @@ def test_marshal_cloned_class end def test_inconsistent_struct - TestMarshal.const_set :StructOrNot, Struct.new(:a) - s = Marshal.dump(StructOrNot.new(1)) - TestMarshal.instance_eval { remove_const :StructOrNot } - TestMarshal.const_set :StructOrNot, Class.new + const_name = "StructOrNot#{Ractor.current.object_id}".to_sym + TestMarshal.const_set const_name, Struct.new(:a) + s = Marshal.dump(TestMarshal.const_get(const_name).new(1)) + TestMarshal.instance_eval { remove_const const_name } + TestMarshal.const_set const_name, Class.new assert_raise(TypeError, "[ruby-dev:31709]") { Marshal.load(s) } ensure - TestMarshal.instance_eval { remove_const :StructOrNot } + TestMarshal.instance_eval { remove_const const_name } end def test_struct_invalid_members - TestMarshal.const_set :StructInvalidMembers, Struct.new(:a) - assert_raise(TypeError, "[ruby-dev:31759]") { - Marshal.load("\004\bIc&TestMarshal::StructInvalidMembers\006:\020__members__\"\bfoo") - TestMarshal::StructInvalidMembers.members - } - ensure - TestMarshal.instance_eval { remove_const :StructInvalidMembers } + omit "global side effects" if multiple_ractors? + begin + TestMarshal.const_set :StructInvalidMembers, Struct.new(:a) + assert_raise(TypeError, "[ruby-dev:31759]") { + Marshal.load("\004\bIc&TestMarshal::StructInvalidMembers\006:\020__members__\"\bfoo") + TestMarshal::StructInvalidMembers.members + } + ensure + TestMarshal.instance_eval { remove_const :StructInvalidMembers } + end end def test_load_range_as_struct @@ -187,6 +191,8 @@ def test_modify_array_during_dump end def test_change_class_name + omit "global side effects" if multiple_ractors? + run_ensure = true self.class.__send__(:remove_const, :C3) if self.class.const_defined?(:C3) eval("class C3; def _dump(s); 'foo'; end; end") m = Marshal.dump(C3.new) @@ -195,10 +201,14 @@ def test_change_class_name eval("C3 = nil") assert_raise(TypeError) { Marshal.load(m) } ensure - self.class.__send__(:remove_const, :C3) if self.class.const_defined?(:C3) + if run_ensure + self.class.__send__(:remove_const, :C3) if self.class.const_defined?(:C3) + end end def test_change_struct + omit "global side effects" if multiple_ractors? + run_ensure = true self.class.__send__(:remove_const, :C3) if self.class.const_defined?(:C3) eval("C3 = Struct.new(:foo, :bar)") m = Marshal.dump(C3.new("FOO", "BAR")) @@ -209,7 +219,9 @@ def test_change_struct eval("C3 = Struct.new(:foo, :baz)") assert_raise(TypeError) { Marshal.load(m) } ensure - self.class.__send__(:remove_const, :C3) if self.class.const_defined?(:C3) + if run_ensure + self.class.__send__(:remove_const, :C3) if self.class.const_defined?(:C3) + end end class C4 @@ -663,28 +675,28 @@ def test_continuation end def test_undumpable_message - c = Module.new {break module_eval("class IO\u{26a1} < IO;self;end")} - assert_raise_with_message(TypeError, /IO\u{26a1}/) { + c = Module.new {break module_eval("class IO#{Ractor.current.object_id}\u{26a1} < IO;self;end")} + assert_raise_with_message(TypeError, /IO#{Ractor.current.object_id}\u{26a1}/) { Marshal.dump(c.new(0, autoclose: false)) } end def test_undumpable_data - c = Module.new {break module_eval("class T\u{23F0 23F3}::O\z/, m::O.name) assert_match(/\A#::C\z/, m::C.name) - self.class.const_set(:M, m) - prefix = self.class.name + "::M::" + m_name = "M#{Ractor.current.object_id}" + self.class.const_set(m_name, m) + prefix = self.class.name + "::#{m_name}::" assert_equal(prefix+"N", m.const_get(:N).name) assert_equal(prefix+"O", m.const_get(:O).name) assert_equal(prefix+"C", m.const_get(:C).name) c = m.class_eval("Bug15891 = Class.new.freeze") assert_equal(prefix+"Bug15891", c.name) ensure - self.class.class_eval {remove_const(:M)} + self.class.class_eval {remove_const(m_name)} end def test_private_class_method @@ -1128,6 +1137,7 @@ def bar; end end def test_s_constants + omit "global side effects" unless main_ractor? c1 = Module.constants Object.module_eval "WALTER = 99" c2 = Module.constants @@ -1756,14 +1766,15 @@ def test_send def test_nonascii_name - c = eval("class ::C\u{df}; self; end") - assert_equal("C\u{df}", c.name, '[ruby-core:24600]') - c = eval("class C\u{df}; self; end") - assert_equal("TestModule::C\u{df}", c.name, '[ruby-core:24600]') + c_name = "C#{Ractor.current.object_id}" + c = eval("class ::#{c_name}\u{df}; self; end") + assert_equal("#{c_name}\u{df}", c.name, '[ruby-core:24600]') + c = eval("class #{c_name}\u{df}; self; end") + assert_equal("TestModule::#{c_name}\u{df}", c.name, '[ruby-core:24600]') c = Module.new.module_eval("class X\u{df} < Module; self; end") assert_match(/::X\u{df}:/, c.new.to_s) ensure - Object.send(:remove_const, "C\u{df}") + Object.send(:remove_const, "#{c_name}\u{df}") end @@ -2129,16 +2140,17 @@ class PrivateClass private_constant :PrivateClass def test_define_module_under_private_constant + const_name = "TestModule#{Ractor.current.object_id}" assert_raise(NameError) do eval %q{class TestModule::PrivateClass; end} end assert_raise(NameError) do - eval %q{module TestModule::PrivateClass::TestModule; end} + eval %Q{module TestModule::PrivateClass::#{const_name}; end} end eval %q{class PrivateClass; end} - eval %q{module PrivateClass::TestModule; end} - assert_instance_of(Module, PrivateClass::TestModule) - PrivateClass.class_eval { remove_const(:TestModule) } + eval %Q{module PrivateClass::#{const_name}; end} + assert_instance_of(Module, PrivateClass.const_get(const_name)) + PrivateClass.class_eval { remove_const(const_name) } end def test_public_constant @@ -2153,6 +2165,7 @@ def test_public_constant end def test_deprecate_constant + omit "Setting Warning[]" if multiple_ractors? c = Class.new c.const_set(:FOO, "foo".freeze) c.deprecate_constant(:FOO) diff --git a/test/ruby/test_object_id.rb b/test/ruby/test_object_id.rb index 3783f6d1fda871..368826480ca233 100644 --- a/test/ruby/test_object_id.rb +++ b/test/ruby/test_object_id.rb @@ -12,7 +12,7 @@ def test_dup_new_id end def test_dup_with_ivar_and_id - omit "class ivars" if @obj.is_a?(Module) && non_main_ractor? + omit "class ivars" if @obj.is_a?(Module) && !main_ractor? id = @obj.object_id @obj.instance_variable_set(:@foo, 42) @@ -22,7 +22,7 @@ def test_dup_with_ivar_and_id end def test_dup_with_id_and_ivar - omit "class ivars" if @obj.is_a?(Module) && non_main_ractor? + omit "class ivars" if @obj.is_a?(Module) && !main_ractor? @obj.instance_variable_set(:@foo, 42) id = @obj.object_id @@ -32,7 +32,7 @@ def test_dup_with_id_and_ivar end def test_dup_with_id_and_ivar_and_frozen - omit "class ivars" if @obj.is_a?(Module) && non_main_ractor? + omit "class ivars" if @obj.is_a?(Module) && !main_ractor? @obj.instance_variable_set(:@foo, 42) @obj.freeze id = @obj.object_id @@ -49,7 +49,7 @@ def test_clone_new_id end def test_clone_with_ivar_and_id - omit "class ivars" if @obj.is_a?(Module) && non_main_ractor? + omit "class ivars" if @obj.is_a?(Module) && !main_ractor? id = @obj.object_id @obj.instance_variable_set(:@foo, 42) @@ -59,7 +59,7 @@ def test_clone_with_ivar_and_id end def test_clone_with_id_and_ivar - omit "class ivars" if @obj.is_a?(Module) && non_main_ractor? + omit "class ivars" if @obj.is_a?(Module) && !main_ractor? @obj.instance_variable_set(:@foo, 42) id = @obj.object_id @@ -69,7 +69,7 @@ def test_clone_with_id_and_ivar end def test_clone_with_id_and_ivar_and_frozen - omit "class ivars" if @obj.is_a?(Module) && non_main_ractor? + omit "class ivars" if @obj.is_a?(Module) && !main_ractor? @obj.instance_variable_set(:@foo, 42) @obj.freeze id = @obj.object_id @@ -82,6 +82,7 @@ def test_clone_with_id_and_ivar_and_frozen def test_marshal_new_id return pass if @obj.is_a?(Module) + omit "class ivars" if @obj.is_a?(Module) && !main_ractor? id = @obj.object_id refute_equal id, Marshal.load(Marshal.dump(@obj)).object_id @@ -123,6 +124,7 @@ def test_marshal_with_id_and_ivar_and_frozen end def test_object_id_need_resize + omit "class ivars" if !main_ractor? && @obj.is_a?(Module) (3 - @obj.instance_variables.size).times do |i| @obj.instance_variable_set("@a_#{i}", "[Bug #21445]") end @@ -171,7 +173,7 @@ class TooComplex < Module end def setup - omit "class ivars" if non_main_ractor? + omit "class ivars" unless main_ractor? if defined?(RubyVM::Shape::SHAPE_MAX_VARIATIONS) assert_equal 8, RubyVM::Shape::SHAPE_MAX_VARIATIONS end diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb index 891e2f1a85fddb..72e7c6cff7344b 100644 --- a/test/ruby/test_parse.rb +++ b/test/ruby/test_parse.rb @@ -1139,6 +1139,7 @@ def test_unused_variable end def test_parsing_begin_statement_inside_method_definition + omit "global side effects" if multiple_ractors? assert_equal :bug_20234, eval("def (begin;end).bug_20234; end") NilClass.remove_method(:bug_20234) assert_equal :bug_20234, eval("def (begin;rescue;end).bug_20234; end") diff --git a/test/ruby/test_primitive.rb b/test/ruby/test_primitive.rb index 038d404d6e152f..782c09c48dc8d3 100644 --- a/test/ruby/test_primitive.rb +++ b/test/ruby/test_primitive.rb @@ -49,6 +49,7 @@ def const end) def test_constant + omit "global side effects" if multiple_ractors? C_Setup.call assert_equal 1, C @@ -109,7 +110,7 @@ class A4 end def test_constant_cache3 - omit "global variable access" if non_main_ractor? + omit "global variable access" unless main_ractor? assert_equal 7, $test_ruby_primitive_constant_cache3 end @@ -121,7 +122,7 @@ class A5 end def test_constatant_cache4 - omit "global variable access" if non_main_ractor? + omit "global variable access" unless main_ractor? assert_equal 8, $test_ruby_primitive_constant_cache4 end @@ -140,12 +141,12 @@ class C6 < B6 $test_ruby_primitive_constant_cache5 = [A6.foo, B6.foo, C6.foo] def test_constant_cache5 - omit "global variable access" if non_main_ractor? + omit "global variable access" unless main_ractor? assert_equal [0, 1, 2], $test_ruby_primitive_constant_cache5 end def test_gvar - omit "global variable access" if non_main_ractor? + omit "global variable access" unless main_ractor? $test_ruby_primitive_gvar = 7 assert_equal 7, $test_ruby_primitive_gvar assert_equal 7, $test_ruby_primitive_gvar @@ -168,7 +169,7 @@ def m end def test_cvar_from_instance_method - omit "class variables" if non_main_ractor? + omit "class variables" unless main_ractor? A7_Setup.call assert_equal 2, A7.new.m @@ -190,7 +191,7 @@ def m end def test_cvar_from_singleton_method - omit "class variables" if non_main_ractor? + omit "class variables" unless main_ractor? A8_Setup.call assert_equal 2, A8.m @@ -210,7 +211,7 @@ def self.m end def test_cvar_from_singleton_method2 - omit "class variables" if non_main_ractor? + omit "class variables" unless main_ractor? A9_Setup.call assert_equal 2, A9.m diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index 94d2dcdbc02708..9982ec9e9670ea 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -9,11 +9,11 @@ class TestProcess < Test::Unit::TestCase RUBY = EnvUtil.rubybin.freeze def setup - Process.waitall + Process.waitall unless multiple_ractors? end def teardown - Process.waitall + Process.waitall unless multiple_ractors? end def windows? @@ -24,6 +24,7 @@ def self.windows? end def with_tmpchdir + omit "Dir.chdir" if multiple_ractors? Dir.mktmpdir {|d| d = File.realpath(d) Dir.chdir(d) { @@ -33,6 +34,7 @@ def with_tmpchdir end def run_in_child(str) # should be called in a temporary directory + omit "Process.spawn" unless main_ractor? File.write("test-script", str) Process.wait spawn(RUBY, "test-script") $? @@ -155,6 +157,7 @@ def test_rlimit_value TRUECOMMAND = Ractor.make_shareable([RUBY, '-e', '']) def test_execopts_opts + omit "spawn" unless main_ractor? assert_nothing_raised { Process.wait Process.spawn(*TRUECOMMAND, {}) } @@ -168,6 +171,7 @@ def test_execopts_opts def test_execopts_pgroup omit "system(:pgroup) is not supported" if windows? + omit "system" unless main_ractor? assert_nothing_raised { system(*TRUECOMMAND, :pgroup=>false) } io = IO.popen([RUBY, "-e", "print Process.getpgrp"]) @@ -196,6 +200,7 @@ def test_execopts_pgroup end def test_execopts_rlimit + omit "IO.popen" unless main_ractor? return unless rlimit_exist? assert_raise(ArgumentError) { system(*TRUECOMMAND, :rlimit_foo=>0) } assert_raise(ArgumentError) { system(*TRUECOMMAND, :rlimit_NOFILE=>0) } @@ -368,6 +373,7 @@ def test_execopts_env end def test_execopt_env_path + omit "spawn" unless main_ractor? bug8004 = '[ruby-core:53103] [Bug #8004]' Dir.mktmpdir do |d| File.write("#{d}/tmp_script.cmd", ": ;\n", perm: 0o755) @@ -378,6 +384,7 @@ def test_execopt_env_path end def _test_execopts_env_popen(cmd) + omit "ENV change" if multiple_ractors? message = cmd.inspect IO.popen({"FOO"=>"BAR"}, cmd) {|io| assert_equal('FOO=BAR', io.read[/^FOO=.*/], message) @@ -446,6 +453,7 @@ def test_execopts_env_single_word end def test_execopts_unsetenv_others + omit "ENV change" if multiple_ractors? h = {} MANDATORY_ENVS.each {|k| e = ENV[k] and h[k] = e} IO.popen([h, *ENVCOMMAND, :unsetenv_others=>true]) {|io| @@ -737,6 +745,7 @@ def test_execopts_redirect_open_fifo_interrupt_print end unless windows? # does not support fifo def test_execopts_redirect_pipe + omit "spawn" unless main_ractor? with_pipe {|r1, w1| with_pipe {|r2, w2| opts = {$stdin=>r1, $stdout=>w2} @@ -930,6 +939,7 @@ def test_execopts_popen_extra_fd if Process.respond_to?(:fork) def test_popen_fork + omit "popen fork" unless main_ractor? IO.popen("-") do |io| if !io puts "fooo" @@ -940,7 +950,7 @@ def test_popen_fork end def test_popen_fork_ensure - pend "ractor_confirm_belonging issue on fork" if non_main_ractor? + omit "popen fork" unless main_ractor? IO.popen("-") do |io| if !io $stderr.reopen($stdout) # issue is here @@ -956,6 +966,7 @@ def test_popen_fork_ensure def test_fd_inheritance omit "inheritance of fd other than stdin,stdout and stderr is not supported" if windows? + omit "spawn" unless main_ractor? with_pipe {|r, w| system(RUBY, '-e', 'IO.new(ARGV[0].to_i, "w").puts(:ba)', w.fileno.to_s, w=>w) w.close @@ -1120,6 +1131,7 @@ def test_execopts_duplex_io end def test_execopts_modification + omit "spawn" unless main_ractor? h = {} Process.wait spawn(*TRUECOMMAND, h) assert_equal({}, h) @@ -1871,6 +1883,7 @@ def test_system_sigpipe if Process.respond_to?(:daemon) def test_daemon_default + omit "fork" unless main_ractor? data = IO.popen("-", "r+") do |f| break f.read if f Process.daemon @@ -1880,6 +1893,7 @@ def test_daemon_default end def test_daemon_noclose + omit "fork" unless main_ractor? data = IO.popen("-", "r+") do |f| break f.read if f Process.daemon(false, true) @@ -1889,6 +1903,7 @@ def test_daemon_noclose end def test_daemon_nochdir_noclose + omit "fork" unless main_ractor? data = IO.popen("-", "r+") do |f| break f.read if f Process.daemon(true, true) @@ -1898,6 +1913,7 @@ def test_daemon_nochdir_noclose end def test_daemon_readwrite + omit "fork" unless main_ractor? data = IO.popen("-", "r+") do |f| if f f.puts "ok?" @@ -1910,6 +1926,7 @@ def test_daemon_readwrite end def test_daemon_pid + omit "fork" unless main_ractor? cpid, dpid = IO.popen("-", "r+") do |f| break f.pid, Integer(f.read) if f Process.daemon(false, true) @@ -1919,6 +1936,7 @@ def test_daemon_pid end def test_daemon_detached + omit "fork" unless main_ractor? IO.popen("-", "r+") do |f| if f assert_equal(f.pid, Process.wait(f.pid)) @@ -1942,6 +1960,7 @@ def test_daemon_detached if File.directory?("/proc/self/task") && /netbsd[a-z]*[1-6]/ !~ RUBY_PLATFORM def test_daemon_no_threads + omit "fork" unless main_ractor? pid, data = IO.popen("-", "r+") do |f| break f.pid, f.readlines if f Process.daemon(true, true) @@ -1953,7 +1972,7 @@ def test_daemon_no_threads end else # darwin def test_daemon_no_threads - pend "Timeout" if non_main_ractor? + omit "fork" unless main_ractor? data = EnvUtil.timeout(3) do IO.popen("-") do |f| break f.readlines.map(&:chomp) if f @@ -2015,6 +2034,7 @@ def test_popen_reopen def test_execopts_new_pgroup return unless windows? + omit "system" unless main_ractor? assert_nothing_raised { system(*TRUECOMMAND, :new_pgroup=>true) } assert_nothing_raised { system(*TRUECOMMAND, :new_pgroup=>false) } @@ -2024,6 +2044,7 @@ def test_execopts_new_pgroup def test_execopts_uid omit "root can use uid option of Kernel#system on Android platform" if RUBY_PLATFORM =~ /android/ + omit "system" unless main_ractor? feature6975 = '[ruby-core:47414]' [30000, [Process.uid, ENV["USER"]]].each do |uid, user| @@ -2056,6 +2077,7 @@ def test_execopts_uid def test_execopts_gid omit "Process.groups not implemented on Windows platform" if windows? omit "root can use Process.groups on Android platform" if RUBY_PLATFORM =~ /android/ + omit "system" unless main_ractor? feature6975 = '[ruby-core:47414]' groups = Process.groups.map do |g| @@ -2141,7 +2163,7 @@ def test_setsid assert_equal(Marshal.load(io), Process.getsid(io.pid)) ensure Process.kill(:KILL, io.pid) rescue nil - Process.wait(io.pid) + Process.wait(io.pid) rescue nil end end end @@ -2410,6 +2432,7 @@ def test_deadlock_by_signal_at_forking end if defined?(fork) def test_process_detach + omit "fork" unless main_ractor? pid = fork {} th = Process.detach(pid) assert_equal pid, th.pid @@ -2601,17 +2624,20 @@ def test_initgroups end def test_last_status + omit "spawn" unless main_ractor? Process.wait spawn(RUBY, "-e", "exit 13") assert_same(Process.last_status, $?) end def test_last_status_failure + omit "system" unless main_ractor? assert_nil system("sad") assert_not_predicate $?, :success? assert_equal $?.exitstatus, 127 end def test_exec_failure_leaves_no_child + omit "spawn" unless main_ractor? assert_raise(Errno::ENOENT) do spawn('inexistent_command') end @@ -2619,6 +2645,7 @@ def test_exec_failure_leaves_no_child end def test__fork + omit "_fork" unless main_ractor? r, w = IO.pipe pid = Process._fork if pid == 0 @@ -2638,6 +2665,7 @@ def test__fork end if Process.respond_to?(:_fork) def test__fork_pid_cache + omit "_fork" unless main_ractor? _parent_pid = Process.pid r, w = IO.pipe pid = Process._fork diff --git a/test/ruby/test_rand.rb b/test/ruby/test_rand.rb index d72d237154ac46..866f12567d8c8d 100644 --- a/test/ruby/test_rand.rb +++ b/test/ruby/test_rand.rb @@ -268,6 +268,7 @@ def test_random_equal end def test_fork_shuffle + omit "fork" unless main_ractor? pid = fork do (1..10).to_a.shuffle raise 'default seed is not set' if srand == 0 diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb index 6f1a45f82d12e1..3afc1fb8b57519 100644 --- a/test/ruby/test_regexp.rb +++ b/test/ruby/test_regexp.rb @@ -1012,7 +1012,7 @@ def test_regsub_no_memory_leak end def test_ignorecase - omit "global variable access" if non_main_ractor? + omit "global variable access" unless main_ractor? v = assert_deprecated_warning(/variable \$= is no longer effective/) { $= } assert_equal(false, v) assert_deprecated_warning(/variable \$= is no longer effective; ignored/) { $= = nil } @@ -1069,7 +1069,7 @@ def test_last_match end def test_getter - omit "global variable access" if non_main_ractor? + omit "global variable access" unless main_ractor? alias $__REGEXP_TEST_LASTMATCH__ $& alias $__REGEXP_TEST_PREMATCH__ $` alias $__REGEXP_TEST_POSTMATCH__ $' @@ -1981,7 +1981,7 @@ def test_s_timeout_memory_leak end def test_bug_20453 - pend "Timeout" if non_main_ractor? + pend "Timeout" unless main_ractor? re = Regexp.new("^(a*)x$", timeout: 0.001) assert_raise(Regexp::TimeoutError) do @@ -1990,7 +1990,7 @@ def test_bug_20453 end def test_bug_20886 - pend "Timeout" if non_main_ractor? + pend "Timeout" unless main_ractor? re = Regexp.new("d()*+|a*a*bc", timeout: 0.02) assert_raise(Regexp::TimeoutError) do re === "b" + "a" * 1000 diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb index ac18d7bb67425e..3c083755f56fc2 100644 --- a/test/ruby/test_require.rb +++ b/test/ruby/test_require.rb @@ -500,6 +500,7 @@ def test_relative end def test_relative_symlink + omit "Dir.chdir" if multiple_ractors? Dir.mktmpdir {|tmp| Dir.chdir(tmp) { Dir.mkdir "a" @@ -519,6 +520,7 @@ def test_relative_symlink end def test_relative_symlink_realpath + omit "Dir.chdir" if multiple_ractors? Dir.mktmpdir {|tmp| Dir.chdir(tmp) { Dir.mkdir "a" @@ -667,6 +669,7 @@ def test_default_loaded_features_encoding end def test_require_changed_current_dir + omit "Dir.chdir" if multiple_ractors? bug7158 = '[ruby-core:47970]' Dir.mktmpdir {|tmp| Dir.chdir(tmp) { @@ -692,6 +695,7 @@ def test_require_changed_current_dir end def test_require_not_modified_load_path + omit "Dir.chdir" if multiple_ractors? bug7158 = '[ruby-core:47970]' Dir.mktmpdir {|tmp| Dir.chdir(tmp) { @@ -713,6 +717,7 @@ def a.to_str end def test_require_changed_home + omit "Dir.chdir" if multiple_ractors? bug7158 = '[ruby-core:47970]' Dir.mktmpdir {|tmp| Dir.chdir(tmp) { @@ -733,6 +738,7 @@ def test_require_changed_home end def test_require_to_path_redefined_in_load_path + omit "Dir.chdir" if multiple_ractors? bug7158 = '[ruby-core:47970]' Dir.mktmpdir {|tmp| Dir.chdir(tmp) { @@ -761,6 +767,7 @@ def a.to_path end def test_require_to_str_redefined_in_load_path + omit "Dir.chdir" if multiple_ractors? bug7158 = '[ruby-core:47970]' Dir.mktmpdir {|tmp| Dir.chdir(tmp) { @@ -789,6 +796,7 @@ def a.to_str end def assert_require_with_shared_array_modified(add, del) + omit "Dir.chdir" if multiple_ractors? bug7383 = '[ruby-core:49518]' Dir.mktmpdir {|tmp| Dir.chdir(tmp) { @@ -826,6 +834,7 @@ def test_require_with_array_shift end def test_require_local_var_on_toplevel + omit "Dir.chdir" if multiple_ractors? bug7536 = '[ruby-core:50701]' Dir.mktmpdir {|tmp| Dir.chdir(tmp) { diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb index 4982229cd15c2d..c64c7c73adc34b 100644 --- a/test/ruby/test_rubyoptions.rb +++ b/test/ruby/test_rubyoptions.rb @@ -44,7 +44,7 @@ def with_tmpchdir end def setup - omit "separate process" if non_main_ractor? + omit "separate process" unless main_ractor? end def test_source_file diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index 6c7719839cf526..f469180dd25da7 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -4,6 +4,7 @@ class TestSetTraceFunc < Test::Unit::TestCase def setup + omit "Tracepoint not working in ractors" unless main_ractor? if defined?(RubyVM) @original_compile_option = RubyVM::InstructionSequence.compile_option RubyVM::InstructionSequence.compile_option = { diff --git a/test/ruby/test_signal.rb b/test/ruby/test_signal.rb index 456cd8476ec34e..3e1cb4173c91b7 100644 --- a/test/ruby/test_signal.rb +++ b/test/ruby/test_signal.rb @@ -27,6 +27,7 @@ def test_signal end if Process.respond_to?(:kill) def test_signal_process_group + omit "Process.spawn" unless main_ractor? bug4362 = '[ruby-dev:43169]' assert_nothing_raised(bug4362) do cmd = [ EnvUtil.rubybin, '--disable=gems' '-e', 'sleep 10' ] @@ -135,6 +136,7 @@ def o.to_str; "SIGINT"; end end if Process.respond_to?(:kill) def test_trap + omit "not ractor safe" unless main_ractor? begin oldtrap = Signal.trap(:INT) {|sig| } @@ -316,6 +318,7 @@ def test_signal_list_dedupe_keys def test_self_stop omit unless Process.respond_to?(:fork) omit unless defined?(Process::WUNTRACED) + omit "fork" unless main_ractor? # Make a process that stops itself child_pid = fork do diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index f287637892fa25..c3fabd9bbd2dbd 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -408,6 +408,8 @@ def test_center end def test_chomp + omit "$/ set" if multiple_ractors? + run_ensure = true verbose, $VERBOSE = $VERBOSE, nil assert_equal(S("hello"), S("hello").chomp("\n")) @@ -474,11 +476,15 @@ def test_chomp s = "foo\r" assert_equal("foo", s.chomp("\n")) ensure - $/ = save - $VERBOSE = verbose + if run_ensure + $/ = save + $VERBOSE = verbose + end end def test_chomp! + omit "$/ set " if multiple_ractors? + run_ensure = true verbose, $VERBOSE = $VERBOSE, nil a = S("hello") @@ -598,8 +604,10 @@ def o.to_str assert_raise(ArgumentError) {String.new.chomp!("", "")} ensure - $/ = save - $VERBOSE = verbose + if run_ensure + $/ = save + $VERBOSE = verbose + end end def test_chop @@ -947,6 +955,8 @@ def test_ivar_set_after_frozen_dup end def test_each + omit "set $/" if multiple_ractors? + run_ensure = true verbose, $VERBOSE = $VERBOSE, nil save = $/ @@ -967,8 +977,10 @@ def test_each assert_equal(S("hello!"), res[0]) assert_equal(S("world"), res[1]) ensure - $/ = save - $VERBOSE = verbose + if run_ensure + $/ = save + $VERBOSE = verbose + end end def test_each_byte @@ -1138,6 +1150,8 @@ def test_byteslice_grapheme_clusters end def test_each_line + omit "set $/" if multiple_ractors? + run_ensure = true verbose, $VERBOSE = $VERBOSE, nil save = $/ @@ -1185,8 +1199,10 @@ def test_each_line S("\n\u0100").each_line("\n") {} end ensure - $/ = save - $VERBOSE = verbose + if run_ensure + $/ = save + $VERBOSE = verbose + end end def test_each_line_chomp @@ -3426,6 +3442,7 @@ def test_uminus_dedup_in_place def test_uminus_frozen return unless @cls == String + omit "not always same" if multiple_ractors? # embedded str1 = ("foobar" * 3).freeze @@ -3733,6 +3750,7 @@ def test_chilled_string end def test_chilled_string_setivar + omit "monkey patch" if multiple_ractors? deprecated = Warning[:deprecated] Warning[:deprecated] = false diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb index 6a659276076059..662269dd521ca3 100644 --- a/test/ruby/test_struct.rb +++ b/test/ruby/test_struct.rb @@ -5,6 +5,7 @@ module TestStruct def test_struct + omit "constant redef" if multiple_ractors? struct_test = @Struct.new("Test", :foo, :bar) assert_equal(@Struct::Test, struct_test) @@ -99,6 +100,7 @@ def test_set end def test_struct_new + omit "global side effects" if multiple_ractors? assert_raise(NameError) { @Struct.new("foo") } assert_nothing_raised { @Struct.new("Foo") } @Struct.instance_eval { remove_const(:Foo) } @@ -114,6 +116,7 @@ def test_struct_new_with_hash end def test_struct_new_with_keyword_init + omit "constant redef" if multiple_ractors? @Struct.new("KeywordInitTrue", :a, :b, keyword_init: true) @Struct.new("KeywordInitFalse", :a, :b, keyword_init: false) @@ -203,10 +206,12 @@ def test_inspect o.a = o assert_match(/^#:...>>$/, o.inspect) - @Struct.new("Foo", :a) - o = @Struct::Foo.new(1) - assert_equal("#", o.inspect) - @Struct.instance_eval { remove_const(:Foo) } + unless multiple_ractors? + @Struct.new("Foo", :a) + o = @Struct::Foo.new(1) + assert_equal("#", o.inspect) + @Struct.instance_eval { remove_const(:Foo) } + end klass = @Struct.new(:a, :b) o = klass.new(1, 2) @@ -359,6 +364,7 @@ def test_error end def test_redefinition_warning + omit "constant redef" unless main_ractor? @Struct.new(name = "RedefinitionWarning") e = EnvUtil.verbose_warning do @Struct.new("RedefinitionWarning") @@ -382,6 +388,7 @@ def test_keyword_args_warning end def test_nonascii + omit "constant redef" if multiple_ractors? struct_test = @Struct.new(name = "R\u{e9}sum\u{e9}", :"r\u{e9}sum\u{e9}") assert_equal(@Struct.const_get("R\u{e9}sum\u{e9}"), struct_test, '[ruby-core:24849]') a = struct_test.new(42) @@ -399,6 +406,7 @@ def test_nonascii end def test_junk + omit "global side effects" if multiple_ractors? struct_test = @Struct.new("Foo", "a\000") o = struct_test.new(1) assert_equal(1, o.send("a\000")) diff --git a/test/ruby/test_super.rb b/test/ruby/test_super.rb index b6abea1c54d162..905800161680c4 100644 --- a/test/ruby/test_super.rb +++ b/test/ruby/test_super.rb @@ -541,6 +541,7 @@ def foo(result) # [Bug #18329] def test_super_missing_prepended_module + omit "GC.stress" if multiple_ractors? a = Module.new do def probe(*methods) prepend(probing_module(methods)) diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index c4a96d03ccd559..2f257ae98b7f25 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -32,7 +32,7 @@ def test_defined_empty_argument end def test_must_ascii_compatible - pend "Tempfile" if non_main_ractor? + pend "Tempfile" unless main_ractor? require 'tempfile' f = Tempfile.new("must_ac_") Encoding.list.each do |enc| @@ -50,7 +50,7 @@ def test_must_ascii_compatible end def test_script_lines - pend "Tempfile" if non_main_ractor? + pend "Tempfile" unless main_ractor? require 'tempfile' f = Tempfile.new("bug4361_") bug4361 = '[ruby-dev:43168]' @@ -320,8 +320,8 @@ def o.kw(**a) a end assert_equal({foo: 1, bar: 2}, o.kw(foo: 1, bar: 2), bug5989) EnvUtil.under_gc_stress do eval("def o.m(k: 0) k end") + assert_equal(42, o.m(k: 42), '[ruby-core:45744]') end unless multiple_ractors? - assert_equal(42, o.m(k: 42), '[ruby-core:45744]') bug7922 = '[ruby-core:52744] [Bug #7922]' def o.bug7922(**) end assert_nothing_raised(ArgumentError, bug7922) {o.bug7922(foo: 42)} @@ -836,7 +836,7 @@ def test_reserved_method_no_args end def test_unassignable - omit "global variable access" if non_main_ractor? + omit "global variable access" unless main_ractor? gvar = global_variables %w[self nil true false __FILE__ __LINE__ __ENCODING__].each do |kwd| assert_syntax_error("#{kwd} = nil", /Can't .* #{kwd}$/) @@ -1488,7 +1488,7 @@ def test_brace_after_literal_argument end def test_return_toplevel - pend "Tempfile" if non_main_ractor? + pend "Tempfile" unless main_ractor? feature4840 = '[ruby-core:36785] [Feature #4840]' line = __LINE__+2 code = "#{<<~"begin;"}#{<<~'end;'}" @@ -1538,7 +1538,7 @@ def test_return_toplevel end def test_eval_return_toplevel - pend "Tempfile" if non_main_ractor? + pend "Tempfile" unless main_ractor? feature4840 = '[ruby-core:36785] [Feature #4840]' line = __LINE__+2 code = "#{<<~"begin;"}#{<<~'end;'}" diff --git a/test/ruby/test_system.rb b/test/ruby/test_system.rb index 3fcdaa6aada73e..2566f06d6105e0 100644 --- a/test/ruby/test_system.rb +++ b/test/ruby/test_system.rb @@ -118,6 +118,7 @@ def test_system_redirect_win if /mswin|mingw/ !~ RUBY_PLATFORM return end + omit "Dir.chdir" if multiple_ractors? Dir.mktmpdir("ruby_script_tmp") do |tmpdir| cmd = nil diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb index a3b74aee186a38..9fdc4d6d29c738 100644 --- a/test/ruby/test_thread.rb +++ b/test/ruby/test_thread.rb @@ -1218,6 +1218,7 @@ def test_machine_stack_size end unless /mswin|mingw/ =~ RUBY_PLATFORM def test_blocking_mutex_unlocked_on_fork + omit "fork" unless main_ractor? bug8433 = '[ruby-core:55102] [Bug #8433]' mutex = Thread::Mutex.new @@ -1243,7 +1244,7 @@ def test_blocking_mutex_unlocked_on_fork end if Process.respond_to?(:fork) def test_fork_in_thread - pend "fork ractor_confirm_belonging issue" if non_main_ractor? + omit "fork" unless main_ractor? bug9751 = '[ruby-core:62070] [Bug #9751]' f = nil th = Thread.start do @@ -1265,6 +1266,7 @@ def test_fork_in_thread end if Process.respond_to?(:fork) def test_fork_value + omit "fork" unless main_ractor? bug18902 = "[Bug #18902]" th = Thread.start { sleep 2 } begin @@ -1280,6 +1282,7 @@ def test_fork_value end if Process.respond_to?(:fork) def test_fork_while_locked + omit "fork" unless main_ractor? m = Thread::Mutex.new thrs = [] 3.times do |i| @@ -1292,6 +1295,7 @@ def test_fork_while_locked def test_fork_while_parent_locked omit 'needs fork' unless Process.respond_to?(:fork) + omit "fork" unless main_ractor? m = Thread::Mutex.new nr = 1 thrs = [] @@ -1458,6 +1462,7 @@ def test_thread_native_thread_id end def test_thread_native_thread_id_across_fork_on_linux + omit "fork" unless main_ractor? begin require '-test-/thread/id' rescue LoadError diff --git a/test/ruby/test_thread_cv.rb b/test/ruby/test_thread_cv.rb index 9c825079909d1d..7abf44428ed150 100644 --- a/test/ruby/test_thread_cv.rb +++ b/test/ruby/test_thread_cv.rb @@ -222,6 +222,7 @@ def test_dump end def test_condvar_fork + omit "fork" unless main_ractor? mutex = Thread::Mutex.new condvar = Thread::ConditionVariable.new thrs = (1..10).map do diff --git a/test/ruby/test_thread_queue.rb b/test/ruby/test_thread_queue.rb index 756dfcd6da5a31..86295fe81f20f4 100644 --- a/test/ruby/test_thread_queue.rb +++ b/test/ruby/test_thread_queue.rb @@ -682,6 +682,7 @@ def test_queue_with_trap end def test_fork_while_queue_waiting + omit "fork" unless main_ractor? q = Thread::Queue.new sq = Thread::SizedQueue.new(1) thq = Thread.new { q.pop } diff --git a/test/ruby/test_time.rb b/test/ruby/test_time.rb index 9cb52a6d92423d..9f618f20b1120b 100644 --- a/test/ruby/test_time.rb +++ b/test/ruby/test_time.rb @@ -442,12 +442,14 @@ def test_marshal_zone assert_equal('UTC', t.zone) assert_equal('UTC', Marshal.load(Marshal.dump(t)).zone) - in_timezone('JST-9') do - t = Time.local(2013, 2, 24) - assert_equal('JST', Time.local(2013, 2, 24).zone) - t = Marshal.load(Marshal.dump(t)) - assert_equal('JST', t.zone) - assert_equal('JST', (t+1).zone, '[ruby-core:81892] [Bug #13710]') + unless multiple_ractors? + in_timezone('JST-9') do + t = Time.local(2013, 2, 24) + assert_equal('JST', Time.local(2013, 2, 24).zone) + t = Marshal.load(Marshal.dump(t)) + assert_equal('JST', t.zone) + assert_equal('JST', (t+1).zone, '[ruby-core:81892] [Bug #13710]') + end end end @@ -476,6 +478,7 @@ def test_marshal_to_s Bug8795 = '[ruby-core:56648] [Bug #8795]'.freeze def test_marshal_broken_offset + omit "global side effects" if multiple_ractors? data = "\x04\bIu:\tTime\r\xEFF\x1C\x80\x00\x00\x00\x00\x06:\voffset" t1 = t2 = nil in_timezone('UTC') do @@ -489,6 +492,7 @@ def test_marshal_broken_offset end def test_marshal_broken_zone + omit "global side effects" if multiple_ractors? data = "\x04\bIu:\tTime\r\xEFF\x1C\x80\x00\x00\x00\x00\x06:\tzone" t1 = t2 = nil in_timezone('UTC') do @@ -1443,6 +1447,7 @@ def test_memsize end def test_deconstruct_keys + omit "global side effects" if multiple_ractors? t = in_timezone('JST-9') { Time.local(2022, 10, 16, 14, 1, 30, 500) } assert_equal( {year: 2022, month: 10, day: 16, wday: 0, yday: 289, diff --git a/test/ruby/test_time_tz.rb b/test/ruby/test_time_tz.rb index 3bb018d80370c4..dc15b029cc991a 100644 --- a/test/ruby/test_time_tz.rb +++ b/test/ruby/test_time_tz.rb @@ -17,6 +17,7 @@ class TestTimeTZ < Test::Unit::TestCase if force_tz_test module Util def with_tz(tz) + omit "global side effects" if multiple_ractors? && self.is_a?(TestTimeTZ) old = ENV["TZ"] begin ENV["TZ"] = tz @@ -29,6 +30,7 @@ def with_tz(tz) else module Util def with_tz(tz) + omit "global side effects" if multiple_ractors? && self.is_a?(TestTimeTZ) if ENV["TZ"] == tz yield end diff --git a/test/ruby/test_variable.rb b/test/ruby/test_variable.rb index e865149b528962..817a1e96b19036 100644 --- a/test/ruby/test_variable.rb +++ b/test/ruby/test_variable.rb @@ -443,11 +443,11 @@ def test_many_instance_variables objects = [Object.new, Hash.new, Module.new] objects.each do |obj| 1000.times do |i| - next if obj.is_a?(Module) && non_main_ractor? + next if obj.is_a?(Module) && !main_ractor? obj.instance_variable_set("@var#{i}", i) end 1000.times do |i| - next if obj.is_a?(Module) && non_main_ractor? + next if obj.is_a?(Module) && !main_ractor? assert_equal(i, obj.instance_variable_get("@var#{i}")) end end diff --git a/tool/lib/core_assertions.rb b/tool/lib/core_assertions.rb index 14499b05c0421b..08f5721e2c2e17 100644 --- a/tool/lib/core_assertions.rb +++ b/tool/lib/core_assertions.rb @@ -157,7 +157,7 @@ def syntax_check(code, fname, line) end def assert_no_memory_leak(args, prepare, code, message=nil, limit: 2.0, rss: false, **opt) - omit "separate process" if non_main_ractor? + omit "separate process" unless main_ractor? # TODO: consider choosing some appropriate limit for RJIT and stop skipping this once it does not randomly fail pend 'assert_no_memory_leak may consider RJIT memory usage as leak' if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? # For previous versions which implemented MJIT @@ -274,7 +274,7 @@ def assert_valid_syntax(code, *args, **opt) end def assert_normal_exit(testsrc, message = '', child_env: nil, **opt) - pend "#{__method__}" if non_main_ractor? + pend "#{__method__}" unless main_ractor? assert_valid_syntax(testsrc, caller_locations(1, 1)[0]) if child_env child_env = [child_env] @@ -286,7 +286,7 @@ def assert_normal_exit(testsrc, message = '', child_env: nil, **opt) end def assert_ruby_status(args, test_stdin="", message=nil, **opt) - pend "#{__method__}" if non_main_ractor? + pend "#{__method__}" unless main_ractor? out, _, status = EnvUtil.invoke_ruby(args, test_stdin, true, :merge_to_stdout, **opt) desc = FailDesc[status, message, out] assert(!status.signaled?, desc) @@ -310,7 +310,7 @@ def separated_runner(token, out = nil) end def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **opt) - pend "#{__method__}" if non_main_ractor? + pend "#{__method__}" unless main_ractor? unless file and line loc, = caller_locations(1,1) file ||= loc.path @@ -679,13 +679,18 @@ def assert_pattern_list(pattern_list, actual, message=nil) end def assert_warning(pat, msg = nil) - return if multiple_ractors? # These envutil methods with blocks are racy across ractors result = nil - stderr = EnvUtil.with_default_internal(of: pat) { - EnvUtil.verbose_warning { + if multiple_ractors? + stderr = EnvUtil.verbose_warning { result = yield } - } + else + stderr = EnvUtil.with_default_internal(of: pat) { + EnvUtil.verbose_warning { + result = yield + } + } + end msg = message(msg) {diff pat, stderr} assert(pat === stderr, msg) result @@ -858,7 +863,7 @@ def assert_all_assertions_foreach(msg = nil, *keys, &block) # :yield: each elements of +seq+. def assert_linear_performance(seq, rehearsal: nil, pre: ->(n) {n}) pend "No PERFORMANCE_CLOCK found" unless defined?(PERFORMANCE_CLOCK) - pend "Timeout" if non_main_ractor? + pend "Timeout" unless main_ractor? # Timeout testing generally doesn't work when RJIT compilation happens. rjit_enabled = defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? diff --git a/tool/lib/envutil.rb b/tool/lib/envutil.rb index bc1093f7df79ec..222d6d566d1390 100644 --- a/tool/lib/envutil.rb +++ b/tool/lib/envutil.rb @@ -43,6 +43,7 @@ def rubybin DEFAULT_SIGNALS.freeze RUBYLIB = ENV["RUBYLIB"].to_s.freeze + MULTIPLE_RACTORS = ENV["RUBY_TESTS_WITH_RACTORS"].to_i > 1 class << self attr_accessor :timeout_scale @@ -195,6 +196,11 @@ def invoke_ruby(args, stdin_data = "", capture_stdout = false, capture_stderr = signal: :TERM, rubybin: EnvUtil.rubybin, precommand: nil, **opt) + + #if Ractor.current != Ractor.main + #raise Test::Unit::PendedError.new("not ractor safe (#{__method__}): uses spawn") + #end + timeout = apply_timeout_scale(timeout) in_c, in_p = IO.pipe @@ -294,7 +300,9 @@ def flush; end ensure stderr, $stderr = $stderr, stderr $VERBOSE = EnvUtil.original_verbose - EnvUtil.original_warning&.each {|i, v| Warning[i] = v} + unless MULTIPLE_RACTORS + EnvUtil.original_warning&.each {|i, v| Warning[i] = v} + end end module_function :verbose_warning @@ -331,16 +339,22 @@ def suppress_warning # NOTE: not safe to use when testing under multiple ractors. def under_gc_stress(stress = true) + raise Test::Unit::PendedError.new("not ractor safe (#{__method__})") if MULTIPLE_RACTORS + run_ensure = true stress, GC.stress = GC.stress, stress yield ensure - GC.stress = stress + if run_ensure + GC.stress = stress + end end module_function :under_gc_stress # NOTE: not safe to use when testing under multiple ractors. def under_gc_compact_stress(val = :empty, &block) raise "compaction doesn't work well on s390x. Omit the test in the caller." if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077 + raise Test::Unit::PendedError.new("not ractor safe (#{__method__})") if MULTIPLE_RACTORS + run_ensure = true if GC.respond_to?(:auto_compact) auto_compact = GC.auto_compact @@ -349,36 +363,50 @@ def under_gc_compact_stress(val = :empty, &block) under_gc_stress(&block) ensure - GC.auto_compact = auto_compact if GC.respond_to?(:auto_compact) + if run_ensure + GC.auto_compact = auto_compact if GC.respond_to?(:auto_compact) + end end module_function :under_gc_compact_stress # NOTE: not safe to use when testing under multiple ractors. def without_gc + raise Test::Unit::PendedError.new("not ractor safe (#{__method__})") if MULTIPLE_RACTORS + run_ensure = true prev_disabled = GC.disable yield ensure - GC.enable unless prev_disabled + if run_ensure + GC.enable unless prev_disabled + end end module_function :without_gc # NOTE: not safe to use when testing under multiple ractors. def with_default_external(enc = nil, of: nil) + raise Test::Unit::PendedError.new("not ractor safe (#{__method__})") if MULTIPLE_RACTORS + run_ensure = true enc = of.encoding if defined?(of.encoding) suppress_warning { Encoding.default_external = enc } yield ensure - suppress_warning { Encoding.default_external = EnvUtil.original_external_encoding } + if run_ensure + suppress_warning { Encoding.default_external = EnvUtil.original_external_encoding } + end end module_function :with_default_external # NOTE: not safe to use when testing under multiple ractors. def with_default_internal(enc = nil, of: nil) + raise Test::Unit::PendedError.new("not ractor safe (#{__method__})") if MULTIPLE_RACTORS + run_ensure = true enc = of.encoding if defined?(of.encoding) suppress_warning { Encoding.default_internal = enc } yield ensure - suppress_warning { Encoding.default_internal = EnvUtil.original_internal_encoding } + if run_ensure + suppress_warning { Encoding.default_internal = EnvUtil.original_internal_encoding } + end end module_function :with_default_internal diff --git a/tool/lib/test/unit.rb b/tool/lib/test/unit.rb index 9a1f86ae31b5e5..137fcb93e42936 100644 --- a/tool/lib/test/unit.rb +++ b/tool/lib/test/unit.rb @@ -1689,6 +1689,11 @@ def _run_suite suite, type end run_tests_inside_ractors_num = ENV["RUBY_TESTS_WITH_RACTORS"].to_i + if run_tests_inside_ractors_num > 1 + def GC.stress=(val) + raise "Cannot call GC.stress=(val) concurrently (it might not be set back properly after teardown)" + end + end tests_run = 0 assertions = all_test_methods.map { |method| inst = suite.new method.to_s @@ -1718,7 +1723,7 @@ def _run_suite suite, type rs = run_tests_inside_ractors_num.times.map do Ractor.new(inst, self) do |instance, runner| res = instance.run runner - testcase_copyable_ivars = {:@_assertions => true, :@__passed__ => true, :@__name__ => true, :@__after_all_ractors_block => true} + testcase_copyable_ivars = {:@_assertions => true, :@__passed__ => true, :@__name__ => true} runner_copyable_ivars = {:@report => true, :@failures => true, :@errors => true, :@skips => true, :@assertion_count => true, :@test_count => true} instance.instance_variables.each do |ivar| unless testcase_copyable_ivars[ivar] From 6e55ba48ef13f3ba9976209c65b4ae349e9826d7 Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Tue, 12 Aug 2025 13:44:15 -0400 Subject: [PATCH 57/60] fixes to encoding/transcoding for ractors --- encoding.c | 2 +- transcode.c | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/encoding.c b/encoding.c index 0a5d61ee4a1692..22e5880afc2868 100644 --- a/encoding.c +++ b/encoding.c @@ -1601,7 +1601,7 @@ enc_set_default_encoding(struct default_encoding *def, VALUE encoding, const cha overridden = TRUE; if (!NIL_P(encoding)) { - enc_check_encoding(encoding); // loads it if necessary. Needs to be done outside of VM lock. + must_encoding(encoding); // loads it if necessary. Needs to be done outside of VM lock. } GLOBAL_ENC_TABLE_LOCKING(enc_table) { diff --git a/transcode.c b/transcode.c index 507bce78e1357f..1362139abb973c 100644 --- a/transcode.c +++ b/transcode.c @@ -1826,7 +1826,9 @@ rb_econv_asciicompat_encoding(const char *ascii_incompat_name) st_table *table2; struct asciicompat_encoding_t data = {0}; - RB_VM_LOCKING() { + unsigned int lev; + RB_VM_LOCK_ENTER_LEV(&lev); + { if (st_lookup(transcoder_table, (st_data_t)ascii_incompat_name, &v)) { table2 = (st_table *)v; /* @@ -1839,12 +1841,14 @@ rb_econv_asciicompat_encoding(const char *ascii_incompat_name) if (table2->num_entries == 1) { data.ascii_incompat_name = ascii_incompat_name; data.ascii_compat_name = NULL; + RB_VM_LOCK_LEAVE_LEV(&lev); st_foreach(table2, asciicompat_encoding_i, (st_data_t)&data); + RB_VM_LOCK_ENTER_LEV(&lev); } } - } + RB_VM_LOCK_LEAVE_LEV(&lev); return data.ascii_compat_name; // can be NULL } @@ -3029,10 +3033,9 @@ econv_s_asciicompat_encoding(VALUE klass, VALUE arg) VALUE enc = Qnil; enc_arg(&arg, &arg_name, &arg_enc); + result_name = rb_econv_asciicompat_encoding(arg_name); RB_VM_LOCKING() { - result_name = rb_econv_asciicompat_encoding(arg_name); - if (result_name) { result_enc = make_encoding(result_name); enc = rb_enc_from_encoding(result_enc); From dd3acdee0d278a1c68d4aa882da02b82f8c0567d Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Tue, 12 Aug 2025 14:09:43 -0400 Subject: [PATCH 58/60] More ractor encoding autoload fixes --- transcode.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/transcode.c b/transcode.c index 1362139abb973c..189a732b98d794 100644 --- a/transcode.c +++ b/transcode.c @@ -2993,10 +2993,12 @@ static rb_encoding * make_encoding(const char *name) { rb_encoding *enc; - RB_VM_LOCKING() { - enc = rb_enc_find(name); - if (!enc) + enc = rb_enc_find(name); + if (!enc) { + RB_VM_LOCKING() { + // TODO: check again. We may need to export `enc_registered` from encoding.c enc = make_dummy_encoding(name); + } } return enc; } From 80483281e1a99a8e401e657e8f1e992066c93545 Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Tue, 12 Aug 2025 21:23:30 -0400 Subject: [PATCH 59/60] More encoding fixes --- transcode.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/transcode.c b/transcode.c index 189a732b98d794..94fbb3d8d6bbfc 100644 --- a/transcode.c +++ b/transcode.c @@ -3036,12 +3036,9 @@ econv_s_asciicompat_encoding(VALUE klass, VALUE arg) enc_arg(&arg, &arg_name, &arg_enc); result_name = rb_econv_asciicompat_encoding(arg_name); - - RB_VM_LOCKING() { - if (result_name) { - result_enc = make_encoding(result_name); - enc = rb_enc_from_encoding(result_enc); - } + result_enc = make_encoding(result_name); + if (result_name) { + enc = rb_enc_from_encoding(result_enc); } return enc; } From 02059b313bcafc3adc27cbe0536b0519c9f0a2c3 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Mon, 18 Aug 2025 16:40:10 -0400 Subject: [PATCH 60/60] Revert encoding check in enc_set_default_encoding It prevents a string from being used for `encoding`. --- encoding.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encoding.c b/encoding.c index 22e5880afc2868..0a5d61ee4a1692 100644 --- a/encoding.c +++ b/encoding.c @@ -1601,7 +1601,7 @@ enc_set_default_encoding(struct default_encoding *def, VALUE encoding, const cha overridden = TRUE; if (!NIL_P(encoding)) { - must_encoding(encoding); // loads it if necessary. Needs to be done outside of VM lock. + enc_check_encoding(encoding); // loads it if necessary. Needs to be done outside of VM lock. } GLOBAL_ENC_TABLE_LOCKING(enc_table) {