Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename and rework File::Stat as File::Info #5584

Merged
merged 3 commits into from Apr 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
161 changes: 91 additions & 70 deletions spec/std/file_spec.cr
Expand Up @@ -224,11 +224,13 @@ describe "File" do

describe "link" do
it "creates a hard link" do
in_path = "#{__DIR__}/data/test_file.txt"
out_path = "#{__DIR__}/data/test_file_link.txt"
begin
File.link("#{__DIR__}/data/test_file.txt", out_path)
File.link(in_path, out_path)
File.exists?(out_path).should be_true
File.symlink?(out_path).should be_false
File.info(in_path).should eq(File.info(out_path))
ensure
File.delete(out_path) if File.exists?(out_path)
end
Expand All @@ -237,10 +239,12 @@ describe "File" do

describe "symlink" do
it "creates a symbolic link" do
in_path = "#{__DIR__}/data/test_file.txt"
out_path = "#{__DIR__}/data/test_file_symlink.txt"
begin
File.symlink("#{__DIR__}/data/test_file.txt", out_path)
File.symlink(in_path, out_path)
File.symlink?(out_path).should be_true
File.info(in_path).should eq(File.info(out_path))
ensure
File.delete(out_path) if File.exists?(out_path)
end
Expand Down Expand Up @@ -325,7 +329,7 @@ describe "File" do
begin
File.write(path, "")
File.chmod(path, 0o775)
File.stat(path).perm.should eq(0o775)
File.info(path).permissions.should eq(File::Permissions.new(0o775))
ensure
File.delete(path) if File.exists?(path)
end
Expand All @@ -336,20 +340,31 @@ describe "File" do
begin
Dir.mkdir(path, 0o775)
File.chmod(path, 0o664)
File.stat(path).perm.should eq(0o664)
File.info(path).permissions.should eq(File::Permissions.new(0o664))
ensure
Dir.rmdir(path) if Dir.exists?(path)
end
end

it "can take File::Permissions" do
path = "#{__DIR__}/data/chmod.txt"
begin
File.write(path, "")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use File.touch(path) here I think

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's copied from the spec two above it with minor tweaks.

Copy link
Contributor

@bew bew Jan 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh ok, probably not worth to change it here then

File.chmod(path, File::Permissions.flags(OwnerAll, GroupAll, OtherExecute, OtherRead))
File.info(path).permissions.should eq(File::Permissions.new(0o775))
ensure
File.delete(path) if File.exists?(path)
end
end

it "follows symlinks" do
path = "#{__DIR__}/data/chmod_destination.txt"
link = "#{__DIR__}/data/chmod.txt"
begin
File.write(path, "")
File.symlink(path, link)
File.chmod(link, 0o775)
File.stat(link).perm.should eq(0o775)
File.info(link).permissions.should eq(File::Permissions.new(0o775))
ensure
File.delete(path) if File.exists?(path)
File.delete(link) if File.symlink?(link)
Expand All @@ -363,83 +378,74 @@ describe "File" do
end
end

it "gets stat for this file" do
stat = File.stat(__FILE__)
stat.blockdev?.should be_false
stat.chardev?.should be_false
stat.directory?.should be_false
stat.file?.should be_true
stat.symlink?.should be_false
stat.socket?.should be_false
it "gets info for this file" do
info = File.info(__FILE__)
info.type.should eq(File::Type::File)
end

it "gets stat for this directory" do
stat = File.stat(__DIR__)
stat.blockdev?.should be_false
stat.chardev?.should be_false
stat.directory?.should be_true
stat.file?.should be_false
stat.symlink?.should be_false
stat.socket?.should be_false
it "gets info for this directory" do
info = File.info(__DIR__)
info.type.should eq(File::Type::Directory)
end

it "gets stat for a character device" do
stat = File.stat("/dev/null")
stat.blockdev?.should be_false
stat.chardev?.should be_true
stat.directory?.should be_false
stat.file?.should be_false
stat.symlink?.should be_false
stat.socket?.should be_false
it "gets info for a character device" do
info = File.info("/dev/null")
info.type.should eq(File::Type::CharacterDevice)
end

it "gets stat for a symlink" do
stat = File.lstat("#{__DIR__}/data/symlink.txt")
stat.blockdev?.should be_false
stat.chardev?.should be_false
stat.directory?.should be_false
stat.file?.should be_false
stat.symlink?.should be_true
stat.socket?.should be_false
it "gets info for a symlink" do
info = File.info("#{__DIR__}/data/symlink.txt", follow_symlinks: false)
info.type.should eq(File::Type::Symlink)
end

it "gets stat for open file" do
it "gets info for open file" do
File.open(__FILE__, "r") do |file|
stat = file.stat
stat.blockdev?.should be_false
stat.chardev?.should be_false
stat.directory?.should be_false
stat.file?.should be_true
stat.symlink?.should be_false
stat.socket?.should be_false
stat.pipe?.should be_false
info = file.info
info.type.should eq(File::Type::File)
end
end

it "gets stat for pipe" do
it "gets info for pipe" do
IO.pipe do |r, w|
r.stat.pipe?.should be_true
w.stat.pipe?.should be_true
r.info.type.should eq(File::Type::Pipe)
w.info.type.should eq(File::Type::Pipe)
end
end

it "gets stat for non-existent file and raises" do
it "gets info for non-existent file and raises" do
expect_raises Errno do
File.stat("non-existent")
File.info("non-existent")
end
end

it "gets stat mtime for new file" do
it "gets info mtime for new file" do
tmp = Tempfile.new "tmp"
begin
(tmp.stat.atime - Time.utc_now).total_seconds.should be < 5
(tmp.stat.ctime - Time.utc_now).total_seconds.should be < 5
(tmp.stat.mtime - Time.utc_now).total_seconds.should be < 5
tmp.info.modification_time.should be_close(Time.now, 5.seconds)
File.info(tmp.path).modification_time.should be_close(Time.now, 5.seconds)
ensure
tmp.delete
end
end

describe "File::Info" do
it "tests equal for the same file" do
File.info(__FILE__).should eq(File.info(__FILE__))
end

it "tests equal for the same directory" do
File.info(__DIR__).should eq(File.info(__DIR__))
end

it "tests unequal for different files" do
File.info(__FILE__).should_not eq(File.info("#{__DIR__}/data/test_file.txt"))
end

it "tests unequal for file and directory" do
File.info(__DIR__).should_not eq(File.info("#{__DIR__}/data/test_file.txt"))
end
end

describe "size" do
it { File.size("#{__DIR__}/data/test_file.txt").should eq(240) }
it do
Expand Down Expand Up @@ -713,11 +719,20 @@ describe "File" do
end
end

it "opens with perm" do
it "opens with perm (int)" do
filename = "#{__DIR__}/data/temp_write.txt"
perm = 0o600
File.open(filename, "w", perm) do |file|
file.stat.perm.should eq(perm)
file.info.permissions.should eq(File::Permissions.new(perm))
end
File.delete filename
end

it "opens with perm (File::Permissions)" do
filename = "#{__DIR__}/data/temp_write.txt"
perm = File::Permissions.flags(OwnerRead, OwnerWrite)
File.open(filename, "w", perm) do |file|
file.info.permissions.should eq(perm)
end
File.delete filename
end
Expand Down Expand Up @@ -920,12 +935,12 @@ describe "File" do
File.rename("baz", "foo\0bar")
end

it_raises_on_null_byte "stat" do
File.stat("foo\0bar")
it_raises_on_null_byte "info" do
File.info("foo\0bar")
end

it_raises_on_null_byte "lstat" do
File.lstat("foo\0bar")
it_raises_on_null_byte "info?" do
File.info?("foo\0bar")
end

it_raises_on_null_byte "exists?" do
Expand Down Expand Up @@ -1071,9 +1086,8 @@ describe "File" do

File.utime(atime, mtime, filename)

stat = File.stat(filename)
stat.atime.should eq(atime)
stat.mtime.should eq(mtime)
info = File.info(filename)
info.modification_time.should eq(mtime)

File.delete filename
end
Expand Down Expand Up @@ -1106,9 +1120,8 @@ describe "File" do
begin
File.touch(filename, time)

stat = File.stat(filename)
stat.atime.should eq(time)
stat.mtime.should eq(time)
info = File.info(filename)
info.modification_time.should eq(time)
ensure
File.delete filename
end
Expand All @@ -1120,9 +1133,8 @@ describe "File" do
begin
File.touch(filename)

stat = File.stat(filename)
stat.atime.should be_close(time, 1.second)
stat.mtime.should be_close(time, 1.second)
info = File.info(filename)
info.modification_time.should be_close(time, 1.second)
ensure
File.delete filename
end
Expand Down Expand Up @@ -1254,4 +1266,13 @@ describe "File" do
File.match?("ab{{c,d}ef,}", "abdef").should be_true
end
end

describe File::Permissions do
it "does to_s" do
perm = File::Permissions.flags(OwnerAll, GroupRead, GroupWrite, OtherRead)
perm.to_s.should eq("rwxrw-r-- (0o764)")
perm.inspect.should eq("rwxrw-r-- (0o764)")
perm.pretty_inspect.should eq("rwxrw-r-- (0o764)")
end
end
end
17 changes: 17 additions & 0 deletions spec/std/file_utils_spec.cr
Expand Up @@ -136,6 +136,23 @@ describe "FileUtils" do
end
end

it "copies permissions" do
src_path = File.join(__DIR__, "data/new_test_file.txt")
out_path = File.join(__DIR__, "data/test_file_cp.txt")
begin
File.write(src_path, "foo")
File.chmod(src_path, 0o700)

FileUtils.cp(src_path, out_path)

File.info(out_path).permissions.should eq(File::Permissions.new(0o700))
FileUtils.cmp(src_path, out_path).should be_true
ensure
File.delete(src_path) if File.exists?(out_path)
File.delete(out_path) if File.exists?(out_path)
end
end

it "raises an error if the directory doesn't exists" do
expect_raises(ArgumentError, "No such directory : not_existing_dir") do
FileUtils.cp({File.join(__DIR__, "data/test_file.text")}, "not_existing_dir")
Expand Down
12 changes: 6 additions & 6 deletions spec/std/http/server/handlers/static_file_handler_spec.cr
Expand Up @@ -24,26 +24,26 @@ describe HTTP::StaticFileHandler do
context "with header If-Modified-Since" do
it "should return 304 Not Modified if file mtime is equal" do
headers = HTTP::Headers.new
headers["If-Modified-Since"] = HTTP.rfc1123_date(File.stat("#{__DIR__}/static/test.txt").mtime)
headers["If-Modified-Since"] = HTTP.rfc1123_date(File.info("#{__DIR__}/static/test.txt").modification_time)
response = handle HTTP::Request.new("GET", "/test.txt", headers), ignore_body: true
response.status_code.should eq(304)
response.headers["Last-Modified"].should eq(HTTP.rfc1123_date(File.stat("#{__DIR__}/static/test.txt").mtime))
response.headers["Last-Modified"].should eq(HTTP.rfc1123_date(File.info("#{__DIR__}/static/test.txt").modification_time))
end

it "should return 304 Not Modified if file mtime is older" do
headers = HTTP::Headers.new
headers["If-Modified-Since"] = HTTP.rfc1123_date(File.stat("#{__DIR__}/static/test.txt").mtime + 1.hour)
headers["If-Modified-Since"] = HTTP.rfc1123_date(File.info("#{__DIR__}/static/test.txt").modification_time + 1.hour)
response = handle HTTP::Request.new("GET", "/test.txt", headers), ignore_body: true
response.status_code.should eq(304)
response.headers["Last-Modified"].should eq(HTTP.rfc1123_date(File.stat("#{__DIR__}/static/test.txt").mtime))
response.headers["Last-Modified"].should eq(HTTP.rfc1123_date(File.info("#{__DIR__}/static/test.txt").modification_time))
end

it "should serve file if file mtime is younger" do
headers = HTTP::Headers.new
headers["If-Modified-Since"] = HTTP.rfc1123_date(File.stat("#{__DIR__}/static/test.txt").mtime - 1.hour)
headers["If-Modified-Since"] = HTTP.rfc1123_date(File.info("#{__DIR__}/static/test.txt").modification_time - 1.hour)
response = handle HTTP::Request.new("GET", "/test.txt")
response.status_code.should eq(200)
response.headers["Last-Modified"].should eq(HTTP.rfc1123_date(File.stat("#{__DIR__}/static/test.txt").mtime))
response.headers["Last-Modified"].should eq(HTTP.rfc1123_date(File.info("#{__DIR__}/static/test.txt").modification_time))
response.body.should eq(File.read("#{__DIR__}/static/test.txt"))
end
end
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/crystal/codegen/cache_dir.cr
Expand Up @@ -107,7 +107,7 @@ module Crystal
private def cleanup_dirs(entries)
entries
.select { |dir| Dir.exists?(dir) }
.sort_by! { |dir| File.stat(dir).mtime rescue Time.epoch(0) }
.sort_by! { |dir| File.info?(dir).try(&.modification_time) || Time.epoch(0) }
.reverse!
.skip(10)
.each { |name| `rm -rf "#{name}"` rescue nil }
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/crystal/macros/macros.cr
Expand Up @@ -158,7 +158,7 @@ class Crystal::Program
# Together with their timestamp
# (this is the list of all effective files that were required)
requires_with_timestamps = result.program.requires.map do |required_file|
epoch = File.stat(required_file).mtime.epoch
epoch = File.info(required_file).modification_time.epoch
RequireWithTimestamp.new(required_file, epoch)
end

Expand Down Expand Up @@ -203,7 +203,7 @@ class Crystal::Program
end

new_requires_with_timestamps = required_files.map do |required_file|
epoch = File.stat(required_file).mtime.epoch
epoch = File.info(required_file).modification_time.epoch
RequireWithTimestamp.new(required_file, epoch)
end

Expand Down
1 change: 1 addition & 0 deletions src/crystal/system/file_info.cr
@@ -0,0 +1 @@
require "./unix/file_info"