fakefs 0.4.0 → 0.4.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.
@@ -1,13 +1,19 @@
1
1
  module FakeFS
2
- class FakeDir < Hash
3
- attr_accessor :name, :parent
4
- attr_reader :ctime, :mtime
2
+ class FakeDir
3
+ attr_accessor :name, :parent, :mode, :uid, :gid, :mtime, :atime
4
+ attr_reader :ctime, :content
5
5
 
6
6
  def initialize(name = nil, parent = nil)
7
- @name = name
8
- @parent = parent
9
- @ctime = Time.now
10
- @mtime = @ctime
7
+ @name = name
8
+ @parent = parent
9
+ @ctime = Time.now
10
+ @mtime = @ctime
11
+ @atime = @ctime
12
+ @mode = 0100000 + (0777 - File.umask)
13
+ @uid = Process.uid
14
+ @gid = Process.gid
15
+ @content = ""
16
+ @entries = {}
11
17
  end
12
18
 
13
19
  def entry
@@ -15,12 +21,12 @@ module FakeFS
15
21
  end
16
22
 
17
23
  def inspect
18
- "(FakeDir name:#{name.inspect} parent:#{parent.to_s.inspect} size:#{size})"
24
+ "(FakeDir name:#{name.inspect} parent:#{parent.to_s.inspect} size:#{@entries.size})"
19
25
  end
20
26
 
21
27
  def clone(parent = nil)
22
28
  clone = Marshal.load(Marshal.dump(self))
23
- clone.each do |key, value|
29
+ clone.entries.each do |value|
24
30
  value.parent = clone
25
31
  end
26
32
  clone.parent = parent if parent
@@ -37,11 +43,31 @@ module FakeFS
37
43
  end
38
44
  end
39
45
 
46
+ def empty?
47
+ @entries.empty?
48
+ end
49
+
50
+ def entries
51
+ @entries.values
52
+ end
53
+
54
+ def matches(pattern)
55
+ @entries.reject {|k,v| pattern !~ k }.values
56
+ end
57
+
58
+ def [](name)
59
+ @entries[name]
60
+ end
61
+
62
+ def []=(name, value)
63
+ @entries[name] = value
64
+ end
65
+
40
66
  def delete(node = self)
41
67
  if node == self
42
68
  parent.delete(self)
43
69
  else
44
- super(node.name)
70
+ @entries.delete(node.name)
45
71
  end
46
72
  end
47
73
  end
@@ -1,11 +1,12 @@
1
1
  module FakeFS
2
2
  class FakeFile
3
- attr_accessor :name, :parent, :content, :mtime
3
+ attr_accessor :name, :parent, :content, :mtime, :atime, :mode, :uid, :gid
4
4
  attr_reader :ctime
5
5
 
6
6
  class Inode
7
7
  def initialize(file_owner)
8
- @content = ""
8
+ #1.9.3 when possible set default external encoding
9
+ @content = "".respond_to?(:encode) ? "".encode(Encoding.default_external) : ""
9
10
  @links = [file_owner]
10
11
  end
11
12
 
@@ -34,6 +35,10 @@ module FakeFS
34
35
  @inode = Inode.new(self)
35
36
  @ctime = Time.now
36
37
  @mtime = @ctime
38
+ @atime = @ctime
39
+ @mode = 0100000 + (0666 - File.umask)
40
+ @uid = Process.uid
41
+ @gid = Process.gid
37
42
  end
38
43
 
39
44
  attr_accessor :inode
@@ -2,8 +2,6 @@ require 'stringio'
2
2
 
3
3
  module FakeFS
4
4
  class File < StringIO
5
- PATH_SEPARATOR = '/'
6
-
7
5
  MODES = [
8
6
  READ_ONLY = "r",
9
7
  READ_WRITE = "r+",
@@ -23,8 +21,8 @@ module FakeFS
23
21
  RealFile::EXCL |
24
22
  RealFile::NONBLOCK |
25
23
  RealFile::TRUNC |
26
- RealFile::NOCTTY |
27
- RealFile::SYNC
24
+ (RealFile.const_defined?(:NOCTTY) ? RealFile::NOCTTY : 0) |
25
+ (RealFile.const_defined?(:SYNC) ? RealFile::SYNC : 0)
28
26
 
29
27
  FILE_CREATION_BITMASK = RealFile::CREAT
30
28
 
@@ -69,9 +67,18 @@ module FakeFS
69
67
  end
70
68
  end
71
69
 
70
+ def self.atime(path)
71
+ if exists?(path)
72
+ FileSystem.find(path).atime
73
+ else
74
+ raise Errno::ENOENT
75
+ end
76
+ end
77
+
72
78
  def self.utime(atime, mtime, *paths)
73
79
  paths.each do |path|
74
80
  if exists?(path)
81
+ FileSystem.find(path).atime = atime
75
82
  FileSystem.find(path).mtime = mtime
76
83
  else
77
84
  raise Errno::ENOENT
@@ -140,9 +147,10 @@ module FakeFS
140
147
  symlink.target
141
148
  end
142
149
 
143
- def self.read(path)
150
+ def self.read(path, *args)
144
151
  file = new(path)
145
152
  if file.exists?
153
+ FileSystem.find(path).atime = Time.now
146
154
  file.read
147
155
  else
148
156
  raise Errno::ENOENT
@@ -224,8 +232,24 @@ module FakeFS
224
232
  return RealFile.split(path)
225
233
  end
226
234
 
235
+ def self.chmod(mode_int, filename)
236
+ FileSystem.find(filename).mode = 0100000 + mode_int
237
+ end
238
+
239
+ def self.chown(owner_int, group_int, filename)
240
+ file = FileSystem.find(filename)
241
+ owner_int.is_a?(Fixnum) or raise TypeError, "can't convert String into Integer"
242
+ group_int.is_a?(Fixnum) or raise TypeError, "can't convert String into Integer"
243
+ file.uid = owner_int
244
+ file.gid = group_int
245
+ end
246
+
247
+ def self.umask
248
+ RealFile.umask
249
+ end
250
+
227
251
  class Stat
228
- attr_reader :ctime, :mtime
252
+ attr_reader :ctime, :mtime, :atime, :mode, :uid, :gid
229
253
 
230
254
  def initialize(file, __lstat = false)
231
255
  if !File.exists?(file)
@@ -237,6 +261,10 @@ module FakeFS
237
261
  @__lstat = __lstat
238
262
  @ctime = @fake_file.ctime
239
263
  @mtime = @fake_file.mtime
264
+ @atime = @fake_file.atime
265
+ @mode = @fake_file.mode
266
+ @uid = @fake_file.uid
267
+ @gid = @fake_file.gid
240
268
  end
241
269
 
242
270
  def symlink?
@@ -247,6 +275,14 @@ module FakeFS
247
275
  File.directory?(@file)
248
276
  end
249
277
 
278
+ # assumes, like above, that all files are readable and writable
279
+ def readable?
280
+ true
281
+ end
282
+ def writable?
283
+ true
284
+ end
285
+
250
286
  def nlink
251
287
  @fake_file.links.size
252
288
  end
@@ -258,13 +294,23 @@ module FakeFS
258
294
  File.size(@file)
259
295
  end
260
296
  end
297
+
298
+ def zero?
299
+ size == 0
300
+ end
301
+
302
+ include Comparable
303
+
304
+ def <=>(other)
305
+ @mtime <=> other.mtime
306
+ end
261
307
  end
262
308
 
263
309
  attr_reader :path
264
310
 
265
311
  def initialize(path, mode = READ_ONLY, perm = nil)
266
312
  @path = path
267
- @mode = mode
313
+ @mode = mode.is_a?(Hash) ? (mode[:mode] || READ_ONLY) : mode
268
314
  @file = FileSystem.find(path)
269
315
  @autoclose = true
270
316
 
@@ -272,13 +318,19 @@ module FakeFS
272
318
 
273
319
  file_creation_mode? ? create_missing_file : check_file_existence!
274
320
 
275
- super(@file.content, mode)
321
+ super(@file.content, @mode)
276
322
  end
277
323
 
278
324
  def exists?
279
325
  true
280
326
  end
281
327
 
328
+ def write(str)
329
+ val = super(str)
330
+ @file.mtime = Time.now
331
+ val
332
+ end
333
+
282
334
  alias_method :tell=, :pos=
283
335
  alias_method :sysread, :read
284
336
  alias_method :syswrite, :write
@@ -289,6 +341,11 @@ module FakeFS
289
341
  undef_method :size
290
342
  undef_method :string
291
343
  undef_method :string=
344
+ if RUBY_PLATFORM == 'java'
345
+ undef_method :to_channel
346
+ undef_method :to_outputstream
347
+ undef_method :to_inputstream
348
+ end
292
349
 
293
350
  def ioctl(integer_cmd, arg)
294
351
  raise NotImplementedError
@@ -326,15 +383,7 @@ module FakeFS
326
383
  end
327
384
 
328
385
  def atime
329
- raise NotImplementedError
330
- end
331
-
332
- def chmod(mode_int)
333
- raise NotImplementedError
334
- end
335
-
336
- def chown(owner_int, group_int)
337
- raise NotImplementedError
386
+ self.class.atime(@path)
338
387
  end
339
388
 
340
389
  def ctime
@@ -349,6 +398,17 @@ module FakeFS
349
398
  self.class.mtime(@path)
350
399
  end
351
400
 
401
+ def chmod(mode_int)
402
+ @file.mode = 0100000 + mode_int
403
+ end
404
+
405
+ def chown(owner_int, group_int)
406
+ owner_int.is_a?(Fixnum) or raise TypeError, "can't convert String into Integer"
407
+ group_int.is_a?(Fixnum) or raise TypeError, "can't convert String into Integer"
408
+ @file.uid = owner_int
409
+ @file.gid = group_int
410
+ end
411
+
352
412
  if RUBY_VERSION >= "1.9"
353
413
  def binmode?
354
414
  raise NotImplementedError
@@ -374,6 +434,14 @@ module FakeFS
374
434
  @autoclose
375
435
  end
376
436
 
437
+ def autoclose?
438
+ @autoclose ? true : false
439
+ end
440
+
441
+ def autoclose=(autoclose)
442
+ @autoclose = autoclose
443
+ end
444
+
377
445
  alias_method :fdatasync, :flush
378
446
 
379
447
  def size
@@ -381,6 +449,11 @@ module FakeFS
381
449
  end
382
450
  end
383
451
 
452
+ if RUBY_VERSION >= "1.9.3"
453
+ def advise(advice, offset=0, len=0)
454
+ end
455
+ end
456
+
384
457
  private
385
458
 
386
459
  def check_modes!
@@ -16,7 +16,7 @@ module FakeFS
16
16
  end
17
17
 
18
18
  def files
19
- fs.values
19
+ fs.entries
20
20
  end
21
21
 
22
22
  def find(path)
@@ -46,19 +46,20 @@ module FakeFS
46
46
 
47
47
  # copies directories and files from the real filesystem
48
48
  # into our fake one
49
- def clone(path)
49
+ def clone(path, target = nil)
50
50
  path = File.expand_path(path)
51
51
  pattern = File.join(path, '**', '*')
52
52
  files = RealFile.file?(path) ? [path] : [path] + RealDir.glob(pattern, RealFile::FNM_DOTMATCH)
53
53
 
54
54
  files.each do |f|
55
+ target_path = target ? f.gsub(path, target) : f
55
56
  if RealFile.file?(f)
56
57
  FileUtils.mkdir_p(File.dirname(f))
57
- File.open(f, File::WRITE_ONLY) do |g|
58
+ File.open(target_path, File::WRITE_ONLY) do |g|
58
59
  g.print RealFile.open(f){|h| h.read }
59
60
  end
60
61
  elsif RealFile.directory?(f)
61
- FileUtils.mkdir_p(f)
62
+ FileUtils.mkdir_p(target_path)
62
63
  elsif RealFile.symlink?(f)
63
64
  FileUtils.ln_s()
64
65
  end
@@ -85,7 +86,7 @@ module FakeFS
85
86
  end
86
87
 
87
88
  def path_parts(path)
88
- path.split(File::PATH_SEPARATOR).reject { |part| part.empty? }
89
+ drop_root(path.split(File::SEPARATOR)).reject { |part| part.empty? }
89
90
  end
90
91
 
91
92
  def normalize_path(path)
@@ -103,6 +104,14 @@ module FakeFS
103
104
 
104
105
  private
105
106
 
107
+ def drop_root(path_parts)
108
+ # we need to remove parts from root dir at least for windows and jruby
109
+ return path_parts if path_parts.nil? || path_parts.empty?
110
+ root = File.expand_path('/').split(File::SEPARATOR).first
111
+ path_parts.shift if path_parts.first == root
112
+ path_parts
113
+ end
114
+
106
115
  def find_recurser(dir, parts)
107
116
  return [] unless dir.respond_to? :[]
108
117
 
@@ -113,17 +122,16 @@ module FakeFS
113
122
  when ['*']
114
123
  parts = [] # end recursion
115
124
  directories_under(dir).map do |d|
116
- d.values.select{|f| f.is_a?(FakeFile) || f.is_a?(FakeDir) }
125
+ d.entries.select{|f| f.is_a?(FakeFile) || f.is_a?(FakeDir) }
117
126
  end.flatten.uniq
118
127
  when []
119
128
  parts = [] # end recursion
120
- dir.values.flatten.uniq
129
+ dir.entries.flatten.uniq
121
130
  else
122
131
  directories_under(dir)
123
132
  end
124
133
  else
125
- regexp_pattern = /\A#{pattern.gsub('?','.').gsub('*', '.*').gsub(/\{(.*?)\}/) { "(#{$1.gsub(',', '|')})" }}\Z/
126
- dir.reject {|k,v| regexp_pattern !~ k }.values
134
+ dir.matches /\A#{pattern.gsub('.', '\.').gsub('?','.').gsub('*', '.*').gsub(/\{(.*?)\}/) { "(#{$1.gsub(',', '|')})" }}\Z/
127
135
  end
128
136
 
129
137
  if parts.empty? # we're done recursing
@@ -134,7 +142,7 @@ module FakeFS
134
142
  end
135
143
 
136
144
  def directories_under(dir)
137
- children = dir.values.select{|f| f.is_a? FakeDir}
145
+ children = dir.entries.select{|f| f.is_a? FakeDir}
138
146
  ([dir] + children + children.map{|c| directories_under(c)}).flatten.uniq
139
147
  end
140
148
  end
@@ -11,7 +11,7 @@ module FakeFS
11
11
  def mkdir(path)
12
12
  parent = path.split('/')
13
13
  parent.pop
14
- raise Errno::ENOENT, "No such file or directory - #{path}" unless parent.join == "" || FileSystem.find(parent.join('/'))
14
+ raise Errno::ENOENT, "No such file or directory - #{path}" unless parent.join == "" || parent.join == "." || FileSystem.find(parent.join('/'))
15
15
  raise Errno::EEXIST, "File exists - #{path}" if FileSystem.find(path)
16
16
  FileSystem.add(path, FakeDir.new)
17
17
  end
@@ -23,14 +23,14 @@ module FakeFS
23
23
  parent.pop
24
24
  raise Errno::ENOENT, "No such file or directory - #{l}" unless parent.join == "" || FileSystem.find(parent.join('/'))
25
25
  raise Errno::ENOENT, l unless FileSystem.find(l)
26
- raise Errno::ENOTEMPTY, l unless FileSystem.find(l).values.empty?
26
+ raise Errno::ENOTEMPTY, l unless FileSystem.find(l).empty?
27
27
  rm(l)
28
28
  end
29
29
  end
30
30
 
31
31
  def rm(list, options = {})
32
32
  Array(list).each do |path|
33
- FileSystem.delete(path) or raise Errno::ENOENT.new(path)
33
+ FileSystem.delete(path) or (!options[:force] && raise(Errno::ENOENT.new(path)))
34
34
  end
35
35
  end
36
36
 
@@ -56,50 +56,58 @@ module FakeFS
56
56
  end
57
57
 
58
58
  def cp(src, dest)
59
- dst_file = FileSystem.find(dest)
60
- src_file = FileSystem.find(src)
61
-
62
- if !src_file
63
- raise Errno::ENOENT, src
59
+ if src.is_a?(Array) && !File.directory?(dest)
60
+ raise Errno::ENOTDIR, dest
64
61
  end
65
62
 
66
- if File.directory? src_file
67
- raise Errno::EISDIR, src
68
- end
63
+ Array(src).each do |src|
64
+ dst_file = FileSystem.find(dest)
65
+ src_file = FileSystem.find(src)
69
66
 
70
- if dst_file && File.directory?(dst_file)
71
- FileSystem.add(File.join(dest, src), src_file.entry.clone(dst_file))
72
- else
73
- FileSystem.delete(dest)
74
- FileSystem.add(dest, src_file.entry.clone)
67
+ if !src_file
68
+ raise Errno::ENOENT, src
69
+ end
70
+
71
+ if File.directory? src_file
72
+ raise Errno::EISDIR, src
73
+ end
74
+
75
+ if dst_file && File.directory?(dst_file)
76
+ FileSystem.add(File.join(dest, src), src_file.entry.clone(dst_file))
77
+ else
78
+ FileSystem.delete(dest)
79
+ FileSystem.add(dest, src_file.entry.clone)
80
+ end
75
81
  end
76
82
  end
77
83
 
78
84
  def cp_r(src, dest)
79
- # This error sucks, but it conforms to the original Ruby
80
- # method.
81
- raise "unknown file type: #{src}" unless dir = FileSystem.find(src)
85
+ Array(src).each do |src|
86
+ # This error sucks, but it conforms to the original Ruby
87
+ # method.
88
+ raise "unknown file type: #{src}" unless dir = FileSystem.find(src)
82
89
 
83
- new_dir = FileSystem.find(dest)
90
+ new_dir = FileSystem.find(dest)
84
91
 
85
- if new_dir && !File.directory?(dest)
86
- raise Errno::EEXIST, dest
87
- end
92
+ if new_dir && !File.directory?(dest)
93
+ raise Errno::EEXIST, dest
94
+ end
88
95
 
89
- if !new_dir && !FileSystem.find(dest+'/../')
90
- raise Errno::ENOENT, dest
91
- end
96
+ if !new_dir && !FileSystem.find(dest+'/../')
97
+ raise Errno::ENOENT, dest
98
+ end
92
99
 
93
- # This last bit is a total abuse and should be thought hard
94
- # about and cleaned up.
95
- if new_dir
96
- if src[-2..-1] == '/.'
97
- dir.values.each{|f| new_dir[f.name] = f.clone(new_dir) }
100
+ # This last bit is a total abuse and should be thought hard
101
+ # about and cleaned up.
102
+ if new_dir
103
+ if src[-2..-1] == '/.'
104
+ dir.entries.each{|f| new_dir[f.name] = f.clone(new_dir) }
105
+ else
106
+ new_dir[dir.name] = dir.entry.clone(new_dir)
107
+ end
98
108
  else
99
- new_dir[dir.name] = dir.entry.clone(new_dir)
109
+ FileSystem.add(dest, dir.entry.clone)
100
110
  end
101
- else
102
- FileSystem.add(dest, dir.entry.clone)
103
111
  end
104
112
  end
105
113
 
@@ -118,7 +126,11 @@ module FakeFS
118
126
  def chown(user, group, list, options={})
119
127
  list = Array(list)
120
128
  list.each do |f|
121
- unless File.exists?(f)
129
+ if File.exists?(f)
130
+ uid = (user.to_s.match(/[0-9]+/) ? user.to_i : Etc.getpwnam(user).uid)
131
+ gid = (group.to_s.match(/[0-9]+/) ? group.to_i : Etc.getgrnam(group).gid)
132
+ File.chown(uid, gid, f)
133
+ else
122
134
  raise Errno::ENOENT, f
123
135
  end
124
136
  end
@@ -126,17 +138,48 @@ module FakeFS
126
138
  end
127
139
 
128
140
  def chown_R(user, group, list, options={})
129
- chown(user, group, list, options={})
141
+ list = Array(list)
142
+ list.each do |file|
143
+ chown(user, group, file)
144
+ [FileSystem.find("#{file}/**/**")].flatten.each do |f|
145
+ chown(user, group, f.to_s)
146
+ end
147
+ end
148
+ list
149
+ end
150
+
151
+ def chmod(mode, list, options={})
152
+ list = Array(list)
153
+ list.each do |f|
154
+ if File.exists?(f)
155
+ File.chmod(mode, f)
156
+ else
157
+ raise Errno::ENOENT, f
158
+ end
159
+ end
160
+ list
161
+ end
162
+
163
+ def chmod_R(mode, list, options={})
164
+ list = Array(list)
165
+ list.each do |file|
166
+ chmod(mode, file)
167
+ [FileSystem.find("#{file}/**/**")].flatten.each do |f|
168
+ chmod(mode, f.to_s)
169
+ end
170
+ end
171
+ list
130
172
  end
131
173
 
132
174
  def touch(list, options={})
133
175
  Array(list).each do |f|
134
- directory = File.dirname(f)
135
- # FIXME this explicit check for '.' shouldn't need to happen
136
- if File.exists?(directory) || directory == '.'
137
- FileSystem.add(f, FakeFile.new)
176
+ if fs = FileSystem.find(f)
177
+ now = Time.now
178
+ fs.mtime = now
179
+ fs.atime = now
138
180
  else
139
- raise Errno::ENOENT, f
181
+ f = File.open(f, 'w')
182
+ f.close
140
183
  end
141
184
  end
142
185
  end