mnoble-fakefs 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.autotest +5 -0
- data/.gitignore +3 -0
- data/CONTRIBUTORS +27 -0
- data/LICENSE +20 -0
- data/README.markdown +123 -0
- data/Rakefile +66 -0
- data/fakefs.gemspec +86 -0
- data/lib/fakefs.rb +3 -0
- data/lib/fakefs/base.rb +45 -0
- data/lib/fakefs/dir.rb +116 -0
- data/lib/fakefs/fake/dir.rb +48 -0
- data/lib/fakefs/fake/file.rb +81 -0
- data/lib/fakefs/fake/symlink.rb +32 -0
- data/lib/fakefs/file.rb +403 -0
- data/lib/fakefs/file_system.rb +139 -0
- data/lib/fakefs/file_test.rb +7 -0
- data/lib/fakefs/fileutils.rb +140 -0
- data/lib/fakefs/safe.rb +11 -0
- data/lib/fakefs/spec_helpers.rb +46 -0
- data/lib/fakefs/version.rb +9 -0
- data/spec/fakefs/spec_helpers_spec.rb +57 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +3 -0
- data/test/fake/file/lstat_test.rb +59 -0
- data/test/fake/file/stat_test.rb +39 -0
- data/test/fake/file/sysseek_test.rb +44 -0
- data/test/fake/file/syswrite_test.rb +62 -0
- data/test/fake/file_test.rb +97 -0
- data/test/fake/symlink_test.rb +10 -0
- data/test/fakefs_test.rb +1611 -0
- data/test/file/stat_test.rb +73 -0
- data/test/safe_test.rb +44 -0
- data/test/test_helper.rb +8 -0
- data/test/verify.rb +31 -0
- metadata +105 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
module FakeFS
|
|
2
|
+
module FileSystem
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
def dir_levels
|
|
6
|
+
@dir_levels ||= []
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def fs
|
|
10
|
+
@fs ||= FakeDir.new('.')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def clear
|
|
14
|
+
@dir_levels = nil
|
|
15
|
+
@fs = nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def files
|
|
19
|
+
fs.values
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def find(path)
|
|
23
|
+
parts = path_parts(normalize_path(path))
|
|
24
|
+
return fs if parts.empty? # '/'
|
|
25
|
+
|
|
26
|
+
entries = find_recurser(fs, parts).flatten
|
|
27
|
+
|
|
28
|
+
case entries.length
|
|
29
|
+
when 0 then nil
|
|
30
|
+
when 1 then entries.first
|
|
31
|
+
else entries
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def add(path, object=FakeDir.new)
|
|
36
|
+
parts = path_parts(normalize_path(path))
|
|
37
|
+
|
|
38
|
+
d = parts[0...-1].inject(fs) do |dir, part|
|
|
39
|
+
dir[part] ||= FakeDir.new(part, dir)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
object.name = parts.last
|
|
43
|
+
object.parent = d
|
|
44
|
+
d[parts.last] ||= object
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# copies directories and files from the real filesystem
|
|
48
|
+
# into our fake one
|
|
49
|
+
def clone(path)
|
|
50
|
+
path = File.expand_path(path)
|
|
51
|
+
pattern = File.join(path, '**', '*')
|
|
52
|
+
files = RealFile.file?(path) ? [path] : [path] + RealDir.glob(pattern, RealFile::FNM_DOTMATCH)
|
|
53
|
+
|
|
54
|
+
files.each do |f|
|
|
55
|
+
if RealFile.file?(f)
|
|
56
|
+
FileUtils.mkdir_p(File.dirname(f))
|
|
57
|
+
File.open(f, File::WRITE_ONLY) do |g|
|
|
58
|
+
g.print RealFile.open(f){|h| h.read }
|
|
59
|
+
end
|
|
60
|
+
elsif RealFile.directory?(f)
|
|
61
|
+
FileUtils.mkdir_p(f)
|
|
62
|
+
elsif RealFile.symlink?(f)
|
|
63
|
+
FileUtils.ln_s()
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def delete(path)
|
|
69
|
+
if node = FileSystem.find(path)
|
|
70
|
+
node.delete
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def chdir(dir, &blk)
|
|
75
|
+
new_dir = find(dir)
|
|
76
|
+
dir_levels.push dir if blk
|
|
77
|
+
|
|
78
|
+
raise Errno::ENOENT, dir unless new_dir
|
|
79
|
+
|
|
80
|
+
dir_levels.push dir if !blk
|
|
81
|
+
blk.call if blk
|
|
82
|
+
ensure
|
|
83
|
+
dir_levels.pop if blk
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def path_parts(path)
|
|
87
|
+
path.split(File::PATH_SEPARATOR).reject { |part| part.empty? }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def normalize_path(path)
|
|
91
|
+
if Pathname.new(path).absolute?
|
|
92
|
+
File.expand_path(path)
|
|
93
|
+
else
|
|
94
|
+
parts = dir_levels + [path]
|
|
95
|
+
File.expand_path(File.join(*parts))
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def current_dir
|
|
100
|
+
find(normalize_path('.'))
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
def find_recurser(dir, parts)
|
|
106
|
+
return [] unless dir.respond_to? :[]
|
|
107
|
+
|
|
108
|
+
pattern , *parts = parts
|
|
109
|
+
matches = case pattern
|
|
110
|
+
when '**'
|
|
111
|
+
case parts
|
|
112
|
+
when ['*']
|
|
113
|
+
parts = [] # end recursion
|
|
114
|
+
directories_under(dir).map do |d|
|
|
115
|
+
d.values.select{|f| f.is_a?(FakeFile) || f.is_a?(FakeDir) }
|
|
116
|
+
end.flatten.uniq
|
|
117
|
+
when []
|
|
118
|
+
parts = [] # end recursion
|
|
119
|
+
dir.values.flatten.uniq
|
|
120
|
+
else
|
|
121
|
+
directories_under(dir)
|
|
122
|
+
end
|
|
123
|
+
else
|
|
124
|
+
dir.reject {|k,v| /\A#{pattern.gsub('?','.').gsub('*', '.*')}\Z/ !~ k }.values
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
if parts.empty? # we're done recursing
|
|
128
|
+
matches
|
|
129
|
+
else
|
|
130
|
+
matches.map{|entry| find_recurser(entry, parts) }
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def directories_under(dir)
|
|
135
|
+
children = dir.values.select{|f| f.is_a? FakeDir}
|
|
136
|
+
([dir] + children + children.map{|c| directories_under(c)}).flatten.uniq
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
module FakeFS
|
|
2
|
+
module FileUtils
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
def mkdir_p(path, options = {})
|
|
6
|
+
FileSystem.add(path, FakeDir.new)
|
|
7
|
+
end
|
|
8
|
+
alias_method :mkpath, :mkdir_p
|
|
9
|
+
|
|
10
|
+
def mkdir(path)
|
|
11
|
+
parent = path.split('/')
|
|
12
|
+
parent.pop
|
|
13
|
+
raise Errno::ENOENT, "No such file or directory - #{path}" unless parent.join == "" || FileSystem.find(parent.join('/'))
|
|
14
|
+
raise Errno::EEXIST, "File exists - #{path}" if FileSystem.find(path)
|
|
15
|
+
FileSystem.add(path, FakeDir.new)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def rmdir(list, options = {})
|
|
19
|
+
list = [ list ] unless list.is_a?(Array)
|
|
20
|
+
list.each do |l|
|
|
21
|
+
parent = l.split('/')
|
|
22
|
+
parent.pop
|
|
23
|
+
raise Errno::ENOENT, "No such file or directory - #{l}" unless parent.join == "" || FileSystem.find(parent.join('/'))
|
|
24
|
+
raise Errno::ENOENT, l unless FileSystem.find(l)
|
|
25
|
+
raise Errno::ENOTEMPTY, l unless FileSystem.find(l).values.empty?
|
|
26
|
+
rm(l)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def rm(list, options = {})
|
|
31
|
+
Array(list).each do |path|
|
|
32
|
+
FileSystem.delete(path)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
alias_method :rm_rf, :rm
|
|
37
|
+
alias_method :rm_r, :rm
|
|
38
|
+
alias_method :rm_f, :rm
|
|
39
|
+
|
|
40
|
+
def ln_s(target, path, options = {})
|
|
41
|
+
options = { :force => false }.merge(options)
|
|
42
|
+
(FileSystem.find(path) and !options[:force]) ? raise(Errno::EEXIST, path) : FileSystem.delete(path)
|
|
43
|
+
FileSystem.add(path, FakeSymlink.new(target))
|
|
44
|
+
end
|
|
45
|
+
def ln_sf(target, path)
|
|
46
|
+
ln_s(target, path, { :force => true })
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def cp(src, dest)
|
|
50
|
+
dst_file = FileSystem.find(dest)
|
|
51
|
+
src_file = FileSystem.find(src)
|
|
52
|
+
|
|
53
|
+
if !src_file
|
|
54
|
+
raise Errno::ENOENT, src
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
if File.directory? src_file
|
|
58
|
+
raise Errno::EISDIR, src
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
if dst_file && File.directory?(dst_file)
|
|
62
|
+
FileSystem.add(File.join(dest, src), src_file.entry.clone(dst_file))
|
|
63
|
+
else
|
|
64
|
+
FileSystem.delete(dest)
|
|
65
|
+
FileSystem.add(dest, src_file.entry.clone)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def cp_r(src, dest)
|
|
70
|
+
# This error sucks, but it conforms to the original Ruby
|
|
71
|
+
# method.
|
|
72
|
+
raise "unknown file type: #{src}" unless dir = FileSystem.find(src)
|
|
73
|
+
|
|
74
|
+
new_dir = FileSystem.find(dest)
|
|
75
|
+
|
|
76
|
+
if new_dir && !File.directory?(dest)
|
|
77
|
+
raise Errno::EEXIST, dest
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
if !new_dir && !FileSystem.find(dest+'/../')
|
|
81
|
+
raise Errno::ENOENT, dest
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# This last bit is a total abuse and should be thought hard
|
|
85
|
+
# about and cleaned up.
|
|
86
|
+
if new_dir
|
|
87
|
+
if src[-2..-1] == '/.'
|
|
88
|
+
dir.values.each{|f| new_dir[f.name] = f.clone(new_dir) }
|
|
89
|
+
else
|
|
90
|
+
new_dir[dir.name] = dir.entry.clone(new_dir)
|
|
91
|
+
end
|
|
92
|
+
else
|
|
93
|
+
FileSystem.add(dest, dir.entry.clone)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def mv(src, dest, options={})
|
|
98
|
+
Array(src).each do |path|
|
|
99
|
+
if target = FileSystem.find(path)
|
|
100
|
+
dest_path = File.directory?(dest) ? File.join(dest, File.basename(path)) : dest
|
|
101
|
+
FileSystem.add(dest_path, target.entry.clone)
|
|
102
|
+
FileSystem.delete(path)
|
|
103
|
+
else
|
|
104
|
+
raise Errno::ENOENT, src
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def chown(user, group, list, options={})
|
|
110
|
+
list = Array(list)
|
|
111
|
+
list.each do |f|
|
|
112
|
+
unless File.exists?(f)
|
|
113
|
+
raise Errno::ENOENT, f
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
list
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def chown_R(user, group, list, options={})
|
|
120
|
+
chown(user, group, list, options={})
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def touch(list, options={})
|
|
124
|
+
Array(list).each do |f|
|
|
125
|
+
directory = File.dirname(f)
|
|
126
|
+
# FIXME this explicit check for '.' shouldn't need to happen
|
|
127
|
+
if File.exists?(directory) || directory == '.'
|
|
128
|
+
FileSystem.add(f, FakeFile.new)
|
|
129
|
+
else
|
|
130
|
+
raise Errno::ENOENT, f
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def cd(dir)
|
|
136
|
+
FileSystem.chdir(dir)
|
|
137
|
+
end
|
|
138
|
+
alias_method :chdir, :cd
|
|
139
|
+
end
|
|
140
|
+
end
|
data/lib/fakefs/safe.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'pathname'
|
|
3
|
+
require 'fakefs/base'
|
|
4
|
+
require 'fakefs/fake/file'
|
|
5
|
+
require 'fakefs/fake/dir'
|
|
6
|
+
require 'fakefs/fake/symlink'
|
|
7
|
+
require 'fakefs/file_system'
|
|
8
|
+
require 'fakefs/fileutils'
|
|
9
|
+
require 'fakefs/file'
|
|
10
|
+
require 'fakefs/file_test'
|
|
11
|
+
require 'fakefs/dir'
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# FakeFS::SpecHelpers provides a simple macro for RSpec example groups to turn FakeFS on and off.
|
|
2
|
+
# To use it simply require 'fakefs/spec_helpers', then include FakeFS::SpecHelpers into any
|
|
3
|
+
# example groups that you wish to use FakeFS in. For example:
|
|
4
|
+
#
|
|
5
|
+
# require 'fakefs/spec_helpers'
|
|
6
|
+
#
|
|
7
|
+
# describe "Some specs that deal with files" do
|
|
8
|
+
# include FakeFS::SpecHelpers
|
|
9
|
+
# ...
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
# Alternatively, you can include FakeFS::SpecHelpers in all your example groups using RSpec's
|
|
13
|
+
# configuration block in your spec helper:
|
|
14
|
+
#
|
|
15
|
+
# require 'fakefs/spec_helpers'
|
|
16
|
+
#
|
|
17
|
+
# Spec::Runner.configure do |config|
|
|
18
|
+
# config.include FakeFS::SpecHelpers
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# If you do the above then use_fakefs will be available in all of your example groups.
|
|
22
|
+
#
|
|
23
|
+
require 'fakefs/safe'
|
|
24
|
+
|
|
25
|
+
module FakeFS
|
|
26
|
+
module SpecHelpers
|
|
27
|
+
def self.extended(example_group)
|
|
28
|
+
example_group.use_fakefs(example_group)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.included(example_group)
|
|
32
|
+
example_group.extend self
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def use_fakefs(describe_block)
|
|
36
|
+
describe_block.before :each do
|
|
37
|
+
FakeFS.activate!
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe_block.after :each do
|
|
41
|
+
FakeFS.deactivate!
|
|
42
|
+
FakeFS::FileSystem.clear
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module FakeFS
|
|
4
|
+
describe SpecHelpers do
|
|
5
|
+
before do
|
|
6
|
+
@rspec_example_group = Class.new do
|
|
7
|
+
def self.before(sym = :each)
|
|
8
|
+
yield if block_given?
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.after(sym = :each)
|
|
12
|
+
yield if block_given?
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe "when extending" do
|
|
18
|
+
context "before each" do
|
|
19
|
+
it "should call it" do
|
|
20
|
+
@rspec_example_group.should_receive(:before).with(:each)
|
|
21
|
+
@rspec_example_group.extend FakeFS::SpecHelpers
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "should call FakeFS.activate!" do
|
|
25
|
+
FakeFS.should_receive(:activate!)
|
|
26
|
+
@rspec_example_group.extend FakeFS::SpecHelpers
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
context "after each" do
|
|
31
|
+
it "should call it" do
|
|
32
|
+
@rspec_example_group.should_receive(:after).with(:each)
|
|
33
|
+
@rspec_example_group.extend FakeFS::SpecHelpers
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "should deactivate fakefs" do
|
|
37
|
+
FakeFS.should_receive(:deactivate!)
|
|
38
|
+
@rspec_example_group.extend FakeFS::SpecHelpers
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should clear the fakefs filesystem for the next run" do
|
|
42
|
+
FakeFS::FileSystem.should_receive(:clear)
|
|
43
|
+
@rspec_example_group.extend FakeFS::SpecHelpers
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
describe "when including" do
|
|
49
|
+
it "should call before :each" do
|
|
50
|
+
@rspec_example_group.should_receive(:before)
|
|
51
|
+
@rspec_example_group.class_eval do
|
|
52
|
+
include FakeFS::SpecHelpers
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
data/spec/spec.opts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class FileStat < Test::Unit::TestCase
|
|
4
|
+
def setup
|
|
5
|
+
FakeFS.activate!
|
|
6
|
+
FakeFS::FileSystem.clear
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def teardown
|
|
10
|
+
FakeFS.deactivate!
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_calling_lstat_should_create_a_new_file_stat_object
|
|
14
|
+
File.open("foo", "w") do |f|
|
|
15
|
+
f << "bar"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
File.open("foo") do |f|
|
|
19
|
+
assert_equal File::Stat, f.lstat.class
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_lstat_should_use_correct_file
|
|
24
|
+
File.open("bar", "w") do |f|
|
|
25
|
+
f << "1"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
File.open("bar") do |f|
|
|
29
|
+
assert_equal 1, f.lstat.size
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_lstat_should_report_on_symlink_itself
|
|
34
|
+
File.open("foo", "w") { |f| f << "some content" }
|
|
35
|
+
File.symlink "foo", "my_symlink"
|
|
36
|
+
|
|
37
|
+
assert_not_equal File.lstat("my_symlink").size, File.lstat("foo").size
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_should_report_on_symlink_itself_with_size_instance_method
|
|
41
|
+
File.open("foo", "w") { |f| f << "some content" }
|
|
42
|
+
File.symlink "foo", "my_symlink"
|
|
43
|
+
|
|
44
|
+
file = File.open("foo")
|
|
45
|
+
symlink = File.open("my_symlink")
|
|
46
|
+
|
|
47
|
+
assert_not_equal file.lstat.size, symlink.lstat.size
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_symlink_size_is_size_of_path_pointed_to
|
|
51
|
+
File.open("a", "w") { |x| x << "foobarbazfoobarbaz" }
|
|
52
|
+
File.symlink "a", "one_char_symlink"
|
|
53
|
+
assert_equal 1, File.lstat("one_char_symlink").size
|
|
54
|
+
|
|
55
|
+
File.open("ab", "w") { |x| x << "foobarbazfoobarbaz" }
|
|
56
|
+
File.symlink "ab", "two_char_symlink"
|
|
57
|
+
assert_equal 2, File.lstat("two_char_symlink").size
|
|
58
|
+
end
|
|
59
|
+
end
|