diff options
Diffstat (limited to 'test/ruby/test_class.rb')
| -rw-r--r-- | test/ruby/test_class.rb | 164 |
1 files changed, 152 insertions, 12 deletions
diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index 456362ef21..82199876ec 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -259,6 +259,46 @@ class TestClass < Test::Unit::TestCase assert_raise(TypeError) { BasicObject.dup } end + def test_class_hierarchy_inside_initialize_dup_bug_21538 + ancestors = sc_ancestors = nil + b = Class.new + b.define_singleton_method(:initialize_dup) do |x| + ancestors = self.ancestors + sc_ancestors = singleton_class.ancestors + super(x) + end + + a = Class.new(b) + + c = a.dup + + expected_ancestors = [c, b, *Object.ancestors] + expected_sc_ancestors = [c.singleton_class, b.singleton_class, *Object.singleton_class.ancestors] + assert_equal expected_ancestors, ancestors + assert_equal expected_sc_ancestors, sc_ancestors + assert_equal expected_ancestors, c.ancestors + assert_equal expected_sc_ancestors, c.singleton_class.ancestors + end + + def test_class_hierarchy_inside_initialize_clone_bug_21538 + ancestors = sc_ancestors = nil + a = Class.new + a.define_singleton_method(:initialize_clone) do |x| + ancestors = self.ancestors + sc_ancestors = singleton_class.ancestors + super(x) + end + + c = a.clone + + expected_ancestors = [c, *Object.ancestors] + expected_sc_ancestors = [c.singleton_class, *Object.singleton_class.ancestors] + assert_equal expected_ancestors, ancestors + assert_equal expected_sc_ancestors, sc_ancestors + assert_equal expected_ancestors, c.ancestors + assert_equal expected_sc_ancestors, c.singleton_class.ancestors + end + def test_singleton_class assert_raise(TypeError) { 1.extend(Module.new) } assert_raise(TypeError) { 1.0.extend(Module.new) } @@ -283,12 +323,8 @@ class TestClass < Test::Unit::TestCase assert_raise(TypeError, bug6863) { Class.new(Class.allocate) } allocator = Class.instance_method(:allocate) - assert_raise_with_message(TypeError, /prohibited/) { - allocator.bind(Rational).call - } - assert_raise_with_message(TypeError, /prohibited/) { - allocator.bind_call(Rational) - } + assert_nothing_raised { allocator.bind(Rational).call } + assert_nothing_raised { allocator.bind_call(Rational) } end def test_nonascii_name @@ -399,21 +435,24 @@ class TestClass < Test::Unit::TestCase end class CloneTest + TEST = :C0 def foo; TEST; end end CloneTest1 = CloneTest.clone CloneTest2 = CloneTest.clone class CloneTest1 + remove_const :TEST TEST = :C1 end class CloneTest2 + remove_const :TEST TEST = :C2 end def test_constant_access_from_method_in_cloned_class - assert_equal :C1, CloneTest1.new.foo, '[Bug #15877]' - assert_equal :C2, CloneTest2.new.foo, '[Bug #15877]' + assert_equal :C0, CloneTest1.new.foo, 'originally [Bug #15877], but behaviour changed' + assert_equal :C0, CloneTest2.new.foo, 'originally [Bug #15877], but behaviour changed' end def test_invalid_superclass @@ -565,7 +604,7 @@ class TestClass < Test::Unit::TestCase obj = Object.new c = obj.singleton_class obj.freeze - assert_raise_with_message(FrozenError, /frozen object/) { + assert_raise_with_message(FrozenError, /frozen Object/) { c.class_eval {def f; end} } end @@ -697,12 +736,37 @@ class TestClass < Test::Unit::TestCase } end + def test_dynamic_module_cpath_constant_namespace # [Bug #20948] + assert_separately([], <<~'RUBY') + module M1 + module Foo + X = 1 + end + end + + module M2 + module Foo + X = 2 + end + end + + results = [M1, M2].map do + module it::Foo + X + end + end + assert_equal([1, 2], results) + RUBY + end + def test_namescope_error_message m = Module.new o = m.module_eval "class A\u{3042}; self; end.new" - assert_raise_with_message(TypeError, /A\u{3042}/) { - o::Foo - } + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(TypeError, /A\u{3042}/) { + o::Foo + } + end end def test_redefinition_mismatch @@ -841,4 +905,80 @@ CODE klass.define_method(:bar) {} assert_equal klass, klass.remove_method(:bar), '[Bug #19164]' end + + def test_method_table_assignment_just_after_class_init + assert_normal_exit "#{<<~"begin;"}\n#{<<~'end;'}", 'm_tbl assignment should be done only when Class object is not promoted' + begin; + GC.stress = true + class C; end + end; + end + + def test_define_singleton_initialize + assert_normal_exit "#{<<~"begin;"}\n#{<<~'end;'}" + begin; + class C + def self.initialize + end + end + end; + end + + def test_singleton_cc_invalidation + assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}") + begin; + class T + def hi + "hi" + end + end + + t = T.new + t.singleton_class + + def hello(t) + t.hi + end + + 5.times do + hello(t) # populate inline cache on `t.singleton_class`. + end + + class T + remove_method :hi # invalidate `t.singleton_class` ccs for `hi` + end + + assert_raise NoMethodError do + hello(t) + end + end; + end + + def test_safe_multi_ractor_subclasses_list_mutation + assert_ractor "#{<<~"begin;"}\n#{<<~'end;'}", signal: :SEGV + begin; + 4.times.map do + Ractor.new do + 20_000.times do + Object.new.singleton_class + end + end + end.each(&:join) + end; + end + + def test_safe_multi_ractor_singleton_class_access + assert_ractor "#{<<~"begin;"}\n#{<<~'end;'}" + begin; + class A; end + 4.times.map do + Ractor.new do + a = A + 100.times do + a = a.singleton_class + end + end + end.each(&:join) + end; + end end |
