From 1d15d5f08032acf1b7bceacbb450d617ff6e0931 Mon Sep 17 00:00:00 2001 From: eregon Date: Wed, 20 Sep 2017 20:18:52 +0000 Subject: Move spec/rubyspec to spec/ruby for consistency * Other ruby implementations use the spec/ruby directory. [Misc #13792] [ruby-core:82287] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59979 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- spec/ruby/core/file/absolute_path_spec.rb | 37 ++ spec/ruby/core/file/atime_spec.rb | 55 ++ spec/ruby/core/file/basename_spec.rb | 170 ++++++ spec/ruby/core/file/birthtime_spec.rb | 56 ++ spec/ruby/core/file/blockdev_spec.rb | 6 + spec/ruby/core/file/chardev_spec.rb | 6 + spec/ruby/core/file/chmod_spec.rb | 239 ++++++++ spec/ruby/core/file/chown_spec.rb | 152 +++++ spec/ruby/core/file/constants/constants_spec.rb | 31 ++ spec/ruby/core/file/constants_spec.rb | 141 +++++ spec/ruby/core/file/ctime_spec.rb | 51 ++ spec/ruby/core/file/delete_spec.rb | 6 + spec/ruby/core/file/directory_spec.rb | 10 + spec/ruby/core/file/dirname_spec.rb | 108 ++++ spec/ruby/core/file/executable_real_spec.rb | 7 + spec/ruby/core/file/executable_spec.rb | 7 + spec/ruby/core/file/exist_spec.rb | 6 + spec/ruby/core/file/exists_spec.rb | 6 + spec/ruby/core/file/expand_path_spec.rb | 242 ++++++++ spec/ruby/core/file/extname_spec.rb | 54 ++ spec/ruby/core/file/file_spec.rb | 16 + spec/ruby/core/file/fixtures/common.rb | 22 + spec/ruby/core/file/fixtures/do_not_remove | 1 + spec/ruby/core/file/fixtures/file_types.rb | 64 +++ spec/ruby/core/file/flock_spec.rb | 106 ++++ spec/ruby/core/file/fnmatch_spec.rb | 10 + spec/ruby/core/file/ftype_spec.rb | 73 +++ spec/ruby/core/file/grpowned_spec.rb | 10 + spec/ruby/core/file/identical_spec.rb | 6 + spec/ruby/core/file/initialize_spec.rb | 23 + spec/ruby/core/file/inspect_spec.rb | 17 + spec/ruby/core/file/join_spec.rb | 139 +++++ spec/ruby/core/file/lchmod_spec.rb | 42 ++ spec/ruby/core/file/lchown_spec.rb | 63 +++ spec/ruby/core/file/link_spec.rb | 39 ++ spec/ruby/core/file/lstat_spec.rb | 33 ++ spec/ruby/core/file/mkfifo_spec.rb | 53 ++ spec/ruby/core/file/mtime_spec.rb | 51 ++ spec/ruby/core/file/new_spec.rb | 162 ++++++ spec/ruby/core/file/null_spec.rb | 15 + spec/ruby/core/file/open_spec.rb | 678 +++++++++++++++++++++++ spec/ruby/core/file/owned_spec.rb | 33 ++ spec/ruby/core/file/path_spec.rb | 29 + spec/ruby/core/file/pipe_spec.rb | 32 ++ spec/ruby/core/file/read_spec.rb | 6 + spec/ruby/core/file/readable_real_spec.rb | 7 + spec/ruby/core/file/readable_spec.rb | 7 + spec/ruby/core/file/readlink_spec.rb | 67 +++ spec/ruby/core/file/realdirpath_spec.rb | 104 ++++ spec/ruby/core/file/realpath_spec.rb | 88 +++ spec/ruby/core/file/rename_spec.rb | 37 ++ spec/ruby/core/file/reopen_spec.rb | 32 ++ spec/ruby/core/file/setgid_spec.rb | 36 ++ spec/ruby/core/file/setuid_spec.rb | 38 ++ spec/ruby/core/file/shared/fnmatch.rb | 241 ++++++++ spec/ruby/core/file/shared/open.rb | 12 + spec/ruby/core/file/shared/read.rb | 15 + spec/ruby/core/file/shared/stat.rb | 32 ++ spec/ruby/core/file/shared/unlink.rb | 63 +++ spec/ruby/core/file/size_spec.rb | 119 ++++ spec/ruby/core/file/socket_spec.rb | 42 ++ spec/ruby/core/file/split_spec.rb | 63 +++ spec/ruby/core/file/stat/atime_spec.rb | 18 + spec/ruby/core/file/stat/birthtime_spec.rb | 27 + spec/ruby/core/file/stat/blksize_spec.rb | 27 + spec/ruby/core/file/stat/blockdev_spec.rb | 7 + spec/ruby/core/file/stat/blocks_spec.rb | 27 + spec/ruby/core/file/stat/chardev_spec.rb | 7 + spec/ruby/core/file/stat/comparison_spec.rb | 66 +++ spec/ruby/core/file/stat/ctime_spec.rb | 18 + spec/ruby/core/file/stat/dev_major_spec.rb | 23 + spec/ruby/core/file/stat/dev_minor_spec.rb | 23 + spec/ruby/core/file/stat/dev_spec.rb | 15 + spec/ruby/core/file/stat/directory_spec.rb | 7 + spec/ruby/core/file/stat/executable_real_spec.rb | 7 + spec/ruby/core/file/stat/executable_spec.rb | 7 + spec/ruby/core/file/stat/file_spec.rb | 7 + spec/ruby/core/file/stat/fixtures/classes.rb | 5 + spec/ruby/core/file/stat/ftype_spec.rb | 68 +++ spec/ruby/core/file/stat/gid_spec.rb | 19 + spec/ruby/core/file/stat/grpowned_spec.rb | 7 + spec/ruby/core/file/stat/ino_spec.rb | 38 ++ spec/ruby/core/file/stat/inspect_spec.rb | 26 + spec/ruby/core/file/stat/mode_spec.rb | 19 + spec/ruby/core/file/stat/mtime_spec.rb | 18 + spec/ruby/core/file/stat/new_spec.rb | 30 + spec/ruby/core/file/stat/nlink_spec.rb | 21 + spec/ruby/core/file/stat/owned_spec.rb | 31 ++ spec/ruby/core/file/stat/pipe_spec.rb | 32 ++ spec/ruby/core/file/stat/rdev_major_spec.rb | 31 ++ spec/ruby/core/file/stat/rdev_minor_spec.rb | 31 ++ spec/ruby/core/file/stat/rdev_spec.rb | 15 + spec/ruby/core/file/stat/readable_real_spec.rb | 7 + spec/ruby/core/file/stat/readable_spec.rb | 7 + spec/ruby/core/file/stat/setgid_spec.rb | 11 + spec/ruby/core/file/stat/setuid_spec.rb | 11 + spec/ruby/core/file/stat/size_spec.rb | 21 + spec/ruby/core/file/stat/socket_spec.rb | 11 + spec/ruby/core/file/stat/sticky_spec.rb | 11 + spec/ruby/core/file/stat/symlink_spec.rb | 7 + spec/ruby/core/file/stat/uid_spec.rb | 18 + spec/ruby/core/file/stat/world_readable_spec.rb | 11 + spec/ruby/core/file/stat/world_writable_spec.rb | 11 + spec/ruby/core/file/stat/writable_real_spec.rb | 7 + spec/ruby/core/file/stat/writable_spec.rb | 7 + spec/ruby/core/file/stat/zero_spec.rb | 7 + spec/ruby/core/file/stat_spec.rb | 45 ++ spec/ruby/core/file/sticky_spec.rb | 50 ++ spec/ruby/core/file/symlink_spec.rb | 57 ++ spec/ruby/core/file/to_path_spec.rb | 49 ++ spec/ruby/core/file/truncate_spec.rb | 177 ++++++ spec/ruby/core/file/umask_spec.rb | 60 ++ spec/ruby/core/file/unlink_spec.rb | 6 + spec/ruby/core/file/utime_spec.rb | 36 ++ spec/ruby/core/file/world_readable_spec.rb | 12 + spec/ruby/core/file/world_writable_spec.rb | 12 + spec/ruby/core/file/writable_real_spec.rb | 7 + spec/ruby/core/file/writable_spec.rb | 7 + spec/ruby/core/file/zero_spec.rb | 13 + 119 files changed, 5471 insertions(+) create mode 100644 spec/ruby/core/file/absolute_path_spec.rb create mode 100644 spec/ruby/core/file/atime_spec.rb create mode 100644 spec/ruby/core/file/basename_spec.rb create mode 100644 spec/ruby/core/file/birthtime_spec.rb create mode 100644 spec/ruby/core/file/blockdev_spec.rb create mode 100644 spec/ruby/core/file/chardev_spec.rb create mode 100644 spec/ruby/core/file/chmod_spec.rb create mode 100644 spec/ruby/core/file/chown_spec.rb create mode 100644 spec/ruby/core/file/constants/constants_spec.rb create mode 100644 spec/ruby/core/file/constants_spec.rb create mode 100644 spec/ruby/core/file/ctime_spec.rb create mode 100644 spec/ruby/core/file/delete_spec.rb create mode 100644 spec/ruby/core/file/directory_spec.rb create mode 100644 spec/ruby/core/file/dirname_spec.rb create mode 100644 spec/ruby/core/file/executable_real_spec.rb create mode 100644 spec/ruby/core/file/executable_spec.rb create mode 100644 spec/ruby/core/file/exist_spec.rb create mode 100644 spec/ruby/core/file/exists_spec.rb create mode 100644 spec/ruby/core/file/expand_path_spec.rb create mode 100644 spec/ruby/core/file/extname_spec.rb create mode 100644 spec/ruby/core/file/file_spec.rb create mode 100644 spec/ruby/core/file/fixtures/common.rb create mode 100644 spec/ruby/core/file/fixtures/do_not_remove create mode 100644 spec/ruby/core/file/fixtures/file_types.rb create mode 100644 spec/ruby/core/file/flock_spec.rb create mode 100644 spec/ruby/core/file/fnmatch_spec.rb create mode 100644 spec/ruby/core/file/ftype_spec.rb create mode 100644 spec/ruby/core/file/grpowned_spec.rb create mode 100644 spec/ruby/core/file/identical_spec.rb create mode 100644 spec/ruby/core/file/initialize_spec.rb create mode 100644 spec/ruby/core/file/inspect_spec.rb create mode 100644 spec/ruby/core/file/join_spec.rb create mode 100644 spec/ruby/core/file/lchmod_spec.rb create mode 100644 spec/ruby/core/file/lchown_spec.rb create mode 100644 spec/ruby/core/file/link_spec.rb create mode 100644 spec/ruby/core/file/lstat_spec.rb create mode 100644 spec/ruby/core/file/mkfifo_spec.rb create mode 100644 spec/ruby/core/file/mtime_spec.rb create mode 100644 spec/ruby/core/file/new_spec.rb create mode 100644 spec/ruby/core/file/null_spec.rb create mode 100644 spec/ruby/core/file/open_spec.rb create mode 100644 spec/ruby/core/file/owned_spec.rb create mode 100644 spec/ruby/core/file/path_spec.rb create mode 100644 spec/ruby/core/file/pipe_spec.rb create mode 100644 spec/ruby/core/file/read_spec.rb create mode 100644 spec/ruby/core/file/readable_real_spec.rb create mode 100644 spec/ruby/core/file/readable_spec.rb create mode 100644 spec/ruby/core/file/readlink_spec.rb create mode 100644 spec/ruby/core/file/realdirpath_spec.rb create mode 100644 spec/ruby/core/file/realpath_spec.rb create mode 100644 spec/ruby/core/file/rename_spec.rb create mode 100644 spec/ruby/core/file/reopen_spec.rb create mode 100644 spec/ruby/core/file/setgid_spec.rb create mode 100644 spec/ruby/core/file/setuid_spec.rb create mode 100644 spec/ruby/core/file/shared/fnmatch.rb create mode 100644 spec/ruby/core/file/shared/open.rb create mode 100644 spec/ruby/core/file/shared/read.rb create mode 100644 spec/ruby/core/file/shared/stat.rb create mode 100644 spec/ruby/core/file/shared/unlink.rb create mode 100644 spec/ruby/core/file/size_spec.rb create mode 100644 spec/ruby/core/file/socket_spec.rb create mode 100644 spec/ruby/core/file/split_spec.rb create mode 100644 spec/ruby/core/file/stat/atime_spec.rb create mode 100644 spec/ruby/core/file/stat/birthtime_spec.rb create mode 100644 spec/ruby/core/file/stat/blksize_spec.rb create mode 100644 spec/ruby/core/file/stat/blockdev_spec.rb create mode 100644 spec/ruby/core/file/stat/blocks_spec.rb create mode 100644 spec/ruby/core/file/stat/chardev_spec.rb create mode 100644 spec/ruby/core/file/stat/comparison_spec.rb create mode 100644 spec/ruby/core/file/stat/ctime_spec.rb create mode 100644 spec/ruby/core/file/stat/dev_major_spec.rb create mode 100644 spec/ruby/core/file/stat/dev_minor_spec.rb create mode 100644 spec/ruby/core/file/stat/dev_spec.rb create mode 100644 spec/ruby/core/file/stat/directory_spec.rb create mode 100644 spec/ruby/core/file/stat/executable_real_spec.rb create mode 100644 spec/ruby/core/file/stat/executable_spec.rb create mode 100644 spec/ruby/core/file/stat/file_spec.rb create mode 100644 spec/ruby/core/file/stat/fixtures/classes.rb create mode 100644 spec/ruby/core/file/stat/ftype_spec.rb create mode 100644 spec/ruby/core/file/stat/gid_spec.rb create mode 100644 spec/ruby/core/file/stat/grpowned_spec.rb create mode 100644 spec/ruby/core/file/stat/ino_spec.rb create mode 100644 spec/ruby/core/file/stat/inspect_spec.rb create mode 100644 spec/ruby/core/file/stat/mode_spec.rb create mode 100644 spec/ruby/core/file/stat/mtime_spec.rb create mode 100644 spec/ruby/core/file/stat/new_spec.rb create mode 100644 spec/ruby/core/file/stat/nlink_spec.rb create mode 100644 spec/ruby/core/file/stat/owned_spec.rb create mode 100644 spec/ruby/core/file/stat/pipe_spec.rb create mode 100644 spec/ruby/core/file/stat/rdev_major_spec.rb create mode 100644 spec/ruby/core/file/stat/rdev_minor_spec.rb create mode 100644 spec/ruby/core/file/stat/rdev_spec.rb create mode 100644 spec/ruby/core/file/stat/readable_real_spec.rb create mode 100644 spec/ruby/core/file/stat/readable_spec.rb create mode 100644 spec/ruby/core/file/stat/setgid_spec.rb create mode 100644 spec/ruby/core/file/stat/setuid_spec.rb create mode 100644 spec/ruby/core/file/stat/size_spec.rb create mode 100644 spec/ruby/core/file/stat/socket_spec.rb create mode 100644 spec/ruby/core/file/stat/sticky_spec.rb create mode 100644 spec/ruby/core/file/stat/symlink_spec.rb create mode 100644 spec/ruby/core/file/stat/uid_spec.rb create mode 100644 spec/ruby/core/file/stat/world_readable_spec.rb create mode 100644 spec/ruby/core/file/stat/world_writable_spec.rb create mode 100644 spec/ruby/core/file/stat/writable_real_spec.rb create mode 100644 spec/ruby/core/file/stat/writable_spec.rb create mode 100644 spec/ruby/core/file/stat/zero_spec.rb create mode 100644 spec/ruby/core/file/stat_spec.rb create mode 100644 spec/ruby/core/file/sticky_spec.rb create mode 100644 spec/ruby/core/file/symlink_spec.rb create mode 100644 spec/ruby/core/file/to_path_spec.rb create mode 100644 spec/ruby/core/file/truncate_spec.rb create mode 100644 spec/ruby/core/file/umask_spec.rb create mode 100644 spec/ruby/core/file/unlink_spec.rb create mode 100644 spec/ruby/core/file/utime_spec.rb create mode 100644 spec/ruby/core/file/world_readable_spec.rb create mode 100644 spec/ruby/core/file/world_writable_spec.rb create mode 100644 spec/ruby/core/file/writable_real_spec.rb create mode 100644 spec/ruby/core/file/writable_spec.rb create mode 100644 spec/ruby/core/file/zero_spec.rb (limited to 'spec/ruby/core/file') 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 = "#" + 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 -- cgit v1.2.3