summaryrefslogtreecommitdiff
path: root/spec/ruby/core/file
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/file')
-rw-r--r--spec/ruby/core/file/absolute_path_spec.rb37
-rw-r--r--spec/ruby/core/file/atime_spec.rb55
-rw-r--r--spec/ruby/core/file/basename_spec.rb170
-rw-r--r--spec/ruby/core/file/birthtime_spec.rb56
-rw-r--r--spec/ruby/core/file/blockdev_spec.rb6
-rw-r--r--spec/ruby/core/file/chardev_spec.rb6
-rw-r--r--spec/ruby/core/file/chmod_spec.rb239
-rw-r--r--spec/ruby/core/file/chown_spec.rb152
-rw-r--r--spec/ruby/core/file/constants/constants_spec.rb31
-rw-r--r--spec/ruby/core/file/constants_spec.rb141
-rw-r--r--spec/ruby/core/file/ctime_spec.rb51
-rw-r--r--spec/ruby/core/file/delete_spec.rb6
-rw-r--r--spec/ruby/core/file/directory_spec.rb10
-rw-r--r--spec/ruby/core/file/dirname_spec.rb108
-rw-r--r--spec/ruby/core/file/executable_real_spec.rb7
-rw-r--r--spec/ruby/core/file/executable_spec.rb7
-rw-r--r--spec/ruby/core/file/exist_spec.rb6
-rw-r--r--spec/ruby/core/file/exists_spec.rb6
-rw-r--r--spec/ruby/core/file/expand_path_spec.rb242
-rw-r--r--spec/ruby/core/file/extname_spec.rb54
-rw-r--r--spec/ruby/core/file/file_spec.rb16
-rw-r--r--spec/ruby/core/file/fixtures/common.rb22
-rw-r--r--spec/ruby/core/file/fixtures/do_not_remove1
-rw-r--r--spec/ruby/core/file/fixtures/file_types.rb64
-rw-r--r--spec/ruby/core/file/flock_spec.rb106
-rw-r--r--spec/ruby/core/file/fnmatch_spec.rb10
-rw-r--r--spec/ruby/core/file/ftype_spec.rb73
-rw-r--r--spec/ruby/core/file/grpowned_spec.rb10
-rw-r--r--spec/ruby/core/file/identical_spec.rb6
-rw-r--r--spec/ruby/core/file/initialize_spec.rb23
-rw-r--r--spec/ruby/core/file/inspect_spec.rb17
-rw-r--r--spec/ruby/core/file/join_spec.rb139
-rw-r--r--spec/ruby/core/file/lchmod_spec.rb42
-rw-r--r--spec/ruby/core/file/lchown_spec.rb63
-rw-r--r--spec/ruby/core/file/link_spec.rb39
-rw-r--r--spec/ruby/core/file/lstat_spec.rb33
-rw-r--r--spec/ruby/core/file/mkfifo_spec.rb53
-rw-r--r--spec/ruby/core/file/mtime_spec.rb51
-rw-r--r--spec/ruby/core/file/new_spec.rb162
-rw-r--r--spec/ruby/core/file/null_spec.rb15
-rw-r--r--spec/ruby/core/file/open_spec.rb678
-rw-r--r--spec/ruby/core/file/owned_spec.rb33
-rw-r--r--spec/ruby/core/file/path_spec.rb29
-rw-r--r--spec/ruby/core/file/pipe_spec.rb32
-rw-r--r--spec/ruby/core/file/read_spec.rb6
-rw-r--r--spec/ruby/core/file/readable_real_spec.rb7
-rw-r--r--spec/ruby/core/file/readable_spec.rb7
-rw-r--r--spec/ruby/core/file/readlink_spec.rb67
-rw-r--r--spec/ruby/core/file/realdirpath_spec.rb104
-rw-r--r--spec/ruby/core/file/realpath_spec.rb88
-rw-r--r--spec/ruby/core/file/rename_spec.rb37
-rw-r--r--spec/ruby/core/file/reopen_spec.rb32
-rw-r--r--spec/ruby/core/file/setgid_spec.rb36
-rw-r--r--spec/ruby/core/file/setuid_spec.rb38
-rw-r--r--spec/ruby/core/file/shared/fnmatch.rb241
-rw-r--r--spec/ruby/core/file/shared/open.rb12
-rw-r--r--spec/ruby/core/file/shared/read.rb15
-rw-r--r--spec/ruby/core/file/shared/stat.rb32
-rw-r--r--spec/ruby/core/file/shared/unlink.rb63
-rw-r--r--spec/ruby/core/file/size_spec.rb119
-rw-r--r--spec/ruby/core/file/socket_spec.rb42
-rw-r--r--spec/ruby/core/file/split_spec.rb63
-rw-r--r--spec/ruby/core/file/stat/atime_spec.rb18
-rw-r--r--spec/ruby/core/file/stat/birthtime_spec.rb27
-rw-r--r--spec/ruby/core/file/stat/blksize_spec.rb27
-rw-r--r--spec/ruby/core/file/stat/blockdev_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/blocks_spec.rb27
-rw-r--r--spec/ruby/core/file/stat/chardev_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/comparison_spec.rb66
-rw-r--r--spec/ruby/core/file/stat/ctime_spec.rb18
-rw-r--r--spec/ruby/core/file/stat/dev_major_spec.rb23
-rw-r--r--spec/ruby/core/file/stat/dev_minor_spec.rb23
-rw-r--r--spec/ruby/core/file/stat/dev_spec.rb15
-rw-r--r--spec/ruby/core/file/stat/directory_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/executable_real_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/executable_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/file_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/fixtures/classes.rb5
-rw-r--r--spec/ruby/core/file/stat/ftype_spec.rb68
-rw-r--r--spec/ruby/core/file/stat/gid_spec.rb19
-rw-r--r--spec/ruby/core/file/stat/grpowned_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/ino_spec.rb38
-rw-r--r--spec/ruby/core/file/stat/inspect_spec.rb26
-rw-r--r--spec/ruby/core/file/stat/mode_spec.rb19
-rw-r--r--spec/ruby/core/file/stat/mtime_spec.rb18
-rw-r--r--spec/ruby/core/file/stat/new_spec.rb30
-rw-r--r--spec/ruby/core/file/stat/nlink_spec.rb21
-rw-r--r--spec/ruby/core/file/stat/owned_spec.rb31
-rw-r--r--spec/ruby/core/file/stat/pipe_spec.rb32
-rw-r--r--spec/ruby/core/file/stat/rdev_major_spec.rb31
-rw-r--r--spec/ruby/core/file/stat/rdev_minor_spec.rb31
-rw-r--r--spec/ruby/core/file/stat/rdev_spec.rb15
-rw-r--r--spec/ruby/core/file/stat/readable_real_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/readable_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/setgid_spec.rb11
-rw-r--r--spec/ruby/core/file/stat/setuid_spec.rb11
-rw-r--r--spec/ruby/core/file/stat/size_spec.rb21
-rw-r--r--spec/ruby/core/file/stat/socket_spec.rb11
-rw-r--r--spec/ruby/core/file/stat/sticky_spec.rb11
-rw-r--r--spec/ruby/core/file/stat/symlink_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/uid_spec.rb18
-rw-r--r--spec/ruby/core/file/stat/world_readable_spec.rb11
-rw-r--r--spec/ruby/core/file/stat/world_writable_spec.rb11
-rw-r--r--spec/ruby/core/file/stat/writable_real_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/writable_spec.rb7
-rw-r--r--spec/ruby/core/file/stat/zero_spec.rb7
-rw-r--r--spec/ruby/core/file/stat_spec.rb45
-rw-r--r--spec/ruby/core/file/sticky_spec.rb50
-rw-r--r--spec/ruby/core/file/symlink_spec.rb57
-rw-r--r--spec/ruby/core/file/to_path_spec.rb49
-rw-r--r--spec/ruby/core/file/truncate_spec.rb177
-rw-r--r--spec/ruby/core/file/umask_spec.rb60
-rw-r--r--spec/ruby/core/file/unlink_spec.rb6
-rw-r--r--spec/ruby/core/file/utime_spec.rb36
-rw-r--r--spec/ruby/core/file/world_readable_spec.rb12
-rw-r--r--spec/ruby/core/file/world_writable_spec.rb12
-rw-r--r--spec/ruby/core/file/writable_real_spec.rb7
-rw-r--r--spec/ruby/core/file/writable_spec.rb7
-rw-r--r--spec/ruby/core/file/zero_spec.rb13
119 files changed, 5471 insertions, 0 deletions
diff --git a/spec/ruby/core/file/absolute_path_spec.rb b/spec/ruby/core/file/absolute_path_spec.rb
new file mode 100644
index 0000000000..b1f4f05aee
--- /dev/null
+++ b/spec/ruby/core/file/absolute_path_spec.rb
@@ -0,0 +1,37 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File.absolute_path" do
+ before :each do
+ @abs = File.expand_path(__FILE__)
+ end
+
+ it "returns the argument if it's an absolute pathname" do
+ File.absolute_path(@abs).should == @abs
+ end
+
+ it "resolves paths relative to the current working directory" do
+ path = File.dirname(@abs)
+ Dir.chdir(path) do
+ File.absolute_path('hello.txt').should == File.join(Dir.pwd, 'hello.txt')
+ end
+ end
+
+ it "does not expand '~' to a home directory." do
+ File.absolute_path('~').should_not == File.expand_path('~')
+ end
+
+ it "does not expand '~user' to a home directory." do
+ path = File.dirname(@abs)
+ Dir.chdir(path) do
+ File.absolute_path('~user').should == File.join(Dir.pwd, '~user')
+ end
+ 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
+ end
+
+ it "calls #to_path on its argument" do
+ File.absolute_path(mock_to_path(@abs)).should == @abs
+ end
+end
diff --git a/spec/ruby/core/file/atime_spec.rb b/spec/ruby/core/file/atime_spec.rb
new file mode 100644
index 0000000000..76e7fbd62a
--- /dev/null
+++ b/spec/ruby/core/file/atime_spec.rb
@@ -0,0 +1,55 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File.atime" do
+ before :each do
+ @file = tmp('test.txt')
+ touch @file
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the last access time for the named file as a Time object" do
+ File.atime(@file)
+ File.atime(@file).should be_kind_of(Time)
+ end
+
+ platform_is :linux do
+ ## 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)
+ if supports_subseconds != 0
+ expected_time = Time.at(Time.now.to_i + 0.123456)
+ File.utime expected_time, 0, @file
+ File.atime(@file).usec.should == expected_time.usec
+ else
+ File.atime(__FILE__).usec.should == 0
+ end
+ end
+ end
+
+ it "raises an Errno::ENOENT exception if the file is not found" do
+ lambda { File.atime('a_fake_file') }.should raise_error(Errno::ENOENT)
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.atime(mock_to_path(@file))
+ end
+end
+
+describe "File#atime" do
+ before :each do
+ @name = File.expand_path(__FILE__)
+ @file = File.open(@name)
+ end
+
+ after :each do
+ @file.close rescue nil
+ end
+
+ it "returns the last access time to self" do
+ @file.atime
+ @file.atime.should be_kind_of(Time)
+ end
+end
diff --git a/spec/ruby/core/file/basename_spec.rb b/spec/ruby/core/file/basename_spec.rb
new file mode 100644
index 0000000000..4cf26062d3
--- /dev/null
+++ b/spec/ruby/core/file/basename_spec.rb
@@ -0,0 +1,170 @@
+# -*- encoding: utf-8 -*-
+require File.expand_path('../../../spec_helper', __FILE__)
+
+# TODO: Fix these
+describe "File.basename" do
+ it "returns the basename of a path (basic cases)" do
+ File.basename("/Some/path/to/test.txt").should == "test.txt"
+ File.basename(File.join("/tmp")).should == "tmp"
+ File.basename(File.join(*%w( g f d s a b))).should == "b"
+ File.basename("/tmp", ".*").should == "tmp"
+ File.basename("/tmp", ".c").should == "tmp"
+ File.basename("/tmp.c", ".c").should == "tmp"
+ File.basename("/tmp.c", ".*").should == "tmp"
+ File.basename("/tmp.c", ".?").should == "tmp.c"
+ File.basename("/tmp.cpp", ".*").should == "tmp"
+ File.basename("/tmp.cpp", ".???").should == "tmp.cpp"
+ File.basename("/tmp.o", ".c").should == "tmp.o"
+ File.basename(File.join("/tmp/")).should == "tmp"
+ File.basename("/").should == "/"
+ File.basename("//").should == "/"
+ File.basename("dir///base", ".*").should == "base"
+ File.basename("dir///base", ".c").should == "base"
+ File.basename("dir///base.c", ".c").should == "base"
+ File.basename("dir///base.c", ".*").should == "base"
+ File.basename("dir///base.o", ".c").should == "base.o"
+ File.basename("dir///base///").should == "base"
+ File.basename("dir//base/", ".*").should == "base"
+ File.basename("dir//base/", ".c").should == "base"
+ File.basename("dir//base.c/", ".c").should == "base"
+ File.basename("dir//base.c/", ".*").should == "base"
+ end
+
+ it "returns the last component of the filename" do
+ File.basename('a').should == 'a'
+ File.basename('/a').should == 'a'
+ File.basename('/a/b').should == 'b'
+ File.basename('/ab/ba/bag').should == 'bag'
+ File.basename('/ab/ba/bag.txt').should == 'bag.txt'
+ File.basename('/').should == '/'
+ File.basename('/foo/bar/baz.rb', '.rb').should == 'baz'
+ File.basename('baz.rb', 'z.rb').should == 'ba'
+ end
+
+ it "returns an string" do
+ File.basename("foo").should be_kind_of(String)
+ end
+
+ it "returns the basename for unix format" do
+ File.basename("/foo/bar").should == "bar"
+ File.basename("/foo/bar.txt").should == "bar.txt"
+ File.basename("bar.c").should == "bar.c"
+ File.basename("/bar").should == "bar"
+ File.basename("/bar/").should == "bar"
+
+ # Considered UNC paths on Windows
+ platform_is :windows do
+ File.basename("baz//foo").should =="foo"
+ File.basename("//foo/bar/baz").should == "baz"
+ end
+ end
+
+ it "returns the basename for edge cases" do
+ File.basename("").should == ""
+ File.basename(".").should == "."
+ File.basename("..").should == ".."
+ platform_is_not :windows do
+ File.basename("//foo/").should == "foo"
+ File.basename("//foo//").should == "foo"
+ end
+ File.basename("foo/").should == "foo"
+ end
+
+ it "ignores a trailing directory separator" do
+ File.basename("foo.rb/", '.rb').should == "foo"
+ File.basename("bar.rb///", '.*').should == "bar"
+ end
+
+ it "returns the basename for unix suffix" do
+ File.basename("bar.c", ".c").should == "bar"
+ File.basename("bar.txt", ".txt").should == "bar"
+ File.basename("/bar.txt", ".txt").should == "bar"
+ File.basename("/foo/bar.txt", ".txt").should == "bar"
+ File.basename("bar.txt", ".exe").should == "bar.txt"
+ File.basename("bar.txt.exe", ".exe").should == "bar.txt"
+ File.basename("bar.txt.exe", ".txt").should == "bar.txt.exe"
+ File.basename("bar.txt", ".*").should == "bar"
+ File.basename("bar.txt.exe", ".*").should == "bar.txt"
+ File.basename("bar.txt.exe", ".txt.exe").should == "bar"
+ end
+
+ platform_is_not :windows do
+ it "takes into consideration the platform path separator(s)" do
+ File.basename("C:\\foo\\bar").should == "C:\\foo\\bar"
+ File.basename("C:/foo/bar").should == "bar"
+ File.basename("/foo/bar\\baz").should == "bar\\baz"
+ end
+ end
+
+ platform_is :windows do
+ it "takes into consideration the platform path separator(s)" do
+ File.basename("C:\\foo\\bar").should == "bar"
+ File.basename("C:/foo/bar").should == "bar"
+ File.basename("/foo/bar\\baz").should == "baz"
+ end
+ end
+
+ it "raises a TypeError if the arguments are not String types" do
+ lambda { File.basename(nil) }.should raise_error(TypeError)
+ lambda { File.basename(1) }.should raise_error(TypeError)
+ lambda { File.basename("bar.txt", 1) }.should raise_error(TypeError)
+ lambda { File.basename(true) }.should raise_error(TypeError)
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.basename(mock_to_path("foo.txt"))
+ end
+
+ it "raises an ArgumentError if passed more than two arguments" do
+ lambda { File.basename('bar.txt', '.txt', '.txt') }.should raise_error(ArgumentError)
+ end
+
+ # specific to MS Windows
+ platform_is :windows do
+ it "returns the basename for windows" do
+ File.basename("C:\\foo\\bar\\baz.txt").should == "baz.txt"
+ File.basename("C:\\foo\\bar").should == "bar"
+ File.basename("C:\\foo\\bar\\").should == "bar"
+ File.basename("C:\\foo").should == "foo"
+ File.basename("C:\\").should == "\\"
+ end
+
+ it "returns basename windows unc" do
+ File.basename("\\\\foo\\bar\\baz.txt").should == "baz.txt"
+ File.basename("\\\\foo\\bar\\baz").should =="baz"
+ end
+
+ it "returns basename windows forward slash" do
+ File.basename("C:/").should == "/"
+ File.basename("C:/foo").should == "foo"
+ File.basename("C:/foo/bar").should == "bar"
+ File.basename("C:/foo/bar/").should == "bar"
+ File.basename("C:/foo/bar//").should == "bar"
+ end
+
+ it "returns basename with windows suffix" do
+ File.basename("c:\\bar.txt", ".txt").should == "bar"
+ File.basename("c:\\foo\\bar.txt", ".txt").should == "bar"
+ File.basename("c:\\bar.txt", ".exe").should == "bar.txt"
+ File.basename("c:\\bar.txt.exe", ".exe").should == "bar.txt"
+ File.basename("c:\\bar.txt.exe", ".txt").should == "bar.txt.exe"
+ File.basename("c:\\bar.txt", ".*").should == "bar"
+ File.basename("c:\\bar.txt.exe", ".*").should == "bar.txt"
+ end
+ end
+
+ with_feature :encoding do
+
+ it "returns the extension for a multibyte filename" do
+ File.basename('/path/Офис.m4a').should == "Офис.m4a"
+ end
+
+ it "returns the basename with the same encoding as the original" do
+ basename = File.basename('C:/Users/Scuby Pagrubý'.encode(Encoding::Windows_1250))
+ basename.should == 'Scuby Pagrubý'.encode(Encoding::Windows_1250)
+ basename.encoding.should == Encoding::Windows_1250
+ end
+
+ end
+
+end
diff --git a/spec/ruby/core/file/birthtime_spec.rb b/spec/ruby/core/file/birthtime_spec.rb
new file mode 100644
index 0000000000..9720ede834
--- /dev/null
+++ b/spec/ruby/core/file/birthtime_spec.rb
@@ -0,0 +1,56 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File.birthtime" do
+ before :each do
+ @file = __FILE__
+ 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)
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.birthtime(mock_to_path(@file))
+ end
+
+ it "raises an Errno::ENOENT exception if the file is not found" do
+ lambda { File.birthtime('bogus') }.should raise_error(Errno::ENOENT)
+ end
+ end
+
+ platform_is :linux, :openbsd do
+ it "raises an NotImplementedError" do
+ lambda { File.birthtime(@file) }.should raise_error(NotImplementedError)
+ end
+ end
+end
+
+describe "File#birthtime" do
+ before :each do
+ @file = File.open(__FILE__)
+ 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)
+ end
+ end
+
+ platform_is :linux, :openbsd do
+ it "raises an NotImplementedError" do
+ lambda { @file.birthtime }.should raise_error(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/ruby/core/file/blockdev_spec.rb b/spec/ruby/core/file/blockdev_spec.rb
new file mode 100644
index 0000000000..f5e03d1ade
--- /dev/null
+++ b/spec/ruby/core/file/blockdev_spec.rb
@@ -0,0 +1,6 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/blockdev', __FILE__)
+
+describe "File.blockdev?" do
+ it_behaves_like :file_blockdev, :blockdev?, File
+end
diff --git a/spec/ruby/core/file/chardev_spec.rb b/spec/ruby/core/file/chardev_spec.rb
new file mode 100644
index 0000000000..963823a206
--- /dev/null
+++ b/spec/ruby/core/file/chardev_spec.rb
@@ -0,0 +1,6 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/chardev', __FILE__)
+
+describe "File.chardev?" do
+ it_behaves_like :file_chardev, :chardev?, File
+end
diff --git a/spec/ruby/core/file/chmod_spec.rb b/spec/ruby/core/file/chmod_spec.rb
new file mode 100644
index 0000000000..8590f3008d
--- /dev/null
+++ b/spec/ruby/core/file/chmod_spec.rb
@@ -0,0 +1,239 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File#chmod" do
+ before :each do
+ @filename = tmp('i_exist.exe')
+ @file = File.open(@filename, 'w')
+ end
+
+ after :each do
+ @file.close
+ rm_r @filename
+ end
+
+ it "returns 0 if successful" do
+ @file.chmod(0755).should == 0
+ end
+
+ platform_is_not :freebsd, :netbsd, :openbsd do
+ it "always succeeds with any numeric values" do
+ vals = [-2**30, -2**16, -2**8, -2, -1,
+ -0.5, 0, 1, 2, 5.555575, 16, 32, 64, 2**8, 2**16, 2**30]
+ vals.each { |v|
+ lambda { @file.chmod(v) }.should_not raise_error
+ }
+ end
+ end
+
+ # -256, -2 and -1 raise Errno::E079 on FreeBSD
+ # -256, -2 and -1 raise Errno::EFTYPE on NetBSD
+ platform_is :freebsd, :netbsd do
+ it "always succeeds with any numeric values" do
+ vals = [-2**30, -2**16, #-2**8, -2, -1,
+ -0.5, 0, 1, 2, 5.555575, 16, 32, 64, 2**8, 2**16, 2**30]
+ vals.each { |v|
+ lambda { @file.chmod(v) }.should_not raise_error
+ }
+ end
+ end
+
+ # -256, -2 and -1 raise Errno::EINVAL on OpenBSD
+ platform_is :openbsd do
+ it "always succeeds with any numeric values" do
+ vals = [#-2**30, -2**16, -2**8, -2, -1,
+ -0.5, 0, 1, 2, 5.555575, 16, 32, 64, 2**8]#, 2**16, 2**30
+ vals.each { |v|
+ lambda { @file.chmod(v) }.should_not raise_error
+ }
+ end
+ end
+ it "invokes to_int on non-integer argument" do
+ mode = File.stat(@filename).mode
+ (obj = mock('mode')).should_receive(:to_int).and_return(mode)
+ @file.chmod(obj)
+ File.stat(@filename).mode.should == mode
+ end
+
+ platform_is :windows do
+ it "with '0444' makes file readable and executable but not writable" do
+ @file.chmod(0444)
+ File.readable?(@filename).should == true
+ File.writable?(@filename).should == false
+ File.executable?(@filename).should == true
+ end
+
+ it "with '0644' makes file readable and writable and also executable" do
+ @file.chmod(0644)
+ File.readable?(@filename).should == true
+ File.writable?(@filename).should == true
+ File.executable?(@filename).should == true
+ end
+ end
+
+ platform_is_not :windows do
+ it "with '0222' makes file writable but not readable or executable" do
+ @file.chmod(0222)
+ File.readable?(@filename).should == false
+ File.writable?(@filename).should == true
+ File.executable?(@filename).should == false
+ end
+
+ it "with '0444' makes file readable but not writable or executable" do
+ @file.chmod(0444)
+ File.readable?(@filename).should == true
+ File.writable?(@filename).should == false
+ File.executable?(@filename).should == false
+ end
+
+ it "with '0666' makes file readable and writable but not executable" do
+ @file.chmod(0666)
+ File.readable?(@filename).should == true
+ File.writable?(@filename).should == true
+ File.executable?(@filename).should == false
+ end
+
+ it "with '0111' makes file executable but not readable or writable" do
+ @file.chmod(0111)
+ File.readable?(@filename).should == false
+ File.writable?(@filename).should == false
+ File.executable?(@filename).should == true
+ end
+
+ it "modifies the permission bits of the files specified" do
+ @file.chmod(0755)
+ File.stat(@filename).mode.should == 33261
+ end
+ end
+end
+
+describe "File.chmod" do
+ before :each do
+ @file = tmp('i_exist.exe')
+ touch @file
+ @count = File.chmod(0755, @file)
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the number of files modified" do
+ @count.should == 1
+ end
+
+ platform_is_not :freebsd, :netbsd, :openbsd do
+ it "always succeeds with any numeric values" do
+ vals = [-2**30, -2**16, -2**8, -2, -1,
+ -0.5, 0, 1, 2, 5.555575, 16, 32, 64, 2**8, 2**16, 2**30]
+ vals.each { |v|
+ lambda { File.chmod(v, @file) }.should_not raise_error
+ }
+ end
+ end
+
+ # -256, -2 and -1 raise Errno::E079 on FreeBSD
+ # -256, -2 and -1 raise Errno::EFTYPE on NetBSD
+ platform_is :freebsd, :netbsd do
+ it "always succeeds with any numeric values" do
+ vals = [-2**30, -2**16, #-2**8, -2, -1,
+ -0.5, 0, 1, 2, 5.555575, 16, 32, 64, 2**8, 2**16, 2**30]
+ vals.each { |v|
+ lambda { File.chmod(v, @file) }.should_not raise_error
+ }
+ end
+ end
+
+ platform_is :openbsd do
+ it "succeeds with valid values" do
+ vals = [-0.5, 0, 1, 2, 5.555575, 16, 32, 64, 2**8]
+ vals.each { |v|
+ lambda { File.chmod(v, @file) }.should_not raise_error
+ }
+ end
+
+ it "fails with invalid values" do
+ vals = [-2**30, -2**16, -2**8, -2, -1, 2**16, 2**30]
+ vals.each { |v|
+ lambda { File.chmod(v, @file) }.should raise_error(Errno::EINVAL)
+ }
+ end
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.chmod(0, mock_to_path(@file))
+ end
+
+ it "throws a TypeError if the given path is not coercable into a string" do
+ lambda { File.chmod(0, []) }.should raise_error(TypeError)
+ end
+
+ it "raises an error for a non existent path" do
+ lambda {
+ File.chmod(0644, "#{@file}.not.existing")
+ }.should raise_error(Errno::ENOENT)
+ end
+
+ it "invokes to_int on non-integer argument" do
+ mode = File.stat(@file).mode
+ (obj = mock('mode')).should_receive(:to_int).and_return(mode)
+ File.chmod(obj, @file)
+ File.stat(@file).mode.should == mode
+ end
+
+ it "invokes to_str on non-string file names" do
+ mode = File.stat(@file).mode
+ (obj = mock('path')).should_receive(:to_str).and_return(@file)
+ File.chmod(mode, obj)
+ File.stat(@file).mode.should == mode
+ end
+
+ platform_is :windows do
+ it "with '0444' makes file readable and executable but not writable" do
+ File.chmod(0444, @file)
+ File.readable?(@file).should == true
+ File.writable?(@file).should == false
+ File.executable?(@file).should == true
+ end
+
+ it "with '0644' makes file readable and writable and also executable" do
+ File.chmod(0644, @file)
+ File.readable?(@file).should == true
+ File.writable?(@file).should == true
+ File.executable?(@file).should == true
+ end
+ end
+
+ platform_is_not :windows do
+ it "with '0222' makes file writable but not readable or executable" do
+ File.chmod(0222, @file)
+ File.readable?(@file).should == false
+ File.writable?(@file).should == true
+ File.executable?(@file).should == false
+ end
+
+ it "with '0444' makes file readable but not writable or executable" do
+ File.chmod(0444, @file)
+ File.readable?(@file).should == true
+ File.writable?(@file).should == false
+ File.executable?(@file).should == false
+ end
+
+ it "with '0666' makes file readable and writable but not executable" do
+ File.chmod(0666, @file)
+ File.readable?(@file).should == true
+ File.writable?(@file).should == true
+ File.executable?(@file).should == false
+ end
+
+ it "with '0111' makes file executable but not readable or writable" do
+ File.chmod(0111, @file)
+ File.readable?(@file).should == false
+ File.writable?(@file).should == false
+ File.executable?(@file).should == true
+ end
+
+ it "modifies the permission bits of the files specified" do
+ File.stat(@file).mode.should == 33261
+ end
+ end
+end
diff --git a/spec/ruby/core/file/chown_spec.rb b/spec/ruby/core/file/chown_spec.rb
new file mode 100644
index 0000000000..a0b46e9e39
--- /dev/null
+++ b/spec/ruby/core/file/chown_spec.rb
@@ -0,0 +1,152 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File.chown" do
+ before :each do
+ @fname = tmp('file_chown_test')
+ touch @fname
+ end
+
+ after :each do
+ rm_r @fname
+ end
+
+ as_superuser do
+ platform_is :windows do
+ it "does not modify the owner id of the file" do
+ File.chown 0, nil, @fname
+ File.stat(@fname).uid.should == 0
+ File.chown 501, nil, @fname
+ File.stat(@fname).uid.should == 0
+ end
+
+ it "does not modify the group id of the file" do
+ File.chown nil, 0, @fname
+ File.stat(@fname).gid.should == 0
+ File.chown nil, 501, @fname
+ File.stat(@fname).gid.should == 0
+ end
+ end
+
+ platform_is_not :windows do
+ it "changes the owner id of the file" do
+ File.chown 501, nil, @fname
+ File.stat(@fname).uid.should == 501
+ File.chown 0, nil, @fname
+ File.stat(@fname).uid.should == 0
+ end
+
+ it "changes the group id of the file" do
+ File.chown nil, 501, @fname
+ File.stat(@fname).gid.should == 501
+ File.chown nil, 0, @fname
+ File.stat(@fname).uid.should == 0
+ end
+
+ it "does not modify the owner id of the file if passed nil or -1" do
+ File.chown 501, nil, @fname
+ File.chown nil, nil, @fname
+ File.stat(@fname).uid.should == 501
+ File.chown nil, -1, @fname
+ File.stat(@fname).uid.should == 501
+ end
+
+ it "does not modify the group id of the file if passed nil or -1" do
+ File.chown nil, 501, @fname
+ File.chown nil, nil, @fname
+ File.stat(@fname).gid.should == 501
+ File.chown nil, -1, @fname
+ File.stat(@fname).gid.should == 501
+ end
+ end
+ end
+
+ it "returns the number of files processed" do
+ File.chown(nil, nil, @fname, @fname).should == 2
+ end
+
+ platform_is_not :windows do
+ it "raises an error for a non existent path" do
+ lambda {
+ File.chown(nil, nil, "#{@fname}_not_existing")
+ }.should raise_error(Errno::ENOENT)
+ end
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.chown(nil, nil, mock_to_path(@fname)).should == 1
+ end
+end
+
+describe "File#chown" do
+ 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
+
+ as_superuser do
+ platform_is :windows do
+ it "does not modify the owner id of the file" do
+ File.chown 0, nil, @fname
+ File.stat(@fname).uid.should == 0
+ File.chown 501, nil, @fname
+ File.stat(@fname).uid.should == 0
+ end
+
+ it "does not modify the group id of the file" do
+ File.chown nil, 0, @fname
+ File.stat(@fname).gid.should == 0
+ File.chown nil, 501, @fname
+ File.stat(@fname).gid.should == 0
+ end
+ end
+
+ platform_is_not :windows do
+ it "changes the owner id of the file" do
+ @file.chown 501, nil
+ @file.stat.uid.should == 501
+ @file.chown 0, nil
+ @file.stat.uid.should == 0
+ end
+
+ it "changes the group id of the file" do
+ @file.chown nil, 501
+ @file.stat.gid.should == 501
+ @file.chown nil, 0
+ @file.stat.uid.should == 0
+ end
+
+ it "does not modify the owner id of the file if passed nil or -1" do
+ @file.chown 501, nil
+ @file.chown nil, nil
+ @file.stat.uid.should == 501
+ @file.chown nil, -1
+ @file.stat.uid.should == 501
+ end
+
+ it "does not modify the group id of the file if passed nil or -1" do
+ @file.chown nil, 501
+ @file.chown nil, nil
+ @file.stat.gid.should == 501
+ @file.chown nil, -1
+ @file.stat.gid.should == 501
+ end
+ end
+ end
+
+ it "returns 0" do
+ @file.chown(nil, nil).should == 0
+ end
+end
+
+describe "File.chown" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "File#chown" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/constants/constants_spec.rb b/spec/ruby/core/file/constants/constants_spec.rb
new file mode 100644
index 0000000000..3b2f67cc30
--- /dev/null
+++ b/spec/ruby/core/file/constants/constants_spec.rb
@@ -0,0 +1,31 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+["APPEND", "CREAT", "EXCL", "FNM_CASEFOLD",
+ "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|
+ describe "File::Constants::#{const}" do
+ it "is defined" do
+ File::Constants.const_defined?(const).should be_true
+ end
+ end
+end
+
+platform_is :windows do
+ describe "File::Constants::BINARY" do
+ it "is defined" do
+ File::Constants.const_defined?(:BINARY).should be_true
+ end
+ end
+end
+
+platform_is_not :windows do
+ ["NOCTTY", "SYNC"].each do |const|
+ describe "File::Constants::#{const}" do
+ it "is defined" do
+ File::Constants.const_defined?(const).should be_true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/constants_spec.rb b/spec/ruby/core/file/constants_spec.rb
new file mode 100644
index 0000000000..0379149a18
--- /dev/null
+++ b/spec/ruby/core/file/constants_spec.rb
@@ -0,0 +1,141 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+# TODO: migrate these to constants/constants_spec.rb
+
+describe "File::Constants" do
+ it "matches mode constants" do
+ File::FNM_NOESCAPE.should_not == nil
+ File::FNM_PATHNAME.should_not == nil
+ File::FNM_DOTMATCH.should_not == nil
+ File::FNM_CASEFOLD.should_not == nil
+ File::FNM_SYSCASE.should_not == nil
+
+ platform_is :windows do #|| VMS
+ File::FNM_SYSCASE.should == 8
+ end
+ end
+
+ # Only these constants are not inherited from the IO class
+ it "the separator constant" do
+ File::SEPARATOR.should_not == nil
+ File::Separator.should_not == nil
+ File::PATH_SEPARATOR.should_not == nil
+ File::SEPARATOR.should == "/"
+
+ platform_is :windows do #|| VMS
+ File::ALT_SEPARATOR.should_not == nil
+ File::PATH_SEPARATOR.should == ";"
+ end
+
+ platform_is_not :windows do
+ File::ALT_SEPARATOR.should == nil
+ File::PATH_SEPARATOR.should == ":"
+ end
+ end
+
+ it "the open mode constants" do
+ File::APPEND.should_not == nil
+ File::CREAT.should_not == nil
+ File::EXCL.should_not == nil
+ File::NONBLOCK.should_not == nil
+ File::RDONLY.should_not == nil
+ File::RDWR.should_not == nil
+ File::TRUNC.should_not == nil
+ File::WRONLY.should_not == nil
+
+ platform_is_not :windows do # Not sure about VMS here
+ File::NOCTTY.should_not == nil
+ end
+ end
+
+ it "lock mode constants" do
+ File::LOCK_EX.should_not == nil
+ File::LOCK_NB.should_not == nil
+ File::LOCK_SH.should_not == nil
+ File::LOCK_UN.should_not == nil
+ end
+end
+
+describe "File::Constants" do
+ # These mode and permission bits are platform dependent
+ it "File::RDONLY" do
+ defined?(File::RDONLY).should == "constant"
+ end
+
+ it "File::WRONLY" do
+ defined?(File::WRONLY).should == "constant"
+ end
+
+ it "File::CREAT" do
+ defined?(File::CREAT).should == "constant"
+ end
+
+ it "File::RDWR" do
+ defined?(File::RDWR).should == "constant"
+ end
+
+ it "File::APPEND" do
+ defined?(File::APPEND).should == "constant"
+ end
+
+ it "File::TRUNC" do
+ defined?(File::TRUNC).should == "constant"
+ end
+
+ platform_is_not :windows do # Not sure about VMS here
+ it "File::NOCTTY" do
+ defined?(File::NOCTTY).should == "constant"
+ end
+ end
+
+ it "File::NONBLOCK" do
+ defined?(File::NONBLOCK).should == "constant"
+ end
+
+ it "File::LOCK_EX" do
+ defined?(File::LOCK_EX).should == "constant"
+ end
+
+ it "File::LOCK_NB" do
+ defined?(File::LOCK_NB).should == "constant"
+ end
+
+ it "File::LOCK_SH" do
+ defined?(File::LOCK_SH).should == "constant"
+ end
+
+ it "File::LOCK_UN" do
+ defined?(File::LOCK_UN).should == "constant"
+ end
+
+ it "File::SEPARATOR" do
+ defined?(File::SEPARATOR).should == "constant"
+ end
+ it "File::Separator" do
+ defined?(File::Separator).should == "constant"
+ end
+
+ it "File::PATH_SEPARATOR" do
+ defined?(File::PATH_SEPARATOR).should == "constant"
+ end
+
+ it "File::SEPARATOR" do
+ defined?(File::SEPARATOR).should == "constant"
+ File::SEPARATOR.should == "/"
+ end
+
+ platform_is :windows do #|| VMS
+ it "File::ALT_SEPARATOR" do
+ defined?(File::ALT_SEPARATOR).should == "constant"
+ File::PATH_SEPARATOR.should == ";"
+ end
+ end
+
+ platform_is_not :windows do
+ it "File::PATH_SEPARATOR" do
+ defined?(File::PATH_SEPARATOR).should == "constant"
+ File::PATH_SEPARATOR.should == ":"
+ end
+ end
+
+end
diff --git a/spec/ruby/core/file/ctime_spec.rb b/spec/ruby/core/file/ctime_spec.rb
new file mode 100644
index 0000000000..c39775fcdd
--- /dev/null
+++ b/spec/ruby/core/file/ctime_spec.rb
@@ -0,0 +1,51 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File.ctime" do
+ before :each do
+ @file = __FILE__
+ end
+
+ after :each do
+ @file = nil
+ end
+
+ it "returns the change time for the named file (the time at which directory information about the file was changed, not the file itself)." do
+ File.ctime(@file)
+ File.ctime(@file).should be_kind_of(Time)
+ end
+
+ platform_is :linux 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)
+ if supports_subseconds != 0
+ File.ctime(__FILE__).usec.should > 0
+ else
+ File.ctime(__FILE__).usec.should == 0
+ end
+ end
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.ctime(mock_to_path(@file))
+ end
+
+ it "raises an Errno::ENOENT exception if the file is not found" do
+ lambda { File.ctime('bogus') }.should raise_error(Errno::ENOENT)
+ end
+end
+
+describe "File#ctime" do
+ before :each do
+ @file = File.open(__FILE__)
+ end
+
+ after :each do
+ @file.close
+ @file = nil
+ end
+
+ it "returns the change time for the named file (the time at which directory information about the file was changed, not the file itself)." do
+ @file.ctime
+ @file.ctime.should be_kind_of(Time)
+ end
+end
diff --git a/spec/ruby/core/file/delete_spec.rb b/spec/ruby/core/file/delete_spec.rb
new file mode 100644
index 0000000000..2e903806d7
--- /dev/null
+++ b/spec/ruby/core/file/delete_spec.rb
@@ -0,0 +1,6 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../shared/unlink', __FILE__)
+
+describe "File.delete" do
+ it_behaves_like(:file_unlink, :delete)
+end
diff --git a/spec/ruby/core/file/directory_spec.rb b/spec/ruby/core/file/directory_spec.rb
new file mode 100644
index 0000000000..d8e8b25121
--- /dev/null
+++ b/spec/ruby/core/file/directory_spec.rb
@@ -0,0 +1,10 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/directory', __FILE__)
+
+describe "File.directory?" do
+ it_behaves_like :file_directory, :directory?, File
+end
+
+describe "File.directory?" do
+ it_behaves_like :file_directory_io, :directory?, File
+end
diff --git a/spec/ruby/core/file/dirname_spec.rb b/spec/ruby/core/file/dirname_spec.rb
new file mode 100644
index 0000000000..f56f0806df
--- /dev/null
+++ b/spec/ruby/core/file/dirname_spec.rb
@@ -0,0 +1,108 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File.dirname" do
+ it "returns all the components of filename except the last one" do
+ File.dirname('/home/jason').should == '/home'
+ File.dirname('/home/jason/poot.txt').should == '/home/jason'
+ File.dirname('poot.txt').should == '.'
+ File.dirname('/holy///schnikies//w00t.bin').should == '/holy///schnikies'
+ File.dirname('').should == '.'
+ File.dirname('/').should == '/'
+ File.dirname('/foo/foo').should == '/foo'
+ end
+
+ it "returns a String" do
+ File.dirname("foo").should be_kind_of(String)
+ end
+
+ it "does not modify its argument" do
+ x = "/usr/bin"
+ File.dirname(x)
+ x.should == "/usr/bin"
+ end
+
+ it "ignores a trailing /" do
+ File.dirname("/foo/bar/").should == "/foo"
+ end
+
+ it "returns the return all the components of filename except the last one (unix format)" do
+ File.dirname("foo").should =="."
+ File.dirname("/foo").should =="/"
+ File.dirname("/foo/bar").should =="/foo"
+ File.dirname("/foo/bar.txt").should =="/foo"
+ File.dirname("/foo/bar/baz").should =="/foo/bar"
+ 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"
+ end
+
+ platform_is_not :windows do
+ it "returns all the components of filename except the last one (edge cases on non-windows)" do
+ File.dirname('/////').should == '/'
+ File.dirname("//foo//").should == "/"
+ File.dirname('foo\bar').should == '.'
+ File.dirname('/foo\bar').should == '/'
+ File.dirname('foo/bar\baz').should == 'foo'
+ end
+ end
+
+ platform_is :windows do
+ it "returns all the components of filename except the last one (edge cases on windows)" do
+ File.dirname("//foo").should == "//foo"
+ File.dirname("//foo//").should == "//foo"
+ File.dirname('/////').should == '//'
+ end
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.dirname(mock_to_path("/")).should == "/"
+ end
+
+ it "raises a TypeError if not passed a String type" do
+ lambda { File.dirname(nil) }.should raise_error(TypeError)
+ lambda { File.dirname(0) }.should raise_error(TypeError)
+ lambda { File.dirname(true) }.should raise_error(TypeError)
+ lambda { File.dirname(false) }.should raise_error(TypeError)
+ end
+
+ # Windows specific tests
+ platform_is :windows do
+ it "returns the return all the components of filename except the last one (Windows format)" do
+ File.dirname("C:\\foo\\bar\\baz.txt").should =="C:\\foo\\bar"
+ File.dirname("C:\\foo\\bar").should =="C:\\foo"
+ File.dirname("C:\\foo\\bar\\").should == "C:\\foo"
+ File.dirname("C:\\foo").should == "C:\\"
+ File.dirname("C:\\").should =="C:\\"
+ end
+
+ it "returns the return all the components of filename except the last one (windows unc)" do
+ File.dirname("\\\\foo\\bar\\baz.txt").should == "\\\\foo\\bar"
+ File.dirname("\\\\foo\\bar\\baz").should == "\\\\foo\\bar"
+ File.dirname("\\\\foo").should =="\\\\foo"
+ File.dirname("\\\\foo\\bar").should =="\\\\foo\\bar"
+ File.dirname("\\\\\\foo\\bar").should =="\\\\foo\\bar"
+ File.dirname("\\\\\\foo").should =="\\\\foo"
+ end
+
+ it "returns the return all the components of filename except the last one (forward_slash)" do
+ File.dirname("C:/").should == "C:/"
+ File.dirname("C:/foo").should == "C:/"
+ File.dirname("C:/foo/bar").should == "C:/foo"
+ File.dirname("C:/foo/bar/").should == "C:/foo"
+ File.dirname("C:/foo/bar//").should == "C:/foo"
+ end
+ end
+end
diff --git a/spec/ruby/core/file/executable_real_spec.rb b/spec/ruby/core/file/executable_real_spec.rb
new file mode 100644
index 0000000000..24d6824169
--- /dev/null
+++ b/spec/ruby/core/file/executable_real_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/executable_real', __FILE__)
+
+describe "File.executable_real?" do
+ it_behaves_like :file_executable_real, :executable_real?, File
+ it_behaves_like :file_executable_real_missing, :executable_real?, File
+end
diff --git a/spec/ruby/core/file/executable_spec.rb b/spec/ruby/core/file/executable_spec.rb
new file mode 100644
index 0000000000..82d6a81a0d
--- /dev/null
+++ b/spec/ruby/core/file/executable_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/executable', __FILE__)
+
+describe "File.executable?" do
+ it_behaves_like :file_executable, :executable?, File
+ it_behaves_like :file_executable_missing, :executable?, File
+end
diff --git a/spec/ruby/core/file/exist_spec.rb b/spec/ruby/core/file/exist_spec.rb
new file mode 100644
index 0000000000..29a410c125
--- /dev/null
+++ b/spec/ruby/core/file/exist_spec.rb
@@ -0,0 +1,6 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/exist', __FILE__)
+
+describe "File.exist?" do
+ it_behaves_like(:file_exist, :exist?, File)
+end
diff --git a/spec/ruby/core/file/exists_spec.rb b/spec/ruby/core/file/exists_spec.rb
new file mode 100644
index 0000000000..70ebd12d86
--- /dev/null
+++ b/spec/ruby/core/file/exists_spec.rb
@@ -0,0 +1,6 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/exist', __FILE__)
+
+describe "File.exists?" do
+ it_behaves_like(:file_exist, :exists?, File)
+end
diff --git a/spec/ruby/core/file/expand_path_spec.rb b/spec/ruby/core/file/expand_path_spec.rb
new file mode 100644
index 0000000000..c57f323c4c
--- /dev/null
+++ b/spec/ruby/core/file/expand_path_spec.rb
@@ -0,0 +1,242 @@
+# -*- encoding: utf-8 -*-
+
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../fixtures/common', __FILE__)
+
+describe "File.expand_path" do
+ before :each do
+ platform_is :windows do
+ @base = `cd`.chomp.tr '\\', '/'
+ @tmpdir = "c:/tmp"
+ @rootdir = "c:/"
+ end
+
+ platform_is_not :windows do
+ @base = Dir.pwd
+ @tmpdir = "/tmp"
+ @rootdir = "/"
+ end
+ end
+
+ with_feature :encoding do
+ before :each do
+ @external = Encoding.default_external
+ end
+
+ after :each do
+ Encoding.default_external = @external
+ end
+ end
+
+ it "converts a pathname to an absolute pathname" do
+ File.expand_path('').should == @base
+ File.expand_path('a').should == File.join(@base, 'a')
+ File.expand_path('a', nil).should == File.join(@base, 'a')
+ end
+
+ it "converts a pathname to an absolute pathname, Ruby-Talk:18512" do
+ # See Ruby-Talk:18512
+ File.expand_path('.a').should == File.join(@base, '.a')
+ File.expand_path('..a').should == File.join(@base, '..a')
+ File.expand_path('a../b').should == File.join(@base, 'a../b')
+ end
+
+ platform_is_not :windows do
+ it "keeps trailing dots on absolute pathname" do
+ # See Ruby-Talk:18512
+ File.expand_path('a.').should == File.join(@base, 'a.')
+ File.expand_path('a..').should == File.join(@base, 'a..')
+ end
+ end
+
+ it "converts a pathname to an absolute pathname, using a complete path" do
+ File.expand_path("", "#{@tmpdir}").should == "#{@tmpdir}"
+ File.expand_path("a", "#{@tmpdir}").should =="#{@tmpdir}/a"
+ File.expand_path("../a", "#{@tmpdir}/xxx").should == "#{@tmpdir}/a"
+ File.expand_path(".", "#{@rootdir}").should == "#{@rootdir}"
+ end
+
+ # FIXME: do not use conditionals like this around #it blocks
+ unless not home = ENV['HOME']
+ platform_is_not :windows do
+ it "converts a pathname to an absolute pathname, using ~ (home) as base" do
+ File.expand_path('~').should == home
+ File.expand_path('~', '/tmp/gumby/ddd').should == home
+ File.expand_path('~/a', '/tmp/gumby/ddd').should == File.join(home, 'a')
+ end
+
+ it "does not return a frozen string" do
+ File.expand_path('~').frozen?.should == false
+ File.expand_path('~', '/tmp/gumby/ddd').frozen?.should == false
+ File.expand_path('~/a', '/tmp/gumby/ddd').frozen?.should == false
+ end
+ end
+ platform_is :windows do
+ it "converts a pathname to an absolute pathname, using ~ (home) as base" do
+ File.expand_path('~').should == home.tr("\\", '/')
+ File.expand_path('~', '/tmp/gumby/ddd').should == home.tr("\\", '/')
+ File.expand_path('~/a', '/tmp/gumby/ddd').should == File.join(home.tr("\\", '/'), 'a')
+ end
+
+ it "does not return a frozen string" do
+ File.expand_path('~').frozen?.should == false
+ File.expand_path('~', '/tmp/gumby/ddd').frozen?.should == false
+ File.expand_path('~/a', '/tmp/gumby/ddd').frozen?.should == false
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ before do
+ @home = ENV['HOME'].chomp('/')
+ end
+
+ # FIXME: these are insane!
+ it "expand path with" do
+ File.expand_path("../../bin", "/tmp/x").should == "/bin"
+ File.expand_path("../../bin", "/tmp").should == "/bin"
+ File.expand_path("../../bin", "/").should == "/bin"
+ File.expand_path("../bin", "tmp/x").should == File.join(@base, 'tmp', 'bin')
+ File.expand_path("../bin", "x/../tmp").should == File.join(@base, 'bin')
+ end
+
+ it "expand_path for commoms unix path give a full path" do
+ File.expand_path('/tmp/').should =='/tmp'
+ File.expand_path('/tmp/../../../tmp').should == '/tmp'
+ File.expand_path('').should == Dir.pwd
+ File.expand_path('./////').should == Dir.pwd
+ File.expand_path('.').should == Dir.pwd
+ File.expand_path(Dir.pwd).should == Dir.pwd
+ File.expand_path('~/').should == @home
+ File.expand_path('~/..badfilename').should == "#{@home}/..badfilename"
+ File.expand_path('..').should == Dir.pwd.split('/')[0...-1].join("/")
+ File.expand_path('~/a','~/b').should == "#{@home}/a"
+ end
+
+ it "does not replace multiple '/' at the beginning of the path" do
+ File.expand_path('////some/path').should == "////some/path"
+ end
+
+ it "replaces multiple '/' with a single '/'" do
+ File.expand_path('/some////path').should == "/some/path"
+ end
+
+ it "raises an ArgumentError if the path is not valid" do
+ lambda { File.expand_path("~a_not_existing_user") }.should raise_error(ArgumentError)
+ end
+
+ it "expands ~ENV['USER'] to the user's home directory" do
+ File.expand_path("~#{ENV['USER']}").should == @home
+ File.expand_path("~#{ENV['USER']}/a").should == "#{@home}/a"
+ end
+
+ it "does not expand ~ENV['USER'] when it's not at the start" do
+ File.expand_path("/~#{ENV['USER']}/a").should == "/~#{ENV['USER']}/a"
+ end
+
+ it "expands ../foo with ~/dir as base dir to /path/to/user/home/foo" do
+ File.expand_path('../foo', '~/dir').should == "#{@home}/foo"
+ end
+ end
+
+ it "accepts objects that have a #to_path method" do
+ File.expand_path(mock_to_path("a"), mock_to_path("#{@tmpdir}"))
+ end
+
+ it "raises a TypeError if not passed a String type" do
+ lambda { File.expand_path(1) }.should raise_error(TypeError)
+ lambda { File.expand_path(nil) }.should raise_error(TypeError)
+ lambda { File.expand_path(true) }.should raise_error(TypeError)
+ end
+
+ platform_is_not :windows do
+ it "expands /./dir to /dir" do
+ File.expand_path("/./dir").should == "/dir"
+ end
+ end
+
+ platform_is :windows do
+ it "expands C:/./dir to C:/dir" do
+ File.expand_path("C:/./dir").should == "C:/dir"
+ end
+ end
+
+ with_feature :encoding 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
+ File.expand_path(path).encoding.should equal(Encoding::CP1251)
+
+ weird_path = [222, 173, 190, 175].pack('C*')
+ File.expand_path(weird_path).encoding.should equal(Encoding::ASCII_8BIT)
+ end
+
+ platform_is_not :windows do
+ it "expands a path when the default external encoding is ASCII-8BIT" do
+ Encoding.default_external = Encoding::ASCII_8BIT
+ path_8bit = [222, 173, 190, 175].pack('C*')
+ File.expand_path( path_8bit, @rootdir).should == "#{@rootdir}" + path_8bit
+ end
+ end
+
+ it "expands a path with multi-byte characters" do
+ File.expand_path("Ångström").should == "#{@base}/Ångström"
+ end
+
+ platform_is_not :windows do
+ it "raises an Encoding::CompatibilityError if the external encoding is not compatible" do
+ Encoding.default_external = Encoding::UTF_16BE
+ lambda { File.expand_path("./a") }.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+ end
+
+ it "does not modify the string argument" do
+ str = "./a/b/../c"
+ File.expand_path(str, @base).should == "#{@base}/a/c"
+ str.should == "./a/b/../c"
+ end
+
+ it "does not modify a HOME string argument" do
+ str = "~/a"
+ File.expand_path(str).should == "#{Dir.home}/a"
+ str.should == "~/a"
+ end
+
+ it "returns a String when passed a String subclass" do
+ str = FileSpecs::SubString.new "./a/b/../c"
+ path = File.expand_path(str, @base)
+ path.should == "#{@base}/a/c"
+ path.should be_an_instance_of(String)
+ end
+end
+
+platform_is_not :windows do
+ describe "File.expand_path when HOME is not set" do
+ before :each do
+ @home = ENV["HOME"]
+ end
+
+ after :each do
+ ENV["HOME"] = @home
+ end
+
+ ruby_version_is ''...'2.4' do
+ it "raises an ArgumentError when passed '~' if HOME is nil" do
+ ENV.delete "HOME"
+ lambda { File.expand_path("~") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed '~/' if HOME is nil" do
+ ENV.delete "HOME"
+ lambda { File.expand_path("~/") }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "raises an ArgumentError when passed '~' if HOME == ''" do
+ ENV["HOME"] = ""
+ lambda { File.expand_path("~") }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/core/file/extname_spec.rb b/spec/ruby/core/file/extname_spec.rb
new file mode 100644
index 0000000000..fedd4fc89f
--- /dev/null
+++ b/spec/ruby/core/file/extname_spec.rb
@@ -0,0 +1,54 @@
+# -*- encoding: utf-8 -*-
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File.extname" do
+ it "returns the extension (the portion of file name in path after the period)." do
+ File.extname("foo.rb").should == ".rb"
+ File.extname("/foo/bar.rb").should == ".rb"
+ File.extname("/foo.rb/bar.c").should == ".c"
+ File.extname("bar").should == ""
+ File.extname(".bashrc").should == ""
+ File.extname("/foo.bar/baz").should == ""
+ File.extname(".app.conf").should == ".conf"
+ end
+
+ it "returns the extension (the portion of file name in path after the period).(edge cases)" do
+ File.extname("").should == ""
+ File.extname(".").should == ""
+ File.extname("/").should == ""
+ File.extname("/.").should == ""
+ File.extname("..").should == ""
+ File.extname("...").should == ""
+ File.extname("....").should == ""
+ File.extname(".foo.").should == ""
+ File.extname("foo.").should == ""
+ end
+
+ it "returns only the last extension of a file with several dots" do
+ File.extname("a.b.c.d.e").should == ".e"
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.extname(mock_to_path("a.b.c.d.e")).should == ".e"
+ end
+
+ it "raises a TypeError if not passed a String type" do
+ lambda { File.extname(nil) }.should raise_error(TypeError)
+ lambda { File.extname(0) }.should raise_error(TypeError)
+ lambda { File.extname(true) }.should raise_error(TypeError)
+ lambda { File.extname(false) }.should raise_error(TypeError)
+ end
+
+ it "raises an ArgumentError if not passed one argument" do
+ lambda { File.extname }.should raise_error(ArgumentError)
+ lambda { File.extname("foo.bar", "foo.baz") }.should raise_error(ArgumentError)
+ end
+
+ with_feature :encoding do
+
+ it "returns the extension for a multibyte filename" do
+ File.extname('Имя.m4a').should == ".m4a"
+ end
+
+ end
+end
diff --git a/spec/ruby/core/file/file_spec.rb b/spec/ruby/core/file/file_spec.rb
new file mode 100644
index 0000000000..99eaacd086
--- /dev/null
+++ b/spec/ruby/core/file/file_spec.rb
@@ -0,0 +1,16 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/file', __FILE__)
+
+describe "File" do
+ it "includes Enumerable" do
+ File.include?(Enumerable).should == true
+ end
+
+ it "includes File::Constants" do
+ File.include?(File::Constants).should == true
+ end
+end
+
+describe "File.file?" do
+ it_behaves_like :file_file, :file?, File
+end
diff --git a/spec/ruby/core/file/fixtures/common.rb b/spec/ruby/core/file/fixtures/common.rb
new file mode 100644
index 0000000000..50721388ad
--- /dev/null
+++ b/spec/ruby/core/file/fixtures/common.rb
@@ -0,0 +1,22 @@
+module FileSpecs
+ class SubString < String; end
+
+ def self.make_closer(obj, exc=nil)
+ ScratchPad << :file_opened
+
+ class << obj
+ attr_accessor :close_exception
+
+ alias_method :original_close, :close
+
+ def close
+ original_close
+ ScratchPad << :file_closed
+
+ raise @close_exception if @close_exception
+ end
+ end
+
+ obj.close_exception = exc
+ end
+end
diff --git a/spec/ruby/core/file/fixtures/do_not_remove b/spec/ruby/core/file/fixtures/do_not_remove
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/spec/ruby/core/file/fixtures/do_not_remove
@@ -0,0 +1 @@
+
diff --git a/spec/ruby/core/file/fixtures/file_types.rb b/spec/ruby/core/file/fixtures/file_types.rb
new file mode 100644
index 0000000000..36a5ff1a95
--- /dev/null
+++ b/spec/ruby/core/file/fixtures/file_types.rb
@@ -0,0 +1,64 @@
+module FileSpecs
+ def self.configure_types
+ return if @configured
+
+ @file = tmp("test.txt")
+ @dir = Dir.pwd
+ @fifo = tmp("test_fifo")
+
+ platform_is_not :windows do
+ @block = `find /dev /devices -type b 2> /dev/null`.split("\n").first
+ @char = `find /dev /devices -type c 2> /dev/null`.split("\n").last
+
+ %w[/dev /usr/bin /usr/local/bin].each do |dir|
+ links = `find #{dir} -type l 2> /dev/null`.split("\n")
+ next if links.empty?
+ @link = links.first
+ break
+ end
+ end
+
+ @configured = true
+ end
+
+ def self.normal_file
+ touch(@file)
+ yield @file
+ ensure
+ rm_r @file
+ end
+
+ def self.directory
+ yield @dir
+ end
+
+ # TODO: need a platform-independent helper here
+ def self.fifo
+ system "mkfifo #{@fifo} 2> /dev/null"
+ yield @fifo
+ ensure
+ rm_r @fifo
+ end
+
+ def self.block_device
+ yield @block
+ end
+
+ def self.character_device
+ yield @char
+ end
+
+ def self.symlink
+ yield @link
+ end
+
+ def self.socket
+ require 'socket'
+ name = tmp("ftype_socket.socket")
+ rm_r name
+ socket = UNIXServer.new name
+ yield name
+ socket.close
+ rm_r name
+ end
+end
diff --git a/spec/ruby/core/file/flock_spec.rb b/spec/ruby/core/file/flock_spec.rb
new file mode 100644
index 0000000000..e14d4252d4
--- /dev/null
+++ b/spec/ruby/core/file/flock_spec.rb
@@ -0,0 +1,106 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File#flock" do
+ before :each do
+ ScratchPad.record []
+
+ @name = tmp("flock_test")
+ touch(@name)
+
+ @file = File.open @name, "w+"
+ end
+
+ after :each do
+ @file.flock File::LOCK_UN
+ @file.close
+
+ rm_r @name
+ end
+
+ it "exclusively locks a file" do
+ @file.flock(File::LOCK_EX).should == 0
+ @file.flock(File::LOCK_UN).should == 0
+ end
+
+ it "non-exclusively locks a file" do
+ @file.flock(File::LOCK_SH).should == 0
+ @file.flock(File::LOCK_UN).should == 0
+ end
+
+ 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"
+ File.open('#{@name}', "w") do |f2|
+ print f2.flock(File::LOCK_EX | File::LOCK_NB).to_s
+ end
+ END_OF_CODE
+ end
+
+ it "blocks if trying to lock an exclusively locked file" do
+ @file.flock File::LOCK_EX
+
+ out = ruby_exe(<<-END_OF_CODE, escape: true)
+ running = false
+
+ t = Thread.new do
+ File.open('#{@name}', "w") do |f2|
+ puts "before"
+ running = true
+ f2.flock(File::LOCK_EX)
+ puts "after"
+ end
+ end
+
+ Thread.pass until running
+ Thread.pass while t.status and t.status != "sleep"
+ sleep 0.1
+
+ t.kill
+ t.join
+ END_OF_CODE
+
+ out.should == "before\n"
+ end
+
+ it "returns 0 if trying to lock a non-exclusively locked file" do
+ @file.flock File::LOCK_SH
+
+ File.open(@name, "r") do |f2|
+ f2.flock(File::LOCK_SH | File::LOCK_NB).should == 0
+ f2.flock(File::LOCK_UN).should == 0
+ 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
+ lambda 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
+ lambda do
+ @write_file.flock File::LOCK_SH
+ end.should raise_error(Errno::EBADF)
+ end
+ end
+end
diff --git a/spec/ruby/core/file/fnmatch_spec.rb b/spec/ruby/core/file/fnmatch_spec.rb
new file mode 100644
index 0000000000..8a4caacfb8
--- /dev/null
+++ b/spec/ruby/core/file/fnmatch_spec.rb
@@ -0,0 +1,10 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../shared/fnmatch', __FILE__)
+
+describe "File.fnmatch" do
+ it_behaves_like(:file_fnmatch, :fnmatch)
+end
+
+describe "File.fnmatch?" do
+ it_behaves_like(:file_fnmatch, :fnmatch?)
+end
diff --git a/spec/ruby/core/file/ftype_spec.rb b/spec/ruby/core/file/ftype_spec.rb
new file mode 100644
index 0000000000..7c010b3e9c
--- /dev/null
+++ b/spec/ruby/core/file/ftype_spec.rb
@@ -0,0 +1,73 @@
+require "#{File.dirname(__FILE__)}/../../spec_helper"
+require "#{File.dirname(__FILE__)}/fixtures/file_types"
+
+describe "File.ftype" do
+ before :all do
+ FileSpecs.configure_types
+ end
+
+ it "raises ArgumentError if not given exactly one filename" do
+ lambda { File.ftype }.should raise_error(ArgumentError)
+ lambda { File.ftype('blah', 'bleh') }.should raise_error(ArgumentError)
+ end
+
+ it "raises Errno::ENOENT if the file is not valid" do
+ l = lambda { File.ftype("/#{$$}#{Time.now.to_f}") }
+ l.should raise_error(Errno::ENOENT)
+ end
+
+ it "returns a String" do
+ FileSpecs.normal_file do |file|
+ File.ftype(file).should be_kind_of(String)
+ end
+ end
+
+ it "returns 'file' when the file is a file" do
+ FileSpecs.normal_file do |file|
+ File.ftype(file).should == 'file'
+ end
+ end
+
+ it "returns 'directory' when the file is a dir" do
+ FileSpecs.directory do |dir|
+ File.ftype(dir).should == 'directory'
+ end
+ end
+
+ # Both FreeBSD and Windows does not have block devices
+ platform_is_not :freebsd, :windows do
+ with_block_device do
+ it "returns 'blockSpecial' when the file is a block" do
+ FileSpecs.block_device do |block|
+ File.ftype(block).should == 'blockSpecial'
+ end
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ it "returns 'characterSpecial' when the file is a char" do
+ FileSpecs.character_device do |char|
+ File.ftype(char).should == 'characterSpecial'
+ end
+ end
+
+ it "returns 'link' when the file is a link" do
+ FileSpecs.symlink do |link|
+ File.ftype(link).should == 'link'
+ end
+ end
+
+ it "returns fifo when the file is a fifo" do
+ FileSpecs.fifo do |fifo|
+ File.ftype(fifo).should == 'fifo'
+ end
+ end
+
+ it "returns 'socket' when the file is a socket" do
+ FileSpecs.socket do |socket|
+ File.ftype(socket).should == 'socket'
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/grpowned_spec.rb b/spec/ruby/core/file/grpowned_spec.rb
new file mode 100644
index 0000000000..0b5514d7ca
--- /dev/null
+++ b/spec/ruby/core/file/grpowned_spec.rb
@@ -0,0 +1,10 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/grpowned', __FILE__)
+
+describe "File.grpowned?" do
+ it_behaves_like :file_grpowned, :grpowned?, File
+
+ it "returns false if file the does not exist" do
+ File.grpowned?("i_am_a_bogus_file").should == false
+ end
+end
diff --git a/spec/ruby/core/file/identical_spec.rb b/spec/ruby/core/file/identical_spec.rb
new file mode 100644
index 0000000000..303337b62d
--- /dev/null
+++ b/spec/ruby/core/file/identical_spec.rb
@@ -0,0 +1,6 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/identical', __FILE__)
+
+describe "File.identical?" do
+ it_behaves_like :file_identical, :identical?, File
+end
diff --git a/spec/ruby/core/file/initialize_spec.rb b/spec/ruby/core/file/initialize_spec.rb
new file mode 100644
index 0000000000..269e13b3ca
--- /dev/null
+++ b/spec/ruby/core/file/initialize_spec.rb
@@ -0,0 +1,23 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File#initialize" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "File#initialize" do
+ after :each do
+ @io.close if @io
+ end
+
+ it "accepts encoding options in mode parameter" do
+ @io = File.new(__FILE__, 'r:UTF-8:iso-8859-1')
+ @io.external_encoding.to_s.should == 'UTF-8'
+ @io.internal_encoding.to_s.should == 'ISO-8859-1'
+ end
+
+ it "accepts encoding options as a hash parameter" do
+ @io = File.new(__FILE__, 'r', encoding: 'UTF-8:iso-8859-1')
+ @io.external_encoding.to_s.should == 'UTF-8'
+ @io.internal_encoding.to_s.should == 'ISO-8859-1'
+ end
+end
diff --git a/spec/ruby/core/file/inspect_spec.rb b/spec/ruby/core/file/inspect_spec.rb
new file mode 100644
index 0000000000..a059fa2c48
--- /dev/null
+++ b/spec/ruby/core/file/inspect_spec.rb
@@ -0,0 +1,17 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File#inspect" do
+ before :each do
+ @name = tmp("file_inspect.txt")
+ @file = File.open @name, "w"
+ end
+
+ after :each do
+ @file.close unless @file.closed?
+ rm_r @name
+ end
+
+ it "returns a String" do
+ @file.inspect.should be_an_instance_of(String)
+ end
+end
diff --git a/spec/ruby/core/file/join_spec.rb b/spec/ruby/core/file/join_spec.rb
new file mode 100644
index 0000000000..7c5955d03b
--- /dev/null
+++ b/spec/ruby/core/file/join_spec.rb
@@ -0,0 +1,139 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File.join" do
+ # see [ruby-core:46804] for the 4 following rules
+ it "changes only boundaries separators" do
+ File.join("file/\\/usr/", "/bin").should == "file/\\/usr/bin"
+ File.join("file://usr", "bin").should == "file://usr/bin"
+ end
+
+ it "respects the given separator if only one part has a boundary separator" do
+ File.join("usr/", "bin").should == "usr/bin"
+ File.join("usr", "/bin").should == "usr/bin"
+ File.join("usr//", "bin").should == "usr//bin"
+ File.join("usr", "//bin").should == "usr//bin"
+ end
+
+ it "joins parts using File::SEPARATOR if there are no boundary separators" do
+ File.join("usr", "bin").should == "usr/bin"
+ end
+
+ it "prefers the separator of the right part if both parts have separators" do
+ File.join("usr/", "//bin").should == "usr//bin"
+ File.join("usr//", "/bin").should == "usr/bin"
+ end
+
+ platform_is :windows do
+ it "respects given separator if only one part has a boundary separator" do
+ File.join("C:\\", 'windows').should == "C:\\windows"
+ File.join("C:", "\\windows").should == "C:\\windows"
+ File.join("\\\\", "usr").should == "\\\\usr"
+ end
+
+ it "prefers the separator of the right part if both parts have separators" do
+ File.join("C:/", "\\windows").should == "C:\\windows"
+ File.join("C:\\", "/windows").should == "C:/windows"
+ end
+ end
+
+ platform_is_not :windows do
+ it "does not treat \\ as a separator on non-Windows" do
+ File.join("usr\\", 'bin').should == "usr\\/bin"
+ File.join("usr", "\\bin").should == "usr/\\bin"
+ File.join("usr/", "\\bin").should == "usr/\\bin"
+ File.join("usr\\", "/bin").should == "usr\\/bin"
+ end
+ end
+
+ it "returns an empty string when given no arguments" do
+ File.join.should == ""
+ end
+
+ it "returns a duplicate string when given a single argument" do
+ str = "usr"
+ File.join(str).should == str
+ File.join(str).should_not equal(str)
+ end
+
+ it "supports any number of arguments" do
+ File.join("a", "b", "c", "d").should == "a/b/c/d"
+ end
+
+ it "flattens nested arrays" do
+ File.join(["a", "b", "c"]).should == "a/b/c"
+ File.join(["a", ["b", ["c"]]]).should == "a/b/c"
+ end
+
+ it "inserts the separator in between empty strings and arrays" do
+ File.join("").should == ""
+ File.join("", "").should == "/"
+ File.join(["", ""]).should == "/"
+ File.join("a", "").should == "a/"
+ File.join("", "a").should == "/a"
+
+ File.join([]).should == ""
+ File.join([], []).should == "/"
+ File.join([[], []]).should == "/"
+ File.join("a", []).should == "a/"
+ File.join([], "a").should == "/a"
+ end
+
+ it "handles leading parts edge cases" do
+ File.join("/bin") .should == "/bin"
+ File.join("", "bin") .should == "/bin"
+ File.join("/", "bin") .should == "/bin"
+ File.join("/", "/bin").should == "/bin"
+ end
+
+ it "handles trailing parts edge cases" do
+ File.join("bin", "") .should == "bin/"
+ File.join("bin/") .should == "bin/"
+ File.join("bin/", "") .should == "bin/"
+ File.join("bin", "/") .should == "bin/"
+ File.join("bin/", "/").should == "bin/"
+ end
+
+ it "handles middle parts edge cases" do
+ File.join("usr", "", "bin") .should == "usr/bin"
+ File.join("usr/", "", "bin") .should == "usr/bin"
+ File.join("usr", "", "/bin").should == "usr/bin"
+ File.join("usr/", "", "/bin").should == "usr/bin"
+ end
+
+ # TODO: See MRI svn r23306. Add patchlevel when there is a release.
+ it "raises an ArgumentError if passed a recursive array" do
+ a = ["a"]
+ a << a
+ lambda { File.join a }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError exception when args are nil" do
+ lambda { File.join nil }.should raise_error(TypeError)
+ end
+
+ it "calls #to_str" do
+ lambda { File.join(mock('x')) }.should raise_error(TypeError)
+
+ bin = mock("bin")
+ bin.should_receive(:to_str).exactly(:twice).and_return("bin")
+ File.join(bin).should == "bin"
+ File.join("usr", bin).should == "usr/bin"
+ end
+
+ it "doesn't mutate the object when calling #to_str" do
+ usr = mock("usr")
+ str = "usr"
+ usr.should_receive(:to_str).and_return(str)
+ File.join(usr, "bin").should == "usr/bin"
+ str.should == "usr"
+ end
+
+ it "calls #to_path" do
+ lambda { File.join(mock('x')) }.should raise_error(TypeError)
+
+ bin = mock("bin")
+ bin.should_receive(:to_path).exactly(:twice).and_return("bin")
+ File.join(bin).should == "bin"
+ File.join("usr", bin).should == "usr/bin"
+ end
+end
diff --git a/spec/ruby/core/file/lchmod_spec.rb b/spec/ruby/core/file/lchmod_spec.rb
new file mode 100644
index 0000000000..2ce265841e
--- /dev/null
+++ b/spec/ruby/core/file/lchmod_spec.rb
@@ -0,0 +1,42 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File.lchmod" do
+ platform_is_not :linux, :windows, :openbsd, :solaris, :aix do
+ before :each do
+ @fname = tmp('file_chmod_test')
+ @lname = @fname + '.lnk'
+
+ touch(@fname) { |f| f.write "rubinius" }
+
+ rm_r @lname
+ File.symlink @fname, @lname
+ end
+
+ after :each do
+ rm_r @lname, @fname
+ end
+
+ it "changes the file mode of the link and not of the file" do
+ File.chmod(0222, @lname).should == 1
+ File.lchmod(0755, @lname).should == 1
+
+ File.lstat(@lname).executable?.should == true
+ File.lstat(@lname).readable?.should == true
+ File.lstat(@lname).writable?.should == true
+
+ File.stat(@lname).executable?.should == false
+ File.stat(@lname).readable?.should == false
+ File.stat(@lname).writable?.should == true
+ end
+ end
+
+ platform_is :linux, :openbsd, :aix do
+ it "returns false from #respond_to?" do
+ File.respond_to?(:lchmod).should be_false
+ end
+
+ it "raises a NotImplementedError when called" do
+ lambda { File.lchmod 0 }.should raise_error(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/ruby/core/file/lchown_spec.rb b/spec/ruby/core/file/lchown_spec.rb
new file mode 100644
index 0000000000..814b0bf534
--- /dev/null
+++ b/spec/ruby/core/file/lchown_spec.rb
@@ -0,0 +1,63 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+as_superuser do
+ describe "File.lchown" do
+ platform_is_not :windows do
+ before :each do
+ @fname = tmp('file_chown_test')
+ @lname = @fname + '.lnk'
+
+ touch(@fname) { |f| f.chown 501, 501 }
+
+ rm_r @lname
+ File.symlink @fname, @lname
+ end
+
+ after :each do
+ rm_r @lname, @fname
+ end
+
+ it "changes the owner id of the file" do
+ File.lchown 502, nil, @lname
+ File.stat(@fname).uid.should == 501
+ File.lstat(@lname).uid.should == 502
+ File.lchown 0, nil, @lname
+ File.stat(@fname).uid.should == 501
+ File.lstat(@lname).uid.should == 0
+ end
+
+ it "changes the group id of the file" do
+ File.lchown nil, 502, @lname
+ File.stat(@fname).gid.should == 501
+ File.lstat(@lname).gid.should == 502
+ File.lchown nil, 0, @lname
+ File.stat(@fname).uid.should == 501
+ File.lstat(@lname).uid.should == 0
+ end
+
+ it "does not modify the owner id of the file if passed nil or -1" do
+ File.lchown 502, nil, @lname
+ File.lchown nil, nil, @lname
+ File.lstat(@lname).uid.should == 502
+ File.lchown nil, -1, @lname
+ File.lstat(@lname).uid.should == 502
+ end
+
+ it "does not modify the group id of the file if passed nil or -1" do
+ File.lchown nil, 502, @lname
+ File.lchown nil, nil, @lname
+ File.lstat(@lname).gid.should == 502
+ File.lchown nil, -1, @lname
+ File.lstat(@lname).gid.should == 502
+ end
+
+ it "returns the number of files processed" do
+ File.lchown(nil, nil, @lname, @lname).should == 2
+ end
+ end
+ end
+end
+
+describe "File.lchown" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/link_spec.rb b/spec/ruby/core/file/link_spec.rb
new file mode 100644
index 0000000000..69d1459672
--- /dev/null
+++ b/spec/ruby/core/file/link_spec.rb
@@ -0,0 +1,39 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File.link" do
+ before :each do
+ @file = tmp("file_link.txt")
+ @link = tmp("file_link.lnk")
+
+ rm_r @link
+ touch @file
+ end
+
+ after :each do
+ rm_r @link, @file
+ end
+
+ platform_is_not :windows do
+ it "link a file with another" do
+ File.link(@file, @link).should == 0
+ File.exist?(@link).should == true
+ File.identical?(@file, @link).should == true
+ end
+
+ it "raises an Errno::EEXIST if the target already exists" do
+ File.link(@file, @link)
+ lambda { File.link(@file, @link) }.should raise_error(Errno::EEXIST)
+ end
+
+ it "raises an ArgumentError if not passed two arguments" do
+ lambda { File.link }.should raise_error(ArgumentError)
+ lambda { File.link(@file) }.should raise_error(ArgumentError)
+ lambda { File.link(@file, @link, @file) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if not passed String types" do
+ lambda { File.link(@file, nil) }.should raise_error(TypeError)
+ lambda { File.link(@file, 1) }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/file/lstat_spec.rb b/spec/ruby/core/file/lstat_spec.rb
new file mode 100644
index 0000000000..6657bfa00e
--- /dev/null
+++ b/spec/ruby/core/file/lstat_spec.rb
@@ -0,0 +1,33 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../shared/stat', __FILE__)
+
+describe "File.lstat" do
+ it_behaves_like :file_stat, :lstat
+end
+
+describe "File.lstat" do
+
+ before :each do
+ @file = tmp('i_exist')
+ @link = tmp('i_am_a_symlink')
+ touch(@file) { |f| f.write 'rubinius' }
+ File.symlink(@file, @link)
+ end
+
+ after :each do
+ rm_r @link, @file
+ end
+
+ platform_is_not :windows do
+ it "returns a File::Stat object with symlink properties for a symlink" do
+ st = File.lstat(@link)
+
+ st.symlink?.should == true
+ st.file?.should == false
+ end
+ end
+end
+
+describe "File#lstat" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/mkfifo_spec.rb b/spec/ruby/core/file/mkfifo_spec.rb
new file mode 100644
index 0000000000..ad6e804b99
--- /dev/null
+++ b/spec/ruby/core/file/mkfifo_spec.rb
@@ -0,0 +1,53 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+ruby_version_is "2.3" do
+ describe "File.mkfifo" do
+ platform_is_not :windows do
+ before do
+ @path = tmp('fifo')
+ end
+
+ after do
+ rm_r(@path)
+ end
+
+ context "when path passed responds to :to_path" do
+ it "creates a FIFO file at the path specified" do
+ File.mkfifo(@path)
+ File.ftype(@path).should == "fifo"
+ end
+ end
+
+ context "when path passed is not a String value" do
+ it "raises a TypeError" do
+ lambda { File.mkfifo(:"/tmp/fifo") }.should raise_error(TypeError)
+ end
+ end
+
+ context "when path does not exist" do
+ it "raises an Errno::ENOENT exception" do
+ lambda { File.mkfifo("/bogus/path") }.should raise_error(Errno::ENOENT)
+ end
+ end
+
+ it "creates a FIFO file at the passed path" do
+ File.mkfifo(@path.to_s)
+ File.ftype(@path).should == "fifo"
+ end
+
+ it "creates a FIFO file with passed mode & ~umask" do
+ File.mkfifo(@path, 0755)
+ File.stat(@path).mode.should == 010755 & ~File.umask
+ end
+
+ it "creates a FIFO file with a default mode of 0666 & ~umask" do
+ File.mkfifo(@path)
+ File.stat(@path).mode.should == 010666 & ~File.umask
+ end
+
+ it "returns 0 after creating the FIFO file" do
+ File.mkfifo(@path).should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/mtime_spec.rb b/spec/ruby/core/file/mtime_spec.rb
new file mode 100644
index 0000000000..56b7e4464e
--- /dev/null
+++ b/spec/ruby/core/file/mtime_spec.rb
@@ -0,0 +1,51 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File.mtime" do
+ before :each do
+ @filename = tmp('i_exist')
+ touch(@filename) { @mtime = Time.now }
+ end
+
+ after :each do
+ rm_r @filename
+ end
+
+ it "returns the modification Time of the file" do
+ File.mtime(@filename).should be_kind_of(Time)
+ File.mtime(@filename).should be_close(@mtime, 2.0)
+ end
+
+ platform_is :linux 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
+ end
+ end
+ end
+
+ it "raises an Errno::ENOENT exception if the file is not found" do
+ lambda { File.mtime('bogus') }.should raise_error(Errno::ENOENT)
+ end
+end
+
+describe "File#mtime" do
+ before :each do
+ @filename = tmp('i_exist')
+ @f = File.open(@filename, 'w')
+ end
+
+ after :each do
+ @f.close
+ rm_r @filename
+ end
+
+ it "returns the modification Time of the file" do
+ @f.mtime.should be_kind_of(Time)
+ end
+
+end
diff --git a/spec/ruby/core/file/new_spec.rb b/spec/ruby/core/file/new_spec.rb
new file mode 100644
index 0000000000..3c72ac48e5
--- /dev/null
+++ b/spec/ruby/core/file/new_spec.rb
@@ -0,0 +1,162 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../shared/open', __FILE__)
+
+describe "File.new" do
+ before :each do
+ @file = tmp('test.txt')
+ @fh = nil
+ @flags = File::CREAT | File::TRUNC | File::WRONLY
+ touch @file
+ end
+
+ after :each do
+ @fh.close if @fh
+ rm_r @file
+ end
+
+ it "returns a new File with mode string" do
+ @fh = File.new(@file, 'w')
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "returns a new File with mode num" do
+ @fh = File.new(@file, @flags)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "returns a new File with modus num and permissions" do
+ rm_r @file
+ File.umask(0011)
+ @fh = File.new(@file, @flags, 0755)
+ @fh.should be_kind_of(File)
+ platform_is_not :windows do
+ File.stat(@file).mode.to_s(8).should == "100744"
+ end
+ File.exist?(@file).should == true
+ end
+
+ it "creates the file and returns writable descriptor when called with 'w' mode and r-o permissions" do
+ # it should be possible to write to such a file via returned descriptior,
+ # even though the file permissions are r-r-r.
+
+ rm_r @file
+ begin
+ f = File.new(@file, "w", 0444)
+ lambda { f.puts("test") }.should_not raise_error(IOError)
+ ensure
+ f.close
+ end
+ File.exist?(@file).should == true
+ File.read(@file).should == "test\n"
+ end
+
+ platform_is_not :windows do
+ it "opens the existing file, does not change permissions even when they are specified" do
+ File.chmod(0644, @file) # r-w perms
+ orig_perms = File.stat(@file).mode & 0777
+ begin
+ f = File.new(@file, "w", 0444) # r-o perms, but they should be ignored
+ f.puts("test")
+ ensure
+ f.close
+ end
+ perms = File.stat(@file).mode & 0777
+ perms.should == orig_perms
+
+ # it should be still possible to read from the file
+ File.read(@file).should == "test\n"
+ end
+ end
+
+ it "returns a new File with modus fd" do
+ @fh = File.new(@file)
+ fh_copy = File.new(@fh.fileno)
+ fh_copy.autoclose = false
+ fh_copy.should be_kind_of(File)
+ File.exist?(@file).should == true
+ 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.exist?(@file).should == true
+ end
+
+ it "raises an Errorno::EEXIST if the file exists when create a new file with File::CREAT|File::EXCL" do
+ lambda { @fh = File.new(@file, File::CREAT|File::EXCL) }.should raise_error(Errno::EEXIST)
+ end
+
+ it "creates a new file when use File::WRONLY|File::APPEND mode" do
+ @fh = File.new(@file, File::WRONLY|File::APPEND)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "returns a new File when use File::APPEND mode" do
+ @fh = File.new(@file, File::APPEND)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "returns a new File when use File::RDONLY|File::APPEND mode" do
+ @fh = File.new(@file, File::RDONLY|File::APPEND)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "returns a new File when use File::RDONLY|File::WRONLY mode" do
+ @fh = File.new(@file, File::RDONLY|File::WRONLY)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ 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.exist?(@file).should == true
+ end
+
+ it "coerces filename using to_str" do
+ name = mock("file")
+ name.should_receive(:to_str).and_return(@file)
+ @fh = File.new(name, "w")
+ File.exist?(@file).should == true
+ end
+
+ it "coerces filename using #to_path" do
+ name = mock("file")
+ name.should_receive(:to_path).and_return(@file)
+ @fh = File.new(name, "w")
+ File.exist?(@file).should == true
+ end
+
+ it "raises a TypeError if the first parameter can't be coerced to a string" do
+ lambda { File.new(true) }.should raise_error(TypeError)
+ lambda { File.new(false) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the first parameter is nil" do
+ lambda { File.new(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises an Errno::EBADF if the first parameter is an invalid file descriptor" do
+ lambda { File.new(-1) }.should raise_error(Errno::EBADF)
+ end
+
+ platform_is_not :windows do
+ it "can't alter mode or permissions when opening a file" do
+ @fh = File.new(@file)
+ lambda {
+ f = File.new(@fh.fileno, @flags)
+ f.autoclose = false
+ }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ platform_is_not :windows do
+ it_behaves_like :open_directory, :new
+ end
+end
diff --git a/spec/ruby/core/file/null_spec.rb b/spec/ruby/core/file/null_spec.rb
new file mode 100644
index 0000000000..b9dd6b658b
--- /dev/null
+++ b/spec/ruby/core/file/null_spec.rb
@@ -0,0 +1,15 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File::NULL" do
+ platform_is :windows do
+ it "returns NUL as a string" do
+ File::NULL.should == 'NUL'
+ end
+ end
+
+ platform_is_not :windows do
+ it "returns /dev/null as a string" do
+ File::NULL.should == '/dev/null'
+ end
+ end
+end
diff --git a/spec/ruby/core/file/open_spec.rb b/spec/ruby/core/file/open_spec.rb
new file mode 100644
index 0000000000..440921a796
--- /dev/null
+++ b/spec/ruby/core/file/open_spec.rb
@@ -0,0 +1,678 @@
+# encoding: utf-8
+
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../fixtures/common', __FILE__)
+require File.expand_path('../shared/open', __FILE__)
+
+describe "File.open" do
+ before :all do
+ @file = tmp("file_open.txt")
+ @unicode_path = tmp("こんにちは.txt")
+ @nonexistent = tmp("fake.txt")
+ rm_r @file, @nonexistent
+ end
+
+ before :each do
+ ScratchPad.record []
+
+ @fh = @fd = nil
+ @flags = File::CREAT | File::TRUNC | File::WRONLY
+ touch @file
+ end
+
+ after :each do
+ @fh.close if @fh and not @fh.closed?
+ rm_r @file, @unicode_path, @nonexistent
+ end
+
+ describe "with a block" do
+ it "does not raise error when file is closed inside the block" do
+ @fh = File.open(@file) { |fh| fh.close; fh }
+ @fh.closed?.should == true
+ end
+
+ it "invokes close on an opened file when exiting the block" do
+ File.open(@file, 'r') { |f| FileSpecs.make_closer f }
+
+ ScratchPad.recorded.should == [:file_opened, :file_closed]
+ end
+
+ it "propagates non-StandardErrors produced by close" do
+ lambda {
+ File.open(@file, 'r') { |f| FileSpecs.make_closer f, Exception }
+ }.should raise_error(Exception)
+
+ ScratchPad.recorded.should == [:file_opened, :file_closed]
+ end
+
+ it "propagates StandardErrors produced by close" do
+ lambda {
+ File.open(@file, 'r') { |f| FileSpecs.make_closer f, StandardError }
+ }.should raise_error(StandardError)
+
+ ScratchPad.recorded.should == [:file_opened, :file_closed]
+ end
+
+ it "does not propagate IOError with 'closed stream' message produced by close" do
+ File.open(@file, 'r') { |f| FileSpecs.make_closer f, IOError.new('closed stream') }
+
+ ScratchPad.recorded.should == [:file_opened, :file_closed]
+ end
+ end
+
+ it "opens the file (basic case)" do
+ @fh = File.open(@file)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens the file with unicode characters" do
+ @fh = File.open(@unicode_path, "w")
+ @fh.should be_kind_of(File)
+ File.exist?(@unicode_path).should == true
+ end
+
+ it "opens a file when called with a block" do
+ File.open(@file) { |fh| }
+ File.exist?(@file).should == true
+ end
+
+ it "opens with mode string" do
+ @fh = File.open(@file, 'w')
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file with mode string and block" do
+ File.open(@file, 'w') { |fh| }
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file with mode num" do
+ @fh = File.open(@file, @flags)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file with mode num and block" do
+ File.open(@file, 'w') { |fh| }
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file with mode and permission as nil" do
+ @fh = File.open(@file, nil, nil)
+ @fh.should be_kind_of(File)
+ end
+
+ # For this test we delete the file first to reset the perms
+ it "opens the file when passed mode, num and permissions" do
+ rm_r @file
+ File.umask(0011)
+ @fh = File.open(@file, @flags, 0755)
+ @fh.should be_kind_of(File)
+ platform_is_not :windows do
+ @fh.lstat.mode.to_s(8).should == "100744"
+ end
+ File.exist?(@file).should == true
+ end
+
+ # For this test we delete the file first to reset the perms
+ it "opens the file when passed mode, num, permissions and block" do
+ rm_r @file
+ File.umask(0022)
+ File.open(@file, "w", 0755){ |fh| }
+ platform_is_not :windows do
+ File.stat(@file).mode.to_s(8).should == "100755"
+ end
+ File.exist?(@file).should == true
+ end
+
+ it "creates the file and returns writable descriptor when called with 'w' mode and r-o permissions" do
+ # it should be possible to write to such a file via returned descriptior,
+ # even though the file permissions are r-r-r.
+
+ File.open(@file, "w", 0444) { |f| f.write("test") }
+ File.read(@file).should == "test"
+ end
+
+ platform_is_not :windows do
+ it "opens the existing file, does not change permissions even when they are specified" do
+ File.chmod(0664, @file)
+ orig_perms = File.stat(@file).mode.to_s(8)
+ File.open(@file, "w", 0444) { |f| f.write("test") }
+
+ File.stat(@file).mode.to_s(8).should == orig_perms
+ File.read(@file).should == "test"
+ end
+ end
+
+ platform_is_not :windows do
+ it "creates a new write-only file when invoked with 'w' and '0222'" do
+ rm_r @file
+ File.open(@file, 'w', 0222) {}
+ File.readable?(@file).should == false
+ File.writable?(@file).should == true
+ end
+ end
+
+ it "opens the file when call with fd" do
+ @fh = File.open(@file)
+ fh_copy = File.open(@fh.fileno)
+ fh_copy.autoclose = false
+ fh_copy.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file with a file descriptor d and a block" do
+ @fh = File.open(@file)
+ @fh.should be_kind_of(File)
+
+ lambda {
+ File.open(@fh.fileno) do |fh|
+ @fd = fh.fileno
+ @fh.close
+ end
+ }.should raise_error(Errno::EBADF)
+ lambda { File.open(@fd) }.should raise_error(Errno::EBADF)
+
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file that no exists when use File::WRONLY mode" do
+ lambda { File.open(@nonexistent, File::WRONLY) }.should raise_error(Errno::ENOENT)
+ end
+
+ it "opens a file that no exists when use File::RDONLY mode" do
+ lambda { File.open(@nonexistent, File::RDONLY) }.should raise_error(Errno::ENOENT)
+ end
+
+ it "opens a file that no exists when use 'r' mode" do
+ lambda { File.open(@nonexistent, 'r') }.should raise_error(Errno::ENOENT)
+ end
+
+ it "opens a file that no exists when use File::EXCL mode" do
+ lambda { File.open(@nonexistent, File::EXCL) }.should raise_error(Errno::ENOENT)
+ end
+
+ it "opens a file that no exists when use File::NONBLOCK mode" do
+ lambda { File.open(@nonexistent, File::NONBLOCK) }.should raise_error(Errno::ENOENT)
+ end
+
+ platform_is_not :openbsd, :windows do
+ it "opens a file that no exists when use File::TRUNC mode" do
+ lambda { File.open(@nonexistent, File::TRUNC) }.should raise_error(Errno::ENOENT)
+ end
+ end
+
+ platform_is :openbsd, :windows do
+ it "does not open a file that does no exists when using File::TRUNC mode" do
+ lambda { File.open(@nonexistent, File::TRUNC) }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ platform_is_not :windows do
+ it "opens a file that no exists when use File::NOCTTY mode" do
+ lambda { File.open(@nonexistent, File::NOCTTY) }.should raise_error(Errno::ENOENT)
+ end
+ end
+
+ it "opens a file that no exists when use File::CREAT mode" do
+ @fh = File.open(@nonexistent, File::CREAT) { |f| f }
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file that no exists when use 'a' mode" do
+ @fh = File.open(@nonexistent, 'a') { |f| f }
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file that no exists when use 'w' mode" do
+ @fh = File.open(@nonexistent, 'w') { |f| f }
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ # Check the grants associated to the differents open modes combinations.
+ it "raises an ArgumentError exception when call with an unknown mode" do
+ lambda { File.open(@file, "q") }.should raise_error(ArgumentError)
+ end
+
+ it "can read in a block when call open with RDONLY mode" do
+ File.open(@file, File::RDONLY) do |f|
+ f.gets.should == nil
+ end
+ end
+
+ it "can read in a block when call open with 'r' mode" do
+ File.open(@file, "r") do |f|
+ f.gets.should == nil
+ end
+ end
+
+ it "raises an IO exception when write in a block opened with RDONLY mode" do
+ File.open(@file, File::RDONLY) do |f|
+ lambda { f.puts "writing ..." }.should raise_error(IOError)
+ end
+ end
+
+ it "raises an IO exception when write in a block opened with 'r' mode" do
+ File.open(@file, "r") do |f|
+ lambda { f.puts "writing ..." }.should raise_error(IOError)
+ end
+ end
+
+ it "can't write in a block when call open with File::WRONLY||File::RDONLY mode" do
+ File.open(@file, File::WRONLY|File::RDONLY ) do |f|
+ f.puts("writing").should == nil
+ end
+ end
+
+ it "can't read in a block when call open with File::WRONLY||File::RDONLY mode" do
+ lambda {
+ File.open(@file, File::WRONLY|File::RDONLY ) do |f|
+ f.gets.should == nil
+ end
+ }.should raise_error(IOError)
+ end
+
+ it "can write in a block when call open with WRONLY mode" do
+ File.open(@file, File::WRONLY) do |f|
+ f.puts("writing").should == nil
+ end
+ end
+
+ it "can write in a block when call open with 'w' mode" do
+ File.open(@file, "w") do |f|
+ f.puts("writing").should == nil
+ end
+ end
+
+ it "raises an IOError when read in a block opened with WRONLY mode" do
+ File.open(@file, File::WRONLY) do |f|
+ lambda { f.gets }.should raise_error(IOError)
+ end
+ end
+
+ it "raises an IOError when read in a block opened with 'w' mode" do
+ File.open(@file, "w") do |f|
+ lambda { f.gets }.should raise_error(IOError)
+ end
+ end
+
+ it "raises an IOError when read in a block opened with 'a' mode" do
+ File.open(@file, "a") do |f|
+ lambda { f.gets }.should raise_error(IOError)
+ end
+ end
+
+ it "raises an IOError when read in a block opened with 'a' mode" do
+ File.open(@file, "a") do |f|
+ f.puts("writing").should == nil
+ lambda { f.gets }.should raise_error(IOError)
+ end
+ end
+
+ it "raises an IOError when read in a block opened with 'a' mode" do
+ File.open(@file, File::WRONLY|File::APPEND ) do |f|
+ lambda { f.gets }.should raise_error(IOError)
+ end
+ end
+
+ it "raises an IOError when read in a block opened with File::WRONLY|File::APPEND mode" do
+ File.open(@file, File::WRONLY|File::APPEND ) do |f|
+ f.puts("writing").should == nil
+ lambda { f.gets }.should raise_error(IOError)
+ end
+ end
+
+ it "raises an IOError when read in a block opened with File::RDONLY|File::APPEND mode" do
+ lambda {
+ File.open(@file, File::RDONLY|File::APPEND ) do |f|
+ f.puts("writing")
+ end
+ }.should raise_error(IOError)
+ end
+
+ it "can read and write in a block when call open with RDWR mode" do
+ File.open(@file, File::RDWR) do |f|
+ f.gets.should == nil
+ f.puts("writing").should == nil
+ f.rewind
+ f.gets.should == "writing\n"
+ end
+ end
+
+ it "can't read in a block when call open with File::EXCL mode" do
+ lambda {
+ File.open(@file, File::EXCL) do |f|
+ f.puts("writing").should == nil
+ end
+ }.should raise_error(IOError)
+ end
+
+ it "can read in a block when call open with File::EXCL mode" do
+ File.open(@file, File::EXCL) do |f|
+ f.gets.should == nil
+ end
+ end
+
+ it "can read and write in a block when call open with File::RDWR|File::EXCL mode" do
+ File.open(@file, File::RDWR|File::EXCL) do |f|
+ f.gets.should == nil
+ f.puts("writing").should == nil
+ f.rewind
+ f.gets.should == "writing\n"
+ end
+ end
+
+ it "raises an Errorno::EEXIST if the file exists when open with File::CREAT|File::EXCL" do
+ lambda {
+ File.open(@file, File::CREAT|File::EXCL) do |f|
+ f.puts("writing")
+ end
+ }.should raise_error(Errno::EEXIST)
+ end
+
+ it "creates a new file when use File::WRONLY|File::APPEND mode" do
+ @fh = File.open(@file, File::WRONLY|File::APPEND)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file when use File::WRONLY|File::APPEND mode" do
+ File.open(@file, File::WRONLY) do |f|
+ f.puts("hello file")
+ end
+ File.open(@file, File::RDWR|File::APPEND) do |f|
+ f.puts("bye file")
+ f.rewind
+ f.gets.should == "hello file\n"
+ f.gets.should == "bye file\n"
+ f.gets.should == nil
+ end
+ end
+
+ it "raises an IOError if the file exists when open with File::RDONLY|File::APPEND" do
+ lambda {
+ File.open(@file, File::RDONLY|File::APPEND) do |f|
+ f.puts("writing").should == nil
+ end
+ }.should raise_error(IOError)
+ end
+
+ platform_is_not :openbsd, :windows do
+ it "truncates the file when passed File::TRUNC mode" do
+ File.open(@file, File::RDWR) { |f| f.puts "hello file" }
+ @fh = File.open(@file, File::TRUNC)
+ @fh.gets.should == nil
+ end
+
+ it "can't read in a block when call open with File::TRUNC mode" do
+ File.open(@file, File::TRUNC) do |f|
+ f.gets.should == nil
+ end
+ end
+ end
+
+ it "opens a file when use File::WRONLY|File::TRUNC mode" do
+ fh1 = File.open(@file, "w")
+ begin
+ @fh = File.open(@file, File::WRONLY|File::TRUNC)
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ ensure
+ fh1.close
+ end
+ end
+
+ platform_is_not :openbsd, :windows do
+ it "can't write in a block when call open with File::TRUNC mode" do
+ lambda {
+ File.open(@file, File::TRUNC) do |f|
+ f.puts("writing")
+ end
+ }.should raise_error(IOError)
+ end
+
+ it "raises an Errorno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do
+ lambda {
+ File.open(@file, File::RDONLY|File::TRUNC) do |f|
+ f.puts("writing").should == nil
+ end
+ }.should raise_error(IOError)
+ end
+ end
+
+ platform_is :openbsd, :windows do
+ it "can't write in a block when call open with File::TRUNC mode" do
+ lambda {
+ File.open(@file, File::TRUNC) do |f|
+ f.puts("writing")
+ end
+ }.should raise_error(Errno::EINVAL)
+ end
+
+ it "raises an Errorno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do
+ lambda {
+ File.open(@file, File::RDONLY|File::TRUNC) do |f|
+ f.puts("writing").should == nil
+ end
+ }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ platform_is_not :windows do
+ it "raises an Errno::EACCES when opening non-permitted file" do
+ @fh = File.open(@file, "w")
+ @fh.chmod(000)
+ lambda { fh1 = File.open(@file); fh1.close }.should raise_error(Errno::EACCES)
+ end
+ end
+
+ it "raises an Errno::EACCES when opening read-only file" do
+ @fh = File.open(@file, "w")
+ @fh.chmod(0444)
+ lambda { File.open(@file, "w") }.should raise_error(Errno::EACCES)
+ end
+
+ it "opens a file for binary read" do
+ @fh = File.open(@file, "rb")
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file for binary write" do
+ @fh = File.open(@file, "wb")
+ @fh.should be_kind_of(File)
+ File.exist?(@file).should == true
+ end
+
+ it "opens a file for read-write and truncate the file" do
+ File.open(@file, "w") { |f| f.puts "testing" }
+ File.size(@file).should > 0
+ File.open(@file, "w+") do |f|
+ f.pos.should == 0
+ f.eof?.should == true
+ end
+ File.size(@file).should == 0
+ end
+
+ it "opens a file for binary read-write starting at the beginning of the file" do
+ File.open(@file, "w") { |f| f.puts "testing" }
+ File.size(@file).should > 0
+ File.open(@file, "rb+") do |f|
+ f.pos.should == 0
+ f.eof?.should == false
+ end
+ end
+
+ it "opens a file for binary read-write and truncate the file" do
+ File.open(@file, "w") { |f| f.puts "testing" }
+ File.size(@file).should > 0
+ File.open(@file, "wb+") do |f|
+ f.pos.should == 0
+ f.eof?.should == true
+ end
+ File.size(@file).should == 0
+ end
+
+ ruby_version_is "2.3" do
+ platform_is :linux do
+ if defined?(File::TMPFILE)
+ it "creates an unnamed temporary file with File::TMPFILE" do
+ dir = tmp("tmpfilespec")
+ mkdir_p dir
+ begin
+ Dir["#{dir}/*"].should == []
+ File.open(dir, "r+", flags: File::TMPFILE) do |io|
+ io.write("ruby")
+ io.flush
+ io.rewind
+ io.read.should == "ruby"
+ Dir["#{dir}/*"].should == []
+ end
+ rescue Errno::EOPNOTSUPP, Errno::EINVAL
+ # EOPNOTSUPP: no support from the filesystem
+ # EINVAL: presumably bug in glibc
+ 1.should == 1
+ ensure
+ rm_r dir
+ end
+ end
+ end
+ end
+ end
+
+ it "raises a TypeError if passed a filename that is not a String or Integer type" do
+ lambda { File.open(true) }.should raise_error(TypeError)
+ lambda { File.open(false) }.should raise_error(TypeError)
+ lambda { File.open(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a SystemCallError if passed an invalid Integer type" do
+ lambda { File.open(-1) }.should raise_error(SystemCallError)
+ end
+
+ it "raises an ArgumentError if passed the wrong number of arguments" do
+ lambda { File.open(@file, File::CREAT, 0755, 'test') }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if passed an invalid string for mode" do
+ lambda { File.open(@file, 'fake') }.should raise_error(ArgumentError)
+ end
+
+ it "defaults external_encoding to ASCII-8BIT for binary modes" do
+ File.open(@file, 'rb') {|f| f.external_encoding.should == Encoding::ASCII_8BIT}
+ File.open(@file, 'wb+') {|f| f.external_encoding.should == Encoding::ASCII_8BIT}
+ end
+
+ it "uses the second argument as an options Hash" do
+ @fh = File.open(@file, mode: "r")
+ @fh.should be_an_instance_of(File)
+ end
+
+ it "calls #to_hash to convert the second argument to a Hash" do
+ options = mock("file open options")
+ options.should_receive(:to_hash).and_return({ mode: "r" })
+
+ @fh = File.open(@file, options)
+ end
+
+ ruby_version_is "2.3" do
+ it "accepts extra flags as a keyword argument and combine with a string mode" do
+ lambda {
+ File.open(@file, "w", flags: File::EXCL) { }
+ }.should raise_error(Errno::EEXIST)
+
+ lambda {
+ File.open(@file, mode: "w", flags: File::EXCL) { }
+ }.should raise_error(Errno::EEXIST)
+ end
+
+ it "accepts extra flags as a keyword argument and combine with an integer mode" do
+ lambda {
+ File.open(@file, File::WRONLY | File::CREAT, flags: File::EXCL) { }
+ }.should raise_error(Errno::EEXIST)
+ end
+ end
+
+ platform_is_not :windows do
+ describe "on a FIFO" do
+ before :each do
+ @fifo = tmp("File_open_fifo")
+ system "mkfifo #{@fifo}"
+ end
+
+ after :each do
+ rm_r @fifo
+ end
+
+ it "opens it as a normal file" do
+ file_w, file_r, read_bytes, written_length = nil
+
+ # open in threads, due to blocking open and writes
+ writer = Thread.new do
+ file_w = File.open(@fifo, 'w')
+ written_length = file_w.syswrite('hello')
+ end
+ reader = Thread.new do
+ file_r = File.open(@fifo, 'r')
+ read_bytes = file_r.sysread(5)
+ end
+
+ begin
+ writer.join
+ reader.join
+
+ written_length.should == 5
+ read_bytes.should == 'hello'
+ ensure
+ file_w.close if file_w
+ file_r.close if file_r
+ end
+ end
+ end
+ end
+
+end
+
+describe "File.open when passed a file descriptor" do
+ before do
+ @content = "File#open when passed a file descriptor"
+ @name = tmp("file_open_with_fd.txt")
+ @fd = new_fd @name, fmode("w:utf-8")
+ @file = nil
+ end
+
+ after do
+ @file.close if @file and not @file.closed?
+ rm_r @name
+ end
+
+ it "opens a file" do
+ @file = File.open(@fd, "w")
+ @file.should be_an_instance_of(File)
+ @file.fileno.should equal(@fd)
+ @file.write @content
+ @file.flush
+ File.read(@name).should == @content
+ end
+
+ it "opens a file when passed a block" do
+ @file = File.open(@fd, "w") do |f|
+ f.should be_an_instance_of(File)
+ f.fileno.should equal(@fd)
+ f.write @content
+ f
+ end
+ File.read(@name).should == @content
+ end
+end
+
+platform_is_not :windows do
+ describe "File.open" do
+ it_behaves_like :open_directory, :open
+ end
+end
diff --git a/spec/ruby/core/file/owned_spec.rb b/spec/ruby/core/file/owned_spec.rb
new file mode 100644
index 0000000000..d19e9cb278
--- /dev/null
+++ b/spec/ruby/core/file/owned_spec.rb
@@ -0,0 +1,33 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/owned', __FILE__)
+
+describe "File.owned?" do
+ it_behaves_like :file_owned, :owned?, File
+end
+
+describe "File.owned?" do
+ before :each do
+ @filename = tmp("i_exist")
+ touch(@filename)
+ end
+
+ after :each do
+ rm_r @filename
+ end
+
+ it "returns false if file does not exist" do
+ File.owned?("I_am_a_bogus_file").should == false
+ end
+
+ it "returns true if the file exist and is owned by the user" do
+ File.owned?(@filename).should == true
+ end
+
+ platform_is_not :windows do
+ it "returns false when the file is not owned by the user" do
+ system_file = '/etc/passwd'
+ File.owned?(system_file).should == false
+ end
+ end
+
+end
diff --git a/spec/ruby/core/file/path_spec.rb b/spec/ruby/core/file/path_spec.rb
new file mode 100644
index 0000000000..5004e128cd
--- /dev/null
+++ b/spec/ruby/core/file/path_spec.rb
@@ -0,0 +1,29 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File#path" do
+ before :each do
+ @name = tmp("file_path")
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ it "returns the pathname used to create file as a string" do
+ File.open(@name,'w') { |file| file.path.should == @name }
+ end
+end
+
+describe "File.path" do
+ before :each do
+ @name = tmp("file_path")
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ it "returns the full path for the given file" do
+ File.path(@name).should == @name
+ end
+end
diff --git a/spec/ruby/core/file/pipe_spec.rb b/spec/ruby/core/file/pipe_spec.rb
new file mode 100644
index 0000000000..ca7392b8ee
--- /dev/null
+++ b/spec/ruby/core/file/pipe_spec.rb
@@ -0,0 +1,32 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/pipe', __FILE__)
+
+describe "File.pipe?" do
+ it_behaves_like :file_pipe, :pipe?, File
+end
+
+describe "File.pipe?" do
+ it "returns false if file does not exist" do
+ File.pipe?("I_am_a_bogus_file").should == false
+ end
+
+ it "returns false if the file is not a pipe" do
+ filename = tmp("i_exist")
+ touch(filename)
+
+ File.pipe?(filename).should == false
+
+ rm_r filename
+ end
+
+ platform_is_not :windows do
+ it "returns true if the file is a pipe" do
+ filename = tmp("i_am_a_pipe")
+ system "mkfifo #{filename}"
+
+ File.pipe?(filename).should == true
+
+ rm_r filename
+ end
+ end
+end
diff --git a/spec/ruby/core/file/read_spec.rb b/spec/ruby/core/file/read_spec.rb
new file mode 100644
index 0000000000..fdbbf58a1c
--- /dev/null
+++ b/spec/ruby/core/file/read_spec.rb
@@ -0,0 +1,6 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../shared/read', __FILE__)
+
+describe "File.read" do
+ it_behaves_like :file_read_directory, :read, File
+end
diff --git a/spec/ruby/core/file/readable_real_spec.rb b/spec/ruby/core/file/readable_real_spec.rb
new file mode 100644
index 0000000000..5fca968611
--- /dev/null
+++ b/spec/ruby/core/file/readable_real_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/readable_real', __FILE__)
+
+describe "File.readable_real?" do
+ it_behaves_like :file_readable_real, :readable_real?, File
+ it_behaves_like :file_readable_real_missing, :readable_real?, File
+end
diff --git a/spec/ruby/core/file/readable_spec.rb b/spec/ruby/core/file/readable_spec.rb
new file mode 100644
index 0000000000..3307e5e30f
--- /dev/null
+++ b/spec/ruby/core/file/readable_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/readable', __FILE__)
+
+describe "File.readable?" do
+ it_behaves_like :file_readable, :readable?, File
+ it_behaves_like :file_readable_missing, :readable?, File
+end
diff --git a/spec/ruby/core/file/readlink_spec.rb b/spec/ruby/core/file/readlink_spec.rb
new file mode 100644
index 0000000000..6db2c09780
--- /dev/null
+++ b/spec/ruby/core/file/readlink_spec.rb
@@ -0,0 +1,67 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File.readlink" do
+ # symlink/readlink are not supported on Windows
+ platform_is_not :windows do
+ describe "File.readlink with absolute paths" do
+ before :each do
+ @file = tmp('file_readlink.txt')
+ @link = tmp('file_readlink.lnk')
+
+ File.symlink(@file, @link)
+ end
+
+ after :each do
+ rm_r @file, @link
+ end
+
+ it "returns the name of the file referenced by the given link" do
+ touch @file
+ File.readlink(@link).should == @file
+ end
+
+ it "returns the name of the file referenced by the given link when the file does not exist" do
+ File.readlink(@link).should == @file
+ end
+
+ it "raises an Errno::ENOENT if there is no such file" do
+ # TODO: missing_file
+ lambda { File.readlink("/this/surely/doesnt/exist") }.should raise_error(Errno::ENOENT)
+ end
+
+ it "raises an Errno::EINVAL if called with a normal file" do
+ touch @file
+ lambda { File.readlink(@file) }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ describe "File.readlink when changing the working directory" do
+ before :each do
+ @cwd = Dir.pwd
+ @tmpdir = tmp("/readlink")
+ Dir.mkdir @tmpdir
+ Dir.chdir @tmpdir
+
+ @link = 'readlink_link'
+ @file = 'readlink_file'
+
+ File.symlink(@file, @link)
+ end
+
+ after :each do
+ rm_r @file, @link
+ Dir.chdir @cwd
+ Dir.rmdir @tmpdir
+ end
+
+ it "returns the name of the file referenced by the given link" do
+ touch @file
+ File.readlink(@link).should == @file
+ end
+
+ it "returns the name of the file referenced by the given link when the file does not exist" do
+ File.readlink(@link).should == @file
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/realdirpath_spec.rb b/spec/ruby/core/file/realdirpath_spec.rb
new file mode 100644
index 0000000000..06900ad461
--- /dev/null
+++ b/spec/ruby/core/file/realdirpath_spec.rb
@@ -0,0 +1,104 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+platform_is_not :windows do
+ describe "File.realdirpath" do
+ before :each do
+ @real_dir = tmp('dir_realdirpath_real')
+ @fake_dir = tmp('dir_realdirpath_fake')
+ @link_dir = tmp('dir_realdirpath_link')
+
+ mkdir_p @real_dir
+ File.symlink(@real_dir, @link_dir)
+
+ @file = File.join(@real_dir, 'file')
+ @link = File.join(@link_dir, 'link')
+
+ touch @file
+ File.symlink(@file, @link)
+
+ @fake_file_in_real_dir = File.join(@real_dir, 'fake_file_in_real_dir')
+ @fake_file_in_fake_dir = File.join(@fake_dir, 'fake_file_in_fake_dir')
+ @fake_link_to_real_dir = File.join(@link_dir, 'fake_link_to_real_dir')
+ @fake_link_to_fake_dir = File.join(@link_dir, 'fake_link_to_fake_dir')
+
+ File.symlink(@fake_file_in_real_dir, @fake_link_to_real_dir)
+ File.symlink(@fake_file_in_fake_dir, @fake_link_to_fake_dir)
+
+ @dir_for_relative_link = File.join(@real_dir, 'dir1')
+ mkdir_p @dir_for_relative_link
+
+ @relative_path_to_file = File.join('..', 'file')
+ @relative_symlink = File.join(@dir_for_relative_link, 'link')
+ File.symlink(@relative_path_to_file, @relative_symlink)
+ end
+
+ after :each do
+ rm_r @file, @link, @fake_link_to_real_dir, @fake_link_to_fake_dir, @real_dir, @link_dir
+ end
+
+ it "returns '/' when passed '/'" do
+ File.realdirpath('/').should == '/'
+ end
+
+ it "returns the real (absolute) pathname not containing symlinks" do
+ File.realdirpath(@link).should == @file
+ end
+
+ it "uses base directory for interpreting relative pathname" do
+ File.realdirpath(File.basename(@link), @link_dir).should == @file
+ end
+
+ it "uses current directory for interpreting relative pathname" do
+ Dir.chdir @link_dir do
+ File.realdirpath(File.basename(@link)).should == @file
+ end
+ end
+
+ it "uses link directory for expanding relative links" do
+ File.realdirpath(@relative_symlink).should == @file
+ end
+
+ it "raises an Errno::ELOOP if the symlink points to itself" do
+ File.unlink @link
+ File.symlink(@link, @link)
+ lambda { File.realdirpath(@link) }.should raise_error(Errno::ELOOP)
+ end
+
+ it "returns the real (absolute) pathname if the file is absent" do
+ File.realdirpath(@fake_file_in_real_dir).should == @fake_file_in_real_dir
+ end
+
+ it "raises Errno::ENOENT if the directory is absent" do
+ lambda { File.realdirpath(@fake_file_in_fake_dir) }.should raise_error(Errno::ENOENT)
+ end
+
+ it "returns the real (absolute) pathname if the symlink points to an absent file" do
+ File.realdirpath(@fake_link_to_real_dir).should == @fake_file_in_real_dir
+ end
+
+ it "raises Errno::ENOENT if the symlink points to an absent directory" do
+ lambda { File.realdirpath(@fake_link_to_fake_dir) }.should raise_error(Errno::ENOENT)
+ end
+ end
+end
+
+platform_is :windows do
+ describe "File.realdirpath" do
+ before :each do
+ @file = tmp("realdirpath")
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the same path" do
+ touch @file
+ File.realdirpath(@file).should == @file
+ end
+
+ it "returns the same path even if the last component does not exist" do
+ File.realdirpath(@file).should == @file
+ end
+ end
+end
diff --git a/spec/ruby/core/file/realpath_spec.rb b/spec/ruby/core/file/realpath_spec.rb
new file mode 100644
index 0000000000..49aed7b88c
--- /dev/null
+++ b/spec/ruby/core/file/realpath_spec.rb
@@ -0,0 +1,88 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+platform_is_not :windows do
+ describe "File.realpath" do
+ before :each do
+ @real_dir = tmp('dir_realpath_real')
+ @link_dir = tmp('dir_realpath_link')
+
+ mkdir_p @real_dir
+ File.symlink(@real_dir, @link_dir)
+
+ @file = File.join(@real_dir, 'file')
+ @link = File.join(@link_dir, 'link')
+
+ touch @file
+ File.symlink(@file, @link)
+
+ @fake_file = File.join(@real_dir, 'fake_file')
+ @fake_link = File.join(@link_dir, 'fake_link')
+
+ File.symlink(@fake_file, @fake_link)
+
+ @dir_for_relative_link = File.join(@real_dir, 'dir1')
+ mkdir_p @dir_for_relative_link
+
+ @relative_path_to_file = File.join('..', 'file')
+ @relative_symlink = File.join(@dir_for_relative_link, 'link')
+ File.symlink(@relative_path_to_file, @relative_symlink)
+ end
+
+ after :each do
+ rm_r @file, @link, @fake_link, @real_dir, @link_dir
+ end
+
+ it "returns '/' when passed '/'" do
+ File.realpath('/').should == '/'
+ end
+
+ it "returns the real (absolute) pathname not containing symlinks" do
+ File.realpath(@link).should == @file
+ end
+
+ it "uses base directory for interpreting relative pathname" do
+ File.realpath(File.basename(@link), @link_dir).should == @file
+ end
+
+ it "uses current directory for interpreting relative pathname" do
+ Dir.chdir @link_dir do
+ File.realpath(File.basename(@link)).should == @file
+ end
+ end
+
+ it "uses link directory for expanding relative links" do
+ File.realpath(@relative_symlink).should == @file
+ end
+
+ it "raises an Errno::ELOOP if the symlink points to itself" do
+ File.unlink @link
+ File.symlink(@link, @link)
+ lambda { File.realpath(@link) }.should raise_error(Errno::ELOOP)
+ end
+
+ it "raises Errno::ENOENT if the file is absent" do
+ lambda { File.realpath(@fake_file) }.should raise_error(Errno::ENOENT)
+ end
+
+ it "raises Errno::ENOENT if the symlink points to an absent file" do
+ lambda { File.realpath(@fake_link) }.should raise_error(Errno::ENOENT)
+ end
+ end
+end
+
+platform_is :windows do
+ describe "File.realpath" do
+ before :each do
+ @file = tmp("realpath")
+ touch @file
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the same path" do
+ File.realpath(@file).should == @file
+ end
+ end
+end
diff --git a/spec/ruby/core/file/rename_spec.rb b/spec/ruby/core/file/rename_spec.rb
new file mode 100644
index 0000000000..a62ba809bd
--- /dev/null
+++ b/spec/ruby/core/file/rename_spec.rb
@@ -0,0 +1,37 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File.rename" do
+ before :each do
+ @old = tmp("file_rename.txt")
+ @new = tmp("file_rename.new")
+
+ rm_r @new
+ touch(@old) { |f| f.puts "hello" }
+ end
+
+ after :each do
+ rm_r @old, @new
+ end
+
+ it "renames a file" do
+ File.exist?(@old).should == true
+ File.exist?(@new).should == false
+ File.rename(@old, @new)
+ File.exist?(@old).should == false
+ File.exist?(@new).should == true
+ end
+
+ it "raises an Errno::ENOENT if the source does not exist" do
+ rm_r @old
+ lambda { File.rename(@old, @new) }.should raise_error(Errno::ENOENT)
+ end
+
+ it "raises an ArgumentError if not passed two arguments" do
+ lambda { File.rename }.should raise_error(ArgumentError)
+ lambda { File.rename(@file) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if not passed String types" do
+ lambda { File.rename(1, 2) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/file/reopen_spec.rb b/spec/ruby/core/file/reopen_spec.rb
new file mode 100644
index 0000000000..2493829740
--- /dev/null
+++ b/spec/ruby/core/file/reopen_spec.rb
@@ -0,0 +1,32 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File#reopen" do
+ before :each do
+ @name_a = tmp("file_reopen_a.txt")
+ @name_b = tmp("file_reopen_b.txt")
+ @content_a = "File#reopen a"
+ @content_b = "File#reopen b"
+
+ touch(@name_a) { |f| f.write @content_a }
+ touch(@name_b) { |f| f.write @content_b }
+
+ @file = nil
+ end
+
+ after :each do
+ @file.close if @file and not @file.closed?
+ rm_r @name_a, @name_b
+ end
+
+ it "resets the stream to a new file path" do
+ file = File.new @name_a, "r"
+ file.read.should == @content_a
+ @file = file.reopen(@name_b, "r")
+ @file.read.should == @content_b
+ end
+
+ it "calls #to_path to convern an Object" do
+ @file = File.new(@name_a).reopen(mock_to_path(@name_b), "r")
+ @file.read.should == @content_b
+ end
+end
diff --git a/spec/ruby/core/file/setgid_spec.rb b/spec/ruby/core/file/setgid_spec.rb
new file mode 100644
index 0000000000..dc63329cc3
--- /dev/null
+++ b/spec/ruby/core/file/setgid_spec.rb
@@ -0,0 +1,36 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/setgid', __FILE__)
+
+describe "File.setgid?" do
+ it_behaves_like :file_setgid, :setgid?, File
+end
+
+describe "File.setgid?" do
+ before :each do
+ @name = tmp('test.txt')
+ touch @name
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ it "returns false if the file was just made" do
+ File.setgid?(@name).should == false
+ end
+
+ it "returns false if the file does not exist" do
+ rm_r @name # delete it prematurely, just for this part
+ File.setgid?(@name).should == false
+ end
+
+ as_superuser do
+ platform_is_not :windows do
+ it "returns true when the gid bit is set" do
+ system "chmod g+s #{@name}"
+
+ File.setgid?(@name).should == true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/setuid_spec.rb b/spec/ruby/core/file/setuid_spec.rb
new file mode 100644
index 0000000000..dcd1d3aed1
--- /dev/null
+++ b/spec/ruby/core/file/setuid_spec.rb
@@ -0,0 +1,38 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/setuid', __FILE__)
+
+describe "File.setuid?" do
+ it_behaves_like :file_setuid, :setuid?, File
+end
+
+describe "File.setuid?" do
+ before :each do
+ @name = tmp('test.txt')
+ touch @name
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ it "returns false if the file was just made" do
+ File.setuid?(@name).should == false
+ end
+
+ it "returns false if the file does not exist" do
+ rm_r @name # delete it prematurely, just for this part
+ File.setuid?(@name).should == false
+ end
+
+ 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
+ end
+ end
+end
diff --git a/spec/ruby/core/file/shared/fnmatch.rb b/spec/ruby/core/file/shared/fnmatch.rb
new file mode 100644
index 0000000000..80e37b3fff
--- /dev/null
+++ b/spec/ruby/core/file/shared/fnmatch.rb
@@ -0,0 +1,241 @@
+describe :file_fnmatch, shared: true do
+ it "matches entire strings" do
+ File.send(@method, 'cat', 'cat').should == true
+ end
+
+ it "does not match partial strings" do
+ File.send(@method, 'cat', 'category').should == false
+ end
+
+ it "does not support { } patterns by default" do
+ File.send(@method, 'c{at,ub}s', 'cats').should == false
+ File.send(@method, 'c{at,ub}s', 'c{at,ub}s').should == true
+ end
+
+ it "supports some { } patterns when File::FNM_EXTGLOB is passed" do
+ File.send(@method, "{a,b}", "a", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{a,b}", "b", File::FNM_EXTGLOB).should == true
+ File.send(@method, "c{at,ub}s", "cats", File::FNM_EXTGLOB).should == true
+ File.send(@method, "c{at,ub}s", "cubs", File::FNM_EXTGLOB).should == true
+ File.send(@method, "-c{at,ub}s-", "-cats-", File::FNM_EXTGLOB).should == true
+ File.send(@method, "-c{at,ub}s-", "-cubs-", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{a,b,c}{d,e,f}{g,h}", "adg", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{a,b,c}{d,e,f}{g,h}", "bdg", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{a,b,c}{d,e,f}{g,h}", "ceh", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{aa,bb,cc,dd}", "aa", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{aa,bb,cc,dd}", "bb", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{aa,bb,cc,dd}", "cc", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{aa,bb,cc,dd}", "dd", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{1,5{a,b{c,d}}}", "1", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{1,5{a,b{c,d}}}", "5a", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{1,5{a,b{c,d}}}", "5bc", File::FNM_EXTGLOB).should == true
+ File.send(@method, "{1,5{a,b{c,d}}}", "5bd", File::FNM_EXTGLOB).should == true
+ File.send(@method, "\\\\{a\\,b,b\\}c}", "\\a,b", File::FNM_EXTGLOB).should == true
+ File.send(@method, "\\\\{a\\,b,b\\}c}", "\\b}c", File::FNM_EXTGLOB).should == true
+ end
+
+ it "doesn't support some { } patterns even when File::FNM_EXTGLOB is passed" do
+ File.send(@method, "a{0..3}b", "a0b", File::FNM_EXTGLOB).should == false
+ File.send(@method, "a{0..3}b", "a1b", File::FNM_EXTGLOB).should == false
+ File.send(@method, "a{0..3}b", "a2b", File::FNM_EXTGLOB).should == false
+ File.send(@method, "a{0..3}b", "a3b", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{0..12}", "0", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{0..12}", "6", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{0..12}", "12", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{3..-2}", "3", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{3..-2}", "0", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{3..-2}", "-2", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{a..g}", "a", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{a..g}", "d", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{a..g}", "g", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{g..a}", "a", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{g..a}", "d", File::FNM_EXTGLOB).should == false
+ File.send(@method, "{g..a}", "g", File::FNM_EXTGLOB).should == false
+ File.send(@method, "escaping: {{,\\,,\\},\\{}", "escaping: {", File::FNM_EXTGLOB).should == false
+ File.send(@method, "escaping: {{,\\,,\\},\\{}", "escaping: ,", File::FNM_EXTGLOB).should == false
+ File.send(@method, "escaping: {{,\\,,\\},\\{}", "escaping: }", File::FNM_EXTGLOB).should == false
+ File.send(@method, "escaping: {{,\\,,\\},\\{}", "escaping: {", File::FNM_EXTGLOB).should == false
+ end
+
+ it "doesn't match an extra } when File::FNM_EXTGLOB is passed" do
+ File.send(@method, 'c{at,ub}}s', 'cats', File::FNM_EXTGLOB).should == false
+ end
+
+ it "matches when both FNM_EXTGLOB and FNM_PATHNAME are passed" do
+ File.send(@method, "?.md", "a.md", File::FNM_EXTGLOB | File::FNM_PATHNAME).should == true
+ end
+
+ it "matches a single character for each ? character" do
+ File.send(@method, 'c?t', 'cat').should == true
+ File.send(@method, 'c??t', 'cat').should == false
+ end
+
+ it "matches zero or more characters for each * character" do
+ File.send(@method, 'c*', 'cats').should == true
+ File.send(@method, 'c*t', 'c/a/b/t').should == true
+ end
+
+ it "matches ranges of characters using bracket expresions (e.g. [a-z])" do
+ File.send(@method, 'ca[a-z]', 'cat').should == true
+ end
+
+ it "matches ranges of characters using bracket expresions, taking case into account" do
+ File.send(@method, '[a-z]', 'D').should == false
+ File.send(@method, '[^a-z]', 'D').should == true
+ File.send(@method, '[A-Z]', 'd').should == false
+ File.send(@method, '[^A-Z]', 'd').should == true
+ File.send(@method, '[a-z]', 'D', File::FNM_CASEFOLD).should == true
+ end
+
+ it "does not match characters outside of the range of the bracket expresion" do
+ File.send(@method, 'ca[x-z]', 'cat').should == false
+ File.send(@method, '/ca[s][s-t]/rul[a-b]/[z]he/[x-Z]orld', '/cats/rule/the/World').should == false
+ end
+
+ it "matches ranges of characters using exclusive bracket expresions (e.g. [^t] or [!t])" do
+ File.send(@method, 'ca[^t]', 'cat').should == false
+ File.send(@method, 'ca[!t]', 'cat').should == false
+ end
+
+ it "matches characters with a case sensitive comparison" do
+ File.send(@method, 'cat', 'CAT').should == false
+ end
+
+ it "matches characters with case insensitive comparison when flags includes FNM_CASEFOLD" do
+ File.send(@method, 'cat', 'CAT', File::FNM_CASEFOLD).should == true
+ end
+
+ platform_is_not :windows do
+ it "doesn't match case sensitive characters on platfroms with case sensitive paths, when flags include FNM_SYSCASE" do
+ File.send(@method, 'cat', 'CAT', File::FNM_SYSCASE).should == false
+ end
+ end
+
+ platform_is :windows do
+ it "matches case sensitive characters on platfroms with case insensitive paths, when flags include FNM_SYSCASE" do
+ File.send(@method, 'cat', 'CAT', File::FNM_SYSCASE).should == true
+ end
+ 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
+ end
+
+ it "does not match '/' characters inside bracket expressions when flags includes FNM_PATHNAME" do
+ File.send(@method, '[/]', '/', File::FNM_PATHNAME).should == false
+ end
+
+ it "matches literal ? or * in path when pattern includes \\? or \\*" do
+ File.send(@method, '\?', '?').should == true
+ File.send(@method, '\?', 'a').should == false
+
+ File.send(@method, '\*', '*').should == true
+ File.send(@method, '\*', 'a').should == false
+ end
+
+ it "matches literal character (e.g. 'a') in path when pattern includes escaped character (e.g. \\a)" do
+ File.send(@method, '\a', 'a').should == true
+ File.send(@method, 'this\b', 'thisb').should == true
+ end
+
+ it "matches '\\' characters in path when flags includes FNM_NOESACPE" do
+ File.send(@method, '\a', '\a', File::FNM_NOESCAPE).should == true
+ File.send(@method, '\a', 'a', File::FNM_NOESCAPE).should == false
+ File.send(@method, '\[foo\]\[bar\]', '[foo][bar]', File::FNM_NOESCAPE).should == false
+ end
+
+ it "escapes special characters inside bracket expression" do
+ File.send(@method, '[\?]', '?').should == true
+ File.send(@method, '[\*]', '*').should == true
+ 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
+ end
+
+ it "matches patterns with leading periods to dotfiles by default" do
+ File.send(@method, '.*', '.profile').should == true
+ File.send(@method, ".*file", "nondotfile").should == false
+ end
+
+ it "matches leading periods in filenames when flags includes FNM_DOTMATCH" do
+ File.send(@method, '*', '.profile', File::FNM_DOTMATCH).should == true
+ File.send(@method, '*', 'home/.profile', File::FNM_DOTMATCH).should == true
+ end
+
+ it "matches multiple directories with ** and *" do
+ files = '**/*.rb'
+ File.send(@method, files, 'main.rb').should == false
+ File.send(@method, files, './main.rb').should == false
+ File.send(@method, files, 'lib/song.rb').should == true
+ File.send(@method, '**.rb', 'main.rb').should == true
+ File.send(@method, '**.rb', './main.rb').should == false
+ File.send(@method, '**.rb', 'lib/song.rb').should == true
+ File.send(@method, '*', 'dave/.profile').should == true
+ end
+
+ it "matches multiple directories with ** when flags includes File::FNM_PATHNAME" do
+ files = '**/*.rb'
+ flags = File::FNM_PATHNAME
+
+ File.send(@method, files, 'main.rb', flags).should == true
+ File.send(@method, files, 'one/two/three/main.rb', flags).should == true
+ File.send(@method, files, './main.rb', flags).should == false
+
+ flags = File::FNM_PATHNAME | File::FNM_DOTMATCH
+
+ File.send(@method, files, './main.rb', flags).should == true
+ File.send(@method, files, 'one/two/.main.rb', flags).should == true
+
+ File.send(@method, "**/best/*", 'lib/my/best/song.rb').should == true
+ end
+
+ 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
+
+ pattern = '**/foo'
+ File.send(@method, pattern, 'a/.b/c/foo', File::FNM_PATHNAME).should be_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
+
+ 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
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.send(@method, '\*', mock_to_path('a')).should == false
+ end
+
+ it "raises a TypeError if the first and second arguments are not string-like" do
+ lambda { File.send(@method, nil, nil, 0, 0) }.should raise_error(ArgumentError)
+ lambda { File.send(@method, 1, 'some/thing') }.should raise_error(TypeError)
+ lambda { File.send(@method, 'some/thing', 1) }.should raise_error(TypeError)
+ lambda { File.send(@method, 1, 1) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the third argument is not an Integer" do
+ lambda { File.send(@method, "*/place", "path/to/file", "flags") }.should raise_error(TypeError)
+ lambda { File.send(@method, "*/place", "path/to/file", nil) }.should raise_error(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)
+ lambda { File.send(@method, "*/place", "path/to/file", flags) }.should_not raise_error
+ end
+
+ it "matches multibyte characters" do
+ File.fnmatch("*/ä/ø/ñ", "a/ä/ø/ñ").should == true
+ end
+end
diff --git a/spec/ruby/core/file/shared/open.rb b/spec/ruby/core/file/shared/open.rb
new file mode 100644
index 0000000000..0ca1bc74db
--- /dev/null
+++ b/spec/ruby/core/file/shared/open.rb
@@ -0,0 +1,12 @@
+require File.expand_path('../../../dir/fixtures/common', __FILE__)
+
+describe :open_directory, shared: true do
+ it "opens directories" do
+ file = File.send(@method, tmp(""))
+ begin
+ file.should be_kind_of(File)
+ ensure
+ file.close
+ end
+ end
+end
diff --git a/spec/ruby/core/file/shared/read.rb b/spec/ruby/core/file/shared/read.rb
new file mode 100644
index 0000000000..916a6222bf
--- /dev/null
+++ b/spec/ruby/core/file/shared/read.rb
@@ -0,0 +1,15 @@
+require File.expand_path('../../../dir/fixtures/common', __FILE__)
+
+describe :file_read_directory, shared: true do
+ platform_is :darwin, :linux, :windows do
+ it "raises an Errno::EISDIR when passed a path that is a directory" do
+ lambda { @object.send(@method, ".") }.should raise_error(Errno::EISDIR)
+ end
+ end
+
+ platform_is :bsd do
+ it "does not raises any exception when passed a path that is a directory" do
+ lambda { @object.send(@method, ".") }.should_not raise_error
+ end
+ end
+end
diff --git a/spec/ruby/core/file/shared/stat.rb b/spec/ruby/core/file/shared/stat.rb
new file mode 100644
index 0000000000..aac710dd2f
--- /dev/null
+++ b/spec/ruby/core/file/shared/stat.rb
@@ -0,0 +1,32 @@
+describe :file_stat, shared: true do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file)
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ 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)
+ 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)
+ end
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.send(@method, mock_to_path(@file))
+ end
+
+ it "raises an Errno::ENOENT if the file does not exist" do
+ lambda {
+ File.send(@method, "fake_file")
+ }.should raise_error(Errno::ENOENT)
+ end
+end
diff --git a/spec/ruby/core/file/shared/unlink.rb b/spec/ruby/core/file/shared/unlink.rb
new file mode 100644
index 0000000000..7b0413b76b
--- /dev/null
+++ b/spec/ruby/core/file/shared/unlink.rb
@@ -0,0 +1,63 @@
+describe :file_unlink, shared: true do
+ before :each do
+ @file1 = tmp('test.txt')
+ @file2 = tmp('test2.txt')
+
+ touch @file1
+ touch @file2
+ end
+
+ after :each do
+ File.send(@method, @file1) if File.exist?(@file1)
+ File.send(@method, @file2) if File.exist?(@file2)
+
+ @file1 = nil
+ @file2 = nil
+ end
+
+ it "returns 0 when called without arguments" do
+ File.send(@method).should == 0
+ end
+
+ it "deletes a single file" do
+ File.send(@method, @file1).should == 1
+ File.exist?(@file1).should == false
+ end
+
+ it "deletes multiple files" do
+ File.send(@method, @file1, @file2).should == 2
+ File.exist?(@file1).should == false
+ File.exist?(@file2).should == false
+ end
+
+ it "raises a TypeError if not passed a String type" do
+ lambda { File.send(@method, 1) }.should raise_error(TypeError)
+ end
+
+ it "raises an Errno::ENOENT when the given file doesn't exist" do
+ lambda { File.send(@method, 'bogus') }.should raise_error(Errno::ENOENT)
+ end
+
+ it "coerces a given parameter into a string if possible" do
+ mock = mock("to_str")
+ mock.should_receive(:to_str).and_return(@file1)
+ File.send(@method, mock).should == 1
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.send(@method, mock_to_path(@file1)).should == 1
+ end
+
+ ruby_version_is "2.3" do
+ platform_is :windows do
+ it "allows deleting an open file with File::SHARE_DELETE" do
+ path = tmp("share_delete.txt")
+ File.open(path, mode: File::CREAT | File::WRONLY | File::BINARY | File::SHARE_DELETE) do |f|
+ File.exist?(path).should be_true
+ File.send(@method, path)
+ end
+ File.exist?(path).should be_false
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/size_spec.rb b/spec/ruby/core/file/size_spec.rb
new file mode 100644
index 0000000000..73c8192b18
--- /dev/null
+++ b/spec/ruby/core/file/size_spec.rb
@@ -0,0 +1,119 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/size', __FILE__)
+
+describe "File.size?" do
+ it_behaves_like :file_size, :size?, File
+end
+
+describe "File.size?" do
+ it_behaves_like :file_size_to_io, :size?, File
+end
+
+describe "File.size?" do
+ it_behaves_like :file_size_nil_when_missing, :size?, File
+end
+
+describe "File.size?" do
+ it_behaves_like :file_size_nil_when_empty, :size?, File
+end
+
+describe "File.size?" do
+ it_behaves_like :file_size_with_file_argument, :size?, File
+end
+
+describe "File.size" do
+ it_behaves_like :file_size, :size, File
+end
+
+describe "File.size" do
+ it_behaves_like :file_size_to_io, :size, File
+end
+
+describe "File.size" do
+ it_behaves_like :file_size_raise_when_missing, :size, File
+end
+
+describe "File.size" do
+ it_behaves_like :file_size_0_when_empty, :size, File
+end
+
+describe "File.size" do
+ it_behaves_like :file_size_with_file_argument, :size, File
+end
+
+describe "File#size" do
+
+ before :each do
+ @name = tmp('i_exist')
+ touch(@name) { |f| f.write 'rubinius' }
+ @file = File.new @name
+ @file_org = @file
+ end
+
+ after :each do
+ @file_org.close unless @file_org.closed?
+ rm_r @name
+ end
+
+ it "is an instance method" do
+ @file.respond_to?(:size).should be_true
+ end
+
+ it "returns the file's size as a Fixnum" do
+ @file.size.should be_an_instance_of(Fixnum)
+ end
+
+ it "returns the file's size in bytes" do
+ @file.size.should == 8
+ end
+
+ platform_is_not :windows do # impossible to remove opened file on Windows
+ it "returns the cached size of the file if subsequently deleted" do
+ rm_r @file.path
+ @file.size.should == 8
+ end
+ end
+
+ it "returns the file's current size even if modified" do
+ File.open(@file.path,'a') {|f| f.write '!'}
+ @file.size.should == 9
+ end
+
+ it "raises an IOError on a closed file" do
+ @file.close
+ lambda { @file.size }.should raise_error(IOError)
+ end
+
+ platform_is_not :windows do
+ it "follows symlinks if necessary" do
+ ln_file = tmp('i_exist_ln')
+ rm_r ln_file
+
+ begin
+ File.symlink(@file.path, ln_file).should == 0
+ file = File.new(ln_file)
+ file.size.should == 8
+ ensure
+ file.close if file && !file.closed?
+ rm_r ln_file
+ end
+ end
+ end
+end
+
+describe "File#size for an empty file" do
+ before :each do
+ @name = tmp('empty')
+ touch(@name)
+ @file = File.new @name
+ end
+
+ after :each do
+ @file.close unless @file.closed?
+ rm_r @name
+ end
+
+ it "returns 0" do
+ @file.size.should == 0
+ end
+end
diff --git a/spec/ruby/core/file/socket_spec.rb b/spec/ruby/core/file/socket_spec.rb
new file mode 100644
index 0000000000..80f33f4b19
--- /dev/null
+++ b/spec/ruby/core/file/socket_spec.rb
@@ -0,0 +1,42 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/socket', __FILE__)
+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/split_spec.rb b/spec/ruby/core/file/split_spec.rb
new file mode 100644
index 0000000000..2479d4b949
--- /dev/null
+++ b/spec/ruby/core/file/split_spec.rb
@@ -0,0 +1,63 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File.split" do
+ before :each do
+ @backslash_ext = "C:\\foo\\bar\\baz.rb"
+ @backslash = "C:\\foo\\bar\\baz"
+ end
+
+ it "splits the string at the last '/' when the last component does not have an extension" do
+ File.split("/foo/bar/baz").should == ["/foo/bar", "baz"]
+ File.split("C:/foo/bar/baz").should == ["C:/foo/bar", "baz"]
+ end
+
+ it "splits the string at the last '/' when the last component has an extension" do
+ File.split("/foo/bar/baz.rb").should == ["/foo/bar", "baz.rb"]
+ File.split("C:/foo/bar/baz.rb").should == ["C:/foo/bar", "baz.rb"]
+ end
+
+ it "splits an empty string into a '.' and an empty string" do
+ File.split("").should == [".", ""]
+ end
+
+ platform_is_not :windows do
+ it "collapses multiple '/' characters and strips trailing ones" do
+ File.split("//foo////").should == ["/", "foo"]
+ end
+ end
+
+ platform_is_not :windows do
+ it "does not split a string that contains '\\'" do
+ File.split(@backslash).should == [".", "C:\\foo\\bar\\baz"]
+ File.split(@backslash_ext).should == [".", "C:\\foo\\bar\\baz.rb"]
+ end
+ end
+
+ platform_is :windows do
+ it "splits the string at the last '\\' when the last component does not have an extension" do
+ File.split(@backslash).should == ["C:\\foo\\bar", "baz"]
+ end
+
+ it "splits the string at the last '\\' when the last component has an extension" do
+ File.split(@backslash_ext).should == ["C:\\foo\\bar", "baz.rb"]
+ end
+ end
+
+ it "raises an ArgumentError when not passed a single argument" do
+ lambda { File.split }.should raise_error(ArgumentError)
+ lambda { File.split('string', 'another string') }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if the argument is not a String type" do
+ lambda { File.split(1) }.should raise_error(TypeError)
+ end
+
+ it "coerces the argument with to_str if it is not a String type" do
+ class C; def to_str; "/rubinius/better/than/ruby"; end; end
+ File.split(C.new).should == ["/rubinius/better/than", "ruby"]
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.split(mock_to_path("")).should == [".", ""]
+ end
+end
diff --git a/spec/ruby/core/file/stat/atime_spec.rb b/spec/ruby/core/file/stat/atime_spec.rb
new file mode 100644
index 0000000000..575c98ce44
--- /dev/null
+++ b/spec/ruby/core/file/stat/atime_spec.rb
@@ -0,0 +1,18 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe "File::Stat#atime" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the atime of a File::Stat object" do
+ st = File.stat(@file)
+ st.atime.should be_kind_of(Time)
+ st.atime.should <= Time.now
+ end
+end
diff --git a/spec/ruby/core/file/stat/birthtime_spec.rb b/spec/ruby/core/file/stat/birthtime_spec.rb
new file mode 100644
index 0000000000..c2ccc319f1
--- /dev/null
+++ b/spec/ruby/core/file/stat/birthtime_spec.rb
@@ -0,0 +1,27 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+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)
+ lambda { st.birthtime }.should raise_error(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/blksize_spec.rb b/spec/ruby/core/file/stat/blksize_spec.rb
new file mode 100644
index 0000000000..4399e6b4bb
--- /dev/null
+++ b/spec/ruby/core/file/stat/blksize_spec.rb
@@ -0,0 +1,27 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe "File::Stat#blksize" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ platform_is_not :windows do
+ it "returns the blksize of a File::Stat object" do
+ st = File.stat(@file)
+ st.blksize.is_a?(Integer).should == true
+ st.blksize.should > 0
+ end
+ end
+
+ platform_is :windows do
+ it "returns nil" do
+ st = File.stat(@file)
+ st.blksize.should == nil
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/blockdev_spec.rb b/spec/ruby/core/file/stat/blockdev_spec.rb
new file mode 100644
index 0000000000..440291f130
--- /dev/null
+++ b/spec/ruby/core/file/stat/blockdev_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/blockdev', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#blockdev?" do
+ it_behaves_like :file_blockdev, :blockdev?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/blocks_spec.rb b/spec/ruby/core/file/stat/blocks_spec.rb
new file mode 100644
index 0000000000..ca0fd2c8a6
--- /dev/null
+++ b/spec/ruby/core/file/stat/blocks_spec.rb
@@ -0,0 +1,27 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe "File::Stat#blocks" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ platform_is_not :windows do
+ it "returns the blocks of a File::Stat object" do
+ st = File.stat(@file)
+ st.blocks.is_a?(Integer).should == true
+ st.blocks.should > 0
+ end
+ end
+
+ platform_is :windows do
+ it "returns nil" do
+ st = File.stat(@file)
+ st.blocks.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/chardev_spec.rb b/spec/ruby/core/file/stat/chardev_spec.rb
new file mode 100644
index 0000000000..25c8c877f7
--- /dev/null
+++ b/spec/ruby/core/file/stat/chardev_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/chardev', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#chardev?" do
+ it_behaves_like :file_chardev, :chardev?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/comparison_spec.rb b/spec/ruby/core/file/stat/comparison_spec.rb
new file mode 100644
index 0000000000..a70a083ab2
--- /dev/null
+++ b/spec/ruby/core/file/stat/comparison_spec.rb
@@ -0,0 +1,66 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe "File::Stat#<=>" do
+ before :each do
+ @name1 = tmp("i_exist")
+ @name2 = tmp("i_exist_too")
+ touch @name1
+ touch @name2
+ end
+
+ after :each do
+ rm_r @name1, @name2
+ end
+
+ it "is able to compare files by the same modification times" do
+ now = Time.now - 1 # 1 second ago to avoid NFS cache issue
+ File.utime(now, now, @name1)
+ File.utime(now, now, @name2)
+
+ File.open(@name1) { |file1|
+ File.open(@name2) { |file2|
+ (file1.stat <=> file2.stat).should == 0
+ }
+ }
+ end
+
+ it "is able to compare files by different modification times" do
+ now = Time.now
+ File.utime(now, now + 100, @name2)
+
+ File.open(@name1) { |file1|
+ File.open(@name2) { |file2|
+ (file1.stat <=> file2.stat).should == -1
+ }
+ }
+
+ File.utime(now, now - 100, @name2)
+
+ File.open(@name1) { |file1|
+ File.open(@name2) { |file2|
+ (file1.stat <=> file2.stat).should == 1
+ }
+ }
+ end
+
+ # TODO: Fix
+ it "includes Comparable and #== shows mtime equality between two File::Stat objects" do
+ File.open(@name1) { |file1|
+ File.open(@name2) { |file2|
+ (file1.stat == file1.stat).should == true
+ (file2.stat == file2.stat).should == true
+ }
+ }
+
+ now = Time.now
+ File.utime(now, now + 100, @name2)
+
+ File.open(@name1) { |file1|
+ File.open(@name2) { |file2|
+ (file1.stat == file2.stat).should == false
+ (file1.stat == file1.stat).should == true
+ (file2.stat == file2.stat).should == true
+ }
+ }
+ end
+end
diff --git a/spec/ruby/core/file/stat/ctime_spec.rb b/spec/ruby/core/file/stat/ctime_spec.rb
new file mode 100644
index 0000000000..2f82dfdab6
--- /dev/null
+++ b/spec/ruby/core/file/stat/ctime_spec.rb
@@ -0,0 +1,18 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe "File::Stat#ctime" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the ctime of a File::Stat object" do
+ st = File.stat(@file)
+ st.ctime.should be_kind_of(Time)
+ st.ctime.should <= Time.now
+ end
+end
diff --git a/spec/ruby/core/file/stat/dev_major_spec.rb b/spec/ruby/core/file/stat/dev_major_spec.rb
new file mode 100644
index 0000000000..0b00fc4d36
--- /dev/null
+++ b/spec/ruby/core/file/stat/dev_major_spec.rb
@@ -0,0 +1,23 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe "File::Stat#dev_major" do
+ before :each do
+ @name = tmp("file.txt")
+ touch(@name)
+ end
+ after :each do
+ rm_r @name
+ end
+
+ platform_is_not :windows do
+ it "returns the major part of File::Stat#dev" do
+ File.stat(@name).dev_major.should be_kind_of(Integer)
+ end
+ end
+
+ platform_is :windows do
+ it "returns nil" do
+ File.stat(@name).dev_major.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/dev_minor_spec.rb b/spec/ruby/core/file/stat/dev_minor_spec.rb
new file mode 100644
index 0000000000..0475e3be81
--- /dev/null
+++ b/spec/ruby/core/file/stat/dev_minor_spec.rb
@@ -0,0 +1,23 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe "File::Stat#dev_minor" do
+ before :each do
+ @name = tmp("file.txt")
+ touch(@name)
+ end
+ after :each do
+ rm_r @name
+ end
+
+ platform_is_not :windows do
+ it "returns the minor part of File::Stat#dev" do
+ File.stat(@name).dev_minor.should be_kind_of(Integer)
+ end
+ end
+
+ platform_is :windows do
+ it "returns nil" do
+ File.stat(@name).dev_minor.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/dev_spec.rb b/spec/ruby/core/file/stat/dev_spec.rb
new file mode 100644
index 0000000000..3cdc704fd7
--- /dev/null
+++ b/spec/ruby/core/file/stat/dev_spec.rb
@@ -0,0 +1,15 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe "File::Stat#dev" do
+ before :each do
+ @name = tmp("file.txt")
+ touch(@name)
+ end
+ after :each do
+ rm_r @name
+ end
+
+ it "returns the number of the device on which the file exists" do
+ File.stat(@name).dev.should be_kind_of(Integer)
+ end
+end
diff --git a/spec/ruby/core/file/stat/directory_spec.rb b/spec/ruby/core/file/stat/directory_spec.rb
new file mode 100644
index 0000000000..5ead2dca49
--- /dev/null
+++ b/spec/ruby/core/file/stat/directory_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/directory', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#directory?" do
+ it_behaves_like :file_directory, :directory?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/executable_real_spec.rb b/spec/ruby/core/file/stat/executable_real_spec.rb
new file mode 100644
index 0000000000..11de0a5b39
--- /dev/null
+++ b/spec/ruby/core/file/stat/executable_real_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/executable_real', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#executable_real?" do
+ it_behaves_like :file_executable_real, :executable_real?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/executable_spec.rb b/spec/ruby/core/file/stat/executable_spec.rb
new file mode 100644
index 0000000000..e3b1093056
--- /dev/null
+++ b/spec/ruby/core/file/stat/executable_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/executable', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#executable?" do
+ it_behaves_like :file_executable, :executable?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/file_spec.rb b/spec/ruby/core/file/stat/file_spec.rb
new file mode 100644
index 0000000000..da79dddb00
--- /dev/null
+++ b/spec/ruby/core/file/stat/file_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/file', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#file?" do
+ it_behaves_like :file_file, :file?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/fixtures/classes.rb b/spec/ruby/core/file/stat/fixtures/classes.rb
new file mode 100644
index 0000000000..4fe9a2a30f
--- /dev/null
+++ b/spec/ruby/core/file/stat/fixtures/classes.rb
@@ -0,0 +1,5 @@
+class FileStat
+ def self.method_missing(meth, file)
+ File.lstat(file).send(meth)
+ end
+end
diff --git a/spec/ruby/core/file/stat/ftype_spec.rb b/spec/ruby/core/file/stat/ftype_spec.rb
new file mode 100644
index 0000000000..588c371c39
--- /dev/null
+++ b/spec/ruby/core/file/stat/ftype_spec.rb
@@ -0,0 +1,68 @@
+require "#{File.dirname(__FILE__)}/../../../spec_helper"
+require "#{File.dirname(__FILE__)}/../fixtures/file_types"
+
+describe "File::Stat#ftype" do
+ before :all do
+ FileSpecs.configure_types
+ end
+
+ it "returns a String" do
+ FileSpecs.normal_file do |file|
+ File.lstat(file).ftype.should be_kind_of(String)
+ end
+ end
+
+ it "returns 'file' when the file is a file" do
+ FileSpecs.normal_file do |file|
+ File.lstat(file).ftype.should == 'file'
+ end
+ end
+
+ it "returns 'directory' when the file is a dir" do
+ FileSpecs.directory do |dir|
+ File.lstat(dir).ftype.should == 'directory'
+ end
+ end
+
+ platform_is_not :windows do
+ it "returns 'characterSpecial' when the file is a char" do
+ FileSpecs.character_device do |char|
+ File.lstat(char).ftype.should == 'characterSpecial'
+ end
+ end
+ end
+
+ platform_is_not :freebsd do # FreeBSD does not have block devices
+ with_block_device do
+ it "returns 'blockSpecial' when the file is a block" do
+ FileSpecs.block_device do |block|
+ File.lstat(block).ftype.should == 'blockSpecial'
+ end
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ it "returns 'link' when the file is a link" do
+ FileSpecs.symlink do |link|
+ File.lstat(link).ftype.should == 'link'
+ end
+ end
+
+ it "returns fifo when the file is a fifo" do
+ FileSpecs.fifo do |fifo|
+ File.lstat(fifo).ftype.should == 'fifo'
+ end
+ end
+
+ # This will silently not execute the block if no socket
+ # can be found. However, if you are running X, there is
+ # a good chance that if nothing else, at least the X
+ # Server socket exists.
+ it "returns 'socket' when the file is a socket" do
+ FileSpecs.socket do |socket|
+ File.lstat(socket).ftype.should == 'socket'
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/gid_spec.rb b/spec/ruby/core/file/stat/gid_spec.rb
new file mode 100644
index 0000000000..27356b6401
--- /dev/null
+++ b/spec/ruby/core/file/stat/gid_spec.rb
@@ -0,0 +1,19 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe "File::Stat#gid" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ File.chown(nil, Process.gid, @file)
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the group owner attribute of a File::Stat object" do
+ st = File.stat(@file)
+ st.gid.is_a?(Integer).should == true
+ st.gid.should == Process.gid
+ end
+end
diff --git a/spec/ruby/core/file/stat/grpowned_spec.rb b/spec/ruby/core/file/stat/grpowned_spec.rb
new file mode 100644
index 0000000000..07a52876d0
--- /dev/null
+++ b/spec/ruby/core/file/stat/grpowned_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/grpowned', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#grpowned?" do
+ it_behaves_like :file_grpowned, :grpowned?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/ino_spec.rb b/spec/ruby/core/file/stat/ino_spec.rb
new file mode 100644
index 0000000000..0339dee54f
--- /dev/null
+++ b/spec/ruby/core/file/stat/ino_spec.rb
@@ -0,0 +1,38 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe "File::Stat#ino" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ platform_is_not :windows do
+ it "returns the ino of a File::Stat object" do
+ st = File.stat(@file)
+ st.ino.should be_kind_of(Integer)
+ st.ino.should > 0
+ end
+ end
+
+ platform_is :windows do
+ ruby_version_is ""..."2.3" do
+ it "returns 0" do
+ st = File.stat(@file)
+ st.ino.should be_kind_of(Integer)
+ st.ino.should == 0
+ end
+ end
+
+ ruby_version_is "2.3" do
+ it "returns BY_HANDLE_FILE_INFORMATION.nFileIndexHigh/Low of a File::Stat object" do
+ st = File.stat(@file)
+ st.ino.should be_kind_of(Integer)
+ st.ino.should > 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/inspect_spec.rb b/spec/ruby/core/file/stat/inspect_spec.rb
new file mode 100644
index 0000000000..dd2ad21da3
--- /dev/null
+++ b/spec/ruby/core/file/stat/inspect_spec.rb
@@ -0,0 +1,26 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe "File::Stat#inspect" do
+
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "produces a nicely formatted description of a File::Stat object" do
+ st = File.stat(@file)
+ expected = "#<File::Stat dev=0x#{st.dev.to_s(16)}, ino=#{st.ino}, mode=#{sprintf("%07o", st.mode)}, nlink=#{st.nlink}"
+ expected << ", uid=#{st.uid}, gid=#{st.gid}, rdev=0x#{st.rdev.to_s(16)}, size=#{st.size}, blksize=#{st.blksize.inspect}"
+ expected << ", blocks=#{st.blocks.inspect}, atime=#{st.atime}, mtime=#{st.mtime}, ctime=#{st.ctime}"
+ platform_is :bsd, :darwin do
+ # Windows has File.birthtime but it's not here since already shown by ctime.
+ expected << ", birthtime=#{st.birthtime}"
+ end
+ expected << ">"
+ st.inspect.should == expected
+ end
+end
diff --git a/spec/ruby/core/file/stat/mode_spec.rb b/spec/ruby/core/file/stat/mode_spec.rb
new file mode 100644
index 0000000000..1c895bf0ce
--- /dev/null
+++ b/spec/ruby/core/file/stat/mode_spec.rb
@@ -0,0 +1,19 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe "File::Stat#mode" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ File.chmod(0644, @file)
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the mode of a File::Stat object" do
+ st = File.stat(@file)
+ st.mode.is_a?(Integer).should == true
+ (st.mode & 0777).should == 0644
+ end
+end
diff --git a/spec/ruby/core/file/stat/mtime_spec.rb b/spec/ruby/core/file/stat/mtime_spec.rb
new file mode 100644
index 0000000000..9dd20dfd65
--- /dev/null
+++ b/spec/ruby/core/file/stat/mtime_spec.rb
@@ -0,0 +1,18 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe "File::Stat#mtime" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the mtime of a File::Stat object" do
+ st = File.stat(@file)
+ st.mtime.should be_kind_of(Time)
+ st.mtime.should <= Time.now
+ end
+end
diff --git a/spec/ruby/core/file/stat/new_spec.rb b/spec/ruby/core/file/stat/new_spec.rb
new file mode 100644
index 0000000000..ec7d81362f
--- /dev/null
+++ b/spec/ruby/core/file/stat/new_spec.rb
@@ -0,0 +1,30 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe "File::Stat#initialize" do
+
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ File.chmod(0755, @file)
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "raises an exception if the file doesn't exist" do
+ lambda { File::Stat.new(tmp("i_am_a_dummy_file_that_doesnt_exist")) }.should raise_error
+ end
+
+ it "creates a File::Stat object for the given file" do
+ st = File::Stat.new(@file)
+ st.should be_kind_of(File::Stat)
+ st.ftype.should == 'file'
+ end
+
+ it "calls #to_path on non-String arguments" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return @file
+ File::Stat.new p
+ end
+end
diff --git a/spec/ruby/core/file/stat/nlink_spec.rb b/spec/ruby/core/file/stat/nlink_spec.rb
new file mode 100644
index 0000000000..e857b07fd1
--- /dev/null
+++ b/spec/ruby/core/file/stat/nlink_spec.rb
@@ -0,0 +1,21 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe "File::Stat#nlink" do
+ before :each do
+ @file = tmp("stat_nlink")
+ @link = @file + ".lnk"
+ touch @file
+ end
+
+ after :each do
+ rm_r @link, @file
+ end
+
+ platform_is_not :windows do
+ it "returns the number of links to a file" do
+ File::Stat.new(@file).nlink.should == 1
+ File.link(@file, @link)
+ File::Stat.new(@file).nlink.should == 2
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/owned_spec.rb b/spec/ruby/core/file/stat/owned_spec.rb
new file mode 100644
index 0000000000..4c4d843bbe
--- /dev/null
+++ b/spec/ruby/core/file/stat/owned_spec.rb
@@ -0,0 +1,31 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/owned', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#owned?" do
+ it_behaves_like :file_owned, :owned?, FileStat
+end
+
+describe "File::Stat#owned?" do
+ before :each do
+ @file = tmp("i_exist")
+ touch(@file)
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns true if the file is owned by the user" do
+ st = File.stat(@file)
+ st.owned?.should == true
+ end
+
+ platform_is_not :windows do
+ it "returns false if the file is not owned by the user" do
+ system_file = '/etc/passwd'
+ st = File.stat(system_file)
+ st.owned?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/pipe_spec.rb b/spec/ruby/core/file/stat/pipe_spec.rb
new file mode 100644
index 0000000000..e4c0b559bb
--- /dev/null
+++ b/spec/ruby/core/file/stat/pipe_spec.rb
@@ -0,0 +1,32 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/pipe', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#pipe?" do
+ it_behaves_like :file_pipe, :pipe?, FileStat
+end
+
+describe "File::Stat#pipe?" do
+ it "returns false if the file is not a pipe" do
+ filename = tmp("i_exist")
+ touch(filename)
+
+ st = File.stat(filename)
+ st.pipe?.should == false
+
+ rm_r filename
+ end
+
+ platform_is_not :windows do
+ it "returns true if the file is a pipe" do
+ filename = tmp("i_am_a_pipe")
+ system "mkfifo #{filename}"
+
+ st = File.stat(filename)
+ st.pipe?.should == true
+
+ rm_r filename
+ 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
new file mode 100644
index 0000000000..f9d514fbc0
--- /dev/null
+++ b/spec/ruby/core/file/stat/rdev_major_spec.rb
@@ -0,0 +1,31 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+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
+ end
+
+ after :each do
+ platform_is_not :solaris do
+ rm_r @name
+ end
+ end
+
+ platform_is_not :windows do
+ it "returns the major part of File::Stat#rdev" do
+ File.stat(@name).rdev_major.should be_kind_of(Integer)
+ end
+ end
+
+ platform_is :windows do
+ it "returns nil" do
+ File.stat(@name).rdev_major.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/rdev_minor_spec.rb b/spec/ruby/core/file/stat/rdev_minor_spec.rb
new file mode 100644
index 0000000000..67399c5e68
--- /dev/null
+++ b/spec/ruby/core/file/stat/rdev_minor_spec.rb
@@ -0,0 +1,31 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+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
+ end
+
+ after :each do
+ platform_is_not :solaris do
+ rm_r @name
+ end
+ end
+
+ platform_is_not :windows do
+ it "returns the minor part of File::Stat#rdev" do
+ File.stat(@name).rdev_minor.should be_kind_of(Integer)
+ end
+ end
+
+ platform_is :windows do
+ it "returns nil" do
+ File.stat(@name).rdev_minor.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/file/stat/rdev_spec.rb b/spec/ruby/core/file/stat/rdev_spec.rb
new file mode 100644
index 0000000000..12f97fb044
--- /dev/null
+++ b/spec/ruby/core/file/stat/rdev_spec.rb
@@ -0,0 +1,15 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe "File::Stat#rdev" do
+ before :each do
+ @name = tmp("file.txt")
+ touch(@name)
+ end
+ after :each do
+ rm_r @name
+ end
+
+ it "returns the number of the device this file represents which the file exists" do
+ File.stat(@name).rdev.should be_kind_of(Integer)
+ end
+end
diff --git a/spec/ruby/core/file/stat/readable_real_spec.rb b/spec/ruby/core/file/stat/readable_real_spec.rb
new file mode 100644
index 0000000000..49412f1df2
--- /dev/null
+++ b/spec/ruby/core/file/stat/readable_real_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/readable_real', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#readable_real?" do
+ it_behaves_like :file_readable_real, :readable_real?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/readable_spec.rb b/spec/ruby/core/file/stat/readable_spec.rb
new file mode 100644
index 0000000000..3d81975309
--- /dev/null
+++ b/spec/ruby/core/file/stat/readable_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/readable', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#readable?" do
+ it_behaves_like :file_readable, :readable?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/setgid_spec.rb b/spec/ruby/core/file/stat/setgid_spec.rb
new file mode 100644
index 0000000000..318a72b437
--- /dev/null
+++ b/spec/ruby/core/file/stat/setgid_spec.rb
@@ -0,0 +1,11 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/setgid', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#setgid?" do
+ it_behaves_like :file_setgid, :setgid?, FileStat
+end
+
+describe "File::Stat#setgid?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/stat/setuid_spec.rb b/spec/ruby/core/file/stat/setuid_spec.rb
new file mode 100644
index 0000000000..5057af0ccc
--- /dev/null
+++ b/spec/ruby/core/file/stat/setuid_spec.rb
@@ -0,0 +1,11 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/setuid', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#setuid?" do
+ it_behaves_like :file_setuid, :setuid?, FileStat
+end
+
+describe "File::Stat#setuid?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/stat/size_spec.rb b/spec/ruby/core/file/stat/size_spec.rb
new file mode 100644
index 0000000000..84db12d591
--- /dev/null
+++ b/spec/ruby/core/file/stat/size_spec.rb
@@ -0,0 +1,21 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/size', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat.size?" do
+ it_behaves_like :file_size, :size?, FileStat
+ it_behaves_like :file_size_nil_when_empty, :size?, FileStat
+end
+
+describe "File::Stat.size" do
+ it_behaves_like :file_size, :size, FileStat
+ it_behaves_like :file_size_0_when_empty, :size, FileStat
+end
+
+describe "File::Stat#size" do
+ it "needs to be reviewed for spec completeness"
+end
+
+describe "File::Stat#size?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/stat/socket_spec.rb b/spec/ruby/core/file/stat/socket_spec.rb
new file mode 100644
index 0000000000..b25d9314f9
--- /dev/null
+++ b/spec/ruby/core/file/stat/socket_spec.rb
@@ -0,0 +1,11 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/socket', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#socket?" do
+ it_behaves_like :file_socket, :socket?, FileStat
+end
+
+describe "File::Stat#socket?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/stat/sticky_spec.rb b/spec/ruby/core/file/stat/sticky_spec.rb
new file mode 100644
index 0000000000..c2fefbe106
--- /dev/null
+++ b/spec/ruby/core/file/stat/sticky_spec.rb
@@ -0,0 +1,11 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/sticky', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#sticky?" do
+ it_behaves_like :file_sticky, :sticky?, FileStat
+end
+
+describe "File::Stat#sticky?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/stat/symlink_spec.rb b/spec/ruby/core/file/stat/symlink_spec.rb
new file mode 100644
index 0000000000..579c1de0ad
--- /dev/null
+++ b/spec/ruby/core/file/stat/symlink_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/symlink', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#symlink?" do
+ it_behaves_like :file_symlink, :symlink?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/uid_spec.rb b/spec/ruby/core/file/stat/uid_spec.rb
new file mode 100644
index 0000000000..75be97c234
--- /dev/null
+++ b/spec/ruby/core/file/stat/uid_spec.rb
@@ -0,0 +1,18 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe "File::Stat#uid" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns the owner attribute of a File::Stat object" do
+ st = File.stat(@file)
+ st.uid.is_a?(Integer).should == true
+ st.uid.should == Process.uid
+ end
+end
diff --git a/spec/ruby/core/file/stat/world_readable_spec.rb b/spec/ruby/core/file/stat/world_readable_spec.rb
new file mode 100644
index 0000000000..178e39a1ea
--- /dev/null
+++ b/spec/ruby/core/file/stat/world_readable_spec.rb
@@ -0,0 +1,11 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/world_readable', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat.world_readable?" do
+ it_behaves_like(:file_world_readable, :world_readable?, FileStat)
+end
+
+describe "File::Stat#world_readable?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/stat/world_writable_spec.rb b/spec/ruby/core/file/stat/world_writable_spec.rb
new file mode 100644
index 0000000000..73a7c6d3ed
--- /dev/null
+++ b/spec/ruby/core/file/stat/world_writable_spec.rb
@@ -0,0 +1,11 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/world_writable', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat.world_writable?" do
+ it_behaves_like(:file_world_writable, :world_writable?, FileStat)
+end
+
+describe "File::Stat#world_writable?" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/file/stat/writable_real_spec.rb b/spec/ruby/core/file/stat/writable_real_spec.rb
new file mode 100644
index 0000000000..e069db507b
--- /dev/null
+++ b/spec/ruby/core/file/stat/writable_real_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/writable_real', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#writable_real?" do
+ it_behaves_like :file_writable_real, :writable_real?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/writable_spec.rb b/spec/ruby/core/file/stat/writable_spec.rb
new file mode 100644
index 0000000000..b720e59f81
--- /dev/null
+++ b/spec/ruby/core/file/stat/writable_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/writable', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#writable?" do
+ it_behaves_like :file_writable, :writable?, FileStat
+end
diff --git a/spec/ruby/core/file/stat/zero_spec.rb b/spec/ruby/core/file/stat/zero_spec.rb
new file mode 100644
index 0000000000..127c706b90
--- /dev/null
+++ b/spec/ruby/core/file/stat/zero_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../../spec_helper', __FILE__)
+require File.expand_path('../../../../shared/file/zero', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "File::Stat#zero?" do
+ it_behaves_like :file_zero, :zero?, FileStat
+end
diff --git a/spec/ruby/core/file/stat_spec.rb b/spec/ruby/core/file/stat_spec.rb
new file mode 100644
index 0000000000..1ea003142e
--- /dev/null
+++ b/spec/ruby/core/file/stat_spec.rb
@@ -0,0 +1,45 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../shared/stat', __FILE__)
+
+describe "File.stat" do
+ it_behaves_like :file_stat, :stat
+end
+
+platform_is_not :windows do
+ describe "File.stat" do
+ before :each do
+ @file = tmp('i_exist')
+ @link = tmp('i_am_a_symlink')
+ touch(@file) { |f| f.write "rubinius" }
+ end
+
+ after :each do
+ rm_r @link, @file
+ end
+
+ it "returns information for a file that has been deleted but is still open" do
+ File.open(@file) do |f|
+ rm_r @file
+
+ st = f.stat
+
+ st.file?.should == true
+ st.zero?.should == false
+ st.size.should == 8
+ st.size?.should == 8
+ st.blksize.should >= 0
+ st.atime.should be_kind_of(Time)
+ st.ctime.should be_kind_of(Time)
+ st.mtime.should be_kind_of(Time)
+ end
+ end
+
+ it "returns a File::Stat object with file properties for a symlink" do
+ File.symlink(@file, @link)
+ st = File.stat(@link)
+
+ st.file?.should == true
+ st.symlink?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/file/sticky_spec.rb b/spec/ruby/core/file/sticky_spec.rb
new file mode 100644
index 0000000000..d01e2b6818
--- /dev/null
+++ b/spec/ruby/core/file/sticky_spec.rb
@@ -0,0 +1,50 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/sticky', __FILE__)
+
+describe "File.sticky?" do
+ it_behaves_like :file_sticky, :sticky?, File
+ it_behaves_like :file_sticky_missing, :sticky?, File
+end
+
+describe "File.sticky?" do
+ platform_is_not :windows do
+ it "returns false if file does not exist" do
+ File.sticky?("I_am_a_bogus_file").should == false
+ end
+
+ it "returns false if the file has not sticky bit set" do
+ filename = tmp("i_exist")
+ touch(filename)
+
+ File.sticky?(filename).should == false
+
+ rm_r filename
+ end
+ end
+
+ platform_is :linux, :darwin do
+ it "returns true if the file has sticky bit set" do
+ filename = tmp("i_exist")
+ touch(filename)
+ system "chmod +t #{filename}"
+
+ File.sticky?(filename).should == true
+
+ rm_r filename
+ end
+ end
+
+ platform_is :bsd do
+ # FreeBSD and NetBSD can't set stiky bit to a normal file
+ it "cannot set sticky bit to a normal file" do
+ filename = tmp("i_exist")
+ touch(filename)
+ stat = File.stat(filename)
+ mode = stat.mode
+ raise_error(Errno::EFTYPE){File.chmod(mode|01000, filename)}
+ File.sticky?(filename).should == false
+
+ rm_r filename
+ end
+ end
+end
diff --git a/spec/ruby/core/file/symlink_spec.rb b/spec/ruby/core/file/symlink_spec.rb
new file mode 100644
index 0000000000..2426b8c9a7
--- /dev/null
+++ b/spec/ruby/core/file/symlink_spec.rb
@@ -0,0 +1,57 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/symlink', __FILE__)
+
+describe "File.symlink" do
+ before :each do
+ @file = tmp("file_symlink.txt")
+ @link = tmp("file_symlink.lnk")
+
+ rm_r @link
+ touch @file
+ end
+
+ after :each do
+ rm_r @link, @file
+ end
+
+ platform_is_not :windows do
+ it "creates a symlink between a source and target file" do
+ File.symlink(@file, @link).should == 0
+ File.identical?(@file, @link).should == true
+ end
+
+ it "creates a symbolic link" do
+ File.symlink(@file, @link)
+ File.symlink?(@link).should == true
+ end
+
+ it "accepts args that have #to_path methods" do
+ File.symlink(mock_to_path(@file), mock_to_path(@link))
+ File.symlink?(@link).should == true
+ end
+
+ it "raises an Errno::EEXIST if the target already exists" do
+ File.symlink(@file, @link)
+ lambda { File.symlink(@file, @link) }.should raise_error(Errno::EEXIST)
+ end
+
+ it "raises an ArgumentError if not called with two arguments" do
+ lambda { File.symlink }.should raise_error(ArgumentError)
+ lambda { File.symlink(@file) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError if not called with String types" do
+ lambda { File.symlink(@file, nil) }.should raise_error(TypeError)
+ lambda { File.symlink(@file, 1) }.should raise_error(TypeError)
+ lambda { File.symlink(1, 1) }.should raise_error(TypeError)
+ end
+ end
+end
+
+describe "File.symlink?" do
+ it_behaves_like :file_symlink, :symlink?, File
+end
+
+describe "File.symlink?" do
+ it_behaves_like :file_symlink_nonexistent, :symlink?, File
+end
diff --git a/spec/ruby/core/file/to_path_spec.rb b/spec/ruby/core/file/to_path_spec.rb
new file mode 100644
index 0000000000..3dc801cc27
--- /dev/null
+++ b/spec/ruby/core/file/to_path_spec.rb
@@ -0,0 +1,49 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File#to_path" do
+ before :each do
+ @name = "file_to_path"
+ @path = tmp(@name)
+ touch @path
+ end
+
+ after :each do
+ @file.close if @file and !@file.closed?
+ rm_r @path
+ end
+
+ it "returns a String" do
+ @file = File.new @path
+ @file.to_path.should be_an_instance_of(String)
+ end
+
+ it "does not normalise the path it returns" do
+ Dir.chdir(tmp("")) do
+ unorm = "./#{@name}"
+ @file = File.new unorm
+ @file.to_path.should == unorm
+ end
+ end
+
+ it "does not canonicalize the path it returns" do
+ dir = File.basename tmp("")
+ path = "#{tmp("")}../#{dir}/#{@name}"
+ @file = File.new path
+ @file.to_path.should == path
+ end
+
+ it "does not absolute-ise the path it returns" do
+ Dir.chdir(tmp("")) do
+ @file = File.new @name
+ @file.to_path.should == @name
+ end
+ end
+
+ with_feature :encoding do
+ it "preserves the encoding of the path" do
+ path = @path.force_encoding("euc-jp")
+ @file = File.new path
+ @file.to_path.encoding.should == Encoding.find("euc-jp")
+ end
+ end
+end
diff --git a/spec/ruby/core/file/truncate_spec.rb b/spec/ruby/core/file/truncate_spec.rb
new file mode 100644
index 0000000000..a120c610b8
--- /dev/null
+++ b/spec/ruby/core/file/truncate_spec.rb
@@ -0,0 +1,177 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File.truncate" do
+ before :each do
+ @name = tmp("test.txt")
+ touch(@name) { |f| f.write("1234567890") }
+ end
+
+ after :each do
+ rm_r @name
+ end
+
+ it "truncates a file" do
+ File.size(@name).should == 10
+
+ File.truncate(@name, 5)
+ File.size(@name).should == 5
+
+ File.open(@name, "r") do |f|
+ f.read(99).should == "12345"
+ f.eof?.should == true
+ end
+ end
+
+ it "truncate a file size to 0" do
+ File.truncate(@name, 0).should == 0
+ IO.read(@name).should == ""
+ end
+
+ it "truncate a file size to 5" do
+ File.size(@name).should == 10
+ File.truncate(@name, 5)
+ File.size(@name).should == 5
+ IO.read(@name).should == "12345"
+ end
+
+ it "truncates to a larger file size than the original file" do
+ File.truncate(@name, 12)
+ File.size(@name).should == 12
+ IO.read(@name).should == "1234567890\000\000"
+ end
+
+ it "truncates to the same size as the original file" do
+ File.truncate(@name, File.size(@name))
+ File.size(@name).should == 10
+ IO.read(@name).should == "1234567890"
+ end
+
+ it "raises an Errno::ENOENT if the file does not exist" do
+ # TODO: missing_file
+ not_existing_file = tmp("file-does-not-exist-for-sure.txt")
+
+ # make sure it doesn't exist for real
+ rm_r not_existing_file
+
+ begin
+ lambda { File.truncate(not_existing_file, 5) }.should raise_error(Errno::ENOENT)
+ ensure
+ rm_r not_existing_file
+ end
+ end
+
+ it "raises an ArgumentError if not passed two arguments" do
+ lambda { File.truncate }.should raise_error(ArgumentError)
+ lambda { File.truncate(@name) }.should raise_error(ArgumentError)
+ end
+
+ platform_is_not :netbsd, :openbsd do
+ it "raises an Errno::EINVAL if the length argument is not valid" do
+ lambda { File.truncate(@name, -1) }.should raise_error(Errno::EINVAL) # May fail
+ end
+ end
+
+ it "raises a TypeError if not passed a String type for the first argument" do
+ lambda { File.truncate(1, 1) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if not passed an Integer type for the second argument" do
+ lambda { File.truncate(@name, nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.truncate(mock_to_path(@name), 0).should == 0
+ end
+end
+
+
+describe "File#truncate" do
+ before :each do
+ @name = tmp("test.txt")
+ @file = File.open @name, 'w'
+ @file.write "1234567890"
+ @file.flush
+ end
+
+ after :each do
+ @file.close unless @file.closed?
+ rm_r @name
+ end
+
+ it "does not move the file write pointer to the specified byte offset" do
+ @file.truncate(3)
+ @file.write "abc"
+ @file.close
+ File.read(@name).should == "123\x00\x00\x00\x00\x00\x00\x00abc"
+ end
+
+ it "does not move the file read pointer to the specified byte offset" do
+ File.open(@name, "r+") do |f|
+ f.read(1).should == "1"
+ f.truncate(0)
+ f.read(1).should == nil
+ end
+ end
+
+ it "truncates a file" do
+ File.size(@name).should == 10
+
+ @file.truncate(5)
+ File.size(@name).should == 5
+ File.open(@name, "r") do |f|
+ f.read(99).should == "12345"
+ f.eof?.should == true
+ end
+ end
+
+ it "truncates a file size to 0" do
+ @file.truncate(0).should == 0
+ IO.read(@name).should == ""
+ end
+
+ it "truncates a file size to 5" do
+ File.size(@name).should == 10
+ @file.truncate(5)
+ File.size(@name).should == 5
+ IO.read(@name).should == "12345"
+ end
+
+ it "truncates a file to a larger size than the original file" do
+ @file.truncate(12)
+ File.size(@name).should == 12
+ IO.read(@name).should == "1234567890\000\000"
+ end
+
+ it "truncates a file to the same size as the original file" do
+ @file.truncate(File.size(@name))
+ File.size(@name).should == 10
+ IO.read(@name).should == "1234567890"
+ end
+
+ it "raises an ArgumentError if not passed one argument" do
+ lambda { @file.truncate }.should raise_error(ArgumentError)
+ lambda { @file.truncate(1) }.should_not raise_error(ArgumentError)
+ end
+
+ platform_is_not :netbsd do
+ it "raises an Errno::EINVAL if the length argument is not valid" do
+ lambda { @file.truncate(-1) }.should raise_error(Errno::EINVAL) # May fail
+ end
+ end
+
+ it "raises an IOError if file is closed" do
+ @file.close
+ @file.closed?.should == true
+ lambda { @file.truncate(42) }.should raise_error(IOError)
+ end
+
+ it "raises an IOError if file is not opened for writing" do
+ File.open(@name, 'r') do |file|
+ lambda { file.truncate(42) }.should raise_error(IOError)
+ end
+ end
+
+ it "raises a TypeError if not passed an Integer type for the for the argument" do
+ lambda { @file.truncate(nil) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/file/umask_spec.rb b/spec/ruby/core/file/umask_spec.rb
new file mode 100644
index 0000000000..2286bf064f
--- /dev/null
+++ b/spec/ruby/core/file/umask_spec.rb
@@ -0,0 +1,60 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File.umask" do
+ before :each do
+ @orig_umask = File.umask
+ @file = tmp('test.txt')
+ touch @file
+ end
+
+ after :each do
+ rm_r @file
+ File.umask(@orig_umask)
+ end
+
+ it "returns a Fixnum" do
+ File.umask.should be_kind_of(Fixnum)
+ end
+
+ platform_is_not :windows do
+ it "returns the current umask value for the process" do
+ File.umask(022)
+ File.umask(006).should == 022
+ File.umask.should == 006
+ end
+
+ it "invokes to_int on non-integer argument" do
+ (obj = mock(022)).should_receive(:to_int).any_number_of_times.and_return(022)
+ File.umask(obj)
+ File.umask(obj).should == 022
+ end
+ end
+
+ it "always succeeds with any integer values" do
+ vals = [-2**30, -2**16, -2**8, -2,
+ -1.5, -1, 0.5, 0, 1, 2, 7.77777, 16, 32, 64, 2**8, 2**16, 2**30]
+ vals.each { |v|
+ lambda { File.umask(v) }.should_not raise_error
+ }
+ end
+
+ it "raises ArgumentError when more than one argument is provided" do
+ lambda { File.umask(022, 022) }.should raise_error(ArgumentError)
+ end
+
+ platform_is :windows do
+ it "returns the current umask value for this process (basic)" do
+ File.umask.should == 0
+ File.umask(022).should == 0
+ File.umask(044).should == 0
+ end
+
+ # The value used here is the value of _S_IWRITE.
+ it "returns the current umask value for this process" do
+ File.umask(0000200)
+ File.umask.should == 0000200
+ File.umask(0006)
+ File.umask.should == 0
+ end
+ end
+end
diff --git a/spec/ruby/core/file/unlink_spec.rb b/spec/ruby/core/file/unlink_spec.rb
new file mode 100644
index 0000000000..a1e96aef6a
--- /dev/null
+++ b/spec/ruby/core/file/unlink_spec.rb
@@ -0,0 +1,6 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../shared/unlink', __FILE__)
+
+describe "File.unlink" do
+ it_behaves_like(:file_unlink, :unlink)
+end
diff --git a/spec/ruby/core/file/utime_spec.rb b/spec/ruby/core/file/utime_spec.rb
new file mode 100644
index 0000000000..73112420d1
--- /dev/null
+++ b/spec/ruby/core/file/utime_spec.rb
@@ -0,0 +1,36 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "File.utime" do
+ 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)
+ File.atime(@file1).to_i.should be_close(@atime.to_i, 2)
+ File.mtime(@file1).to_i.should be_close(@mtime.to_i, 2)
+ File.atime(@file2).to_i.should be_close(@atime.to_i, 2)
+ File.mtime(@file2).to_i.should be_close(@mtime.to_i, 2)
+ end
+
+ it "uses the current times if two nil values are passed" do
+ File.utime(nil, nil, @file1, @file2)
+ File.atime(@file1).to_i.should be_close(Time.now.to_i, 2)
+ File.mtime(@file1).to_i.should be_close(Time.now.to_i, 2)
+ File.atime(@file2).to_i.should be_close(Time.now.to_i, 2)
+ File.mtime(@file2).to_i.should be_close(Time.now.to_i, 2)
+ 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
+end
diff --git a/spec/ruby/core/file/world_readable_spec.rb b/spec/ruby/core/file/world_readable_spec.rb
new file mode 100644
index 0000000000..a130f0d115
--- /dev/null
+++ b/spec/ruby/core/file/world_readable_spec.rb
@@ -0,0 +1,12 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/world_readable', __FILE__)
+
+describe "File.world_readable?" do
+ it_behaves_like(:file_world_readable, :world_readable?, File)
+
+ it "returns nil if the file does not exist" do
+ file = rand.to_s + $$.to_s
+ File.exist?(file).should be_false
+ File.world_readable?(file).should be_nil
+ end
+end
diff --git a/spec/ruby/core/file/world_writable_spec.rb b/spec/ruby/core/file/world_writable_spec.rb
new file mode 100644
index 0000000000..5a39643ef9
--- /dev/null
+++ b/spec/ruby/core/file/world_writable_spec.rb
@@ -0,0 +1,12 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/world_writable', __FILE__)
+
+describe "File.world_writable?" do
+ it_behaves_like(:file_world_writable, :world_writable?, File)
+
+ it "returns nil if the file does not exist" do
+ file = rand.to_s + $$.to_s
+ File.exist?(file).should be_false
+ File.world_writable?(file).should be_nil
+ end
+end
diff --git a/spec/ruby/core/file/writable_real_spec.rb b/spec/ruby/core/file/writable_real_spec.rb
new file mode 100644
index 0000000000..36f576e222
--- /dev/null
+++ b/spec/ruby/core/file/writable_real_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/writable_real', __FILE__)
+
+describe "File.writable_real?" do
+ it_behaves_like :file_writable_real, :writable_real?, File
+ it_behaves_like :file_writable_real_missing, :writable_real?, File
+end
diff --git a/spec/ruby/core/file/writable_spec.rb b/spec/ruby/core/file/writable_spec.rb
new file mode 100644
index 0000000000..4f6213ec77
--- /dev/null
+++ b/spec/ruby/core/file/writable_spec.rb
@@ -0,0 +1,7 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/writable', __FILE__)
+
+describe "File.writable?" do
+ it_behaves_like :file_writable, :writable?, File
+ it_behaves_like :file_writable_missing, :writable?, File
+end
diff --git a/spec/ruby/core/file/zero_spec.rb b/spec/ruby/core/file/zero_spec.rb
new file mode 100644
index 0000000000..0fc087faff
--- /dev/null
+++ b/spec/ruby/core/file/zero_spec.rb
@@ -0,0 +1,13 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../../../shared/file/zero', __FILE__)
+
+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