diff options
Diffstat (limited to 'test/ruby/test_module.rb')
| -rw-r--r-- | test/ruby/test_module.rb | 422 |
1 files changed, 368 insertions, 54 deletions
diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index 2d7bdb47fc..9ed6c1e321 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -9,18 +9,18 @@ class TestModule < Test::Unit::TestCase yield end - def assert_method_defined?(klass, mid, message="") + def assert_method_defined?(klass, (mid, *args), message="") message = build_message(message, "#{klass}\##{mid} expected to be defined.") _wrap_assertion do - klass.method_defined?(mid) or + klass.method_defined?(mid, *args) or raise Test::Unit::AssertionFailedError, message, caller(3) end end - def assert_method_not_defined?(klass, mid, message="") + def assert_method_not_defined?(klass, (mid, *args), message="") message = build_message(message, "#{klass}\##{mid} expected to not be defined.") _wrap_assertion do - klass.method_defined?(mid) and + klass.method_defined?(mid, *args) and raise Test::Unit::AssertionFailedError, message, caller(3) end end @@ -253,6 +253,14 @@ class TestModule < Test::Unit::TestCase assert_operator(Math, :const_defined?, "PI") assert_not_operator(Math, :const_defined?, :IP) assert_not_operator(Math, :const_defined?, "IP") + + # Test invalid symbol name + # [Bug #20245] + EnvUtil.under_gc_stress do + assert_raise(EncodingError) do + Math.const_defined?("\xC3") + end + end end def each_bad_constants(m, &b) @@ -404,19 +412,7 @@ class TestModule < Test::Unit::TestCase assert_equal([:MIXIN, :USER], User.constants.sort) end - def test_self_initialize_copy - bug9535 = '[ruby-dev:47989] [Bug #9535]' - m = Module.new do - def foo - :ok - end - initialize_copy(self) - end - assert_equal(:ok, Object.new.extend(m).foo, bug9535) - end - def test_initialize_copy_empty - bug9813 = '[ruby-dev:48182] [Bug #9813]' m = Module.new do def x end @@ -426,12 +422,56 @@ class TestModule < Test::Unit::TestCase assert_equal([:x], m.instance_methods) assert_equal([:@x], m.instance_variables) assert_equal([:X], m.constants) - m.module_eval do - initialize_copy(Module.new) + + m = Class.new(Module) do + def initialize_copy(other) + # leave uninitialized + end + end.new.dup + c = Class.new + assert_operator(c.include(m), :<, m) + cp = Module.instance_method(:initialize_copy) + assert_raise(TypeError) do + cp.bind_call(m, Module.new) end - assert_empty(m.instance_methods, bug9813) - assert_empty(m.instance_variables, bug9813) - assert_empty(m.constants, bug9813) + end + + class Bug18185 < Module + module InstanceMethods + end + attr_reader :ancestor_list + def initialize + @ancestor_list = ancestors + include InstanceMethods + end + class Foo + attr_reader :key + def initialize(key:) + @key = key + end + end + end + + def test_module_subclass_initialize + mod = Bug18185.new + c = Class.new(Bug18185::Foo) do + include mod + end + anc = c.ancestors + assert_include(anc, mod) + assert_equal(1, anc.count(BasicObject), ->{anc.inspect}) + b = c.new(key: 1) + assert_equal(1, b.key) + assert_not_include(mod.ancestor_list, BasicObject) + end + + def test_module_collected_extended_object + m1 = labeled_module("m1") + m2 = labeled_module("m2") + Object.new.extend(m1) + GC.start + m1.include(m2) + assert_equal([m1, m2], m1.ancestors) end def test_dup @@ -478,6 +518,16 @@ class TestModule < Test::Unit::TestCase assert_raise(ArgumentError) { Module.new { include } } end + def test_include_before_initialize + m = Class.new(Module) do + def initialize(...) + include Enumerable + super + end + end.new + assert_operator(m, :<, Enumerable) + end + def test_prepend_self m = Module.new assert_equal([m], m.ancestors) @@ -512,8 +562,28 @@ class TestModule < Test::Unit::TestCase assert_equal(2, a2.b) end + def test_ancestry_of_duped_classes + m = Module.new + sc = Class.new + a = Class.new(sc) do + def b; 2 end + prepend m + end + + a2 = a.dup.new + + assert_kind_of Object, a2 + assert_kind_of sc, a2 + refute_kind_of a, a2 + assert_kind_of m, a2 + + assert_kind_of Class, a2.class + assert_kind_of sc.singleton_class, a2.class + assert_same sc, a2.class.superclass + end + def test_gc_prepend_chain - assert_separately([], <<-EOS) + assert_ruby_status([], <<-EOS) 10000.times { |i| m1 = Module.new do def foo; end @@ -696,6 +766,37 @@ class TestModule < Test::Unit::TestCase assert_equal([:m1, :m0, :m, :sc, :m1, :m0, :c], sc.new.m) end + def test_include_into_module_after_prepend_bug_20871 + bar = Module.new{def bar; 'bar'; end} + foo = Module.new{def foo; 'foo'; end} + m = Module.new + c = Class.new{include m} + m.prepend bar + Class.new{include m} + m.include foo + assert_include c.ancestors, foo + assert_equal "foo", c.new.foo + end + + def test_protected_include_into_included_module + m1 = Module.new do + def other_foo(other) + other.foo + end + + protected + def foo + :ok + end + end + m2 = Module.new + c1 = Class.new { include m2 } + c2 = Class.new { include m2 } + m2.include(m1) + + assert_equal :ok, c1.new.other_foo(c2.new) + end + def test_instance_methods assert_equal([:user, :user2], User.instance_methods(false).sort) assert_equal([:user, :user2, :mixin].sort, User.instance_methods(true).sort) @@ -712,40 +813,40 @@ class TestModule < Test::Unit::TestCase def test_method_defined? [User, Class.new{include User}, Class.new{prepend User}].each do |klass| [[], [true]].each do |args| - assert !klass.method_defined?(:wombat, *args) - assert klass.method_defined?(:mixin, *args) - assert klass.method_defined?(:user, *args) - assert klass.method_defined?(:user2, *args) - assert !klass.method_defined?(:user3, *args) + assert_method_not_defined?(klass, [:wombat, *args]) + assert_method_defined?(klass, [:mixin, *args]) + assert_method_defined?(klass, [:user, *args]) + assert_method_defined?(klass, [:user2, *args]) + assert_method_not_defined?(klass, [:user3, *args]) - assert !klass.method_defined?("wombat", *args) - assert klass.method_defined?("mixin", *args) - assert klass.method_defined?("user", *args) - assert klass.method_defined?("user2", *args) - assert !klass.method_defined?("user3", *args) + assert_method_not_defined?(klass, ["wombat", *args]) + assert_method_defined?(klass, ["mixin", *args]) + assert_method_defined?(klass, ["user", *args]) + assert_method_defined?(klass, ["user2", *args]) + assert_method_not_defined?(klass, ["user3", *args]) end end end 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) + assert_method_defined?(User, [:user, false]) + assert_method_not_defined?(User, [:mixin, false]) + assert_method_defined?(Mixin, [:mixin, false]) User.const_set(:FOO, c = Class.new) c.prepend(User) - assert !c.method_defined?(:user, false) + assert_method_not_defined?(c, [:user, false]) c.define_method(:user){} - assert c.method_defined?(:user, false) + assert_method_defined?(c, [:user, false]) - assert !c.method_defined?(:mixin, false) + assert_method_not_defined?(c, [:mixin, false]) c.define_method(:mixin){} - assert c.method_defined?(:mixin, false) + assert_method_defined?(c, [:mixin, false]) - assert !c.method_defined?(:userx, false) + assert_method_not_defined?(c, [:userx, false]) c.define_method(:userx){} - assert c.method_defined?(:userx, false) + assert_method_defined?(c, [:userx, false]) # cleanup User.class_eval do @@ -895,6 +996,15 @@ class TestModule < Test::Unit::TestCase assert_equal([:bClass1], BClass.public_instance_methods(false)) end + def test_undefined_instance_methods + assert_equal([], AClass.undefined_instance_methods) + assert_equal([], BClass.undefined_instance_methods) + c = Class.new(AClass) {undef aClass} + assert_equal([:aClass], c.undefined_instance_methods) + c = Class.new(c) + assert_equal([], c.undefined_instance_methods) + end + def test_s_public o = (c = Class.new(AClass)).new assert_raise(NoMethodError, /private method/) {o.aClass1} @@ -973,6 +1083,28 @@ class TestModule < Test::Unit::TestCase assert_raise(NoMethodError, /protected method/) {o.aClass2} end + def test_visibility_method_return_value + no_arg_results = nil + c = Module.new do + singleton_class.send(:public, :public, :private, :protected, :module_function) + def foo; end + def bar; end + no_arg_results = [public, private, protected, module_function] + end + + assert_equal([nil]*4, no_arg_results) + + assert_equal(:foo, c.private(:foo)) + assert_equal(:foo, c.public(:foo)) + assert_equal(:foo, c.protected(:foo)) + assert_equal(:foo, c.module_function(:foo)) + + assert_equal([:foo, :bar], c.private(:foo, :bar)) + assert_equal([:foo, :bar], c.public(:foo, :bar)) + assert_equal([:foo, :bar], c.protected(:foo, :bar)) + assert_equal([:foo, :bar], c.module_function(:foo, :bar)) + end + def test_s_constants c1 = Module.constants Object.module_eval "WALTER = 99" @@ -1047,6 +1179,7 @@ class TestModule < Test::Unit::TestCase def test_attr_obsoleted_flag c = Class.new do extend Test::Unit::Assertions + extend Test::Unit::CoreAssertions def initialize @foo = :foo @bar = :bar @@ -1140,8 +1273,11 @@ class TestModule < Test::Unit::TestCase assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-16le"), :foo) } assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-32be"), :foo) } assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-32le"), :foo) } + cx = EnvUtil.labeled_class("X\u{3042}") - assert_raise_with_message(TypeError, /X\u{3042}/) { c1.const_set(cx, :foo) } + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(TypeError, /X\u{3042}/) { c1.const_set(cx, :foo) } + end end def test_const_get_invalid_name @@ -1197,8 +1333,6 @@ class TestModule < Test::Unit::TestCase end end include LangModuleSpecInObject - module LangModuleTop - end puts "ok" if LangModuleSpecInObject::LangModuleTop == LangModuleTop INPUT @@ -1300,6 +1434,7 @@ class TestModule < Test::Unit::TestCase c.instance_eval { attr_reader :"." } end + c = Class.new assert_equal([:a], c.class_eval { attr :a }) assert_equal([:b, :c], c.class_eval { attr :b, :c }) assert_equal([:d], c.class_eval { attr_reader :d }) @@ -1308,6 +1443,16 @@ class TestModule < Test::Unit::TestCase assert_equal([:h=, :i=], c.class_eval { attr_writer :h, :i }) assert_equal([:j, :j=], c.class_eval { attr_accessor :j }) assert_equal([:k, :k=, :l, :l=], c.class_eval { attr_accessor :k, :l }) + + c = Class.new + assert_equal([:a], c.class_eval { attr "a" }) + assert_equal([:b, :c], c.class_eval { attr "b", "c" }) + assert_equal([:d], c.class_eval { attr_reader "d" }) + assert_equal([:e, :f], c.class_eval { attr_reader "e", "f" }) + assert_equal([:g=], c.class_eval { attr_writer "g" }) + assert_equal([:h=, :i=], c.class_eval { attr_writer "h", "i" }) + assert_equal([:j, :j=], c.class_eval { attr_accessor "j" }) + assert_equal([:k, :k=, :l, :l=], c.class_eval { attr_accessor "k", "l" }) end def test_alias_method @@ -1339,8 +1484,8 @@ class TestModule < Test::Unit::TestCase class << o; self; end.instance_eval { undef_method(:foo) } end - %w(object_id __send__ initialize).each do |n| - assert_in_out_err([], <<-INPUT, [], %r"warning: undefining `#{n}' may cause serious problems$") + %w(object_id __id__ __send__ initialize).each do |n| + assert_in_out_err([], <<-INPUT, [], %r"warning: undefining '#{n}' may cause serious problems$") $VERBOSE = false Class.new.instance_eval { undef_method(:#{n}) } INPUT @@ -1599,6 +1744,47 @@ class TestModule < Test::Unit::TestCase assert_equal("TestModule::C\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}") + end + + + def test_const_added + eval(<<~RUBY) + module TestConstAdded + @memo = [] + class << self + attr_accessor :memo + + def const_added(sym) + memo << sym + end + end + CONST = 1 + module SubModule + end + + class SubClass + end + end + TestConstAdded::OUTSIDE_CONST = 2 + module TestConstAdded::OutsideSubModule; end + class TestConstAdded::OutsideSubClass; end + RUBY + TestConstAdded.const_set(:CONST_SET, 3) + assert_equal [ + :CONST, + :SubModule, + :SubClass, + :OUTSIDE_CONST, + :OutsideSubModule, + :OutsideSubClass, + :CONST_SET, + ], TestConstAdded.memo + ensure + if self.class.const_defined? :TestConstAdded + self.class.send(:remove_const, :TestConstAdded) + end end def test_method_added @@ -1970,9 +2156,8 @@ class TestModule < Test::Unit::TestCase Warning[:deprecated] = false Class.new(c)::FOO end - assert_warn('') do - Warning[:deprecated] = false - c.class_eval "FOO" + assert_warn(/deprecated/) do + c.class_eval {remove_const "FOO"} end end @@ -2182,6 +2367,18 @@ class TestModule < Test::Unit::TestCase assert_equal(:foo, removed) end + def test_frozen_prepend_remove_method + [Module, Class].each do |klass| + mod = klass.new do + prepend(Module.new) + def foo; end + end + mod.freeze + assert_raise(FrozenError, '[Bug #19166]') { mod.send(:remove_method, :foo) } + assert_equal([:foo], mod.instance_methods(false)) + end + end + def test_prepend_class_ancestors bug6658 = '[ruby-core:45919]' m = labeled_module("m") @@ -2688,6 +2885,7 @@ class TestModule < Test::Unit::TestCase def test_invalid_attr %W[ + foo= foo? @foo @@foo @@ -2818,17 +3016,17 @@ class TestModule < Test::Unit::TestCase bug11532 = '[ruby-core:70828] [Bug #11532]' c = Class.new {const_set(:A, 1)}.freeze - assert_raise_with_message(FrozenError, /frozen class/, bug11532) { + assert_raise_with_message(FrozenError, /frozen Class/, bug11532) { c.class_eval {private_constant :A} } c = Class.new {const_set(:A, 1); private_constant :A}.freeze - assert_raise_with_message(FrozenError, /frozen class/, bug11532) { + assert_raise_with_message(FrozenError, /frozen Class/, bug11532) { c.class_eval {public_constant :A} } c = Class.new {const_set(:A, 1)}.freeze - assert_raise_with_message(FrozenError, /frozen class/, bug11532) { + assert_raise_with_message(FrozenError, /frozen Class/, bug11532) { c.class_eval {deprecate_constant :A} } end @@ -2875,7 +3073,7 @@ class TestModule < Test::Unit::TestCase end def test_prepend_gc - assert_separately [], %{ + assert_ruby_status [], %{ module Foo end class Object @@ -2972,6 +3170,19 @@ class TestModule < Test::Unit::TestCase end; end + def test_define_method_changes_visibility_with_existing_method_bug_19749 + c = Class.new do + def a; end + private def b; end + + define_method(:b, instance_method(:b)) + private + define_method(:a, instance_method(:a)) + end + assert_equal([:b], c.public_instance_methods(false)) + assert_equal([:a], c.private_instance_methods(false)) + end + def test_define_method_with_unbound_method # Passing an UnboundMethod to define_method succeeds if it is from an ancestor assert_nothing_raised do @@ -3093,6 +3304,109 @@ class TestModule < Test::Unit::TestCase assert_match(/::Foo$/, mod.name, '[Bug #14895]') end + def test_iclass_memory_leak + # [Bug #19550] + assert_no_memory_leak([], <<~PREP, <<~CODE, rss: true) + code = proc do + mod = Module.new + Class.new do + include mod + end + end + 1_000.times(&code) + PREP + 3_000_000.times(&code) + CODE + end + + def test_complemented_method_entry_memory_leak + # [Bug #19894] [Bug #19896] + assert_no_memory_leak([], <<~PREP, <<~CODE, rss: true) + code = proc do + $c = Class.new do + def foo; end + end + + $m = Module.new do + refine $c do + def foo; end + end + end + + Class.new do + using $m + + def initialize + o = $c.new + o.method(:foo).unbind + end + end.new + end + 1_000.times(&code) + PREP + 300_000.times(&code) + CODE + end + + def test_module_clone_memory_leak + # [Bug #19901] + assert_no_memory_leak([], <<~PREP, <<~CODE, rss: true) + code = proc do + Module.new.clone + end + 1_000.times(&code) + PREP + 1_000_000.times(&code) + CODE + end + + def test_set_temporary_name + m = Module.new + assert_nil m.name + + m.const_set(:N, Module.new) + + assert_match(/\A#<Module:0x\h+>::N\z/, m::N.name) + assert_same m::N, m::N.set_temporary_name(name = "fake_name_under_M") + name.upcase! + assert_equal("fake_name_under_M", m::N.name) + assert_raise(FrozenError) {m::N.name.upcase!} + assert_same m::N, m::N.set_temporary_name(nil) + assert_nil(m::N.name) + + m::N.const_set(:O, Module.new) + m.const_set(:Recursive, m) + m::N.const_set(:Recursive, m) + m.const_set(:A, 42) + + assert_same m, m.set_temporary_name(name = "fake_name") + name.upcase! + assert_equal("fake_name", m.name) + assert_raise(FrozenError) {m.name.upcase!} + assert_equal("fake_name::N", m::N.name) + assert_equal("fake_name::N::O", m::N::O.name) + + assert_same m, m.set_temporary_name(nil) + assert_nil m.name + assert_nil m::N.name + assert_nil m::N::O.name + + assert_raise_with_message(ArgumentError, "empty class/module name") do + m.set_temporary_name("") + end + %w[A A::B ::A ::A::B].each do |name| + assert_raise_with_message(ArgumentError, /must not be a constant path/) do + m.set_temporary_name(name) + end + end + + [Object, User, AClass].each do |mod| + assert_raise_with_message(RuntimeError, /permanent name/) do + mod.set_temporary_name("fake_name") + end + end + end + private def assert_top_method_is_private(method) @@ -3100,7 +3414,7 @@ class TestModule < Test::Unit::TestCase methods = singleton_class.private_instance_methods(false) assert_include(methods, :#{method}, ":#{method} should be private") - assert_raise_with_message(NoMethodError, "private method `#{method}' called for main:Object") { + assert_raise_with_message(NoMethodError, /^private method '#{method}' called for /) { recv = self recv.#{method} } |
