diff options
Diffstat (limited to 'spec/ruby/core/file')
29 files changed, 420 insertions, 298 deletions
diff --git a/spec/ruby/core/file/absolute_path_spec.rb b/spec/ruby/core/file/absolute_path_spec.rb index e35c80ec3c..315eead34f 100644 --- a/spec/ruby/core/file/absolute_path_spec.rb +++ b/spec/ruby/core/file/absolute_path_spec.rb @@ -85,7 +85,7 @@ describe "File.absolute_path" do end it "accepts a second argument of a directory from which to resolve the path" do - File.absolute_path(__FILE__, File.dirname(__FILE__)).should == @abs + File.absolute_path(__FILE__, __dir__).should == @abs end it "calls #to_path on its argument" do diff --git a/spec/ruby/core/file/atime_spec.rb b/spec/ruby/core/file/atime_spec.rb index 1b47576e6b..e47e70e5ac 100644 --- a/spec/ruby/core/file/atime_spec.rb +++ b/spec/ruby/core/file/atime_spec.rb @@ -27,6 +27,9 @@ describe "File.atime" do else File.atime(__FILE__).usec.should == 0 end + rescue Errno::ENOENT => e + # Native Windows don't have stat command. + skip e.message end end end diff --git a/spec/ruby/core/file/basename_spec.rb b/spec/ruby/core/file/basename_spec.rb index 989409d76b..87695ab97b 100644 --- a/spec/ruby/core/file/basename_spec.rb +++ b/spec/ruby/core/file/basename_spec.rb @@ -151,8 +151,34 @@ describe "File.basename" do File.basename("c:\\bar.txt", ".*").should == "bar" File.basename("c:\\bar.txt.exe", ".*").should == "bar.txt" end + + it "handles Shift JIS 0x5C (\\) as second byte of a multi-byte sequence" do + # dir\fileソname.txt + path = "dir\\file\x83\x5cname.txt".b.force_encoding(Encoding::SHIFT_JIS) + path.valid_encoding?.should be_true + File.basename(path).should == "file\x83\x5cname.txt".b.force_encoding(Encoding::SHIFT_JIS) + end end + it "rejects strings encoded with non ASCII-compatible encodings" do + Encoding.list.reject(&:ascii_compatible?).reject(&:dummy?).each do |enc| + begin + path = "/foo/bar".encode(enc) + rescue Encoding::ConverterNotFoundError + next + end + + -> { + File.basename(path) + }.should raise_error(Encoding::CompatibilityError) + end + end + + it "works with all ASCII-compatible encodings" do + Encoding.list.select(&:ascii_compatible?).each do |enc| + File.basename("/foo/bar".encode(enc)).should == "bar".encode(enc) + end + end it "returns the extension for a multibyte filename" do File.basename('/path/Офис.m4a').should == "Офис.m4a" diff --git a/spec/ruby/core/file/birthtime_spec.rb b/spec/ruby/core/file/birthtime_spec.rb index 755601df64..f82eaf7cca 100644 --- a/spec/ruby/core/file/birthtime_spec.rb +++ b/spec/ruby/core/file/birthtime_spec.rb @@ -1,60 +1,56 @@ require_relative '../../spec_helper' -describe "File.birthtime" do - before :each do - @file = __FILE__ - end +platform_is :windows, :darwin, :freebsd, :netbsd, :linux do + not_implemented_messages = [ + "birthtime() function is unimplemented", # unsupported OS/version + "birthtime is unimplemented", # unsupported filesystem + ] + + describe "File.birthtime" do + before :each do + @file = __FILE__ + end - after :each do - @file = nil - end + after :each do + @file = nil + end - platform_is :windows, :darwin, :freebsd, :netbsd do it "returns the birth time for the named file as a Time object" do File.birthtime(@file) File.birthtime(@file).should be_kind_of(Time) + rescue NotImplementedError => e + e.message.should.start_with?(*not_implemented_messages) end it "accepts an object that has a #to_path method" do + File.birthtime(@file) # Avoid to failure of mock object with old Kernel and glibc File.birthtime(mock_to_path(@file)) + rescue NotImplementedError => e + e.message.should.start_with?(*not_implemented_messages) end it "raises an Errno::ENOENT exception if the file is not found" do -> { File.birthtime('bogus') }.should raise_error(Errno::ENOENT) + rescue NotImplementedError => e + e.message.should.start_with?(*not_implemented_messages) end end - platform_is :openbsd do - it "raises an NotImplementedError" do - -> { File.birthtime(@file) }.should raise_error(NotImplementedError) + describe "File#birthtime" do + before :each do + @file = File.open(__FILE__) end - end - - # TODO: depends on Linux kernel version -end -describe "File#birthtime" do - before :each do - @file = File.open(__FILE__) - end - - after :each do - @file.close - @file = nil - end + after :each do + @file.close + @file = nil + end - platform_is :windows, :darwin, :freebsd, :netbsd do it "returns the birth time for self" do @file.birthtime @file.birthtime.should be_kind_of(Time) + rescue NotImplementedError => e + e.message.should.start_with?(*not_implemented_messages) end end - - platform_is :openbsd do - it "raises an NotImplementedError" do - -> { @file.birthtime }.should raise_error(NotImplementedError) - end - end - - # TODO: depends on Linux kernel version end diff --git a/spec/ruby/core/file/chown_spec.rb b/spec/ruby/core/file/chown_spec.rb index 8cc8f0d04b..4db0e3712c 100644 --- a/spec/ruby/core/file/chown_spec.rb +++ b/spec/ruby/core/file/chown_spec.rb @@ -78,15 +78,15 @@ describe "File.chown" do end describe "File#chown" do - before :each do - @fname = tmp('file_chown_test') - @file = File.open(@fname, 'w') - end + before :each do + @fname = tmp('file_chown_test') + @file = File.open(@fname, 'w') + end - after :each do - @file.close unless @file.closed? - rm_r @fname - end + after :each do + @file.close unless @file.closed? + rm_r @fname + end as_superuser do platform_is :windows do diff --git a/spec/ruby/core/file/constants/constants_spec.rb b/spec/ruby/core/file/constants/constants_spec.rb index 86946822c5..bba248c21e 100644 --- a/spec/ruby/core/file/constants/constants_spec.rb +++ b/spec/ruby/core/file/constants/constants_spec.rb @@ -4,7 +4,7 @@ require_relative '../../../spec_helper' "FNM_DOTMATCH", "FNM_EXTGLOB", "FNM_NOESCAPE", "FNM_PATHNAME", "FNM_SYSCASE", "LOCK_EX", "LOCK_NB", "LOCK_SH", "LOCK_UN", "NONBLOCK", "RDONLY", - "RDWR", "TRUNC", "WRONLY"].each do |const| + "RDWR", "TRUNC", "WRONLY", "SHARE_DELETE"].each do |const| describe "File::Constants::#{const}" do it "is defined" do File::Constants.const_defined?(const).should be_true diff --git a/spec/ruby/core/file/ctime_spec.rb b/spec/ruby/core/file/ctime_spec.rb index d17ba1a77f..718f26d5cc 100644 --- a/spec/ruby/core/file/ctime_spec.rb +++ b/spec/ruby/core/file/ctime_spec.rb @@ -22,6 +22,9 @@ describe "File.ctime" do else File.ctime(__FILE__).usec.should == 0 end + rescue Errno::ENOENT => e + # Windows don't have stat command. + skip e.message end end diff --git a/spec/ruby/core/file/dirname_spec.rb b/spec/ruby/core/file/dirname_spec.rb index cf0f909f59..8e6016ce6f 100644 --- a/spec/ruby/core/file/dirname_spec.rb +++ b/spec/ruby/core/file/dirname_spec.rb @@ -11,19 +11,32 @@ describe "File.dirname" do File.dirname('/foo/foo').should == '/foo' end - ruby_version_is '3.1' do + context "when level is passed" do it "returns all the components of filename except the last parts by the level" do File.dirname('/home/jason', 2).should == '/' File.dirname('/home/jason/poot.txt', 2).should == '/home' end - it "returns the same string if the level is 0" do + it "returns the same String if the level is 0" do File.dirname('poot.txt', 0).should == 'poot.txt' File.dirname('/', 0).should == '/' end it "raises ArgumentError if the level is negative" do - -> {File.dirname('/home/jason', -1)}.should raise_error(ArgumentError) + -> { + File.dirname('/home/jason', -1) + }.should raise_error(ArgumentError, "negative level: -1") + end + + it "returns '/' when level exceeds the number of segments in the path" do + File.dirname("/home/jason", 100).should == '/' + end + + it "calls #to_int if passed not numeric value" do + object = Object.new + def object.to_int; 2; end + + File.dirname("/a/b/c/d", object).should == '/a/b' end end @@ -50,19 +63,19 @@ describe "File.dirname" do end it "returns all the components of filename except the last one (edge cases on all platforms)" do - File.dirname("").should == "." - File.dirname(".").should == "." - File.dirname("./").should == "." - File.dirname("./b/./").should == "./b" - File.dirname("..").should == "." - File.dirname("../").should == "." - File.dirname("/").should == "/" - File.dirname("/.").should == "/" - File.dirname("/foo/").should == "/" - File.dirname("/foo/.").should == "/foo" - File.dirname("/foo/./").should == "/foo" - File.dirname("/foo/../.").should == "/foo/.." - File.dirname("foo/../").should == "foo" + File.dirname("").should == "." + File.dirname(".").should == "." + File.dirname("./").should == "." + File.dirname("./b/./").should == "./b" + File.dirname("..").should == "." + File.dirname("../").should == "." + File.dirname("/").should == "/" + File.dirname("/.").should == "/" + File.dirname("/foo/").should == "/" + File.dirname("/foo/.").should == "/foo" + File.dirname("/foo/./").should == "/foo" + File.dirname("/foo/../.").should == "/foo/.." + File.dirname("foo/../").should == "foo" end platform_is_not :windows do diff --git a/spec/ruby/core/file/empty_spec.rb b/spec/ruby/core/file/empty_spec.rb index 77f132303e..e8c9941676 100644 --- a/spec/ruby/core/file/empty_spec.rb +++ b/spec/ruby/core/file/empty_spec.rb @@ -4,10 +4,4 @@ require_relative '../../shared/file/zero' describe "File.empty?" do it_behaves_like :file_zero, :empty?, File it_behaves_like :file_zero_missing, :empty?, File - - platform_is :solaris do - it "returns false for /dev/null" do - File.empty?('/dev/null').should == true - end - end end diff --git a/spec/ruby/core/file/exist_spec.rb b/spec/ruby/core/file/exist_spec.rb index ddb5febcba..b5600e5b07 100644 --- a/spec/ruby/core/file/exist_spec.rb +++ b/spec/ruby/core/file/exist_spec.rb @@ -4,3 +4,9 @@ require_relative '../../shared/file/exist' describe "File.exist?" do it_behaves_like :file_exist, :exist?, File end + +describe "File.exists?" do + it "has been removed" do + File.should_not.respond_to?(:exists?) + end +end diff --git a/spec/ruby/core/file/expand_path_spec.rb b/spec/ruby/core/file/expand_path_spec.rb index c31f885b92..1abcf93900 100644 --- a/spec/ruby/core/file/expand_path_spec.rb +++ b/spec/ruby/core/file/expand_path_spec.rb @@ -137,7 +137,7 @@ describe "File.expand_path" do it "returns a String in the same encoding as the argument" do Encoding.default_external = Encoding::SHIFT_JIS - path = "./a".force_encoding Encoding::CP1251 + path = "./a".dup.force_encoding Encoding::CP1251 File.expand_path(path).encoding.should equal(Encoding::CP1251) weird_path = [222, 173, 190, 175].pack('C*') diff --git a/spec/ruby/core/file/flock_spec.rb b/spec/ruby/core/file/flock_spec.rb index 751e99d994..23ddf89ed8 100644 --- a/spec/ruby/core/file/flock_spec.rb +++ b/spec/ruby/core/file/flock_spec.rb @@ -30,7 +30,7 @@ describe "File#flock" do it "returns false if trying to lock an exclusively locked file" do @file.flock File::LOCK_EX - ruby_exe(<<-END_OF_CODE, escape: true).should == "false" + ruby_exe(<<-END_OF_CODE).should == "false" File.open('#{@name}', "w") do |f2| print f2.flock(File::LOCK_EX | File::LOCK_NB).to_s end @@ -40,7 +40,7 @@ describe "File#flock" do it "blocks if trying to lock an exclusively locked file" do @file.flock File::LOCK_EX - out = ruby_exe(<<-END_OF_CODE, escape: true) + out = ruby_exe(<<-END_OF_CODE) running = false t = Thread.new do @@ -72,35 +72,3 @@ describe "File#flock" do end end end - -platform_is :solaris do - describe "File#flock on Solaris" do - before :each do - @name = tmp("flock_test") - touch(@name) - - @read_file = File.open @name, "r" - @write_file = File.open @name, "w" - end - - after :each do - @read_file.flock File::LOCK_UN - @read_file.close - @write_file.flock File::LOCK_UN - @write_file.close - rm_r @name - end - - it "fails with EBADF acquiring exclusive lock on read-only File" do - -> do - @read_file.flock File::LOCK_EX - end.should raise_error(Errno::EBADF) - end - - it "fails with EBADF acquiring shared lock on read-only File" do - -> do - @write_file.flock File::LOCK_SH - end.should raise_error(Errno::EBADF) - end - end -end diff --git a/spec/ruby/core/file/lchmod_spec.rb b/spec/ruby/core/file/lchmod_spec.rb index 7420b95e4a..3c44374983 100644 --- a/spec/ruby/core/file/lchmod_spec.rb +++ b/spec/ruby/core/file/lchmod_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' describe "File.lchmod" do - platform_is_not :linux, :windows, :openbsd, :solaris, :aix do + platform_is_not :linux, :windows, :openbsd, :aix do before :each do @fname = tmp('file_chmod_test') @lname = @fname + '.lnk' diff --git a/spec/ruby/core/file/lutime_spec.rb b/spec/ruby/core/file/lutime_spec.rb index 1f0625f61e..0f6df42ea3 100644 --- a/spec/ruby/core/file/lutime_spec.rb +++ b/spec/ruby/core/file/lutime_spec.rb @@ -1,7 +1,12 @@ require_relative '../../spec_helper' +require_relative 'shared/update_time' -describe "File.lutime" do - platform_is_not :windows do +platform_is_not :windows do + describe "File.lutime" do + it_behaves_like :update_time, :lutime + end + + describe "File.lutime" do before :each do @atime = Time.utc(2000) @mtime = Time.utc(2001) diff --git a/spec/ruby/core/file/mtime_spec.rb b/spec/ruby/core/file/mtime_spec.rb index 5304bbf057..0e9c95caee 100644 --- a/spec/ruby/core/file/mtime_spec.rb +++ b/spec/ruby/core/file/mtime_spec.rb @@ -26,6 +26,9 @@ describe "File.mtime" do else File.mtime(__FILE__).usec.should == 0 end + rescue Errno::ENOENT => e + # Windows don't have stat command. + skip e.message end end end diff --git a/spec/ruby/core/file/new_spec.rb b/spec/ruby/core/file/new_spec.rb index 004f78503a..1e82a070b1 100644 --- a/spec/ruby/core/file/new_spec.rb +++ b/spec/ruby/core/file/new_spec.rb @@ -78,13 +78,29 @@ describe "File.new" do File.should.exist?(@file) end + it "returns a new read-only File when mode is not specified" do + @fh = File.new(@file) + + -> { @fh.puts("test") }.should raise_error(IOError) + @fh.read.should == "" + File.should.exist?(@file) + end + + it "returns a new read-only File when mode is not specified but flags option is present" do + @fh = File.new(@file, flags: File::CREAT) + + -> { @fh.puts("test") }.should raise_error(IOError) + @fh.read.should == "" + File.should.exist?(@file) + end + it "creates a new file when use File::EXCL mode" do @fh = File.new(@file, File::EXCL) @fh.should be_kind_of(File) File.should.exist?(@file) end - it "raises an Errorno::EEXIST if the file exists when create a new file with File::CREAT|File::EXCL" do + it "raises an Errno::EEXIST if the file exists when create a new file with File::CREAT|File::EXCL" do -> { @fh = File.new(@file, File::CREAT|File::EXCL) }.should raise_error(Errno::EEXIST) end @@ -112,13 +128,32 @@ describe "File.new" do File.should.exist?(@file) end - it "creates a new file when use File::WRONLY|File::TRUNC mode" do @fh = File.new(@file, File::WRONLY|File::TRUNC) @fh.should be_kind_of(File) File.should.exist?(@file) end + it "returns a new read-only File when use File::RDONLY|File::CREAT mode" do + @fh = File.new(@file, File::RDONLY|File::CREAT) + @fh.should be_kind_of(File) + File.should.exist?(@file) + + # it's read-only + -> { @fh.puts("test") }.should raise_error(IOError) + @fh.read.should == "" + end + + it "returns a new read-only File when use File::CREAT mode" do + @fh = File.new(@file, File::CREAT) + @fh.should be_kind_of(File) + File.should.exist?(@file) + + # it's read-only + -> { @fh.puts("test") }.should raise_error(IOError) + @fh.read.should == "" + end + it "coerces filename using to_str" do name = mock("file") name.should_receive(:to_str).and_return(@file) @@ -133,6 +168,32 @@ describe "File.new" do File.should.exist?(@file) end + it "accepts options as a keyword argument" do + @fh = File.new(@file, 'w', 0755, flags: @flags) + @fh.should be_kind_of(File) + @fh.close + + -> { + @fh = File.new(@file, 'w', 0755, {flags: @flags}) + }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)") + end + + it "bitwise-ORs mode and flags option" do + -> { + @fh = File.new(@file, 'w', flags: File::EXCL) + }.should raise_error(Errno::EEXIST, /File exists/) + + -> { + @fh = File.new(@file, mode: 'w', flags: File::EXCL) + }.should raise_error(Errno::EEXIST, /File exists/) + end + + it "does not use the given block and warns to use File::open" do + -> { + @fh = File.new(@file) { raise } + }.should complain(/warning: File::new\(\) does not take block; use File::open\(\) instead/) + end + it "raises a TypeError if the first parameter can't be coerced to a string" do -> { File.new(true) }.should raise_error(TypeError) -> { File.new(false) }.should raise_error(TypeError) diff --git a/spec/ruby/core/file/open_spec.rb b/spec/ruby/core/file/open_spec.rb index 1729780570..6bfc16bbf9 100644 --- a/spec/ruby/core/file/open_spec.rb +++ b/spec/ruby/core/file/open_spec.rb @@ -314,7 +314,7 @@ describe "File.open" do end end - it "raises an IOError when read in a block opened with File::RDONLY|File::APPEND mode" do + it "raises an IOError when write in a block opened with File::RDONLY|File::APPEND mode" do -> { File.open(@file, File::RDONLY|File::APPEND ) do |f| f.puts("writing") @@ -354,7 +354,7 @@ describe "File.open" do end end - it "raises an Errorno::EEXIST if the file exists when open with File::CREAT|File::EXCL" do + it "raises an Errno::EEXIST if the file exists when open with File::CREAT|File::EXCL" do -> { File.open(@file, File::CREAT|File::EXCL) do |f| f.puts("writing") @@ -423,7 +423,7 @@ describe "File.open" do }.should raise_error(IOError) end - it "raises an Errorno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do + it "raises an Errno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do -> { File.open(@file, File::RDONLY|File::TRUNC) do |f| f.puts("writing").should == nil @@ -441,7 +441,7 @@ describe "File.open" do }.should raise_error(Errno::EINVAL) end - it "raises an Errorno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do + it "raises an Errno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do -> { File.open(@file, File::RDONLY|File::TRUNC) do |f| f.puts("writing").should == nil @@ -565,6 +565,15 @@ describe "File.open" do File.open(@file, 'wb+') {|f| f.external_encoding.should == Encoding::BINARY} end + it "accepts options as a keyword argument" do + @fh = File.open(@file, 'w', 0755, flags: File::CREAT) + @fh.should be_an_instance_of(File) + + -> { + File.open(@file, 'w', 0755, {flags: File::CREAT}) + }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)") + end + it "uses the second argument as an options Hash" do @fh = File.open(@file, mode: "r") @fh.should be_an_instance_of(File) diff --git a/spec/ruby/core/file/path_spec.rb b/spec/ruby/core/file/path_spec.rb index dfa0c4ec02..726febcc2b 100644 --- a/spec/ruby/core/file/path_spec.rb +++ b/spec/ruby/core/file/path_spec.rb @@ -37,4 +37,45 @@ describe "File.path" do path.should_receive(:to_path).and_return("abc") File.path(path).should == "abc" end + + it "raises TypeError when #to_path result is not a string" do + path = mock("path") + path.should_receive(:to_path).and_return(nil) + -> { File.path(path) }.should raise_error TypeError + + path = mock("path") + path.should_receive(:to_path).and_return(42) + -> { File.path(path) }.should raise_error TypeError + end + + it "raises ArgumentError for string argument contains NUL character" do + -> { File.path("\0") }.should raise_error ArgumentError + -> { File.path("a\0") }.should raise_error ArgumentError + -> { File.path("a\0c") }.should raise_error ArgumentError + end + + it "raises ArgumentError when #to_path result contains NUL character" do + path = mock("path") + path.should_receive(:to_path).and_return("\0") + -> { File.path(path) }.should raise_error ArgumentError + + path = mock("path") + path.should_receive(:to_path).and_return("a\0") + -> { File.path(path) }.should raise_error ArgumentError + + path = mock("path") + path.should_receive(:to_path).and_return("a\0c") + -> { File.path(path) }.should raise_error ArgumentError + end + + it "raises Encoding::CompatibilityError for ASCII-incompatible string argument" do + path = "abc".encode(Encoding::UTF_32BE) + -> { File.path(path) }.should raise_error Encoding::CompatibilityError + end + + it "raises Encoding::CompatibilityError when #to_path result is ASCII-incompatible" do + path = mock("path") + path.should_receive(:to_path).and_return("abc".encode(Encoding::UTF_32BE)) + -> { File.path(path) }.should raise_error Encoding::CompatibilityError + end end diff --git a/spec/ruby/core/file/realpath_spec.rb b/spec/ruby/core/file/realpath_spec.rb index bd27e09da6..bd25bfdecf 100644 --- a/spec/ruby/core/file/realpath_spec.rb +++ b/spec/ruby/core/file/realpath_spec.rb @@ -54,6 +54,10 @@ platform_is_not :windows do File.realpath(@relative_symlink).should == @file end + it "removes the file element when going one level up" do + File.realpath('../', @file).should == @real_dir + end + it "raises an Errno::ELOOP if the symlink points to itself" do File.unlink @link File.symlink(@link, @link) diff --git a/spec/ruby/core/file/setuid_spec.rb b/spec/ruby/core/file/setuid_spec.rb index 281ef01ab9..9e5e86df61 100644 --- a/spec/ruby/core/file/setuid_spec.rb +++ b/spec/ruby/core/file/setuid_spec.rb @@ -26,10 +26,6 @@ describe "File.setuid?" do platform_is_not :windows do it "returns true when the gid bit is set" do - platform_is :solaris do - # Solaris requires execute bit before setting suid - system "chmod u+x #{@name}" - end system "chmod u+s #{@name}" File.setuid?(@name).should == true diff --git a/spec/ruby/core/file/shared/fnmatch.rb b/spec/ruby/core/file/shared/fnmatch.rb index 94f22144b0..db4b5c5d8c 100644 --- a/spec/ruby/core/file/shared/fnmatch.rb +++ b/spec/ruby/core/file/shared/fnmatch.rb @@ -102,6 +102,7 @@ describe :file_fnmatch, shared: true do it "matches ranges of characters using exclusive bracket expression (e.g. [^t] or [!t])" do File.send(@method, 'ca[^t]', 'cat').should == false + File.send(@method, 'ca[^t]', 'cas').should == true File.send(@method, 'ca[!t]', 'cat').should == false end @@ -125,6 +126,13 @@ describe :file_fnmatch, shared: true do end end + it "matches wildcard with characters when flags includes FNM_PATHNAME" do + File.send(@method, '*a', 'aa', File::FNM_PATHNAME).should == true + File.send(@method, 'a*', 'aa', File::FNM_PATHNAME).should == true + File.send(@method, 'a*', 'aaa', File::FNM_PATHNAME).should == true + File.send(@method, '*a', 'aaa', File::FNM_PATHNAME).should == true + end + it "does not match '/' characters with ? or * when flags includes FNM_PATHNAME" do File.send(@method, '?', '/', File::FNM_PATHNAME).should == false File.send(@method, '*', '/', File::FNM_PATHNAME).should == false @@ -165,9 +173,19 @@ describe :file_fnmatch, shared: true do File.should_not.send(@method, '*/*', 'dave/.profile', File::FNM_PATHNAME) end - it "matches patterns with leading periods to dotfiles by default" do + it "matches patterns with leading periods to dotfiles" do File.send(@method, '.*', '.profile').should == true + File.send(@method, '.*', '.profile', File::FNM_PATHNAME).should == true File.send(@method, ".*file", "nondotfile").should == false + File.send(@method, ".*file", "nondotfile", File::FNM_PATHNAME).should == false + end + + it "does not match directories with leading periods by default with FNM_PATHNAME" do + File.send(@method, '.*', '.directory/nondotfile', File::FNM_PATHNAME).should == false + File.send(@method, '.*', '.directory/.profile', File::FNM_PATHNAME).should == false + File.send(@method, '.*', 'foo/.directory/nondotfile', File::FNM_PATHNAME).should == false + File.send(@method, '.*', 'foo/.directory/.profile', File::FNM_PATHNAME).should == false + File.send(@method, '**/.dotfile', '.dotsubdir/.dotfile', File::FNM_PATHNAME).should == false end it "matches leading periods in filenames when flags includes FNM_DOTMATCH" do @@ -221,6 +239,33 @@ describe :file_fnmatch, shared: true do File.send(@method, pattern, 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true end + it "has special handling for ./ when using * and FNM_PATHNAME" do + File.send(@method, './*', '.', File::FNM_PATHNAME).should be_false + File.send(@method, './*', './', File::FNM_PATHNAME).should be_true + File.send(@method, './*/', './', File::FNM_PATHNAME).should be_false + File.send(@method, './**', './', File::FNM_PATHNAME).should be_true + File.send(@method, './**/', './', File::FNM_PATHNAME).should be_true + File.send(@method, './*', '.', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_false + File.send(@method, './*', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true + File.send(@method, './*/', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_false + File.send(@method, './**', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true + File.send(@method, './**/', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true + end + + it "matches **/* with FNM_PATHNAME to recurse directories" do + File.send(@method, 'nested/**/*', 'nested/subdir', File::FNM_PATHNAME).should be_true + File.send(@method, 'nested/**/*', 'nested/subdir/file', File::FNM_PATHNAME).should be_true + File.send(@method, 'nested/**/*', 'nested/.dotsubdir', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true + File.send(@method, 'nested/**/*', 'nested/.dotsubir/.dotfile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true + end + + it "matches ** with FNM_PATHNAME only in current directory" do + File.send(@method, 'nested/**', 'nested/subdir', File::FNM_PATHNAME).should be_true + File.send(@method, 'nested/**', 'nested/subdir/file', File::FNM_PATHNAME).should be_false + File.send(@method, 'nested/**', 'nested/.dotsubdir', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true + File.send(@method, 'nested/**', 'nested/.dotsubir/.dotfile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_false + end + it "accepts an object that has a #to_path method" do File.send(@method, '\*', mock_to_path('a')).should == false end diff --git a/spec/ruby/core/file/shared/path.rb b/spec/ruby/core/file/shared/path.rb index ee8109ba05..5a9fe1b0c5 100644 --- a/spec/ruby/core/file/shared/path.rb +++ b/spec/ruby/core/file/shared/path.rb @@ -1,7 +1,7 @@ describe :file_path, shared: true do before :each do - @name = "file_to_path" - @path = tmp(@name) + @path = tmp("file_to_path") + @name = File.basename(@path) touch @path end @@ -77,18 +77,6 @@ describe :file_path, shared: true do after :each do rm_r @dir end - - ruby_version_is ""..."3.1" do - it "raises IOError if file was opened with File::TMPFILE" do - begin - File.open(@dir, File::RDWR | File::TMPFILE) do |f| - -> { f.send(@method) }.should raise_error(IOError) - end - rescue Errno::EOPNOTSUPP, Errno::EINVAL, Errno::EISDIR - skip "no support from the filesystem" - end - end - end end end end diff --git a/spec/ruby/core/file/shared/update_time.rb b/spec/ruby/core/file/shared/update_time.rb new file mode 100644 index 0000000000..3fe7266a00 --- /dev/null +++ b/spec/ruby/core/file/shared/update_time.rb @@ -0,0 +1,105 @@ +describe :update_time, shared: true do + before :all do + @time_is_float = platform_is :windows + end + + before :each do + @atime = Time.now + @mtime = Time.now + @file1 = tmp("specs_file_utime1") + @file2 = tmp("specs_file_utime2") + touch @file1 + touch @file2 + end + + after :each do + rm_r @file1, @file2 + end + + it "sets the access and modification time of each file" do + File.send(@method, @atime, @mtime, @file1, @file2) + + if @time_is_float + File.atime(@file1).should be_close(@atime, 0.0001) + File.mtime(@file1).should be_close(@mtime, 0.0001) + File.atime(@file2).should be_close(@atime, 0.0001) + File.mtime(@file2).should be_close(@mtime, 0.0001) + else + File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE) + File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE) + File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE) + File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE) + end + end + + it "uses the current times if two nil values are passed" do + tn = Time.now + File.send(@method, nil, nil, @file1, @file2) + + if @time_is_float + File.atime(@file1).should be_close(tn, 0.050) + File.mtime(@file1).should be_close(tn, 0.050) + File.atime(@file2).should be_close(tn, 0.050) + File.mtime(@file2).should be_close(tn, 0.050) + else + File.atime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE) + File.mtime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE) + File.atime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE) + File.mtime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE) + end + end + + it "accepts an object that has a #to_path method" do + File.send(@method, @atime, @mtime, mock_to_path(@file1), mock_to_path(@file2)) + end + + it "accepts numeric atime and mtime arguments" do + if @time_is_float + File.send(@method, @atime.to_f, @mtime.to_f, @file1, @file2) + + File.atime(@file1).should be_close(@atime, 0.0001) + File.mtime(@file1).should be_close(@mtime, 0.0001) + File.atime(@file2).should be_close(@atime, 0.0001) + File.mtime(@file2).should be_close(@mtime, 0.0001) + else + File.send(@method, @atime.to_i, @mtime.to_i, @file1, @file2) + + File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE) + File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE) + File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE) + File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE) + end + end + + it "may set nanosecond precision" do + t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r) + File.send(@method, t, t, @file1) + + File.atime(@file1).nsec.should.between?(0, 123500000) + File.mtime(@file1).nsec.should.between?(0, 123500000) + end + + it "returns the number of filenames in the arguments" do + File.send(@method, @atime.to_f, @mtime.to_f, @file1, @file2).should == 2 + end + + platform_is :linux do + platform_is pointer_size: 64 do + it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10 or 2038-01-19 or 2486-07-02)" do + # https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Timestamps + # "Therefore, timestamps should not overflow until May 2446." + # https://lwn.net/Articles/804382/ + # "On-disk timestamps hitting the y2038 limit..." + # The problem seems to be being improved, but currently it actually fails on XFS on RHEL8 + # https://rubyci.org/logs/rubyci.s3.amazonaws.com/rhel8/ruby-master/log/20201112T123004Z.fail.html.gz + # Amazon Linux 2023 returns 2486-07-02 in this example + # http://rubyci.s3.amazonaws.com/amazon2023/ruby-master/log/20230322T063004Z.fail.html.gz + time = Time.at(1<<44) + File.send(@method, time, time, @file1) + + [559444, 2486, 2446, 2038].should.include? File.atime(@file1).year + [559444, 2486, 2446, 2038].should.include? File.mtime(@file1).year + end + end + end +end diff --git a/spec/ruby/core/file/socket_spec.rb b/spec/ruby/core/file/socket_spec.rb index 5d12e21f55..d3f4eb013a 100644 --- a/spec/ruby/core/file/socket_spec.rb +++ b/spec/ruby/core/file/socket_spec.rb @@ -1,42 +1,10 @@ require_relative '../../spec_helper' require_relative '../../shared/file/socket' -require 'socket' describe "File.socket?" do it_behaves_like :file_socket, :socket?, File -end -describe "File.socket?" do it "returns false if file does not exist" do File.socket?("I_am_a_bogus_file").should == false end - - it "returns false if the file is not a socket" do - filename = tmp("i_exist") - touch(filename) - - File.socket?(filename).should == false - - rm_r filename - end -end - -platform_is_not :windows do - describe "File.socket?" do - before :each do - # We need a really short name here. - # On Linux the path length is limited to 107, see unix(7). - @name = tmp("s") - @server = UNIXServer.new @name - end - - after :each do - @server.close - rm_r @name - end - - it "returns true if the file is a socket" do - File.socket?(@name).should == true - end - end end diff --git a/spec/ruby/core/file/stat/birthtime_spec.rb b/spec/ruby/core/file/stat/birthtime_spec.rb index a727bbe566..9aa39297b2 100644 --- a/spec/ruby/core/file/stat/birthtime_spec.rb +++ b/spec/ruby/core/file/stat/birthtime_spec.rb @@ -1,27 +1,29 @@ require_relative '../../../spec_helper' -describe "File::Stat#birthtime" do - before :each do - @file = tmp('i_exist') - touch(@file) { |f| f.write "rubinius" } - end +platform_is(:windows, :darwin, :freebsd, :netbsd, + *ruby_version_is("4.0") { :linux }, + ) do + not_implemented_messages = [ + "birthtime() function is unimplemented", # unsupported OS/version + "birthtime is unimplemented", # unsupported filesystem + ] - after :each do - rm_r @file - end + describe "File::Stat#birthtime" do + before :each do + @file = tmp('i_exist') + touch(@file) { |f| f.write "rubinius" } + end + + after :each do + rm_r @file + end - platform_is :windows, :darwin, :freebsd, :netbsd do it "returns the birthtime of a File::Stat object" do st = File.stat(@file) st.birthtime.should be_kind_of(Time) st.birthtime.should <= Time.now - end - end - - platform_is :linux, :openbsd do - it "raises an NotImplementedError" do - st = File.stat(@file) - -> { st.birthtime }.should raise_error(NotImplementedError) + rescue NotImplementedError => e + e.message.should.start_with?(*not_implemented_messages) end end end diff --git a/spec/ruby/core/file/stat/rdev_major_spec.rb b/spec/ruby/core/file/stat/rdev_major_spec.rb index f8a8d1b107..e08d19c03a 100644 --- a/spec/ruby/core/file/stat/rdev_major_spec.rb +++ b/spec/ruby/core/file/stat/rdev_major_spec.rb @@ -2,19 +2,12 @@ require_relative '../../../spec_helper' describe "File::Stat#rdev_major" do before :each do - platform_is :solaris do - @name = "/dev/zfs" - end - platform_is_not :solaris do - @name = tmp("file.txt") - touch(@name) - end + @name = tmp("file.txt") + touch(@name) end after :each do - platform_is_not :solaris do - rm_r @name - end + rm_r @name end platform_is_not :windows do diff --git a/spec/ruby/core/file/stat/rdev_minor_spec.rb b/spec/ruby/core/file/stat/rdev_minor_spec.rb index dc30c1f56c..ace5b8a732 100644 --- a/spec/ruby/core/file/stat/rdev_minor_spec.rb +++ b/spec/ruby/core/file/stat/rdev_minor_spec.rb @@ -2,19 +2,12 @@ require_relative '../../../spec_helper' describe "File::Stat#rdev_minor" do before :each do - platform_is :solaris do - @name = "/dev/zfs" - end - platform_is_not :solaris do - @name = tmp("file.txt") - touch(@name) - end + @name = tmp("file.txt") + touch(@name) end after :each do - platform_is_not :solaris do - rm_r @name - end + rm_r @name end platform_is_not :windows do diff --git a/spec/ruby/core/file/utime_spec.rb b/spec/ruby/core/file/utime_spec.rb index a191e29240..d87626be50 100644 --- a/spec/ruby/core/file/utime_spec.rb +++ b/spec/ruby/core/file/utime_spec.rb @@ -1,100 +1,6 @@ require_relative '../../spec_helper' +require_relative 'shared/update_time' describe "File.utime" do - - before :all do - @time_is_float = platform_is :windows - end - - before :each do - @atime = Time.now - @mtime = Time.now - @file1 = tmp("specs_file_utime1") - @file2 = tmp("specs_file_utime2") - touch @file1 - touch @file2 - end - - after :each do - rm_r @file1, @file2 - end - - it "sets the access and modification time of each file" do - File.utime(@atime, @mtime, @file1, @file2) - if @time_is_float - File.atime(@file1).should be_close(@atime, 0.0001) - File.mtime(@file1).should be_close(@mtime, 0.0001) - File.atime(@file2).should be_close(@atime, 0.0001) - File.mtime(@file2).should be_close(@mtime, 0.0001) - else - File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE) - File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE) - File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE) - File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE) - end - end - - it "uses the current times if two nil values are passed" do - tn = Time.now - File.utime(nil, nil, @file1, @file2) - if @time_is_float - File.atime(@file1).should be_close(tn, 0.050) - File.mtime(@file1).should be_close(tn, 0.050) - File.atime(@file2).should be_close(tn, 0.050) - File.mtime(@file2).should be_close(tn, 0.050) - else - File.atime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE) - File.mtime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE) - File.atime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE) - File.mtime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE) - end - end - - it "accepts an object that has a #to_path method" do - File.utime(@atime, @mtime, mock_to_path(@file1), mock_to_path(@file2)) - end - - it "accepts numeric atime and mtime arguments" do - if @time_is_float - File.utime(@atime.to_f, @mtime.to_f, @file1, @file2) - File.atime(@file1).should be_close(@atime, 0.0001) - File.mtime(@file1).should be_close(@mtime, 0.0001) - File.atime(@file2).should be_close(@atime, 0.0001) - File.mtime(@file2).should be_close(@mtime, 0.0001) - else - File.utime(@atime.to_i, @mtime.to_i, @file1, @file2) - File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE) - File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE) - File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE) - File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE) - end - end - - it "may set nanosecond precision" do - t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r) - File.utime(t, t, @file1) - File.atime(@file1).nsec.should.between?(0, 123500000) - File.mtime(@file1).nsec.should.between?(0, 123500000) - end - - it "returns the number of filenames in the arguments" do - File.utime(@atime.to_f, @mtime.to_f, @file1, @file2).should == 2 - end - - platform_is :linux do - platform_is wordsize: 64 do - it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10 or 2038-01-19)" do - # https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Timestamps - # "Therefore, timestamps should not overflow until May 2446." - # https://lwn.net/Articles/804382/ - # "On-disk timestamps hitting the y2038 limit..." - # The problem seems to be being improved, but currently it actually fails on XFS on RHEL8 - # https://rubyci.org/logs/rubyci.s3.amazonaws.com/rhel8/ruby-master/log/20201112T123004Z.fail.html.gz - time = Time.at(1<<44) - File.utime(time, time, @file1) - [559444, 2446, 2038].should.include? File.atime(@file1).year - [559444, 2446, 2038].should.include? File.mtime(@file1).year - end - end - end + it_behaves_like :update_time, :utime end diff --git a/spec/ruby/core/file/zero_spec.rb b/spec/ruby/core/file/zero_spec.rb index 63dd85ee46..01c7505ef2 100644 --- a/spec/ruby/core/file/zero_spec.rb +++ b/spec/ruby/core/file/zero_spec.rb @@ -4,10 +4,4 @@ require_relative '../../shared/file/zero' describe "File.zero?" do it_behaves_like :file_zero, :zero?, File it_behaves_like :file_zero_missing, :zero?, File - - platform_is :solaris do - it "returns false for /dev/null" do - File.zero?('/dev/null').should == true - end - end end |
