summaryrefslogtreecommitdiff
path: root/test/ruby/test_class.rb
diff options
context:
space:
mode:
Diffstat (limited to 'test/ruby/test_class.rb')
-rw-r--r--test/ruby/test_class.rb358
1 files changed, 338 insertions, 20 deletions
diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb
index 93278a918f..8f12e06685 100644
--- a/test/ruby/test_class.rb
+++ b/test/ruby/test_class.rb
@@ -96,6 +96,13 @@ class TestClass < Test::Unit::TestCase
def test_superclass_of_basicobject
assert_equal(nil, BasicObject.superclass)
+
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ module Mod end
+ BasicObject.include(Mod)
+ assert_equal(nil, BasicObject.superclass)
+ end;
end
def test_module_function
@@ -131,6 +138,48 @@ class TestClass < Test::Unit::TestCase
[:module_function, :extend_object, :append_features, :prepend_features])
end
+ def test_visibility_inside_method
+ assert_warn(/calling private without arguments inside a method may not have the intended effect/, '[ruby-core:79751]') do
+ Class.new do
+ def self.foo
+ private
+ end
+ foo
+ end
+ end
+
+ assert_warn(/calling protected without arguments inside a method may not have the intended effect/, '[ruby-core:79751]') do
+ Class.new do
+ def self.foo
+ protected
+ end
+ foo
+ end
+ end
+
+ assert_warn(/calling public without arguments inside a method may not have the intended effect/, '[ruby-core:79751]') do
+ Class.new do
+ def self.foo
+ public
+ end
+ foo
+ end
+ end
+
+ assert_warn(/calling private without arguments inside a method may not have the intended effect/, '[ruby-core:79751]') do
+ Class.new do
+ class << self
+ alias priv private
+ end
+
+ def self.foo
+ priv
+ end
+ foo
+ end
+ end
+ end
+
def test_method_redefinition
feature2155 = '[ruby-dev:39400]'
@@ -210,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) }
@@ -232,6 +321,10 @@ class TestClass < Test::Unit::TestCase
assert_raise(TypeError) { Class.allocate.superclass }
bug6863 = '[ruby-core:47148]'
assert_raise(TypeError, bug6863) { Class.new(Class.allocate) }
+
+ allocator = Class.instance_method(:allocate)
+ assert_nothing_raised { allocator.bind(Rational).call }
+ assert_nothing_raised { allocator.bind_call(Rational) }
end
def test_nonascii_name
@@ -259,10 +352,11 @@ class TestClass < Test::Unit::TestCase
def test_invalid_return_from_class_definition
assert_syntax_error("class C; return; end", /Invalid return/)
+ assert_syntax_error("class << Object; return; end", /Invalid return/)
end
def test_invalid_yield_from_class_definition
- assert_raise(LocalJumpError) {
+ assert_raise(SyntaxError) {
EnvUtil.suppress_warning {eval("class C; yield; end")}
}
end
@@ -305,6 +399,17 @@ class TestClass < Test::Unit::TestCase
assert_equal(42, PrivateClass.new.foo)
end
+ def test_private_const_access
+ assert_raise_with_message NameError, /uninitialized/ do
+ begin
+ eval('class ::TestClass::PrivateClass; end')
+ rescue NameError
+ end
+
+ Object.const_get "NOT_AVAILABLE_CONST_NAME_#{__LINE__}"
+ end
+ end
+
StrClone = String.clone
Class.new(StrClone)
@@ -329,18 +434,22 @@ class TestClass < Test::Unit::TestCase
end;
end
- module M
- C = 1
+ class CloneTest
+ def foo; TEST; end
+ end
- def self.m
- C
- end
+ CloneTest1 = CloneTest.clone
+ CloneTest2 = CloneTest.clone
+ class CloneTest1
+ TEST = :C1
+ end
+ class CloneTest2
+ TEST = :C2
end
- def test_constant_access_from_method_in_cloned_module # [ruby-core:47834]
- m = M.dup
- assert_equal 1, m::C
- assert_equal 1, m.m
+ 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]'
end
def test_invalid_superclass
@@ -429,6 +538,53 @@ class TestClass < Test::Unit::TestCase
assert_equal(:foo, d.foo)
end
+ def test_clone_singleton_class_exists
+ klass = Class.new do
+ def self.bar; :bar; end
+ end
+
+ o = klass.new
+ o.singleton_class
+ clone = o.clone
+
+ assert_empty(o.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.instance_methods(false))
+ assert_empty(o.singleton_class.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.singleton_class.instance_methods(false))
+ end
+
+ def test_clone_when_singleton_class_of_singleton_class_exists
+ klass = Class.new do
+ def self.bar; :bar; end
+ end
+
+ o = klass.new
+ o.singleton_class.singleton_class
+ clone = o.clone
+
+ assert_empty(o.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.instance_methods(false))
+ assert_empty(o.singleton_class.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.singleton_class.instance_methods(false))
+ end
+
+ def test_clone_when_method_exists_on_singleton_class_of_singleton_class
+ klass = Class.new do
+ def self.bar; :bar; end
+ end
+
+ o = klass.new
+ o.singleton_class.singleton_class.define_method(:s2_method) { :s2 }
+ clone = o.clone
+
+ assert_empty(o.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.instance_methods(false))
+ assert_equal(:s2, o.singleton_class.s2_method)
+ assert_equal(:s2, clone.singleton_class.s2_method)
+ assert_equal([:s2_method], o.singleton_class.singleton_class.instance_methods(false))
+ assert_equal([:s2_method], clone.singleton_class.singleton_class.instance_methods(false))
+ end
+
def test_singleton_class_p
feature7609 = '[ruby-core:51087] [Feature #7609]'
assert_predicate(self.singleton_class, :singleton_class?, feature7609)
@@ -445,7 +601,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
@@ -580,28 +736,36 @@ class TestClass < Test::Unit::TestCase
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
m = Module.new
- m.module_eval "A = 1"
- assert_raise_with_message(TypeError, /is not a class/) {
+ m.module_eval "A = 1", __FILE__, line = __LINE__
+ e = assert_raise_with_message(TypeError, /is not a class/) {
m.module_eval "class A; end"
}
+ assert_include(e.message, "#{__FILE__}:#{line}: previous definition")
n = "M\u{1f5ff}"
- m.module_eval "#{n} = 42"
- assert_raise_with_message(TypeError, "#{n} is not a class") {
+ m.module_eval "#{n} = 42", __FILE__, line = __LINE__
+ e = assert_raise_with_message(TypeError, /#{n} is not a class/) {
m.module_eval "class #{n}; end"
}
+ assert_include(e.message, "#{__FILE__}:#{line}: previous definition")
assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}")
begin;
- Date = (class C\u{1f5ff}; self; end).new
+ module Bug
+ module Class
+ TestClassDefinedInC = (class C\u{1f5ff}; self; end).new
+ end
+ end
assert_raise_with_message(TypeError, /C\u{1f5ff}/) {
- require 'date'
+ require '-test-/class'
}
end;
end
@@ -627,4 +791,158 @@ class TestClass < Test::Unit::TestCase
end;
end
+
+ def test_assign_frozen_class_to_const
+ c = Class.new.freeze
+ assert_same(c, Module.new.module_eval("self::Foo = c"))
+ c = Class.new.freeze
+ assert_same(c, Module.new.const_set(:Foo, c))
+ end
+
+ def test_subclasses
+ c = Class.new
+ sc = Class.new(c)
+ ssc = Class.new(sc)
+ [c, sc, ssc].each do |k|
+ k.include Module.new
+ k.new.define_singleton_method(:force_singleton_class){}
+ end
+ assert_equal([sc], c.subclasses)
+ assert_equal([ssc], sc.subclasses)
+ assert_equal([], ssc.subclasses)
+
+ object_subclasses = Object.subclasses
+ assert_include(object_subclasses, c)
+ assert_not_include(object_subclasses, sc)
+ assert_not_include(object_subclasses, ssc)
+ object_subclasses.each do |subclass|
+ assert_equal Object, subclass.superclass, "Expected #{subclass}.superclass to be Object"
+ end
+ end
+
+ def test_attached_object
+ c = Class.new
+ sc = c.singleton_class
+ obj = c.new
+
+ assert_equal(obj, obj.singleton_class.attached_object)
+ assert_equal(c, sc.attached_object)
+
+ assert_raise_with_message(TypeError, /is not a singleton class/) do
+ c.attached_object
+ end
+
+ assert_raise_with_message(TypeError, /'NilClass' is not a singleton class/) do
+ nil.singleton_class.attached_object
+ end
+
+ assert_raise_with_message(TypeError, /'FalseClass' is not a singleton class/) do
+ false.singleton_class.attached_object
+ end
+
+ assert_raise_with_message(TypeError, /'TrueClass' is not a singleton class/) do
+ true.singleton_class.attached_object
+ end
+ end
+
+ def test_subclass_gc
+ c = Class.new
+ 10_000.times do
+ cc = Class.new(c)
+ 100.times { Class.new(cc) }
+ end
+ assert(c.subclasses.size <= 10_000)
+ end
+
+ def test_subclass_gc_stress
+ 10000.times do
+ c = Class.new
+ 100.times { Class.new(c) }
+ assert(c.subclasses.size <= 100)
+ end
+ end
+
+ def test_classext_memory_leak
+ assert_no_memory_leak([], <<-PREP, <<-CODE, rss: true)
+code = proc { Class.new }
+1_000.times(&code)
+PREP
+3_000_000.times(&code)
+CODE
+ end
+
+ def test_instance_freeze_dont_freeze_the_class_bug_19164
+ klass = Class.new
+ klass.prepend(Module.new)
+
+ klass.new.freeze
+ 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_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;'}"
+ 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