1# frozen_string_literal: false 2require 'test/unit' 3 4require 'tempfile' 5require 'tmpdir' 6 7class TestRequire < Test::Unit::TestCase 8 def test_load_error_path 9 filename = "should_not_exist" 10 error = assert_raise(LoadError) do 11 require filename 12 end 13 assert_equal filename, error.path 14 end 15 16 def test_require_invalid_shared_object 17 Tempfile.create(["test_ruby_test_require", ".so"]) {|t| 18 t.puts "dummy" 19 t.close 20 21 assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}") 22 begin; 23 $:.replace([IO::NULL]) 24 assert_raise(LoadError) do 25 require \"#{ t.path }\" 26 end 27 end; 28 } 29 end 30 31 def test_require_too_long_filename 32 assert_separately(["RUBYOPT"=>nil], "#{<<~"begin;"}\n#{<<~"end;"}") 33 begin; 34 $:.replace([IO::NULL]) 35 assert_raise(LoadError) do 36 require '#{ "foo/" * 10000 }foo' 37 end 38 end; 39 40 begin 41 assert_in_out_err(["-S", "-w", "foo/" * 1024 + "foo"], "") do |r, e| 42 assert_equal([], r) 43 assert_operator(2, :<=, e.size) 44 assert_match(/warning: openpath: pathname too long \(ignored\)/, e.first) 45 assert_match(/\(LoadError\)/, e.last) 46 end 47 rescue Errno::EINVAL 48 # too long commandline may be blocked by OS. 49 end 50 end 51 52 def test_require_nonascii 53 bug3758 = '[ruby-core:31915]' 54 ["\u{221e}", "\x82\xa0".force_encoding("cp932")].each do |path| 55 assert_raise_with_message(LoadError, /#{path}\z/, bug3758) {require path} 56 end 57 end 58 59 def test_require_nonascii_path 60 bug8165 = '[ruby-core:53733] [Bug #8165]' 61 encoding = 'filesystem' 62 assert_require_nonascii_path(encoding, bug8165) 63 end 64 65 def test_require_insecure_path 66 assert_require_insecure_path("foo") 67 encoding = 'filesystem' 68 assert_require_insecure_path(nil, encoding) 69 end 70 71 def test_require_nonascii_path_utf8 72 bug8676 = '[ruby-core:56136] [Bug #8676]' 73 encoding = Encoding::UTF_8 74 return if Encoding.find('filesystem') == encoding 75 assert_require_nonascii_path(encoding, bug8676) 76 end 77 78 def test_require_insecure_path_utf8 79 encoding = Encoding::UTF_8 80 return if Encoding.find('filesystem') == encoding 81 assert_require_insecure_path(nil, encoding) 82 end 83 84 def test_require_nonascii_path_shift_jis 85 bug8676 = '[ruby-core:56136] [Bug #8676]' 86 encoding = Encoding::Shift_JIS 87 return if Encoding.find('filesystem') == encoding 88 assert_require_nonascii_path(encoding, bug8676) 89 end 90 91 def test_require_insecure_path_shift_jis 92 encoding = Encoding::Shift_JIS 93 return if Encoding.find('filesystem') == encoding 94 assert_require_insecure_path(nil, encoding) 95 end 96 97 case RUBY_PLATFORM 98 when /cygwin/, /mswin/, /mingw/, /darwin/ 99 def self.ospath_encoding(path) 100 Encoding::UTF_8 101 end 102 else 103 def self.ospath_encoding(path) 104 path.encoding 105 end 106 end 107 108 SECURITY_WARNING = 109 if /mswin|mingw/ =~ RUBY_PLATFORM 110 nil 111 else 112 proc do |require_path| 113 $SAFE = 1 114 require(require_path) 115 ensure 116 $SAFE = 0 117 end 118 end 119 120 def prepare_require_path(dir, encoding) 121 Dir.mktmpdir {|tmp| 122 begin 123 require_path = File.join(tmp, dir, 'foo.rb').encode(encoding) 124 rescue 125 skip "cannot convert path encoding to #{encoding}" 126 end 127 Dir.mkdir(File.dirname(require_path)) 128 open(require_path, "wb") {|f| f.puts '$:.push __FILE__'} 129 begin 130 load_path = $:.dup 131 features = $".dup 132 yield require_path 133 ensure 134 $:.replace(load_path) 135 $".replace(features) 136 end 137 } 138 end 139 140 def assert_require_nonascii_path(encoding, bug) 141 prepare_require_path("\u3042" * 5, encoding) {|require_path| 142 begin 143 # leave paths for require encoding objects 144 bug = "#{bug} require #{encoding} path" 145 require_path = "#{require_path}" 146 $:.clear 147 assert_nothing_raised(LoadError, bug) { 148 assert(require(require_path), bug) 149 assert_equal(self.class.ospath_encoding(require_path), $:.last.encoding, '[Bug #8753]') 150 assert(!require(require_path), bug) 151 } 152 end 153 } 154 end 155 156 def assert_require_insecure_path(dirname, encoding = nil) 157 return unless SECURITY_WARNING 158 dirname ||= "\u3042" * 5 159 encoding ||= dirname.encoding 160 prepare_require_path(dirname, encoding) {|require_path| 161 require_path.untaint 162 require(require_path) 163 $".pop 164 File.chmod(0777, File.dirname(require_path)) 165 ospath = (require_path.encode('filesystem') rescue 166 require_path.encode(self.class.ospath_encoding(require_path))) 167 e = nil 168 stderr = EnvUtil.verbose_warning do 169 e = assert_raise(SecurityError) do 170 SECURITY_WARNING.call(require_path) 171 end 172 end 173 assert_include(e.message, "loading from unsafe path") 174 assert_include(stderr, "Insecure world writable dir") 175 require_path = require_path.encode(self.class.ospath_encoding(require_path)) 176 assert_include(e.message, require_path) 177 assert_include(stderr, File.dirname(require_path)) 178 } 179 end 180 181 def test_require_path_home_1 182 env_rubypath, env_home = ENV["RUBYPATH"], ENV["HOME"] 183 pathname_too_long = /pathname too long \(ignored\).*\(LoadError\)/m 184 185 ENV["RUBYPATH"] = "~" 186 ENV["HOME"] = "/foo" * 1024 187 assert_in_out_err(%w(-S -w test_ruby_test_require), "", [], pathname_too_long) 188 189 ensure 190 env_rubypath ? ENV["RUBYPATH"] = env_rubypath : ENV.delete("RUBYPATH") 191 env_home ? ENV["HOME"] = env_home : ENV.delete("HOME") 192 end 193 194 def test_require_path_home_2 195 env_rubypath, env_home = ENV["RUBYPATH"], ENV["HOME"] 196 pathname_too_long = /pathname too long \(ignored\).*\(LoadError\)/m 197 198 ENV["RUBYPATH"] = "~" + "/foo" * 1024 199 ENV["HOME"] = "/foo" 200 assert_in_out_err(%w(-S -w test_ruby_test_require), "", [], pathname_too_long) 201 202 ensure 203 env_rubypath ? ENV["RUBYPATH"] = env_rubypath : ENV.delete("RUBYPATH") 204 env_home ? ENV["HOME"] = env_home : ENV.delete("HOME") 205 end 206 207 def test_require_path_home_3 208 env_rubypath, env_home = ENV["RUBYPATH"], ENV["HOME"] 209 210 Tempfile.create(["test_ruby_test_require", ".rb"]) {|t| 211 t.puts "p :ok" 212 t.close 213 214 ENV["RUBYPATH"] = "~" 215 ENV["HOME"] = t.path 216 assert_in_out_err(%w(-S test_ruby_test_require), "", [], /\(LoadError\)/) 217 218 ENV["HOME"], name = File.split(t.path) 219 assert_in_out_err(["-S", name], "", %w(:ok), []) 220 } 221 ensure 222 env_rubypath ? ENV["RUBYPATH"] = env_rubypath : ENV.delete("RUBYPATH") 223 env_home ? ENV["HOME"] = env_home : ENV.delete("HOME") 224 end 225 226 def test_require_with_unc 227 Tempfile.create(["test_ruby_test_require", ".rb"]) {|t| 228 t.puts "puts __FILE__" 229 t.close 230 231 path = File.expand_path(t.path).sub(/\A(\w):/, '//127.0.0.1/\1$') 232 skip "local drive #$1: is not shared" unless File.exist?(path) 233 args = ['--disable-gems', "-I#{File.dirname(path)}"] 234 assert_in_out_err(args, "#{<<~"END;"}", [path], []) 235 begin 236 require '#{File.basename(path)}' 237 rescue Errno::EPERM 238 end 239 END; 240 } 241 end if /mswin|mingw/ =~ RUBY_PLATFORM 242 243 def test_require_twice 244 Dir.mktmpdir do |tmp| 245 req = File.join(tmp, "very_long_file_name.rb") 246 File.write(req, "p :ok\n") 247 assert_file.exist?(req) 248 req[/.rb$/i] = "" 249 assert_in_out_err(['--disable-gems'], <<-INPUT, %w(:ok), []) 250 require "#{req}" 251 require "#{req}" 252 INPUT 253 end 254 end 255 256 def assert_syntax_error_backtrace 257 Dir.mktmpdir do |tmp| 258 req = File.join(tmp, "test.rb") 259 File.write(req, "'\n") 260 e = assert_raise_with_message(SyntaxError, /unterminated/) { 261 yield req 262 } 263 assert_not_nil(bt = e.backtrace) 264 assert_not_empty(bt.find_all {|b| b.start_with? __FILE__}) 265 end 266 end 267 268 def test_require_syntax_error 269 assert_syntax_error_backtrace {|req| require req} 270 end 271 272 def test_load_syntax_error 273 assert_syntax_error_backtrace {|req| load req} 274 end 275 276 def test_define_class 277 begin 278 require "socket" 279 rescue LoadError 280 return 281 end 282 283 assert_separately([], <<-INPUT) 284 BasicSocket = 1 285 assert_raise(TypeError) do 286 require 'socket' 287 end 288 INPUT 289 290 assert_separately([], <<-INPUT) 291 class BasicSocket; end 292 assert_raise(TypeError) do 293 require 'socket' 294 end 295 INPUT 296 297 assert_separately([], <<-INPUT) 298 class BasicSocket < IO; end 299 assert_nothing_raised do 300 require 'socket' 301 end 302 INPUT 303 end 304 305 def test_define_class_under 306 begin 307 require "zlib" 308 rescue LoadError 309 return 310 end 311 312 assert_separately([], <<-INPUT) 313 module Zlib; end 314 Zlib::Error = 1 315 assert_raise(TypeError) do 316 require 'zlib' 317 end 318 INPUT 319 320 assert_separately([], <<-INPUT) 321 module Zlib; end 322 class Zlib::Error; end 323 assert_raise(TypeError) do 324 require 'zlib' 325 end 326 INPUT 327 328 assert_separately([], <<-INPUT) 329 module Zlib; end 330 class Zlib::Error < StandardError; end 331 assert_nothing_raised do 332 require 'zlib' 333 end 334 INPUT 335 end 336 337 def test_define_module 338 begin 339 require "zlib" 340 rescue LoadError 341 return 342 end 343 344 assert_separately([], <<-INPUT) 345 Zlib = 1 346 assert_raise(TypeError) do 347 require 'zlib' 348 end 349 INPUT 350 end 351 352 def test_define_module_under 353 begin 354 require "socket" 355 rescue LoadError 356 return 357 end 358 359 assert_separately([], <<-INPUT) 360 class BasicSocket < IO; end 361 class Socket < BasicSocket; end 362 Socket::Constants = 1 363 assert_raise(TypeError) do 364 require 'socket' 365 end 366 INPUT 367 end 368 369 def test_load 370 Tempfile.create(["test_ruby_test_require", ".rb"]) {|t| 371 t.puts "module Foo; end" 372 t.puts "at_exit { p :wrap_end }" 373 t.puts "at_exit { raise 'error in at_exit test' }" 374 t.puts "p :ok" 375 t.close 376 377 assert_in_out_err([], <<-INPUT, %w(:ok :end :wrap_end), /error in at_exit test/) 378 load(#{ t.path.dump }, true) 379 GC.start 380 p :end 381 INPUT 382 383 assert_raise(ArgumentError) { at_exit } 384 } 385 end 386 387 def test_load_scope 388 bug1982 = '[ruby-core:25039] [Bug #1982]' 389 Tempfile.create(["test_ruby_test_require", ".rb"]) {|t| 390 t.puts "Hello = 'hello'" 391 t.puts "class Foo" 392 t.puts " p Hello" 393 t.puts "end" 394 t.close 395 396 assert_in_out_err([], <<-INPUT, %w("hello"), [], bug1982) 397 load(#{ t.path.dump }, true) 398 INPUT 399 } 400 end 401 402 def test_load_ospath 403 bug = '[ruby-list:49994] path in ospath' 404 base = "test_load\u{3042 3044 3046 3048 304a}".encode(Encoding::Windows_31J) 405 path = nil 406 Tempfile.create([base, ".rb"]) do |t| 407 path = t.path 408 409 assert_raise_with_message(LoadError, /#{base}/) { 410 load(File.join(File.dirname(path), base)) 411 } 412 413 t.puts "warn 'ok'" 414 t.close 415 assert_include(path, base) 416 assert_warn("ok\n", bug) { 417 assert_nothing_raised(LoadError, bug) { 418 load(path) 419 } 420 } 421 end 422 end 423 424 def test_tainted_loadpath 425 Tempfile.create(["test_ruby_test_require", ".rb"]) {|t| 426 abs_dir, file = File.split(t.path) 427 abs_dir = File.expand_path(abs_dir).untaint 428 429 assert_separately([], <<-INPUT) 430 abs_dir = "#{ abs_dir }" 431 $: << abs_dir 432 assert_nothing_raised {require "#{ file }"} 433 INPUT 434 435 assert_separately([], <<-INPUT) 436 abs_dir = "#{ abs_dir }" 437 $: << abs_dir.taint 438 assert_nothing_raised {require "#{ file }"} 439 INPUT 440 441 assert_separately([], <<-INPUT) 442 abs_dir = "#{ abs_dir }" 443 $: << abs_dir.taint 444 $SAFE = 1 445 assert_raise(SecurityError) {require "#{ file }"} 446 INPUT 447 448 assert_separately([], <<-INPUT) 449 abs_dir = "#{ abs_dir }" 450 $: << abs_dir << 'elsewhere'.taint 451 assert_nothing_raised {require "#{ file }"} 452 INPUT 453 } 454 end 455 456 def test_relative 457 load_path = $:.dup 458 $:.delete(".") 459 Dir.mktmpdir do |tmp| 460 Dir.chdir(tmp) do 461 Dir.mkdir('x') 462 File.open('x/t.rb', 'wb') {} 463 File.open('x/a.rb', 'wb') {|f| f.puts("require_relative('t.rb')")} 464 assert require('./x/t.rb') 465 assert !require(File.expand_path('x/t.rb')) 466 assert_nothing_raised(LoadError) {require('./x/a.rb')} 467 assert_raise(LoadError) {require('x/t.rb')} 468 File.unlink(*Dir.glob('x/*')) 469 Dir.rmdir("#{tmp}/x") 470 $:.replace(load_path) 471 load_path = nil 472 assert(!require('tmpdir')) 473 end 474 end 475 ensure 476 $:.replace(load_path) if load_path 477 end 478 479 def test_relative_symlink 480 Dir.mktmpdir {|tmp| 481 Dir.chdir(tmp) { 482 Dir.mkdir "a" 483 Dir.mkdir "b" 484 File.open("a/lib.rb", "w") {|f| f.puts 'puts "a/lib.rb"' } 485 File.open("b/lib.rb", "w") {|f| f.puts 'puts "b/lib.rb"' } 486 File.open("a/tst.rb", "w") {|f| f.puts 'require_relative "lib"' } 487 begin 488 File.symlink("../a/tst.rb", "b/tst.rb") 489 result = IO.popen([EnvUtil.rubybin, "b/tst.rb"], &:read) 490 assert_equal("a/lib.rb\n", result, "[ruby-dev:40040]") 491 rescue NotImplementedError, Errno::EACCES 492 skip "File.symlink is not implemented" 493 end 494 } 495 } 496 end 497 498 def test_frozen_loaded_features 499 bug3756 = '[ruby-core:31913]' 500 assert_in_out_err(['-e', '$LOADED_FEATURES.freeze; require "ostruct"'], "", 501 [], /\$LOADED_FEATURES is frozen; cannot append feature \(RuntimeError\)$/, 502 bug3756) 503 end 504 505 def test_race_exception 506 bug5754 = '[ruby-core:41618]' 507 path = nil 508 stderr = $stderr 509 verbose = $VERBOSE 510 Tempfile.create(%w"bug5754 .rb") {|tmp| 511 path = tmp.path 512 tmp.print "#{<<~"begin;"}\n#{<<~"end;"}" 513 begin; 514 th = Thread.current 515 t = th[:t] 516 scratch = th[:scratch] 517 518 if scratch.empty? 519 scratch << :pre 520 Thread.pass until t.stop? 521 raise RuntimeError 522 else 523 scratch << :post 524 end 525 end; 526 tmp.close 527 528 class << (output = "") 529 alias write concat 530 end 531 $stderr = output 532 533 start = false 534 535 scratch = [] 536 t1_res = nil 537 t2_res = nil 538 539 t1 = Thread.new do 540 Thread.pass until start 541 begin 542 require(path) 543 rescue RuntimeError 544 end 545 546 t1_res = require(path) 547 end 548 549 t2 = Thread.new do 550 Thread.pass until scratch[0] 551 t2_res = require(path) 552 end 553 554 t1[:scratch] = t2[:scratch] = scratch 555 t1[:t] = t2 556 t2[:t] = t1 557 558 $VERBOSE = true 559 start = true 560 561 assert_nothing_raised(ThreadError, bug5754) {t1.join} 562 assert_nothing_raised(ThreadError, bug5754) {t2.join} 563 564 $VERBOSE = false 565 566 assert_equal(true, (t1_res ^ t2_res), bug5754 + " t1:#{t1_res} t2:#{t2_res}") 567 assert_equal([:pre, :post], scratch, bug5754) 568 569 assert_match(/circular require/, output) 570 assert_match(/in #{__method__}'$/o, output) 571 } 572 ensure 573 $VERBOSE = verbose 574 $stderr = stderr 575 $".delete(path) 576 end 577 578 def test_loaded_features_encoding 579 bug6377 = '[ruby-core:44750]' 580 loadpath = $:.dup 581 features = $".dup 582 $".clear 583 $:.clear 584 Dir.mktmpdir {|tmp| 585 $: << tmp 586 open(File.join(tmp, "foo.rb"), "w") {} 587 require "foo" 588 assert_send([Encoding, :compatible?, tmp, $"[0]], bug6377) 589 } 590 ensure 591 $:.replace(loadpath) 592 $".replace(features) 593 end 594 595 def test_require_changed_current_dir 596 bug7158 = '[ruby-core:47970]' 597 Dir.mktmpdir {|tmp| 598 Dir.chdir(tmp) { 599 Dir.mkdir("a") 600 Dir.mkdir("b") 601 open(File.join("a", "foo.rb"), "w") {} 602 open(File.join("b", "bar.rb"), "w") {|f| 603 f.puts "p :ok" 604 } 605 assert_in_out_err([], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158) 606 begin; 607 $:.replace([IO::NULL]) 608 $: << "." 609 Dir.chdir("a") 610 require "foo" 611 Dir.chdir("../b") 612 p :ng unless require "bar" 613 Dir.chdir("..") 614 p :ng if require "b/bar" 615 end; 616 } 617 } 618 end 619 620 def test_require_not_modified_load_path 621 bug7158 = '[ruby-core:47970]' 622 Dir.mktmpdir {|tmp| 623 Dir.chdir(tmp) { 624 open("foo.rb", "w") {} 625 assert_in_out_err([], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158) 626 begin; 627 $:.replace([IO::NULL]) 628 a = Object.new 629 def a.to_str 630 "#{tmp}" 631 end 632 $: << a 633 require "foo" 634 last_path = $:.pop 635 p :ok if last_path == a && last_path.class == Object 636 end; 637 } 638 } 639 end 640 641 def test_require_changed_home 642 bug7158 = '[ruby-core:47970]' 643 Dir.mktmpdir {|tmp| 644 Dir.chdir(tmp) { 645 open("foo.rb", "w") {} 646 Dir.mkdir("a") 647 open(File.join("a", "bar.rb"), "w") {} 648 assert_in_out_err([], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158) 649 begin; 650 $:.replace([IO::NULL]) 651 $: << '~' 652 ENV['HOME'] = "#{tmp}" 653 require "foo" 654 ENV['HOME'] = "#{tmp}/a" 655 p :ok if require "bar" 656 end; 657 } 658 } 659 end 660 661 def test_require_to_path_redefined_in_load_path 662 bug7158 = '[ruby-core:47970]' 663 Dir.mktmpdir {|tmp| 664 Dir.chdir(tmp) { 665 open("foo.rb", "w") {} 666 assert_in_out_err([{"RUBYOPT"=>nil}, '--disable-gems'], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158) 667 begin; 668 $:.replace([IO::NULL]) 669 a = Object.new 670 def a.to_path 671 "bar" 672 end 673 $: << a 674 begin 675 require "foo" 676 p [:ng, $LOAD_PATH, ENV['RUBYLIB']] 677 rescue LoadError => e 678 raise unless e.path == "foo" 679 end 680 def a.to_path 681 "#{tmp}" 682 end 683 p :ok if require "foo" 684 end; 685 } 686 } 687 end 688 689 def test_require_to_str_redefined_in_load_path 690 bug7158 = '[ruby-core:47970]' 691 Dir.mktmpdir {|tmp| 692 Dir.chdir(tmp) { 693 open("foo.rb", "w") {} 694 assert_in_out_err([{"RUBYOPT"=>nil}, '--disable-gems'], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158) 695 begin; 696 $:.replace([IO::NULL]) 697 a = Object.new 698 def a.to_str 699 "foo" 700 end 701 $: << a 702 begin 703 require "foo" 704 p [:ng, $LOAD_PATH, ENV['RUBYLIB']] 705 rescue LoadError => e 706 raise unless e.path == "foo" 707 end 708 def a.to_str 709 "#{tmp}" 710 end 711 p :ok if require "foo" 712 end; 713 } 714 } 715 end 716 717 def assert_require_with_shared_array_modified(add, del) 718 bug7383 = '[ruby-core:49518]' 719 Dir.mktmpdir {|tmp| 720 Dir.chdir(tmp) { 721 open("foo.rb", "w") {} 722 Dir.mkdir("a") 723 open(File.join("a", "bar.rb"), "w") {} 724 assert_in_out_err(['--disable-gems'], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7383) 725 begin; 726 $:.replace([IO::NULL]) 727 $:.#{add} "#{tmp}" 728 $:.#{add} "#{tmp}/a" 729 require "foo" 730 $:.#{del} 731 # Expanded load path cache should be rebuilt. 732 begin 733 require "bar" 734 rescue LoadError => e 735 if e.path == "bar" 736 p :ok 737 else 738 raise 739 end 740 end 741 end; 742 } 743 } 744 end 745 746 def test_require_with_array_pop 747 assert_require_with_shared_array_modified("push", "pop") 748 end 749 750 def test_require_with_array_shift 751 assert_require_with_shared_array_modified("unshift", "shift") 752 end 753 754 def test_require_local_var_on_toplevel 755 bug7536 = '[ruby-core:50701]' 756 Dir.mktmpdir {|tmp| 757 Dir.chdir(tmp) { 758 open("bar.rb", "w") {|f| f.puts 'TOPLEVEL_BINDING.eval("lib = 2")' } 759 assert_in_out_err(%w[-r./bar.rb], "#{<<~"begin;"}\n#{<<~"end;"}", %w([:lib] 2), [], bug7536) 760 begin; 761 puts TOPLEVEL_BINDING.eval("local_variables").inspect 762 puts TOPLEVEL_BINDING.eval("lib").inspect 763 end; 764 } 765 } 766 end 767 768 def test_require_with_loaded_features_pop 769 bug7530 = '[ruby-core:50645]' 770 Tempfile.create(%w'bug-7530- .rb') {|script| 771 script.close 772 assert_in_out_err([{"RUBYOPT" => nil}, "-", script.path], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7530, timeout: 60) 773 begin; 774 PATH = ARGV.shift 775 THREADS = 4 776 ITERATIONS_PER_THREAD = 1000 777 778 THREADS.times.map { 779 Thread.new do 780 ITERATIONS_PER_THREAD.times do 781 require PATH 782 $".delete_if {|p| Regexp.new(PATH) =~ p} 783 end 784 end 785 }.each(&:join) 786 p :ok 787 end; 788 } 789 end 790 791 def test_loading_fifo_threading_raise 792 Tempfile.create(%w'fifo .rb') {|f| 793 f.close 794 File.unlink(f.path) 795 File.mkfifo(f.path) 796 assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 3) 797 begin; 798 th = Thread.current 799 Thread.start {begin sleep(0.001) end until th.stop?; th.raise(IOError)} 800 assert_raise(IOError) do 801 load(ARGV[0]) 802 end 803 end; 804 } 805 end if File.respond_to?(:mkfifo) 806 807 def test_loading_fifo_threading_success 808 Tempfile.create(%w'fifo .rb') {|f| 809 f.close 810 File.unlink(f.path) 811 File.mkfifo(f.path) 812 813 assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 3) 814 begin; 815 path = ARGV[0] 816 th = Thread.current 817 $ok = false 818 Thread.start { 819 begin 820 sleep(0.001) 821 end until th.stop? 822 open(path, File::WRONLY | File::NONBLOCK) {|fifo_w| 823 fifo_w.print "$ok = true\n__END__\n" # ensure finishing 824 } 825 } 826 827 load(path) 828 assert($ok) 829 end; 830 } 831 end if File.respond_to?(:mkfifo) 832 833 def test_loading_fifo_fd_leak 834 Tempfile.create(%w'fifo .rb') {|f| 835 f.close 836 File.unlink(f.path) 837 File.mkfifo(f.path) 838 assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 3) 839 begin; 840 Process.setrlimit(Process::RLIMIT_NOFILE, 50) 841 th = Thread.current 842 100.times do |i| 843 Thread.start {begin sleep(0.001) end until th.stop?; th.raise(IOError)} 844 assert_raise(IOError, "\#{i} time") do 845 begin 846 tap {tap {tap {load(ARGV[0])}}} 847 rescue LoadError 848 GC.start 849 retry 850 end 851 end 852 end 853 end; 854 } 855 end if File.respond_to?(:mkfifo) and defined?(Process::RLIMIT_NOFILE) 856 857 def test_throw_while_loading 858 Tempfile.create(%w'bug-11404 .rb') do |f| 859 f.puts 'sleep' 860 f.close 861 862 assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~'end;'}") 863 begin; 864 path = ARGV[0] 865 class Error < RuntimeError 866 def exception(*) 867 begin 868 throw :blah 869 rescue UncaughtThrowError 870 end 871 self 872 end 873 end 874 875 assert_throw(:blah) do 876 x = Thread.current 877 Thread.start { 878 sleep 0.00001 879 x.raise Error.new 880 } 881 load path 882 end 883 end; 884 end 885 end 886 887 def test_symlink_load_path 888 Dir.mktmpdir {|tmp| 889 Dir.mkdir(File.join(tmp, "real")) 890 begin 891 File.symlink "real", File.join(tmp, "symlink") 892 rescue NotImplementedError, Errno::EACCES 893 skip "File.symlink is not implemented" 894 end 895 File.write(File.join(tmp, "real/a.rb"), "print __FILE__") 896 result = IO.popen([EnvUtil.rubybin, "-I#{tmp}/symlink", "-e", "require 'a.rb'"], &:read) 897 assert_operator(result, :end_with?, "/real/a.rb") 898 } 899 end 900end 901