PageRenderTime 29ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/test/ruby/test_autoload.rb

http://github.com/ruby/ruby
Ruby | 466 lines | 430 code | 30 blank | 6 comment | 6 complexity | 13d943bfa217383e36e82552c7fd72cd MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-3.0
  1. # frozen_string_literal: false
  2. require 'test/unit'
  3. require 'tempfile'
  4. class TestAutoload < Test::Unit::TestCase
  5. def test_autoload_so
  6. # Date is always available, unless excluded intentionally.
  7. assert_in_out_err([], <<-INPUT, [], [])
  8. autoload :Date, "date"
  9. begin Date; rescue LoadError; end
  10. INPUT
  11. end
  12. def test_non_realpath_in_loadpath
  13. require 'tmpdir'
  14. tmpdir = Dir.mktmpdir('autoload')
  15. tmpdirs = [tmpdir]
  16. tmpdirs.unshift(tmpdir + '/foo')
  17. Dir.mkdir(tmpdirs[0])
  18. tmpfiles = [tmpdir + '/foo.rb', tmpdir + '/foo/bar.rb']
  19. open(tmpfiles[0] , 'w') do |f|
  20. f.puts <<-INPUT
  21. $:.unshift(File.expand_path('..', __FILE__)+'/./foo')
  22. module Foo
  23. autoload :Bar, 'bar'
  24. end
  25. p Foo::Bar
  26. INPUT
  27. end
  28. open(tmpfiles[1], 'w') do |f|
  29. f.puts 'class Foo::Bar; end'
  30. end
  31. assert_in_out_err([tmpfiles[0]], "", ["Foo::Bar"], [])
  32. ensure
  33. File.unlink(*tmpfiles) rescue nil if tmpfiles
  34. tmpdirs.each {|dir| Dir.rmdir(dir)}
  35. end
  36. def test_autoload_p
  37. bug4565 = '[ruby-core:35679]'
  38. require 'tmpdir'
  39. Dir.mktmpdir('autoload') {|tmpdir|
  40. tmpfile = tmpdir + '/foo.rb'
  41. tmpfile2 = tmpdir + '/bar.rb'
  42. a = Module.new do
  43. autoload :X, tmpfile
  44. autoload :Y, tmpfile2
  45. end
  46. b = Module.new do
  47. include a
  48. end
  49. assert_equal(true, a.const_defined?(:X))
  50. assert_equal(true, b.const_defined?(:X))
  51. assert_equal(tmpfile, a.autoload?(:X), bug4565)
  52. assert_equal(tmpfile, b.autoload?(:X), bug4565)
  53. assert_equal(tmpfile, a.autoload?(:X, false))
  54. assert_equal(tmpfile, a.autoload?(:X, nil))
  55. assert_nil(b.autoload?(:X, false))
  56. assert_nil(b.autoload?(:X, nil))
  57. assert_equal(true, a.const_defined?("Y"))
  58. assert_equal(true, b.const_defined?("Y"))
  59. assert_equal(tmpfile2, a.autoload?("Y"))
  60. assert_equal(tmpfile2, b.autoload?("Y"))
  61. }
  62. end
  63. def test_autoload_with_unqualified_file_name # [ruby-core:69206]
  64. Object.send(:remove_const, :A) if Object.const_defined?(:A)
  65. lp = $LOAD_PATH.dup
  66. lf = $LOADED_FEATURES.dup
  67. Dir.mktmpdir('autoload') { |tmpdir|
  68. $LOAD_PATH << tmpdir
  69. Dir.chdir(tmpdir) do
  70. eval <<-END
  71. class ::Object
  72. module A
  73. autoload :C, 'test-ruby-core-69206'
  74. end
  75. end
  76. END
  77. File.write("test-ruby-core-69206.rb", 'module A; class C; end; end')
  78. assert_kind_of Class, ::A::C
  79. end
  80. }
  81. ensure
  82. $LOAD_PATH.replace lp
  83. $LOADED_FEATURES.replace lf
  84. Object.send(:remove_const, :A) if Object.const_defined?(:A)
  85. end
  86. def test_require_explicit
  87. Tempfile.create(['autoload', '.rb']) {|file|
  88. file.puts 'class Object; AutoloadTest = 1; end'
  89. file.close
  90. add_autoload(file.path)
  91. begin
  92. assert_nothing_raised do
  93. assert(require file.path)
  94. assert_equal(1, ::AutoloadTest)
  95. end
  96. ensure
  97. remove_autoload_constant
  98. end
  99. }
  100. end
  101. def test_threaded_accessing_constant
  102. # Suppress "warning: loading in progress, circular require considered harmful"
  103. EnvUtil.default_warning {
  104. Tempfile.create(['autoload', '.rb']) {|file|
  105. file.puts 'sleep 0.5; class AutoloadTest; X = 1; end'
  106. file.close
  107. add_autoload(file.path)
  108. begin
  109. assert_nothing_raised do
  110. t1 = Thread.new { ::AutoloadTest::X }
  111. t2 = Thread.new { ::AutoloadTest::X }
  112. [t1, t2].each(&:join)
  113. end
  114. ensure
  115. remove_autoload_constant
  116. end
  117. }
  118. }
  119. end
  120. def test_threaded_accessing_inner_constant
  121. # Suppress "warning: loading in progress, circular require considered harmful"
  122. EnvUtil.default_warning {
  123. Tempfile.create(['autoload', '.rb']) {|file|
  124. file.puts 'class AutoloadTest; sleep 0.5; X = 1; end'
  125. file.close
  126. add_autoload(file.path)
  127. begin
  128. assert_nothing_raised do
  129. t1 = Thread.new { ::AutoloadTest::X }
  130. t2 = Thread.new { ::AutoloadTest::X }
  131. [t1, t2].each(&:join)
  132. end
  133. ensure
  134. remove_autoload_constant
  135. end
  136. }
  137. }
  138. end
  139. def test_nameerror_when_autoload_did_not_define_the_constant
  140. Tempfile.create(['autoload', '.rb']) {|file|
  141. file.puts ''
  142. file.close
  143. add_autoload(file.path)
  144. begin
  145. assert_raise(NameError) do
  146. AutoloadTest
  147. end
  148. ensure
  149. remove_autoload_constant
  150. end
  151. }
  152. end
  153. def test_override_autoload
  154. Tempfile.create(['autoload', '.rb']) {|file|
  155. file.puts ''
  156. file.close
  157. add_autoload(file.path)
  158. begin
  159. eval %q(class AutoloadTest; end)
  160. assert_equal(Class, AutoloadTest.class)
  161. ensure
  162. remove_autoload_constant
  163. end
  164. }
  165. end
  166. def test_override_while_autoloading
  167. Tempfile.create(['autoload', '.rb']) {|file|
  168. file.puts 'class AutoloadTest; sleep 0.5; end'
  169. file.close
  170. add_autoload(file.path)
  171. begin
  172. # while autoloading...
  173. t = Thread.new { AutoloadTest }
  174. sleep 0.1
  175. # override it
  176. EnvUtil.suppress_warning {
  177. eval %q(AutoloadTest = 1)
  178. }
  179. t.join
  180. assert_equal(1, AutoloadTest)
  181. ensure
  182. remove_autoload_constant
  183. end
  184. }
  185. end
  186. def ruby_impl_require
  187. Kernel.module_eval do
  188. alias old_require require
  189. end
  190. called_with = []
  191. Kernel.send :define_method, :require do |path|
  192. called_with << path
  193. old_require path
  194. end
  195. yield called_with
  196. ensure
  197. Kernel.module_eval do
  198. undef require
  199. alias require old_require
  200. undef old_require
  201. end
  202. end
  203. def test_require_implemented_in_ruby_is_called
  204. ruby_impl_require do |called_with|
  205. Tempfile.create(['autoload', '.rb']) {|file|
  206. file.puts 'class AutoloadTest; end'
  207. file.close
  208. add_autoload(file.path)
  209. begin
  210. assert(Object::AutoloadTest)
  211. ensure
  212. remove_autoload_constant
  213. end
  214. assert_equal [file.path], called_with
  215. }
  216. end
  217. end
  218. def test_autoload_while_autoloading
  219. ruby_impl_require do |called_with|
  220. Tempfile.create(%w(a .rb)) do |a|
  221. Tempfile.create(%w(b .rb)) do |b|
  222. a.puts "require '#{b.path}'; class AutoloadTest; end"
  223. b.puts "class AutoloadTest; module B; end; end"
  224. [a, b].each(&:flush)
  225. add_autoload(a.path)
  226. begin
  227. assert(Object::AutoloadTest)
  228. ensure
  229. remove_autoload_constant
  230. end
  231. assert_equal [a.path, b.path], called_with
  232. end
  233. end
  234. end
  235. end
  236. def test_bug_13526
  237. script = File.join(__dir__, 'bug-13526.rb')
  238. assert_ruby_status([script], '', '[ruby-core:81016] [Bug #13526]')
  239. end
  240. def test_autoload_private_constant
  241. Dir.mktmpdir('autoload') do |tmpdir|
  242. File.write(tmpdir+"/test-bug-14469.rb", "#{<<~"begin;"}\n#{<<~'end;'}")
  243. begin;
  244. class AutoloadTest
  245. ZZZ = :ZZZ
  246. private_constant :ZZZ
  247. end
  248. end;
  249. assert_separately(%W[-I #{tmpdir}], "#{<<-"begin;"}\n#{<<-'end;'}")
  250. bug = '[ruby-core:85516] [Bug #14469]'
  251. begin;
  252. class AutoloadTest
  253. autoload :ZZZ, "test-bug-14469.rb"
  254. end
  255. assert_raise(NameError, bug) {AutoloadTest::ZZZ}
  256. end;
  257. end
  258. end
  259. def test_autoload_deprecate_constant
  260. Dir.mktmpdir('autoload') do |tmpdir|
  261. File.write(tmpdir+"/test-bug-14469.rb", "#{<<~"begin;"}\n#{<<~'end;'}")
  262. begin;
  263. class AutoloadTest
  264. ZZZ = :ZZZ
  265. deprecate_constant :ZZZ
  266. end
  267. end;
  268. assert_separately(%W[-I #{tmpdir}], "#{<<-"begin;"}\n#{<<-'end;'}")
  269. bug = '[ruby-core:85516] [Bug #14469]'
  270. begin;
  271. class AutoloadTest
  272. autoload :ZZZ, "test-bug-14469.rb"
  273. end
  274. assert_warning(/ZZZ is deprecated/, bug) {AutoloadTest::ZZZ}
  275. end;
  276. end
  277. end
  278. def test_autoload_private_constant_before_autoload
  279. Dir.mktmpdir('autoload') do |tmpdir|
  280. File.write(tmpdir+"/test-bug-11055.rb", "#{<<~"begin;"}\n#{<<~'end;'}")
  281. begin;
  282. class AutoloadTest
  283. ZZZ = :ZZZ
  284. end
  285. end;
  286. assert_separately(%W[-I #{tmpdir}], "#{<<-"begin;"}\n#{<<-'end;'}")
  287. bug = '[Bug #11055]'
  288. begin;
  289. class AutoloadTest
  290. autoload :ZZZ, "test-bug-11055.rb"
  291. private_constant :ZZZ
  292. ZZZ
  293. end
  294. assert_raise(NameError, bug) {AutoloadTest::ZZZ}
  295. end;
  296. assert_separately(%W[-I #{tmpdir}], "#{<<-"begin;"}\n#{<<-'end;'}")
  297. bug = '[Bug #11055]'
  298. begin;
  299. class AutoloadTest
  300. autoload :ZZZ, "test-bug-11055.rb"
  301. private_constant :ZZZ
  302. end
  303. assert_raise(NameError, bug) {AutoloadTest::ZZZ}
  304. end;
  305. end
  306. end
  307. def test_autoload_deprecate_constant_before_autoload
  308. Dir.mktmpdir('autoload') do |tmpdir|
  309. File.write(tmpdir+"/test-bug-11055.rb", "#{<<~"begin;"}\n#{<<~'end;'}")
  310. begin;
  311. class AutoloadTest
  312. ZZZ = :ZZZ
  313. end
  314. end;
  315. assert_separately(%W[-I #{tmpdir}], "#{<<-"begin;"}\n#{<<-'end;'}")
  316. bug = '[Bug #11055]'
  317. begin;
  318. class AutoloadTest
  319. autoload :ZZZ, "test-bug-11055.rb"
  320. deprecate_constant :ZZZ
  321. end
  322. assert_warning(/ZZZ is deprecated/, bug) {class AutoloadTest; ZZZ; end}
  323. assert_warning(/ZZZ is deprecated/, bug) {AutoloadTest::ZZZ}
  324. end;
  325. assert_separately(%W[-I #{tmpdir}], "#{<<-"begin;"}\n#{<<-'end;'}")
  326. bug = '[Bug #11055]'
  327. begin;
  328. class AutoloadTest
  329. autoload :ZZZ, "test-bug-11055.rb"
  330. deprecate_constant :ZZZ
  331. end
  332. assert_warning(/ZZZ is deprecated/, bug) {AutoloadTest::ZZZ}
  333. end;
  334. end
  335. end
  336. def test_autoload_fork
  337. EnvUtil.default_warning do
  338. Tempfile.create(['autoload', '.rb']) {|file|
  339. file.puts 'sleep 0.3; class AutoloadTest; end'
  340. file.close
  341. add_autoload(file.path)
  342. begin
  343. thrs = []
  344. 3.times do
  345. thrs << Thread.new { AutoloadTest && nil }
  346. thrs << Thread.new { fork { AutoloadTest } }
  347. end
  348. thrs.each(&:join)
  349. thrs.each do |th|
  350. pid = th.value or next
  351. _, status = Process.waitpid2(pid)
  352. assert_predicate status, :success?
  353. end
  354. ensure
  355. remove_autoload_constant
  356. assert_nil $!, '[ruby-core:86410] [Bug #14634]'
  357. end
  358. }
  359. end
  360. end if Process.respond_to?(:fork)
  361. def test_autoload_same_file
  362. Dir.mktmpdir('autoload') do |tmpdir|
  363. File.write("#{tmpdir}/test-bug-14742.rb", "#{<<~'begin;'}\n#{<<~'end;'}")
  364. begin;
  365. module Foo; end
  366. module Bar; end
  367. end;
  368. 3.times do # timing-dependent, needs a few times to hit [Bug #14742]
  369. assert_separately(%W[-I #{tmpdir}], "#{<<-'begin;'}\n#{<<-'end;'}")
  370. begin;
  371. autoload :Foo, 'test-bug-14742'
  372. autoload :Bar, 'test-bug-14742'
  373. t1 = Thread.new do Foo end
  374. t2 = Thread.new do Bar end
  375. t1.join
  376. t2.join
  377. bug = '[ruby-core:86935] [Bug #14742]'
  378. assert_instance_of Module, t1.value, bug
  379. assert_instance_of Module, t2.value, bug
  380. end;
  381. end
  382. end
  383. end
  384. def test_autoload_same_file_with_raise
  385. Dir.mktmpdir('autoload') do |tmpdir|
  386. File.write("#{tmpdir}/test-bug-16177.rb", "#{<<~'begin;'}\n#{<<~'end;'}")
  387. begin;
  388. raise '[ruby-core:95055] [Bug #16177]'
  389. end;
  390. assert_raise(RuntimeError, '[ruby-core:95055] [Bug #16177]') do
  391. assert_separately(%W[-I #{tmpdir}], "#{<<-'begin;'}\n#{<<-'end;'}")
  392. begin;
  393. autoload :Foo, 'test-bug-16177'
  394. autoload :Bar, 'test-bug-16177'
  395. t1 = Thread.new do Foo end
  396. t2 = Thread.new do Bar end
  397. t1.join
  398. t2.join
  399. end;
  400. end
  401. end
  402. end
  403. def test_source_location
  404. klass = self.class
  405. bug = "Bug16764"
  406. Dir.mktmpdir('autoload') do |tmpdir|
  407. path = "#{tmpdir}/test-#{bug}.rb"
  408. File.write(path, "#{klass}::#{bug} = __FILE__\n")
  409. klass.autoload(:Bug16764, path)
  410. assert_equal [__FILE__, __LINE__-1], klass.const_source_location(bug)
  411. assert_equal path, klass.const_get(bug)
  412. assert_equal [path, 1], klass.const_source_location(bug)
  413. end
  414. end
  415. def test_no_memory_leak
  416. assert_no_memory_leak([], '', "#{<<~"begin;"}\n#{<<~'end;'}", 'many autoloads', timeout: 60)
  417. begin;
  418. 200000.times do |i|
  419. m = Module.new
  420. m.instance_eval do
  421. autoload :Foo, 'x'
  422. autoload :Bar, i.to_s
  423. end
  424. end
  425. end;
  426. end
  427. def add_autoload(path)
  428. (@autoload_paths ||= []) << path
  429. ::Object.class_eval {autoload(:AutoloadTest, path)}
  430. end
  431. def remove_autoload_constant
  432. $".replace($" - @autoload_paths)
  433. ::Object.class_eval {remove_const(:AutoloadTest)}
  434. TestAutoload.class_eval {remove_const(:AutoloadTest)} if defined? TestAutoload::AutoloadTest
  435. end
  436. end