diff options
| author | Luke Kanies <luke@madstop.com> | 2008-12-10 21:36:39 -0600 |
|---|---|---|
| committer | James Turnbull <james@lovedthanlost.net> | 2008-12-12 09:44:42 +1100 |
| commit | 2385a78a7c455affed26955142a4d4d3ce53c37f (patch) | |
| tree | 1cf8df7b6812309371e25ddc0287df086e3afc4a /spec/unit | |
| parent | 2961b832de8d0bd6f73dfb6a65a424fd6eb7746a (diff) | |
| download | puppet-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-x | spec/unit/util/file_locking.rb | 146 | ||||
| -rwxr-xr-x | spec/unit/util/storage.rb | 4 |
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() |
