~ubuntu-branches/ubuntu/wily/puppet/wily

« back to all changes in this revision

Viewing changes to spec/unit/file_system/file_spec.rb

  • Committer: Package Import Robot
  • Author(s): Stig Sandbeck Mathisen
  • Date: 2014-04-17 14:50:28 UTC
  • mfrom: (3.1.59 sid)
  • Revision ID: package-import@ubuntu.com-20140417145028-j3p3dwvp8ggpzvaf
Tags: 3.5.1-1
ImportedĀ upstreamĀ releaseĀ 3.5.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
require 'spec_helper'
2
 
require 'puppet/file_system'
3
 
require 'puppet/util/platform'
4
 
 
5
 
describe Puppet::FileSystem::File do
6
 
  include PuppetSpec::Files
7
 
 
8
 
  context "#exclusive_open" do
9
 
    it "opens ands allows updating of an existing file" do
10
 
      file = Puppet::FileSystem::File.new(file_containing("file_to_update", "the contents"))
11
 
 
12
 
      file.exclusive_open(0660, 'r+') do |fh|
13
 
        old = fh.read
14
 
        fh.truncate(0)
15
 
        fh.rewind
16
 
        fh.write("updated #{old}")
17
 
      end
18
 
 
19
 
      expect(file.read).to eq("updated the contents")
20
 
    end
21
 
 
22
 
    it "opens, creates ands allows updating of a new file" do
23
 
      file = Puppet::FileSystem::File.new(tmpfile("file_to_update"))
24
 
 
25
 
      file.exclusive_open(0660, 'w') do |fh|
26
 
        fh.write("updated new file")
27
 
      end
28
 
 
29
 
      expect(file.read).to eq("updated new file")
30
 
    end
31
 
 
32
 
    it "excludes other processes from updating at the same time", :unless => Puppet::Util::Platform.windows? do
33
 
      file = Puppet::FileSystem::File.new(file_containing("file_to_update", "0"))
34
 
 
35
 
      increment_counter_in_multiple_processes(file, 5, 'r+')
36
 
 
37
 
      expect(file.read).to eq("5")
38
 
    end
39
 
 
40
 
    it "excludes other processes from updating at the same time even when creating the file", :unless => Puppet::Util::Platform.windows? do
41
 
      file = Puppet::FileSystem::File.new(tmpfile("file_to_update"))
42
 
 
43
 
      increment_counter_in_multiple_processes(file, 5, 'a+')
44
 
 
45
 
      expect(file.read).to eq("5")
46
 
    end
47
 
 
48
 
    it "times out if the lock cannot be aquired in a specified amount of time", :unless => Puppet::Util::Platform.windows? do
49
 
      file = tmpfile("file_to_update")
50
 
 
51
 
      child = spawn_process_that_locks(file)
52
 
 
53
 
      expect do
54
 
        Puppet::FileSystem::File.new(file).exclusive_open(0666, 'a', 0.1) do |f|
55
 
        end
56
 
      end.to raise_error(Timeout::Error)
57
 
 
58
 
      Process.kill(9, child)
59
 
    end
60
 
 
61
 
    def spawn_process_that_locks(file)
62
 
      read, write = IO.pipe
63
 
 
64
 
      child = Kernel.fork do
65
 
        read.close
66
 
        Puppet::FileSystem::File.new(file).exclusive_open(0666, 'a') do |fh|
67
 
          write.write(true)
68
 
          write.close
69
 
          sleep 10
70
 
        end
71
 
      end
72
 
 
73
 
      write.close
74
 
      read.read
75
 
      read.close
76
 
 
77
 
      child
78
 
    end
79
 
 
80
 
    def increment_counter_in_multiple_processes(file, num_procs, options)
81
 
      children = []
82
 
      5.times do |number|
83
 
        children << Kernel.fork do
84
 
          file.exclusive_open(0660, options) do |fh|
85
 
            fh.rewind
86
 
            contents = (fh.read || 0).to_i
87
 
            fh.truncate(0)
88
 
            fh.rewind
89
 
            fh.write((contents + 1).to_s)
90
 
          end
91
 
          exit(0)
92
 
        end
93
 
      end
94
 
 
95
 
      children.each { |pid| Process.wait(pid) }
96
 
    end
97
 
  end
98
 
 
99
 
  describe "symlink",
100
 
    :if => ! Puppet.features.manages_symlinks? &&
101
 
    Puppet.features.microsoft_windows? do
102
 
 
103
 
    let (:file) { Puppet::FileSystem::File.new(tmpfile("somefile")) }
104
 
    let (:missing_file) { Puppet::FileSystem::File.new(tmpfile("missingfile")) }
105
 
    let (:expected_msg) { "This version of Windows does not support symlinks.  Windows Vista / 2008 or higher is required." }
106
 
 
107
 
    before :each do
108
 
      FileUtils.touch(file.path)
109
 
    end
110
 
 
111
 
    it "should raise an error when trying to create a symlink" do
112
 
      expect { file.symlink('foo') }.to raise_error(Puppet::Util::Windows::Error)
113
 
    end
114
 
 
115
 
    it "should return false when trying to check if a path is a symlink" do
116
 
      file.symlink?.should be_false
117
 
    end
118
 
 
119
 
    it "should raise an error when trying to read a symlink" do
120
 
      expect { file.readlink }.to raise_error(Puppet::Util::Windows::Error)
121
 
    end
122
 
 
123
 
    it "should return a File::Stat instance when calling stat on an existing file" do
124
 
      file.stat.should be_instance_of(File::Stat)
125
 
    end
126
 
 
127
 
    it "should raise Errno::ENOENT when calling stat on a missing file" do
128
 
      expect { missing_file.stat }.to raise_error(Errno::ENOENT)
129
 
    end
130
 
 
131
 
    it "should fall back to stat when trying to lstat a file" do
132
 
      Puppet::Util::Windows::File.expects(:stat).with(file.path)
133
 
 
134
 
      file.lstat
135
 
    end
136
 
  end
137
 
 
138
 
  describe "symlink", :if => Puppet.features.manages_symlinks? do
139
 
 
140
 
    let (:file) { Puppet::FileSystem::File.new(tmpfile("somefile")) }
141
 
    let (:missing_file) { Puppet::FileSystem::File.new(tmpfile("missingfile")) }
142
 
    let (:dir) { Puppet::FileSystem::File.new(tmpdir("somedir")) }
143
 
 
144
 
    before :each do
145
 
      FileUtils.touch(file.path)
146
 
    end
147
 
 
148
 
    it "should return true for exist? on a present file" do
149
 
      file.exist?.should be_true
150
 
      Puppet::FileSystem::File.exist?(file.path).should be_true
151
 
    end
152
 
 
153
 
    it "should return false for exist? on a non-existant file" do
154
 
      missing_file.exist?.should be_false
155
 
      Puppet::FileSystem::File.exist?(missing_file.path).should be_false
156
 
    end
157
 
 
158
 
    it "should return true for exist? on a present directory" do
159
 
      dir.exist?.should be_true
160
 
      Puppet::FileSystem::File.exist?(dir.path).should be_true
161
 
    end
162
 
 
163
 
    it "should return false for exist? on a dangling symlink" do
164
 
      symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
165
 
      missing_file.symlink(symlink.path)
166
 
 
167
 
      missing_file.exist?.should be_false
168
 
      symlink.exist?.should be_false
169
 
    end
170
 
 
171
 
    it "should return true for exist? on valid symlinks" do
172
 
      [file, dir].each do |target|
173
 
        symlink = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_link"))
174
 
        target.symlink(symlink.path)
175
 
 
176
 
        target.exist?.should be_true
177
 
        symlink.exist?.should be_true
178
 
      end
179
 
    end
180
 
 
181
 
    it "should not create a symlink when the :noop option is specified" do
182
 
      [file, dir].each do |target|
183
 
        symlink = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_link"))
184
 
        target.symlink(symlink.path, { :noop => true })
185
 
 
186
 
        target.exist?.should be_true
187
 
        symlink.exist?.should be_false
188
 
      end
189
 
    end
190
 
 
191
 
    it "should raise Errno::EEXIST if trying to create a file / directory symlink when the symlink path already exists as a file" do
192
 
      existing_file = Puppet::FileSystem::File.new(tmpfile("#{file.path.basename.to_s}_link"))
193
 
      FileUtils.touch(existing_file.path)
194
 
 
195
 
      [file, dir].each do |target|
196
 
        expect { target.symlink(existing_file.path) }.to raise_error(Errno::EEXIST)
197
 
 
198
 
        existing_file.exist?.should be_true
199
 
        existing_file.symlink?.should be_false
200
 
      end
201
 
    end
202
 
 
203
 
    it "should silently fail if trying to create a file / directory symlink when the symlink path already exists as a directory" do
204
 
      existing_dir = Puppet::FileSystem::File.new(tmpdir("#{file.path.basename.to_s}_dir"))
205
 
 
206
 
      [file, dir].each do |target|
207
 
        target.symlink(existing_dir.path).should == 0
208
 
 
209
 
        existing_dir.exist?.should be_true
210
 
        File.directory?(existing_dir.path).should be_true
211
 
        existing_dir.symlink?.should be_false
212
 
      end
213
 
    end
214
 
 
215
 
    it "should silently fail to modify an existing directory symlink to reference a new file or directory" do
216
 
      [file, dir].each do |target|
217
 
        existing_dir = Puppet::FileSystem::File.new(tmpdir("#{target.path.basename.to_s}_dir"))
218
 
        symlink = Puppet::FileSystem::File.new(tmpfile("#{existing_dir.path.basename.to_s}_link"))
219
 
        existing_dir.symlink(symlink.path)
220
 
 
221
 
        symlink.readlink.should == existing_dir.path.to_s
222
 
 
223
 
        # now try to point it at the new target, no error raised, but file system unchanged
224
 
        target.symlink(symlink.path).should == 0
225
 
        symlink.readlink.should == existing_dir.path.to_s
226
 
      end
227
 
    end
228
 
 
229
 
    it "should raise Errno::EEXIST if trying to modify a file symlink to reference a new file or directory" do
230
 
      symlink = Puppet::FileSystem::File.new(tmpfile("#{file.path.basename.to_s}_link"))
231
 
      file_2 = Puppet::FileSystem::File.new(tmpfile("#{file.path.basename.to_s}_2"))
232
 
      FileUtils.touch(file_2.path)
233
 
      # symlink -> file_2
234
 
      file_2.symlink(symlink.path)
235
 
 
236
 
      [file, dir].each do |target|
237
 
        expect { target.symlink(symlink.path) }.to raise_error(Errno::EEXIST)
238
 
        symlink.readlink.should == file_2.path.to_s
239
 
      end
240
 
    end
241
 
 
242
 
    it "should delete the existing file when creating a file / directory symlink with :force when the symlink path exists as a file" do
243
 
      [file, dir].each do |target|
244
 
        existing_file = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_existing"))
245
 
        FileUtils.touch(existing_file.path)
246
 
        existing_file.symlink?.should be_false
247
 
 
248
 
        target.symlink(existing_file.path, { :force => true })
249
 
 
250
 
        existing_file.symlink?.should be_true
251
 
        existing_file.readlink.should == target.path.to_s
252
 
      end
253
 
    end
254
 
 
255
 
    it "should modify an existing file symlink when using :force to reference a new file or directory" do
256
 
      [file, dir].each do |target|
257
 
        existing_file = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_existing"))
258
 
        FileUtils.touch(existing_file.path)
259
 
        existing_symlink = Puppet::FileSystem::File.new(tmpfile("#{existing_file.path.basename.to_s}_link"))
260
 
        existing_file.symlink(existing_symlink.path)
261
 
 
262
 
        existing_symlink.readlink.should == existing_file.path.to_s
263
 
 
264
 
        target.symlink(existing_symlink.path, { :force => true })
265
 
 
266
 
        existing_symlink.readlink.should == target.path.to_s
267
 
      end
268
 
    end
269
 
 
270
 
    it "should silently fail if trying to overwrite an existing directory with a new symlink when using :force to reference a file or directory" do
271
 
      [file, dir].each do |target|
272
 
        existing_dir = Puppet::FileSystem::File.new(tmpdir("#{target.path.basename.to_s}_existing"))
273
 
 
274
 
        target.symlink(existing_dir.path, { :force => true }).should == 0
275
 
 
276
 
        existing_dir.symlink?.should be_false
277
 
      end
278
 
    end
279
 
 
280
 
    it "should silently fail if trying to modify an existing directory symlink when using :force to reference a new file or directory" do
281
 
      [file, dir].each do |target|
282
 
        existing_dir = Puppet::FileSystem::File.new(tmpdir("#{target.path.basename.to_s}_existing"))
283
 
        existing_symlink = Puppet::FileSystem::File.new(tmpfile("#{existing_dir.path.basename.to_s}_link"))
284
 
        existing_dir.symlink(existing_symlink.path)
285
 
 
286
 
        existing_symlink.readlink.should == existing_dir.path.to_s
287
 
 
288
 
        target.symlink(existing_symlink.path, { :force => true }).should == 0
289
 
 
290
 
        existing_symlink.readlink.should == existing_dir.path.to_s
291
 
      end
292
 
    end
293
 
 
294
 
    it "should accept a string, Pathname or object with to_str (Puppet::Util::WatchedFile) for exist?" do
295
 
      [ tmpfile('bogus1'),
296
 
        Pathname.new(tmpfile('bogus2')),
297
 
        Puppet::Util::WatchedFile.new(tmpfile('bogus3'))
298
 
        ].each { |f| Puppet::FileSystem::File.exist?(f).should be_false  }
299
 
    end
300
 
 
301
 
    it "should return a File::Stat instance when calling stat on an existing file" do
302
 
      file.stat.should be_instance_of(File::Stat)
303
 
    end
304
 
 
305
 
    it "should raise Errno::ENOENT when calling stat on a missing file" do
306
 
      expect { missing_file.stat }.to raise_error(Errno::ENOENT)
307
 
    end
308
 
 
309
 
    it "should be able to create a symlink, and verify it with symlink?" do
310
 
      symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
311
 
      file.symlink(symlink.path)
312
 
 
313
 
      symlink.symlink?.should be_true
314
 
    end
315
 
 
316
 
    it "should report symlink? as false on file, directory and missing files" do
317
 
      [file, dir, missing_file].each do |f|
318
 
        f.symlink?.should be_false
319
 
      end
320
 
    end
321
 
 
322
 
    it "should return a File::Stat with ftype 'link' when calling lstat on a symlink pointing to existing file" do
323
 
      symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
324
 
      file.symlink(symlink.path)
325
 
 
326
 
      stat = symlink.lstat
327
 
      stat.should be_instance_of(File::Stat)
328
 
      stat.ftype.should == 'link'
329
 
    end
330
 
 
331
 
    it "should return a File::Stat of ftype 'link' when calling lstat on a symlink pointing to missing file" do
332
 
      symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
333
 
      missing_file.symlink(symlink.path)
334
 
 
335
 
      stat = symlink.lstat
336
 
      stat.should be_instance_of(File::Stat)
337
 
      stat.ftype.should == 'link'
338
 
    end
339
 
 
340
 
    it "should return a File::Stat of ftype 'file' when calling stat on a symlink pointing to existing file" do
341
 
      symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
342
 
      file.symlink(symlink.path)
343
 
 
344
 
      stat = symlink.stat
345
 
      stat.should be_instance_of(File::Stat)
346
 
      stat.ftype.should == 'file'
347
 
    end
348
 
 
349
 
    it "should return a File::Stat of ftype 'directory' when calling stat on a symlink pointing to existing directory" do
350
 
      symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
351
 
      dir.symlink(symlink.path)
352
 
 
353
 
      stat = symlink.stat
354
 
      stat.should be_instance_of(File::Stat)
355
 
      stat.ftype.should == 'directory'
356
 
    end
357
 
 
358
 
    it "should return a File::Stat of ftype 'file' when calling stat on a symlink pointing to another symlink" do
359
 
      # point symlink -> file
360
 
      symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
361
 
      file.symlink(symlink.path)
362
 
 
363
 
      # point symlink2 -> symlink
364
 
      symlink2 = Puppet::FileSystem::File.new(tmpfile("somefile_link2"))
365
 
      symlink.symlink(symlink2.path)
366
 
 
367
 
      symlink2.stat.ftype.should == 'file'
368
 
    end
369
 
 
370
 
 
371
 
    it "should raise Errno::ENOENT when calling stat on a dangling symlink" do
372
 
      symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
373
 
      missing_file.symlink(symlink.path)
374
 
 
375
 
      expect { symlink.stat }.to raise_error(Errno::ENOENT)
376
 
    end
377
 
 
378
 
    it "should be able to readlink to resolve the physical path to a symlink" do
379
 
      symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
380
 
      file.symlink(symlink.path)
381
 
 
382
 
      file.exist?.should be_true
383
 
      symlink.readlink.should == file.path.to_s
384
 
    end
385
 
 
386
 
    it "should not resolve entire symlink chain with readlink on a symlink'd symlink" do
387
 
      # point symlink -> file
388
 
      symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
389
 
      file.symlink(symlink.path)
390
 
 
391
 
      # point symlink2 -> symlink
392
 
      symlink2 = Puppet::FileSystem::File.new(tmpfile("somefile_link2"))
393
 
      symlink.symlink(symlink2.path)
394
 
 
395
 
      file.exist?.should be_true
396
 
      symlink2.readlink.should == symlink.path.to_s
397
 
    end
398
 
 
399
 
    it "should be able to readlink to resolve the physical path to a dangling symlink" do
400
 
      symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
401
 
      missing_file.symlink(symlink.path)
402
 
 
403
 
      missing_file.exist?.should be_false
404
 
      symlink.readlink.should == missing_file.path.to_s
405
 
    end
406
 
 
407
 
    it "should delete only the symlink and not the target when calling unlink instance method" do
408
 
      [file, dir].each do |target|
409
 
        symlink = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_link"))
410
 
        target.symlink(symlink.path)
411
 
 
412
 
        target.exist?.should be_true
413
 
        symlink.readlink.should == target.path.to_s
414
 
 
415
 
        symlink.unlink.should == 1 # count of files
416
 
 
417
 
        target.exist?.should be_true
418
 
        symlink.exist?.should be_false
419
 
      end
420
 
    end
421
 
 
422
 
    it "should delete only the symlink and not the target when calling unlink class method" do
423
 
      [file, dir].each do |target|
424
 
        symlink = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_link"))
425
 
        target.symlink(symlink.path)
426
 
 
427
 
        target.exist?.should be_true
428
 
        symlink.readlink.should == target.path.to_s
429
 
 
430
 
        Puppet::FileSystem::File.unlink(symlink.path).should == 1  # count of files
431
 
 
432
 
        target.exist?.should be_true
433
 
        symlink.exist?.should be_false
434
 
      end
435
 
    end
436
 
 
437
 
    describe "unlink" do
438
 
      it "should delete files with unlink" do
439
 
        file.exist?.should be_true
440
 
 
441
 
        file.unlink.should == 1  # count of files
442
 
 
443
 
        file.exist?.should be_false
444
 
      end
445
 
 
446
 
      it "should delete files with unlink class method" do
447
 
        file.exist?.should be_true
448
 
 
449
 
        Puppet::FileSystem::File.unlink(file.path).should == 1  # count of files
450
 
 
451
 
        file.exist?.should be_false
452
 
      end
453
 
 
454
 
      it "should delete multiple files with unlink class method" do
455
 
        paths = (1..3).collect do |i|
456
 
          f = Puppet::FileSystem::File.new(tmpfile("somefile_#{i}"))
457
 
          FileUtils.touch(f.path)
458
 
          f.exist?.should be_true
459
 
          f.path.to_s
460
 
        end
461
 
 
462
 
        Puppet::FileSystem::File.unlink(*paths).should == 3  # count of files
463
 
 
464
 
        paths.each { |p| Puppet::FileSystem::File.exist?(p).should be_false  }
465
 
      end
466
 
 
467
 
      it "should raise Errno::EPERM or Errno::EISDIR when trying to delete a directory with the unlink class method" do
468
 
        dir.exist?.should be_true
469
 
 
470
 
        ex = nil
471
 
        begin
472
 
          Puppet::FileSystem::File.unlink(dir.path)
473
 
        rescue Exception => e
474
 
          ex = e
475
 
        end
476
 
 
477
 
        [
478
 
          Errno::EPERM, # Windows and OSX
479
 
          Errno::EISDIR # Linux
480
 
        ].should include ex.class
481
 
 
482
 
        dir.exist?.should be_true
483
 
      end
484
 
    end
485
 
  end
486
 
end