diff options
Diffstat (limited to 'test/ruby/test_autoload.rb')
| -rw-r--r-- | test/ruby/test_autoload.rb | 285 |
1 files changed, 271 insertions, 14 deletions
diff --git a/test/ruby/test_autoload.rb b/test/ruby/test_autoload.rb index 3b013c12ae..82bf2d9d2c 100644 --- a/test/ruby/test_autoload.rb +++ b/test/ruby/test_autoload.rb @@ -65,7 +65,27 @@ p Foo::Bar } end + def test_autoload_p_with_static_extensions + require 'rbconfig' + omit unless RbConfig::CONFIG['EXTSTATIC'] == 'static' + begin + require 'fcntl.so' + rescue LoadError + omit('fcntl not included in the build') + end + + assert_separately(['--disable-all'], <<~RUBY) + autoload :Fcntl, 'fcntl.so' + + assert_equal('fcntl.so', autoload?(:Fcntl)) + assert(Object.const_defined?(:Fcntl)) + assert_equal('constant', defined?(Fcntl), '[Bug #19115]') + RUBY + end + def test_autoload_with_unqualified_file_name # [ruby-core:69206] + Object.send(:remove_const, :A) if Object.const_defined?(:A) + lp = $LOAD_PATH.dup lf = $LOADED_FEATURES.dup @@ -76,12 +96,12 @@ p Foo::Bar eval <<-END class ::Object module A - autoload :C, 'b' + autoload :C, 'test-ruby-core-69206' end end END - File.open('b.rb', 'w') {|file| file.puts 'module A; class C; end; end'} + File.write("test-ruby-core-69206.rb", 'module A; class C; end; end') assert_kind_of Class, ::A::C end } @@ -148,6 +168,7 @@ p Foo::Bar end def test_nameerror_when_autoload_did_not_define_the_constant + verbose_bak, $VERBOSE = $VERBOSE, nil Tempfile.create(['autoload', '.rb']) {|file| file.puts '' file.close @@ -160,6 +181,8 @@ p Foo::Bar remove_autoload_constant end } + ensure + $VERBOSE = verbose_bak end def test_override_autoload @@ -201,11 +224,18 @@ p Foo::Bar Kernel.module_eval do alias old_require require end + Ruby::Box.module_eval do + alias old_require require + end called_with = [] Kernel.send :define_method, :require do |path| called_with << path old_require path end + Ruby::Box.send :define_method, :require do |path| + called_with << path + old_require path + end yield called_with ensure Kernel.module_eval do @@ -213,6 +243,11 @@ p Foo::Bar alias require old_require undef old_require end + Ruby::Box.module_eval do + undef require + alias require old_require + undef old_require + end end def test_require_implemented_in_ruby_is_called @@ -226,7 +261,8 @@ p Foo::Bar ensure remove_autoload_constant end - assert_equal [file.path], called_with + # .dup to prevent breaking called_with by autoloading pp, etc + assert_equal [file.path], called_with.dup } end end @@ -244,20 +280,25 @@ p Foo::Bar ensure remove_autoload_constant end - assert_equal [a.path, b.path], called_with + # .dup to prevent breaking called_with by autoloading pp, etc + assert_equal [a.path, b.path], called_with.dup end end end end def test_bug_13526 + # Skip this on macOS 10.13 because of the following error: + # http://rubyci.s3.amazonaws.com/osx1013/ruby-master/log/20231011T014505Z.fail.html.gz + require "rbconfig" + script = File.join(__dir__, 'bug-13526.rb') assert_ruby_status([script], '', '[ruby-core:81016] [Bug #13526]') end def test_autoload_private_constant Dir.mktmpdir('autoload') do |tmpdir| - File.write(tmpdir+"/zzz.rb", "#{<<~"begin;"}\n#{<<~'end;'}") + File.write(tmpdir+"/test-bug-14469.rb", "#{<<~"begin;"}\n#{<<~'end;'}") begin; class AutoloadTest ZZZ = :ZZZ @@ -268,7 +309,7 @@ p Foo::Bar bug = '[ruby-core:85516] [Bug #14469]' begin; class AutoloadTest - autoload :ZZZ, "zzz.rb" + autoload :ZZZ, "test-bug-14469.rb" end assert_raise(NameError, bug) {AutoloadTest::ZZZ} end; @@ -277,7 +318,7 @@ p Foo::Bar def test_autoload_deprecate_constant Dir.mktmpdir('autoload') do |tmpdir| - File.write(tmpdir+"/zzz.rb", "#{<<~"begin;"}\n#{<<~'end;'}") + File.write(tmpdir+"/test-bug-14469.rb", "#{<<~"begin;"}\n#{<<~'end;'}") begin; class AutoloadTest ZZZ = :ZZZ @@ -288,7 +329,67 @@ p Foo::Bar bug = '[ruby-core:85516] [Bug #14469]' begin; class AutoloadTest - autoload :ZZZ, "zzz.rb" + autoload :ZZZ, "test-bug-14469.rb" + end + assert_warning(/ZZZ is deprecated/, bug) {AutoloadTest::ZZZ} + end; + end + end + + def test_autoload_private_constant_before_autoload + Dir.mktmpdir('autoload') do |tmpdir| + File.write(tmpdir+"/test-bug-11055.rb", "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + class AutoloadTest + ZZZ = :ZZZ + end + end; + assert_separately(%W[-I #{tmpdir}], "#{<<-"begin;"}\n#{<<-'end;'}") + bug = '[Bug #11055]' + begin; + class AutoloadTest + autoload :ZZZ, "test-bug-11055.rb" + private_constant :ZZZ + ZZZ + end + assert_raise(NameError, bug) {AutoloadTest::ZZZ} + end; + assert_separately(%W[-I #{tmpdir}], "#{<<-"begin;"}\n#{<<-'end;'}") + bug = '[Bug #11055]' + begin; + class AutoloadTest + autoload :ZZZ, "test-bug-11055.rb" + private_constant :ZZZ + end + assert_raise(NameError, bug) {AutoloadTest::ZZZ} + end; + end + end + + def test_autoload_deprecate_constant_before_autoload + Dir.mktmpdir('autoload') do |tmpdir| + File.write(tmpdir+"/test-bug-11055.rb", "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + class AutoloadTest + ZZZ = :ZZZ + end + end; + assert_separately(%W[-I #{tmpdir}], "#{<<-"begin;"}\n#{<<-'end;'}") + bug = '[Bug #11055]' + begin; + class AutoloadTest + autoload :ZZZ, "test-bug-11055.rb" + deprecate_constant :ZZZ + end + assert_warning(/ZZZ is deprecated/, bug) {class AutoloadTest; ZZZ; end} + assert_warning(/ZZZ is deprecated/, bug) {AutoloadTest::ZZZ} + end; + assert_separately(%W[-I #{tmpdir}], "#{<<-"begin;"}\n#{<<-'end;'}") + bug = '[Bug #11055]' + begin; + class AutoloadTest + autoload :ZZZ, "test-bug-11055.rb" + deprecate_constant :ZZZ end assert_warning(/ZZZ is deprecated/, bug) {AutoloadTest::ZZZ} end; @@ -323,7 +424,7 @@ p Foo::Bar def test_autoload_same_file Dir.mktmpdir('autoload') do |tmpdir| - File.write("#{tmpdir}/b.rb", "#{<<~'begin;'}\n#{<<~'end;'}") + File.write("#{tmpdir}/test-bug-14742.rb", "#{<<~'begin;'}\n#{<<~'end;'}") begin; module Foo; end module Bar; end @@ -331,8 +432,8 @@ p Foo::Bar 3.times do # timing-dependent, needs a few times to hit [Bug #14742] assert_separately(%W[-I #{tmpdir}], "#{<<-'begin;'}\n#{<<-'end;'}") begin; - autoload :Foo, 'b' - autoload :Bar, 'b' + autoload :Foo, 'test-bug-14742' + autoload :Bar, 'test-bug-14742' t1 = Thread.new do Foo end t2 = Thread.new do Bar end t1.join @@ -345,8 +446,62 @@ p Foo::Bar end end - def test_no_leak - assert_no_memory_leak([], '', <<~'end;', 'many autoloads', timeout: 60) + def test_autoload_same_file_with_raise + Dir.mktmpdir('autoload') do |tmpdir| + File.write("#{tmpdir}/test-bug-16177.rb", "#{<<~'begin;'}\n#{<<~'end;'}") + begin; + raise '[ruby-core:95055] [Bug #16177]' + end; + assert_raise(RuntimeError, '[ruby-core:95055] [Bug #16177]') do + assert_separately(%W[-I #{tmpdir}], "#{<<-'begin;'}\n#{<<-'end;'}") + begin; + autoload :Foo, 'test-bug-16177' + autoload :Bar, 'test-bug-16177' + t1 = Thread.new do Foo end + t2 = Thread.new do Bar end + t1.join + t2.join + end; + end + end + end + + def test_source_location + bug = "Bug16764" + Dir.mktmpdir('autoload') do |tmpdir| + path = "#{tmpdir}/test-#{bug}.rb" + File.write(path, "C::#{bug} = __FILE__\n") + assert_separately(%W[-I #{tmpdir}], "#{<<-"begin;"}\n#{<<-"end;"}") + begin; + class C; end + C.autoload(:Bug16764, #{path.dump}) + assert_equal [__FILE__, __LINE__-1], C.const_source_location(#{bug.dump}) + assert_equal #{path.dump}, C.const_get(#{bug.dump}) + assert_equal [#{path.dump}, 1], C.const_source_location(#{bug.dump}) + end; + end + end + + def test_source_location_after_require + bug = "Bug18624" + Dir.mktmpdir('autoload') do |tmpdir| + path = "#{tmpdir}/test-#{bug}.rb" + File.write(path, "C::#{bug} = __FILE__\n") + assert_separately(%W[-I #{tmpdir}], "#{<<-"begin;"}\n#{<<-"end;"}") + begin; + class C; end + C.autoload(:Bug18624, #{path.dump}) + require #{path.dump} + assert_equal [#{path.dump}, 1], C.const_source_location(#{bug.dump}) + assert_equal #{path.dump}, C.const_get(#{bug.dump}) + assert_equal [#{path.dump}, 1], C.const_source_location(#{bug.dump}) + end; + end + end + + def test_no_memory_leak + assert_no_memory_leak([], '', "#{<<~"begin;"}\n#{<<~'end;'}", 'many autoloads', timeout: 60) + begin; 200000.times do |i| m = Module.new m.instance_eval do @@ -357,6 +512,32 @@ p Foo::Bar end; end + def test_autoload_after_failed_and_removed_from_loaded_features + Dir.mktmpdir('autoload') do |tmpdir| + autoload_path = File.join(tmpdir, "test-bug-15790.rb") + File.write(autoload_path, '') + + assert_separately(%W[-I #{tmpdir}], <<-RUBY) + $VERBOSE = nil + path = #{File.realpath(autoload_path).inspect} + autoload :X, path + assert_equal(path, Object.autoload?(:X)) + + assert_raise(NameError){X} + assert_nil(Object.autoload?(:X)) + assert_equal(false, Object.const_defined?(:X)) + + $LOADED_FEATURES.delete(path) + assert_equal(false, Object.const_defined?(:X)) + assert_nil(Object.autoload?(:X)) + + assert_raise(NameError){X} + assert_equal(false, Object.const_defined?(:X)) + assert_nil(Object.autoload?(:X)) + RUBY + end + end + def add_autoload(path) (@autoload_paths ||= []) << path ::Object.class_eval {autoload(:AutoloadTest, path)} @@ -364,6 +545,82 @@ p Foo::Bar def remove_autoload_constant $".replace($" - @autoload_paths) - ::Object.class_eval {remove_const(:AutoloadTest)} + ::Object.class_eval {remove_const(:AutoloadTest)} if defined? Object::AutoloadTest + TestAutoload.class_eval {remove_const(:AutoloadTest)} if defined? TestAutoload::AutoloadTest + end + + def test_autoload_module_gc + Dir.mktmpdir('autoload') do |tmpdir| + autoload_path = File.join(tmpdir, "autoload_module_gc.rb") + File.write(autoload_path, "X = 1; Y = 2;") + + x = Module.new + x.autoload :X, "./feature.rb" + + 1000.times do + y = Module.new + y.autoload :Y, "./feature.rb" + end + + x = y = nil + + # Ensure the internal data structures are cleaned up correctly / don't crash: + GC.start + end + end + + def test_autoload_parallel_race + Dir.mktmpdir('autoload') do |tmpdir| + autoload_path = File.join(tmpdir, "autoload_parallel_race.rb") + File.write(autoload_path, 'module Foo; end; module Bar; end') + + assert_ruby_status([], <<-RUBY, timeout: 100) + autoload_path = #{File.realpath(autoload_path).inspect} + + # This should work with no errors or failures. + 1000.times do + autoload :Foo, autoload_path + autoload :Bar, autoload_path + + t1 = Thread.new {Foo} + t2 = Thread.new {Bar} + + t1.join + GC.start # force GC. + t2.join + + Object.send(:remove_const, :Foo) + Object.send(:remove_const, :Bar) + + $LOADED_FEATURES.delete(autoload_path) + end + RUBY + end + end + + def test_autoload_parent_namespace + Dir.mktmpdir('autoload') do |tmpdir| + autoload_path = File.join(tmpdir, "some_const.rb") + File.write(autoload_path, 'class SomeConst; end') + + assert_separately(%W[-I #{tmpdir}], <<-RUBY) + module SomeNamespace + autoload :SomeConst, #{File.realpath(autoload_path).inspect} + assert_warning(%r{/some_const\.rb to define SomeNamespace::SomeConst but it didn't}) do + assert_not_nil SomeConst + end + end + RUBY + end + end + + private + + def assert_separately(*args, **kwargs) + super(*args, timeout: 60, **kwargs) + end + + def assert_ruby_status(*args, **kwargs) + super(*args, timeout: 60, **kwargs) end end |
