summaryrefslogtreecommitdiff
path: root/test/ruby/test_autoload.rb
diff options
context:
space:
mode:
Diffstat (limited to 'test/ruby/test_autoload.rb')
-rw-r--r--test/ruby/test_autoload.rb285
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