summaryrefslogtreecommitdiffstats
path: root/spec/unit
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2008-12-10 21:36:39 -0600
committerJames Turnbull <james@lovedthanlost.net>2008-12-12 09:44:42 +1100
commit2385a78a7c455affed26955142a4d4d3ce53c37f (patch)
tree1cf8df7b6812309371e25ddc0287df086e3afc4a /spec/unit
parent2961b832de8d0bd6f73dfb6a65a424fd6eb7746a (diff)
downloadpuppet-2385a78a7c455affed26955142a4d4d3ce53c37f.tar.gz
puppet-2385a78a7c455affed26955142a4d4d3ce53c37f.tar.xz
puppet-2385a78a7c455affed26955142a4d4d3ce53c37f.zip
Preparing to fix #1812 - Moving locking code to a module
This moves the locking code out of Puppet::Util into a separate module, to make the code cleaner. Signed-off-by: Luke Kanies <luke@madstop.com>
Diffstat (limited to 'spec/unit')
-rwxr-xr-xspec/unit/util/file_locking.rb146
-rwxr-xr-xspec/unit/util/storage.rb4
2 files changed, 148 insertions, 2 deletions
diff --git a/spec/unit/util/file_locking.rb b/spec/unit/util/file_locking.rb
new file mode 100755
index 000000000..a8b0c1840
--- /dev/null
+++ b/spec/unit/util/file_locking.rb
@@ -0,0 +1,146 @@
+#!/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
+ it "should use a global shared mutex" do
+ sync = mock 'sync'
+ sync.expects(:synchronize).with(Sync::SH)
+ Puppet::Util.expects(:sync).with("/file").returns sync
+
+ Puppet::Util::FileLocking.readlock '/file'
+ end
+
+ it "should use a shared lock on the file" do
+ sync = mock 'sync'
+ sync.expects(:synchronize).yields
+ Puppet::Util.expects(:sync).with("/file").returns sync
+
+ 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
+ end
+
+ describe "when acquiring a write lock" do
+ before do
+ @sync = mock 'sync'
+ Puppet::Util.stubs(:sync).returns @sync
+ @sync.stubs(:synchronize).yields
+ end
+
+ it "should fail if the parent directory does not exist" do
+ FileTest.expects(:directory?).with("/my/dir").returns false
+
+ lambda { Puppet::Util::FileLocking.writelock('/my/dir/file') }.should raise_error(Puppet::DevError)
+ end
+
+ it "should use a global exclusive mutex" do
+ sync = mock 'sync'
+ sync.expects(:synchronize).with(Sync::EX)
+ Puppet::Util.expects(:sync).with("/file").returns sync
+
+ Puppet::Util::FileLocking.writelock '/file'
+ end
+
+ it "should use any specified mode when opening the file" do
+ File.expects(:open).with("/file", "w", :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", "w", 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", "w", 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 write to a temporary file" do
+ fh = mock 'fh'
+ File.expects(:open).yields fh
+
+ lfh = mock 'locked_filehandle'
+ fh.expects(:lock_exclusive).yields lfh
+
+ tf = mock 'tmp_filehandle'
+ File.expects(:open).with { |path, *args| path == "/file.tmp" }.yields tf
+
+ result = nil
+ File.stubs(:rename)
+ Puppet::Util::FileLocking.writelock('/file') { |f| result = f }
+ result.should equal(tf)
+ end
+
+ it "should rename the temporary file to the normal file" do
+ fh = stub 'fh'
+ fh.stubs(:lock_exclusive).yields fh
+ File.stubs(:open).yields fh
+
+ File.expects(:rename).with("/file.tmp", "/file")
+ Puppet::Util::FileLocking.writelock('/file') { |f| }
+ end
+
+ it "should fail if it cannot rename the file" do
+ fh = stub 'fh'
+ fh.stubs(:lock_exclusive).yields fh
+ File.stubs(:open).yields fh
+
+ File.expects(:rename).with("/file.tmp", "/file").raises(RuntimeError)
+ lambda { Puppet::Util::FileLocking.writelock('/file') { |f| } }.should raise_error(Puppet::Error)
+ end
+
+ it "should remove the temporary file if the rename fails" do
+ fh = stub 'fh'
+ fh.stubs(:lock_exclusive).yields fh
+ File.stubs(:open).yields fh
+
+ File.expects(:rename).with("/file.tmp", "/file").raises(RuntimeError)
+ File.expects(:exist?).with("/file.tmp").returns true
+ File.expects(:unlink).with("/file.tmp")
+ lambda { Puppet::Util::FileLocking.writelock('/file') { |f| } }.should raise_error(Puppet::Error)
+ end
+ end
+end
diff --git a/spec/unit/util/storage.rb b/spec/unit/util/storage.rb
index eb495bc0b..934df0725 100755
--- a/spec/unit/util/storage.rb
+++ b/spec/unit/util/storage.rb
@@ -181,7 +181,7 @@ describe Puppet::Util::Storage do
end
it "should fail gracefully on load() if it cannot get a read lock on the state file" do
- Puppet::Util.expects(:readlock).yields(false)
+ Puppet::Util::FileLocking.expects(:readlock).yields(false)
test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}}
YAML.expects(:load).returns(test_yaml)
@@ -223,7 +223,7 @@ describe Puppet::Util::Storage do
end
it "should raise an exception if it cannot get a write lock on the state file" do
- Puppet::Util.expects(:writelock).yields(false)
+ Puppet::Util::FileLocking.expects(:writelock).yields(false)
Puppet::Util::Storage.cache(:yayness)
proc { Puppet::Util::Storage.store() }.should raise_error()