summaryrefslogtreecommitdiff
path: root/spec/ruby/core/file/shared
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/file/shared')
-rw-r--r--spec/ruby/core/file/shared/fnmatch.rb91
-rw-r--r--spec/ruby/core/file/shared/open.rb2
-rw-r--r--spec/ruby/core/file/shared/path.rb33
-rw-r--r--spec/ruby/core/file/shared/read.rb8
-rw-r--r--spec/ruby/core/file/shared/stat.rb6
-rw-r--r--spec/ruby/core/file/shared/unlink.rb4
-rw-r--r--spec/ruby/core/file/shared/update_time.rb105
7 files changed, 207 insertions, 42 deletions
diff --git a/spec/ruby/core/file/shared/fnmatch.rb b/spec/ruby/core/file/shared/fnmatch.rb
index a8488fd30a..b9140d027d 100644
--- a/spec/ruby/core/file/shared/fnmatch.rb
+++ b/spec/ruby/core/file/shared/fnmatch.rb
@@ -75,6 +75,14 @@ describe :file_fnmatch, shared: true do
File.send(@method, 'c*t', 'c/a/b/t').should == true
end
+ it "does not match unterminated range of characters" do
+ File.send(@method, 'abc[de', 'abcd').should == false
+ end
+
+ it "does not match unterminated range of characters as a literal" do
+ File.send(@method, 'abc[de', 'abc[de').should == false
+ end
+
it "matches ranges of characters using bracket expression (e.g. [a-z])" do
File.send(@method, 'ca[a-z]', 'cat').should == true
end
@@ -94,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
@@ -117,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
@@ -151,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
@@ -196,21 +222,48 @@ describe :file_fnmatch, shared: true do
it "returns false if '/' in pattern do not match '/' in path when flags includes FNM_PATHNAME" do
pattern = '*/*'
- File.send(@method, pattern, 'dave/.profile', File::FNM_PATHNAME).should be_false
+ File.send(@method, pattern, 'dave/.profile', File::FNM_PATHNAME).should == false
pattern = '**/foo'
- File.send(@method, pattern, 'a/.b/c/foo', File::FNM_PATHNAME).should be_false
+ File.send(@method, pattern, 'a/.b/c/foo', File::FNM_PATHNAME).should == false
end
it "returns true if '/' in pattern match '/' in path when flags includes FNM_PATHNAME" do
pattern = '*/*'
- File.send(@method, pattern, 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true
+ File.send(@method, pattern, 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true
pattern = '**/foo'
- File.send(@method, pattern, 'a/b/c/foo', File::FNM_PATHNAME).should be_true
- File.send(@method, pattern, '/a/b/c/foo', File::FNM_PATHNAME).should be_true
- File.send(@method, pattern, 'c:/a/b/c/foo', File::FNM_PATHNAME).should be_true
- File.send(@method, pattern, 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true
+ File.send(@method, pattern, 'a/b/c/foo', File::FNM_PATHNAME).should == true
+ File.send(@method, pattern, '/a/b/c/foo', File::FNM_PATHNAME).should == true
+ File.send(@method, pattern, 'c:/a/b/c/foo', File::FNM_PATHNAME).should == true
+ File.send(@method, pattern, 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true
+ end
+
+ it "has special handling for ./ when using * and FNM_PATHNAME" do
+ File.send(@method, './*', '.', File::FNM_PATHNAME).should == false
+ File.send(@method, './*', './', File::FNM_PATHNAME).should == true
+ File.send(@method, './*/', './', File::FNM_PATHNAME).should == false
+ File.send(@method, './**', './', File::FNM_PATHNAME).should == true
+ File.send(@method, './**/', './', File::FNM_PATHNAME).should == true
+ File.send(@method, './*', '.', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == false
+ File.send(@method, './*', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true
+ File.send(@method, './*/', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == false
+ File.send(@method, './**', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true
+ File.send(@method, './**/', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true
+ end
+
+ it "matches **/* with FNM_PATHNAME to recurse directories" do
+ File.send(@method, 'nested/**/*', 'nested/subdir', File::FNM_PATHNAME).should == true
+ File.send(@method, 'nested/**/*', 'nested/subdir/file', File::FNM_PATHNAME).should == true
+ File.send(@method, 'nested/**/*', 'nested/.dotsubdir', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true
+ File.send(@method, 'nested/**/*', 'nested/.dotsubir/.dotfile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true
+ end
+
+ it "matches ** with FNM_PATHNAME only in current directory" do
+ File.send(@method, 'nested/**', 'nested/subdir', File::FNM_PATHNAME).should == true
+ File.send(@method, 'nested/**', 'nested/subdir/file', File::FNM_PATHNAME).should == false
+ File.send(@method, 'nested/**', 'nested/.dotsubdir', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true
+ File.send(@method, 'nested/**', 'nested/.dotsubir/.dotfile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == false
end
it "accepts an object that has a #to_path method" do
@@ -218,21 +271,21 @@ describe :file_fnmatch, shared: true do
end
it "raises a TypeError if the first and second arguments are not string-like" do
- -> { File.send(@method, nil, nil, 0, 0) }.should raise_error(ArgumentError)
- -> { File.send(@method, 1, 'some/thing') }.should raise_error(TypeError)
- -> { File.send(@method, 'some/thing', 1) }.should raise_error(TypeError)
- -> { File.send(@method, 1, 1) }.should raise_error(TypeError)
+ -> { File.send(@method, nil, nil, 0, 0) }.should.raise(ArgumentError)
+ -> { File.send(@method, 1, 'some/thing') }.should.raise(TypeError)
+ -> { File.send(@method, 'some/thing', 1) }.should.raise(TypeError)
+ -> { File.send(@method, 1, 1) }.should.raise(TypeError)
end
it "raises a TypeError if the third argument is not an Integer" do
- -> { File.send(@method, "*/place", "path/to/file", "flags") }.should raise_error(TypeError)
- -> { File.send(@method, "*/place", "path/to/file", nil) }.should raise_error(TypeError)
+ -> { File.send(@method, "*/place", "path/to/file", "flags") }.should.raise(TypeError)
+ -> { File.send(@method, "*/place", "path/to/file", nil) }.should.raise(TypeError)
end
it "does not raise a TypeError if the third argument can be coerced to an Integer" do
flags = mock("flags")
flags.should_receive(:to_int).and_return(10)
- -> { File.send(@method, "*/place", "path/to/file", flags) }.should_not raise_error
+ -> { File.send(@method, "*/place", "path/to/file", flags) }.should_not.raise
end
it "matches multibyte characters" do
diff --git a/spec/ruby/core/file/shared/open.rb b/spec/ruby/core/file/shared/open.rb
index 677a82a351..67149235ca 100644
--- a/spec/ruby/core/file/shared/open.rb
+++ b/spec/ruby/core/file/shared/open.rb
@@ -4,7 +4,7 @@ describe :open_directory, shared: true do
it "opens directories" do
file = File.send(@method, tmp(""))
begin
- file.should be_kind_of(File)
+ file.should.is_a?(File)
ensure
file.close
end
diff --git a/spec/ruby/core/file/shared/path.rb b/spec/ruby/core/file/shared/path.rb
index d964acc855..6c6f7d4234 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
@@ -12,7 +12,24 @@ describe :file_path, shared: true do
it "returns a String" do
@file = File.new @path
- @file.send(@method).should be_an_instance_of(String)
+ @file.send(@method).should.instance_of?(String)
+ end
+
+ it "returns a different String on every call" do
+ @file = File.new @path
+ path1 = @file.send(@method)
+ path2 = @file.send(@method)
+ path1.should == path2
+ path1.should_not.equal?(path2)
+ end
+
+ it "returns a mutable String" do
+ @file = File.new @path.dup.freeze
+ path = @file.send(@method)
+ path.should == @path
+ path.should_not.frozen?
+ path << "test"
+ @file.send(@method).should == @path
end
it "calls to_str on argument and returns exact value" do
@@ -60,16 +77,6 @@ describe :file_path, shared: true do
after :each 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)
- end
- rescue Errno::EOPNOTSUPP, Errno::EINVAL, Errno::EISDIR
- skip "no support from the filesystem"
- end
- end
end
end
end
diff --git a/spec/ruby/core/file/shared/read.rb b/spec/ruby/core/file/shared/read.rb
index a2d479966d..f60800bb2f 100644
--- a/spec/ruby/core/file/shared/read.rb
+++ b/spec/ruby/core/file/shared/read.rb
@@ -1,15 +1,15 @@
require_relative '../../dir/fixtures/common'
describe :file_read_directory, shared: true do
- platform_is :darwin, :linux, :openbsd, :windows do
+ platform_is :darwin, :linux, :freebsd, :openbsd, :windows do
it "raises an Errno::EISDIR when passed a path that is a directory" do
- -> { @object.send(@method, ".") }.should raise_error(Errno::EISDIR)
+ -> { @object.send(@method, ".") }.should.raise(Errno::EISDIR)
end
end
- platform_is :freebsd, :netbsd do
+ platform_is :netbsd do
it "does not raises any exception when passed a path that is a directory" do
- -> { @object.send(@method, ".") }.should_not raise_error
+ -> { @object.send(@method, ".") }.should_not.raise
end
end
end
diff --git a/spec/ruby/core/file/shared/stat.rb b/spec/ruby/core/file/shared/stat.rb
index fdaf97ea61..879a7f11ff 100644
--- a/spec/ruby/core/file/shared/stat.rb
+++ b/spec/ruby/core/file/shared/stat.rb
@@ -10,13 +10,13 @@ describe :file_stat, shared: true do
it "returns a File::Stat object if the given file exists" do
st = File.send(@method, @file)
- st.should be_an_instance_of(File::Stat)
+ st.should.instance_of?(File::Stat)
end
it "returns a File::Stat object when called on an instance of File" do
File.open(@file) do |f|
st = f.send(@method)
- st.should be_an_instance_of(File::Stat)
+ st.should.instance_of?(File::Stat)
end
end
@@ -27,6 +27,6 @@ describe :file_stat, shared: true do
it "raises an Errno::ENOENT if the file does not exist" do
-> {
File.send(@method, "fake_file")
- }.should raise_error(Errno::ENOENT)
+ }.should.raise(Errno::ENOENT)
end
end
diff --git a/spec/ruby/core/file/shared/unlink.rb b/spec/ruby/core/file/shared/unlink.rb
index e339e93271..0032907ba2 100644
--- a/spec/ruby/core/file/shared/unlink.rb
+++ b/spec/ruby/core/file/shared/unlink.rb
@@ -31,11 +31,11 @@ describe :file_unlink, shared: true do
end
it "raises a TypeError if not passed a String type" do
- -> { File.send(@method, 1) }.should raise_error(TypeError)
+ -> { File.send(@method, 1) }.should.raise(TypeError)
end
it "raises an Errno::ENOENT when the given file doesn't exist" do
- -> { File.send(@method, 'bogus') }.should raise_error(Errno::ENOENT)
+ -> { File.send(@method, 'bogus') }.should.raise(Errno::ENOENT)
end
it "coerces a given parameter into a string if possible" do
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