diff options
Diffstat (limited to 'spec/ruby/core/dir')
29 files changed, 1130 insertions, 308 deletions
diff --git a/spec/ruby/core/dir/chdir_spec.rb b/spec/ruby/core/dir/chdir_spec.rb index 729ac403e3..2dc598e2a9 100644 --- a/spec/ruby/core/dir/chdir_spec.rb +++ b/spec/ruby/core/dir/chdir_spec.rb @@ -19,14 +19,14 @@ describe "Dir.chdir" do end it "defaults to $HOME with no arguments" do - if ENV['HOME'] - Dir.chdir - current_dir = Dir.pwd + skip "$HOME not valid directory" unless ENV['HOME'] && File.directory?(ENV['HOME']) - Dir.chdir(ENV['HOME']) - home = Dir.pwd - current_dir.should == home - end + Dir.chdir + current_dir = Dir.pwd + + Dir.chdir(ENV['HOME']) + home = Dir.pwd + current_dir.should == home end it "changes to the specified directory" do @@ -70,6 +70,8 @@ describe "Dir.chdir" do end it "defaults to the home directory when given a block but no argument" do + skip "$HOME not valid directory" unless ENV['HOME'] && File.directory?(ENV['HOME']) + # Windows will return a path with forward slashes for ENV["HOME"] so we have # to compare the route representations returned by Dir.chdir. current_dir = "" @@ -88,15 +90,15 @@ describe "Dir.chdir" do end it "raises an Errno::ENOENT if the directory does not exist" do - -> { Dir.chdir DirSpecs.nonexistent }.should raise_error(Errno::ENOENT) - -> { Dir.chdir(DirSpecs.nonexistent) { } }.should raise_error(Errno::ENOENT) + -> { Dir.chdir DirSpecs.nonexistent }.should.raise(Errno::ENOENT) + -> { Dir.chdir(DirSpecs.nonexistent) { } }.should.raise(Errno::ENOENT) end it "raises an Errno::ENOENT if the original directory no longer exists" do - dir1 = tmp('/testdir1') - dir2 = tmp('/testdir2') - File.should_not.exist?(dir1) - File.should_not.exist?(dir2) + dir1 = tmp('testdir1') + dir2 = tmp('testdir2') + Dir.should_not.exist?(dir1) + Dir.should_not.exist?(dir2) Dir.mkdir dir1 Dir.mkdir dir2 begin @@ -104,10 +106,10 @@ describe "Dir.chdir" do Dir.chdir dir1 do Dir.chdir(dir2) { Dir.unlink dir1 } end - }.should raise_error(Errno::ENOENT) + }.should.raise(Errno::ENOENT) ensure - Dir.unlink dir1 if File.exist?(dir1) - Dir.unlink dir2 if File.exist?(dir2) + Dir.unlink dir1 if Dir.exist?(dir1) + Dir.unlink dir2 if Dir.exist?(dir2) end end @@ -122,3 +124,95 @@ describe "Dir.chdir" do Dir.pwd.should == @original end end + +describe "Dir#chdir" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + before :each do + @original = Dir.pwd + end + + after :each do + Dir.chdir(@original) + end + + it "changes the current working directory to self" do + dir = Dir.new(DirSpecs.mock_dir) + dir.chdir + Dir.pwd.should == DirSpecs.mock_dir + ensure + dir.close + end + + it "changes the current working directory to self for duration of the block when a block is given" do + dir = Dir.new(DirSpecs.mock_dir) + pwd_in_block = nil + + dir.chdir { pwd_in_block = Dir.pwd } + + pwd_in_block.should == DirSpecs.mock_dir + Dir.pwd.should == @original + ensure + dir.close + end + + it "returns 0 when successfully changing directory" do + dir = Dir.new(DirSpecs.mock_dir) + dir.chdir.should == 0 + ensure + dir.close + end + + it "returns the value of the block when a block is given" do + dir = Dir.new(DirSpecs.mock_dir) + dir.chdir { :block_value }.should == :block_value + ensure + dir.close + end + + platform_is_not :windows do + it "does not raise an Errno::ENOENT if the original directory no longer exists" do + dir_name1 = tmp('testdir1') + dir_name2 = tmp('testdir2') + Dir.should_not.exist?(dir_name1) + Dir.should_not.exist?(dir_name2) + Dir.mkdir dir_name1 + Dir.mkdir dir_name2 + + dir2 = Dir.new(dir_name2) + + begin + Dir.chdir(dir_name1) do + dir2.chdir { Dir.unlink dir_name1 } + end + Dir.pwd.should == @original + ensure + Dir.unlink dir_name1 if Dir.exist?(dir_name1) + Dir.unlink dir_name2 if Dir.exist?(dir_name2) + end + ensure + dir2.close + end + end + + it "always returns to the original directory when given a block" do + dir = Dir.new(DirSpecs.mock_dir) + + begin + dir.chdir do + raise StandardError, "something bad happened" + end + rescue StandardError + end + + Dir.pwd.should == @original + ensure + dir.close + end +end diff --git a/spec/ruby/core/dir/children_spec.rb b/spec/ruby/core/dir/children_spec.rb index e0325a24b8..6e6da1dd44 100644 --- a/spec/ruby/core/dir/children_spec.rb +++ b/spec/ruby/core/dir/children_spec.rb @@ -47,90 +47,101 @@ describe "Dir.children" do encoding = Encoding.find("filesystem") encoding = Encoding::BINARY if encoding == Encoding::US_ASCII platform_is_not :windows do - children.should include("こんにちは.txt".force_encoding(encoding)) + children.should.include?("こんにちは.txt".dup.force_encoding(encoding)) end - children.first.encoding.should equal(Encoding.find("filesystem")) + children.first.encoding.should.equal?(Encoding.find("filesystem")) end it "returns children encoded with the specified encoding" do dir = File.join(DirSpecs.mock_dir, 'special') children = Dir.children(dir, encoding: "euc-jp").sort - children.first.encoding.should equal(Encoding::EUC_JP) + children.first.encoding.should.equal?(Encoding::EUC_JP) end it "returns children transcoded to the default internal encoding" do Encoding.default_internal = Encoding::EUC_KR children = Dir.children(File.join(DirSpecs.mock_dir, 'special')).sort - children.first.encoding.should equal(Encoding::EUC_KR) + children.first.encoding.should.equal?(Encoding::EUC_KR) end it "raises a SystemCallError if called with a nonexistent directory" do - -> { Dir.children DirSpecs.nonexistent }.should raise_error(SystemCallError) + -> { Dir.children DirSpecs.nonexistent }.should.raise(SystemCallError) end end -ruby_version_is "2.6" do - describe "Dir#children" do - before :all do - DirSpecs.create_mock_dirs - end +describe "Dir#children" do + before :all do + DirSpecs.create_mock_dirs + end - before :each do - @internal = Encoding.default_internal - end + before :each do + @internal = Encoding.default_internal + end - after :all do - DirSpecs.delete_mock_dirs - end + after :all do + DirSpecs.delete_mock_dirs + end - after :each do - Encoding.default_internal = @internal - @dir.close if @dir - end + after :each do + Encoding.default_internal = @internal + @dir.close if @dir + end - it "returns an Array of filenames in an existing directory including dotfiles" do - @dir = Dir.new(DirSpecs.mock_dir) - a = @dir.children.sort - @dir.close + it "returns an Array of filenames in an existing directory including dotfiles" do + @dir = Dir.new(DirSpecs.mock_dir) + a = @dir.children.sort + @dir.close - a.should == DirSpecs.expected_paths - %w[. ..] + a.should == DirSpecs.expected_paths - %w[. ..] - @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested") - a = @dir.children.sort - a.should == %w|.dotfile.ext directory| - end + @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested") + a = @dir.children.sort + a.should == %w|.dotfile.ext directory| + end - it "accepts an options Hash" do - @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8") - a = @dir.children.sort - a.should == %w|.dotfile.ext directory| - end + it "accepts an encoding keyword for the encoding of the entries" do + @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8") + dirs = @dir.to_a.sort + dirs.each { |d| d.encoding.should == Encoding::UTF_8 } + end - it "returns children encoded with the filesystem encoding by default" do - # This spec depends on the locale not being US-ASCII because if it is, the - # children that are not ascii_only? will be BINARY encoded. - @dir = Dir.new(File.join(DirSpecs.mock_dir, 'special')) - children = @dir.children.sort - encoding = Encoding.find("filesystem") - encoding = Encoding::BINARY if encoding == Encoding::US_ASCII - platform_is_not :windows do - children.should include("こんにちは.txt".force_encoding(encoding)) - end - children.first.encoding.should equal(Encoding.find("filesystem")) + it "returns children encoded with the filesystem encoding by default" do + # This spec depends on the locale not being US-ASCII because if it is, the + # children that are not ascii_only? will be BINARY encoded. + @dir = Dir.new(File.join(DirSpecs.mock_dir, 'special')) + children = @dir.children.sort + encoding = Encoding.find("filesystem") + encoding = Encoding::BINARY if encoding == Encoding::US_ASCII + platform_is_not :windows do + children.should.include?("こんにちは.txt".dup.force_encoding(encoding)) end + children.first.encoding.should.equal?(Encoding.find("filesystem")) + end - it "returns children encoded with the specified encoding" do - path = File.join(DirSpecs.mock_dir, 'special') - @dir = Dir.new(path, encoding: "euc-jp") - children = @dir.children.sort - children.first.encoding.should equal(Encoding::EUC_JP) - end + it "returns children encoded with the specified encoding" do + path = File.join(DirSpecs.mock_dir, 'special') + @dir = Dir.new(path, encoding: "euc-jp") + children = @dir.children.sort + children.first.encoding.should.equal?(Encoding::EUC_JP) + end - it "returns children transcoded to the default internal encoding" do - Encoding.default_internal = Encoding::EUC_KR - @dir = Dir.new(File.join(DirSpecs.mock_dir, 'special')) - children = @dir.children.sort - children.first.encoding.should equal(Encoding::EUC_KR) - end + it "returns children transcoded to the default internal encoding" do + Encoding.default_internal = Encoding::EUC_KR + @dir = Dir.new(File.join(DirSpecs.mock_dir, 'special')) + children = @dir.children.sort + children.first.encoding.should.equal?(Encoding::EUC_KR) + end + + it "returns the same result when called repeatedly" do + @dir = Dir.open DirSpecs.mock_dir + + a = [] + @dir.each {|dir| a << dir} + + b = [] + @dir.each {|dir| b << dir} + + a.sort.should == b.sort + a.sort.should == DirSpecs.expected_paths end end diff --git a/spec/ruby/core/dir/chroot_spec.rb b/spec/ruby/core/dir/chroot_spec.rb index a5ca8943fc..79ad9759b0 100644 --- a/spec/ruby/core/dir/chroot_spec.rb +++ b/spec/ruby/core/dir/chroot_spec.rb @@ -21,17 +21,17 @@ platform_is_not :windows do end it "raises an Errno::EPERM exception if the directory exists" do - -> { Dir.chroot('.') }.should raise_error(Errno::EPERM) + -> { Dir.chroot('.') }.should.raise(Errno::EPERM) end it "raises a SystemCallError if the directory doesn't exist" do - -> { Dir.chroot('xgwhwhsjai2222jg') }.should raise_error(SystemCallError) + -> { Dir.chroot('xgwhwhsjai2222jg') }.should.raise(SystemCallError) end it "calls #to_path on non-String argument" do p = mock('path') p.should_receive(:to_path).and_return('.') - -> { Dir.chroot(p) }.should raise_error(Errno::EPERM) + -> { Dir.chroot(p) }.should.raise(Errno::EPERM) end end end diff --git a/spec/ruby/core/dir/close_spec.rb b/spec/ruby/core/dir/close_spec.rb index 5fad5eecfb..9902d98934 100644 --- a/spec/ruby/core/dir/close_spec.rb +++ b/spec/ruby/core/dir/close_spec.rb @@ -11,9 +11,43 @@ describe "Dir#close" do it "does not raise an IOError even if the Dir instance is closed" do dir = Dir.open DirSpecs.mock_dir - dir.close - -> { - dir.close - }.should_not raise_error(IOError) + dir.close.should == nil + dir.close.should == nil + + platform_is_not :windows do + -> { dir.fileno }.should.raise(IOError, /closed directory/) + end + end + + it "returns nil" do + dir = Dir.open DirSpecs.mock_dir + dir.close.should == nil + end + + ruby_version_is ''...'3.4' do + platform_is_not :windows do + it "does not raise an error even if the file descriptor is closed with another Dir instance" do + dir = Dir.open DirSpecs.mock_dir + dir_new = Dir.for_fd(dir.fileno) + + dir.close + dir_new.close + + -> { dir.fileno }.should.raise(IOError, /closed directory/) + -> { dir_new.fileno }.should.raise(IOError, /closed directory/) + end + end + end + + ruby_version_is '3.4' do + platform_is_not :windows do + it "raises an error if the file descriptor is closed with another Dir instance" do + dir = Dir.open DirSpecs.mock_dir + dir_new = Dir.for_fd(dir.fileno) + dir.close + + -> { dir_new.close }.should.raise(Errno::EBADF, 'Bad file descriptor - closedir') + end + end end end diff --git a/spec/ruby/core/dir/each_child_spec.rb b/spec/ruby/core/dir/each_child_spec.rb index 93b4a1aec1..4d6575df39 100644 --- a/spec/ruby/core/dir/each_child_spec.rb +++ b/spec/ruby/core/dir/each_child_spec.rb @@ -10,6 +10,11 @@ describe "Dir.each_child" do DirSpecs.delete_mock_dirs end + it "accepts an encoding keyword for the encoding of the entries" do + dirs = Dir.each_child("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").to_a.sort + dirs.each {|dir| dir.encoding.should == Encoding::UTF_8} + end + it "yields all names in an existing directory to the provided block" do a, b = [], [] @@ -31,12 +36,12 @@ describe "Dir.each_child" do end it "raises a SystemCallError if passed a nonexistent directory" do - -> { Dir.each_child(DirSpecs.nonexistent) {} }.should raise_error(SystemCallError) + -> { Dir.each_child(DirSpecs.nonexistent) {} }.should.raise(SystemCallError) end describe "when no block is given" do it "returns an Enumerator" do - Dir.each_child(DirSpecs.mock_dir).should be_an_instance_of(Enumerator) + Dir.each_child(DirSpecs.mock_dir).should.instance_of?(Enumerator) Dir.each_child(DirSpecs.mock_dir).to_a.sort.should == DirSpecs.expected_paths - %w[. ..] end @@ -50,52 +55,63 @@ describe "Dir.each_child" do end end -ruby_version_is "2.6" do - describe "Dir#each_child" do - before :all do - DirSpecs.create_mock_dirs - end +describe "Dir#each_child" do + before :all do + DirSpecs.create_mock_dirs + end - after :all do - DirSpecs.delete_mock_dirs - end + after :all do + DirSpecs.delete_mock_dirs + end - after :each do - @dir.close if @dir - end + after :each do + @dir.close if @dir + end - it "yields all names in an existing directory to the provided block" do - a, b = [], [] - @dir = Dir.new(DirSpecs.mock_dir) - @dir2 = Dir.new("#{DirSpecs.mock_dir}/deeply/nested") + it "yields all names in an existing directory to the provided block" do + a, b = [], [] + @dir = Dir.new(DirSpecs.mock_dir) + @dir2 = Dir.new("#{DirSpecs.mock_dir}/deeply/nested") - @dir.each_child { |f| a << f } - @dir2.each_child { |f| b << f } - @dir2.close + @dir.each_child { |f| a << f } + @dir2.each_child { |f| b << f } + @dir2.close - a.sort.should == DirSpecs.expected_paths - %w|. ..| - b.sort.should == %w|.dotfile.ext directory| - end + a.sort.should == DirSpecs.expected_paths - %w|. ..| + b.sort.should == %w|.dotfile.ext directory| + end - it "returns self when successful" do - @dir = Dir.new(DirSpecs.mock_dir) - @dir.each_child { |f| f }.should == @dir - end + it "returns self when successful" do + @dir = Dir.new(DirSpecs.mock_dir) + @dir.each_child { |f| f }.should == @dir + end - describe "when no block is given" do - it "returns an Enumerator" do - @dir = Dir.new(DirSpecs.mock_dir) + it "returns the same result when called repeatedly" do + @dir = Dir.open DirSpecs.mock_dir - @dir.each_child.should be_an_instance_of(Enumerator) - @dir.each_child.to_a.sort.should == DirSpecs.expected_paths - %w|. ..| - end + a = [] + @dir.each {|dir| a << dir} - describe "returned Enumerator" do - describe "size" do - it "should return nil" do - @dir = Dir.new(DirSpecs.mock_dir) - @dir.each_child.size.should == nil - end + b = [] + @dir.each {|dir| b << dir} + + a.sort.should == b.sort + a.sort.should == DirSpecs.expected_paths + end + + describe "when no block is given" do + it "returns an Enumerator" do + @dir = Dir.new(DirSpecs.mock_dir) + + @dir.each_child.should.instance_of?(Enumerator) + @dir.each_child.to_a.sort.should == DirSpecs.expected_paths - %w|. ..| + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + @dir = Dir.new(DirSpecs.mock_dir) + @dir.each_child.size.should == nil end end end diff --git a/spec/ruby/core/dir/each_spec.rb b/spec/ruby/core/dir/each_spec.rb index 8c69a7212b..e997e340b1 100644 --- a/spec/ruby/core/dir/each_spec.rb +++ b/spec/ruby/core/dir/each_spec.rb @@ -32,12 +32,23 @@ describe "Dir#each" do @dir.each {}.should == @dir @dir.read.should == nil @dir.rewind - ls.should include(@dir.read) + ls.should.include?(@dir.read) + end + + it "returns the same result when called repeatedly" do + a = [] + @dir.each {|dir| a << dir} + + b = [] + @dir.each {|dir| b << dir} + + a.sort.should == b.sort + a.sort.should == DirSpecs.expected_paths end describe "when no block is given" do it "returns an Enumerator" do - @dir.each.should be_an_instance_of(Enumerator) + @dir.each.should.instance_of?(Enumerator) @dir.each.to_a.sort.should == DirSpecs.expected_paths end diff --git a/spec/ruby/core/dir/empty_spec.rb b/spec/ruby/core/dir/empty_spec.rb index 8cc8757798..3b6b2bac85 100644 --- a/spec/ruby/core/dir/empty_spec.rb +++ b/spec/ruby/core/dir/empty_spec.rb @@ -12,20 +12,20 @@ describe "Dir.empty?" do it "returns true for empty directories" do result = Dir.empty? @empty_dir - result.should be_true + result.should == true end it "returns false for non-empty directories" do result = Dir.empty? __dir__ - result.should be_false + result.should == false end it "returns false for a non-directory" do result = Dir.empty? __FILE__ - result.should be_false + result.should == false end it "raises ENOENT for nonexistent directories" do - -> { Dir.empty? tmp("nonexistent") }.should raise_error(Errno::ENOENT) + -> { Dir.empty? tmp("nonexistent") }.should.raise(Errno::ENOENT) end end diff --git a/spec/ruby/core/dir/entries_spec.rb b/spec/ruby/core/dir/entries_spec.rb index 33568b6fc4..f3ca49b26d 100644 --- a/spec/ruby/core/dir/entries_spec.rb +++ b/spec/ruby/core/dir/entries_spec.rb @@ -35,9 +35,9 @@ describe "Dir.entries" do Dir.entries(p) end - it "accepts an options Hash" do - a = Dir.entries("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").sort - a.should == %w|. .. .dotfile.ext directory| + it "accepts an encoding keyword for the encoding of the entries" do + dirs = Dir.entries("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").to_a.sort + dirs.each {|dir| dir.encoding.should == Encoding::UTF_8} end it "returns entries encoded with the filesystem encoding by default" do @@ -47,24 +47,24 @@ describe "Dir.entries" do encoding = Encoding.find("filesystem") encoding = Encoding::BINARY if encoding == Encoding::US_ASCII platform_is_not :windows do - entries.should include("こんにちは.txt".force_encoding(encoding)) + entries.should.include?("こんにちは.txt".dup.force_encoding(encoding)) end - entries.first.encoding.should equal(Encoding.find("filesystem")) + entries.first.encoding.should.equal?(Encoding.find("filesystem")) end it "returns entries encoded with the specified encoding" do dir = File.join(DirSpecs.mock_dir, 'special') entries = Dir.entries(dir, encoding: "euc-jp").sort - entries.first.encoding.should equal(Encoding::EUC_JP) + entries.first.encoding.should.equal?(Encoding::EUC_JP) end it "returns entries transcoded to the default internal encoding" do Encoding.default_internal = Encoding::EUC_KR entries = Dir.entries(File.join(DirSpecs.mock_dir, 'special')).sort - entries.first.encoding.should equal(Encoding::EUC_KR) + entries.first.encoding.should.equal?(Encoding::EUC_KR) end it "raises a SystemCallError if called with a nonexistent directory" do - -> { Dir.entries DirSpecs.nonexistent }.should raise_error(SystemCallError) + -> { Dir.entries DirSpecs.nonexistent }.should.raise(SystemCallError) end end diff --git a/spec/ruby/core/dir/exist_spec.rb b/spec/ruby/core/dir/exist_spec.rb index 43987b0f32..0b8e521894 100644 --- a/spec/ruby/core/dir/exist_spec.rb +++ b/spec/ruby/core/dir/exist_spec.rb @@ -13,3 +13,9 @@ describe "Dir.exist?" do it_behaves_like :dir_exist, :exist? end + +describe "Dir.exists?" do + it "has been removed" do + Dir.should_not.respond_to?(:exists?) + end +end diff --git a/spec/ruby/core/dir/fchdir_spec.rb b/spec/ruby/core/dir/fchdir_spec.rb new file mode 100644 index 0000000000..bd1a92b05e --- /dev/null +++ b/spec/ruby/core/dir/fchdir_spec.rb @@ -0,0 +1,71 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/common' + +platform_is_not :windows do + describe "Dir.fchdir" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + before :each do + @original = Dir.pwd + end + + after :each do + Dir.chdir(@original) + end + + it "changes the current working directory to the directory specified by the integer file descriptor" do + dir = Dir.new(DirSpecs.mock_dir) + Dir.fchdir dir.fileno + Dir.pwd.should == DirSpecs.mock_dir + ensure + dir.close + end + + it "returns 0 when successfully changing directory" do + dir = Dir.new(DirSpecs.mock_dir) + Dir.fchdir(dir.fileno).should == 0 + ensure + dir.close + end + + it "returns the value of the block when a block is given" do + dir = Dir.new(DirSpecs.mock_dir) + Dir.fchdir(dir.fileno) { :block_value }.should == :block_value + ensure + dir.close + end + + it "changes to the specified directory for the duration of the block" do + dir = Dir.new(DirSpecs.mock_dir) + Dir.fchdir(dir.fileno) { Dir.pwd }.should == DirSpecs.mock_dir + Dir.pwd.should == @original + ensure + dir.close + end + + it "raises a SystemCallError if the file descriptor given is not valid" do + -> { Dir.fchdir(-1) }.should.raise(SystemCallError, "Bad file descriptor - fchdir") + -> { Dir.fchdir(-1) { } }.should.raise(SystemCallError, "Bad file descriptor - fchdir") + end + + it "raises a SystemCallError if the file descriptor given is not for a directory" do + -> { Dir.fchdir $stdout.fileno }.should.raise(SystemCallError, /(Not a directory|Invalid argument) - fchdir/) + -> { Dir.fchdir($stdout.fileno) { } }.should.raise(SystemCallError, /(Not a directory|Invalid argument) - fchdir/) + end + end +end + +platform_is :windows do + describe "Dir.fchdir" do + it "raises NotImplementedError" do + -> { Dir.fchdir 1 }.should.raise(NotImplementedError) + -> { Dir.fchdir(1) { } }.should.raise(NotImplementedError) + end + end +end diff --git a/spec/ruby/core/dir/fileno_spec.rb b/spec/ruby/core/dir/fileno_spec.rb index bb84ef5378..3b563eb18f 100644 --- a/spec/ruby/core/dir/fileno_spec.rb +++ b/spec/ruby/core/dir/fileno_spec.rb @@ -27,11 +27,11 @@ describe "Dir#fileno" do if has_dir_fileno it "returns the file descriptor of the dir" do - @dir.fileno.should be_kind_of(Integer) + @dir.fileno.should.is_a?(Integer) end else it "raises an error when not implemented on the platform" do - -> { @dir.fileno }.should raise_error(NotImplementedError) + -> { @dir.fileno }.should.raise(NotImplementedError) end end end diff --git a/spec/ruby/core/dir/fixtures/common.rb b/spec/ruby/core/dir/fixtures/common.rb index a1ea3db215..cfec91f68f 100644 --- a/spec/ruby/core/dir/fixtures/common.rb +++ b/spec/ruby/core/dir/fixtures/common.rb @@ -81,6 +81,8 @@ module DirSpecs special/} special/test{1}/file[1] + special/{}/special + special/test\ +()[]{}/hello_world.erb ] platform_is_not :windows do @@ -91,19 +93,40 @@ module DirSpecs special/| special/こんにちは.txt + special/\a ] + @mock_dir_files << "special/_\u{1f60e}.erb" end end @mock_dir_files end + def self.mock_dir_links + unless @mock_dir_links + @mock_dir_links = [] + platform_is_not :windows do + @mock_dir_links += [ + ['special/ln', 'subdir_one'] + ] + end + end + @mock_dir_links + end + def self.create_mock_dirs + delete_mock_dirs mock_dir_files.each do |name| file = File.join mock_dir, name mkdir_p File.dirname(file) touch file end + mock_dir_links.each do |link, target| + full_link = File.join mock_dir, link + full_target = File.join mock_dir, target + + File.symlink full_target, full_link + end end def self.delete_mock_dirs @@ -150,33 +173,31 @@ module DirSpecs end end + def self.expected_paths_with_type + [ + [".", :directory], + ["..", :directory], + [".dotfile", :file], + [".dotsubdir", :directory], + ["brace", :directory], + ["deeply", :directory], + ["dir", :directory], + ["dir_filename_ordering", :file], + ["file_one.ext", :file], + ["file_two.ext", :file], + ["nested", :directory], + ["nondotfile", :file], + ["special", :directory], + ["subdir_one", :directory], + ["subdir_two", :directory], + ] + end + def self.expected_paths - %w[ - . - .. - .dotfile - .dotsubdir - brace - deeply - dir - dir_filename_ordering - file_one.ext - file_two.ext - nested - nondotfile - special - subdir_one - subdir_two - ] + expected_paths_with_type.map(&:first) end - if RUBY_VERSION > '3.1' - def self.expected_glob_paths - expected_paths - ['..'] - end - else - def self.expected_glob_paths - expected_paths - end + def self.expected_glob_paths + expected_paths - ['..'] end end diff --git a/spec/ruby/core/dir/for_fd_spec.rb b/spec/ruby/core/dir/for_fd_spec.rb new file mode 100644 index 0000000000..bbc75e0f8f --- /dev/null +++ b/spec/ruby/core/dir/for_fd_spec.rb @@ -0,0 +1,77 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/common' + +quarantine! do # leads to "Errno::EBADF: Bad file descriptor - closedir" in DirSpecs.delete_mock_dirs +platform_is_not :windows do + describe "Dir.for_fd" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + before :each do + @original = Dir.pwd + end + + after :each do + Dir.chdir(@original) + end + + it "returns a new Dir object representing the directory specified by the given integer directory file descriptor" do + dir = Dir.new(DirSpecs.mock_dir) + dir_new = Dir.for_fd(dir.fileno) + + dir_new.should.instance_of?(Dir) + dir_new.children.should == dir.children + dir_new.fileno.should == dir.fileno + ensure + dir.close + end + + it "returns a new Dir object without associated path" do + dir = Dir.new(DirSpecs.mock_dir) + dir_new = Dir.for_fd(dir.fileno) + + dir_new.path.should == nil + ensure + dir.close + end + + it "calls #to_int to convert a value to an Integer" do + dir = Dir.new(DirSpecs.mock_dir) + obj = mock("fd") + obj.should_receive(:to_int).and_return(dir.fileno) + + dir_new = Dir.for_fd(obj) + dir_new.fileno.should == dir.fileno + ensure + dir.close + end + + it "raises TypeError when value cannot be converted to Integer" do + -> { + Dir.for_fd(nil) + }.should raise_consistent_error(TypeError, "no implicit conversion of nil into Integer") + end + + it "raises a SystemCallError if the file descriptor given is not valid" do + -> { Dir.for_fd(-1) }.should.raise(SystemCallError, "Bad file descriptor - fdopendir") + end + + it "raises a SystemCallError if the file descriptor given is not for a directory" do + -> { Dir.for_fd $stdout.fileno }.should.raise(SystemCallError, "Not a directory - fdopendir") + end + end +end + +platform_is :windows do + describe "Dir.for_fd" do + it "raises NotImplementedError" do + -> { Dir.for_fd 1 }.should.raise(NotImplementedError) + end + end +end +end diff --git a/spec/ruby/core/dir/foreach_spec.rb b/spec/ruby/core/dir/foreach_spec.rb index 1560b85f8a..2a2265a029 100644 --- a/spec/ruby/core/dir/foreach_spec.rb +++ b/spec/ruby/core/dir/foreach_spec.rb @@ -31,17 +31,29 @@ describe "Dir.foreach" do end it "raises a SystemCallError if passed a nonexistent directory" do - -> { Dir.foreach(DirSpecs.nonexistent) {} }.should raise_error(SystemCallError) + -> { Dir.foreach(DirSpecs.nonexistent) {} }.should.raise(SystemCallError) end it "returns an Enumerator if no block given" do - Dir.foreach(DirSpecs.mock_dir).should be_an_instance_of(Enumerator) + Dir.foreach(DirSpecs.mock_dir).should.instance_of?(Enumerator) Dir.foreach(DirSpecs.mock_dir).to_a.sort.should == DirSpecs.expected_paths end + it "accepts an encoding keyword for the encoding of the entries" do + dirs = Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").to_a.sort + dirs.each { |dir| dir.encoding.should == Encoding::UTF_8 } + + dirs = Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: Encoding::ISO_8859_1).to_a.sort + dirs.each { |dir| dir.encoding.should == Encoding::ISO_8859_1 } + + Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: Encoding::ISO_8859_1) do |f| + f.encoding.should == Encoding::ISO_8859_1 + end + end + describe "when no block is given" do it "returns an Enumerator" do - Dir.foreach(DirSpecs.mock_dir).should be_an_instance_of(Enumerator) + Dir.foreach(DirSpecs.mock_dir).should.instance_of?(Enumerator) Dir.foreach(DirSpecs.mock_dir).to_a.sort.should == DirSpecs.expected_paths end diff --git a/spec/ruby/core/dir/glob_spec.rb b/spec/ruby/core/dir/glob_spec.rb index 61553ab0b1..9e81feb15f 100644 --- a/spec/ruby/core/dir/glob_spec.rb +++ b/spec/ruby/core/dir/glob_spec.rb @@ -29,6 +29,23 @@ describe "Dir.glob" do %w!file_one.ext file_two.ext! end + it 'returns matching file paths when supplied :base keyword argument' do + dir = tmp('dir_glob_base') + file_1 = "#{dir}/lib/bloop.rb" + file_2 = "#{dir}/lib/soup.rb" + file_3 = "#{dir}/lib/mismatched_file_type.txt" + file_4 = "#{dir}/mismatched_directory.rb" + + touch file_1 + touch file_2 + touch file_3 + touch file_4 + + Dir.glob('**/*.rb', base: "#{dir}/lib").sort.should == ["bloop.rb", "soup.rb"].sort + ensure + rm_r dir + end + it "calls #to_path to convert multiple patterns" do pat1 = mock('file_one.ext') pat1.should_receive(:to_path).and_return('file_one.ext') @@ -62,7 +79,9 @@ describe "Dir.glob" do nested/ nested/.dotsubir/ special/ + special/test\ +()[]{}/ special/test{1}/ + special/{}/ subdir_one/ subdir_two/ ] @@ -70,31 +89,15 @@ describe "Dir.glob" do Dir.glob('**/', File::FNM_DOTMATCH).sort.should == expected end - ruby_version_is ''...'3.1' do - it "recursively matches files and directories in nested dot subdirectory with 'nested/**/*' from the current directory and option File::FNM_DOTMATCH" do - expected = %w[ - nested/. - nested/.dotsubir - nested/.dotsubir/. - nested/.dotsubir/.dotfile - nested/.dotsubir/nondotfile - ] - - Dir.glob('nested/**/*', File::FNM_DOTMATCH).sort.should == expected.sort - end - end - - ruby_version_is '3.1' do - it "recursively matches files and directories in nested dot subdirectory except . with 'nested/**/*' from the current directory and option File::FNM_DOTMATCH" do - expected = %w[ - nested/. - nested/.dotsubir - nested/.dotsubir/.dotfile - nested/.dotsubir/nondotfile - ] + it "recursively matches files and directories in nested dot subdirectory except . with 'nested/**/*' from the current directory and option File::FNM_DOTMATCH" do + expected = %w[ + nested/. + nested/.dotsubir + nested/.dotsubir/.dotfile + nested/.dotsubir/nondotfile + ] - Dir.glob('nested/**/*', File::FNM_DOTMATCH).sort.should == expected.sort - end + Dir.glob('nested/**/*', File::FNM_DOTMATCH).sort.should == expected.sort end # This is a separate case to check **/ coming after a constant @@ -112,7 +115,9 @@ describe "Dir.glob" do ./nested/ ./nested/.dotsubir/ ./special/ + ./special/test\ +()[]{}/ ./special/test{1}/ + ./special/{}/ ./subdir_one/ ./subdir_two/ ] @@ -133,10 +138,18 @@ describe "Dir.glob" do Dir.glob('{deeply/**/,subdir_two/*}').sort.should == expected end + it "preserves multiple /s before a **" do + expected = %w[ + deeply//nested/directory/structure + ] + + Dir.glob('{deeply//**/structure}').sort.should == expected + end + it "accepts a block and yields it with each elements" do ary = [] ret = Dir.glob(["file_o*", "file_t*"]) { |t| ary << t } - ret.should be_nil + ret.should == nil ary.should == %w!file_one.ext file_two.ext! end @@ -153,6 +166,131 @@ describe "Dir.glob" do Dir.glob('**/**/**').should_not.empty? end + it "handles **/** with base keyword argument" do + Dir.glob('**/**', base: "dir").should == ["filename_ordering"] + + expected = %w[ + nested + nested/directory + nested/directory/structure + nested/directory/structure/bar + nested/directory/structure/baz + nested/directory/structure/file_one + nested/directory/structure/file_one.ext + nested/directory/structure/foo + nondotfile + ].sort + + Dir.glob('**/**', base: "deeply").sort.should == expected + end + + it "handles **/ with base keyword argument" do + expected = %w[ + / + directory/ + directory/structure/ + ] + Dir.glob('**/', base: "deeply/nested").sort.should == expected + end + + it "handles **/nondotfile with base keyword argument" do + expected = %w[ + deeply/nondotfile + nondotfile + subdir_one/nondotfile + subdir_two/nondotfile + ] + Dir.glob('**/nondotfile', base: ".").sort.should == expected + end + + it "handles **/nondotfile with base keyword argument and FNM_DOTMATCH" do + expected = %w[ + .dotsubdir/nondotfile + deeply/nondotfile + nested/.dotsubir/nondotfile + nondotfile + subdir_one/nondotfile + subdir_two/nondotfile + ] + Dir.glob('**/nondotfile', File::FNM_DOTMATCH, base: ".").sort.should == expected + end + + it "handles **/.dotfile with base keyword argument" do + expected = %w[ + .dotfile + deeply/.dotfile + subdir_one/.dotfile + ] + Dir.glob('**/.dotfile', base: ".").sort.should == expected + end + + it "handles **/.dotfile with base keyword argument and FNM_DOTMATCH" do + expected = %w[ + .dotfile + .dotsubdir/.dotfile + deeply/.dotfile + nested/.dotsubir/.dotfile + subdir_one/.dotfile + ] + Dir.glob('**/.dotfile', File::FNM_DOTMATCH, base: ".").sort.should == expected + end + + it "handles **/.* with base keyword argument" do + expected = %w[ + .dotfile.ext + directory/structure/.ext + ].sort + + Dir.glob('**/.*', base: "deeply/nested").sort.should == expected + end + + it "handles **/.* with base keyword argument and FNM_DOTMATCH" do + expected = %w[ + . + .dotfile.ext + directory/structure/.ext + ].sort + + Dir.glob('**/.*', File::FNM_DOTMATCH, base: "deeply/nested").sort.should == expected + end + + it "handles **/** with base keyword argument and FNM_DOTMATCH" do + expected = %w[ + . + .dotfile.ext + directory + directory/structure + directory/structure/.ext + directory/structure/bar + directory/structure/baz + directory/structure/file_one + directory/structure/file_one.ext + directory/structure/foo + ].sort + + Dir.glob('**/**', File::FNM_DOTMATCH, base: "deeply/nested").sort.should == expected + end + + it "handles **/*pattern* with base keyword argument and FNM_DOTMATCH" do + expected = %w[ + .dotfile.ext + directory/structure/file_one + directory/structure/file_one.ext + ] + + Dir.glob('**/*file*', File::FNM_DOTMATCH, base: "deeply/nested").sort.should == expected + end + + it "handles **/glob with base keyword argument and FNM_EXTGLOB" do + expected = %w[ + directory/structure/bar + directory/structure/file_one + directory/structure/file_one.ext + ] + + Dir.glob('**/*{file,bar}*', File::FNM_EXTGLOB, base: "deeply/nested").sort.should == expected + end + it "handles simple filename patterns" do Dir.glob('.dotfile').should == ['.dotfile'] end @@ -195,5 +333,30 @@ describe "Dir.glob" do Dir.rmdir('no_permission') end end + + it "will follow symlinks when processing a `*/` pattern." do + expected = ['special/ln/nondotfile'] + Dir.glob('special/*/nondotfile').should == expected + end + + it "will not follow symlinks when recursively traversing directories" do + expected = %w[ + deeply/nondotfile + nondotfile + subdir_one/nondotfile + subdir_two/nondotfile + ] + Dir.glob('**/nondotfile').sort.should == expected + end + + it "will follow symlinks when testing directory after recursive directory in pattern" do + expected = %w[ + deeply/nondotfile + special/ln/nondotfile + subdir_one/nondotfile + subdir_two/nondotfile + ] + Dir.glob('**/*/nondotfile').sort.should == expected + end end end diff --git a/spec/ruby/core/dir/home_spec.rb b/spec/ruby/core/dir/home_spec.rb index 713ba9db9a..f0b20e0687 100644 --- a/spec/ruby/core/dir/home_spec.rb +++ b/spec/ruby/core/dir/home_spec.rb @@ -19,16 +19,46 @@ describe "Dir.home" do it "returns a non-frozen string" do Dir.home.should_not.frozen? end - end - describe "when called with the current user name" do - platform_is :solaris do - it "returns the named user's home directory from the user database" do - Dir.home(ENV['USER']).should == `getent passwd #{ENV['USER']}|cut -d: -f6`.chomp + it "returns a string with the filesystem encoding" do + Dir.home.encoding.should == Encoding.find("filesystem") + end + + platform_is_not :windows do + it "works even if HOME is unset" do + ENV.delete('HOME') + Dir.home.should.start_with?('/') + Dir.home.encoding.should == Encoding.find("filesystem") + end + end + + platform_is :windows do + it "returns the home directory with forward slashs and as UTF-8" do + ENV['HOME'] = "C:\\rubyspäc\\home" + home = Dir.home + home.should == "C:/rubyspäc/home" + home.encoding.should == Encoding::UTF_8 + end + + it "retrieves the directory from HOME, USERPROFILE, HOMEDRIVE/HOMEPATH and the WinAPI in that order" do + old_dirs = [ENV.delete('HOME'), ENV.delete('USERPROFILE'), ENV.delete('HOMEDRIVE'), ENV.delete('HOMEPATH')] + + Dir.home.should == old_dirs[1].gsub("\\", "/") + ENV['HOMEDRIVE'] = "C:" + ENV['HOMEPATH'] = "\\rubyspec\\home1" + Dir.home.should == "C:/rubyspec/home1" + ENV['USERPROFILE'] = "C:\\rubyspec\\home2" + Dir.home.should == "C:/rubyspec/home2" + ENV['HOME'] = "C:\\rubyspec\\home3" + Dir.home.should == "C:/rubyspec/home3" + ensure + ENV['HOME'], ENV['USERPROFILE'], ENV['HOMEDRIVE'], ENV['HOMEPATH'] = *old_dirs end end + end - platform_is_not :windows, :solaris, :android do + describe "when called with the current user name" do + platform_is_not :windows, :android, :wasi do it "returns the named user's home directory, from the user database" do Dir.home(ENV['USER']).should == `echo ~#{ENV['USER']}`.chomp end @@ -37,9 +67,19 @@ describe "Dir.home" do it "returns a non-frozen string" do Dir.home(ENV['USER']).should_not.frozen? end + + it "returns a string with the filesystem encoding" do + Dir.home(ENV['USER']).encoding.should == Encoding.find("filesystem") + end end it "raises an ArgumentError if the named user doesn't exist" do - -> { Dir.home('geuw2n288dh2k') }.should raise_error(ArgumentError) + -> { Dir.home('geuw2n288dh2k') }.should.raise(ArgumentError) + end + + describe "when called with a nil user name" do + it "returns the current user's home directory, reading $HOME first" do + Dir.home(nil).should == "/rubyspec_home" + end end end diff --git a/spec/ruby/core/dir/inspect_spec.rb b/spec/ruby/core/dir/inspect_spec.rb index 37338a97d4..eabaa54ce0 100644 --- a/spec/ruby/core/dir/inspect_spec.rb +++ b/spec/ruby/core/dir/inspect_spec.rb @@ -11,7 +11,7 @@ describe "Dir#inspect" do end it "returns a String" do - @dir.inspect.should be_an_instance_of(String) + @dir.inspect.should.instance_of?(String) end it "includes the class name" do @@ -19,6 +19,6 @@ describe "Dir#inspect" do end it "includes the directory name" do - @dir.inspect.should include(Dir.getwd) + @dir.inspect.should.include?(Dir.getwd) end end diff --git a/spec/ruby/core/dir/mkdir_spec.rb b/spec/ruby/core/dir/mkdir_spec.rb index 0ed28f5a99..37513e417a 100644 --- a/spec/ruby/core/dir/mkdir_spec.rb +++ b/spec/ruby/core/dir/mkdir_spec.rb @@ -46,7 +46,7 @@ describe "Dir.mkdir" do end end - it "calls #to_path on non-String arguments" do + it "calls #to_path on non-String path arguments" do DirSpecs.clear_dirs p = mock('path') p.should_receive(:to_path).and_return(DirSpecs.mock_dir('nonexisting')) @@ -54,16 +54,32 @@ describe "Dir.mkdir" do DirSpecs.clear_dirs end + it "calls #to_int on non-Integer permissions argument" do + DirSpecs.clear_dirs + path = DirSpecs.mock_dir('nonexisting') + permissions = mock('permissions') + permissions.should_receive(:to_int).and_return(0666) + Dir.mkdir(path, permissions) + DirSpecs.clear_dirs + end + + it "raises TypeError if non-Integer permissions argument does not have #to_int method" do + path = DirSpecs.mock_dir('nonexisting') + permissions = Object.new + + -> { Dir.mkdir(path, permissions) }.should.raise(TypeError, 'no implicit conversion of Object into Integer') + end + it "raises a SystemCallError if any of the directories in the path before the last does not exist" do - -> { Dir.mkdir "#{DirSpecs.nonexistent}/subdir" }.should raise_error(SystemCallError) + -> { Dir.mkdir "#{DirSpecs.nonexistent}/subdir" }.should.raise(SystemCallError) end it "raises Errno::EEXIST if the specified directory already exists" do - -> { Dir.mkdir("#{DirSpecs.mock_dir}/dir") }.should raise_error(Errno::EEXIST) + -> { Dir.mkdir("#{DirSpecs.mock_dir}/dir") }.should.raise(Errno::EEXIST) end it "raises Errno::EEXIST if the argument points to the existing file" do - -> { Dir.mkdir("#{DirSpecs.mock_dir}/file_one.ext") }.should raise_error(Errno::EEXIST) + -> { Dir.mkdir("#{DirSpecs.mock_dir}/file_one.ext") }.should.raise(Errno::EEXIST) end end @@ -84,7 +100,7 @@ platform_is_not :windows do it "raises a SystemCallError when lacking adequate permissions in the parent dir" do Dir.mkdir @dir, 0000 - -> { Dir.mkdir "#{@dir}/subdir" }.should raise_error(SystemCallError) + -> { Dir.mkdir "#{@dir}/subdir" }.should.raise(SystemCallError) end end end diff --git a/spec/ruby/core/dir/read_spec.rb b/spec/ruby/core/dir/read_spec.rb index 59de2e81cf..3f842457f4 100644 --- a/spec/ruby/core/dir/read_spec.rb +++ b/spec/ruby/core/dir/read_spec.rb @@ -15,7 +15,7 @@ describe "Dir#read" do # an FS does not necessarily impose order ls = Dir.entries DirSpecs.mock_dir dir = Dir.open DirSpecs.mock_dir - ls.should include(dir.read) + ls.should.include?(dir.read) dir.close end @@ -39,5 +39,38 @@ describe "Dir#read" do entries.sort.should == DirSpecs.expected_paths end + platform_is_not :windows do + it "returns all directory entries even when encoding conversion will fail" do + dir = Dir.open(File.join(DirSpecs.mock_dir, 'special')) + utf8_entries = [] + begin + while entry = dir.read + utf8_entries << entry + end + ensure + dir.close + end + old_internal_encoding = Encoding::default_internal + old_external_encoding = Encoding::default_external + Encoding.default_internal = Encoding::UTF_8 + Encoding.default_external = Encoding::SHIFT_JIS + shift_jis_entries = [] + begin + Dir.open(File.join(DirSpecs.mock_dir, 'special')) do |d| + -> { + while entry = d.read + shift_jis_entries << entry + end + }.should_not.raise + end + ensure + Encoding.default_internal = old_internal_encoding + Encoding.default_external = old_external_encoding + end + shift_jis_entries.size.should == utf8_entries.size + shift_jis_entries.filter { |f| f.encoding == Encoding::SHIFT_JIS }.size.should == 1 + end + end + it_behaves_like :dir_closed, :read end diff --git a/spec/ruby/core/dir/scan_spec.rb b/spec/ruby/core/dir/scan_spec.rb new file mode 100644 index 0000000000..3aa071337b --- /dev/null +++ b/spec/ruby/core/dir/scan_spec.rb @@ -0,0 +1,224 @@ +# encoding: utf-8 + +require_relative '../../spec_helper' +require_relative 'fixtures/common' +require_relative '../file/fixtures/file_types' + +ruby_version_is "4.1" do + describe "Dir.scan" do + before :all do + FileSpecs.configure_types + end + + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + before :each do + @internal = Encoding.default_internal + end + + after :each do + Encoding.default_internal = @internal + end + + it "returns an Array of filename and type pairs in an existing directory including dotfiles" do + a = Dir.scan(DirSpecs.mock_dir).sort + + a.should == DirSpecs.expected_paths_with_type - [[".", :directory], ["..", :directory]] + + a = Dir.scan("#{DirSpecs.mock_dir}/deeply/nested").sort + a.should == [[".dotfile.ext", :file], ["directory", :directory]] + end + + it "yields filename and type in an existing directory including dotfiles" do + a = [] + Dir.scan(DirSpecs.mock_dir) do |n, t| + a << [n, t] + end + a.sort! + a.should == DirSpecs.expected_paths_with_type - [[".", :directory], ["..", :directory]] + + a = [] + Dir.scan("#{DirSpecs.mock_dir}/deeply/nested") do |n, t| + a << [n, t] + end + a.sort! + a.should == [[".dotfile.ext", :file], ["directory", :directory]] + end + + it "calls #to_path on non-String arguments" do + p = mock('path') + p.should_receive(:to_path).and_return(DirSpecs.mock_dir) + Dir.scan(p) + end + + it "accepts an options Hash" do + a = Dir.scan("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").sort + a.should == [[".dotfile.ext", :file], ["directory", :directory]] + end + + it "returns children names encoded with the filesystem encoding by default" do + # This spec depends on the locale not being US-ASCII because if it is, the + # children that are not ascii_only? will be BINARY encoded. + children = Dir.scan(File.join(DirSpecs.mock_dir, 'special')).sort + encoding = Encoding.find("filesystem") + encoding = Encoding::BINARY if encoding == Encoding::US_ASCII + platform_is_not :windows do + children.should.include?(["こんにちは.txt".dup.force_encoding(encoding), :file]) + end + children.first.first.encoding.should.equal?(Encoding.find("filesystem")) + end + + it "returns children names encoded with the specified encoding" do + dir = File.join(DirSpecs.mock_dir, 'special') + children = Dir.scan(dir, encoding: "euc-jp").sort + children.first.first.encoding.should.equal?(Encoding::EUC_JP) + end + + it "returns children names transcoded to the default internal encoding" do + Encoding.default_internal = Encoding::EUC_KR + children = Dir.scan(File.join(DirSpecs.mock_dir, 'special')).sort + children.first.first.encoding.should.equal?(Encoding::EUC_KR) + end + + it "raises a SystemCallError if called with a nonexistent directory" do + -> { Dir.scan DirSpecs.nonexistent }.should.raise(SystemCallError) + end + + it "handles symlink" do + FileSpecs.symlink do |path| + Dir.scan(File.dirname(path)).map(&:last).should.include?(:link) + end + end + + platform_is_not :windows do + it "handles socket" do + FileSpecs.socket do |path| + Dir.scan(File.dirname(path)).map(&:last).should.include?(:socket) + end + end + + it "handles FIFO" do + FileSpecs.fifo do |path| + Dir.scan(File.dirname(path)).map(&:last).should.include?(:fifo) + end + end + + it "handles character devices" do + FileSpecs.character_device do |path| + Dir.scan(File.dirname(path)).map(&:last).should.include?(:characterSpecial) + end + end + end + + platform_is_not :freebsd, :windows do + with_block_device do + it "handles block devices" do + FileSpecs.block_device do |path| + Dir.scan(File.dirname(path)).map(&:last).should.include?(:blockSpecial) + end + end + end + end + end + + describe "Dir#scan" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + before :each do + @internal = Encoding.default_internal + end + + after :each do + Encoding.default_internal = @internal + @dir.close if @dir + end + + it "returns an Array of filenames in an existing directory including dotfiles" do + @dir = Dir.new(DirSpecs.mock_dir) + a = @dir.scan.sort + @dir.close + + a.should == DirSpecs.expected_paths_with_type - [[".", :directory], ["..", :directory]] + + @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested") + a = @dir.scan.sort + a.should == [[".dotfile.ext", :file], ["directory", :directory]] + end + + it "yields filename and type in an existing directory including dotfiles" do + @dir = Dir.new(DirSpecs.mock_dir) + a = [] + @dir.scan do |n, t| + a << [n, t] + end + a.sort! + a.should == DirSpecs.expected_paths_with_type - [[".", :directory], ["..", :directory]] + + @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested") + a = [] + @dir.scan do |n, t| + a << [n, t] + end + a.sort! + a.should == [[".dotfile.ext", :file], ["directory", :directory]] + end + + it "accepts an encoding keyword for the encoding of the entries" do + @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8") + dirs = @dir.to_a.sort + dirs.each { |d| d.encoding.should == Encoding::UTF_8 } + end + + it "returns children names encoded with the filesystem encoding by default" do + # This spec depends on the locale not being US-ASCII because if it is, the + # children that are not ascii_only? will be BINARY encoded. + @dir = Dir.new(File.join(DirSpecs.mock_dir, 'special')) + children = @dir.scan.sort + encoding = Encoding.find("filesystem") + encoding = Encoding::BINARY if encoding == Encoding::US_ASCII + platform_is_not :windows do + children.should.include?(["こんにちは.txt".dup.force_encoding(encoding), :file]) + end + children.first.first.encoding.should.equal?(Encoding.find("filesystem")) + end + + it "returns children names encoded with the specified encoding" do + path = File.join(DirSpecs.mock_dir, 'special') + @dir = Dir.new(path, encoding: "euc-jp") + children = @dir.children.sort + children.first.encoding.should.equal?(Encoding::EUC_JP) + end + + it "returns children names transcoded to the default internal encoding" do + Encoding.default_internal = Encoding::EUC_KR + @dir = Dir.new(File.join(DirSpecs.mock_dir, 'special')) + children = @dir.scan.sort + children.first.first.encoding.should.equal?(Encoding::EUC_KR) + end + + it "returns the same result when called repeatedly" do + @dir = Dir.open DirSpecs.mock_dir + + a = [] + @dir.each {|dir| a << dir} + + b = [] + @dir.each {|dir| b << dir} + + a.sort.should == b.sort + a.sort.should == DirSpecs.expected_paths + end + end +end diff --git a/spec/ruby/core/dir/shared/chroot.rb b/spec/ruby/core/dir/shared/chroot.rb index b14a433670..e4e6103799 100644 --- a/spec/ruby/core/dir/shared/chroot.rb +++ b/spec/ruby/core/dir/shared/chroot.rb @@ -2,8 +2,8 @@ describe :dir_chroot_as_root, shared: true do before :all do DirSpecs.create_mock_dirs - @real_root = "../" * (File.dirname(__FILE__).count('/') - 1) - @ref_dir = File.join("/", Dir.new('/').entries.first) + @real_root = "../" * (__dir__.count('/') - 1) + @ref_dir = File.join("/", File.basename(Dir["/*"].first)) end after :all do @@ -14,24 +14,27 @@ describe :dir_chroot_as_root, shared: true do DirSpecs.delete_mock_dirs end + # Pending until https://github.com/ruby/ruby/runs/8075149420 is fixed + compilations_ci = ENV["GITHUB_WORKFLOW"] == "Compilations" + it "can be used to change the process' root directory" do - -> { Dir.send(@method, File.dirname(__FILE__)) }.should_not raise_error + -> { Dir.send(@method, __dir__) }.should_not.raise File.should.exist?("/#{File.basename(__FILE__)}") - end + end unless compilations_ci it "returns 0 if successful" do Dir.send(@method, '/').should == 0 end it "raises an Errno::ENOENT exception if the directory doesn't exist" do - -> { Dir.send(@method, 'xgwhwhsjai2222jg') }.should raise_error(Errno::ENOENT) + -> { Dir.send(@method, 'xgwhwhsjai2222jg') }.should.raise(Errno::ENOENT) end it "can be escaped from with ../" do Dir.send(@method, @real_root) File.should.exist?(@ref_dir) File.should_not.exist?("/#{File.basename(__FILE__)}") - end + end unless compilations_ci it "calls #to_path on non-String argument" do p = mock('path') diff --git a/spec/ruby/core/dir/shared/closed.rb b/spec/ruby/core/dir/shared/closed.rb index 17d8332c2a..c868fd6e6d 100644 --- a/spec/ruby/core/dir/shared/closed.rb +++ b/spec/ruby/core/dir/shared/closed.rb @@ -4,6 +4,6 @@ describe :dir_closed, shared: true do dir = Dir.open DirSpecs.mock_dir dir.close dir.send(@method) {} - }.should raise_error(IOError) + }.should.raise(IOError) end end diff --git a/spec/ruby/core/dir/shared/delete.rb b/spec/ruby/core/dir/shared/delete.rb index 49e88360e8..ba013e8615 100644 --- a/spec/ruby/core/dir/shared/delete.rb +++ b/spec/ruby/core/dir/shared/delete.rb @@ -17,26 +17,16 @@ describe :dir_delete, shared: true do Dir.send(@method, p) end - platform_is_not :solaris do - it "raises an Errno::ENOTEMPTY when trying to remove a nonempty directory" do - -> do - Dir.send @method, DirSpecs.mock_rmdir("nonempty") - end.should raise_error(Errno::ENOTEMPTY) - end - end - - platform_is :solaris do - it "raises an Errno::EEXIST when trying to remove a nonempty directory" do - -> do - Dir.send @method, DirSpecs.mock_rmdir("nonempty") - end.should raise_error(Errno::EEXIST) - end + it "raises an Errno::ENOTEMPTY when trying to remove a nonempty directory" do + -> do + Dir.send @method, DirSpecs.mock_rmdir("nonempty") + end.should.raise(Errno::ENOTEMPTY) end it "raises an Errno::ENOENT when trying to remove a non-existing directory" do -> do Dir.send @method, DirSpecs.nonexistent - end.should raise_error(Errno::ENOENT) + end.should.raise(Errno::ENOENT) end it "raises an Errno::ENOTDIR when trying to remove a non-directory" do @@ -44,7 +34,7 @@ describe :dir_delete, shared: true do touch(file) -> do Dir.send @method, file - end.should raise_error(Errno::ENOTDIR) + end.should.raise(Errno::ENOTDIR) end # this won't work on Windows, since chmod(0000) does not remove all permissions @@ -56,7 +46,7 @@ describe :dir_delete, shared: true do File.chmod(0000, parent) -> do Dir.send @method, child - end.should raise_error(Errno::EACCES) + end.should.raise(Errno::EACCES) end end end diff --git a/spec/ruby/core/dir/shared/exist.rb b/spec/ruby/core/dir/shared/exist.rb index 765d1b656c..4ceaccea66 100644 --- a/spec/ruby/core/dir/shared/exist.rb +++ b/spec/ruby/core/dir/shared/exist.rb @@ -1,56 +1,57 @@ describe :dir_exist, shared: true do it "returns true if the given directory exists" do - Dir.send(@method, File.dirname(__FILE__)).should be_true + Dir.send(@method, __dir__).should == true end it "returns true for '.'" do - Dir.send(@method, '.').should be_true + Dir.send(@method, '.').should == true end it "returns true for '..'" do - Dir.send(@method, '..').should be_true + Dir.send(@method, '..').should == true end it "understands non-ASCII paths" do subdir = File.join(tmp("\u{9876}\u{665}")) - Dir.send(@method, subdir).should be_false + Dir.send(@method, subdir).should == false Dir.mkdir(subdir) - Dir.send(@method, subdir).should be_true + Dir.send(@method, subdir).should == true Dir.rmdir(subdir) end it "understands relative paths" do - Dir.send(@method, File.dirname(__FILE__) + '/../').should be_true + Dir.send(@method, __dir__ + '/../').should == true end it "returns false if the given directory doesn't exist" do - Dir.send(@method, 'y26dg27n2nwjs8a/').should be_false + Dir.send(@method, 'y26dg27n2nwjs8a/').should == false end it "doesn't require the name to have a trailing slash" do - dir = File.dirname(__FILE__) + dir = __dir__ dir.sub!(/\/$/,'') - Dir.send(@method, dir).should be_true + Dir.send(@method, dir).should == true end it "doesn't expand paths" do - Dir.send(@method, File.expand_path('~')).should be_true - Dir.send(@method, '~').should be_false + skip "$HOME not valid directory" unless ENV['HOME'] && File.directory?(ENV['HOME']) + Dir.send(@method, File.expand_path('~')).should == true + Dir.send(@method, '~').should == false end it "returns false if the argument exists but is a file" do File.should.exist?(__FILE__) - Dir.send(@method, __FILE__).should be_false + Dir.send(@method, __FILE__).should == false end it "doesn't set $! when file doesn't exist" do Dir.send(@method, "/path/to/non/existent/dir") - $!.should be_nil + $!.should == nil end it "calls #to_path on non String arguments" do p = mock('path') - p.should_receive(:to_path).and_return(File.dirname(__FILE__)) + p.should_receive(:to_path).and_return(__dir__) Dir.send(@method, p) end end diff --git a/spec/ruby/core/dir/shared/glob.rb b/spec/ruby/core/dir/shared/glob.rb index c35f7d71ca..86aa105259 100644 --- a/spec/ruby/core/dir/shared/glob.rb +++ b/spec/ruby/core/dir/shared/glob.rb @@ -12,8 +12,8 @@ describe :dir_glob, shared: true do end it "raises an Encoding::CompatibilityError if the argument encoding is not compatible with US-ASCII" do - pattern = "file*".force_encoding Encoding::UTF_16BE - -> { Dir.send(@method, pattern) }.should raise_error(Encoding::CompatibilityError) + pattern = "files*".dup.force_encoding Encoding::UTF_16BE + -> { Dir.send(@method, pattern) }.should.raise(Encoding::CompatibilityError) end it "calls #to_path to convert a pattern" do @@ -23,26 +23,29 @@ describe :dir_glob, shared: true do Dir.send(@method, obj).should == %w[file_one.ext] end - ruby_version_is ""..."2.6" do - it "splits the string on \\0 if there is only one string given" do - Dir.send(@method, "file_o*\0file_t*").should == - %w!file_one.ext file_two.ext! - end + it "raises an ArgumentError if the string contains \\0" do + -> {Dir.send(@method, "file_o*\0file_t*")}.should.raise ArgumentError, /nul-separated/ end - ruby_version_is "2.6"..."2.7" do - it "splits the string on \\0 if there is only one string given and warns" do - -> { - Dir.send(@method, "file_o*\0file_t*").should == - %w!file_one.ext file_two.ext! - }.should complain(/warning: use glob patterns list instead of nul-separated patterns/) - end + it "result is sorted by default" do + result = Dir.send(@method, '*') + result.should == result.sort end - ruby_version_is "2.7" do - it "raises an ArgumentError if the string contains \\0" do - -> {Dir.send(@method, "file_o*\0file_t*")}.should raise_error ArgumentError, /nul-separated/ - end + it "result is sorted with sort: true" do + result = Dir.send(@method, '*', sort: true) + result.should == result.sort + end + + it "sort: false returns same files" do + result = Dir.send(@method,'*', sort: false) + result.sort.should == Dir.send(@method, '*').sort + end + + it "raises an ArgumentError if sort: is not true or false" do + -> { Dir.send(@method, '*', sort: 0) }.should.raise ArgumentError, /expected true or false/ + -> { Dir.send(@method, '*', sort: nil) }.should.raise ArgumentError, /expected true or false/ + -> { Dir.send(@method, '*', sort: 'false') }.should.raise ArgumentError, /expected true or false/ end it "matches non-dotfiles with '*'" do @@ -71,6 +74,10 @@ describe :dir_glob, shared: true do Dir.send(@method, 'special/+').should == ['special/+'] end + it "matches directories with special characters when escaped" do + Dir.send(@method, 'special/\{}/special').should == ["special/{}/special"] + end + platform_is_not :windows do it "matches regexp special *" do Dir.send(@method, 'special/\*').should == ['special/*'] @@ -83,6 +90,14 @@ describe :dir_glob, shared: true do it "matches regexp special |" do Dir.send(@method, 'special/|').should == ['special/|'] end + + it "matches files with backslashes in their name" do + Dir.glob('special/\\\\{a,b}').should == ['special/\a'] + end + + it "matches directory with special characters in their name in complex patterns" do + Dir.glob("special/test +()\\[\\]\\{\\}/hello_world{.{en},}{.{html},}{+{phone},}{.{erb},}").should == ['special/test +()[]{}/hello_world.erb'] + end end it "matches regexp special ^" do @@ -121,16 +136,8 @@ describe :dir_glob, shared: true do Dir.send(@method, 'special/test\{1\}/*').should == ['special/test{1}/file[1]'] end - ruby_version_is ''...'3.1' do - it "matches dotfiles with '.*'" do - Dir.send(@method, '.*').sort.should == %w|. .. .dotfile .dotsubdir|.sort - end - end - - ruby_version_is '3.1' do - it "matches dotfiles except .. with '.*'" do - Dir.send(@method, '.*').sort.should == %w|. .dotfile .dotsubdir|.sort - end + it "matches dotfiles except .. with '.*'" do + Dir.send(@method, '.*').sort.should == %w|. .dotfile .dotsubdir|.sort end it "matches non-dotfiles with '*<non-special characters>'" do @@ -175,16 +182,8 @@ describe :dir_glob, shared: true do Dir.send(@method, '**').sort.should == expected end - ruby_version_is ''...'3.1' do - it "matches dotfiles in the current directory with '.**'" do - Dir.send(@method, '.**').sort.should == %w|. .. .dotsubdir .dotfile|.sort - end - end - - ruby_version_is '3.1' do - it "matches dotfiles in the current directory except .. with '.**'" do - Dir.send(@method, '.**').sort.should == %w|. .dotsubdir .dotfile|.sort - end + it "matches dotfiles in the current directory except .. with '.**'" do + Dir.send(@method, '.**').sort.should == %w|. .dotsubdir .dotfile|.sort end it "recursively matches any nondot subdirectories with '**/'" do @@ -197,7 +196,9 @@ describe :dir_glob, shared: true do dir/ nested/ special/ + special/test\ +()[]{}/ special/test{1}/ + special/{}/ subdir_one/ subdir_two/ ] @@ -205,19 +206,17 @@ describe :dir_glob, shared: true do Dir.send(@method, '**/').sort.should == expected end - ruby_version_is ''...'3.1' do - it "recursively matches any subdirectories including ./ and ../ with '.**/'" do - Dir.chdir("#{DirSpecs.mock_dir}/subdir_one") do - Dir.send(@method, '.**/').sort.should == %w|./ ../|.sort - end - end + it "recursively matches any subdirectories except './' or '../' with '**/' from the base directory if that is specified" do + expected = %w[ + nested/directory + ] + + Dir.send(@method, '**/*ory', base: 'deeply').sort.should == expected end - ruby_version_is '3.1' do - it "recursively matches any subdirectories including ./ with '.**/'" do - Dir.chdir("#{DirSpecs.mock_dir}/subdir_one") do - Dir.send(@method, '.**/').should == ['./'] - end + it "recursively matches any subdirectories including ./ with '.**/'" do + Dir.chdir("#{DirSpecs.mock_dir}/subdir_one") do + Dir.send(@method, '.**/').should == ['./'] end end @@ -374,7 +373,7 @@ describe :dir_glob, shared: true do it "raises TypeError when cannot convert value to string" do -> { Dir.send(@method, "*", base: []) - }.should raise_error(TypeError) + }.should.raise(TypeError) end it "handles '' as current directory path" do diff --git a/spec/ruby/core/dir/shared/open.rb b/spec/ruby/core/dir/shared/open.rb index 920845cba1..9ac3a40694 100644 --- a/spec/ruby/core/dir/shared/open.rb +++ b/spec/ruby/core/dir/shared/open.rb @@ -1,18 +1,18 @@ describe :dir_open, shared: true do it "returns a Dir instance representing the specified directory" do dir = Dir.send(@method, DirSpecs.mock_dir) - dir.should be_kind_of(Dir) + dir.should.is_a?(Dir) dir.close end it "raises a SystemCallError if the directory does not exist" do -> do Dir.send @method, DirSpecs.nonexistent - end.should raise_error(SystemCallError) + end.should.raise(SystemCallError) end it "may take a block which is yielded to with the Dir instance" do - Dir.send(@method, DirSpecs.mock_dir) {|dir| dir.should be_kind_of(Dir)} + Dir.send(@method, DirSpecs.mock_dir) {|dir| dir.should.is_a?(Dir)} end it "returns the value of the block if a block is given" do @@ -21,7 +21,7 @@ describe :dir_open, shared: true do it "closes the Dir instance when the block exits if given a block" do closed_dir = Dir.send(@method, DirSpecs.mock_dir) { |dir| dir } - -> { closed_dir.read }.should raise_error(IOError) + -> { closed_dir.read }.should.raise(IOError) end it "closes the Dir instance when the block exits the block even due to an exception" do @@ -32,9 +32,9 @@ describe :dir_open, shared: true do closed_dir = dir raise "dir specs" end - end.should raise_error(RuntimeError, "dir specs") + end.should.raise(RuntimeError, "dir specs") - -> { closed_dir.read }.should raise_error(IOError) + -> { closed_dir.read }.should.raise(IOError) end it "calls #to_path on non-String arguments" do @@ -45,7 +45,7 @@ describe :dir_open, shared: true do it "accepts an options Hash" do dir = Dir.send(@method, DirSpecs.mock_dir, encoding: "utf-8") {|d| d } - dir.should be_kind_of(Dir) + dir.should.is_a?(Dir) end it "calls #to_hash to convert the options object" do @@ -53,12 +53,12 @@ describe :dir_open, shared: true do options.should_receive(:to_hash).and_return({ encoding: Encoding::UTF_8 }) dir = Dir.send(@method, DirSpecs.mock_dir, **options) {|d| d } - dir.should be_kind_of(Dir) + dir.should.is_a?(Dir) end it "ignores the :encoding option if it is nil" do dir = Dir.send(@method, DirSpecs.mock_dir, encoding: nil) {|d| d } - dir.should be_kind_of(Dir) + dir.should.is_a?(Dir) end platform_is_not :windows do diff --git a/spec/ruby/core/dir/shared/path.rb b/spec/ruby/core/dir/shared/path.rb index 494dcca775..7647c04421 100644 --- a/spec/ruby/core/dir/shared/path.rb +++ b/spec/ruby/core/dir/shared/path.rb @@ -22,7 +22,7 @@ describe :dir_path, shared: true do path = DirSpecs.mock_dir.force_encoding Encoding::IBM866 dir = Dir.open path begin - dir.send(@method).encoding.should equal(Encoding::IBM866) + dir.send(@method).encoding.should.equal?(Encoding::IBM866) ensure dir.close end diff --git a/spec/ruby/core/dir/shared/pos.rb b/spec/ruby/core/dir/shared/pos.rb index 2165932d99..11712cc75d 100644 --- a/spec/ruby/core/dir/shared/pos.rb +++ b/spec/ruby/core/dir/shared/pos.rb @@ -8,9 +8,9 @@ describe :dir_pos, shared: true do end it "returns an Integer representing the current position in the directory" do - @dir.send(@method).should be_kind_of(Integer) - @dir.send(@method).should be_kind_of(Integer) - @dir.send(@method).should be_kind_of(Integer) + @dir.send(@method).should.is_a?(Integer) + @dir.send(@method).should.is_a?(Integer) + @dir.send(@method).should.is_a?(Integer) end it "returns a different Integer if moved from previous position" do @@ -18,8 +18,8 @@ describe :dir_pos, shared: true do @dir.read b = @dir.send(@method) - a.should be_kind_of(Integer) - b.should be_kind_of(Integer) + a.should.is_a?(Integer) + b.should.is_a?(Integer) a.should_not == b end diff --git a/spec/ruby/core/dir/shared/pwd.rb b/spec/ruby/core/dir/shared/pwd.rb index 2a8d7fe790..ed47fe382a 100644 --- a/spec/ruby/core/dir/shared/pwd.rb +++ b/spec/ruby/core/dir/shared/pwd.rb @@ -37,9 +37,9 @@ describe :dir_pwd, shared: true do it "returns a String with the filesystem encoding" do enc = Dir.send(@method).encoding if @fs_encoding == Encoding::US_ASCII - [Encoding::US_ASCII, Encoding::BINARY].should include(enc) + [Encoding::US_ASCII, Encoding::BINARY].should.include?(enc) else - enc.should equal(@fs_encoding) + enc.should.equal?(@fs_encoding) end end end |
