1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
#!/usr/bin/env ruby
Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") }
require 'puppet/util/file_locking'
class FileLocker
include Puppet::Util::FileLocking
end
describe Puppet::Util::FileLocking do
it "should have a module method for getting a read lock on files" do
Puppet::Util::FileLocking.should respond_to(:readlock)
end
it "should have a module method for getting a write lock on files" do
Puppet::Util::FileLocking.should respond_to(:writelock)
end
it "should have an instance method for getting a read lock on files" do
FileLocker.new.private_methods.should be_include("readlock")
end
it "should have an instance method for getting a write lock on files" do
FileLocker.new.private_methods.should be_include("writelock")
end
describe "when acquiring a read lock" do
before do
File.stubs(:exists?).with('/file').returns true
File.stubs(:file?).with('/file').returns true
end
it "should use a global shared mutex" do
Puppet::Util.expects(:synchronize_on).with('/file',Sync::SH).once
Puppet::Util::FileLocking.readlock '/file'
end
it "should use a shared lock on the file" do
Puppet::Util.expects(:synchronize_on).with('/file',Sync::SH).yields
fh = mock 'filehandle'
File.expects(:open).with("/file").yields fh
fh.expects(:lock_shared).yields "locked_fh"
result = nil
Puppet::Util::FileLocking.readlock('/file') { |l| result = l }
result.should == "locked_fh"
end
it "should only work on regular files" do
File.expects(:file?).with('/file').returns false
proc { Puppet::Util::FileLocking.readlock('/file') }.should raise_error(ArgumentError)
end
it "should create missing files" do
Puppet::Util.expects(:synchronize_on).with('/file',Sync::SH).yields
File.expects(:exists?).with('/file').returns false
File.expects(:open).with('/file').once
Puppet::Util::FileLocking.readlock('/file')
end
end
describe "when acquiring a write lock" do
before do
Puppet::Util.stubs(:synchronize_on).yields
File.stubs(:file?).with('/file').returns true
File.stubs(:exists?).with('/file').returns true
end
it "should fail if the parent directory does not exist" do
FileTest.expects(:directory?).with("/my/dir").returns false
File.stubs(:file?).with('/my/dir/file').returns true
File.stubs(:exists?).with('/my/dir/file').returns true
lambda { Puppet::Util::FileLocking.writelock('/my/dir/file') }.should raise_error(Puppet::DevError)
end
it "should use a global exclusive mutex" do
Puppet::Util.expects(:synchronize_on).with("/file",Sync::EX)
Puppet::Util::FileLocking.writelock '/file'
end
it "should use any specified mode when opening the file" do
File.expects(:open).with("/file", File::Constants::CREAT | File::Constants::WRONLY , :mymode)
Puppet::Util::FileLocking.writelock('/file', :mymode)
end
it "should use the mode of the existing file if no mode is specified" do
File.expects(:stat).with("/file").returns(mock("stat", :mode => 0755))
File.expects(:open).with("/file", File::Constants::CREAT | File::Constants::WRONLY, 0755)
Puppet::Util::FileLocking.writelock('/file')
end
it "should use 0600 as the mode if no mode is specified and the file does not exist" do
File.expects(:stat).raises(Errno::ENOENT)
File.expects(:open).with("/file", File::Constants::CREAT | File::Constants::WRONLY, 0600)
Puppet::Util::FileLocking.writelock('/file')
end
it "should create an exclusive file lock" do
fh = mock 'fh'
File.expects(:open).yields fh
fh.expects(:lock_exclusive)
Puppet::Util::FileLocking.writelock('/file')
end
it "should allow the caller to write to the locked file" do
fh = mock 'fh'
File.expects(:open).yields fh
lfh = mock 'locked_filehandle'
fh.expects(:lock_exclusive).yields(lfh)
lfh.stubs(:seek)
lfh.stubs(:truncate)
lfh.expects(:print).with "foo"
Puppet::Util::FileLocking.writelock('/file') do |f|
f.print "foo"
end
end
it "should truncate the file under an exclusive lock" do
fh = mock 'fh'
File.expects(:open).yields fh
lfh = mock 'locked_filehandle'
fh.expects(:lock_exclusive).yields(lfh)
lfh.expects(:seek).with(0, IO::SEEK_SET)
lfh.expects(:truncate).with(0)
lfh.stubs(:print)
Puppet::Util::FileLocking.writelock('/file') do |f|
f.print "foo"
end
end
it "should only work on regular files" do
File.expects(:file?).with('/file').returns false
proc { Puppet::Util::FileLocking.writelock('/file') }.should raise_error(ArgumentError)
end
it "should create missing files" do
Puppet::Util.expects(:synchronize_on).with('/file',Sync::EX).yields
File.expects(:exists?).with('/file').returns false
File.expects(:open).with('/file', File::Constants::CREAT | File::Constants::WRONLY, 0600).once
Puppet::Util::FileLocking.writelock('/file')
end
end
end
|