1# frozen_string_literal: false 2require "test/unit" 3require "fileutils" 4require "tmpdir" 5require "socket" 6require '-test-/file' 7 8class TestFileExhaustive < Test::Unit::TestCase 9 DRIVE = Dir.pwd[%r'\A(?:[a-z]:|//[^/]+/[^/]+)'i] 10 POSIX = /cygwin|mswin|bccwin|mingw|emx/ !~ RUBY_PLATFORM 11 NTFS = !(/mingw|mswin|bccwin/ !~ RUBY_PLATFORM) 12 13 def assert_incompatible_encoding 14 d = "\u{3042}\u{3044}".encode("utf-16le") 15 assert_raise(Encoding::CompatibilityError) {yield d} 16 m = Class.new {define_method(:to_path) {d}} 17 assert_raise(Encoding::CompatibilityError) {yield m.new} 18 end 19 20 def setup 21 @dir = Dir.mktmpdir("rubytest-file") 22 File.chown(-1, Process.gid, @dir) 23 end 24 25 def teardown 26 GC.start 27 FileUtils.remove_entry_secure @dir 28 end 29 30 def make_tmp_filename(prefix) 31 "#{@dir}/#{prefix}.test" 32 end 33 34 def rootdir 35 return @rootdir if defined? @rootdir 36 @rootdir = "#{DRIVE}/" 37 @rootdir 38 end 39 40 def nofile 41 return @nofile if defined? @nofile 42 @nofile = make_tmp_filename("nofile") 43 @nofile 44 end 45 46 def make_file(content, file) 47 open(file, "w") {|fh| fh << content } 48 end 49 50 def zerofile 51 return @zerofile if defined? @zerofile 52 @zerofile = make_tmp_filename("zerofile") 53 make_file("", @zerofile) 54 @zerofile 55 end 56 57 def regular_file 58 return @file if defined? @file 59 @file = make_tmp_filename("file") 60 make_file("foo", @file) 61 @file 62 end 63 64 def utf8_file 65 return @utf8file if defined? @utf8file 66 @utf8file = make_tmp_filename("\u3066\u3059\u3068") 67 make_file("foo", @utf8file) 68 @utf8file 69 end 70 71 def notownedfile 72 return @notownedfile if defined? @notownedfile 73 if Process.euid != 0 74 @notownedfile = '/' 75 else 76 @notownedfile = nil 77 end 78 @notownedfile 79 end 80 81 def suidfile 82 return @suidfile if defined? @suidfile 83 if POSIX 84 @suidfile = make_tmp_filename("suidfile") 85 make_file("", @suidfile) 86 File.chmod 04500, @suidfile 87 @suidfile 88 else 89 @suidfile = nil 90 end 91 end 92 93 def sgidfile 94 return @sgidfile if defined? @sgidfile 95 if POSIX 96 @sgidfile = make_tmp_filename("sgidfile") 97 make_file("", @sgidfile) 98 File.chmod 02500, @sgidfile 99 @sgidfile 100 else 101 @sgidfile = nil 102 end 103 end 104 105 def stickyfile 106 return @stickyfile if defined? @stickyfile 107 if POSIX 108 @stickyfile = make_tmp_filename("stickyfile") 109 Dir.mkdir(@stickyfile) 110 File.chmod 01500, @stickyfile 111 @stickyfile 112 else 113 @stickyfile = nil 114 end 115 end 116 117 def symlinkfile 118 return @symlinkfile if defined? @symlinkfile 119 @symlinkfile = make_tmp_filename("symlinkfile") 120 begin 121 File.symlink(regular_file, @symlinkfile) 122 rescue NotImplementedError, Errno::EACCES, Errno::EPERM 123 @symlinkfile = nil 124 end 125 @symlinkfile 126 end 127 128 def hardlinkfile 129 return @hardlinkfile if defined? @hardlinkfile 130 @hardlinkfile = make_tmp_filename("hardlinkfile") 131 begin 132 File.link(regular_file, @hardlinkfile) 133 rescue NotImplementedError, Errno::EINVAL # EINVAL for Windows Vista 134 @hardlinkfile = nil 135 end 136 @hardlinkfile 137 end 138 139 def fifo 140 return @fifo if defined? @fifo 141 if POSIX 142 fn = make_tmp_filename("fifo") 143 File.mkfifo(fn) 144 @fifo = fn 145 else 146 @fifo = nil 147 end 148 @fifo 149 end 150 151 def socket 152 return @socket if defined? @socket 153 if defined? UNIXServer 154 socket = make_tmp_filename("s") 155 UNIXServer.open(socket).close 156 @socket = socket 157 else 158 @socket = nil 159 end 160 end 161 162 def chardev 163 return @chardev if defined? @chardev 164 @chardev = File::NULL == "/dev/null" ? "/dev/null" : nil 165 @chardev 166 end 167 168 def blockdev 169 return @blockdev if defined? @blockdev 170 if /linux/ =~ RUBY_PLATFORM 171 @blockdev = %w[/dev/loop0 /dev/sda /dev/vda /dev/xvda1].find {|f| File.exist? f } 172 else 173 @blockdev = nil 174 end 175 @blockdev 176 end 177 178 def test_path 179 [regular_file, utf8_file].each do |file| 180 assert_equal(file, File.open(file) {|f| f.path}) 181 assert_equal(file, File.path(file)) 182 o = Object.new 183 class << o; self; end.class_eval do 184 define_method(:to_path) { file } 185 end 186 assert_equal(file, File.path(o)) 187 end 188 end 189 190 def assert_integer(n) 191 assert_kind_of(Integer, n) 192 end 193 194 def assert_integer_or_nil(n) 195 msg = ->{"#{n.inspect} is neither Integer nor nil."} 196 if n 197 assert_kind_of(Integer, n, msg) 198 else 199 assert_nil(n, msg) 200 end 201 end 202 203 def test_stat 204 fn1 = regular_file 205 hardlinkfile 206 sleep(1.1) 207 fn2 = fn1 + "2" 208 make_file("foo", fn2) 209 fs1, fs2 = File.stat(fn1), File.stat(fn2) 210 assert_nothing_raised do 211 assert_equal(0, fs1 <=> fs1) 212 assert_equal(-1, fs1 <=> fs2) 213 assert_equal(1, fs2 <=> fs1) 214 assert_nil(fs1 <=> nil) 215 assert_integer(fs1.dev) 216 assert_integer_or_nil(fs1.rdev) 217 assert_integer_or_nil(fs1.dev_major) 218 assert_integer_or_nil(fs1.dev_minor) 219 assert_integer_or_nil(fs1.rdev_major) 220 assert_integer_or_nil(fs1.rdev_minor) 221 assert_integer(fs1.ino) 222 assert_integer(fs1.mode) 223 unless /emx|mswin|mingw/ =~ RUBY_PLATFORM 224 # on Windows, nlink is always 1. but this behavior will be changed 225 # in the future. 226 assert_equal(hardlinkfile ? 2 : 1, fs1.nlink) 227 end 228 assert_integer(fs1.uid) 229 assert_integer(fs1.gid) 230 assert_equal(3, fs1.size) 231 assert_integer_or_nil(fs1.blksize) 232 assert_integer_or_nil(fs1.blocks) 233 assert_kind_of(Time, fs1.atime) 234 assert_kind_of(Time, fs1.mtime) 235 assert_kind_of(Time, fs1.ctime) 236 assert_kind_of(String, fs1.inspect) 237 end 238 assert_raise(Errno::ENOENT) { File.stat(nofile) } 239 assert_kind_of(File::Stat, File.open(fn1) {|f| f.stat}) 240 assert_raise(Errno::ENOENT) { File.lstat(nofile) } 241 assert_kind_of(File::Stat, File.open(fn1) {|f| f.lstat}) 242 end 243 244 def test_stat_drive_root 245 assert_nothing_raised { File.stat(DRIVE + "/") } 246 assert_nothing_raised { File.stat(DRIVE + "/.") } 247 assert_nothing_raised { File.stat(DRIVE + "/..") } 248 assert_raise(Errno::ENOENT) { File.stat(DRIVE + "/...") } 249 # want to test the root of empty drive, but there is no method to test it... 250 end if DRIVE 251 252 def test_stat_dotted_prefix 253 Dir.mktmpdir do |dir| 254 prefix = File.join(dir, "...a") 255 Dir.mkdir(prefix) 256 assert_file.exist?(prefix) 257 258 assert_nothing_raised { File.stat(prefix) } 259 260 Dir.chdir(dir) do 261 assert_nothing_raised { File.stat(File.basename(prefix)) } 262 end 263 end 264 end if NTFS 265 266 def test_lstat 267 return unless symlinkfile 268 assert_equal(false, File.stat(symlinkfile).symlink?) 269 assert_equal(true, File.lstat(symlinkfile).symlink?) 270 f = File.new(symlinkfile) 271 assert_equal(false, f.stat.symlink?) 272 assert_equal(true, f.lstat.symlink?) 273 f.close 274 end 275 276 def test_directory_p 277 assert_file.directory?(@dir) 278 assert_file.not_directory?(@dir+"/...") 279 assert_file.not_directory?(regular_file) 280 assert_file.not_directory?(utf8_file) 281 assert_file.not_directory?(nofile) 282 end 283 284 def test_pipe_p 285 assert_file.not_pipe?(@dir) 286 assert_file.not_pipe?(regular_file) 287 assert_file.not_pipe?(utf8_file) 288 assert_file.not_pipe?(nofile) 289 assert_file.pipe?(fifo) if fifo 290 end 291 292 def test_symlink_p 293 assert_file.not_symlink?(@dir) 294 assert_file.not_symlink?(regular_file) 295 assert_file.not_symlink?(utf8_file) 296 assert_file.symlink?(symlinkfile) if symlinkfile 297 assert_file.not_symlink?(hardlinkfile) if hardlinkfile 298 assert_file.not_symlink?(nofile) 299 end 300 301 def test_socket_p 302 assert_file.not_socket?(@dir) 303 assert_file.not_socket?(regular_file) 304 assert_file.not_socket?(utf8_file) 305 assert_file.not_socket?(nofile) 306 assert_file.socket?(socket) if socket 307 end 308 309 def test_blockdev_p 310 assert_file.not_blockdev?(@dir) 311 assert_file.not_blockdev?(regular_file) 312 assert_file.not_blockdev?(utf8_file) 313 assert_file.not_blockdev?(nofile) 314 assert_file.blockdev?(blockdev) if blockdev 315 end 316 317 def test_chardev_p 318 assert_file.not_chardev?(@dir) 319 assert_file.not_chardev?(regular_file) 320 assert_file.not_chardev?(utf8_file) 321 assert_file.not_chardev?(nofile) 322 assert_file.chardev?(chardev) if chardev 323 end 324 325 def test_exist_p 326 assert_file.exist?(@dir) 327 assert_file.exist?(regular_file) 328 assert_file.exist?(utf8_file) 329 assert_file.not_exist?(nofile) 330 end 331 332 def test_readable_p 333 return if Process.euid == 0 334 File.chmod(0200, regular_file) 335 assert_file.not_readable?(regular_file) 336 File.chmod(0600, regular_file) 337 assert_file.readable?(regular_file) 338 339 File.chmod(0200, utf8_file) 340 assert_file.not_readable?(utf8_file) 341 File.chmod(0600, utf8_file) 342 assert_file.readable?(utf8_file) 343 344 assert_file.not_readable?(nofile) 345 end if POSIX 346 347 def test_readable_real_p 348 return if Process.euid == 0 349 File.chmod(0200, regular_file) 350 assert_file.not_readable_real?(regular_file) 351 File.chmod(0600, regular_file) 352 assert_file.readable_real?(regular_file) 353 354 File.chmod(0200, utf8_file) 355 assert_file.not_readable_real?(utf8_file) 356 File.chmod(0600, utf8_file) 357 assert_file.readable_real?(utf8_file) 358 359 assert_file.not_readable_real?(nofile) 360 end if POSIX 361 362 def test_world_readable_p 363 File.chmod(0006, regular_file) 364 assert_file.world_readable?(regular_file) 365 File.chmod(0060, regular_file) 366 assert_file.not_world_readable?(regular_file) 367 File.chmod(0600, regular_file) 368 assert_file.not_world_readable?(regular_file) 369 370 File.chmod(0006, utf8_file) 371 assert_file.world_readable?(utf8_file) 372 File.chmod(0060, utf8_file) 373 assert_file.not_world_readable?(utf8_file) 374 File.chmod(0600, utf8_file) 375 assert_file.not_world_readable?(utf8_file) 376 377 assert_file.not_world_readable?(nofile) 378 end if POSIX 379 380 def test_writable_p 381 return if Process.euid == 0 382 File.chmod(0400, regular_file) 383 assert_file.not_writable?(regular_file) 384 File.chmod(0600, regular_file) 385 assert_file.writable?(regular_file) 386 387 File.chmod(0400, utf8_file) 388 assert_file.not_writable?(utf8_file) 389 File.chmod(0600, utf8_file) 390 assert_file.writable?(utf8_file) 391 392 assert_file.not_writable?(nofile) 393 end if POSIX 394 395 def test_writable_real_p 396 return if Process.euid == 0 397 File.chmod(0400, regular_file) 398 assert_file.not_writable_real?(regular_file) 399 File.chmod(0600, regular_file) 400 assert_file.writable_real?(regular_file) 401 402 File.chmod(0400, utf8_file) 403 assert_file.not_writable_real?(utf8_file) 404 File.chmod(0600, utf8_file) 405 assert_file.writable_real?(utf8_file) 406 407 assert_file.not_writable_real?(nofile) 408 end if POSIX 409 410 def test_world_writable_p 411 File.chmod(0006, regular_file) 412 assert_file.world_writable?(regular_file) 413 File.chmod(0060, regular_file) 414 assert_file.not_world_writable?(regular_file) 415 File.chmod(0600, regular_file) 416 assert_file.not_world_writable?(regular_file) 417 418 File.chmod(0006, utf8_file) 419 assert_file.world_writable?(utf8_file) 420 File.chmod(0060, utf8_file) 421 assert_file.not_world_writable?(utf8_file) 422 File.chmod(0600, utf8_file) 423 assert_file.not_world_writable?(utf8_file) 424 425 assert_file.not_world_writable?(nofile) 426 end if POSIX 427 428 def test_executable_p 429 File.chmod(0100, regular_file) 430 assert_file.executable?(regular_file) 431 File.chmod(0600, regular_file) 432 assert_file.not_executable?(regular_file) 433 434 File.chmod(0100, utf8_file) 435 assert_file.executable?(utf8_file) 436 File.chmod(0600, utf8_file) 437 assert_file.not_executable?(utf8_file) 438 439 assert_file.not_executable?(nofile) 440 end if POSIX 441 442 def test_executable_real_p 443 File.chmod(0100, regular_file) 444 assert_file.executable_real?(regular_file) 445 File.chmod(0600, regular_file) 446 assert_file.not_executable_real?(regular_file) 447 448 File.chmod(0100, utf8_file) 449 assert_file.executable_real?(utf8_file) 450 File.chmod(0600, utf8_file) 451 assert_file.not_executable_real?(utf8_file) 452 453 assert_file.not_executable_real?(nofile) 454 end if POSIX 455 456 def test_file_p 457 assert_file.not_file?(@dir) 458 assert_file.file?(regular_file) 459 assert_file.file?(utf8_file) 460 assert_file.not_file?(nofile) 461 end 462 463 def test_zero_p 464 assert_nothing_raised { File.zero?(@dir) } 465 assert_file.not_zero?(regular_file) 466 assert_file.not_zero?(utf8_file) 467 assert_file.zero?(zerofile) 468 assert_file.not_zero?(nofile) 469 end 470 471 def test_empty_p 472 assert_nothing_raised { File.empty?(@dir) } 473 assert_file.not_empty?(regular_file) 474 assert_file.not_empty?(utf8_file) 475 assert_file.empty?(zerofile) 476 assert_file.not_empty?(nofile) 477 end 478 479 def test_size_p 480 assert_nothing_raised { File.size?(@dir) } 481 assert_equal(3, File.size?(regular_file)) 482 assert_equal(3, File.size?(utf8_file)) 483 assert_file.not_size?(zerofile) 484 assert_file.not_size?(nofile) 485 end 486 487 def test_owned_p 488 assert_file.owned?(regular_file) 489 assert_file.owned?(utf8_file) 490 assert_file.not_owned?(notownedfile) if notownedfile 491 end if POSIX 492 493 def test_grpowned_p ## xxx 494 assert_file.grpowned?(regular_file) 495 assert_file.grpowned?(utf8_file) 496 end if POSIX 497 498 def io_open(file_name) 499 # avoid File.open since we do not want #to_path 500 io = IO.for_fd(IO.sysopen(file_name)) 501 yield io 502 ensure 503 io&.close 504 end 505 506 def test_suid 507 assert_file.not_setuid?(regular_file) 508 assert_file.not_setuid?(utf8_file) 509 if suidfile 510 assert_file.setuid?(suidfile) 511 io_open(suidfile) { |io| assert_file.setuid?(io) } 512 end 513 end 514 515 def test_sgid 516 assert_file.not_setgid?(regular_file) 517 assert_file.not_setgid?(utf8_file) 518 if sgidfile 519 assert_file.setgid?(sgidfile) 520 io_open(sgidfile) { |io| assert_file.setgid?(io) } 521 end 522 end 523 524 def test_sticky 525 assert_file.not_sticky?(regular_file) 526 assert_file.not_sticky?(utf8_file) 527 if stickyfile 528 assert_file.sticky?(stickyfile) 529 io_open(stickyfile) { |io| assert_file.sticky?(io) } 530 end 531 end 532 533 def test_path_identical_p 534 assert_file.identical?(regular_file, regular_file) 535 assert_file.not_identical?(regular_file, zerofile) 536 assert_file.not_identical?(regular_file, nofile) 537 assert_file.not_identical?(nofile, regular_file) 538 end 539 540 def path_identical_p(file) 541 [regular_file, utf8_file].each do |file| 542 assert_file.identical?(file, file) 543 assert_file.not_identical?(file, zerofile) 544 assert_file.not_identical?(file, nofile) 545 assert_file.not_identical?(nofile, file) 546 end 547 end 548 549 def test_io_identical_p 550 [regular_file, utf8_file].each do |file| 551 open(file) {|f| 552 assert_file.identical?(f, f) 553 assert_file.identical?(file, f) 554 assert_file.identical?(f, file) 555 } 556 end 557 end 558 559 def test_closed_io_identical_p 560 [regular_file, utf8_file].each do |file| 561 io = open(file) {|f| f} 562 assert_raise(IOError) { 563 File.identical?(file, io) 564 } 565 File.unlink(file) 566 assert_file.not_exist?(file) 567 end 568 end 569 570 def test_s_size 571 assert_integer(File.size(@dir)) 572 assert_equal(3, File.size(regular_file)) 573 assert_equal(3, File.size(utf8_file)) 574 assert_equal(0, File.size(zerofile)) 575 assert_raise(Errno::ENOENT) { File.size(nofile) } 576 end 577 578 def test_ftype 579 assert_equal("directory", File.ftype(@dir)) 580 assert_equal("file", File.ftype(regular_file)) 581 assert_equal("file", File.ftype(utf8_file)) 582 assert_equal("link", File.ftype(symlinkfile)) if symlinkfile 583 assert_equal("file", File.ftype(hardlinkfile)) if hardlinkfile 584 assert_raise(Errno::ENOENT) { File.ftype(nofile) } 585 end 586 587 def test_atime 588 [regular_file, utf8_file].each do |file| 589 t1 = File.atime(file) 590 t2 = File.open(file) {|f| f.atime} 591 assert_kind_of(Time, t1) 592 assert_kind_of(Time, t2) 593 # High Sierra's APFS can handle nano-sec precise. 594 # t1 value is difference from t2 on APFS. 595 if Bug::File::Fs.fsname(Dir.tmpdir) == "apfs" 596 assert_equal(t1.to_i, t2.to_i) 597 else 598 assert_equal(t1, t2) 599 end 600 end 601 assert_raise(Errno::ENOENT) { File.atime(nofile) } 602 end 603 604 def test_mtime 605 [regular_file, utf8_file].each do |file| 606 t1 = File.mtime(file) 607 t2 = File.open(file) {|f| f.mtime} 608 assert_kind_of(Time, t1) 609 assert_kind_of(Time, t2) 610 assert_equal(t1, t2) 611 end 612 assert_raise(Errno::ENOENT) { File.mtime(nofile) } 613 end 614 615 def test_ctime 616 [regular_file, utf8_file].each do |file| 617 t1 = File.ctime(file) 618 t2 = File.open(file) {|f| f.ctime} 619 assert_kind_of(Time, t1) 620 assert_kind_of(Time, t2) 621 assert_equal(t1, t2) 622 end 623 assert_raise(Errno::ENOENT) { File.ctime(nofile) } 624 end 625 626 def test_chmod 627 [regular_file, utf8_file].each do |file| 628 assert_equal(1, File.chmod(0444, file)) 629 assert_equal(0444, File.stat(file).mode % 01000) 630 assert_equal(0, File.open(file) {|f| f.chmod(0222)}) 631 assert_equal(0222, File.stat(file).mode % 01000) 632 File.chmod(0600, file) 633 end 634 assert_raise(Errno::ENOENT) { File.chmod(0600, nofile) } 635 end if POSIX 636 637 def test_lchmod 638 [regular_file, utf8_file].each do |file| 639 assert_equal(1, File.lchmod(0444, file)) 640 assert_equal(0444, File.stat(file).mode % 01000) 641 File.lchmod(0600, regular_file) 642 end 643 assert_raise(Errno::ENOENT) { File.lchmod(0600, nofile) } 644 rescue NotImplementedError 645 end if POSIX 646 647 def test_chown ## xxx 648 end 649 650 def test_lchown ## xxx 651 end 652 653 def test_symlink 654 return unless symlinkfile 655 assert_equal("link", File.ftype(symlinkfile)) 656 assert_raise(Errno::EEXIST) { File.symlink(regular_file, regular_file) } 657 assert_raise(Errno::EEXIST) { File.symlink(utf8_file, utf8_file) } 658 end 659 660 def test_utime 661 t = Time.local(2000) 662 File.utime(t + 1, t + 2, zerofile) 663 assert_equal(t + 1, File.atime(zerofile)) 664 assert_equal(t + 2, File.mtime(zerofile)) 665 end 666 667 def test_utime_symlinkfile 668 return unless symlinkfile 669 t = Time.local(2000) 670 stat = File.lstat(symlinkfile) 671 assert_equal(1, File.utime(t, t, symlinkfile)) 672 assert_equal(t, File.stat(regular_file).atime) 673 assert_equal(t, File.stat(regular_file).mtime) 674 end 675 676 def test_lutime 677 return unless File.respond_to?(:lutime) 678 return unless symlinkfile 679 680 r = File.stat(regular_file) 681 t = Time.local(2000) 682 File.lutime(t + 1, t + 2, symlinkfile) 683 rescue NotImplementedError => e 684 skip(e.message) 685 else 686 stat = File.stat(regular_file) 687 assert_equal(r.atime, stat.atime) 688 assert_equal(r.mtime, stat.mtime) 689 690 stat = File.lstat(symlinkfile) 691 assert_equal(t + 1, stat.atime) 692 assert_equal(t + 2, stat.mtime) 693 end 694 695 def test_hardlink 696 return unless hardlinkfile 697 assert_equal("file", File.ftype(hardlinkfile)) 698 assert_raise(Errno::EEXIST) { File.link(regular_file, regular_file) } 699 assert_raise(Errno::EEXIST) { File.link(utf8_file, utf8_file) } 700 end 701 702 def test_readlink 703 return unless symlinkfile 704 assert_equal(regular_file, File.readlink(symlinkfile)) 705 assert_raise(Errno::EINVAL) { File.readlink(regular_file) } 706 assert_raise(Errno::EINVAL) { File.readlink(utf8_file) } 707 assert_raise(Errno::ENOENT) { File.readlink(nofile) } 708 if fs = Encoding.find("filesystem") 709 assert_equal(fs, File.readlink(symlinkfile).encoding) 710 end 711 rescue NotImplementedError 712 end 713 714 def test_readlink_long_path 715 return unless symlinkfile 716 bug9157 = '[ruby-core:58592] [Bug #9157]' 717 assert_separately(["-", symlinkfile, bug9157], "#{<<~begin}#{<<~"end;"}") 718 begin 719 symlinkfile, bug9157 = *ARGV 720 100.step(1000, 100) do |n| 721 File.unlink(symlinkfile) 722 link = "foo"*n 723 begin 724 File.symlink(link, symlinkfile) 725 rescue Errno::ENAMETOOLONG 726 break 727 end 728 assert_equal(link, File.readlink(symlinkfile), bug9157) 729 end 730 end; 731 end 732 733 if NTFS 734 def test_readlink_junction 735 base = File.basename(nofile) 736 err = IO.popen(%W"cmd.exe /c mklink /j #{base} .", chdir: @dir, err: %i[child out], &:read) 737 skip err unless $?.success? 738 assert_equal(@dir, File.readlink(nofile)) 739 end 740 741 def test_realpath_mount_point 742 vol = IO.popen(["mountvol", DRIVE, "/l"], &:read).strip 743 Dir.mkdir(mnt = File.join(@dir, mntpnt = "mntpnt")) 744 system("mountvol", mntpnt, vol, chdir: @dir) 745 assert_equal(mnt, File.realpath(mnt)) 746 ensure 747 system("mountvol", mntpnt, "/d", chdir: @dir) 748 end 749 end 750 751 def test_unlink 752 assert_equal(1, File.unlink(regular_file)) 753 make_file("foo", regular_file) 754 755 assert_equal(1, File.unlink(utf8_file)) 756 make_file("foo", utf8_file) 757 758 assert_raise(Errno::ENOENT) { File.unlink(nofile) } 759 end 760 761 def test_rename 762 [regular_file, utf8_file].each do |file| 763 assert_equal(0, File.rename(file, nofile)) 764 assert_file.not_exist?(file) 765 assert_file.exist?(nofile) 766 assert_equal(0, File.rename(nofile, file)) 767 assert_raise(Errno::ENOENT) { File.rename(nofile, file) } 768 end 769 end 770 771 def test_umask 772 prev = File.umask(0777) 773 assert_equal(0777, File.umask) 774 open(nofile, "w") { } 775 assert_equal(0, File.stat(nofile).mode % 01000) 776 File.unlink(nofile) 777 assert_equal(0777, File.umask(prev)) 778 assert_raise(ArgumentError) { File.umask(0, 1, 2) } 779 end if POSIX 780 781 def test_expand_path 782 assert_equal(regular_file, File.expand_path(File.basename(regular_file), File.dirname(regular_file))) 783 assert_equal(utf8_file, File.expand_path(File.basename(utf8_file), File.dirname(utf8_file))) 784 end 785 786 if NTFS 787 def test_expand_path_ntfs 788 [regular_file, utf8_file].each do |file| 789 assert_equal(file, File.expand_path(file + " ")) 790 assert_equal(file, File.expand_path(file + ".")) 791 assert_equal(file, File.expand_path(file + "::$DATA")) 792 end 793 assert_match(/\Ac:\//i, File.expand_path('c:'), '[ruby-core:31591]') 794 assert_match(/\Ac:\//i, File.expand_path('c:foo', 'd:/bar')) 795 assert_match(/\Ae:\//i, File.expand_path('e:foo', 'd:/bar')) 796 assert_match(%r'\Ac:/bar/foo\z'i, File.expand_path('c:foo', 'c:/bar')) 797 end 798 end 799 800 case RUBY_PLATFORM 801 when /darwin/ 802 def test_expand_path_hfs 803 ["\u{feff}", *"\u{2000}"..."\u{2100}"].each do |c| 804 file = regular_file + c 805 full_path = File.expand_path(file) 806 mesg = proc {File.basename(full_path).dump} 807 begin 808 open(file) {} 809 rescue 810 # High Sierra's APFS cannot use filenames with undefined character 811 next if Bug::File::Fs.fsname(Dir.tmpdir) == "apfs" 812 assert_equal(file, full_path, mesg) 813 else 814 assert_equal(regular_file, full_path, mesg) 815 end 816 end 817 end 818 end 819 820 if DRIVE 821 def test_expand_path_absolute 822 assert_match(%r"\Az:/foo\z"i, File.expand_path('/foo', "z:/bar")) 823 assert_match(%r"\A//host/share/foo\z"i, File.expand_path('/foo', "//host/share/bar")) 824 assert_match(%r"\A#{DRIVE}/foo\z"i, File.expand_path('/foo')) 825 end 826 else 827 def test_expand_path_absolute 828 assert_equal("/foo", File.expand_path('/foo')) 829 end 830 end 831 832 def test_expand_path_memsize 833 bug9934 = '[ruby-core:63114] [Bug #9934]' 834 require "objspace" 835 path = File.expand_path("/foo") 836 assert_operator(ObjectSpace.memsize_of(path), :<=, path.bytesize + GC::INTERNAL_CONSTANTS[:RVALUE_SIZE], bug9934) 837 path = File.expand_path("/a"*25) 838 assert_equal(path.bytesize+1 + GC::INTERNAL_CONSTANTS[:RVALUE_SIZE], ObjectSpace.memsize_of(path), bug9934) 839 end 840 841 def test_expand_path_encoding 842 drive = (DRIVE ? 'C:' : '') 843 if Encoding.find("filesystem") == Encoding::CP1251 844 a = "#{drive}/\u3042\u3044\u3046\u3048\u304a".encode("cp932") 845 else 846 a = "#{drive}/\u043f\u0440\u0438\u0432\u0435\u0442".encode("cp1251") 847 end 848 assert_equal(a, File.expand_path(a)) 849 a = "#{drive}/\225\\\\" 850 if File::ALT_SEPARATOR == '\\' 851 [%W"cp437 #{drive}/\225", %W"cp932 #{drive}/\225\\"] 852 elsif File.directory?("#{@dir}/\\") 853 [%W"cp437 /\225", %W"cp932 /\225\\"] 854 else 855 [["cp437", a], ["cp932", a]] 856 end.each do |cp, expected| 857 assert_equal(expected.force_encoding(cp), File.expand_path(a.dup.force_encoding(cp)), cp) 858 end 859 860 path = "\u3042\u3044\u3046\u3048\u304a".encode("EUC-JP") 861 assert_equal("#{Dir.pwd}/#{path}".encode("CP932"), File.expand_path(path).encode("CP932")) 862 863 path = "\u3042\u3044\u3046\u3048\u304a".encode("CP51932") 864 assert_equal("#{Dir.pwd}/#{path}", File.expand_path(path)) 865 866 assert_incompatible_encoding {|d| File.expand_path(d)} 867 end 868 869 def test_expand_path_encoding_filesystem 870 home = ENV["HOME"] 871 ENV["HOME"] = "#{DRIVE}/UserHome" 872 873 path = "~".encode("US-ASCII") 874 dir = "C:/".encode("IBM437") 875 fs = Encoding.find("filesystem") 876 877 assert_equal fs, File.expand_path(path).encoding 878 assert_equal fs, File.expand_path(path, dir).encoding 879 ensure 880 ENV["HOME"] = home 881 end 882 883 UnknownUserHome = "~foo_bar_baz_unknown_user_wahaha".freeze 884 885 def test_expand_path_home 886 assert_kind_of(String, File.expand_path("~")) if ENV["HOME"] 887 assert_raise(ArgumentError) { File.expand_path(UnknownUserHome) } 888 assert_raise(ArgumentError) { File.expand_path(UnknownUserHome, "/") } 889 begin 890 bug3630 = '[ruby-core:31537]' 891 home = ENV["HOME"] 892 home_drive = ENV["HOMEDRIVE"] 893 home_path = ENV["HOMEPATH"] 894 user_profile = ENV["USERPROFILE"] 895 ENV["HOME"] = nil 896 ENV["HOMEDRIVE"] = nil 897 ENV["HOMEPATH"] = nil 898 ENV["USERPROFILE"] = nil 899 ENV["HOME"] = "~" 900 assert_raise(ArgumentError, bug3630) { File.expand_path("~") } 901 ENV["HOME"] = "." 902 assert_raise(ArgumentError, bug3630) { File.expand_path("~") } 903 ensure 904 ENV["HOME"] = home 905 ENV["HOMEDRIVE"] = home_drive 906 ENV["HOMEPATH"] = home_path 907 ENV["USERPROFILE"] = user_profile 908 end 909 end 910 911 def test_expand_path_home_dir_string 912 home = ENV["HOME"] 913 new_home = "#{DRIVE}/UserHome" 914 ENV["HOME"] = new_home 915 bug8034 = "[ruby-core:53168]" 916 917 assert_equal File.join(new_home, "foo"), File.expand_path("foo", "~"), bug8034 918 assert_equal File.join(new_home, "bar", "foo"), File.expand_path("foo", "~/bar"), bug8034 919 920 assert_raise(ArgumentError) { File.expand_path(".", UnknownUserHome) } 921 assert_nothing_raised(ArgumentError) { File.expand_path("#{DRIVE}/", UnknownUserHome) } 922 ENV["HOME"] = "#{DRIVE}UserHome" 923 assert_raise(ArgumentError) { File.expand_path("~") } 924 ensure 925 ENV["HOME"] = home 926 end 927 928 if /mswin|mingw/ =~ RUBY_PLATFORM 929 def test_expand_path_home_memory_leak_in_path 930 assert_no_memory_leak_at_expand_path_home('', 'in path') 931 end 932 933 def test_expand_path_home_memory_leak_in_base 934 assert_no_memory_leak_at_expand_path_home('".",', 'in base') 935 end 936 937 def assert_no_memory_leak_at_expand_path_home(arg, message) 938 prep = 'ENV["HOME"] = "foo"*100' 939 assert_no_memory_leak([], prep, <<-TRY, "memory leaked at non-absolute home #{message}") 940 10000.times do 941 begin 942 File.expand_path(#{arg}"~/a") 943 rescue ArgumentError => e 944 next 945 ensure 946 abort("ArgumentError (non-absolute home) expected") unless e 947 end 948 end 949 GC.start 950 TRY 951 end 952 end 953 954 955 def test_expand_path_remove_trailing_alternative_data 956 assert_equal File.join(rootdir, "aaa"), File.expand_path("#{rootdir}/aaa::$DATA") 957 assert_equal File.join(rootdir, "aa:a"), File.expand_path("#{rootdir}/aa:a:$DATA") 958 assert_equal File.join(rootdir, "aaa:$DATA"), File.expand_path("#{rootdir}/aaa:$DATA") 959 end if DRIVE 960 961 def test_expand_path_resolve_empty_string_current_directory 962 assert_equal(Dir.pwd, File.expand_path("")) 963 end 964 965 def test_expand_path_resolve_dot_current_directory 966 assert_equal(Dir.pwd, File.expand_path(".")) 967 end 968 969 def test_expand_path_resolve_file_name_relative_current_directory 970 assert_equal(File.join(Dir.pwd, "foo"), File.expand_path("foo")) 971 end 972 973 def test_ignore_nil_dir_string 974 assert_equal(File.join(Dir.pwd, "foo"), File.expand_path("foo", nil)) 975 end 976 977 def test_expand_path_resolve_file_name_and_dir_string_relative 978 assert_equal(File.join(Dir.pwd, "bar", "foo"), 979 File.expand_path("foo", "bar")) 980 end 981 982 def test_expand_path_cleanup_dots_file_name 983 bug = "[ruby-talk:18512]" 984 985 assert_equal(File.join(Dir.pwd, ".a"), File.expand_path(".a"), bug) 986 assert_equal(File.join(Dir.pwd, "..a"), File.expand_path("..a"), bug) 987 988 if DRIVE 989 # cleanup dots only on Windows 990 assert_equal(File.join(Dir.pwd, "a"), File.expand_path("a."), bug) 991 assert_equal(File.join(Dir.pwd, "a"), File.expand_path("a.."), bug) 992 else 993 assert_equal(File.join(Dir.pwd, "a."), File.expand_path("a."), bug) 994 assert_equal(File.join(Dir.pwd, "a.."), File.expand_path("a.."), bug) 995 end 996 end 997 998 def test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_a_complete_path 999 assert_equal(@dir, File.expand_path("", "#{@dir}")) 1000 assert_equal(File.join(@dir, "a"), File.expand_path("a", "#{@dir}")) 1001 assert_equal(File.join(@dir, "a"), File.expand_path("../a", "#{@dir}/xxx")) 1002 assert_equal(rootdir, File.expand_path(".", "#{rootdir}")) 1003 end 1004 1005 def test_expand_path_ignores_supplied_dir_if_path_contains_a_drive_letter 1006 assert_equal(rootdir, File.expand_path(rootdir, "D:/")) 1007 end if DRIVE 1008 1009 def test_expand_path_removes_trailing_slashes_from_absolute_path 1010 assert_equal(File.join(rootdir, "foo"), File.expand_path("#{rootdir}foo/")) 1011 assert_equal(File.join(rootdir, "foo.rb"), File.expand_path("#{rootdir}foo.rb/")) 1012 end 1013 1014 def test_expand_path_removes_trailing_spaces_from_absolute_path 1015 assert_equal(File.join(rootdir, "a"), File.expand_path("#{rootdir}a ")) 1016 end if DRIVE 1017 1018 def test_expand_path_converts_a_pathname_which_starts_with_a_slash_using_dir_s_drive 1019 assert_match(%r"\Az:/foo\z"i, File.expand_path('/foo', "z:/bar")) 1020 end if DRIVE 1021 1022 def test_expand_path_converts_a_pathname_which_starts_with_a_slash_and_unc_pathname 1023 assert_equal("//foo", File.expand_path('//foo', "//bar")) 1024 assert_equal("//bar/foo", File.expand_path('/foo', "//bar")) 1025 assert_equal("//foo", File.expand_path('//foo', "/bar")) 1026 end if DRIVE 1027 1028 def test_expand_path_converts_a_dot_with_unc_dir 1029 assert_equal("//", File.expand_path('.', "//")) 1030 end 1031 1032 def test_expand_path_preserves_unc_path_root 1033 assert_equal("//", File.expand_path("//")) 1034 assert_equal("//", File.expand_path("//.")) 1035 assert_equal("//", File.expand_path("//..")) 1036 end 1037 1038 def test_expand_path_converts_a_pathname_which_starts_with_a_slash_using_host_share 1039 assert_match(%r"\A//host/share/foo\z"i, File.expand_path('/foo', "//host/share/bar")) 1040 end if DRIVE 1041 1042 def test_expand_path_converts_a_pathname_which_starts_with_a_slash_using_a_current_drive 1043 assert_match(%r"\A#{DRIVE}/foo\z"i, File.expand_path('/foo')) 1044 end 1045 1046 def test_expand_path_returns_tainted_strings_or_not 1047 assert_equal(true, File.expand_path('foo').tainted?) 1048 assert_equal(true, File.expand_path('foo'.taint).tainted?) 1049 assert_equal(true, File.expand_path('/foo'.taint).tainted?) 1050 assert_equal(true, File.expand_path('foo', 'bar').tainted?) 1051 assert_equal(true, File.expand_path('foo', '/bar'.taint).tainted?) 1052 assert_equal(true, File.expand_path('foo'.taint, '/bar').tainted?) 1053 assert_equal(true, File.expand_path('~').tainted?) if ENV["HOME"] 1054 1055 if DRIVE 1056 assert_equal(true, File.expand_path('/foo').tainted?) 1057 assert_equal(false, File.expand_path('//foo').tainted?) 1058 assert_equal(true, File.expand_path('C:/foo'.taint).tainted?) 1059 assert_equal(false, File.expand_path('C:/foo').tainted?) 1060 assert_equal(true, File.expand_path('foo', '/bar').tainted?) 1061 assert_equal(true, File.expand_path('foo', 'C:/bar'.taint).tainted?) 1062 assert_equal(true, File.expand_path('foo'.taint, 'C:/bar').tainted?) 1063 assert_equal(false, File.expand_path('foo', 'C:/bar').tainted?) 1064 assert_equal(false, File.expand_path('C:/foo/../bar').tainted?) 1065 assert_equal(false, File.expand_path('foo', '//bar').tainted?) 1066 else 1067 assert_equal(false, File.expand_path('/foo').tainted?) 1068 assert_equal(false, File.expand_path('foo', '/bar').tainted?) 1069 end 1070 end 1071 1072 def test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_home_as_base 1073 old_home = ENV["HOME"] 1074 home = ENV["HOME"] = "#{DRIVE}/UserHome" 1075 assert_equal(home, File.expand_path("~")) 1076 assert_equal(home, File.expand_path("~", "C:/FooBar")) 1077 assert_equal(File.join(home, "a"), File.expand_path("~/a", "C:/FooBar")) 1078 ensure 1079 ENV["HOME"] = old_home 1080 end 1081 1082 def test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_unc_home 1083 old_home = ENV["HOME"] 1084 unc_home = ENV["HOME"] = "//UserHome" 1085 assert_equal(unc_home, File.expand_path("~")) 1086 ensure 1087 ENV["HOME"] = old_home 1088 end if DRIVE 1089 1090 def test_expand_path_does_not_modify_a_home_string_argument 1091 old_home = ENV["HOME"] 1092 home = ENV["HOME"] = "#{DRIVE}/UserHome" 1093 str = "~/a" 1094 assert_equal("#{home}/a", File.expand_path(str)) 1095 assert_equal("~/a", str) 1096 ensure 1097 ENV["HOME"] = old_home 1098 end 1099 1100 def test_expand_path_raises_argument_error_for_any_supplied_username 1101 bug = '[ruby-core:39597]' 1102 assert_raise(ArgumentError, bug) { File.expand_path("~anything") } 1103 end if DRIVE 1104 1105 def test_expand_path_for_existent_username 1106 user = ENV['USER'] 1107 skip "ENV['USER'] is not set" unless user 1108 assert_equal(ENV['HOME'], File.expand_path("~#{user}")) 1109 end unless DRIVE 1110 1111 def test_expand_path_error_for_nonexistent_username 1112 user = "\u{3086 3046 3066 3044}:\u{307F 3084 304A 3046}" 1113 assert_raise_with_message(ArgumentError, /#{user}/) {File.expand_path("~#{user}")} 1114 end unless DRIVE 1115 1116 def test_expand_path_error_for_non_absolute_home 1117 old_home = ENV["HOME"] 1118 ENV["HOME"] = "./UserHome" 1119 assert_raise_with_message(ArgumentError, /non-absolute home/) {File.expand_path("~")} 1120 ensure 1121 ENV["HOME"] = old_home 1122 end 1123 1124 def test_expand_path_raises_a_type_error_if_not_passed_a_string_type 1125 assert_raise(TypeError) { File.expand_path(1) } 1126 assert_raise(TypeError) { File.expand_path(nil) } 1127 assert_raise(TypeError) { File.expand_path(true) } 1128 end 1129 1130 def test_expand_path_expands_dot_dir 1131 assert_equal("#{DRIVE}/dir", File.expand_path("#{DRIVE}/./dir")) 1132 end 1133 1134 def test_expand_path_does_not_expand_wildcards 1135 assert_equal("#{DRIVE}/*", File.expand_path("./*", "#{DRIVE}/")) 1136 assert_equal("#{Dir.pwd}/*", File.expand_path("./*", Dir.pwd)) 1137 assert_equal("#{DRIVE}/?", File.expand_path("./?", "#{DRIVE}/")) 1138 assert_equal("#{Dir.pwd}/?", File.expand_path("./?", Dir.pwd)) 1139 end if DRIVE 1140 1141 def test_expand_path_does_not_modify_the_string_argument 1142 str = "./a/b/../c" 1143 assert_equal("#{Dir.pwd}/a/c", File.expand_path(str, Dir.pwd)) 1144 assert_equal("./a/b/../c", str) 1145 end 1146 1147 def test_expand_path_returns_a_string_when_passed_a_string_subclass 1148 sub = Class.new(String) 1149 str = sub.new "./a/b/../c" 1150 path = File.expand_path(str, Dir.pwd) 1151 assert_equal("#{Dir.pwd}/a/c", path) 1152 assert_instance_of(String, path) 1153 end 1154 1155 def test_expand_path_accepts_objects_that_have_a_to_path_method 1156 klass = Class.new { def to_path; "a/b/c"; end } 1157 obj = klass.new 1158 assert_equal("#{Dir.pwd}/a/b/c", File.expand_path(obj)) 1159 end 1160 1161 def test_expand_path_with_drive_letter 1162 bug10858 = '[ruby-core:68130] [Bug #10858]' 1163 assert_match(%r'/bar/foo\z'i, File.expand_path('z:foo', 'bar'), bug10858) 1164 assert_equal('z:/bar/foo', File.expand_path('z:foo', '/bar'), bug10858) 1165 end if DRIVE 1166 1167 if /darwin/ =~ RUBY_PLATFORM and Encoding.find("filesystem") == Encoding::UTF_8 1168 def test_expand_path_compose 1169 pp = Object.new.extend(Test::Unit::Assertions) 1170 def pp.mu_pp(str) #:nodoc: 1171 str.dump 1172 end 1173 1174 Dir.mktmpdir do |dir| 1175 Dir.chdir(dir) do 1176 orig = %W"d\u{e9}tente x\u{304c 304e 3050 3052 3054}" 1177 orig.each do |o| 1178 Dir.mkdir(o) 1179 n = Dir.chdir(o) {File.expand_path(".")} 1180 pp.assert_equal(o, File.basename(n)) 1181 end 1182 end 1183 end 1184 end 1185 end 1186 1187 def test_basename 1188 assert_equal(File.basename(regular_file).sub(/\.test$/, ""), File.basename(regular_file, ".test")) 1189 assert_equal(File.basename(utf8_file).sub(/\.test$/, ""), File.basename(utf8_file, ".test")) 1190 assert_equal("", s = File.basename("")) 1191 assert_not_predicate(s, :frozen?, '[ruby-core:24199]') 1192 assert_equal("foo", s = File.basename("foo")) 1193 assert_not_predicate(s, :frozen?, '[ruby-core:24199]') 1194 assert_equal("foo", File.basename("foo", ".ext")) 1195 assert_equal("foo", File.basename("foo.ext", ".ext")) 1196 assert_equal("foo", File.basename("foo.ext", ".*")) 1197 end 1198 1199 if NTFS 1200 def test_basename_strip 1201 [regular_file, utf8_file].each do |file| 1202 basename = File.basename(file) 1203 assert_equal(basename, File.basename(file + " ")) 1204 assert_equal(basename, File.basename(file + ".")) 1205 assert_equal(basename, File.basename(file + "::$DATA")) 1206 basename.chomp!(".test") 1207 assert_equal(basename, File.basename(file + " ", ".test")) 1208 assert_equal(basename, File.basename(file + ".", ".test")) 1209 assert_equal(basename, File.basename(file + "::$DATA", ".test")) 1210 assert_equal(basename, File.basename(file + " ", ".*")) 1211 assert_equal(basename, File.basename(file + ".", ".*")) 1212 assert_equal(basename, File.basename(file + "::$DATA", ".*")) 1213 end 1214 end 1215 else 1216 def test_basename_strip 1217 [regular_file, utf8_file].each do |file| 1218 basename = File.basename(file) 1219 assert_equal(basename + " ", File.basename(file + " ")) 1220 assert_equal(basename + ".", File.basename(file + ".")) 1221 assert_equal(basename + "::$DATA", File.basename(file + "::$DATA")) 1222 assert_equal(basename + " ", File.basename(file + " ", ".test")) 1223 assert_equal(basename + ".", File.basename(file + ".", ".test")) 1224 assert_equal(basename + "::$DATA", File.basename(file + "::$DATA", ".test")) 1225 assert_equal(basename, File.basename(file + ".", ".*")) 1226 basename.chomp!(".test") 1227 assert_equal(basename, File.basename(file + " ", ".*")) 1228 assert_equal(basename, File.basename(file + "::$DATA", ".*")) 1229 end 1230 end 1231 end 1232 1233 if File::ALT_SEPARATOR == '\\' 1234 def test_basename_backslash 1235 a = "foo/\225\\\\" 1236 [%W"cp437 \225", %W"cp932 \225\\"].each do |cp, expected| 1237 assert_equal(expected.force_encoding(cp), File.basename(a.dup.force_encoding(cp)), cp) 1238 end 1239 end 1240 end 1241 1242 def test_basename_encoding 1243 assert_incompatible_encoding {|d| File.basename(d)} 1244 assert_incompatible_encoding {|d| File.basename(d, ".*")} 1245 assert_raise(Encoding::CompatibilityError) {File.basename("foo.ext", ".*".encode("utf-16le"))} 1246 1247 s = "foo\x93_a".force_encoding("cp932") 1248 assert_equal(s, File.basename(s, "_a")) 1249 1250 s = "\u4032.\u3024" 1251 assert_equal(s, File.basename(s, ".\x95\\".force_encoding("cp932"))) 1252 end 1253 1254 def test_dirname 1255 assert_equal(@dir, File.dirname(regular_file)) 1256 assert_equal(@dir, File.dirname(utf8_file)) 1257 assert_equal(".", File.dirname("")) 1258 end 1259 1260 def test_dirname_encoding 1261 assert_incompatible_encoding {|d| File.dirname(d)} 1262 end 1263 1264 if File::ALT_SEPARATOR == '\\' 1265 def test_dirname_backslash 1266 a = "\225\\\\foo" 1267 [%W"cp437 \225", %W"cp932 \225\\"].each do |cp, expected| 1268 assert_equal(expected.force_encoding(cp), File.dirname(a.dup.force_encoding(cp)), cp) 1269 end 1270 end 1271 end 1272 1273 def test_extname 1274 assert_equal(".test", File.extname(regular_file)) 1275 assert_equal(".test", File.extname(utf8_file)) 1276 prefixes = ["", "/", ".", "/.", "bar/.", "/bar/."] 1277 infixes = ["", " ", "."] 1278 infixes2 = infixes + [".ext "] 1279 appendixes = [""] 1280 if NTFS 1281 appendixes << " " << "." << "::$DATA" << "::$DATA.bar" 1282 end 1283 prefixes.each do |prefix| 1284 appendixes.each do |appendix| 1285 infixes.each do |infix| 1286 path = "#{prefix}foo#{infix}#{appendix}" 1287 assert_equal("", File.extname(path), "File.extname(#{path.inspect})") 1288 end 1289 infixes2.each do |infix| 1290 path = "#{prefix}foo#{infix}.ext#{appendix}" 1291 assert_equal(".ext", File.extname(path), "File.extname(#{path.inspect})") 1292 end 1293 end 1294 end 1295 bug3175 = '[ruby-core:29627]' 1296 assert_equal(".rb", File.extname("/tmp//bla.rb"), bug3175) 1297 1298 assert_incompatible_encoding {|d| File.extname(d)} 1299 end 1300 1301 def test_split 1302 [regular_file, utf8_file].each do |file| 1303 d, b = File.split(file) 1304 assert_equal(File.dirname(file), d) 1305 assert_equal(File.basename(file), b) 1306 end 1307 end 1308 1309 def test_join 1310 s = "foo" + File::SEPARATOR + "bar" + File::SEPARATOR + "baz" 1311 assert_equal(s, File.join("foo", "bar", "baz")) 1312 assert_equal(s, File.join(["foo", "bar", "baz"])) 1313 1314 o = Object.new 1315 def o.to_path; "foo"; end 1316 assert_equal(s, File.join(o, "bar", "baz")) 1317 assert_equal(s, File.join("foo" + File::SEPARATOR, "bar", File::SEPARATOR + "baz")) 1318 end 1319 1320 def test_join_alt_separator 1321 if File::ALT_SEPARATOR == '\\' 1322 a = "\225\\" 1323 b = "foo" 1324 [%W"cp437 \225\\foo", %W"cp932 \225\\/foo"].each do |cp, expected| 1325 assert_equal(expected.force_encoding(cp), File.join(a.dup.force_encoding(cp), b.dup.force_encoding(cp)), cp) 1326 end 1327 end 1328 end 1329 1330 def test_join_ascii_incompatible 1331 bug7168 = '[ruby-core:48012]' 1332 names = %w"a b".map {|s| s.encode(Encoding::UTF_16LE)} 1333 assert_raise(Encoding::CompatibilityError, bug7168) {File.join(*names)} 1334 assert_raise(Encoding::CompatibilityError, bug7168) {File.join(names)} 1335 1336 a = Object.new 1337 b = names[1] 1338 names = [a, "b"] 1339 a.singleton_class.class_eval do 1340 define_method(:to_path) do 1341 names[1] = b 1342 "a" 1343 end 1344 end 1345 assert_raise(Encoding::CompatibilityError, bug7168) {File.join(names)} 1346 end 1347 1348 def test_join_with_changed_separator 1349 assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}") 1350 bug = '[ruby-core:79579] [Bug #13223]' 1351 begin; 1352 class File 1353 remove_const :Separator 1354 remove_const :SEPARATOR 1355 end 1356 GC.start 1357 assert_equal("hello/world", File.join("hello", "world"), bug) 1358 end; 1359 end 1360 1361 def test_truncate 1362 [regular_file, utf8_file].each do |file| 1363 assert_equal(0, File.truncate(file, 1)) 1364 assert_file.exist?(file) 1365 assert_equal(1, File.size(file)) 1366 assert_equal(0, File.truncate(file, 0)) 1367 assert_file.exist?(file) 1368 assert_file.zero?(file) 1369 make_file("foo", file) 1370 assert_raise(Errno::ENOENT) { File.truncate(nofile, 0) } 1371 1372 f = File.new(file, "w") 1373 assert_equal(0, f.truncate(2)) 1374 assert_file.exist?(file) 1375 assert_equal(2, File.size(file)) 1376 assert_equal(0, f.truncate(0)) 1377 assert_file.exist?(file) 1378 assert_file.zero?(file) 1379 f.close 1380 make_file("foo", file) 1381 1382 assert_raise(IOError) { File.open(file) {|ff| ff.truncate(0)} } 1383 end 1384 rescue NotImplementedError 1385 end 1386 1387 def test_flock_exclusive 1388 File.open(regular_file, "r+") do |f| 1389 f.flock(File::LOCK_EX) 1390 assert_separately(["-rtimeout", "-", regular_file], "#{<<~begin}#{<<~"end;"}") 1391 begin 1392 open(ARGV[0], "r") do |f| 1393 Timeout.timeout(0.1) do 1394 assert(!f.flock(File::LOCK_SH|File::LOCK_NB)) 1395 end 1396 end 1397 end; 1398 assert_separately(["-rtimeout", "-", regular_file], "#{<<~begin}#{<<~"end;"}") 1399 begin 1400 open(ARGV[0], "r") do |f| 1401 assert_raise(Timeout::Error) do 1402 Timeout.timeout(0.1) do 1403 f.flock(File::LOCK_SH) 1404 end 1405 end 1406 end 1407 end; 1408 f.flock(File::LOCK_UN) 1409 end 1410 rescue NotImplementedError 1411 end 1412 1413 def test_flock_shared 1414 File.open(regular_file, "r+") do |f| 1415 f.flock(File::LOCK_SH) 1416 assert_separately(["-rtimeout", "-", regular_file], "#{<<~begin}#{<<~"end;"}") 1417 begin 1418 open(ARGV[0], "r") do |f| 1419 Timeout.timeout(0.1) do 1420 assert(f.flock(File::LOCK_SH)) 1421 end 1422 end 1423 end; 1424 assert_separately(["-rtimeout", "-", regular_file], "#{<<~begin}#{<<~"end;"}") 1425 begin 1426 open(ARGV[0], "r+") do |f| 1427 assert_raise(Timeout::Error) do 1428 Timeout.timeout(0.1) do 1429 f.flock(File::LOCK_EX) 1430 end 1431 end 1432 end 1433 end; 1434 f.flock(File::LOCK_UN) 1435 end 1436 rescue NotImplementedError 1437 end 1438 1439 def test_test 1440 fn1 = regular_file 1441 hardlinkfile 1442 sleep(1.1) 1443 fn2 = fn1 + "2" 1444 make_file("foo", fn2) 1445 [ 1446 @dir, 1447 fn1, 1448 zerofile, 1449 notownedfile, 1450 suidfile, 1451 sgidfile, 1452 stickyfile, 1453 symlinkfile, 1454 hardlinkfile, 1455 chardev, 1456 blockdev, 1457 fifo, 1458 socket 1459 ].compact.each do |f| 1460 assert_equal(File.atime(f), test(?A, f)) 1461 assert_equal(File.ctime(f), test(?C, f)) 1462 assert_equal(File.mtime(f), test(?M, f)) 1463 assert_equal(File.blockdev?(f), test(?b, f)) 1464 assert_equal(File.chardev?(f), test(?c, f)) 1465 assert_equal(File.directory?(f), test(?d, f)) 1466 assert_equal(File.exist?(f), test(?e, f)) 1467 assert_equal(File.file?(f), test(?f, f)) 1468 assert_equal(File.setgid?(f), test(?g, f)) 1469 assert_equal(File.grpowned?(f), test(?G, f)) 1470 assert_equal(File.sticky?(f), test(?k, f)) 1471 assert_equal(File.symlink?(f), test(?l, f)) 1472 assert_equal(File.owned?(f), test(?o, f)) 1473 assert_nothing_raised { test(?O, f) } 1474 assert_equal(File.pipe?(f), test(?p, f)) 1475 assert_equal(File.readable?(f), test(?r, f)) 1476 assert_equal(File.readable_real?(f), test(?R, f)) 1477 assert_equal(File.size?(f), test(?s, f)) 1478 assert_equal(File.socket?(f), test(?S, f)) 1479 assert_equal(File.setuid?(f), test(?u, f)) 1480 assert_equal(File.writable?(f), test(?w, f)) 1481 assert_equal(File.writable_real?(f), test(?W, f)) 1482 assert_equal(File.executable?(f), test(?x, f)) 1483 assert_equal(File.executable_real?(f), test(?X, f)) 1484 assert_equal(File.zero?(f), test(?z, f)) 1485 end 1486 assert_equal(false, test(?-, @dir, fn1)) 1487 assert_equal(true, test(?-, fn1, fn1)) 1488 assert_equal(true, test(?=, fn1, fn1)) 1489 assert_equal(false, test(?>, fn1, fn1)) 1490 assert_equal(false, test(?<, fn1, fn1)) 1491 unless /cygwin/ =~ RUBY_PLATFORM 1492 assert_equal(false, test(?=, fn1, fn2)) 1493 assert_equal(false, test(?>, fn1, fn2)) 1494 assert_equal(true, test(?>, fn2, fn1)) 1495 assert_equal(true, test(?<, fn1, fn2)) 1496 assert_equal(false, test(?<, fn2, fn1)) 1497 end 1498 assert_raise(ArgumentError) { test } 1499 assert_raise(Errno::ENOENT) { test(?A, nofile) } 1500 assert_raise(ArgumentError) { test(?a) } 1501 assert_raise(ArgumentError) { test("\0".ord) } 1502 end 1503 1504 def test_stat_init 1505 fn1 = regular_file 1506 hardlinkfile 1507 sleep(1.1) 1508 fn2 = fn1 + "2" 1509 make_file("foo", fn2) 1510 fs1, fs2 = File::Stat.new(fn1), File::Stat.new(fn2) 1511 assert_nothing_raised do 1512 assert_equal(0, fs1 <=> fs1) 1513 assert_equal(-1, fs1 <=> fs2) 1514 assert_equal(1, fs2 <=> fs1) 1515 assert_nil(fs1 <=> nil) 1516 assert_integer(fs1.dev) 1517 assert_integer_or_nil(fs1.rdev) 1518 assert_integer_or_nil(fs1.dev_major) 1519 assert_integer_or_nil(fs1.dev_minor) 1520 assert_integer_or_nil(fs1.rdev_major) 1521 assert_integer_or_nil(fs1.rdev_minor) 1522 assert_integer(fs1.ino) 1523 assert_integer(fs1.mode) 1524 assert_equal(hardlinkfile ? 2 : 1, fs1.nlink) 1525 assert_integer(fs1.uid) 1526 assert_integer(fs1.gid) 1527 assert_equal(3, fs1.size) 1528 assert_integer_or_nil(fs1.blksize) 1529 assert_integer_or_nil(fs1.blocks) 1530 assert_kind_of(Time, fs1.atime) 1531 assert_kind_of(Time, fs1.mtime) 1532 assert_kind_of(Time, fs1.ctime) 1533 assert_kind_of(String, fs1.inspect) 1534 end 1535 assert_raise(Errno::ENOENT) { File::Stat.new(nofile) } 1536 assert_kind_of(File::Stat, File::Stat.new(fn1).dup) 1537 assert_raise(TypeError) do 1538 File::Stat.new(fn1).instance_eval { initialize_copy(0) } 1539 end 1540 end 1541 1542 def test_stat_new_utf8 1543 assert_nothing_raised do 1544 File::Stat.new(utf8_file) 1545 end 1546 end 1547 1548 def test_stat_ftype 1549 assert_equal("directory", File::Stat.new(@dir).ftype) 1550 assert_equal("file", File::Stat.new(regular_file).ftype) 1551 # File::Stat uses stat 1552 assert_equal("file", File::Stat.new(symlinkfile).ftype) if symlinkfile 1553 assert_equal("file", File::Stat.new(hardlinkfile).ftype) if hardlinkfile 1554 end 1555 1556 def test_stat_directory_p 1557 assert_predicate(File::Stat.new(@dir), :directory?) 1558 assert_not_predicate(File::Stat.new(regular_file), :directory?) 1559 end 1560 1561 def test_stat_pipe_p 1562 assert_not_predicate(File::Stat.new(@dir), :pipe?) 1563 assert_not_predicate(File::Stat.new(regular_file), :pipe?) 1564 assert_predicate(File::Stat.new(fifo), :pipe?) if fifo 1565 IO.pipe {|r, w| 1566 assert_predicate(r.stat, :pipe?) 1567 assert_predicate(w.stat, :pipe?) 1568 } 1569 end 1570 1571 def test_stat_symlink_p 1572 assert_not_predicate(File::Stat.new(@dir), :symlink?) 1573 assert_not_predicate(File::Stat.new(regular_file), :symlink?) 1574 # File::Stat uses stat 1575 assert_not_predicate(File::Stat.new(symlinkfile), :symlink?) if symlinkfile 1576 assert_not_predicate(File::Stat.new(hardlinkfile), :symlink?) if hardlinkfile 1577 end 1578 1579 def test_stat_socket_p 1580 assert_not_predicate(File::Stat.new(@dir), :socket?) 1581 assert_not_predicate(File::Stat.new(regular_file), :socket?) 1582 assert_predicate(File::Stat.new(socket), :socket?) if socket 1583 end 1584 1585 def test_stat_blockdev_p 1586 assert_not_predicate(File::Stat.new(@dir), :blockdev?) 1587 assert_not_predicate(File::Stat.new(regular_file), :blockdev?) 1588 assert_predicate(File::Stat.new(blockdev), :blockdev?) if blockdev 1589 end 1590 1591 def test_stat_chardev_p 1592 assert_not_predicate(File::Stat.new(@dir), :chardev?) 1593 assert_not_predicate(File::Stat.new(regular_file), :chardev?) 1594 assert_predicate(File::Stat.new(chardev), :chardev?) if chardev 1595 end 1596 1597 def test_stat_readable_p 1598 return if Process.euid == 0 1599 File.chmod(0200, regular_file) 1600 assert_not_predicate(File::Stat.new(regular_file), :readable?) 1601 File.chmod(0600, regular_file) 1602 assert_predicate(File::Stat.new(regular_file), :readable?) 1603 end if POSIX 1604 1605 def test_stat_readable_real_p 1606 return if Process.euid == 0 1607 File.chmod(0200, regular_file) 1608 assert_not_predicate(File::Stat.new(regular_file), :readable_real?) 1609 File.chmod(0600, regular_file) 1610 assert_predicate(File::Stat.new(regular_file), :readable_real?) 1611 end if POSIX 1612 1613 def test_stat_world_readable_p 1614 File.chmod(0006, regular_file) 1615 assert_predicate(File::Stat.new(regular_file), :world_readable?) 1616 File.chmod(0060, regular_file) 1617 assert_not_predicate(File::Stat.new(regular_file), :world_readable?) 1618 File.chmod(0600, regular_file) 1619 assert_not_predicate(File::Stat.new(regular_file), :world_readable?) 1620 end if POSIX 1621 1622 def test_stat_writable_p 1623 return if Process.euid == 0 1624 File.chmod(0400, regular_file) 1625 assert_not_predicate(File::Stat.new(regular_file), :writable?) 1626 File.chmod(0600, regular_file) 1627 assert_predicate(File::Stat.new(regular_file), :writable?) 1628 end if POSIX 1629 1630 def test_stat_writable_real_p 1631 return if Process.euid == 0 1632 File.chmod(0400, regular_file) 1633 assert_not_predicate(File::Stat.new(regular_file), :writable_real?) 1634 File.chmod(0600, regular_file) 1635 assert_predicate(File::Stat.new(regular_file), :writable_real?) 1636 end if POSIX 1637 1638 def test_stat_world_writable_p 1639 File.chmod(0006, regular_file) 1640 assert_predicate(File::Stat.new(regular_file), :world_writable?) 1641 File.chmod(0060, regular_file) 1642 assert_not_predicate(File::Stat.new(regular_file), :world_writable?) 1643 File.chmod(0600, regular_file) 1644 assert_not_predicate(File::Stat.new(regular_file), :world_writable?) 1645 end if POSIX 1646 1647 def test_stat_executable_p 1648 File.chmod(0100, regular_file) 1649 assert_predicate(File::Stat.new(regular_file), :executable?) 1650 File.chmod(0600, regular_file) 1651 assert_not_predicate(File::Stat.new(regular_file), :executable?) 1652 end if POSIX 1653 1654 def test_stat_executable_real_p 1655 File.chmod(0100, regular_file) 1656 assert_predicate(File::Stat.new(regular_file), :executable_real?) 1657 File.chmod(0600, regular_file) 1658 assert_not_predicate(File::Stat.new(regular_file), :executable_real?) 1659 end if POSIX 1660 1661 def test_stat_file_p 1662 assert_not_predicate(File::Stat.new(@dir), :file?) 1663 assert_predicate(File::Stat.new(regular_file), :file?) 1664 end 1665 1666 def test_stat_zero_p 1667 assert_nothing_raised { File::Stat.new(@dir).zero? } 1668 assert_not_predicate(File::Stat.new(regular_file), :zero?) 1669 assert_predicate(File::Stat.new(zerofile), :zero?) 1670 end 1671 1672 def test_stat_size_p 1673 assert_nothing_raised { File::Stat.new(@dir).size? } 1674 assert_equal(3, File::Stat.new(regular_file).size?) 1675 assert_not_predicate(File::Stat.new(zerofile), :size?) 1676 end 1677 1678 def test_stat_owned_p 1679 assert_predicate(File::Stat.new(regular_file), :owned?) 1680 assert_not_predicate(File::Stat.new(notownedfile), :owned?) if notownedfile 1681 end if POSIX 1682 1683 def test_stat_grpowned_p ## xxx 1684 assert_predicate(File::Stat.new(regular_file), :grpowned?) 1685 end if POSIX 1686 1687 def test_stat_suid 1688 assert_not_predicate(File::Stat.new(regular_file), :setuid?) 1689 assert_predicate(File::Stat.new(suidfile), :setuid?) if suidfile 1690 end 1691 1692 def test_stat_sgid 1693 assert_not_predicate(File::Stat.new(regular_file), :setgid?) 1694 assert_predicate(File::Stat.new(sgidfile), :setgid?) if sgidfile 1695 end 1696 1697 def test_stat_sticky 1698 assert_not_predicate(File::Stat.new(regular_file), :sticky?) 1699 assert_predicate(File::Stat.new(stickyfile), :sticky?) if stickyfile 1700 end 1701 1702 def test_stat_size 1703 assert_integer(File::Stat.new(@dir).size) 1704 assert_equal(3, File::Stat.new(regular_file).size) 1705 assert_equal(0, File::Stat.new(zerofile).size) 1706 end 1707 1708 def test_stat_special_file 1709 # test for special files such as pagefile.sys on Windows 1710 assert_nothing_raised do 1711 Dir::glob("C:/*.sys") {|f| File::Stat.new(f) } 1712 end 1713 end if DRIVE 1714 1715 def test_path_check 1716 assert_nothing_raised { ENV["PATH"] } 1717 end 1718 1719 def test_size 1720 [regular_file, utf8_file].each do |file| 1721 assert_equal(3, File.open(file) {|f| f.size }) 1722 File.open(file, "a") do |f| 1723 f.write("bar") 1724 assert_equal(6, f.size) 1725 end 1726 end 1727 end 1728 1729 def test_absolute_path 1730 assert_equal(File.join(Dir.pwd, "~foo"), File.absolute_path("~foo")) 1731 dir = File.expand_path("/bar") 1732 assert_equal(File.join(dir, "~foo"), File.absolute_path("~foo", dir)) 1733 end 1734end 1735