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.
@@ -0,0 +1,48 @@
1
+ module FakeFS
2
+ class FakeDir < Hash
3
+ attr_accessor :name, :parent
4
+ attr_reader :ctime, :mtime
5
+
6
+ def initialize(name = nil, parent = nil)
7
+ @name = name
8
+ @parent = parent
9
+ @ctime = Time.now
10
+ @mtime = @ctime
11
+ end
12
+
13
+ def entry
14
+ self
15
+ end
16
+
17
+ def inspect
18
+ "(FakeDir name:#{name.inspect} parent:#{parent.to_s.inspect} size:#{size})"
19
+ end
20
+
21
+ def clone(parent = nil)
22
+ clone = Marshal.load(Marshal.dump(self))
23
+ clone.each do |key, value|
24
+ value.parent = clone
25
+ end
26
+ clone.parent = parent if parent
27
+ clone
28
+ end
29
+
30
+ def to_s
31
+ if parent && parent.to_s != '.'
32
+ File.join(parent.to_s, name)
33
+ elsif parent && parent.to_s == '.'
34
+ "#{File::PATH_SEPARATOR}#{name}"
35
+ else
36
+ name
37
+ end
38
+ end
39
+
40
+ def delete(node = self)
41
+ if node == self
42
+ parent.delete(self)
43
+ else
44
+ super(node.name)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,81 @@
1
+ module FakeFS
2
+ class FakeFile
3
+ attr_accessor :name, :parent, :content, :mtime
4
+ attr_reader :ctime
5
+
6
+ class Inode
7
+ def initialize(file_owner)
8
+ @content = ""
9
+ @links = [file_owner]
10
+ end
11
+
12
+ attr_accessor :content
13
+ attr_accessor :links
14
+
15
+ def link(file)
16
+ links << file unless links.include?(file)
17
+ file.inode = self
18
+ end
19
+
20
+ def unlink(file)
21
+ links.delete(file)
22
+ end
23
+
24
+ def clone
25
+ clone = super
26
+ clone.content = content.dup
27
+ clone
28
+ end
29
+ end
30
+
31
+ def initialize(name = nil, parent = nil)
32
+ @name = name
33
+ @parent = parent
34
+ @inode = Inode.new(self)
35
+ @ctime = Time.now
36
+ @mtime = @ctime
37
+ end
38
+
39
+ attr_accessor :inode
40
+
41
+ def content
42
+ @inode.content
43
+ end
44
+
45
+ def content=(str)
46
+ @inode.content = str
47
+ end
48
+
49
+ def links
50
+ @inode.links
51
+ end
52
+
53
+ def link(other_file)
54
+ @inode.link(other_file)
55
+ end
56
+
57
+ def clone(parent = nil)
58
+ clone = super()
59
+ clone.parent = parent if parent
60
+ clone.inode = inode.clone
61
+ clone
62
+ end
63
+
64
+ def entry
65
+ self
66
+ end
67
+
68
+ def inspect
69
+ "(FakeFile name:#{name.inspect} parent:#{parent.to_s.inspect} size:#{content.size})"
70
+ end
71
+
72
+ def to_s
73
+ File.join(parent.to_s, name)
74
+ end
75
+
76
+ def delete
77
+ inode.unlink(self)
78
+ parent.delete(self)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,32 @@
1
+ module FakeFS
2
+ class FakeSymlink
3
+ attr_accessor :name, :target
4
+ alias_method :to_s, :name
5
+
6
+ def initialize(target)
7
+ @target = target
8
+ end
9
+
10
+ def inspect
11
+ "symlink(#{target.split('/').last})"
12
+ end
13
+
14
+ def entry
15
+ FileSystem.find(target)
16
+ end
17
+
18
+ def delete
19
+ parent.delete(self)
20
+ end
21
+
22
+ def respond_to?(method)
23
+ entry.respond_to?(method)
24
+ end
25
+
26
+ private
27
+
28
+ def method_missing(*args, &block)
29
+ entry.send(*args, &block)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,403 @@
1
+ require 'stringio'
2
+
3
+ module FakeFS
4
+ class File < StringIO
5
+ PATH_SEPARATOR = '/'
6
+
7
+ MODES = [
8
+ READ_ONLY = "r",
9
+ READ_WRITE = "r+",
10
+ WRITE_ONLY = "w",
11
+ READ_WRITE_TRUNCATE = "w+",
12
+ APPEND_WRITE_ONLY = "a",
13
+ APPEND_READ_WRITE = "a+"
14
+ ]
15
+
16
+ FILE_CREATION_MODES = MODES - [READ_ONLY, READ_WRITE]
17
+
18
+ MODE_BITMASK = RealFile::RDONLY |
19
+ RealFile::WRONLY |
20
+ RealFile::RDWR |
21
+ RealFile::APPEND |
22
+ RealFile::CREAT |
23
+ RealFile::EXCL |
24
+ RealFile::NONBLOCK |
25
+ RealFile::TRUNC |
26
+ RealFile::NOCTTY |
27
+ RealFile::SYNC
28
+
29
+ FILE_CREATION_BITMASK = RealFile::CREAT
30
+
31
+ def self.extname(path)
32
+ RealFile.extname(path)
33
+ end
34
+
35
+ def self.join(*parts)
36
+ RealFile.join(parts)
37
+ end
38
+
39
+ def self.exist?(path)
40
+ !!FileSystem.find(path)
41
+ end
42
+
43
+ class << self
44
+ alias_method :exists?, :exist?
45
+
46
+ # Assuming that everyone can read and write files
47
+ alias_method :readable?, :exist?
48
+ alias_method :writable?, :exist?
49
+ end
50
+
51
+ def self.mtime(path)
52
+ if exists?(path)
53
+ FileSystem.find(path).mtime
54
+ else
55
+ raise Errno::ENOENT
56
+ end
57
+ end
58
+
59
+ def self.ctime(path)
60
+ if exists?(path)
61
+ FileSystem.find(path).ctime
62
+ else
63
+ raise Errno::ENOENT
64
+ end
65
+ end
66
+
67
+ def self.utime(atime, mtime, *paths)
68
+ paths.each do |path|
69
+ if exists?(path)
70
+ FileSystem.find(path).mtime = mtime
71
+ else
72
+ raise Errno::ENOENT
73
+ end
74
+ end
75
+
76
+ paths.size
77
+ end
78
+
79
+ def self.size(path)
80
+ read(path).length
81
+ end
82
+
83
+ def self.size?(path)
84
+ if exists?(path) && !size(path).zero?
85
+ true
86
+ else
87
+ nil
88
+ end
89
+ end
90
+
91
+ def self.const_missing(name)
92
+ RealFile.const_get(name)
93
+ end
94
+
95
+ def self.directory?(path)
96
+ if path.respond_to? :entry
97
+ path.entry.is_a? FakeDir
98
+ else
99
+ result = FileSystem.find(path)
100
+ result ? result.entry.is_a?(FakeDir) : false
101
+ end
102
+ end
103
+
104
+ def self.symlink?(path)
105
+ if path.respond_to? :entry
106
+ path.is_a? FakeSymlink
107
+ else
108
+ FileSystem.find(path).is_a? FakeSymlink
109
+ end
110
+ end
111
+
112
+ def self.file?(path)
113
+ if path.respond_to? :entry
114
+ path.entry.is_a? FakeFile
115
+ else
116
+ result = FileSystem.find(path)
117
+ result ? result.entry.is_a?(FakeFile) : false
118
+ end
119
+ end
120
+
121
+ def self.expand_path(*args)
122
+ RealFile.expand_path(*args)
123
+ end
124
+
125
+ def self.basename(*args)
126
+ RealFile.basename(*args)
127
+ end
128
+
129
+ def self.dirname(path)
130
+ RealFile.dirname(path)
131
+ end
132
+
133
+ def self.readlink(path)
134
+ symlink = FileSystem.find(path)
135
+ FileSystem.find(symlink.target).to_s
136
+ end
137
+
138
+ def self.read(path)
139
+ file = new(path)
140
+ if file.exists?
141
+ file.read
142
+ else
143
+ raise Errno::ENOENT
144
+ end
145
+ end
146
+
147
+ def self.readlines(path)
148
+ read(path).split("\n")
149
+ end
150
+
151
+ def self.rename(source, dest)
152
+ if directory?(source) && file?(dest)
153
+ raise Errno::ENOTDIR, "Not a directory - #{source} or #{dest}"
154
+ elsif file?(source) && directory?(dest)
155
+ raise Errno::EISDIR, "Is a directory - #{source} or #{dest}"
156
+ end
157
+
158
+ if target = FileSystem.find(source)
159
+ FileSystem.add(dest, target.entry.clone)
160
+ FileSystem.delete(source)
161
+ else
162
+ raise Errno::ENOENT, "No such file or directory - #{source} or #{dest}"
163
+ end
164
+
165
+ 0
166
+ end
167
+
168
+ def self.link(source, dest)
169
+ if directory?(source)
170
+ raise Errno::EPERM, "Operation not permitted - #{source} or #{dest}"
171
+ end
172
+
173
+ if !exists?(source)
174
+ raise Errno::ENOENT, "No such file or directory - #{source} or #{dest}"
175
+ end
176
+
177
+ if exists?(dest)
178
+ raise Errno::EEXIST, "File exists - #{source} or #{dest}"
179
+ end
180
+
181
+ source = FileSystem.find(source)
182
+ dest = FileSystem.add(dest, source.entry.clone)
183
+ source.link(dest)
184
+
185
+ 0
186
+ end
187
+
188
+ def self.delete(file_name, *additional_file_names)
189
+ if !exists?(file_name)
190
+ raise Errno::ENOENT, "No such file or directory - #{file_name}"
191
+ end
192
+
193
+ FileUtils.rm(file_name)
194
+
195
+ additional_file_names.each do |file_name|
196
+ FileUtils.rm(file_name)
197
+ end
198
+
199
+ additional_file_names.size + 1
200
+ end
201
+
202
+ class << self
203
+ alias_method :unlink, :delete
204
+ end
205
+
206
+ def self.symlink(source, dest)
207
+ FileUtils.ln_s(source, dest)
208
+ end
209
+
210
+ def self.stat(file)
211
+ File::Stat.new(file)
212
+ end
213
+
214
+ def self.lstat(file)
215
+ File::Stat.new(file, true)
216
+ end
217
+
218
+ class Stat
219
+ attr_reader :ctime, :mtime
220
+
221
+ def initialize(file, __lstat = false)
222
+ if !File.exists?(file)
223
+ raise(Errno::ENOENT, "No such file or directory - #{file}")
224
+ end
225
+
226
+ @file = file
227
+ @fake_file = FileSystem.find(@file)
228
+ @__lstat = __lstat
229
+ @ctime = @fake_file.ctime
230
+ @mtime = @fake_file.mtime
231
+ end
232
+
233
+ def symlink?
234
+ File.symlink?(@file)
235
+ end
236
+
237
+ def directory?
238
+ File.directory?(@file)
239
+ end
240
+
241
+ def nlink
242
+ @fake_file.links.size
243
+ end
244
+
245
+ def size
246
+ if @__lstat && symlink?
247
+ @fake_file.target.size
248
+ else
249
+ File.size(@file)
250
+ end
251
+ end
252
+ end
253
+
254
+ attr_reader :path
255
+
256
+ def initialize(path, mode = READ_ONLY, perm = nil)
257
+ @path = path
258
+ @mode = mode
259
+ @file = FileSystem.find(path)
260
+ @autoclose = true
261
+
262
+ check_modes!
263
+
264
+ file_creation_mode? ? create_missing_file : check_file_existence!
265
+
266
+ super(@file.content, mode)
267
+ end
268
+
269
+ def exists?
270
+ true
271
+ end
272
+
273
+ alias_method :tell=, :pos=
274
+ alias_method :sysread, :read
275
+ alias_method :syswrite, :write
276
+
277
+ undef_method :closed_read?
278
+ undef_method :closed_write?
279
+ undef_method :length
280
+ undef_method :size
281
+ undef_method :string
282
+ undef_method :string=
283
+
284
+ def ioctl(integer_cmd, arg)
285
+ raise NotImplementedError
286
+ end
287
+
288
+ def read_nonblock(maxlen, outbuf = nil)
289
+ raise NotImplementedError
290
+ end
291
+
292
+ def stat
293
+ self.class.stat(@path)
294
+ end
295
+
296
+ def lstat
297
+ self.class.lstat(@path)
298
+ end
299
+
300
+ def sysseek(position, whence = SEEK_SET)
301
+ seek(position, whence)
302
+ pos
303
+ end
304
+
305
+ alias_method :to_i, :fileno
306
+
307
+ def to_io
308
+ self
309
+ end
310
+
311
+ def write_nonblock(string)
312
+ raise NotImplementedError
313
+ end
314
+
315
+ def readpartial(maxlen, outbuf = nil)
316
+ raise NotImplementedError
317
+ end
318
+
319
+ def atime
320
+ raise NotImplementedError
321
+ end
322
+
323
+ def chmod(mode_int)
324
+ raise NotImplementedError
325
+ end
326
+
327
+ def chown(owner_int, group_int)
328
+ raise NotImplementedError
329
+ end
330
+
331
+ def ctime
332
+ self.class.ctime(@path)
333
+ end
334
+
335
+ def flock(locking_constant)
336
+ raise NotImplementedError
337
+ end
338
+
339
+ def mtime
340
+ self.class.mtime(@path)
341
+ end
342
+
343
+ if RUBY_VERSION >= "1.9"
344
+ def binmode?
345
+ raise NotImplementedError
346
+ end
347
+
348
+ def close_on_exec=(bool)
349
+ raise NotImplementedError
350
+ end
351
+
352
+ def close_on_exec?
353
+ raise NotImplementedError
354
+ end
355
+
356
+ def to_path
357
+ raise NotImplementedError
358
+ end
359
+ end
360
+
361
+ if RUBY_VERSION >= "1.9.2"
362
+ attr_writer :autoclose
363
+
364
+ def autoclose?
365
+ @autoclose
366
+ end
367
+
368
+ alias_method :fdatasync, :flush
369
+
370
+ def size
371
+ File.size(@path)
372
+ end
373
+ end
374
+
375
+ private
376
+
377
+ def check_modes!
378
+ StringIO.new("", @mode)
379
+ end
380
+
381
+ def check_file_existence!
382
+ raise Errno::ENOENT, @path unless @file
383
+ end
384
+
385
+ def file_creation_mode?
386
+ mode_in?(FILE_CREATION_MODES) || mode_in_bitmask?(FILE_CREATION_BITMASK)
387
+ end
388
+
389
+ def mode_in?(list)
390
+ list.any? { |element| @mode.include?(element) } if @mode.respond_to?(:include?)
391
+ end
392
+
393
+ def mode_in_bitmask?(mask)
394
+ (@mode & mask) != 0 if @mode.is_a?(Integer)
395
+ end
396
+
397
+ def create_missing_file
398
+ if !File.exists?(@path)
399
+ @file = FileSystem.add(path, FakeFile.new)
400
+ end
401
+ end
402
+ end
403
+ end