fakefs 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -4,7 +4,7 @@ FakeFS
4
4
  Mocha is great. But when your library is all about manipulating the
5
5
  filesystem, you really want to test the behavior and not the implementation.
6
6
 
7
- If you're mocking and stubbing every call to FileUtils or File, you're
7
+ If you're mocking and stubbing every call to FileUtils or File, you're
8
8
  tightly coupling your tests with the implementation.
9
9
 
10
10
  def test_creates_directory
@@ -51,18 +51,51 @@ Don't Fake the FS Immediately
51
51
  How is this different than MockFS?
52
52
  ----------------------------------
53
53
 
54
- FakeFS provides a test suite and works with symlinks. It's also strictly a
54
+ FakeFS provides a test suite and works with symlinks. It's also strictly a
55
55
  test-time dependency: your actual library does not need to use or know about
56
56
  FakeFS.
57
57
 
58
58
 
59
59
  Speed?
60
60
  ------
61
- http://gist.github.com/156091
61
+ <http://gist.github.com/156091>
62
62
 
63
63
 
64
- Authors
65
- -------
64
+ Installation
65
+ ------------
66
66
 
67
- * Chris Wanstrath [chris@ozmm.org]
68
- * Pat Nakajima [http://github.com/nakajima]
67
+ ### [Gemcutter](http://gemcutter.org/)
68
+
69
+ $ gem install fakefs
70
+
71
+ ### [Rip](http://hellorip.com)
72
+
73
+ $ rip install git://github.com/defunkt/fakefs.git
74
+
75
+
76
+ Contributors
77
+ ------------
78
+
79
+ * Chris Wanstrath
80
+ * David Reese
81
+ * Jeff Hodges
82
+ * Jon Yurek
83
+ * Matt Freels
84
+ * Myles Eftos
85
+ * Pat Nakajima
86
+ * Rob Sanheim
87
+ * Scott Taylor
88
+ * Tymon Tobolski
89
+ * msassak
90
+
91
+
92
+ Meta
93
+ ----
94
+
95
+ * Code: `git clone git://github.com/defunkt/fakefs.git`
96
+ * Docs: <http://defunkt.github.com/fakefs>
97
+ * Bugs: <http://github.com/defunkt/fakefs/issues>
98
+ * List: <http://groups.google.com/group/fakefs>
99
+ * Test: <http://runcoderun.com/defunkt/fakefs>
100
+ * Gems: <http://gemcutter.org/gems/fakefs>
101
+ * Boss: Chris Wanstrath :: <http://github.com/defunkt>
data/Rakefile CHANGED
@@ -1,30 +1,16 @@
1
- task :default do
2
- Dir['test/*_test.rb'].each { |file| require file }
1
+ desc "Run tests"
2
+ task :test do
3
+ Dir['test/**/*_test.rb'].each { |file| require file }
3
4
  end
4
5
 
6
+ task :default => :test
7
+
5
8
  begin
6
9
  require 'jeweler'
7
10
 
8
- # We're not putting VERSION or VERSION.yml in the root,
9
- # so we have to help Jeweler find our version.
10
11
  $LOAD_PATH.unshift File.dirname(__FILE__) + '/lib'
11
12
  require 'fakefs/version'
12
13
 
13
- FakeFS::Version.instance_eval do
14
- def refresh
15
- end
16
- end
17
-
18
- class Jeweler
19
- def version_helper
20
- FakeFS::Version
21
- end
22
-
23
- def version_exists?
24
- true
25
- end
26
- end
27
-
28
14
  Jeweler::Tasks.new do |gemspec|
29
15
  gemspec.name = "fakefs"
30
16
  gemspec.summary = "A fake filesystem. Use it in your tests."
@@ -33,8 +19,15 @@ begin
33
19
  gemspec.description = "A fake filesystem. Use it in your tests."
34
20
  gemspec.authors = ["Chris Wanstrath"]
35
21
  gemspec.has_rdoc = false
22
+ gemspec.version = FakeFS::Version.to_s
36
23
  end
37
24
  rescue LoadError
38
25
  puts "Jeweler not available."
39
- puts "Install it with: gem install technicalpickles-jeweler"
26
+ puts "Install it with: gem install jeweler"
27
+ end
28
+
29
+ begin
30
+ require 'sdoc_helpers'
31
+ rescue LoadError
32
+ puts "sdoc support not enabled. Please gem install sdoc-helpers."
40
33
  end
data/lib/fakefs/base.rb CHANGED
@@ -31,7 +31,8 @@ end
31
31
  def FakeFS
32
32
  return ::FakeFS unless block_given?
33
33
  ::FakeFS.activate!
34
- yield
34
+ result = yield
35
35
  ::FakeFS.deactivate!
36
+ result
36
37
  end
37
38
 
data/lib/fakefs/dir.rb CHANGED
@@ -1,7 +1,55 @@
1
1
  module FakeFS
2
2
  class Dir
3
- def self.glob(pattern)
4
- [FileSystem.find(pattern) || []].flatten.map{|e| e.to_s}.sort
3
+ include Enumerable
4
+
5
+ def initialize(string)
6
+ raise Errno::ENOENT, string unless FileSystem.find(string)
7
+ @path = string
8
+ @open = true
9
+ @pointer = 0
10
+ @contents = [ '.', '..', ] + FileSystem.find(@path).values
11
+ end
12
+
13
+ def close
14
+ @open = false
15
+ @pointer = nil
16
+ @contents = nil
17
+ nil
18
+ end
19
+
20
+ def each(&block)
21
+ while f = read
22
+ yield f
23
+ end
24
+ end
25
+
26
+ def path
27
+ @path
28
+ end
29
+
30
+ def pos
31
+ @pointer
32
+ end
33
+
34
+ def pos=(integer)
35
+ @pointer = integer
36
+ end
37
+
38
+ def read
39
+ raise IOError, "closed directory" if @pointer == nil
40
+ n = @contents[@pointer]
41
+ @pointer += 1
42
+ n.to_s.gsub(path + '/', '') if n
43
+ end
44
+
45
+ def rewind
46
+ @pointer = 0
47
+ end
48
+
49
+ def seek(integer)
50
+ raise IOError, "closed directory" if @pointer == nil
51
+ @pointer = integer
52
+ @contents[integer]
5
53
  end
6
54
 
7
55
  def self.[](pattern)
@@ -12,12 +60,55 @@ module FakeFS
12
60
  FileSystem.chdir(dir, &blk)
13
61
  end
14
62
 
63
+ def self.chroot(string)
64
+ # Not implemented yet
65
+ end
66
+
67
+ def self.delete(string)
68
+ raise SystemCallError, "No such file or directory - #{string}" unless FileSystem.find(string).values.empty?
69
+ FileSystem.delete(string)
70
+ end
71
+
72
+ def self.entries(dirname)
73
+ raise SystemCallError, dirname unless FileSystem.find(dirname)
74
+ Dir.new(dirname).map { |file| file }
75
+ end
76
+
77
+ def self.foreach(dirname, &block)
78
+ Dir.open(dirname) { |file| yield file }
79
+ end
80
+
81
+ def self.glob(pattern)
82
+ [FileSystem.find(pattern) || []].flatten.map{|e| e.to_s}.sort
83
+ end
84
+
85
+ def self.mkdir(string, integer = 0)
86
+ parent = string.split('/')
87
+ parent.pop
88
+ raise Errno::ENOENT, "No such file or directory - #{string}" unless parent.join == "" || FileSystem.find(parent.join('/'))
89
+ FileUtils.mkdir_p(string)
90
+ end
91
+
92
+ def self.open(string, &block)
93
+ if block_given?
94
+ Dir.new(string).each { |file| yield(file) }
95
+ else
96
+ Dir.new(string)
97
+ end
98
+ end
99
+
100
+ def self.tmpdir
101
+ '/tmp'
102
+ end
103
+
15
104
  def self.pwd
16
105
  FileSystem.current_dir.to_s
17
106
  end
18
107
 
19
108
  class << self
20
109
  alias_method :getwd, :pwd
110
+ alias_method :rmdir, :delete
111
+ alias_method :unlink, :delete
21
112
  end
22
113
  end
23
114
  end
@@ -33,5 +33,13 @@ module FakeFS
33
33
  name
34
34
  end
35
35
  end
36
+
37
+ def delete(node = self)
38
+ if node == self
39
+ parent.delete(self)
40
+ else
41
+ super(node.name)
42
+ end
43
+ end
36
44
  end
37
45
  end
@@ -1,16 +1,54 @@
1
1
  module FakeFS
2
2
  class FakeFile
3
- attr_accessor :name, :parent, :content
3
+ attr_accessor :name, :parent
4
+
5
+ class Inode
6
+ def initialize(file_owner)
7
+ @content = ""
8
+ @links = [file_owner]
9
+ end
10
+
11
+ attr_accessor :content
12
+ attr_accessor :links
13
+
14
+ def link(file)
15
+ links << file unless links.include?(file)
16
+ file.inode = self
17
+ end
18
+
19
+ def unlink(file)
20
+ links.delete(file)
21
+ end
22
+ end
4
23
 
5
24
  def initialize(name = nil, parent = nil)
6
- @name = name
25
+ @name = name
7
26
  @parent = parent
8
- @content = ''
27
+ @inode = Inode.new(self)
28
+ end
29
+
30
+ attr_accessor :inode
31
+
32
+ def content
33
+ @inode.content
34
+ end
35
+
36
+ def content=(str)
37
+ @inode.content = str
38
+ end
39
+
40
+ def links
41
+ @inode.links
42
+ end
43
+
44
+ def link(other_file)
45
+ @inode.link(other_file)
9
46
  end
10
47
 
11
48
  def clone(parent = nil)
12
49
  clone = super()
13
50
  clone.parent = parent if parent
51
+ clone.inode = inode.clone
14
52
  clone
15
53
  end
16
54
 
@@ -25,5 +63,10 @@ module FakeFS
25
63
  def to_s
26
64
  File.join(parent.to_s, name)
27
65
  end
66
+
67
+ def delete
68
+ inode.unlink(self)
69
+ parent.delete(self)
70
+ end
28
71
  end
29
72
  end
@@ -15,12 +15,18 @@ module FakeFS
15
15
  FileSystem.find(target)
16
16
  end
17
17
 
18
- def method_missing(*args, &block)
19
- entry.send(*args, &block)
18
+ def delete
19
+ parent.delete(self)
20
20
  end
21
21
 
22
22
  def respond_to?(method)
23
23
  entry.respond_to?(method)
24
24
  end
25
+
26
+ private
27
+
28
+ def method_missing(*args, &block)
29
+ entry.send(*args, &block)
30
+ end
25
31
  end
26
32
  end
data/lib/fakefs/file.rb CHANGED
@@ -2,6 +2,35 @@ module FakeFS
2
2
  class File
3
3
  PATH_SEPARATOR = '/'
4
4
 
5
+ MODES = [
6
+ READ_ONLY = "r",
7
+ READ_WRITE = "r+",
8
+ WRITE_ONLY = "w",
9
+ READ_WRITE_TRUNCATE = "w+",
10
+ APPEND_WRITE_ONLY = "a",
11
+ APPEND_READ_WRITE = "a+"
12
+ ]
13
+
14
+ FILE_CREATION_MODES = MODES - [READ_ONLY, READ_WRITE]
15
+
16
+ READ_ONLY_MODES = [
17
+ READ_ONLY
18
+ ]
19
+
20
+ WRITE_ONLY_MODES = [
21
+ WRITE_ONLY,
22
+ APPEND_WRITE_ONLY
23
+ ]
24
+
25
+ TRUNCATION_MODES = [
26
+ WRITE_ONLY,
27
+ READ_WRITE_TRUNCATE
28
+ ]
29
+
30
+ def self.extname(path)
31
+ RealFile.extname(path)
32
+ end
33
+
5
34
  def self.join(*parts)
6
35
  parts * PATH_SEPARATOR
7
36
  end
@@ -14,6 +43,10 @@ module FakeFS
14
43
  alias_method :exists?, :exist?
15
44
  end
16
45
 
46
+ def self.size(path)
47
+ read(path).length
48
+ end
49
+
17
50
  def self.const_missing(name)
18
51
  RealFile.const_get(name)
19
52
  end
@@ -61,11 +94,11 @@ module FakeFS
61
94
  FileSystem.find(symlink.target).to_s
62
95
  end
63
96
 
64
- def self.open(path, mode='r')
97
+ def self.open(path, mode=READ_ONLY, perm = 0644)
65
98
  if block_given?
66
- yield new(path, mode)
99
+ yield new(path, mode, perm)
67
100
  else
68
- new(path, mode)
101
+ new(path, mode, perm)
69
102
  end
70
103
  end
71
104
 
@@ -81,13 +114,86 @@ module FakeFS
81
114
  def self.readlines(path)
82
115
  read(path).split("\n")
83
116
  end
117
+
118
+ def self.link(source, dest)
119
+ if directory?(source)
120
+ raise Errno::EPERM, "Operation not permitted - #{source} or #{dest}"
121
+ end
122
+
123
+ if !exists?(source)
124
+ raise Errno::ENOENT, "No such file or directory - #{source} or #{dest}"
125
+ end
126
+
127
+ if exists?(dest)
128
+ raise Errno::EEXIST, "File exists - #{source} or #{dest}"
129
+ end
130
+
131
+ source = FileSystem.find(source)
132
+ dest = FileSystem.add(dest, source.entry.clone)
133
+ source.link(dest)
134
+
135
+ 0
136
+ end
137
+
138
+ def self.delete(file_name, *additional_file_names)
139
+ if !exists?(file_name)
140
+ raise Errno::ENOENT, "No such file or directory - #{file_name}"
141
+ end
142
+
143
+ FileUtils.rm(file_name)
144
+
145
+ additional_file_names.each do |file_name|
146
+ FileUtils.rm(file_name)
147
+ end
148
+
149
+ additional_file_names.size + 1
150
+ end
151
+
152
+ class << self
153
+ alias_method :unlink, :delete
154
+ end
155
+
156
+ def self.symlink(source, dest)
157
+ FileUtils.ln_s(source, dest)
158
+ end
159
+
160
+ def self.stat(file)
161
+ File::Stat.new(file)
162
+ end
163
+
164
+ class Stat
165
+ def initialize(file)
166
+ if !File.exists?(file)
167
+ raise(Errno::ENOENT, "No such file or directory - #{file}")
168
+ end
169
+
170
+ @file = file
171
+ end
172
+
173
+ def symlink?
174
+ File.symlink?(@file)
175
+ end
176
+
177
+ def directory?
178
+ File.directory?(@file)
179
+ end
180
+
181
+ def nlink
182
+ FileSystem.find(@file).links.size
183
+ end
184
+ end
84
185
 
85
186
  attr_reader :path
86
- def initialize(path, mode = nil)
187
+
188
+ def initialize(path, mode = READ_ONLY, perm = nil)
87
189
  @path = path
88
190
  @mode = mode
89
191
  @file = FileSystem.find(path)
90
192
  @open = true
193
+
194
+ check_valid_mode
195
+ file_creation_mode? ? create_missing_file : check_file_existence!
196
+ truncate_file if truncation_mode?
91
197
  end
92
198
 
93
199
  def close
@@ -95,7 +201,9 @@ module FakeFS
95
201
  end
96
202
 
97
203
  def read
98
- raise IOError.new('closed stream') unless @open
204
+ raise IOError, 'closed stream' unless @open
205
+ raise IOError, 'not opened for reading' if write_only?
206
+
99
207
  @file.content
100
208
  end
101
209
 
@@ -103,16 +211,15 @@ module FakeFS
103
211
  @file
104
212
  end
105
213
 
106
- def puts(content)
107
- write(content + "\n")
214
+ def puts(*content)
215
+ content.flatten.each do |obj|
216
+ write(obj.to_s + "\n")
217
+ end
108
218
  end
109
219
 
110
220
  def write(content)
111
- raise IOError.new('closed stream') unless @open
112
-
113
- if !File.exists?(@path)
114
- @file = FileSystem.add(path, FakeFile.new)
115
- end
221
+ raise IOError, 'closed stream' unless @open
222
+ raise IOError, 'not open for writing' if read_only?
116
223
 
117
224
  @file.content += content
118
225
  end
@@ -120,5 +227,49 @@ module FakeFS
120
227
  alias_method :<<, :write
121
228
 
122
229
  def flush; self; end
230
+
231
+ private
232
+
233
+ def check_file_existence!
234
+ unless @file
235
+ raise Errno::ENOENT, "No such file or directory - #{@file}"
236
+ end
237
+ end
238
+
239
+ def check_valid_mode
240
+ if !mode_in?(MODES)
241
+ raise ArgumentError, "illegal access mode #{@mode}"
242
+ end
243
+ end
244
+
245
+ def read_only?
246
+ mode_in? READ_ONLY_MODES
247
+ end
248
+
249
+ def file_creation_mode?
250
+ mode_in? FILE_CREATION_MODES
251
+ end
252
+
253
+ def write_only?
254
+ mode_in? WRITE_ONLY_MODES
255
+ end
256
+
257
+ def truncation_mode?
258
+ mode_in? TRUNCATION_MODES
259
+ end
260
+
261
+ def mode_in?(list)
262
+ list.include?(@mode)
263
+ end
264
+
265
+ def create_missing_file
266
+ if !File.exists?(@path)
267
+ @file = FileSystem.add(path, FakeFile.new)
268
+ end
269
+ end
270
+
271
+ def truncate_file
272
+ @file.content = ""
273
+ end
123
274
  end
124
275
  end