From bdcdc26d490ff354670b0adfb63831f419e3a88e Mon Sep 17 00:00:00 2001 From: Scott Taylor Date: Thu, 1 Oct 2009 01:39:07 -0400 Subject: [PATCH] Add hard links. Closes #11 --- lib/fakefs/fake/file.rb | 40 +++++++++++++++++-- lib/fakefs/file.rb | 22 +++++++++-- test/fake/file_test.rb | 88 +++++++++++++++++++++++++++++++++++++++++ test/fakefs_test.rb | 8 ++++ test/file/stat_test.rb | 16 ++++++++ 5 files changed, 168 insertions(+), 6 deletions(-) create mode 100644 test/fake/file_test.rb diff --git a/lib/fakefs/fake/file.rb b/lib/fakefs/fake/file.rb index c8ce6df..57c98f6 100644 --- a/lib/fakefs/fake/file.rb +++ b/lib/fakefs/fake/file.rb @@ -1,16 +1,50 @@ module FakeFS class FakeFile - attr_accessor :name, :parent, :content + attr_accessor :name, :parent + + class Inode + def initialize(file_owner) + @content = "" + @links = [file_owner] + end + + attr_accessor :content + attr_accessor :links + + def link(file) + links << file unless links.include?(file) + file.inode = self + end + end def initialize(name = nil, parent = nil) - @name = name + @name = name @parent = parent - @content = '' + @inode = Inode.new(self) + end + + attr_accessor :inode + + def content + @inode.content + end + + def content=(str) + @inode.content = str + end + + def links + @inode.links + end + + def link(other_file) + @inode.link(other_file) end def clone(parent = nil) clone = super() clone.parent = parent if parent + clone.inode = inode.clone clone end diff --git a/lib/fakefs/file.rb b/lib/fakefs/file.rb index b85b704..a7e3869 100644 --- a/lib/fakefs/file.rb +++ b/lib/fakefs/file.rb @@ -116,10 +116,22 @@ module FakeFS end def self.link(source, dest) - raise(Errno::ENOENT, "No such file or directory - #{source} or #{dest}") if !File.exists?(source) - raise(Errno::EEXIST, "File exists - #{source} or #{dest}") if File.exists?(dest) + if directory?(source) + raise Errno::EPERM, "Operation not permitted - #{source} or #{dest}" + end + + if !exists?(source) + raise Errno::ENOENT, "No such file or directory - #{source} or #{dest}" + end - FileUtils.cp(source, dest) + if exists?(dest) + raise Errno::EEXIST, "File exists - #{source} or #{dest}" + end + + source = FileSystem.find(source) + dest = FileSystem.add(dest, source.entry.clone) + source.link(dest) + 0 end @@ -147,6 +159,10 @@ module FakeFS def directory? File.directory?(@file) end + + def nlink + FileSystem.find(@file).links.size + end end attr_reader :path diff --git a/test/fake/file_test.rb b/test/fake/file_test.rb new file mode 100644 index 0000000..dcd87af --- /dev/null +++ b/test/fake/file_test.rb @@ -0,0 +1,88 @@ +$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', '..', 'lib') +require 'fakefs/safe' +require 'test/unit' + +class FakeFileTest < Test::Unit::TestCase + include FakeFS + + def setup + FileSystem.clear + + @file = FakeFile.new + end + + def test_fake_file_has_empty_content_by_default + assert_equal "", @file.content + end + + def test_fake_file_can_read_and_write_to_content + @file.content = "foobar" + assert_equal "foobar", @file.content + end + + def test_fake_file_has_1_link_by_default + assert_equal [@file], @file.links + end + + def test_fake_file_can_create_link + other_file = FakeFile.new + + @file.link(other_file) + + assert_equal [@file, other_file], @file.links + end + + def test_fake_file_wont_add_link_to_same_file_twice + other_file = FakeFile.new + + @file.link other_file + @file.link other_file + + assert_equal [@file, other_file], @file.links + end + + def test_links_are_mutual + other_file = FakeFile.new + + @file.link(other_file) + + assert_equal [@file, other_file], other_file.links + end + + def test_can_link_multiple_files + file_two = FakeFile.new + file_three = FakeFile.new + + @file.link file_two + @file.link file_three + + assert_equal [@file, file_two, file_three], @file.links + assert_equal [@file, file_two, file_three], file_two.links + assert_equal [@file, file_two, file_three], file_three.links + end + + def test_links_share_same_content + other_file = FakeFile.new + + @file.link other_file + + @file.content = "foobar" + + assert_equal "foobar", other_file.content + end + + def test_clone_creates_new_inode + clone = @file.clone + assert !clone.inode.equal?(@file.inode) + end + + def test_cloning_does_not_use_same_content_object + clone = @file.clone + + clone.content = "foo" + @file.content = "bar" + + assert_equal "foo", clone.content + assert_equal "bar", @file.content + end +end diff --git a/test/fakefs_test.rb b/test/fakefs_test.rb index 690bcf1..70cbf42 100644 --- a/test/fakefs_test.rb +++ b/test/fakefs_test.rb @@ -957,6 +957,14 @@ class FakeFSTest < Test::Unit::TestCase assert_equal "some content", File.read("/bar") end + def test_hard_link_with_directory_raises_error + Dir.mkdir "/foo" + + assert_raises(Errno::EPERM) do + File.link("/foo", "/bar") + end + end + def test_file_stat_returns_file_stat_object FileUtils.touch("/foo") assert_equal File::Stat, File.stat("/foo").class diff --git a/test/file/stat_test.rb b/test/file/stat_test.rb index fd12353..4121774 100644 --- a/test/file/stat_test.rb +++ b/test/file/stat_test.rb @@ -21,6 +21,10 @@ class FileStatTest < Test::Unit::TestCase Dir.mkdir(*args) end + def ln(*args) + File.link(*args) + end + def test_file_stat_init_with_non_existant_file assert_raises(Errno::ENOENT) do File::Stat.new("/foo") @@ -51,4 +55,16 @@ class FileStatTest < Test::Unit::TestCase assert File::Stat.new("/foo").directory? end + + def test_one_file_has_hard_link + touch "testfile" + assert_equal 1, File.stat("testfile").nlink + end + + def test_two_hard_links_show_nlinks_as_two + touch "testfile" + ln "testfile", "testfile.bak" + + assert_equal 2, File.stat("testfile").nlink + end end