1 # frozen_string_literal: false
7 class TestDir < Test::Unit::TestCase
11 @root = File.realpath(Dir.mktmpdir('__test_dir__'))
12 @nodir = File.join(@root, "dummy")
16 FileUtils.touch(File.join(@root, i))
18 FileUtils.mkdir(File.join(@root, i))
19 @dirs << File.join(i, "")
26 FileUtils.remove_entry_secure @root if File.directory?(@root)
35 break unless name = dir.read
38 for x,y in cache.sort_by {|z| z[0] % 3 } # shuffle
40 assert_equal(y, dir.read)
48 assert_raise(Errno::ENOENT) { Dir.open(@nodir) }
53 assert_match(/^#<Dir:#{ Regexp.quote(@root) }>$/, d.inspect)
54 assert_match(/^#<Dir:.*>$/, Dir.allocate.inspect)
61 assert_equal(@root, d.path)
62 assert_nil(Dir.allocate.path)
71 break unless x = d.read
73 assert_equal(x, d.read)
81 a = (0..5).map { d.read }
83 b = (0..5).map { d.read }
91 env_home = ENV["HOME"]
92 env_logdir = ENV["LOGDIR"]
96 assert_raise(Errno::ENOENT) { Dir.chdir(@nodir) }
97 assert_raise(ArgumentError) { Dir.chdir }
100 assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(pwd) }
102 assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(@root) }
103 assert_equal(@root, Dir.pwd)
105 assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(pwd) }
107 assert_raise(RuntimeError) { Thread.new { Thread.current.report_on_exception = false; Dir.chdir(@root) }.join }
108 assert_raise(RuntimeError) { Thread.new { Thread.current.report_on_exception = false; Dir.chdir(@root) { } }.join }
110 assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(pwd) }
112 assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(@root) }
113 assert_equal(@root, Dir.pwd)
115 assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(pwd) }
117 assert_equal(@root, Dir.pwd)
119 assert_equal(pwd, Dir.pwd)
126 abort("cannot return the original directory: #{ pwd }")
128 ENV["HOME"] = env_home
129 ENV["LOGDIR"] = env_logdir
132 def test_chdir_conflict
134 q = Thread::Queue.new
137 Dir.chdir(pwd) rescue $!
141 assert_instance_of(RuntimeError, t.value)
146 Dir.chdir(pwd){} rescue $!
150 assert_instance_of(RuntimeError, t.value)
154 def test_chroot_nodir
155 omit if RUBY_PLATFORM =~ /android/
156 assert_raise(NotImplementedError, Errno::ENOENT, Errno::EPERM
157 ) { Dir.chroot(File.join(@nodir, "")) }
163 assert_nothing_raised(IOError) { d.close }
164 assert_raise(IOError) { d.read }
168 assert_equal((%w(.) + ("a".."z").to_a).map{|f| File.join(@root, f) },
169 Dir.glob(File.join(@root, "*"), File::FNM_DOTMATCH))
170 assert_equal([@root] + ("a".."z").map {|f| File.join(@root, f) },
171 Dir.glob([@root, File.join(@root, "*")]))
172 assert_equal([@root] + ("a".."z").map {|f| File.join(@root, f) },
173 Dir.glob([@root, File.join(@root, "*")], sort: false).sort)
174 assert_equal([@root] + ("a".."z").map {|f| File.join(@root, f) },
175 Dir.glob([@root, File.join(@root, "*")], sort: true))
176 assert_raise_with_message(ArgumentError, /nul-separated/) do
177 Dir.glob(@root + "\0\0\0" + File.join(@root, "*"))
179 assert_raise_with_message(ArgumentError, /expected true or false/) do
180 Dir.glob(@root, sort: 1)
182 assert_raise_with_message(ArgumentError, /expected true or false/) do
183 Dir.glob(@root, sort: nil)
186 assert_equal(("a".."z").step(2).map {|f| File.join(File.join(@root, f), "") },
187 Dir.glob(File.join(@root, "*/")))
188 assert_equal([File.join(@root, '//a')], Dir.glob(@root + '//a'))
190 FileUtils.touch(File.join(@root, "{}"))
191 assert_equal(%w({} a).map{|f| File.join(@root, f) },
192 Dir.glob(File.join(@root, '{\{\},a}')))
193 assert_equal([], Dir.glob(File.join(@root, '[')))
194 assert_equal([], Dir.glob(File.join(@root, '[a-\\')))
196 assert_equal([File.join(@root, "a")], Dir.glob(File.join(@root, 'a\\')))
197 assert_equal(("a".."f").map {|f| File.join(@root, f) }, Dir.glob(File.join(@root, '[abc/def]')))
199 open(File.join(@root, "}}{}"), "wb") {}
200 open(File.join(@root, "}}a"), "wb") {}
201 assert_equal(%w(}}{} }}a).map {|f| File.join(@root, f)}, Dir.glob(File.join(@root, '}}{\{\},a}')))
202 assert_equal(%w(}}{} }}a b c).map {|f| File.join(@root, f)}, Dir.glob(File.join(@root, '{\}\}{\{\},a},b,c}')))
203 assert_raise(ArgumentError) {
204 Dir.glob([[@root, File.join(@root, "*")].join("\0")])
208 def test_glob_recursive
209 bug6977 = '[ruby-core:47418]'
210 bug8006 = '[ruby-core:53108] [Bug #8006]'
212 assert_include(Dir.glob("a/**/*", File::FNM_DOTMATCH), "a/.", bug8006)
215 assert_not_include(Dir.glob("a/**/*", File::FNM_DOTMATCH), "a/b/.")
217 FileUtils.mkdir_p("a/b/c/d/e/f")
218 assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/e/f"), bug6977)
219 assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/d/e/f"), bug6977)
220 assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/c/d/e/f"), bug6977)
221 assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/b/c/d/e/f"), bug6977)
222 assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/c/?/e/f"), bug6977)
223 assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/c/**/d/e/f"), bug6977)
224 assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/c/**/d/e/f"), bug6977)
226 bug8283 = '[ruby-core:54387] [Bug #8283]'
227 dirs = ["a/.x", "a/b/.y"]
228 FileUtils.mkdir_p(dirs)
229 dirs.map {|dir| open("#{dir}/z", "w") {}}
230 assert_equal([], Dir.glob("a/**/z"), bug8283)
231 assert_equal(["a/.x/z"], Dir.glob("a/**/.x/z"), bug8283)
232 assert_equal(["a/.x/z"], Dir.glob("a/.x/**/z"), bug8283)
233 assert_equal(["a/b/.y/z"], Dir.glob("a/**/.y/z"), bug8283)
237 def test_glob_recursive_directory
239 ['d', 'e'].each do |path|
240 FileUtils.mkdir_p("c/#{path}/a/b/c")
241 FileUtils.touch("c/#{path}/a/a.file")
242 FileUtils.touch("c/#{path}/a/b/b.file")
243 FileUtils.touch("c/#{path}/a/b/c/c.file")
245 bug15540 = '[ruby-core:91110] [Bug #15540]'
246 assert_equal(["c/d/a/", "c/d/a/b/", "c/d/a/b/c/", "c/e/a/", "c/e/a/b/", "c/e/a/b/c/"],
247 Dir.glob('c/{d,e}/a/**/'), bug15540)
249 assert_equal(["c/e/a/", "c/e/a/b/", "c/e/a/b/c/", "c/d/a/", "c/d/a/b/", "c/d/a/b/c/"],
250 Dir.glob('c/{e,d}/a/**/'))
254 def test_glob_starts_with_brace
256 bug15649 = '[ruby-core:91728] [Bug #15649]'
257 assert_equal(["#{@root}/a", "#{@root}/b"],
258 Dir.glob("{#{@root}/a,#{@root}/b}"), bug15649)
264 assert_equal(["#{@root}/a", "#{@root}/b"], Dir.glob("#{@root}/[ba]"))
265 assert_equal(["#{@root}/b", "#{@root}/a"], Dir.glob(%W"#{@root}/b #{@root}/a"))
266 assert_equal(["#{@root}/b", "#{@root}/a"], Dir.glob("#{@root}/{b,a}"))
268 assert_equal(["a", "b"], Dir.glob("[ba]", base: @root))
269 assert_equal(["b", "a"], Dir.glob(%W"b a", base: @root))
270 assert_equal(["b", "a"], Dir.glob("{b,a}", base: @root))
273 if Process.const_defined?(:RLIMIT_NOFILE)
274 def test_glob_too_may_open_files
275 assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}", chdir: @root)
278 Process.setrlimit(Process::RLIMIT_NOFILE, n)
281 n.times {files << File.open('b')}
282 rescue Errno::EMFILE, Errno::ENFILE => e
284 assert_raise(e.class) {
292 files = %w[a/foo.c c/bar.c]
293 files.each {|n| File.write(File.join(@root, n), "")}
294 Dir.mkdir(File.join(@root, "a/dir"))
295 dirs = @dirs + %w[a/dir/]
298 assert_equal(files, Dir.glob("*/*.c", base: @root))
299 assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: ".")})
300 assert_equal(%w[foo.c], Dir.chdir(@root) {Dir.glob("*.c", base: "a")})
301 assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: "")})
302 assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: nil)})
303 assert_equal(@dirs, Dir.glob("*/", base: @root))
304 assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: ".")})
305 assert_equal(%w[dir/], Dir.chdir(@root) {Dir.glob("*/", base: "a")})
306 assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: "")})
307 assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: nil)})
308 assert_equal(dirs, Dir.glob("**/*/", base: @root))
309 assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: ".")})
310 assert_equal(%w[dir/], Dir.chdir(@root) {Dir.glob("**/*/", base: "a")})
311 assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: "")})
312 assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: nil)})
314 assert_equal(files, Dir.glob("*/*.c", base: @root, sort: false).sort)
315 assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: ".", sort: false).sort})
316 assert_equal(%w[foo.c], Dir.chdir(@root) {Dir.glob("*.c", base: "a", sort: false).sort})
317 assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: "", sort: false).sort})
318 assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: nil, sort: false).sort})
319 assert_equal(@dirs, Dir.glob("*/", base: @root))
320 assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: ".", sort: false).sort})
321 assert_equal(%w[dir/], Dir.chdir(@root) {Dir.glob("*/", base: "a", sort: false).sort})
322 assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: "", sort: false).sort})
323 assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: nil, sort: false).sort})
324 assert_equal(dirs, Dir.glob("**/*/", base: @root))
325 assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: ".", sort: false).sort})
326 assert_equal(%w[dir/], Dir.chdir(@root) {Dir.glob("**/*/", base: "a", sort: false).sort})
327 assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: "", sort: false).sort})
328 assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: nil, sort: false).sort})
331 def test_glob_base_dir
332 files = %w[a/foo.c c/bar.c]
333 files.each {|n| File.write(File.join(@root, n), "")}
334 Dir.mkdir(File.join(@root, "a/dir"))
335 dirs = @dirs + %w[a/dir/]
338 assert_equal(files, Dir.open(@root) {|d| Dir.glob("*/*.c", base: d)})
339 assert_equal(%w[foo.c], Dir.chdir(@root) {Dir.open("a") {|d| Dir.glob("*.c", base: d)}})
340 assert_equal(@dirs, Dir.open(@root) {|d| Dir.glob("*/", base: d)})
341 assert_equal(%w[dir/], Dir.chdir(@root) {Dir.open("a") {|d| Dir.glob("*/", base: d)}})
342 assert_equal(dirs, Dir.open(@root) {|d| Dir.glob("**/*/", base: d)})
343 assert_equal(%w[dir/], Dir.chdir(@root) {Dir.open("a") {|d| Dir.glob("**/*/", base: d)}})
345 assert_equal(files, Dir.open(@root) {|d| Dir.glob("*/*.c", base: d, sort: false).sort})
346 assert_equal(%w[foo.c], Dir.chdir(@root) {Dir.open("a") {|d| Dir.glob("*.c", base: d, sort: false).sort}})
347 assert_equal(@dirs, Dir.open(@root) {|d| Dir.glob("*/", base: d, sort: false).sort})
348 assert_equal(%w[dir/], Dir.chdir(@root) {Dir.open("a") {|d| Dir.glob("*/", base: d, sort: false).sort}})
349 assert_equal(dirs, Dir.open(@root) {|d| Dir.glob("**/*/", base: d, sort: false).sort})
350 assert_equal(%w[dir/], Dir.chdir(@root) {Dir.open("a") {|d| Dir.glob("**/*/", base: d, sort: false).sort}})
353 def test_glob_ignore_casefold_invalid_encoding
354 bug14456 = "[ruby-core:85448]"
355 filename = "\u00AAa123".encode('ISO-8859-1')
356 File.write(File.join(@root, filename), "")
357 matches = Dir.chdir(@root) {|d| Dir.glob("*a123".encode('UTF-8'), File::FNM_CASEFOLD)}
358 assert_equal(1, matches.size, bug14456)
359 matches.each{|f| f.force_encoding('ISO-8859-1')}
360 # Handle MacOS/Windows, which saves under a different filename
361 assert_include([filename, "\u00C2\u00AAa123".encode('ISO-8859-1')], matches.first, bug14456)
364 def assert_entries(entries, children_only = false)
366 expected = ("a".."z").to_a
367 expected = %w(. ..) + expected unless children_only
368 assert_equal(expected, entries)
372 assert_entries(Dir.open(@root) {|dir| dir.entries})
373 assert_entries(Dir.entries(@root))
374 assert_raise(ArgumentError) {Dir.entries(@root+"\0")}
375 [Encoding::UTF_8, Encoding::ASCII_8BIT].each do |enc|
376 assert_equal(enc, Dir.entries(@root, encoding: enc).first.encoding)
381 assert_entries(Dir.open(@root) {|dir| dir.each.to_a})
382 assert_entries(Dir.foreach(@root).to_a)
383 assert_raise(ArgumentError) {Dir.foreach(@root+"\0").to_a}
384 newdir = @root+"/new"
385 e = Dir.foreach(newdir)
386 assert_raise(Errno::ENOENT) {e.to_a}
388 File.write(newdir+"/a", "")
389 assert_equal(%w[. .. a], e.to_a.sort)
390 [Encoding::UTF_8, Encoding::ASCII_8BIT].each do |enc|
391 e = Dir.foreach(newdir, encoding: enc)
392 assert_equal(enc, e.to_a.first.encoding)
397 assert_entries(Dir.open(@root) {|dir| dir.children}, true)
398 assert_entries(Dir.children(@root), true)
399 assert_raise(ArgumentError) {Dir.children(@root+"\0")}
400 [Encoding::UTF_8, Encoding::ASCII_8BIT].each do |enc|
401 assert_equal(enc, Dir.children(@root, encoding: enc).first.encoding)
406 assert_entries(Dir.open(@root) {|dir| dir.each_child.to_a}, true)
407 assert_entries(Dir.each_child(@root).to_a, true)
408 assert_raise(ArgumentError) {Dir.each_child(@root+"\0").to_a}
409 newdir = @root+"/new"
410 e = Dir.each_child(newdir)
411 assert_raise(Errno::ENOENT) {e.to_a}
413 File.write(newdir+"/a", "")
414 assert_equal(%w[a], e.to_a)
415 [Encoding::UTF_8, Encoding::ASCII_8BIT].each do |enc|
416 e = Dir.each_child(newdir, encoding: enc)
417 assert_equal(enc, e.to_a.first.encoding)
422 dir = Dir.open(@root, encoding: "UTF-8")
424 while name = dir.read
425 assert_equal(Encoding.find("UTF-8"), name.encoding)
431 dir = Dir.open(@root, encoding: "ASCII-8BIT")
433 while name = dir.read
434 assert_equal(Encoding.find("ASCII-8BIT"), name.encoding)
441 def test_unknown_keywords
442 bug8060 = '[ruby-dev:47152] [Bug #8060]'
443 assert_raise_with_message(ArgumentError, /unknown keyword/, bug8060) do
444 Dir.open(@root, xawqij: "a") {}
450 ["dummy", *"a".."z"].each do |f|
451 File.symlink(File.join(@root, f),
452 File.join(@root, "symlink-#{ f }"))
454 rescue NotImplementedError, Errno::EACCES
458 assert_equal([*"a".."z", *"symlink-a".."symlink-z"].each_slice(2).map {|f, _| File.join(@root, f + "/") }.sort,
459 Dir.glob(File.join(@root, "*/")))
461 assert_equal([@root + "/", *[*"a".."z"].each_slice(2).map {|f, _| File.join(@root, f + "/") }],
462 Dir.glob(File.join(@root, "**/")))
465 def test_glob_metachar
466 bug8597 = '[ruby-core:55764] [Bug #8597]'
467 assert_empty(Dir.glob(File.join(@root, "<")), bug8597)
471 feature5994 = "[ruby-core:42469] [Feature #5994]"
472 feature5994 << "\nDir.glob should return the filename with actual cases on the filesystem"
473 Dir.chdir(File.join(@root, "a")) do
474 open("FileWithCases", "w") {}
475 return unless File.exist?("filewithcases")
476 assert_equal(%w"FileWithCases", Dir.glob("filewithcases"), feature5994)
479 assert_equal(%w"a/FileWithCases", Dir.glob("A/filewithcases"), feature5994)
483 def test_glob_super_root
484 bug9648 = '[ruby-core:61552] [Bug #9648]'
485 roots = Dir.glob("/*")
486 assert_equal(roots.map {|n| "/..#{n}"}, Dir.glob("/../*"), bug9648)
489 if /mswin|mingw/ =~ RUBY_PLATFORM
490 def test_glob_legacy_short_name
491 bug10819 = '[ruby-core:67954] [Bug #10819]'
492 bug11206 = '[ruby-core:69435] [Bug #11206]'
493 omit unless /\A\w:/ =~ ENV["ProgramFiles"]
494 short = "#$&/PROGRA~1"
495 omit unless File.directory?(short)
496 entries = Dir.glob("#{short}/Common*")
497 assert_not_empty(entries, bug10819)
498 long = File.expand_path(short)
499 assert_equal(Dir.glob("#{long}/Common*"), entries, bug10819)
500 wild = short.sub(/1\z/, '*')
501 assert_not_include(Dir.glob(wild), long, bug11206)
502 assert_include(Dir.glob(wild, File::FNM_SHORTNAME), long, bug10819)
503 assert_empty(entries - Dir.glob("#{wild}/Common*", File::FNM_SHORTNAME), bug10819)
508 env_home = ENV["HOME"]
509 env_logdir = ENV["LOGDIR"]
514 assert_nothing_raised(ArgumentError) do
515 assert_equal(@nodir, Dir.home)
517 assert_nothing_raised(ArgumentError) do
518 assert_equal(@nodir, Dir.home(""))
520 if user = ENV["USER"]
521 tilde = windows? ? "~" : "~#{user}"
522 assert_nothing_raised(ArgumentError) do
523 assert_equal(File.expand_path(tilde), Dir.home(user))
526 %W[no:such:user \u{7559 5b88}:\u{756a}].each do |user|
527 assert_raise_with_message(ArgumentError, /#{user}/) {Dir.home(user)}
530 ENV["HOME"] = env_home
531 ENV["LOGDIR"] = env_logdir
534 def test_symlinks_not_resolved
535 Dir.mktmpdir do |dirname|
536 Dir.chdir(dirname) do
538 File.symlink('some-dir', 'dir-symlink')
539 rescue NotImplementedError, Errno::EACCES
543 Dir.mkdir('some-dir')
544 File.write('some-dir/foo', 'some content')
546 assert_equal [ 'dir-symlink', 'some-dir' ], Dir['*']
547 assert_equal [ 'dir-symlink', 'some-dir', 'some-dir/foo' ], Dir['**/*']
554 if d.respond_to? :fileno
555 assert_kind_of(Integer, d.fileno)
557 assert_raise(NotImplementedError) { d.fileno }
563 assert_not_send([Dir, :empty?, @root])
564 a = File.join(@root, "a")
565 assert_send([Dir, :empty?, a])
566 %w[A .dot].each do |tmp|
567 tmp = File.join(a, tmp)
569 assert_not_send([Dir, :empty?, a])
571 assert_send([Dir, :empty?, a])
573 assert_not_send([Dir, :empty?, a])
575 assert_send([Dir, :empty?, a])
577 assert_raise(Errno::ENOENT) {Dir.empty?(@nodir)}
578 assert_not_send([Dir, :empty?, File.join(@root, "b")])
579 assert_raise(ArgumentError) {Dir.empty?(@root+"\0")}
582 def test_glob_gc_for_fd
583 assert_separately(["-C", @root], "#{<<-"begin;"}\n#{<<-"end;"}", timeout: 3)
585 Process.setrlimit(Process::RLIMIT_NOFILE, 50)
588 tap {tap {tap {(0..100).each {fs << open(IO::NULL)}}}}
594 assert_not_empty(list)
595 assert_equal([*"a".."z"], list)
597 end if defined?(Process::RLIMIT_NOFILE)
599 def test_glob_array_with_destructive_element
600 args = Array.new(100, "")
601 pat = Struct.new(:ary).new(args)
602 args.push(pat, *Array.new(100) {"."*40})
608 assert_empty(Dir.glob(args))