diff options
Diffstat (limited to 'spec/ruby/core/file')
-rw-r--r-- | spec/ruby/core/file/absolute_path_spec.rb | 76 | ||||
-rw-r--r-- | spec/ruby/core/file/atime_spec.rb | 7 | ||||
-rw-r--r-- | spec/ruby/core/file/ctime_spec.rb | 5 | ||||
-rw-r--r-- | spec/ruby/core/file/dirname_spec.rb | 39 | ||||
-rw-r--r-- | spec/ruby/core/file/exist_spec.rb | 8 | ||||
-rw-r--r-- | spec/ruby/core/file/expand_path_spec.rb | 2 | ||||
-rw-r--r-- | spec/ruby/core/file/extname_spec.rb | 4 | ||||
-rw-r--r-- | spec/ruby/core/file/flock_spec.rb | 4 | ||||
-rw-r--r-- | spec/ruby/core/file/lutime_spec.rb | 9 | ||||
-rw-r--r-- | spec/ruby/core/file/mtime_spec.rb | 21 | ||||
-rw-r--r-- | spec/ruby/core/file/new_spec.rb | 65 | ||||
-rw-r--r-- | spec/ruby/core/file/open_spec.rb | 25 | ||||
-rw-r--r-- | spec/ruby/core/file/realpath_spec.rb | 4 | ||||
-rw-r--r-- | spec/ruby/core/file/shared/fnmatch.rb | 55 | ||||
-rw-r--r-- | spec/ruby/core/file/shared/path.rb | 18 | ||||
-rw-r--r-- | spec/ruby/core/file/shared/update_time.rb | 105 | ||||
-rw-r--r-- | spec/ruby/core/file/utime_spec.rb | 94 |
17 files changed, 361 insertions, 180 deletions
diff --git a/spec/ruby/core/file/absolute_path_spec.rb b/spec/ruby/core/file/absolute_path_spec.rb index 9f39923472..315eead34f 100644 --- a/spec/ruby/core/file/absolute_path_spec.rb +++ b/spec/ruby/core/file/absolute_path_spec.rb @@ -1,54 +1,52 @@ require_relative '../../spec_helper' -ruby_version_is "2.7" do - describe "File.absolute_path?" do - before :each do - @abs = File.expand_path(__FILE__) - end +describe "File.absolute_path?" do + before :each do + @abs = File.expand_path(__FILE__) + end - it "returns true if it's an absolute pathname" do - File.absolute_path?(@abs).should be_true - end + it "returns true if it's an absolute pathname" do + File.absolute_path?(@abs).should be_true + end - it "returns false if it's a relative path" do - File.absolute_path?(File.basename(__FILE__)).should be_false - end + it "returns false if it's a relative path" do + File.absolute_path?(File.basename(__FILE__)).should be_false + end - it "returns false if it's a tricky relative path" do - File.absolute_path?("C:foo\\bar").should be_false - end + it "returns false if it's a tricky relative path" do + File.absolute_path?("C:foo\\bar").should be_false + end - it "does not expand '~' to a home directory." do - File.absolute_path?('~').should be_false - end + it "does not expand '~' to a home directory." do + File.absolute_path?('~').should be_false + end - it "does not expand '~user' to a home directory." do - path = File.dirname(@abs) - Dir.chdir(path) do - File.absolute_path?('~user').should be_false - end + it "does not expand '~user' to a home directory." do + path = File.dirname(@abs) + Dir.chdir(path) do + File.absolute_path?('~user').should be_false end + end - it "calls #to_path on its argument" do - mock = mock_to_path(File.expand_path(__FILE__)) + it "calls #to_path on its argument" do + mock = mock_to_path(File.expand_path(__FILE__)) - File.absolute_path?(mock).should be_true - end + File.absolute_path?(mock).should be_true + end - platform_is_not :windows do - it "takes into consideration the platform's root" do - File.absolute_path?("C:\\foo\\bar").should be_false - File.absolute_path?("C:/foo/bar").should be_false - File.absolute_path?("/foo/bar\\baz").should be_true - end + platform_is_not :windows do + it "takes into consideration the platform's root" do + File.absolute_path?("C:\\foo\\bar").should be_false + File.absolute_path?("C:/foo/bar").should be_false + File.absolute_path?("/foo/bar\\baz").should be_true end + end - platform_is :windows do - it "takes into consideration the platform path separator(s)" do - File.absolute_path?("C:\\foo\\bar").should be_true - File.absolute_path?("C:/foo/bar").should be_true - File.absolute_path?("/foo/bar\\baz").should be_false - end + platform_is :windows do + it "takes into consideration the platform path separator(s)" do + File.absolute_path?("C:\\foo\\bar").should be_true + File.absolute_path?("C:/foo/bar").should be_true + File.absolute_path?("/foo/bar\\baz").should be_false end end end @@ -87,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 cef07ba010..e47e70e5ac 100644 --- a/spec/ruby/core/file/atime_spec.rb +++ b/spec/ruby/core/file/atime_spec.rb @@ -16,10 +16,10 @@ describe "File.atime" do end platform_is :linux, :windows do - platform_is_not :"powerpc64le-linux" do # https://bugs.ruby-lang.org/issues/17926 + unless ENV.key?('TRAVIS') # https://bugs.ruby-lang.org/issues/17926 ## NOTE also that some Linux systems disable atime (e.g. via mount params) for better filesystem speed. it "returns the last access time for the named file with microseconds" do - supports_subseconds = Integer(`stat -c%x '#{__FILE__}'`[/\.(\d+)/, 1], 10) + supports_subseconds = Integer(`stat -c%x '#{__FILE__}'`[/\.(\d{1,6})/, 1], 10) if supports_subseconds != 0 expected_time = Time.at(Time.now.to_i + 0.123456) File.utime expected_time, 0, @file @@ -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/ctime_spec.rb b/spec/ruby/core/file/ctime_spec.rb index b16eb13c1e..718f26d5cc 100644 --- a/spec/ruby/core/file/ctime_spec.rb +++ b/spec/ruby/core/file/ctime_spec.rb @@ -16,12 +16,15 @@ describe "File.ctime" do platform_is :linux, :windows do it "returns the change time for the named file (the time at which directory information about the file was changed, not the file itself) with microseconds." do - supports_subseconds = Integer(`stat -c%z '#{__FILE__}'`[/\.(\d+)/, 1], 10) + supports_subseconds = Integer(`stat -c%z '#{__FILE__}'`[/\.(\d{1,6})/, 1], 10) if supports_subseconds != 0 File.ctime(__FILE__).usec.should > 0 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..8dd6c4ca88 100644 --- a/spec/ruby/core/file/dirname_spec.rb +++ b/spec/ruby/core/file/dirname_spec.rb @@ -12,18 +12,33 @@ describe "File.dirname" do end ruby_version_is '3.1' 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 - 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) + 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 + 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, "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 end diff --git a/spec/ruby/core/file/exist_spec.rb b/spec/ruby/core/file/exist_spec.rb index ddb5febcba..2633376880 100644 --- a/spec/ruby/core/file/exist_spec.rb +++ b/spec/ruby/core/file/exist_spec.rb @@ -4,3 +4,11 @@ require_relative '../../shared/file/exist' describe "File.exist?" do it_behaves_like :file_exist, :exist?, File end + +ruby_version_is "3.2" do + describe "File.exists?" do + it "has been removed" do + File.should_not.respond_to?(:exists?) + end + 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/extname_spec.rb b/spec/ruby/core/file/extname_spec.rb index e182ed44f2..d20cf813d9 100644 --- a/spec/ruby/core/file/extname_spec.rb +++ b/spec/ruby/core/file/extname_spec.rb @@ -33,14 +33,14 @@ describe "File.extname" do end describe "for a filename ending with a dot" do - guard -> { platform_is :windows or ruby_version_is ""..."2.7" } do + platform_is :windows do it "returns ''" do File.extname(".foo.").should == "" File.extname("foo.").should == "" end end - guard -> { platform_is_not :windows and ruby_version_is "2.7" } do + platform_is_not :windows do it "returns '.'" do File.extname(".foo.").should == "." File.extname("foo.").should == "." diff --git a/spec/ruby/core/file/flock_spec.rb b/spec/ruby/core/file/flock_spec.rb index 751e99d994..070d830bc4 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 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 8d47d3021a..0e9c95caee 100644 --- a/spec/ruby/core/file/mtime_spec.rb +++ b/spec/ruby/core/file/mtime_spec.rb @@ -16,14 +16,19 @@ describe "File.mtime" do end platform_is :linux, :windows do - it "returns the modification Time of the file with microseconds" do - supports_subseconds = Integer(`stat -c%y '#{__FILE__}'`[/\.(\d+)/, 1], 10) - if supports_subseconds != 0 - expected_time = Time.at(Time.now.to_i + 0.123456) - File.utime 0, expected_time, @filename - File.mtime(@filename).usec.should == expected_time.usec - else - File.mtime(__FILE__).usec.should == 0 + unless ENV.key?('TRAVIS') # https://bugs.ruby-lang.org/issues/17926 + it "returns the modification Time of the file with microseconds" do + supports_subseconds = Integer(`stat -c%y '#{__FILE__}'`[/\.(\d{1,6})/, 1], 10) + if supports_subseconds != 0 + expected_time = Time.at(Time.now.to_i + 0.123456) + File.utime 0, expected_time, @filename + File.mtime(@filename).usec.should == expected_time.usec + 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 c7dd34d5c6..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 @@ -494,6 +494,14 @@ describe "File.open" do File.open(@file, "w") { |f| f.puts "testing" } File.size(@file).should > 0 File.open(@file, "rb+") do |f| + f.binmode?.should == true + f.external_encoding.should == Encoding::ASCII_8BIT + f.pos.should == 0 + f.should_not.eof? + end + File.open(@file, "r+b") do |f| + f.binmode?.should == true + f.external_encoding.should == Encoding::ASCII_8BIT f.pos.should == 0 f.should_not.eof? end @@ -557,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/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/shared/fnmatch.rb b/spec/ruby/core/file/shared/fnmatch.rb index 00682bb64c..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 @@ -159,15 +167,25 @@ describe :file_fnmatch, shared: true do end it "does not match leading periods in filenames with wildcards by default" do - File.send(@method, '*', '.profile').should == false - File.send(@method, '*', 'home/.profile').should == true - File.send(@method, '*/*', 'home/.profile').should == true - File.send(@method, '*/*', 'dave/.profile', File::FNM_PATHNAME).should == false + File.should_not.send(@method, '*', '.profile') + File.should.send(@method, '*', 'home/.profile') + File.should.send(@method, '*/*', 'home/.profile') + 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 0a5abe33f0..aa2a64cf25 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 @@ -78,13 +78,15 @@ describe :file_path, shared: true do rm_r @dir end - 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) + 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 - rescue Errno::EOPNOTSUPP, Errno::EINVAL, Errno::EISDIR - skip "no support from the filesystem" 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..9c063a8e93 --- /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 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 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/utime_spec.rb b/spec/ruby/core/file/utime_spec.rb index 59eef20c66..d87626be50 100644 --- a/spec/ruby/core/file/utime_spec.rb +++ b/spec/ruby/core/file/utime_spec.rb @@ -1,96 +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 - - 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 |