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/ext/-test-/stack/stack.c b/ext/-test-/stack/stack.c index 8ff32f9737b218..30348cc22d47e0 100644 --- a/ext/-test-/stack/stack.c +++ b/ext/-test-/stack/stack.c @@ -30,6 +30,9 @@ asan_p(VALUE klass) void Init_stack(VALUE klass) { +#ifdef HAVE_RB_EXT_RACTOR_SAFE + RB_EXT_RACTOR_SAFE(true); +#endif rb_define_singleton_method(rb_cThread, "alloca_overflow", stack_alloca_overflow, 0); rb_define_singleton_method(rb_cThread, "asan?", asan_p, 0); } diff --git a/test/.excludes-ractor/Prism/TestCompilePrism.rb b/test/.excludes-ractor/Prism/TestCompilePrism.rb new file mode 100644 index 00000000000000..db5ba693cf8984 --- /dev/null +++ b/test/.excludes-ractor/Prism/TestCompilePrism.rb @@ -0,0 +1,27 @@ +# Prism::TestCompilePrism#test_repeated_proc_params../ruby/st.c:2621: Assertion Failed: set_rebuild_table_with:new_tab->num_entries == tab->num_entries +exclude(/^test_/, "FIXME: bug. Skip all tests for now") + +exclude(/^test_GlobalVariable/, "global variables") +exclude(:test_ClassVariableTargetNode, "class variables") +exclude(:test_ClassVariableAndWriteNode, "class variables") +exclude(:test_ClassVariableOperatorWriteNode, "class variables") +exclude(:test_ClassVariableOrWriteNode, "class variables") +exclude(:test_ClassVariableWriteNode, "class variables") +exclude(:test_ConstantWriteNode, "global side effects") +exclude(:test_InstanceVariableTargetNode, "class ivars") +exclude(:test_InstanceVariableReadNode, "class ivars") +exclude(:test_InstanceVariableWriteNode, "class ivars") +exclude(:test_EmbeddedVariableNode, "class ivars") +exclude(:test_InterpolatedMatchLastLineNode, "global variables") +exclude(:test_InterpolatedRegularExpressionNode, "global variables") +exclude(:test_InterpolatedStringNode, "global variables") +exclude(:test_InterpolatedSymbolNode, "global variables") +exclude(:test_AliasGlobalVariableNode, "global variables") +exclude(:test_ClassVariableReadNode, "class variables") +exclude(:test_ForNode_gvar, "gvars") +exclude(:test_PostExecutionNode_set_ivar, "set ivar of main") +exclude(:test_PinnedVariableNode_ractor_unsafe, "ractor incompatible") +exclude(:test_DefinedNode_ractor_unsafe, "ractor incompatible") +exclude(:test_ConstantPathOrWriteNode_ractor_unsafe, "ractor incompatible") +exclude(:test_ConstantPathOperatorWriteNode_ractor_unsafe, "ractor incompatible") +exclude(:test_IfNode_ractor_unsafe, "gvars") diff --git a/test/.excludes-ractor/RubyVM.rb b/test/.excludes-ractor/RubyVM.rb new file mode 100644 index 00000000000000..386a982dc79af6 --- /dev/null +++ b/test/.excludes-ractor/RubyVM.rb @@ -0,0 +1,2 @@ +exclude(:test_of_proc_and_method, "ractor incompatible") +exclude(:test_parse_file_raises_syntax_error, "ractor incompatible") diff --git a/test/.excludes-ractor/TestAlias.rb b/test/.excludes-ractor/TestAlias.rb new file mode 100644 index 00000000000000..95fe32e4d658bb --- /dev/null +++ b/test/.excludes-ractor/TestAlias.rb @@ -0,0 +1,2 @@ +exclude(:test_alias_class_method_added, "class ivars") +exclude(:test_alias_module_method_added, "module ivars") diff --git a/test/.excludes-ractor/TestAllocation.rb b/test/.excludes-ractor/TestAllocation.rb new file mode 100644 index 00000000000000..c909538bff7ad8 --- /dev/null +++ b/test/.excludes-ractor/TestAllocation.rb @@ -0,0 +1 @@ +exclude(/^test_/, "checks allocations in subprocess") diff --git a/test/.excludes-ractor/TestArgf.rb b/test/.excludes-ractor/TestArgf.rb new file mode 100644 index 00000000000000..fea05b4fd73c53 --- /dev/null +++ b/test/.excludes-ractor/TestArgf.rb @@ -0,0 +1 @@ +exclude(/^test_/, "ARGF is not shareable") diff --git a/test/.excludes-ractor/TestArray.rb b/test/.excludes-ractor/TestArray.rb new file mode 100644 index 00000000000000..d33c7113e56976 --- /dev/null +++ b/test/.excludes-ractor/TestArray.rb @@ -0,0 +1,9 @@ +exclude(/_with_callcc$/, "ractor incompatible") +exclude(:test_join, "Uses $,") +exclude(:test_to_s, "Uses $,") +exclude(:test_equal_resize, "global variables") +exclude(:test_replace_wb_variable_width_alloc, "GC.verify_internal_consistency") +exclude(:test_concat_under_gc_stress, "EnvUtil.under_gc_stress") +exclude(:test_product_under_gc_stress, "EnvUtil.under_gc_stress") +exclude(:test_to_h_single_element_with_object, "global side effects") +exclude(:test_to_h_block_single_element_with_object, "global side effects") diff --git a/test/.excludes-ractor/TestArraySubclass.rb b/test/.excludes-ractor/TestArraySubclass.rb new file mode 100644 index 00000000000000..54dd8ae3c1d282 --- /dev/null +++ b/test/.excludes-ractor/TestArraySubclass.rb @@ -0,0 +1,2 @@ +path = File.expand_path("../TestArray.rb", __FILE__) +instance_eval File.read(path), path diff --git a/test/.excludes-ractor/TestAssignment.rb b/test/.excludes-ractor/TestAssignment.rb new file mode 100644 index 00000000000000..14d2c64ee264b6 --- /dev/null +++ b/test/.excludes-ractor/TestAssignment.rb @@ -0,0 +1,5 @@ +exclude(:test_assignment, "ractor incompatible") +exclude(:test_massign_const_order, "ractor incompatible") +exclude(:test_next, "ractor incompatible") +exclude(:test_return, "ractor incompatible") +exclude(:test_yield, "ractor incompatible") diff --git a/test/.excludes-ractor/TestAssignmentGen.rb b/test/.excludes-ractor/TestAssignmentGen.rb new file mode 100644 index 00000000000000..15e177545c7117 --- /dev/null +++ b/test/.excludes-ractor/TestAssignmentGen.rb @@ -0,0 +1 @@ +exclude(:test_assignment, "ractor incompatible") diff --git a/test/.excludes-ractor/TestAst.rb b/test/.excludes-ractor/TestAst.rb new file mode 100644 index 00000000000000..3c589ee653c966 --- /dev/null +++ b/test/.excludes-ractor/TestAst.rb @@ -0,0 +1,2 @@ +exclude(:test_parse_file_raises_syntax_error, "Tempfile") +exclude(:test_of_proc_and_method, "Tempfile") diff --git a/test/.excludes-ractor/TestAutoload.rb b/test/.excludes-ractor/TestAutoload.rb new file mode 100644 index 00000000000000..08db55ced48929 --- /dev/null +++ b/test/.excludes-ractor/TestAutoload.rb @@ -0,0 +1 @@ +exclude(/^test_/, "ractor incompatible") diff --git a/test/.excludes-ractor/TestBacktrace.rb b/test/.excludes-ractor/TestBacktrace.rb new file mode 100644 index 00000000000000..421722c740035e --- /dev/null +++ b/test/.excludes-ractor/TestBacktrace.rb @@ -0,0 +1,3 @@ +exclude(:test_caller_locations_absolute_path, "ractor incompatible") +exclude(:test_caller_locations_lineno, "ractor incompatible") +exclude(:test_caller_locations_path, "ractor incompatible") diff --git a/test/.excludes-ractor/TestBasicInstructions.rb b/test/.excludes-ractor/TestBasicInstructions.rb new file mode 100644 index 00000000000000..cfa070aa5ab992 --- /dev/null +++ b/test/.excludes-ractor/TestBasicInstructions.rb @@ -0,0 +1 @@ +exclude(/^test_.*/, "Lots of unfrozen strings") diff --git a/test/.excludes-ractor/TestBeginEndBlock.rb b/test/.excludes-ractor/TestBeginEndBlock.rb new file mode 100644 index 00000000000000..852c72ca7ae5fd --- /dev/null +++ b/test/.excludes-ractor/TestBeginEndBlock.rb @@ -0,0 +1,2 @@ +exclude(:test_internal_errinfo_at_exit, "uses fork") +exclude(:test_rescue_at_exit, "uses subprocess") diff --git a/test/.excludes-ractor/TestBignum.rb b/test/.excludes-ractor/TestBignum.rb new file mode 100644 index 00000000000000..d76133ae0c4a77 --- /dev/null +++ b/test/.excludes-ractor/TestBignum.rb @@ -0,0 +1,2 @@ +exclude(:test_bignum, "global variables") +exclude(:test_interrupt_during_bigdivrem, "Signal.trap proc accesses outers") diff --git a/test/.excludes-ractor/TestCaseFold.rb b/test/.excludes-ractor/TestCaseFold.rb new file mode 100644 index 00000000000000..44d435c2afa12d --- /dev/null +++ b/test/.excludes-ractor/TestCaseFold.rb @@ -0,0 +1 @@ +exclude(/^test_/, "TODO: tests use define_method") diff --git a/test/.excludes-ractor/TestClass.rb b/test/.excludes-ractor/TestClass.rb new file mode 100644 index 00000000000000..429c0b86a5fa6c --- /dev/null +++ b/test/.excludes-ractor/TestClass.rb @@ -0,0 +1,5 @@ +exclude(:test_s_inherited, "class variables") +exclude(:test_singleton_class_should_has_own_namespace, "global variables") +exclude(:test_nonascii_name, "global side effects") +exclude(:test_check_inheritable_break_with_object, "global side effects") +exclude(/^test_subclass_gc/, "Takes long time") diff --git a/test/.excludes-ractor/TestConst.rb b/test/.excludes-ractor/TestConst.rb new file mode 100644 index 00000000000000..2de3826e167ffc --- /dev/null +++ b/test/.excludes-ractor/TestConst.rb @@ -0,0 +1 @@ +exclude(:test_const, "ractor incompatible") diff --git a/test/.excludes-ractor/TestDefaultGems.rb b/test/.excludes-ractor/TestDefaultGems.rb new file mode 100644 index 00000000000000..0f1407c7d4e4fb --- /dev/null +++ b/test/.excludes-ractor/TestDefaultGems.rb @@ -0,0 +1 @@ +exclude(:test_validate_gemspec, "loading gemspecs accesses load path") diff --git a/test/.excludes-ractor/TestDefined.rb b/test/.excludes-ractor/TestDefined.rb new file mode 100644 index 00000000000000..78596077ca2030 --- /dev/null +++ b/test/.excludes-ractor/TestDefined.rb @@ -0,0 +1,4 @@ +exclude(:test_autoloaded_noload, "accesses load path") +exclude(:test_defined_global_variable, "ractor incompatible") +exclude(:test_super_in_basic_object, "ractor incompatible") +exclude(:test_respond_to, "Uses Warning[]=") diff --git a/test/.excludes-ractor/TestDir.rb b/test/.excludes-ractor/TestDir.rb new file mode 100644 index 00000000000000..6ac61d494140b9 --- /dev/null +++ b/test/.excludes-ractor/TestDir.rb @@ -0,0 +1 @@ +exclude(/^test_/, "Lots of Dir.chdir") diff --git a/test/.excludes-ractor/TestDir_M17N.rb b/test/.excludes-ractor/TestDir_M17N.rb new file mode 100644 index 00000000000000..a37caed1195c44 --- /dev/null +++ b/test/.excludes-ractor/TestDir_M17N.rb @@ -0,0 +1 @@ +exclude(/^test_.*/, "Lots of Dir.chdir") diff --git a/test/.excludes-ractor/TestEnumerable.rb b/test/.excludes-ractor/TestEnumerable.rb new file mode 100644 index 00000000000000..b83f9c7a5f57f9 --- /dev/null +++ b/test/.excludes-ractor/TestEnumerable.rb @@ -0,0 +1 @@ +exclude(:test_zip_ractor_unsafe, "global side effects") diff --git a/test/.excludes-ractor/TestEnumerator.rb b/test/.excludes-ractor/TestEnumerator.rb new file mode 100644 index 00000000000000..d0d3e78c824db6 --- /dev/null +++ b/test/.excludes-ractor/TestEnumerator.rb @@ -0,0 +1 @@ +exclude(/under_gc_compact_stress/, "EnvUtil.under_gc_compact_stress") diff --git a/test/.excludes-ractor/TestEnv.rb b/test/.excludes-ractor/TestEnv.rb new file mode 100644 index 00000000000000..08db55ced48929 --- /dev/null +++ b/test/.excludes-ractor/TestEnv.rb @@ -0,0 +1 @@ +exclude(/^test_/, "ractor incompatible") diff --git a/test/.excludes-ractor/TestEval.rb b/test/.excludes-ractor/TestEval.rb new file mode 100644 index 00000000000000..413e42f424a981 --- /dev/null +++ b/test/.excludes-ractor/TestEval.rb @@ -0,0 +1,16 @@ +exclude(:test_cvar_scope_with_instance_eval, "ractor incompatible") +exclude(:test_define_method_toplevel, "ractor incompatible") +exclude(:test_eval_with_toplevel_binding, "ractor incompatible") +exclude(:test_fixnum_instance_eval_cvar, "ractor incompatible") +exclude(:test_instance_eval_cvar, "ractor incompatible") +exclude(:test_instance_exec_cvar, "ractor incompatible") +exclude(:test_instance_eval_on_argf_singleton_class, "ractor incompatible") +exclude(:test_nil_instance_eval_cvar, "ractor incompatible") +exclude(:test_eval_basic_ractor_unsafe, "cvars and gvars") +exclude(:test_eval_binding_basic_ractor_unsafe, "cvars and gvars") +exclude(:test_module_eval_string_basic_ractor_unsafe, "cvars and gvars") +exclude(:test_module_eval_block_basic_ractor_unsafe, "cvars and gvars") +exclude(:test_instance_eval_string_basic_ractor_unsafe, "cvars and gvars") +exclude(:test_instance_eval_block_basic_ractor_unsafe, "cvars and gvars") +exclude(:test_instance_exec_block_basic_ractor_unsafe, "cvars and gvars") +exclude(:test_eval_orig_ractor_unsafe, "global side effects") diff --git a/test/.excludes-ractor/TestException.rb b/test/.excludes-ractor/TestException.rb new file mode 100644 index 00000000000000..ada2151ce60474 --- /dev/null +++ b/test/.excludes-ractor/TestException.rb @@ -0,0 +1,11 @@ +exclude(:test_kernel_warn_uplevel, "uses capture_warning_warn") +exclude(:test_thread_signal_location, "subprocess") +exclude(:test_warn_deprecated_backwards_compatibility_category, "ractor incompatible") +exclude(:test_warning_warn, "ractor incompatible") +exclude(:test_warning_warn_circular_require_backtrace, "ractor incompatible") +exclude(:test_warning_category, "global side effects") +exclude(:test_type_error_message_encoding, "global side effects") +exclude(:test_too_many_args_in_eval, "TODO: freezes process") +exclude(:test_catch_throw_in_require, "Tempfile") +exclude(:test_catch_throw_in_require_cant_be_rescued, "Tempfile") +exclude(:test_detailed_message_under_gc_compact_stress, "EnvUtil.under_gc_compact_stress") diff --git a/test/.excludes-ractor/TestFiber.rb b/test/.excludes-ractor/TestFiber.rb new file mode 100644 index 00000000000000..714aade300f67a --- /dev/null +++ b/test/.excludes-ractor/TestFiber.rb @@ -0,0 +1 @@ +exclude(:test_fork_from_fiber, "fork") diff --git a/test/.excludes-ractor/TestFile.rb b/test/.excludes-ractor/TestFile.rb new file mode 100644 index 00000000000000..9c2c67f8063eb2 --- /dev/null +++ b/test/.excludes-ractor/TestFile.rb @@ -0,0 +1,18 @@ +exclude(:test_chown, "Tempfile") +exclude(:test_each_byte_extended_file, "Tempfile") +exclude(:test_empty_file_bom, "Tempfile") +exclude(:test_getbyte_extended_file, "Tempfile") +exclude(:test_gets_extended_file, "Tempfile") +exclude(:test_gets_para_extended_file, "Tempfile") +exclude(:test_read_all_extended_file, "Tempfile") +exclude(:test_realdirpath_junction, "Dir.chdir") +exclude(:test_realpath_ractor_unsafe, "Dir.chdir") +exclude(:test_realdirpath_ractor_unsafe, "Dir.chdir") +exclude(:test_stat, "Tempfile") +exclude(:test_truncate_beyond_eof, "Tempfile") +exclude(:test_truncate_rbuf, "Tempfile") +exclude(:test_truncate_size, "Tempfile") +exclude(:test_truncate_wbuf, "Tempfile") +exclude(:test_utime, "Tempfile") +exclude(/^test_bom/, "Tempfile") +# done diff --git a/test/.excludes-ractor/TestFileExhaustive.rb b/test/.excludes-ractor/TestFileExhaustive.rb new file mode 100644 index 00000000000000..78a27ad584a60b --- /dev/null +++ b/test/.excludes-ractor/TestFileExhaustive.rb @@ -0,0 +1,14 @@ +exclude(:test_expand_path_compose, "Dir.chdir") +exclude(:test_expand_path_home, "global side effects") +exclude(:test_expand_path_home_dir_string, "global side effects") +exclude(:test_expand_path_memsize, "ObjectSpace.memsize_of") +exclude(:test_expand_path_encoding_filesystem, "global side effects") +exclude(:test_expand_path_error_for_non_absolute_home, "global side effects") +exclude(:test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_home_as_base, "global side effects") +exclude(:test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_unc_home, "global side effects") +exclude(:test_expand_path_does_not_modify_a_home_string_argument, "global side effects") +exclude(:test_flock_exclusive, "subprocess") +exclude(:test_flock_shared, "subprocess") +exclude(:test_stat_dotted_prefix_ractor_unsafe, "Dir.chdir") +exclude(:test_utime_ractor_unsafe, "Dir.chdir") +exclude(:test_umask, "global side effects") diff --git a/test/.excludes-ractor/TestFlip.rb b/test/.excludes-ractor/TestFlip.rb new file mode 100644 index 00000000000000..900d90453e2122 --- /dev/null +++ b/test/.excludes-ractor/TestFlip.rb @@ -0,0 +1 @@ +exclude(:test_input_line_number_range, "Accesses $.") diff --git a/test/.excludes-ractor/TestFloat.rb b/test/.excludes-ractor/TestFloat.rb new file mode 100644 index 00000000000000..cf86ac5c615558 --- /dev/null +++ b/test/.excludes-ractor/TestFloat.rb @@ -0,0 +1 @@ +exclude(:test_invalid_str, "EnvUtil.under_gc_stress") diff --git a/test/.excludes-ractor/TestGCCompact.rb b/test/.excludes-ractor/TestGCCompact.rb new file mode 100644 index 00000000000000..d52a1e84383af0 --- /dev/null +++ b/test/.excludes-ractor/TestGCCompact.rb @@ -0,0 +1 @@ +exclude(/^test_/, "GC.auto_compact = true then setting it back is racy") diff --git a/test/.excludes-ractor/TestGc.rb b/test/.excludes-ractor/TestGc.rb new file mode 100644 index 00000000000000..82f4e17304be07 --- /dev/null +++ b/test/.excludes-ractor/TestGc.rb @@ -0,0 +1 @@ +exclude(/^test_/, "ractor incompatible") diff --git a/test/.excludes-ractor/TestHash.rb b/test/.excludes-ractor/TestHash.rb new file mode 100644 index 00000000000000..1fbc8a63de61cb --- /dev/null +++ b/test/.excludes-ractor/TestHash.rb @@ -0,0 +1,7 @@ +exclude(:test_AREF_fstring_key, "EnvUtil.without_gc") +exclude(:test_iterlevel_in_ivar_bug19589, "stack level too deep with ractors") +exclude(:test_memory_size_after_delete, "ObjectSpace.memsize_of not yet ractor safe") +exclude(:test_to_s, "global variables") +exclude(:test_inspect, "global side effects") +exclude(:test_broken_hash_value, "too many objects") +exclude(:test_replace_bug15358, "GC.start") diff --git a/test/.excludes-ractor/TestHash/TestSubHash.rb b/test/.excludes-ractor/TestHash/TestSubHash.rb new file mode 100644 index 00000000000000..61f25181f1f9e3 --- /dev/null +++ b/test/.excludes-ractor/TestHash/TestSubHash.rb @@ -0,0 +1,2 @@ +path = File.expand_path("../../TestHash.rb", __FILE__) +instance_eval File.read(path), path diff --git a/test/.excludes-ractor/TestHashOnly.rb b/test/.excludes-ractor/TestHashOnly.rb new file mode 100644 index 00000000000000..814ac65d772773 --- /dev/null +++ b/test/.excludes-ractor/TestHashOnly.rb @@ -0,0 +1,2 @@ +path = File.expand_path("../TestHash.rb", __FILE__) +instance_eval File.read(path), path diff --git a/test/.excludes-ractor/TestIO.rb b/test/.excludes-ractor/TestIO.rb new file mode 100644 index 00000000000000..c489665ed681c5 --- /dev/null +++ b/test/.excludes-ractor/TestIO.rb @@ -0,0 +1,27 @@ +exclude(:test_binmode_pipe, "global side effects") +exclude(:test_advise_pipe, "ractor incompatible") +exclude(:test_autoclose_false_closed_by_finalizer, "Tempfile") +exclude(:test_autoclose_true_closed_by_finalizer, "Tempfile") +exclude(:test_autoclose, "TODO: assert_raise buggy") +exclude(:test_copy_stream_dup_buffer, "Tempfile") +exclude(:test_copy_stream_no_busy_wait, "unpredictable") +exclude(:test_copy_stream_socket7, "fork") +exclude(:test_copy_stream_strio_to_tempfile, "Tempfile") +exclude(:test_fcntl_dupfd, "Tempfile") +exclude(:test_fcntl_lock_freebsd, "Tempfile + fork") +exclude(:test_fcntl_lock_linux, "Tempfile + fork") +exclude(:test_flush_in_finalizer1, "Tempfile + ObjectSpace.each_object") +exclude(:test_flush_in_finalizer2, "Tempfile + ObjectSpace.each_object") +exclude(:test_pid_after_close_read, "TODO: buggy") +exclude(:test_print_separators, "global variable access") +exclude(:test_race_between_read, "Tempfile") +exclude(:test_readline_limit_without_separator, "Tempfile") +exclude(:test_readline_separators_limits, "Tempfile") +exclude(:test_set_lineno_gets, "global variable access") +exclude(:test_set_lineno_readline, "global variable access") +exclude(:test_threaded_flush, "subprocesses") +exclude(:test_try_convert, "uses STDOUT") +exclude(:test_close_read_write_separately, "TODO: buggy with ractors") +exclude(:test_exception_at_close, "TODO: assert_raise sometimes fails under multiple ractors") +exclude(:test_std_fileno_ractor_unsafe, "unshareable objects") +exclude(/buffer_not_raise_shared_string_error/, "Tempfile") diff --git a/test/.excludes-ractor/TestIOBuffer.rb b/test/.excludes-ractor/TestIOBuffer.rb new file mode 100644 index 00000000000000..885edb1fa50269 --- /dev/null +++ b/test/.excludes-ractor/TestIOBuffer.rb @@ -0,0 +1,13 @@ +exclude(:test_bug_21210, "GC.verify_compaction_references") +exclude(:test_read, "Tempfile") +exclude(:test_read_with_with_length, "Tempfile") +exclude(:test_read_with_with_offset, "Tempfile") +exclude(:test_read_with_length_and_offset, "Tempfile") +exclude(:test_pread, "Tempfile") +exclude(:test_pread_offset, "Tempfile") +exclude(:test_private, "Tempfile") +exclude(:test_pwrite, "Tempfile") +exclude(:test_pwrite_offset, "Tempfile") +exclude(:test_shared, "fork") +exclude(:test_write, "Tempfile") +exclude(:test_write_with_length_and_offset, "Tempfile") diff --git a/test/.excludes-ractor/TestIO_M17N.rb b/test/.excludes-ractor/TestIO_M17N.rb new file mode 100644 index 00000000000000..1c2af0646a8596 --- /dev/null +++ b/test/.excludes-ractor/TestIO_M17N.rb @@ -0,0 +1,2 @@ +exclude(:test_each_codepoint_need_more, "Tempfile") +exclude(:test_pipe_terminator_conversion, "Timeout.timeout") diff --git a/test/.excludes-ractor/TestISeq.rb b/test/.excludes-ractor/TestISeq.rb new file mode 100644 index 00000000000000..41407edfcc09ae --- /dev/null +++ b/test/.excludes-ractor/TestISeq.rb @@ -0,0 +1 @@ +exclude(/^test_/, "not ractor safe") diff --git a/test/.excludes-ractor/TestInteger.rb b/test/.excludes-ractor/TestInteger.rb new file mode 100644 index 00000000000000..5bf548f3773177 --- /dev/null +++ b/test/.excludes-ractor/TestInteger.rb @@ -0,0 +1 @@ +exclude(:test_aref, "Takes too long") diff --git a/test/.excludes-ractor/TestKeyword.rb b/test/.excludes-ractor/TestKeyword.rb new file mode 100644 index 00000000000000..3a7212149dd185 --- /dev/null +++ b/test/.excludes-ractor/TestKeyword.rb @@ -0,0 +1 @@ +exclude(:test_Thread_new_kwsplat, "global side effects") diff --git a/test/.excludes-ractor/TestM17N.rb b/test/.excludes-ractor/TestM17N.rb new file mode 100644 index 00000000000000..2b3109969e3b92 --- /dev/null +++ b/test/.excludes-ractor/TestM17N.rb @@ -0,0 +1,2 @@ +exclude(:test_string_inspect_encoding, "global side effects") +exclude(:test_object_utf16_32_inspect, "global side effects") diff --git a/test/.excludes-ractor/TestMarshal.rb b/test/.excludes-ractor/TestMarshal.rb new file mode 100644 index 00000000000000..bff69b03e9d0c8 --- /dev/null +++ b/test/.excludes-ractor/TestMarshal.rb @@ -0,0 +1,6 @@ +exclude(:test_context_switch, "class variables") +exclude(:test_struct_invalid_members, "global side effects") +exclude(:test_change_class_name, "global side effects") +exclude(:test_change_struct, "global side effects") +exclude(:test_continuation, "callcc") +exclude(:test_singleton, "class ivar") diff --git a/test/.excludes-ractor/TestMarshal/TestMarshalFreeze.rb b/test/.excludes-ractor/TestMarshal/TestMarshalFreeze.rb new file mode 100644 index 00000000000000..e69a996966caf6 --- /dev/null +++ b/test/.excludes-ractor/TestMarshal/TestMarshalFreeze.rb @@ -0,0 +1,2 @@ +path = File.expand_path("../../TestMarshal.rb", __FILE__) +instance_eval File.read(path), path diff --git a/test/.excludes-ractor/TestMarshal/TestMarshalFreezeProc.rb b/test/.excludes-ractor/TestMarshal/TestMarshalFreezeProc.rb new file mode 100644 index 00000000000000..e69a996966caf6 --- /dev/null +++ b/test/.excludes-ractor/TestMarshal/TestMarshalFreezeProc.rb @@ -0,0 +1,2 @@ +path = File.expand_path("../../TestMarshal.rb", __FILE__) +instance_eval File.read(path), path diff --git a/test/.excludes-ractor/TestMath.rb b/test/.excludes-ractor/TestMath.rb new file mode 100644 index 00000000000000..111e0a9521fc7f --- /dev/null +++ b/test/.excludes-ractor/TestMath.rb @@ -0,0 +1,3 @@ +exclude(:test_override_bignum_to_f, "global side effects") +exclude(:test_override_integer_to_f, "global side effects") +exclude(:test_override_rational_to_f, "global side effects") diff --git a/test/.excludes-ractor/TestMetaclass.rb b/test/.excludes-ractor/TestMetaclass.rb new file mode 100644 index 00000000000000..76901928c7ab50 --- /dev/null +++ b/test/.excludes-ractor/TestMetaclass.rb @@ -0,0 +1 @@ +exclude(/^test_/, "global side effects") diff --git a/test/.excludes-ractor/TestMethod.rb b/test/.excludes-ractor/TestMethod.rb new file mode 100644 index 00000000000000..2d1a1e18a071cd --- /dev/null +++ b/test/.excludes-ractor/TestMethod.rb @@ -0,0 +1,5 @@ +exclude(:test_unbind, "global side effects") +exclude(:test_define_method_in_private_scope, "TOPLEVEL_BINDING") +exclude(:test_method_list, "ObjectSpace.each_object") +exclude(:test_singleton_define_method_in_private_scope, "TOPLEVEL_BINDING") +exclude(:test_method_in_method_visibility_should_be_public, "global side effects") diff --git a/test/.excludes-ractor/TestModule.rb b/test/.excludes-ractor/TestModule.rb new file mode 100644 index 00000000000000..c6365784935f0e --- /dev/null +++ b/test/.excludes-ractor/TestModule.rb @@ -0,0 +1,29 @@ +exclude(:test_attr_obsoleted_flag, "class ivars") +exclude(:test_attr_public_at_toplevel, "TOPLEVEL_BINDING") +exclude(:test_class_eval, "class variable access") +exclude(:test_class_variable_defined, "class vars") +exclude(:test_class_variable_get, "class vars") +exclude(:test_class_variable_in_dup_class, "class vars") +exclude(:test_class_variable_set, "class vars") +exclude(:test_class_variables, "class vars") +exclude(:test_remove_class_variable, "class vars") +exclude(:test_const_added, "class ivars") +exclude(:test_const_defined_invalid_symbol_name, "EnvUtil.under_gc_stress") +exclude(:test_const_set, "class ivars") +exclude(:test_extend_module_with_protected_method, "class ivars") +exclude(:test_initialize_copy_empty, "class ivars") +exclude(:test_module_subclass_initialize, "class ivars") +exclude(:test_private_constant_const_missing, "class ivars") +exclude(:test_private_constant_reopen, "TOPLEVEL_BINDING") +exclude(:test_s_constants, "global side effects") +exclude(:test_s_nesting, "global variable access") +exclude(:test_uninitialized_attr_class, "class ivars") +exclude(:test_uninitialized_toplevel_constant, "TOPLEVEL_BINDING") +exclude(:test_nested_get, "global side effects") +exclude(:test_nested_get_symbol, "global side effects") +exclude(:test_nested_defined, "global side effects") +exclude(:test_nested_defined_symbol, "global side effects") +exclude(:test_deprecate_constant, "Warning[]=") +exclude(:test_dup, "global side effects") +exclude(:test_module_exec, "global side effects") +exclude(:test_module_eval, "global side effects") diff --git a/test/.excludes-ractor/TestNamespace.rb b/test/.excludes-ractor/TestNamespace.rb new file mode 100644 index 00000000000000..1656f5a70a0767 --- /dev/null +++ b/test/.excludes-ractor/TestNamespace.rb @@ -0,0 +1 @@ +exclude(:test_global_variables, "global variable access") diff --git a/test/.excludes-ractor/TestObjectSpace.rb b/test/.excludes-ractor/TestObjectSpace.rb new file mode 100644 index 00000000000000..e98428547f1928 --- /dev/null +++ b/test/.excludes-ractor/TestObjectSpace.rb @@ -0,0 +1 @@ +exclude(/^test_/, "TODO: should be safe when ractor-local GC lands") diff --git a/test/.excludes-ractor/TestParse.rb b/test/.excludes-ractor/TestParse.rb new file mode 100644 index 00000000000000..f9b0b7b979cd89 --- /dev/null +++ b/test/.excludes-ractor/TestParse.rb @@ -0,0 +1,6 @@ +exclude(:test_dstr, "class vars") +exclude(:test_global_variable, "global variable access") +exclude(:test_mlhs_node, "class ivars") +exclude(:test_shareable_constant_value_nested, "can't access unshareables") +exclude(:test_shareable_constant_value_simple, "can't access unshareables") +exclude(:test_parsing_begin_statement_inside_method_definition, "global side effects") diff --git a/test/.excludes-ractor/TestPatternMatching.rb b/test/.excludes-ractor/TestPatternMatching.rb new file mode 100644 index 00000000000000..5bff39721ce503 --- /dev/null +++ b/test/.excludes-ractor/TestPatternMatching.rb @@ -0,0 +1,4 @@ +exclude(:test_deconstruct_keys, "class ivars") +exclude(:test_hash_pattern, "class ivars") +exclude(:test_hash_pattern, "class ivars") +exclude(:test_pin_operator_value_pattern_ractor_unsafe, "cvars and gvars") diff --git a/test/.excludes-ractor/TestProcess.rb b/test/.excludes-ractor/TestProcess.rb new file mode 100644 index 00000000000000..3a0fa4db13858f --- /dev/null +++ b/test/.excludes-ractor/TestProcess.rb @@ -0,0 +1,26 @@ +exclude(:test__fork, "fork") +exclude(:test__fork_pid_cache, "fork") +exclude(:test_argv0_frozen, "gvars") +exclude(:test_concurrent_group_and_pid_wait, "Timeout.timeout") +exclude(:test_daemon_default, "fork") +exclude(:test_daemon_detached, "fork") +exclude(:test_daemon_no_threads, "fork") +exclude(:test_daemon_nochdir_noclose, "fork") +exclude(:test_daemon_noclose, "fork") +exclude(:test_daemon_pid, "fork") +exclude(:test_daemon_readwrite, "fork") +exclude(/test_execopts_env_popen/, "ENV changes") +exclude(:test_execopts_redirect_tempfile, "Tempfile") +exclude(:test_popen_fork, "fork") +exclude(:test_popen_fork_ensure, "fork") +exclude(:test_execopts_env_popen, "ENV changes") +exclude(:test_execopts_unsetenv_others, "ENV changes") +exclude(:test_process_detach, "fork") +exclude(:test_seteuid_name, "unsafe Etc method") +exclude(:test_signals_work_after_exec_fail, "Timeout.timeout") +exclude(:test_system_sigpipe, "Timeout.timeout") +exclude(:test_threading_works_after_exec_fail, "Timeout.timeout") +exclude(:test_to_hash_on_arguments, "separate processes") +exclude(:test_waitall, "global side effects") +exclude(:test_uid_from_name_ractor_unsafe, "Etc.getpwuid") +exclude(:test_gid_from_name_ractor_unsafe, "Etc.getgrgid") diff --git a/test/.excludes-ractor/TestRand.rb b/test/.excludes-ractor/TestRand.rb new file mode 100644 index 00000000000000..1f1255119f8e67 --- /dev/null +++ b/test/.excludes-ractor/TestRand.rb @@ -0,0 +1,2 @@ +exclude(:test_fork_shuffle, "fork") +exclude(:test_rand_reseed_on_fork, "fork") diff --git a/test/.excludes-ractor/TestRange.rb b/test/.excludes-ractor/TestRange.rb new file mode 100644 index 00000000000000..e79eac1fd2fc43 --- /dev/null +++ b/test/.excludes-ractor/TestRange.rb @@ -0,0 +1,3 @@ +exclude(:test_comparison_when_recursive, "Timeout.timeout") +exclude(:test_range_bsearch_for_floats, "Can take a long time") +exclude(:test_bsearch_typechecks_return_values, "global side effects") diff --git a/test/.excludes-ractor/TestReadPartial.rb b/test/.excludes-ractor/TestReadPartial.rb new file mode 100644 index 00000000000000..7eda414cc2ea18 --- /dev/null +++ b/test/.excludes-ractor/TestReadPartial.rb @@ -0,0 +1,2 @@ +exclude(:test_open_pipe, "Timeout.timeout") +exclude(:test_with_stdio, "Timeout.timeout") diff --git a/test/.excludes-ractor/TestRefinement.rb b/test/.excludes-ractor/TestRefinement.rb new file mode 100644 index 00000000000000..6f626a6d82e254 --- /dev/null +++ b/test/.excludes-ractor/TestRefinement.rb @@ -0,0 +1 @@ +exclude(/^test_/, "TODO: make it work reliably, create new sandbox binding for each test") diff --git a/test/.excludes-ractor/TestRegexp.rb b/test/.excludes-ractor/TestRegexp.rb new file mode 100644 index 00000000000000..e3dda20316700c --- /dev/null +++ b/test/.excludes-ractor/TestRegexp.rb @@ -0,0 +1,2 @@ +exclude(:test_getter, "gvars") +exclude(:test_ignorecase, "gvars") diff --git a/test/.excludes-ractor/TestRequire.rb b/test/.excludes-ractor/TestRequire.rb new file mode 100644 index 00000000000000..c48871b94572ba --- /dev/null +++ b/test/.excludes-ractor/TestRequire.rb @@ -0,0 +1,39 @@ +exclude(:test_load, "Tempfile") +exclude(:test_load_error_path, "Tempfile") +exclude(:test_load_into_module, "Tempfile") +exclude(:test_load_scope, "Tempfile") +exclude(:test_loaded_features_encoding, "gvars") +exclude(:test_loading_fifo_fd_leak, "Tempfile") +exclude(:test_loading_fifo_threading_raise, "Tempfile") +exclude(:test_loading_fifo_threading_success, "Tempfile") +exclude(:test_private_in_wrapped_load, "Tempfile") +exclude(:test_provide_in_required_file, "gvars") +exclude(:test_public_in_wrapped_load, "Tempfile") +exclude(:test_race_exception, "Tempfile") +exclude(:test_relative, "gvars") +exclude(:test_relative_symlink, "Dir.chdir") +exclude(:test_relative_symlink_realpath, "Dir.chdir") +exclude(:test_require_invalid_shared_object, "Tempfile") +exclude(:test_require_twice, "ractor incompatible") +exclude(:test_require_with_loaded_features_pop, "Tempfile") +exclude(:test_require_with_unc, "Tempfile") +exclude(:test_resolve_feature_path, "gvars") +exclude(:test_resolve_feature_path_with_missing_feature, "$LOAD_PATH") +exclude(:test_require_nonascii_path, "$LOAD_PATH") +exclude(:test_require_nonascii_path_utf8, "$LOAD_PATH") +exclude(:test_require_nonascii_path_shift_jis, "$LOAD_PATH") +exclude(:test_throw_while_loading, "Tempfile") +exclude(:test_require_path_home_1, "global side effects") +exclude(:test_require_path_home_2, "global side effects") +exclude(:test_require_path_home_3, "global side effects") +exclude(:test_require_syntax_error, "$LOADED_FEATURES") +exclude(:test_require_syntax_error_rescued, "$LOADED_FEATURES") +exclude(:test_load_syntax_error, "$LOADED_FEATURES") +exclude(:test_require_changed_current_dir, "Dir.chdir") +exclude(:test_require_changed_home, "Dir.chdir") +exclude(:test_require_to_path_redefined_in_load_path, "Dir.chdir") +exclude(:test_require_not_modified_load_path, "Dir.chdir") +exclude(:test_require_to_str_redefined_in_load_path, "Dir.chdir") +exclude(:test_require_with_array_pop, "Dir.chdir") +exclude(:test_require_with_array_shift, "Dir.chdir") +exclude(:test_require_local_var_on_toplevel, "Dir.chdir") diff --git a/test/.excludes-ractor/TestRequireLib.rb b/test/.excludes-ractor/TestRequireLib.rb new file mode 100644 index 00000000000000..9bf6ed2b493b25 --- /dev/null +++ b/test/.excludes-ractor/TestRequireLib.rb @@ -0,0 +1 @@ +exclude(/^test_/, "subprocesses") diff --git a/test/.excludes-ractor/TestRubyOptimization.rb b/test/.excludes-ractor/TestRubyOptimization.rb new file mode 100644 index 00000000000000..f712b76476ba54 --- /dev/null +++ b/test/.excludes-ractor/TestRubyOptimization.rb @@ -0,0 +1,3 @@ +exclude(:test_string_freeze_saves_memory, "ObjectSpace.memsize_of") +exclude(:test_opt_new, "RubyVM::Iseq.compile/eval not working across multiple ractors") +exclude(/tailcall/, "ractor incompatible") diff --git a/test/.excludes-ractor/TestRubyOptions.rb b/test/.excludes-ractor/TestRubyOptions.rb new file mode 100644 index 00000000000000..9bf6ed2b493b25 --- /dev/null +++ b/test/.excludes-ractor/TestRubyOptions.rb @@ -0,0 +1 @@ +exclude(/^test_/, "subprocesses") diff --git a/test/.excludes-ractor/TestRubyPrimitive.rb b/test/.excludes-ractor/TestRubyPrimitive.rb new file mode 100644 index 00000000000000..094c3bc016d635 --- /dev/null +++ b/test/.excludes-ractor/TestRubyPrimitive.rb @@ -0,0 +1,9 @@ +exclude(:test_constant, "global side effects") +exclude(:test_constant_cache3, "global variable access") +exclude(:test_constant_cache4, "global variable access") +exclude(:test_constant_cache5, "global variable access") +exclude(:test_cvar_from_instance_method, "class variables") +exclude(:test_cvar_from_singleton_method, "class variables") +exclude(:test_cvar_from_singleton_method2, "class variables") +exclude(:test_gvar, "global variable access") +exclude(:test_opassign_ractor_unsafe, "cvars and gvars") diff --git a/test/.excludes-ractor/TestRubyVM.rb b/test/.excludes-ractor/TestRubyVM.rb new file mode 100644 index 00000000000000..4e84d90f27c261 --- /dev/null +++ b/test/.excludes-ractor/TestRubyVM.rb @@ -0,0 +1 @@ +exclude(:test_keep_script_lines, "global side effects") diff --git a/test/.excludes-ractor/TestSetTraceFunc.rb b/test/.excludes-ractor/TestSetTraceFunc.rb new file mode 100644 index 00000000000000..374ba4c555f4c9 --- /dev/null +++ b/test/.excludes-ractor/TestSetTraceFunc.rb @@ -0,0 +1 @@ +exclude(/^test_/, "Tracepoint not working in ractors") diff --git a/test/.excludes-ractor/TestShapes.rb b/test/.excludes-ractor/TestShapes.rb new file mode 100644 index 00000000000000..6a97b591098c65 --- /dev/null +++ b/test/.excludes-ractor/TestShapes.rb @@ -0,0 +1,7 @@ +exclude(:test_class_too_complex_during_delete, "class ivars (anon)") +exclude(:test_generic_too_complex_during_delete, "class ivars (anon)") +exclude(:test_object_too_complex_during_delete, "class ivars (anon class)") +exclude(:test_ordered_alloc_is_not_complex, "ObjectSpace.dump") +exclude(:test_too_many_ivs_on_class, "class ivars") +exclude(:test_removing_when_too_many_ivs_on_class, "class ivars") +exclude(:test_removing_when_too_many_ivs_on_module, "class ivars") diff --git a/test/.excludes-ractor/TestSignal.rb b/test/.excludes-ractor/TestSignal.rb new file mode 100644 index 00000000000000..00ae0caf98b7e0 --- /dev/null +++ b/test/.excludes-ractor/TestSignal.rb @@ -0,0 +1 @@ +exclude(/^test_/, "racy") diff --git a/test/.excludes-ractor/TestSleep.rb b/test/.excludes-ractor/TestSleep.rb new file mode 100644 index 00000000000000..3f462d0271ab0a --- /dev/null +++ b/test/.excludes-ractor/TestSleep.rb @@ -0,0 +1 @@ +exclude(:test_sleep_5sec, "EnvUtil.without_gc") diff --git a/test/.excludes-ractor/TestSprintfComb.rb b/test/.excludes-ractor/TestSprintfComb.rb new file mode 100644 index 00000000000000..3b853bc04ebca1 --- /dev/null +++ b/test/.excludes-ractor/TestSprintfComb.rb @@ -0,0 +1 @@ +exclude(/^test_/, "unshareable procs") # FIXME diff --git a/test/.excludes-ractor/TestString.rb b/test/.excludes-ractor/TestString.rb new file mode 100644 index 00000000000000..684459bef08bab --- /dev/null +++ b/test/.excludes-ractor/TestString.rb @@ -0,0 +1,12 @@ +exclude(:test_chomp, "gvars") +exclude(:test_chomp!, "gvars") +exclude(:test_crypt_no_memory_leak, "assert_no_memory_leak, may as well skip") +exclude(:test_each, "gvars") +exclude(:test_each_line, "gvars") +exclude(:test_fs, "gvars") +exclude(:test_split, "gvars") +exclude(:test_split_with_block, "gvars") +exclude(:test_string_interpolations_across_heaps_get_embedded, "ObjectSpace.dump") +exclude(:test_uminus_frozen, "unpredictable") +exclude(:test_uplus_minus_ractor_unsafe, "ObjectSpace.dump") +exclude(:test_chilled_string_setivar, "global side effects") diff --git a/test/.excludes-ractor/TestString2.rb b/test/.excludes-ractor/TestString2.rb new file mode 100644 index 00000000000000..de9c96c11021e1 --- /dev/null +++ b/test/.excludes-ractor/TestString2.rb @@ -0,0 +1,2 @@ +path = File.expand_path("../TestString.rb", __FILE__) +instance_eval File.read(path), path diff --git a/test/.excludes-ractor/TestStringMemory.rb b/test/.excludes-ractor/TestStringMemory.rb new file mode 100644 index 00000000000000..d357478d3c24ca --- /dev/null +++ b/test/.excludes-ractor/TestStringMemory.rb @@ -0,0 +1 @@ +exclude(/^test_/, "ObjectSpace.trace_object_allocations") diff --git a/test/.excludes-ractor/TestStringchar.rb b/test/.excludes-ractor/TestStringchar.rb new file mode 100644 index 00000000000000..1f90501aa0687e --- /dev/null +++ b/test/.excludes-ractor/TestStringchar.rb @@ -0,0 +1 @@ +exclude(:test_string_ractor_unsafe, "gvars") diff --git a/test/.excludes-ractor/TestStruct.rb b/test/.excludes-ractor/TestStruct.rb new file mode 100644 index 00000000000000..8880a5701b93c6 --- /dev/null +++ b/test/.excludes-ractor/TestStruct.rb @@ -0,0 +1,8 @@ +exclude(:test_struct, "global side effects") +exclude(:test_struct_new, "global side effects") +exclude(:test_struct_new_with_keyword_init, "global side effects") +exclude(:test_redefinition_warning, "global side effects") +exclude(:test_nonascii, "global side effects") +exclude(:test_junk, "global side effects") +exclude(:test_comparison_when_recursive, "Timeout.timeout") +exclude(:test_inspect_ractor_unsafe, "global side effects") diff --git a/test/.excludes-ractor/TestStruct/SubStruct.rb b/test/.excludes-ractor/TestStruct/SubStruct.rb new file mode 100644 index 00000000000000..380af4fbc53494 --- /dev/null +++ b/test/.excludes-ractor/TestStruct/SubStruct.rb @@ -0,0 +1,2 @@ +path = File.expand_path("../../TestStruct.rb", __FILE__) +instance_eval File.read(path), path diff --git a/test/.excludes-ractor/TestStruct/TopStruct.rb b/test/.excludes-ractor/TestStruct/TopStruct.rb new file mode 100644 index 00000000000000..380af4fbc53494 --- /dev/null +++ b/test/.excludes-ractor/TestStruct/TopStruct.rb @@ -0,0 +1,2 @@ +path = File.expand_path("../../TestStruct.rb", __FILE__) +instance_eval File.read(path), path diff --git a/test/.excludes-ractor/TestSuper.rb b/test/.excludes-ractor/TestSuper.rb new file mode 100644 index 00000000000000..f3b519252db24d --- /dev/null +++ b/test/.excludes-ractor/TestSuper.rb @@ -0,0 +1,3 @@ +exclude(:test_super_missing_prepended_module, "GC.stress=") +exclude(:test_public_zsuper_with_prepend, "Timeout.timeout") +exclude(:test_overlaid_ractor_unsafe, "unshareable proc") diff --git a/test/.excludes-ractor/TestSymbol.rb b/test/.excludes-ractor/TestSymbol.rb new file mode 100644 index 00000000000000..55066685f21320 --- /dev/null +++ b/test/.excludes-ractor/TestSymbol.rb @@ -0,0 +1,3 @@ +exclude(:test_to_proc_arg_with_refinements, "FIXME: ractor_confirm_belonging issue") +exclude(:test_to_proc_arity_with_refinements, "FIXME: ractor_confirm_belonging issue") +exclude(:test_to_proc_lambda_with_refinements, "FIXME: ractor_confirm_belonging issue") diff --git a/test/.excludes-ractor/TestSyntax.rb b/test/.excludes-ractor/TestSyntax.rb new file mode 100644 index 00000000000000..8b32bf9d642182 --- /dev/null +++ b/test/.excludes-ractor/TestSyntax.rb @@ -0,0 +1,5 @@ +exclude(:test_eval_return_toplevel, "Tempfile") +exclude(:test_must_ascii_compatible, "Tempfile") +exclude(:test_return_toplevel, "Tempfile") +exclude(:test_script_lines, "Tempfile") +exclude(:test_unassignable, "gvars") diff --git a/test/.excludes-ractor/TestSystem.rb b/test/.excludes-ractor/TestSystem.rb new file mode 100644 index 00000000000000..210ddddd44c36f --- /dev/null +++ b/test/.excludes-ractor/TestSystem.rb @@ -0,0 +1 @@ +exclude(:test_system_redirect_win, "Dir.chdir") diff --git a/test/.excludes-ractor/TestThread.rb b/test/.excludes-ractor/TestThread.rb new file mode 100644 index 00000000000000..1301526b407da2 --- /dev/null +++ b/test/.excludes-ractor/TestThread.rb @@ -0,0 +1,13 @@ +exclude(:test_blocking_mutex_unlocked_on_fork, "fork") +exclude(:test_fork_in_thread, "fork") +exclude(:test_fork_value, "fork") +exclude(:test_fork_while_locked, "fork") +exclude(:test_fork_while_parent_locked, "fork") +exclude(:test_fork_while_mutex_locked_by_forker, "fork") +exclude(:test_join_limit_, "ractor incompatible") +exclude(:test_local_barrier, "gvars") +exclude(:test_thread_interrupt_for_killed_thread, "Timeout.timeout") +exclude(:test_thread_native_thread_id_across_fork_on_linux, "fork") +exclude(:test_thread_timer_and_ensure, "ractor incompatible") +exclude(:test_thread_timer_and_interrupt, "Timeout.timeout") +exclude(/^test_join_limit_negative_/, "Timeout.timeout") diff --git a/test/.excludes-ractor/TestThreadConditionVariable.rb b/test/.excludes-ractor/TestThreadConditionVariable.rb new file mode 100644 index 00000000000000..03123308dd4016 --- /dev/null +++ b/test/.excludes-ractor/TestThreadConditionVariable.rb @@ -0,0 +1,4 @@ +exclude(:test_condvar_fork, "fork") +exclude(:test_condvar_wait_and_broadcast, "Timeout.timeout") +exclude(:test_condvar_wait_deadlock_2, "Timeout.timeout") +exclude(:test_condvar_timed_wait, "FIXME: unpredictable") diff --git a/test/.excludes-ractor/TestThreadQueue.rb b/test/.excludes-ractor/TestThreadQueue.rb new file mode 100644 index 00000000000000..23ed7cbb9739a3 --- /dev/null +++ b/test/.excludes-ractor/TestThreadQueue.rb @@ -0,0 +1,2 @@ +exclude(:test_fork_while_queue_waiting, "fork") +exclude(:test_queue_thread_raise, "Timeout.timeout") diff --git a/test/.excludes-ractor/TestTime.rb b/test/.excludes-ractor/TestTime.rb new file mode 100644 index 00000000000000..24d042063ceda6 --- /dev/null +++ b/test/.excludes-ractor/TestTime.rb @@ -0,0 +1,7 @@ +exclude(:test_2038, "class ivars") +exclude(:test_time_interval, "Timeout.timeout") +exclude(:test_timegm, "class ivars") +exclude(:test_marshal_broken_offset, "global side effects") +exclude(:test_marshal_broken_zone, "global side effects") +exclude(:test_marshal_zone_ractor_unsafe, "global side effects") +exclude(:test_deconstruct_keys, "global side effects") diff --git a/test/.excludes-ractor/TestTrace.rb b/test/.excludes-ractor/TestTrace.rb new file mode 100644 index 00000000000000..9f1869f0bc6ee8 --- /dev/null +++ b/test/.excludes-ractor/TestTrace.rb @@ -0,0 +1 @@ +exclude(/^test_/, "TODO: Tracepoint doesn't work well in ractors") diff --git a/test/.excludes-ractor/TestVariable.rb b/test/.excludes-ractor/TestVariable.rb new file mode 100644 index 00000000000000..b4d06c86414c06 --- /dev/null +++ b/test/.excludes-ractor/TestVariable.rb @@ -0,0 +1,12 @@ +exclude(:test_cloned_allows_setting_cvar, "cvars") +exclude(:test_cloned_classes_copy_cvar_cache, "cvars") +exclude(:test_cvar_overtaken_by_module, "cvars") +exclude(:test_cvar_overtaken_by_parent_class, "cvars") +exclude(:test_global_variable_popped, "gvars") +exclude(:test_global_variables, "gvars") +exclude(:test_include_refined_module_class_variable, "cvars") +exclude(:test_remove_instance_variables_class, "class ivars") +exclude(:test_set_class_variable_on_frozen_object, "cvars") +exclude(:test_setting_class_variable_on_module_through_inheritance, "cvars") +exclude(:test_singleton_class_included_class_variable, "cvars") +exclude(:test_variable, "cvars") 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/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/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..b108d646ffa7ac 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,20 @@ def to_codepoints(string) end def setup - @@tests ||= read_tests + @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 +62,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 +76,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 +95,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/test_array.rb b/test/ruby/test_array.rb index a3ac0a6a0b420d..9af96e6a3d1db3 100644 --- a/test/ruby/test_array.rb +++ b/test/ruby/test_array.rb @@ -659,7 +659,9 @@ def test_concat assert_raise(TypeError) { @cls[0].concat(:foo) } assert_raise(FrozenError) { @cls[0].freeze.concat(:foo) } + end + def test_concat_under_gc_stress a = @cls[nil] def (x = Object.new).to_ary ary = Array.new(2) @@ -1174,6 +1176,7 @@ def test_values_at def test_join assert_deprecated_warning {$, = ""} + a = @cls[] assert_equal("", assert_deprecated_warn(/non-nil value/) {a.join}) assert_equal("", a.join(',')) @@ -1975,14 +1978,14 @@ def test_to_s $, = nil 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 @@ -1992,14 +1995,17 @@ def test_to_h [[:first_one, :ok], :not_ok].to_h } assert_equal "wrong element type Symbol at 1 (expected array)", e.message - array = [eval("class C\u{1f5ff}; self; end").new] - assert_raise_with_message(TypeError, /C\u{1f5ff}/) {array.to_h} e = assert_raise(ArgumentError) { [[:first_one, :ok], [1, 2], [:not_ok]].to_h } assert_equal "wrong array length at 2 (expected 2, was 1)", e.message end + def test_to_h_single_element_with_object + array = [eval("class C\u{1f5ff}; self; end").new] + assert_raise_with_message(TypeError, /C\u{1f5ff}/) {array.to_h} + end + def test_to_h_block array = StubToH assert_equal({"key" => "value", "obtained" => "via_to_ary"}, @@ -2012,15 +2018,18 @@ def test_to_h_block [[:first_one, :ok], :not_ok].to_h {|k, v| v ? [k, v] : k} } assert_equal "wrong element type Symbol at 1 (expected array)", e.message - array = [1] - k = eval("class C\u{1f5ff}; self; end").new - assert_raise_with_message(TypeError, /C\u{1f5ff}/) {array.to_h {k}} e = assert_raise(ArgumentError) { [[:first_one, :ok], [1, 2], [:not_ok]].to_h {|kv| kv} } assert_equal "wrong array length at 2 (expected 2, was 1)", e.message end + def test_to_h_block_single_element_with_object + array = [1] + k = eval("class C\u{1f5ff}; self; end").new + assert_raise_with_message(TypeError, /C\u{1f5ff}/) {array.to_h {k}} + end + def test_min assert_equal(3, [3].min) assert_equal(1, [1, 2, 3, 1, 2].min) @@ -2435,13 +2444,6 @@ def test_product assert_equal(@cls[[1],[2]], @cls[1,2].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) - def (o = Object.new).to_ary; GC.start; [3,4] end acc = [1,2].product(*[o]*10) assert_equal([1,2].product([3,4], [3,4], [3,4], [3,4], [3,4], [3,4], [3,4], [3,4], [3,4], [3,4]), @@ -2452,6 +2454,15 @@ def (o = Object.new).to_ary; GC.start; [3,4] end a.all? {|x| assert_not_include(x, 0)} end + def test_product_under_gc_stress + 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) + end + def test_permutation a = @cls[] assert_equal(1, a.permutation(0).size) diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb index c7a946dec868bb..a55e4f74fd5f0f 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 = EnvUtil.suppress_warning { 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_beginendblock.rb b/test/ruby/test_beginendblock.rb index 3706efab52dd0b..2153dba236f6aa 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') diff --git a/test/ruby/test_bignum.rb b/test/ruby/test_bignum.rb index dd6f4baa4c292b..c944ccc21adffa 100644 --- a/test/ruby/test_bignum.rb +++ b/test/ruby/test_bignum.rb @@ -70,17 +70,17 @@ 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) + 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)) diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index f40817e7a1ef54..ca6eb3eb6eafda 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -243,6 +243,9 @@ def test_check_inheritable assert_raise(TypeError) { Class.new(c) } assert_raise(TypeError) { Class.new(Class) } assert_raise(TypeError) { eval("class Foo < Class; end") } + end + + def test_check_inheritable_break_with_object m = "M\u{1f5ff}" o = Class.new {break eval("class #{m}; self; end.new")} assert_raise_with_message(TypeError, /#{m}/) {Class.new(o)} @@ -505,20 +508,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 @@ -805,7 +814,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..2fde50e130cb58 100644 --- a/test/ruby/test_compile_prism.rb +++ b/test/ruby/test_compile_prism.rb @@ -134,9 +134,7 @@ 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) assert_prism_eval("defined? A") assert_prism_eval("defined? ::A") assert_prism_eval("defined? A::B") @@ -151,29 +149,11 @@ 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") + 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") @@ -274,6 +254,28 @@ def self.m1; defined?(return) end assert_prism_eval("defined?(unless true; 1; end)") end + def test_DefinedNode_ractor_unsafe + assert_prism_eval("defined? $a") + assert_prism_eval("defined? @@a") + 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 + def test_GlobalVariableReadNode assert_prism_eval("$pit = 1; $pit") end @@ -350,11 +352,17 @@ def test_ConstantPathAndWriteNode def test_ConstantPathOrWriteNode assert_prism_eval("Prism::CPOrWN = nil; Prism::CPOrWN ||= 1") assert_prism_eval("Prism::CPOrWN ||= 1") + end + + def test_ConstantPathOrWriteNode_ractor_unsafe assert_prism_eval("::CPOrWN = nil; ::CPOrWN ||= 1") end def test_ConstantPathOperatorWriteNode assert_prism_eval("Prism::CPOWN = 0; Prism::CPOWN += 1") + end + + def test_ConstantPathOperatorWriteNode_ractor_unsafe assert_prism_eval("::CPOWN = 0; ::CPOWN += 1") end @@ -375,15 +383,15 @@ def test_GlobalVariableWriteNode 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 @@ -992,11 +1000,14 @@ 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') assert_prism_eval('if true or true; end'); end + def test_IfNode_ractor_unsafe + assert_prism_eval('if 1..; end') # accesses $. implicitly + assert_prism_eval('if 1..2; end') + end + def test_OrNode assert_prism_eval("true || 1") assert_prism_eval("false || 1") @@ -1047,9 +1058,7 @@ 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?) assert_prism_eval("for foo, in [1,2,3] do end") assert_prism_eval("for i, j in {a: 'b'} do; i; j; end") @@ -1058,6 +1067,10 @@ def test_ForNode assert_prism_eval("for *x in [[1,2], [3,4]] do; x; end") end + def test_ForNode_gvar + assert_prism_eval("for $i in [1,2] do; $i; end") + end + ############################################################################ # Throws # ############################################################################ @@ -1207,9 +1220,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 +1231,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,12 +1861,15 @@ 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") assert_prism_eval("foo = 1; END { foo.nil? }") assert_prism_eval("foo = 1; END { END { foo.nil? }}") end + def test_PostExecutionNode_set_ivar + assert_prism_eval("END { @b }; @b = 1") + assert_prism_eval("END { @b; 0 }; @b = 1") + end + def test_ProgramNode assert_prism_eval("") assert_prism_eval("1") @@ -2612,11 +2628,14 @@ def test_PinnedExpressionNode end def test_PinnedVariableNode + assert_prism_eval("prism = 1; 1 in ^prism") + assert_prism_eval("[1].each { 1 => ^it }") + end + + def test_PinnedVariableNode_ractor_unsafe 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") - assert_prism_eval("prism = 1; 1 in ^prism") - assert_prism_eval("[1].each { 1 => ^it }") end ############################################################################ @@ -2723,7 +2742,7 @@ def test_parse_file private def compare_eval(source, raw:, location:) - source = raw ? source : "class Prism::TestCompilePrism\n#{source}\nend" + source = raw ? source : "class Prism::TestCompilePrism#{Ractor.current.object_id}\n#{source}\nend" ruby_eval = RubyVM::InstructionSequence.compile_parsey(source).eval prism_eval = RubyVM::InstructionSequence.compile_prism(source).eval 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_enum.rb b/test/ruby/test_enum.rb index 237bdc8a4d0155..70f2a311d3a151 100644 --- a/test/ruby/test_enum.rb +++ b/test/ruby/test_enum.rb @@ -802,6 +802,9 @@ def ary.each; [3, 4].each{|e|yield e}; end assert_equal([[1, 3], [2, 4], [3, nil], [1, nil], [2, nil]], @obj.zip(ary)) def ary.to_ary; [5, 6]; end assert_equal([[1, 5], [2, 6], [3, nil], [1, nil], [2, nil]], @obj.zip(ary)) + end + + def test_zip_ractor_unsafe obj = eval("class C\u{1f5ff}; self; end").new assert_raise_with_message(TypeError, /C\u{1f5ff}/) {(1..1).zip(obj)} end diff --git a/test/ruby/test_enumerator.rb b/test/ruby/test_enumerator.rb index cd62cd8acb7746..765ea57262084c 100644 --- a/test/ruby/test_enumerator.rb +++ b/test/ruby/test_enumerator.rb @@ -495,10 +495,12 @@ def test_generator assert_raise(LocalJumpError) {Enumerator::Generator.new} assert_raise(TypeError) {Enumerator::Generator.new(1)} - obj = eval("class C\u{1f5ff}; self; end").new - assert_raise_with_message(TypeError, /C\u{1f5ff}/) { - Enumerator::Generator.new(obj) - } + unless multiple_ractors? + obj = eval("class C\u{1f5ff}; self; end").new + assert_raise_with_message(TypeError, /C\u{1f5ff}/) { + Enumerator::Generator.new(obj) + } + end end def test_generator_args 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..6ffbd89b45c773 100644 --- a/test/ruby/test_eval.rb +++ b/test/ruby/test_eval.rb @@ -28,8 +28,6 @@ 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") assert_equal 15, eval("Const") assert_equal 16, eval("7 + 9") @@ -39,12 +37,19 @@ def test_eval_basic 1.times { assert_equal 12, eval("@ivar") - assert_equal 13, eval("@@cvar") - assert_equal 14, eval("$gvar__eval") assert_equal 15, eval("Const") } end + def test_eval_basic_ractor_unsafe + assert_equal 13, eval("@@cvar") + assert_equal 14, eval("$gvar__eval") + 1.times do + assert_equal 13, eval("@@cvar") + assert_equal 14, eval("$gvar__eval") + end + end + def test_eval_binding_basic assert_equal nil, eval("nil", binding()) assert_equal true, eval("true", binding()) @@ -56,8 +61,6 @@ 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()) assert_equal 15, eval("Const", binding()) assert_equal 16, eval("7 + 9", binding()) @@ -67,12 +70,19 @@ def test_eval_binding_basic 1.times { assert_equal 12, eval("@ivar") - assert_equal 13, eval("@@cvar") - assert_equal 14, eval("$gvar__eval") assert_equal 15, eval("Const") } end + def test_eval_binding_basic_ractor_unsafe + assert_equal 13, eval("@@cvar", binding()) + assert_equal 14, eval("$gvar__eval", binding()) + 1.times do + assert_equal 13, eval("@@cvar", binding()) + assert_equal 14, eval("$gvar__eval", binding()) + end + end + def test_module_eval_string_basic c = self.class assert_equal nil, c.module_eval("nil") @@ -83,8 +93,6 @@ 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") 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,12 +102,20 @@ 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") assert_equal 15, c.module_eval("Const") } end + def test_module_eval_string_basic_ractor_unsafe + c = self.class + assert_equal 13, c.module_eval("@@cvar") + assert_equal 14, c.module_eval("$gvar__eval") + 1.times do + assert_equal 13, c.module_eval("@@cvar") + assert_equal 14, c.module_eval("$gvar__eval") + end + end + def test_module_eval_block_basic c = self.class assert_equal nil, c.module_eval { nil } @@ -110,8 +126,6 @@ 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 } 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,12 +135,20 @@ 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 } assert_equal 15, c.module_eval { Const } } end + def test_module_eval_block_basic_ractor_unsafe + c = self.class + assert_equal 13, c.module_eval { @@cvar } + assert_equal 14, c.module_eval { $gvar__eval } + 1.times do + assert_equal 13, c.module_eval { @@cvar } + assert_equal 14, c.module_eval { $gvar__eval } + end + end + def test_module_eval_block_symbol assert_equal "Math", Math.module_eval(&:to_s) end @@ -150,8 +172,6 @@ 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") 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,9 +180,18 @@ def test_instance_eval_string_basic 1.times { assert_equal 12, o.instance_eval("@ivar") unless o.frozen? + assert_equal 15, o.instance_eval("Const") + } + end + end + + def test_instance_eval_string_basic_ractor_unsafe + forall_TYPE do |o| + assert_equal 13, o.instance_eval("@@cvar") + assert_equal 14, o.instance_eval("$gvar__eval") + 1.times { assert_equal 13, o.instance_eval("@@cvar") assert_equal 14, o.instance_eval("$gvar__eval") - assert_equal 15, o.instance_eval("Const") } end end @@ -178,8 +207,6 @@ 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 } 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,9 +215,18 @@ def test_instance_eval_block_basic 1.times { assert_equal 12, o.instance_eval { @ivar } unless o.frozen? + assert_equal 15, o.instance_eval { Const } + } + end + end + + def test_instance_eval_block_basic_ractor_unsafe + forall_TYPE do |o| + assert_equal 13, o.instance_eval { @@cvar } + assert_equal 14, o.instance_eval { $gvar__eval } + 1.times { assert_equal 13, o.instance_eval { @@cvar } assert_equal 14, o.instance_eval { $gvar__eval } - assert_equal 15, o.instance_eval { Const } } end end @@ -270,8 +306,6 @@ 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 } 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,9 +314,18 @@ def test_instance_exec_block_basic 1.times { assert_equal 12, o.instance_exec { @ivar } unless o.frozen? + assert_equal 15, o.instance_exec { Const } + } + end + end + + def test_instance_exec_block_basic_ractor_unsafe + forall_TYPE do |o| + assert_equal 13, o.instance_exec { @@cvar } + assert_equal 14, o.instance_exec { $gvar__eval } + 1.times { assert_equal 13, o.instance_exec { @@cvar } assert_equal 14, o.instance_exec { $gvar__eval } - assert_equal 15, o.instance_exec { Const } } end end @@ -346,14 +389,6 @@ def test_eval_orig assert(!eval('nil')) assert(!eval('false')) - $foo = 'assert(true)' - begin - eval $foo - rescue - assert(false) - end - - assert_equal('assert(true)', eval("$foo")) assert_equal(true, eval("true")) i = i = 5 assert(eval("i == 5")) @@ -371,24 +406,6 @@ def test_eval_orig end assert(!bad) - # !! use class_eval to avoid nested definition - x = self.class.class_eval %q( - module EvTest - EVTEST1 = 25 - evtest2 = 125 - evtest2 = evtest2 - binding - end - ) - assert_equal(25, eval("EVTEST1", x)) # constant in module - assert_equal(125, eval("evtest2", x)) # local var in module - bad = true - begin - eval("EVTEST1") - rescue NameError # must raise error - bad = false - end - assert(!bad) x = binding eval "i = 1", x @@ -418,9 +435,39 @@ module EvTest assert_equal(55, foo22) }.call + end + + def test_eval_orig_ractor_unsafe + $foo = 'assert(true)' + begin + eval $foo + rescue + assert(false) + end + assert_equal('assert(true)', eval("$foo")) + + # !! use class_eval to avoid nested definition + x = self.class.class_eval %q( + module EvTest + EVTEST1 = 25 + evtest2 = 125 + evtest2 = evtest2 + binding + end + ) + assert_equal(25, eval("EVTEST1", x)) # constant in module + assert_equal(125, eval("evtest2", x)) # local var in module + bad = true + begin + eval("EVTEST1") + rescue NameError # must raise error + bad = false + end + assert(!bad) + self.class.class_eval do remove_const :EvTest - end + end if x end def test_nil_instance_eval_cvar diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb index 17ff5a2e82996b..9ee506c6f5916b 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 @@ -1063,38 +1063,39 @@ def test_message_of_name_error end def capture_warning_warn(category: false) - verbose = $VERBOSE - categories = Warning.categories.to_h {|cat| [cat, Warning[cat]]} - warning = [] + begin + verbose = $VERBOSE + categories = Warning.categories.to_h {|cat| [cat, Warning[cat]]} + warning = [] - ::Warning.class_eval do - alias_method :warn2, :warn - remove_method :warn + ::Warning.class_eval do + alias_method :warn2, :warn + remove_method :warn - if category - define_method(:warn) do |str, category: nil| - warning << [str, category] - end - else - define_method(:warn) do |str, category: nil| - warning << str + if category + define_method(:warn) do |str, category: nil| + warning << [str, category] + end + else + define_method(:warn) do |str, category: nil| + warning << str + end end end - end - $VERBOSE = true - Warning.categories.each {|cat| Warning[cat] = true} - yield - - return warning - ensure - $VERBOSE = verbose - categories.each {|cat, flag| Warning[cat] = flag} + $VERBOSE = true + Warning.categories.each {|cat| Warning[cat] = true} + yield + return warning + ensure + $VERBOSE = verbose + categories.each {|cat, flag| Warning[cat] = flag} - ::Warning.class_eval do - remove_method :warn - alias_method :warn, :warn2 - remove_method :warn2 + ::Warning.class_eval do + remove_method :warn + alias_method :warn, :warn2 + remove_method :warn2 + end end end diff --git a/test/ruby/test_file.rb b/test/ruby/test_file.rb index a3d6221c0f924b..636f4447256722 100644 --- a/test/ruby/test_file.rb +++ b/test/ruby/test_file.rb @@ -250,10 +250,6 @@ 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(".")}) - realpath = File.join(realdir, "test") - File.write(realpath, "") - assert_equal(realpath, Dir.chdir(realdir) {File.realpath("test")}) if File::ALT_SEPARATOR bug2961 = '[ruby-core:28653]' assert_equal(realdir, File.realpath(realdir.tr(File::SEPARATOR, File::ALT_SEPARATOR)), bug2961) @@ -261,6 +257,16 @@ def test_realpath } end + def test_realpath_ractor_unsafe + Dir.mktmpdir('rubytest-realpath') {|tmpdir| + realdir = File.realpath(tmpdir) + assert_equal(realdir, Dir.chdir(realdir) {File.realpath(".")}) + realpath = File.join(realdir, "test") + File.write(realpath, "") + assert_equal(realpath, Dir.chdir(realdir) {File.realpath("test")}) + } + end + def test_realpath_encoding fsenc = Encoding.find("filesystem") nonascii = "\u{0391 0410 0531 10A0 05d0 2C00 3042}" @@ -300,8 +306,6 @@ 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")}) } begin result = File.realdirpath("bar", "//:/foo") @@ -313,6 +317,14 @@ def test_realdirpath end end + def test_realdirpath_ractor_unsafe + Dir.mktmpdir('rubytest-realdirpath') {|tmpdir| + realdir = File.realpath(tmpdir) + assert_equal(realdir, Dir.chdir(realdir) {File.realdirpath(".")}) + assert_equal(File.join(realdir, "foo"), Dir.chdir(realdir) {File.realdirpath("foo")}) + } + end + def test_realdirpath_junction Dir.mktmpdir('rubytest-realpath') {|tmpdir| Dir.chdir(tmpdir) do diff --git a/test/ruby/test_file_exhaustive.rb b/test/ruby/test_file_exhaustive.rb index 222578be269581..16bc5d9271f30f 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) @@ -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 @@ -279,13 +279,20 @@ 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) assert_nothing_raised { File.stat(prefix) } + end + end if NTFS + def test_stat_dotted_prefix_ractor_unsafe + Dir.mktmpdir("#{Ractor.current.object_id}") do |dir| + prefix = File.join(dir, "...a") + Dir.mkdir(prefix) + assert_file.exist?(prefix) Dir.chdir(dir) do assert_nothing_raised { File.stat(File.basename(prefix)) } end @@ -711,6 +718,9 @@ def test_utime File.utime(t + 1, t + 2, zerofile) assert_equal(t + 1, File.atime(zerofile)) assert_equal(t + 2, File.mtime(zerofile)) + end + + def test_utime_ractor_unsafe Dir.mktmpdir do |dir| Dir.chdir(dir) do path = "foo\u{30b3 30d4 30fc}" @@ -925,17 +935,19 @@ def test_expand_path_encoding end def test_expand_path_encoding_filesystem - home = ENV["HOME"] - ENV["HOME"] = "#{DRIVE}/UserHome" + begin + home = ENV["HOME"] + ENV["HOME"] = "#{DRIVE}/UserHome" - path = "~".encode("US-ASCII") - dir = "C:/".encode("IBM437") - fs = Encoding.find("filesystem") + path = "~".encode("US-ASCII") + dir = "C:/".encode("IBM437") + fs = Encoding.find("filesystem") - assert_equal fs, File.expand_path(path).encoding - assert_equal fs, File.expand_path(path, dir).encoding - ensure - ENV["HOME"] = home + assert_equal fs, File.expand_path(path).encoding + assert_equal fs, File.expand_path(path, dir).encoding + ensure + ENV["HOME"] = home + end end UnknownUserHome = "~foo_bar_baz_unknown_user_wahaha".freeze @@ -967,20 +979,22 @@ def test_expand_path_home end def test_expand_path_home_dir_string - home = ENV["HOME"] - new_home = "#{DRIVE}/UserHome" - ENV["HOME"] = new_home - bug8034 = "[ruby-core:53168]" - - assert_equal File.join(new_home, "foo"), File.expand_path("foo", "~"), bug8034 - assert_equal File.join(new_home, "bar", "foo"), File.expand_path("foo", "~/bar"), bug8034 - - assert_raise(ArgumentError) { File.expand_path(".", UnknownUserHome) } - assert_nothing_raised(ArgumentError) { File.expand_path("#{DRIVE}/", UnknownUserHome) } - ENV["HOME"] = "#{DRIVE}UserHome" - assert_raise(ArgumentError) { File.expand_path("~") } - ensure - ENV["HOME"] = home + begin + home = ENV["HOME"] + new_home = "#{DRIVE}/UserHome" + ENV["HOME"] = new_home + bug8034 = "[ruby-core:53168]" + + assert_equal File.join(new_home, "foo"), File.expand_path("foo", "~"), bug8034 + assert_equal File.join(new_home, "bar", "foo"), File.expand_path("foo", "~/bar"), bug8034 + + assert_raise(ArgumentError) { File.expand_path(".", UnknownUserHome) } + assert_nothing_raised(ArgumentError) { File.expand_path("#{DRIVE}/", UnknownUserHome) } + ENV["HOME"] = "#{DRIVE}UserHome" + assert_raise(ArgumentError) { File.expand_path("~") } + ensure + ENV["HOME"] = home + end end if /mswin|mingw/ =~ RUBY_PLATFORM @@ -1102,31 +1116,37 @@ 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 - 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 + begin + 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 + end end def test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_unc_home - old_home = ENV["HOME"] - unc_home = ENV["HOME"] = "//UserHome" - assert_equal(unc_home, File.expand_path("~")) - ensure - ENV["HOME"] = old_home + begin + old_home = ENV["HOME"] + unc_home = ENV["HOME"] = "//UserHome" + assert_equal(unc_home, File.expand_path("~")) + ensure + ENV["HOME"] = old_home + end end if DRIVE def test_expand_path_does_not_modify_a_home_string_argument - 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 + begin + 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 + end end def test_expand_path_raises_argument_error_for_any_supplied_username @@ -1146,11 +1166,13 @@ def test_expand_path_error_for_nonexistent_username end unless DRIVE def test_expand_path_error_for_non_absolute_home - old_home = ENV["HOME"] - ENV["HOME"] = "./UserHome" - assert_raise_with_message(ArgumentError, /non-absolute home/) {File.expand_path("~")} - ensure - ENV["HOME"] = old_home + begin + old_home = ENV["HOME"] + ENV["HOME"] = "./UserHome" + assert_raise_with_message(ArgumentError, /non-absolute home/) {File.expand_path("~")} + ensure + ENV["HOME"] = old_home + end end def test_expand_path_raises_a_type_error_if_not_passed_a_string_type 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 a8a937f078a9da..a2d2aca368346b 100644 --- a/test/ruby/test_gc.rb +++ b/test/ruby/test_gc.rb @@ -330,10 +330,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 diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index 32384f5a5c1fd4..8894dc7dcaa92f 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 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 0025aa5a7dd502..11e42e6b7387ce 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 @@ -1145,7 +1146,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 @@ -1916,6 +1917,7 @@ def test_pid_after_close_read end def make_tempfile + pend "Tempfile" unless main_ractor? t = Tempfile.new("test_io") t.binmode t.puts "foo" @@ -2539,7 +2541,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?) @@ -2590,7 +2592,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 @@ -2823,22 +2825,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 @@ -3629,7 +3634,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 @@ -3686,14 +3691,17 @@ def test_setpos end def test_std_fileno - assert_equal(0, STDIN.fileno) - assert_equal(1, STDOUT.fileno) - assert_equal(2, STDERR.fileno) assert_equal(0, $stdin.fileno) assert_equal(1, $stdout.fileno) assert_equal(2, $stderr.fileno) end + def test_std_fileno_ractor_unsafe + assert_equal(0, STDIN.fileno) + assert_equal(1, STDOUT.fileno) + assert_equal(2, STDERR.fileno) + end + def test_frozen_fileno bug9865 = '[ruby-dev:48241] [Bug #9865]' with_pipe do |r,w| @@ -3786,7 +3794,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 @@ -4459,4 +4467,20 @@ 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 + begin + fork { } + :ng + rescue Ractor::IsolationError + :ok + end + end + assert_equal :ok, r.value + RUBY + end end diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb index 62c46678882a10..88a9e902594a72 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) diff --git a/test/ruby/test_io_m17n.rb b/test/ruby/test_io_m17n.rb index b01d627d920d7e..465858fa67e218 100644 --- a/test/ruby/test_io_m17n.rb +++ b/test/ruby/test_io_m17n.rb @@ -11,9 +11,10 @@ class TestIO_M17N < Test::Unit::TestCase Encoding::EUC_JP, Encoding::Shift_JIS, Encoding::UTF_8 - ] + ].freeze def with_tmpdir + omit "Dir.chdir" unless main_ractor? Dir.mktmpdir {|dir| Dir.chdir(dir) { yield dir @@ -404,18 +405,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 @@ -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 diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb index fa716787fe9841..1f1d320fd2e793 100644 --- a/test/ruby/test_iseq.rb +++ b/test/ruby/test_iseq.rb @@ -688,8 +688,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 c836abd0c663b4..000d049a6e2e89 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) @@ -966,7 +966,7 @@ def test_Thread_new_kwsplat assert_equal([1, h3], t.new(**h3, &f).value) assert_equal([1, h3], t.new(a: 1, **h2, &f).value) ensure - Thread.report_on_exception = true + Thread.report_on_exception = true if main_ractor? end def test_Fiber_resume_kwsplat 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.rb b/test/ruby/test_m17n.rb index 9f7a3c7f4b7ec4..026148e5f48a98 100644 --- a/test/ruby/test_m17n.rb +++ b/test/ruby/test_m17n.rb @@ -186,35 +186,33 @@ def test_string_inspect_invalid end def test_string_inspect_encoding - [ - Encoding::UTF_8, - Encoding::EUC_JP, - Encoding::Windows_31J, - Encoding::GB18030, - ].each do |e| - EnvUtil.with_default_external(e) do - str = "\x81\x30\x81\x30".force_encoding('GB18030') - assert_equal(Encoding::GB18030 == e ? %{"#{str}"} : '"\x{81308130}"', str.inspect) - str = e("\xa1\x8f\xa1\xa1") - expected = "\"\\xA1\x8F\xA1\xA1\"".force_encoding("EUC-JP") - assert_equal(Encoding::EUC_JP == e ? expected : "\"\\xA1\\x{8FA1A1}\"", str.inspect) - str = s("\x81@") - assert_equal(Encoding::Windows_31J == e ? %{"#{str}"} : '"\x{8140}"', str.inspect) - str = "\u3042\u{10FFFD}" - assert_equal(Encoding::UTF_8 == e ? %{"#{str}"} : '"\u3042\u{10FFFD}"', str.inspect) - end - end - - EnvUtil.with_default_external(Encoding::UTF_8) do - [ - Encoding::UTF_16BE, - Encoding::UTF_16LE, - Encoding::UTF_32BE, - Encoding::UTF_32LE, - Encoding::UTF8_SOFTBANK - ].each do |e| - str = "abc".encode(e) - assert_equal('"abc"', str.inspect) + EnvUtil.suppress_warning do + begin + orig_int = Encoding.default_internal + orig_ext = Encoding.default_external + Encoding.default_internal = nil + [Encoding::UTF_8, Encoding::EUC_JP, Encoding::Windows_31J, Encoding::GB18030]. + each do |e| + Encoding.default_external = e + str = "\x81\x30\x81\x30".force_encoding('GB18030') + assert_equal(Encoding::GB18030 == e ? %{"#{str}"} : '"\x{81308130}"', str.inspect) + str = e("\xa1\x8f\xa1\xa1") + expected = "\"\\xA1\x8F\xA1\xA1\"".force_encoding("EUC-JP") + assert_equal(Encoding::EUC_JP == e ? expected : "\"\\xA1\\x{8FA1A1}\"", str.inspect) + str = s("\x81@") + assert_equal(Encoding::Windows_31J == e ? %{"#{str}"} : '"\x{8140}"', str.inspect) + str = "\u3042\u{10FFFD}" + assert_equal(Encoding::UTF_8 == e ? %{"#{str}"} : '"\u3042\u{10FFFD}"', str.inspect) + end + Encoding.default_external = Encoding::UTF_8 + [Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE, + Encoding::UTF8_SOFTBANK].each do |e| + str = "abc".encode(e) + assert_equal('"abc"', str.inspect) + end + ensure + Encoding.default_internal = orig_int + Encoding.default_external = orig_ext end end end @@ -248,11 +246,20 @@ def test_utf_without_bom_valid end def test_object_utf16_32_inspect - EnvUtil.with_default_external(Encoding::UTF_8) do - o = Object.new - [Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE].each do |e| - o.instance_eval "undef inspect;def inspect;'abc'.encode('#{e}');end" - assert_equal '[abc]', [o].inspect + EnvUtil.suppress_warning do + begin + orig_int = Encoding.default_internal + orig_ext = Encoding.default_external + Encoding.default_internal = nil + Encoding.default_external = Encoding::UTF_8 + o = Object.new + [Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE].each do |e| + o.instance_eval "undef inspect;def inspect;'abc'.encode('#{e}');end" + assert_equal '[abc]', [o].inspect + end + ensure + Encoding.default_internal = orig_int + Encoding.default_external = orig_ext end end end 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..de3d73d48bf3b5 100644 --- a/test/ruby/test_marshal.rb +++ b/test/ruby/test_marshal.rb @@ -72,23 +72,26 @@ 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 } + 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 @@ -334,11 +337,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 @@ -404,14 +407,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 +424,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 @@ -662,28 +665,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} do + MethodInMethodClass_Setup = Ractor.make_shareable(proc do remove_const :MethodInMethodClass if defined? MethodInMethodClass class MethodInMethodClass @@ -1573,7 +1574,7 @@ def m2 end private end - end + end) def test_method_in_method_visibility_should_be_public MethodInMethodClass_Setup.call diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index 62b2a7164fb4ff..c8324b9de3a942 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 @@ -253,8 +253,9 @@ def test_const_defined? assert_operator(Math, :const_defined?, "PI") assert_not_operator(Math, :const_defined?, :IP) assert_not_operator(Math, :const_defined?, "IP") + end - # Test invalid symbol name + def test_const_defined_invalid_symbol_name # [Bug #20245] EnvUtil.under_gc_stress do assert_raise(EncodingError) do @@ -832,8 +833,9 @@ def test_method_defined_without_include_super assert User.method_defined?(:user, false) assert !User.method_defined?(:mixin, false) assert Mixin.method_defined?(:mixin, false) + const_name = "FOO#{Ractor.current.object_id}" - User.const_set(:FOO, c = Class.new) + User.const_set(const_name, c = Class.new) c.prepend(User) assert !c.method_defined?(:user, false) @@ -850,7 +852,7 @@ def test_method_defined_without_include_super # cleanup User.class_eval do - remove_const :FOO + remove_const const_name end end @@ -942,15 +944,16 @@ def test_classpath assert_match(/::N$/, m::N.name) assert_match(/\A#::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 @@ -1738,14 +1741,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 @@ -2017,7 +2021,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} @@ -2026,7 +2030,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) @@ -2040,7 +2044,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} @@ -2049,7 +2053,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) @@ -2068,8 +2072,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) @@ -2108,21 +2112,22 @@ 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 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 } @@ -2133,7 +2138,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 @@ -2825,7 +2830,7 @@ 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 list = [] @@ -3242,7 +3247,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_object.rb b/test/ruby/test_object.rb index cccd7359e1108e..b64b4eaf6e5ac1 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) && !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..368826480ca233 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) && !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) && !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) && !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) && !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) && !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) && !main_ractor? @obj.instance_variable_set(:@foo, 42) @obj.freeze id = @obj.object_id @@ -76,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 @@ -117,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 @@ -165,6 +173,7 @@ class TooComplex < Module end def setup + 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_objectspace.rb b/test/ruby/test_objectspace.rb index a479547599a03a..86e3a532bb7bad 100644 --- a/test/ruby/test_objectspace.rb +++ b/test/ruby/test_objectspace.rb @@ -7,10 +7,10 @@ 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 {\ 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 +29,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) diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb index 98e95b98afaf77..36df50ed54b537 100644 --- a/test/ruby/test_parse.rb +++ b/test/ruby/test_parse.rb @@ -1449,11 +1449,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| diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb index 92a3244fc2ae88..9c04e352006390 100644 --- a/test/ruby/test_pattern_matching.rb +++ b/test/ruby/test_pattern_matching.rb @@ -429,7 +429,9 @@ def test_pin_operator_value_pattern true end end + end + def test_pin_operator_value_pattern_ractor_unsafe assert_block do @@TestPatternMatching = /a/ case 'abc' diff --git a/test/ruby/test_primitive.rb b/test/ruby/test_primitive.rb index f1db934000dc23..2116fd2d5a5924 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 @@ -119,7 +119,7 @@ class A5 } end - def test_constatant_cache4 + def test_constant_cache4 assert_equal 8, $test_ruby_primitive_constant_cache4 end @@ -224,19 +224,6 @@ def test_opassign @iv += 2 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 obj = A10.new obj.a = 9 @@ -265,6 +252,22 @@ def test_opassign assert_equal [0, :bar, 4], a end + def test_opassign_ractor_unsafe + # 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 + end + def test_opassign_and_or a = 1 a ||= 2 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..82bc7d0e87c3eb 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -6,14 +6,14 @@ require 'rbconfig' class TestProcess < Test::Unit::TestCase - RUBY = EnvUtil.rubybin + 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 "global side effects" unless main_ractor? File.write("test-script", str) Process.wait spawn(RUBY, "test-script") $? @@ -152,7 +154,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 +297,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 +339,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 +458,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 +514,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 +551,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 +605,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 +623,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 +741,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 +854,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 +901,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 @@ -941,7 +944,7 @@ def test_popen_fork def test_popen_fork_ensure IO.popen("-") do |io| if !io - STDERR.reopen(STDOUT) + $stderr.reopen($stdout) # issue is here raise "fooo" else assert_empty io.read @@ -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 @@ -1684,26 +1687,32 @@ 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) - end exc = assert_raise_kind_of(ArgumentError, SystemCallError) { Process::UID.from_name("\u{4e0d 5b58 5728}") } assert_match(/\u{4e0d 5b58 5728}/, exc.message) if exc.is_a?(ArgumentError) end + + def test_uid_from_name_ractor_unsafe + if u = Etc.getpwuid(Process.uid) + assert_equal(Process.uid, Process::UID.from_name(u.name), u.name) + end + end end 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) - end exc = assert_raise_kind_of(ArgumentError, SystemCallError) do Process::GID.from_name("\u{4e0d 5b58 5728}") # fu son zai ("absent" in Kanji) end assert_match(/\u{4e0d 5b58 5728}/, exc.message) if exc.is_a?(ArgumentError) end + + def test_gid_from_name_ractor_unsafe + if g = Etc.getgrgid(Process.gid) + assert_equal(Process.gid, Process::GID.from_name(g.name), g.name) + end + end end def test_uid_re_exchangeable_p @@ -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 @@ -1894,7 +1903,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 @@ -2130,7 +2139,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 @@ -2601,7 +2610,7 @@ def test_exec_failure_leaves_no_child assert_raise(Errno::ENOENT) do spawn('inexistent_command') end - assert_empty(Process.waitall) + assert_empty(Process.waitall) unless multiple_ractors? end def test__fork diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb index 6ce434790be13b..5fc08c40adb6b0 100644 --- a/test/ruby/test_refinement.rb +++ b/test/ruby/test_refinement.rb @@ -4,6 +4,9 @@ class TestRefinement < Test::Unit::TestCase module Sandbox BINDING = binding + def self.create_binding # TODO: create new one each time for multiple ractors + BINDING + end end class Foo @@ -231,13 +234,14 @@ def test_method_should_use_refinements assert_raise(NameError) { foo.method(:z) } assert_equal("FooExt#z", FooExtClient.method_z(foo).call) assert_raise(NameError) { foo.method(:z) } - assert_equal(8, eval(<<~EOS, Sandbox::BINDING)) + b = Sandbox.create_binding + assert_equal(8, eval(<<~EOS, b)) meth = 2.method(:pow) using MethodIntegerPowEx meth.call(3) EOS - assert_equal(:refine_pow, eval_using(MethodIntegerPowEx, "2.pow(3)")) - assert_equal(:refine_pow, eval_using(MethodIntegerPowEx, "2.method(:pow).(3)")) + assert_equal(:refine_pow, eval_using(MethodIntegerPowEx, "2.pow(3)"), b) + assert_equal(:refine_pow, eval_using(MethodIntegerPowEx, "2.method(:pow).(3)"), b) end module InstanceMethodIntegerPowEx @@ -254,12 +258,13 @@ def test_instance_method_should_use_refinements assert_raise(NameError) { Foo.instance_method(:z) } assert_equal("FooExt#z", FooExtClient.instance_method_z(foo).bind(foo).call) assert_raise(NameError) { Foo.instance_method(:z) } - assert_equal(4, eval(<<~EOS, Sandbox::BINDING)) + b = Sandbox.create_binding + assert_equal(4, eval(<<~EOS, b)) meth = Integer.instance_method(:abs) using InstanceMethodIntegerPowEx meth.bind(-4).call EOS - assert_equal(:refine_abs, eval_using(InstanceMethodIntegerPowEx, "Integer.instance_method(:abs).bind(-4).call")) + assert_equal(:refine_abs, eval_using(InstanceMethodIntegerPowEx, "Integer.instance_method(:abs).bind(-4).call", b)) end def test_no_local_rebinding @@ -542,7 +547,7 @@ def foo def test_main_using_is_private assert_raise(NoMethodError) do - eval("recv = self; recv.using Module.new", Sandbox::BINDING) + eval("recv = self; recv.using Module.new", Sandbox.create_binding) end end @@ -557,7 +562,7 @@ class UsingClass def test_module_using_class assert_raise(TypeError) do - eval("using TestRefinement::UsingClass", Sandbox::BINDING) + eval("using TestRefinement::UsingClass", Sandbox.create_binding) end end @@ -720,12 +725,12 @@ def test_refine_with_proc def test_using_in_module assert_raise(RuntimeError) do - eval(<<-EOF, Sandbox::BINDING) - $main = self + eval(<<-EOF, Sandbox.create_binding) + main = self module M end - module M2 - $main.send(:using, M) + M2 = Module.new do + main.send(:using, M) end EOF end @@ -733,16 +738,16 @@ module M2 def test_using_in_method assert_raise(RuntimeError) do - eval(<<-EOF, Sandbox::BINDING) - $main = self + eval(<<-EOF, Sandbox.create_binding) + main = self module M end - class C - def call_using_in_method - $main.send(:using, M) + c = Class.new do + define_method(:call_using_in_method) do + main.send(:using, M) end end - C.new.call_using_in_method + c.new.call_using_in_method EOF end end @@ -861,8 +866,9 @@ def foo(*args) def test_super_in_block bug7925 = '[ruby-core:52750] [Bug #7925]' + b = Sandbox.create_binding x = eval_using(SuperInBlock::R, - "TestRefinement:: SuperInBlock::C.new.foo(#{bug7925.dump})") + "TestRefinement::SuperInBlock::C.new.foo(#{bug7925.dump})", b) assert_equal([:foo, :ref, bug7925], x, bug7925) end @@ -919,7 +925,7 @@ def test_module_using_in_method def test_module_using_invalid_self assert_raise(RuntimeError) do - eval <<-EOF, Sandbox::BINDING + eval <<-EOF, Sandbox.create_binding module TestRefinement::TestModuleUsingInvalidSelf Module.new.send(:using, TestRefinement::FooExt) end @@ -1766,21 +1772,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 @@ -2601,7 +2607,7 @@ def foo end module B - BAR = "bar" + BAR = "bar".freeze def bar "#{foo}:#{BAR}" @@ -2714,7 +2720,7 @@ def test private - def eval_using(mod, s) - eval("using #{mod}; #{s}", Sandbox::BINDING) + def eval_using(mod, s, b = nil) + eval("using #{mod}; #{s}", b || Sandbox.create_binding) end end diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb index 9f1e03e6499782..b72e39fe3de6ec 100644 --- a/test/ruby/test_regexp.rb +++ b/test/ruby/test_regexp.rb @@ -1630,8 +1630,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 +1644,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 diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb index 4f6c62dc35c174..c8c63bacf2cc46 100644 --- a/test/ruby/test_require.rb +++ b/test/ruby/test_require.rb @@ -161,7 +161,6 @@ def test_require_path_home_2 ENV["RUBYPATH"] = "~" + "/foo" * 1024 ENV["HOME"] = "/foo" assert_in_out_err(%w(-S -w test_ruby_test_require), "", [], pathname_too_long) - ensure env_rubypath ? ENV["RUBYPATH"] = env_rubypath : ENV.delete("RUBYPATH") env_home ? ENV["HOME"] = env_home : ENV.delete("HOME") @@ -217,18 +216,20 @@ def test_require_twice end def assert_syntax_error_backtrace - loaded_features = $LOADED_FEATURES.dup - Dir.mktmpdir do |tmp| - req = File.join(tmp, "test.rb") - File.write(req, ",\n") - e = assert_raise_with_message(SyntaxError, /unexpected/) { - yield req - } - assert_not_nil(bt = e.backtrace, "no backtrace") - assert_not_empty(bt.find_all {|b| b.start_with? __FILE__}, proc {bt.inspect}) + begin + loaded_features = $LOADED_FEATURES.dup + Dir.mktmpdir do |tmp| + req = File.join(tmp, "test.rb") + File.write(req, ",\n") + e = assert_raise_with_message(SyntaxError, /unexpected/) { + yield req + } + assert_not_nil(bt = e.backtrace, "no backtrace") + assert_not_empty(bt.find_all {|b| b.start_with? __FILE__}, proc {bt.inspect}) + end + ensure + $LOADED_FEATURES.replace loaded_features end - ensure - $LOADED_FEATURES.replace loaded_features end def test_require_syntax_error diff --git a/test/ruby/test_rubyvm.rb b/test/ruby/test_rubyvm.rb index 225cb45f33769f..b7a9091b268658 100644 --- a/test/ruby/test_rubyvm.rb +++ b/test/ruby/test_rubyvm.rb @@ -35,39 +35,40 @@ def foo def test_keep_script_lines omit if ParserSupport.prism_enabled? pend if ENV['RUBY_ISEQ_DUMP_DEBUG'] # TODO + begin + prev_conf = RubyVM.keep_script_lines - prev_conf = RubyVM.keep_script_lines + # keep + RubyVM.keep_script_lines = true - # keep - RubyVM.keep_script_lines = true + ast, iseq = *parse_and_compile - ast, iseq = *parse_and_compile + lines = ast.script_lines + assert_equal Array, lines.class - lines = ast.script_lines - assert_equal Array, lines.class - - lines = iseq.script_lines - assert_equal Array, lines.class - iseq.each_child{|child| - assert_equal lines, child.script_lines - } - assert lines.frozen? + lines = iseq.script_lines + assert_equal Array, lines.class + iseq.each_child{|child| + assert_equal lines, child.script_lines + } + assert lines.frozen? - # don't keep - RubyVM.keep_script_lines = false + # don't keep + RubyVM.keep_script_lines = false - ast, iseq = *parse_and_compile + ast, iseq = *parse_and_compile - lines = ast.script_lines - assert_equal nil, lines + lines = ast.script_lines + assert_equal nil, lines - lines = iseq.script_lines - assert_equal nil, lines - iseq.each_child{|child| - assert_equal lines, child.script_lines - } + lines = iseq.script_lines + assert_equal nil, lines + iseq.each_child{|child| + assert_equal lines, child.script_lines + } - ensure - RubyVM.keep_script_lines = prev_conf + ensure + RubyVM.keep_script_lines = prev_conf + end end end diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index ccf24521694484..58a714b5c2225e 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -1563,12 +1563,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 +2103,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 +2154,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_string.rb b/test/ruby/test_string.rb index 1e0f31ba7c540a..8915b16ba670be 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)) @@ -738,11 +738,14 @@ def test_crypt assert_raise(ArgumentError) {S("mypassword".encode(enc)).crypt(S("aa"))} end - @cls == String and - assert_no_memory_leak([], "s = ''; salt_proc = proc{#{(crypt_supports_des_crypt? ? '..' : good_salt).inspect}}", "#{<<~"begin;"}\n#{<<~'end;'}") + end + def test_crypt_no_memory_leak + good_salt = "$2a$04$0WVaz0pV3jzfZ5G5tpmHWu" + @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 } + 1000.times { s.crypt(-salt_proc.call).clear } end; end @@ -3385,11 +3388,12 @@ def test_uplus_minus assert_same(str, +str) assert_not_same(str, -str) - require 'objspace' + end + def test_uplus_minus_ractor_unsafe + require 'objspace' str = "test_uplus_minus_str".freeze assert_includes ObjectSpace.dump(str), '"fstring":true' - assert_predicate(str, :frozen?) assert_not_predicate(+str, :frozen?) assert_predicate(-str, :frozen?) diff --git a/test/ruby/test_stringchar.rb b/test/ruby/test_stringchar.rb index e13beef69c1883..647f74131849ba 100644 --- a/test/ruby/test_stringchar.rb +++ b/test/ruby/test_stringchar.rb @@ -41,10 +41,6 @@ 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") - foo = "abc" assert_equal("abc = abc", "#{foo} = abc") @@ -65,6 +61,12 @@ def test_string assert_equal("", x.sub(/.*\.([^\.]+)$/, '<\&>')) end + def test_string_ractor_unsafe + $foo = "abc" + assert_equal("abc = abc", "#$foo = abc") + assert_equal("abc = abc", "#{$foo} = abc") + end + def test_char # character constants(assumes ASCII) assert_equal(?a, "a"[0]) diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb index 01e5cc68f6eadc..5e1e1271105443 100644 --- a/test/ruby/test_struct.rb +++ b/test/ruby/test_struct.rb @@ -203,11 +203,6 @@ 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) } - klass = @Struct.new(:a, :b) o = klass.new(1, 2) assert_equal("#", o.inspect) @@ -229,6 +224,13 @@ def test_inspect assert_include(methods, :"@a=") end + def test_inspect_ractor_unsafe + @Struct.new("Foo", :a) + o = @Struct::Foo.new(1) + assert_equal("#", o.inspect) + @Struct.instance_eval { remove_const(:Foo) } + end + def test_init_copy klass = @Struct.new(:a) o = klass.new(1) diff --git a/test/ruby/test_super.rb b/test/ruby/test_super.rb index 8e973b0f7f4a08..60ca4a3d078c15 100644 --- a/test/ruby/test_super.rb +++ b/test/ruby/test_super.rb @@ -213,7 +213,9 @@ def obj.reverse overlaid.call([1,2,3]) str.reverse end + end + def test_overlaid_ractor_unsafe assert_nothing_raised('[ruby-core:27230]') do mid=Indexed.new mid.instance_eval(&Overlaid) @@ -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 diff --git a/test/ruby/test_symbol.rb b/test/ruby/test_symbol.rb index c50febf5d1c6fa..13e1f1fcfd3a1c 100644 --- a/test/ruby/test_symbol.rb +++ b/test/ruby/test_symbol.rb @@ -492,10 +492,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 94a2e03940bf5b..d8b93d8cffe839 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -220,7 +220,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 @@ -314,8 +318,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") - end - assert_equal(42, o.m(k: 42), '[ruby-core:45744]') + assert_equal(42, o.m(k: 42), '[ruby-core:45744]') + end unless multiple_ractors? bug7922 = '[ruby-core:52744] [Bug #7922]' def o.bug7922(**) end assert_nothing_raised(ArgumentError, bug7922) {o.bug7922(foo: 42)} @@ -684,14 +688,17 @@ def test_duplicated_kw end def test_duplicated_rest_kw - assert_syntax_error("def foo(*a, a: 1) end", /duplicated argument name/) - assert_nothing_raised {def foo(*_, _: 1) end} + unless multiple_ractors? + assert_syntax_error("def foo(*a, a: 1) end", /duplicated argument name/) + assert_nothing_raised {def foo(*_, _: 1) end} + defined_foo = true + end (obj = Object.new).instance_eval("def foo(*_, x: 42, _: 1) x end") assert_equal(42, obj.foo(42)) assert_equal(42, obj.foo(2, _: 0)) assert_equal(2, obj.foo(x: 2, _: 0)) ensure - self.class.remove_method(:foo) + self.class.remove_method(:foo) if defined_foo end def test_duplicated_opt_kw @@ -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 diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb index 01a1e51025181c..9de43a778c0258 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 @@ -255,12 +257,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 +273,7 @@ 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 t = Thread.new { sleep } begin assert_nothing_raised(Timeout::Error) do @@ -280,7 +284,7 @@ def test_join_argument_conversion ensure t.kill end - end + end)) end def test_kill_main_thread @@ -1239,7 +1243,7 @@ def test_fork_in_thread f = nil th = Thread.start do unless f = IO.popen("-") - STDERR.reopen(STDOUT) + $stderr.reopen($stdout) exit end Process.wait2(f.pid) diff --git a/test/ruby/test_thread_cv.rb b/test/ruby/test_thread_cv.rb index 81201f134f0c71..2d9a2ac2ede582 100644 --- a/test/ruby/test_thread_cv.rb +++ b/test/ruby/test_thread_cv.rb @@ -84,8 +84,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 diff --git a/test/ruby/test_time.rb b/test/ruby/test_time.rb index 333edb80218a64..7e7a2660e5fe00 100644 --- a/test/ruby/test_time.rb +++ b/test/ruby/test_time.rb @@ -440,7 +440,9 @@ def test_marshal_zone t = Time.utc(2013, 2, 24) assert_equal('UTC', t.zone) assert_equal('UTC', Marshal.load(Marshal.dump(t)).zone) + end + def test_marshal_zone_ractor_unsafe in_timezone('JST-9') do t = Time.local(2013, 2, 24) assert_equal('JST', Time.local(2013, 2, 24).zone) @@ -472,7 +474,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" diff --git a/test/ruby/test_time_tz.rb b/test/ruby/test_time_tz.rb index f66cd9bec2eedc..bbfbedeeacfdcd 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 @@ -88,7 +90,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 +98,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 +381,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 +406,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 +419,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 +572,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 + if will_run_in_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 +748,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 +770,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_variable.rb b/test/ruby/test_variable.rb index 68434e0b6c479b..14ed7fcf76ccc0 100644 --- a/test/ruby/test_variable.rb +++ b/test/ruby/test_variable.rb @@ -487,9 +487,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) && !main_ractor? obj.instance_variable_set("@var#{i}", i) end 1000.times do |i| + next if obj.is_a?(Module) && !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 ce9dfca3900e0e..ea8d8f49aa4586 100644 --- a/test/ruby/test_yjit.rb +++ b/test/ruby/test_yjit.rb @@ -1794,7 +1794,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_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..f8aa334e7bc0f0 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__}" if respond_to?(:main_ractor?) && !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 respond_to?(:main_ractor?) && !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 @@ -270,6 +274,7 @@ def assert_valid_syntax(code, *args, **opt) end def assert_normal_exit(testsrc, message = '', child_env: nil, **opt) + pend "#{__method__}" if respond_to?(:main_ractor?) && !main_ractor? assert_valid_syntax(testsrc, caller_locations(1, 1)[0]) if child_env child_env = [child_env] @@ -281,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 respond_to?(:main_ractor?) && !main_ractor? out, _, status = EnvUtil.invoke_ruby(args, test_stdin, true, :merge_to_stdout, **opt) desc = FailDesc[status, message, out] assert(!status.signaled?, desc) @@ -288,7 +294,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 +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 respond_to?(:main_ractor?) && !main_ractor? unless file and line loc, = caller_locations(1,1) file ||= loc.path @@ -389,7 +396,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 @@ -500,9 +507,19 @@ def assert_raise_with_message(exception, expected, msg = nil, &block) assert_respond_to(expected, :===) assert = :assert_match end - - ex = assert_raise(exception, msg || proc {"Exception(#{exception}) with message matches to #{expected.inspect}"}) do - yield + ex = m = nil + if respond_to?(:multiple_ractors?) && multiple_ractors? + ex = assert_raise(exception, msg || proc {"Exception(#{exception}) with message matches to #{expected.inspect}"}) do + yield + end + m = ex.message + else + EnvUtil.with_default_internal(of: expected) do + ex = assert_raise(exception, msg || proc {"Exception(#{exception}) with message matches to #{expected.inspect}"}) do + yield + end + m = ex.message + end end m = ex.message msg = message(msg, "") {"Expected Exception(#{exception}) was raised, but the message doesn't match"} @@ -554,7 +571,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]) @@ -673,11 +690,17 @@ def assert_pattern_list(pattern_list, actual, message=nil) def assert_warning(pat, msg = nil) result = nil - stderr = EnvUtil.with_default_internal(of: pat) { - EnvUtil.verbose_warning { + if respond_to?(:multiple_ractors?) && 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 @@ -699,27 +722,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 +873,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 respond_to?(:main_ractor?) && !main_ractor? # Timeout testing generally doesn't work when RJIT compilation happens. rjit_enabled = defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? @@ -967,11 +996,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 = (!respond_to?(:main_ractor?)) || 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 - version_match? ver, @macos + if macos && is_main_ractor + @macos = macos + elsif is_main_ractor + macos = @macos + end + 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..6377e50ba12018 100644 --- a/tool/lib/envutil.rb +++ b/tool/lib/envutil.rb @@ -36,12 +36,14 @@ 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 + MULTIPLE_RACTORS = ENV["RUBY_TESTS_WITH_RACTORS"].to_i > 1 class << self attr_accessor :timeout_scale @@ -54,7 +56,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] @@ -293,7 +295,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 @@ -329,50 +333,64 @@ def suppress_warning module_function :suppress_warning def under_gc_stress(stress = true) - stress, GC.stress = GC.stress, stress - yield - ensure - GC.stress = stress + raise Test::Unit::PendedError.new("not ractor safe (#{__method__})") if MULTIPLE_RACTORS + begin + stress, GC.stress = GC.stress, stress + yield + ensure + GC.stress = stress + end end module_function :under_gc_stress 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 + begin + if GC.respond_to?(:auto_compact) + auto_compact = GC.auto_compact + GC.auto_compact = val + end - if GC.respond_to?(:auto_compact) - auto_compact = GC.auto_compact - GC.auto_compact = val + under_gc_stress(&block) + ensure + GC.auto_compact = auto_compact if GC.respond_to?(:auto_compact) end - - under_gc_stress(&block) - ensure - GC.auto_compact = auto_compact if GC.respond_to?(:auto_compact) end module_function :under_gc_compact_stress def without_gc - prev_disabled = GC.disable - yield - ensure - GC.enable unless prev_disabled + raise Test::Unit::PendedError.new("not ractor safe (#{__method__})") if MULTIPLE_RACTORS + begin + prev_disabled = GC.disable + yield + ensure + GC.enable unless prev_disabled + end end module_function :without_gc def with_default_external(enc = nil, of: nil) + raise Test::Unit::PendedError.new("not ractor safe (#{__method__})") if MULTIPLE_RACTORS enc = of.encoding if defined?(of.encoding) suppress_warning { Encoding.default_external = enc } - yield - ensure - suppress_warning { Encoding.default_external = EnvUtil.original_external_encoding } + begin + yield + ensure + suppress_warning { Encoding.default_external = EnvUtil.original_external_encoding } + end end module_function :with_default_external def with_default_internal(enc = nil, of: nil) + raise Test::Unit::PendedError.new("not ractor safe (#{__method__})") if MULTIPLE_RACTORS enc = of.encoding if defined?(of.encoding) suppress_warning { Encoding.default_internal = enc } - yield - ensure - suppress_warning { Encoding.default_internal = EnvUtil.original_internal_encoding } + begin + yield + ensure + suppress_warning { Encoding.default_internal = EnvUtil.original_internal_encoding } + end end module_function :with_default_internal @@ -401,8 +419,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) @@ -481,10 +499,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 diff --git a/tool/lib/leakchecker.rb b/tool/lib/leakchecker.rb index 69df9a64b87192..975a88b1d6529b 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..5bb31c69f33d8e 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 if ractors_enabled? orig_args -= args args = @init_hook.call(args, options) if @init_hook + @init_hook = nil if ractors_enabled? 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 = [] @@ -1505,6 +1511,9 @@ class Runner # :nodoc: all attr_accessor :info_signal + TESTCASE_COPYABLE_IVARS = Ractor.make_shareable({:@_assertions => true, :@__passed__ => true, :@__name__ => true}) + RUNNER_COPYABLE_IVARS = Ractor.make_shareable({:@report => true, :@failures => true, :@errors => true, :@skips => true, :@assertion_count => true, :@test_count => true}) + ## # Lazy accessor for options. @@ -1513,10 +1522,19 @@ def options end @@installed_at_exit ||= false - @@out = $stdout @@after_tests = [] @@current_repeat_count = 0 + class << self + def ractors_enabled? + ENV["RUBY_TESTS_WITH_RACTORS"].to_i > 0 + end + end + def ractors_enabled? + self.class.ractors_enabled? + end + private :ractors_enabled? + ## # A simple hook allowing you to run a block of code after _all_ of # the tests are done. Eg: @@ -1527,20 +1545,50 @@ def self.after_tests &block @@after_tests << block end - ## - # Returns the stream to use for output. + if ractors_enabled? + OUT = 1 + def self.output + case OUT + when 1 + $stdout + when 2 + $stderr + else + raise "Not supported" + end + end + + ## + # Sets Test::Unit::Runner to write output to +stream+. $stdout is the default + # output. NOTE: if not $stdout or $stderr, may not be ractor safe! - def self.output - @@out + def self.output= stream + saved_verbose = $VERBOSE + begin + $VERBOSE = nil + fd_num = stream.to_i + if [1,2].include?(fd_num) + const_set(:OUT, fd_num) + else + raise ArgumentError, "Must give stdout or stderr (fd 1 or 2), got fd=#{fd_num}" + end + ensure + $VERBOSE = saved_verbose + end + end + else + @@out = $stdout + def self.output + @@out + end + def self.output=(out) + @@out = out + end end ## - # Sets Test::Unit::Runner to write output to +stream+. $stdout is the default - # output + # Returns the stream to use for output. - def self.output= stream - @@out = stream - end ## # Tells Test::Unit::Runner to delegate to +runner+, an instance of a @@ -1667,9 +1715,15 @@ def _run_suite suite, type trace = true end + run_tests_inside_ractors_num = ENV["RUBY_TESTS_WITH_RACTORS"].to_i + if run_tests_inside_ractors_num > 0 + 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 + inst = suite.new method.to_s _start_method(inst) inst._assertions = 0 @@ -1680,13 +1734,68 @@ def _run_suite suite, type if trace ObjectSpace.trace_object_allocations {inst.run self} else - inst.run self + 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 + Ractor.new(inst, self) do |instance, runner| + res = instance.run runner + testcase_copyable_ivars = TESTCASE_COPYABLE_IVARS + runner_copyable_ivars = RUNNER_COPYABLE_IVARS + 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 + end + ractor_results = [] + while rs.any? + r, obj = Ractor.select(*rs) + inst, runner, res = *obj + ractor_results << [res, inst, 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, inst, runner)| + res << res0 + _merge_results_from_ractor(inst, runner) + end + inst._assertions = self.assertion_count - old_assertion_count + res + else + inst.run self + end end + tests_run += 1 + print "%.2f s = " % (Time.now - start_time) if @verbose print result puts if @verbose - $stdout.flush leakchecker.check("#{inst.class}\##{inst.__name__}") @@ -1697,6 +1806,18 @@ 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(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) end def _end_method(inst) @@ -1734,8 +1855,10 @@ def initialize # :nodoc: @report = [] @errors = @failures = @skips = 0 @verbose = false - @mutex = Thread::Mutex.new - @info_signal = Signal.list['INFO'] + unless ractors_enabled? + @mutex = Thread::Mutex.new + @info_signal = Signal.list['INFO'] + end @repeat_count = nil end @@ -1763,6 +1886,11 @@ def _run args = [] self.options.merge! args puts "Run options: #{help}" + 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} " \ + "ractor#{ractors_num > 1 ? 's' : ''})" + end 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..7bcf272b993c6d 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 respond_to?(:main_ractor?) && !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..bb7acefcfa4d1e 100644 --- a/tool/lib/test/unit/testcase.rb +++ b/tool/lib/test/unit/testcase.rb @@ -50,6 +50,23 @@ 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 + + def multiple_ractors? + ENV["RUBY_TESTS_WITH_RACTORS"].to_i > 1 + end + + # In order to guard generating methods dynamically that will run inside another ractor + def will_run_in_main_ractor? + ENV["RUBY_TESTS_WITH_RACTORS"].to_i == 0 + end end ## @@ -141,7 +158,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,22 +215,24 @@ 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? + $@.delete(RUN_TEST_TRACE) if $@ + $0 = progname + end end 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 ## 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 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