From a02c6bbe7ccac05593c9e07e9f0215ab6ceda8ad Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Fri, 16 May 2008 11:22:26 -0500 Subject: Fixing a mock in the redhat interface test. It mocked :exists? instead of :exist?, and my provider work changed the method call because :exists? is going away in ruby. --- spec/unit/provider/interface/redhat.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/unit/provider/interface/redhat.rb b/spec/unit/provider/interface/redhat.rb index 5a7a8dfcd..99ac50f01 100755 --- a/spec/unit/provider/interface/redhat.rb +++ b/spec/unit/provider/interface/redhat.rb @@ -9,12 +9,12 @@ provider_class = Puppet::Type.type(:interface).provider(:redhat) describe provider_class do it "should not be functional on systems without a network-scripts directory" do - FileTest.expects(:exists?).with("/etc/sysconfig/network-scripts").returns(false) + FileTest.expects(:exist?).with("/etc/sysconfig/network-scripts").returns(false) provider_class.should_not be_suitable end it "should be functional on systems with a network-scripts directory" do - FileTest.expects(:exists?).with("/etc/sysconfig/network-scripts").returns(true) + FileTest.expects(:exist?).with("/etc/sysconfig/network-scripts").returns(true) provider_class.should be_suitable end end -- cgit From 8008bbc447764e0ca28169284f4d6df3a86dcdd6 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Fri, 16 May 2008 15:42:20 -0500 Subject: Modified the 'factpath' setting to automatically configure Facter to load facts there if a new enough version of Facter is used. --- CHANGELOG | 4 ++++ lib/puppet/defaults.rb | 8 +++++--- spec/integration/defaults.rb | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) create mode 100755 spec/integration/defaults.rb diff --git a/CHANGELOG b/CHANGELOG index f076f3784..b0fbf7e4c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ + Modified the 'factpath' setting to automatically configure + Facter to load facts there if a new enough version of + Facter is used. + Added ldap providers for users and groups. Added support for the --all option to puppetca --clean. If diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index df2fb9425..57299b7e7 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -505,9 +505,11 @@ module Puppet # Central fact information. self.setdefaults(:main, - :factpath => ["$vardir/facts", - "Where Puppet should look for facts. Multiple directories should - be colon-separated, like normal PATH variables."], + :factpath => {:default => "$vardir/facts", + :desc => "Where Puppet should look for facts. Multiple directories should + be colon-separated, like normal PATH variables.", + :call_on_define => true, # Call our hook with the default value, so we always get the value added to facter. + :hook => proc { |value| Facter.search(value) if Facter.respond_to?(:search) }}, :factdest => ["$vardir/facts", "Where Puppet should store facts that it pulls down from the central server."], diff --git a/spec/integration/defaults.rb b/spec/integration/defaults.rb new file mode 100755 index 000000000..b14a141fb --- /dev/null +++ b/spec/integration/defaults.rb @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +require 'puppet/defaults' + +describe "Puppet defaults" do + describe "when setting the :factpath" do + after { Puppet.settings.clear } + + it "should add the :factpath to Facter's search paths" do + Facter.expects(:search).with("/my/fact/path") + + Puppet.settings[:factpath] = "/my/fact/path" + end + end +end -- cgit From c5da40122493eacf158458f0d79e335e2d0c4c9d Mon Sep 17 00:00:00 2001 From: Paul Lathrop Date: Sat, 17 May 2008 00:54:07 -0700 Subject: Add unit tests for Puppet::Util::Storage --- spec/unit/util/storage.rb | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100755 spec/unit/util/storage.rb diff --git a/spec/unit/util/storage.rb b/spec/unit/util/storage.rb new file mode 100755 index 000000000..38e445127 --- /dev/null +++ b/spec/unit/util/storage.rb @@ -0,0 +1,72 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'yaml' +require 'sync' +require 'tempfile' + +describe Puppet::Util::Storage do + + before(:all) do + Puppet[:statedir] = Dir.tmpdir() + end + + it "it should re-initialize to a clean state when the clear() method is called" do + end + + it "it should use the main settings section if the state dir is not a directory" do + FileTest.expects(:directory?).with(Puppet[:statedir]).returns(false) + Puppet.settings.expects(:use).with(:main) + Puppet::Util::Storage.load() + end + + it "it should initialize with an empty state when the state file does not exist" do + File.expects(:exists?).with(Puppet[:statefile]).returns(false) + Puppet::Util::Storage.load() + Puppet::Util::Storage.stateinspect().should == {}.inspect() + end + + describe "when the state file exists" do + it "it should attempt to get a read lock on the file" do + File.expects(:exists?).with(Puppet[:statefile]).returns(true) + Puppet::Util.expects(:benchmark).with(:debug, "Loaded state").yields() + Puppet::Util.expects(:readlock).with(Puppet[:statefile]) + Puppet::Util::Storage.load() + end + + describe "and the file contents are valid" do + it "it should initialize with the correct state from the state file" do + File.expects(:exists?).with(Puppet[:statefile]).returns(true) + Puppet::Util.expects(:benchmark).with(:debug, "Loaded state").yields() + Puppet::Util.expects(:readlock).with(Puppet[:statefile]).yields(0) + test_yaml = {'File["/root"]'=>{"name"=>{:a=>:b,:c=>:d}}} + YAML.expects(:load).returns(test_yaml) + + Puppet::Util::Storage.load() + Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + end + end + + describe "and the file contents are invalid" do + # Commented out because the previous test's existence causes this one to fail. +# it "it should not initialize from the state file" do +# File.expects(:exists?).with(Puppet[:statefile]).returns(true) +# Puppet::Util.expects(:benchmark).with(:debug, "Loaded state").yields() +# Puppet::Util.expects(:readlock).with(Puppet[:statefile]).yields(0) +# YAML.expects(:load).raises(YAML::Error) +# File.expects(:rename).with(Puppet[:statefile], Puppet[:statefile] + ".bad").returns(0) + +# Puppet::Util::Storage.load() +# Puppet::Util::Storage.stateinspect().should == {}.inspect() +# end + + it "it should attempt to rename the state file" do + + end + + end + + end + +end -- cgit From d7f25ff5715a0d17eca8a590df6d9dd8b93ae443 Mon Sep 17 00:00:00 2001 From: Paul Lathrop Date: Sat, 17 May 2008 04:26:54 -0700 Subject: Rewritten tests for Puppet::Util::Storage. --- spec/unit/util/storage.rb | 255 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 211 insertions(+), 44 deletions(-) diff --git a/spec/unit/util/storage.rb b/spec/unit/util/storage.rb index 38e445127..58a1549c8 100755 --- a/spec/unit/util/storage.rb +++ b/spec/unit/util/storage.rb @@ -7,66 +7,233 @@ require 'sync' require 'tempfile' describe Puppet::Util::Storage do - before(:all) do Puppet[:statedir] = Dir.tmpdir() + @file_test = Puppet.type(:file).create(:name => "/yayness", :check => %w{checksum type}) + @exec_test = Puppet.type(:exec).create(:name => "/bin/ls /yayness") + @bogus_objects = [ {}, [], "foo", 42, nil, Tempfile.new('storage_test') ] end - it "it should re-initialize to a clean state when the clear() method is called" do + before(:each) do + Puppet::Util::Storage.clear() end - it "it should use the main settings section if the state dir is not a directory" do - FileTest.expects(:directory?).with(Puppet[:statedir]).returns(false) - Puppet.settings.expects(:use).with(:main) - Puppet::Util::Storage.load() + it "it should return an empty hash when caching a symbol" do + Puppet::Util::Storage.cache(:yayness).should == {} + Puppet::Util::Storage.cache(:more_yayness).should == {} end - - it "it should initialize with an empty state when the state file does not exist" do - File.expects(:exists?).with(Puppet[:statefile]).returns(false) - Puppet::Util::Storage.load() + it "it should add the symbol to it's internal state when caching a symbol" do + Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.cache(:bubblyness) + Puppet::Util::Storage.stateinspect().should == {:yayness=>{},:bubblyness=>{}}.inspect() + end + it "it should return an empty hash when caching a Puppet::Type" do + Puppet::Util::Storage.cache(@file_test).should == {} + Puppet::Util::Storage.cache(@exec_test).should == {} + end + it "it should add the resource ref to it's internal state when caching a Puppet::Type" do Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.cache(@file_test) + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}}.inspect() + Puppet::Util::Storage.cache(@exec_test) + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, "Exec[/bin/ls /yayness]"=>{}}.inspect() end - describe "when the state file exists" do - it "it should attempt to get a read lock on the file" do - File.expects(:exists?).with(Puppet[:statefile]).returns(true) - Puppet::Util.expects(:benchmark).with(:debug, "Loaded state").yields() - Puppet::Util.expects(:readlock).with(Puppet[:statefile]) - Puppet::Util::Storage.load() + it "it should raise an ArgumentError when caching invalid objects" do + @bogus_objects.each do |object| + proc { Puppet::Util::Storage.cache(object) }.should raise_error() end - - describe "and the file contents are valid" do - it "it should initialize with the correct state from the state file" do - File.expects(:exists?).with(Puppet[:statefile]).returns(true) - Puppet::Util.expects(:benchmark).with(:debug, "Loaded state").yields() - Puppet::Util.expects(:readlock).with(Puppet[:statefile]).yields(0) - test_yaml = {'File["/root"]'=>{"name"=>{:a=>:b,:c=>:d}}} - YAML.expects(:load).returns(test_yaml) - - Puppet::Util::Storage.load() - Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + end + it "it should not add anything to it's internal state when caching invalid objects" do + @bogus_objects.each do |object| + begin + Puppet::Util::Storage.cache(object) + rescue + Puppet::Util::Storage.stateinspect().should == {}.inspect() end end + end - describe "and the file contents are invalid" do - # Commented out because the previous test's existence causes this one to fail. -# it "it should not initialize from the state file" do -# File.expects(:exists?).with(Puppet[:statefile]).returns(true) -# Puppet::Util.expects(:benchmark).with(:debug, "Loaded state").yields() -# Puppet::Util.expects(:readlock).with(Puppet[:statefile]).yields(0) -# YAML.expects(:load).raises(YAML::Error) -# File.expects(:rename).with(Puppet[:statefile], Puppet[:statefile] + ".bad").returns(0) - -# Puppet::Util::Storage.load() -# Puppet::Util::Storage.stateinspect().should == {}.inspect() -# end - - it "it should attempt to rename the state file" do - - end + it "it should clear it's internal state when clear() is called" do + Puppet::Util::Storage.cache(@file_test) + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() + Puppet::Util::Storage.clear() + Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.cache(@exec_test) + Puppet::Util::Storage.cache(:bubblyness) + Puppet::Util::Storage.stateinspect().should == {"Exec[/bin/ls /yayness]"=>{}, :bubblyness=>{}}.inspect() + Puppet::Util::Storage.clear() + Puppet::Util::Storage.stateinspect().should == {}.inspect() + end - end + it "it should not fail to load if Puppet[:statedir] does not exist" do + transient = Tempfile.new('storage_test') + path = transient.path() + transient.close!() + FileTest.exists?(path).should be_false() + Puppet[:statedir] = path + proc { Puppet::Util::Storage.load() }.should_not raise_error() + end + + it "it should not fail to load if Puppet[:statefile] does not exist" do + transient = Tempfile.new('storage_test') + path = transient.path() + transient.close!() + FileTest.exists?(path).should be_false() + Puppet[:statefile] = path + proc { Puppet::Util::Storage.load() }.should_not raise_error() + end + it "it should not lose it's internal state if load() is called and Puppet[:statefile] does not exist" do + transient = Tempfile.new('storage_test') + path = transient.path() + transient.close!() + FileTest.exists?(path).should be_false() + + Puppet::Util::Storage.cache(@file_test) + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() + + Puppet[:statefile] = path + proc { Puppet::Util::Storage.load() }.should_not raise_error() + + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() end + it "it should overwrite it's internal state if load() is called and Puppet[:statefile] exists" do + # Should the state be overwritten even if Puppet[:statefile] is not valid YAML? + state_file = Tempfile.new('storage_test') + + Puppet::Util::Storage.cache(@file_test) + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() + + Puppet[:statefile] = state_file.path() + proc { Puppet::Util::Storage.load() }.should_not raise_error() + + Puppet::Util::Storage.stateinspect().should == {}.inspect() + + state_file.close!() + end + + it "it should restore it's internal state from Puppet[:statefile] if the file contains valid YAML" do + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} + YAML.expects(:load).returns(test_yaml) + + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + + state_file.close!() + end + + it "it should initialize with a clear internal state if the state file does not contain valid YAML" do + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + state_file.write(:booness) + + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.stateinspect().should == {}.inspect() + + state_file.close!() + end + + it "it should raise an error if the state file does not contain valid YAML and cannot be renamed" do + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + state_file.write(:booness) + File.chmod(0000, state_file.path()) + + proc { Puppet::Util::Storage.load() }.should raise_error() + + state_file.close!() + end + + it "it should attempt to rename the state file if the file is corrupted" do + # We fake corruption by causing YAML.load to raise an exception + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + YAML.expects(:load).raises(Puppet::Error) + File.expects(:rename).at_least_once + + proc { Puppet::Util::Storage.load() }.should_not raise_error() + + state_file.close!() + end + + it "it should fail gracefully on load() if Puppet[:statefile] is not a regular file" do + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + state_file.close!() + Dir.mkdir(Puppet[:statefile]) + File.expects(:rename).returns(0) + + proc { Puppet::Util::Storage.load() }.should_not raise_error() + + Dir.rmdir(Puppet[:statefile]) + end + + it "it should fail gracefully on load() if it cannot get a read lock on Puppet[:statefile]" do + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + Puppet::Util.expects(:readlock).yields(false) + test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} + YAML.expects(:load).returns(test_yaml) + + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + + state_file.close!() + end + + it "it should raise an exception on store() if Puppet[:statefile] is not a regular file" do + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + state_file.close!() + Dir.mkdir(Puppet[:statefile]) + Puppet::Util::Storage.cache(@file_test) + Puppet::Util::Storage.cache(:yayness) + + proc { Puppet::Util::Storage.store() }.should raise_error() + + Dir.rmdir(Puppet[:statefile]) + end + + it "it should raise an exception on store() if it cannot get a write lock on Puppet[:statefile]" do + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + Puppet::Util.expects(:writelock).yields(false) + Puppet::Util::Storage.cache(@file_test) + Puppet::Util::Storage.cache(:yayness) + + proc { Puppet::Util::Storage.store() }.should raise_error() + + state_file.close!() + end + + it "it should load() the same information that it store()s" do + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + Puppet::Util::Storage.cache(@file_test) + Puppet::Util::Storage.cache(:yayness) + + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() + proc { Puppet::Util::Storage.store() }.should_not raise_error() + + Puppet::Util::Storage.clear() + Puppet::Util::Storage.stateinspect().should == {}.inspect() + + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() + + state_file.close!() + end + + after(:all) do + @bogus_objects.last.close!() + end end -- cgit From ee041293d99605e4283235cb3ee5286447ffadd4 Mon Sep 17 00:00:00 2001 From: Paul Lathrop Date: Sat, 17 May 2008 23:51:25 -0700 Subject: Refactored tests based on feedback from Luke. --- spec/unit/util/storage.rb | 345 +++++++++++++++++++++++----------------------- 1 file changed, 169 insertions(+), 176 deletions(-) diff --git a/spec/unit/util/storage.rb b/spec/unit/util/storage.rb index 58a1549c8..309e5a200 100755 --- a/spec/unit/util/storage.rb +++ b/spec/unit/util/storage.rb @@ -9,231 +9,224 @@ require 'tempfile' describe Puppet::Util::Storage do before(:all) do Puppet[:statedir] = Dir.tmpdir() - @file_test = Puppet.type(:file).create(:name => "/yayness", :check => %w{checksum type}) - @exec_test = Puppet.type(:exec).create(:name => "/bin/ls /yayness") - @bogus_objects = [ {}, [], "foo", 42, nil, Tempfile.new('storage_test') ] end before(:each) do Puppet::Util::Storage.clear() end - it "it should return an empty hash when caching a symbol" do - Puppet::Util::Storage.cache(:yayness).should == {} - Puppet::Util::Storage.cache(:more_yayness).should == {} - end - it "it should add the symbol to it's internal state when caching a symbol" do - Puppet::Util::Storage.stateinspect().should == {}.inspect() - Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() - Puppet::Util::Storage.cache(:bubblyness) - Puppet::Util::Storage.stateinspect().should == {:yayness=>{},:bubblyness=>{}}.inspect() - end - it "it should return an empty hash when caching a Puppet::Type" do - Puppet::Util::Storage.cache(@file_test).should == {} - Puppet::Util::Storage.cache(@exec_test).should == {} - end - it "it should add the resource ref to it's internal state when caching a Puppet::Type" do - Puppet::Util::Storage.stateinspect().should == {}.inspect() - Puppet::Util::Storage.cache(@file_test) - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}}.inspect() - Puppet::Util::Storage.cache(@exec_test) - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, "Exec[/bin/ls /yayness]"=>{}}.inspect() - end - - it "it should raise an ArgumentError when caching invalid objects" do - @bogus_objects.each do |object| - proc { Puppet::Util::Storage.cache(object) }.should raise_error() + describe "when caching a symbol" do + it "it should return an empty hash" do + Puppet::Util::Storage.cache(:yayness).should == {} + Puppet::Util::Storage.cache(:more_yayness).should == {} end - end - it "it should not add anything to it's internal state when caching invalid objects" do - @bogus_objects.each do |object| - begin - Puppet::Util::Storage.cache(object) - rescue - Puppet::Util::Storage.stateinspect().should == {}.inspect() - end + + it "it should add the symbol to its internal state" do + Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.cache(:bubblyness) + Puppet::Util::Storage.stateinspect().should == {:yayness=>{},:bubblyness=>{}}.inspect() end end - it "it should clear it's internal state when clear() is called" do - Puppet::Util::Storage.cache(@file_test) - Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() - Puppet::Util::Storage.clear() - Puppet::Util::Storage.stateinspect().should == {}.inspect() - Puppet::Util::Storage.cache(@exec_test) - Puppet::Util::Storage.cache(:bubblyness) - Puppet::Util::Storage.stateinspect().should == {"Exec[/bin/ls /yayness]"=>{}, :bubblyness=>{}}.inspect() - Puppet::Util::Storage.clear() - Puppet::Util::Storage.stateinspect().should == {}.inspect() - end + describe "when caching a Puppet::Type" do + before(:all) do + @file_test = Puppet.type(:file).create(:name => "/yayness", :check => %w{checksum type}) + @exec_test = Puppet.type(:exec).create(:name => "/bin/ls /yayness") + end - it "it should not fail to load if Puppet[:statedir] does not exist" do - transient = Tempfile.new('storage_test') - path = transient.path() - transient.close!() - FileTest.exists?(path).should be_false() - Puppet[:statedir] = path - proc { Puppet::Util::Storage.load() }.should_not raise_error() - end + it "it should return an empty hash" do + Puppet::Util::Storage.cache(@file_test).should == {} + Puppet::Util::Storage.cache(@exec_test).should == {} + end - it "it should not fail to load if Puppet[:statefile] does not exist" do - transient = Tempfile.new('storage_test') - path = transient.path() - transient.close!() - FileTest.exists?(path).should be_false() - Puppet[:statefile] = path - proc { Puppet::Util::Storage.load() }.should_not raise_error() + it "it should add the resource ref to its internal state" do + Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.cache(@file_test) + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}}.inspect() + Puppet::Util::Storage.cache(@exec_test) + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, "Exec[/bin/ls /yayness]"=>{}}.inspect() + end end - it "it should not lose it's internal state if load() is called and Puppet[:statefile] does not exist" do - transient = Tempfile.new('storage_test') - path = transient.path() - transient.close!() - FileTest.exists?(path).should be_false() - - Puppet::Util::Storage.cache(@file_test) - Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() + describe "when caching invalid objects" do + before(:all) do + @bogus_objects = [ {}, [], "foo", 42, nil, Tempfile.new('storage_test') ] + end - Puppet[:statefile] = path - proc { Puppet::Util::Storage.load() }.should_not raise_error() + it "it should raise an ArgumentError" do + @bogus_objects.each do |object| + proc { Puppet::Util::Storage.cache(object) }.should raise_error() + end + end - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() + it "it should not add anything to its internal state" do + @bogus_objects.each do |object| + begin + Puppet::Util::Storage.cache(object) + rescue + Puppet::Util::Storage.stateinspect().should == {}.inspect() + end + end + end end - it "it should overwrite it's internal state if load() is called and Puppet[:statefile] exists" do - # Should the state be overwritten even if Puppet[:statefile] is not valid YAML? - state_file = Tempfile.new('storage_test') - - Puppet::Util::Storage.cache(@file_test) + it "it should clear its internal state when clear() is called" do Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() - - Puppet[:statefile] = state_file.path() - proc { Puppet::Util::Storage.load() }.should_not raise_error() - + Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.clear() Puppet::Util::Storage.stateinspect().should == {}.inspect() - - state_file.close!() end - it "it should restore it's internal state from Puppet[:statefile] if the file contains valid YAML" do - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} - YAML.expects(:load).returns(test_yaml) + describe "when loading from the state file" do + describe "when the state file/directory does not exist" do + before(:each) do + transient = Tempfile.new('storage_test') + @path = transient.path() + transient.close!() + end + + it "it should not fail to load()" do + FileTest.exists?(@path).should be_false() + Puppet[:statedir] = @path + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet[:statefile] = @path + proc { Puppet::Util::Storage.load() }.should_not raise_error() + end + + it "it should not lose its internal state when load() is called" do + FileTest.exists?(@path).should be_false() - proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + + Puppet[:statefile] = @path + proc { Puppet::Util::Storage.load() }.should_not raise_error() + + Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + end + end - state_file.close!() - end - - it "it should initialize with a clear internal state if the state file does not contain valid YAML" do - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - state_file.write(:booness) + describe "when the state file/directory exists" do + before(:each) do + @state_file = Tempfile.new('storage_test') + @saved_statefile = Puppet[:statefile] + Puppet[:statefile] = @state_file.path() + end - proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == {}.inspect() + it "it should overwrite its internal state if load() is called" do + # Should the state be overwritten even if Puppet[:statefile] is not valid YAML? + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() - state_file.close!() - end + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.stateinspect().should == {}.inspect() + end - it "it should raise an error if the state file does not contain valid YAML and cannot be renamed" do - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - state_file.write(:booness) - File.chmod(0000, state_file.path()) + it "it should restore its internal state if the state file contains valid YAML" do + test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} + YAML.expects(:load).returns(test_yaml) - proc { Puppet::Util::Storage.load() }.should raise_error() + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + end + + it "it should initialize with a clear internal state if the state file does not contain valid YAML" do + @state_file.write(:booness) - state_file.close!() - end + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.stateinspect().should == {}.inspect() + end - it "it should attempt to rename the state file if the file is corrupted" do - # We fake corruption by causing YAML.load to raise an exception - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - YAML.expects(:load).raises(Puppet::Error) - File.expects(:rename).at_least_once + it "it should raise an error if the state file does not contain valid YAML and cannot be renamed" do + @state_file.write(:booness) + File.chmod(0000, @state_file.path()) - proc { Puppet::Util::Storage.load() }.should_not raise_error() + proc { Puppet::Util::Storage.load() }.should raise_error() + end - state_file.close!() - end + it "it should attempt to rename the state file if the file is corrupted" do + # We fake corruption by causing YAML.load to raise an exception + YAML.expects(:load).raises(Puppet::Error) + File.expects(:rename).at_least_once - it "it should fail gracefully on load() if Puppet[:statefile] is not a regular file" do - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - state_file.close!() - Dir.mkdir(Puppet[:statefile]) - File.expects(:rename).returns(0) + proc { Puppet::Util::Storage.load() }.should_not raise_error() + end - proc { Puppet::Util::Storage.load() }.should_not raise_error() + it "it should fail gracefully on load() if the state file is not a regular file" do + @state_file.close!() + Dir.mkdir(Puppet[:statefile]) + File.expects(:rename).returns(0) - Dir.rmdir(Puppet[:statefile]) - end + proc { Puppet::Util::Storage.load() }.should_not raise_error() - it "it should fail gracefully on load() if it cannot get a read lock on Puppet[:statefile]" do - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - Puppet::Util.expects(:readlock).yields(false) - test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} - YAML.expects(:load).returns(test_yaml) + Dir.rmdir(Puppet[:statefile]) + end - proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + it "it should fail gracefully on load() if it cannot get a read lock on the state file" do + Puppet::Util.expects(:readlock).yields(false) + test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} + YAML.expects(:load).returns(test_yaml) - state_file.close!() + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + end + + after(:each) do + @state_file.close!() + Puppet[:statefile] = @saved_statefile + end + end end - it "it should raise an exception on store() if Puppet[:statefile] is not a regular file" do - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - state_file.close!() - Dir.mkdir(Puppet[:statefile]) - Puppet::Util::Storage.cache(@file_test) - Puppet::Util::Storage.cache(:yayness) - - proc { Puppet::Util::Storage.store() }.should raise_error() + describe "when storing to the state file" do + before(:each) do + @state_file = Tempfile.new('storage_test') + @saved_statefile = Puppet[:statefile] + Puppet[:statefile] = @state_file.path() + end - Dir.rmdir(Puppet[:statefile]) - end + it "it should create the state file if it does not exist" do + @state_file.close!() + FileTest.exists?(Puppet[:statefile]).should be_false() + Puppet::Util::Storage.cache(:yayness) - it "it should raise an exception on store() if it cannot get a write lock on Puppet[:statefile]" do - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - Puppet::Util.expects(:writelock).yields(false) - Puppet::Util::Storage.cache(@file_test) - Puppet::Util::Storage.cache(:yayness) + proc { Puppet::Util::Storage.store() }.should_not raise_error() + FileTest.exists?(Puppet[:statefile]).should be_true() + end - proc { Puppet::Util::Storage.store() }.should raise_error() + it "it should raise an exception if the state file is not a regular file" do + @state_file.close!() + Dir.mkdir(Puppet[:statefile]) + Puppet::Util::Storage.cache(:yayness) - state_file.close!() - end + proc { Puppet::Util::Storage.store() }.should raise_error() - it "it should load() the same information that it store()s" do - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - Puppet::Util::Storage.cache(@file_test) - Puppet::Util::Storage.cache(:yayness) + Dir.rmdir(Puppet[:statefile]) + end - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() - proc { Puppet::Util::Storage.store() }.should_not raise_error() + it "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::Storage.cache(:yayness) - Puppet::Util::Storage.clear() - Puppet::Util::Storage.stateinspect().should == {}.inspect() + proc { Puppet::Util::Storage.store() }.should raise_error() + end - proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() + it "it should load() the same information that it store()s" do + Puppet::Util::Storage.cache(:yayness) - state_file.close!() - end + Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + proc { Puppet::Util::Storage.store() }.should_not raise_error() + Puppet::Util::Storage.clear() + Puppet::Util::Storage.stateinspect().should == {}.inspect() + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + end - after(:all) do - @bogus_objects.last.close!() + after(:each) do + @state_file.close!() + Puppet[:statefile] = @saved_statefile + end end end -- cgit From 6a6a1d99d7cd5186b3d778007abaa54a27d5e373 Mon Sep 17 00:00:00 2001 From: Paul Lathrop Date: Sun, 18 May 2008 15:30:13 -0700 Subject: Another refactor based on feedback from Luke. This includes adding an accessor for @@state to make testing a bit cleaner. --- lib/puppet/util/storage.rb | 4 ++++ spec/unit/util/storage.rb | 42 +++++++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/lib/puppet/util/storage.rb b/lib/puppet/util/storage.rb index 9358a28e9..dc4e9cd71 100644 --- a/lib/puppet/util/storage.rb +++ b/lib/puppet/util/storage.rb @@ -6,6 +6,10 @@ class Puppet::Util::Storage include Singleton include Puppet::Util + def self.state + return @@state + end + def initialize self.class.load end diff --git a/spec/unit/util/storage.rb b/spec/unit/util/storage.rb index 309e5a200..a1f868cf2 100755 --- a/spec/unit/util/storage.rb +++ b/spec/unit/util/storage.rb @@ -22,11 +22,15 @@ describe Puppet::Util::Storage do end it "it should add the symbol to its internal state" do - Puppet::Util::Storage.stateinspect().should == {}.inspect() Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.state().should == {:yayness=>{}} + end + + it "it should not clobber existing state when caching additional objects" do + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.state().should == {:yayness=>{}} Puppet::Util::Storage.cache(:bubblyness) - Puppet::Util::Storage.stateinspect().should == {:yayness=>{},:bubblyness=>{}}.inspect() + Puppet::Util::Storage.state().should == {:yayness=>{},:bubblyness=>{}} end end @@ -42,11 +46,11 @@ describe Puppet::Util::Storage do end it "it should add the resource ref to its internal state" do - Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.state().should == {} Puppet::Util::Storage.cache(@file_test) - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}}.inspect() + Puppet::Util::Storage.state().should == {"File[/yayness]"=>{}} Puppet::Util::Storage.cache(@exec_test) - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, "Exec[/bin/ls /yayness]"=>{}}.inspect() + Puppet::Util::Storage.state().should == {"File[/yayness]"=>{}, "Exec[/bin/ls /yayness]"=>{}} end end @@ -66,7 +70,7 @@ describe Puppet::Util::Storage do begin Puppet::Util::Storage.cache(object) rescue - Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.state().should == {} end end end @@ -74,9 +78,9 @@ describe Puppet::Util::Storage do it "it should clear its internal state when clear() is called" do Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.state().should == {:yayness=>{}} Puppet::Util::Storage.clear() - Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.state().should == {} end describe "when loading from the state file" do @@ -99,12 +103,12 @@ describe Puppet::Util::Storage do FileTest.exists?(@path).should be_false() Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.state().should == {:yayness=>{}} Puppet[:statefile] = @path proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.state().should == {:yayness=>{}} end end @@ -118,10 +122,10 @@ describe Puppet::Util::Storage do it "it should overwrite its internal state if load() is called" do # Should the state be overwritten even if Puppet[:statefile] is not valid YAML? Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.state().should == {:yayness=>{}} proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.state().should == {} end it "it should restore its internal state if the state file contains valid YAML" do @@ -129,14 +133,14 @@ describe Puppet::Util::Storage do YAML.expects(:load).returns(test_yaml) proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + Puppet::Util::Storage.state().should == test_yaml end it "it should initialize with a clear internal state if the state file does not contain valid YAML" do @state_file.write(:booness) proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.state().should == {} end it "it should raise an error if the state file does not contain valid YAML and cannot be renamed" do @@ -170,7 +174,7 @@ describe Puppet::Util::Storage do YAML.expects(:load).returns(test_yaml) proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + Puppet::Util::Storage.state().should == test_yaml end after(:each) do @@ -216,12 +220,12 @@ describe Puppet::Util::Storage do it "it should load() the same information that it store()s" do Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.state().should == {:yayness=>{}} proc { Puppet::Util::Storage.store() }.should_not raise_error() Puppet::Util::Storage.clear() - Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.state().should == {} proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.state().should == {:yayness=>{}} end after(:each) do -- cgit From 0820819e0f7e81f2e68893258145877f756b5395 Mon Sep 17 00:00:00 2001 From: Paul Lathrop Date: Sun, 18 May 2008 16:51:35 -0700 Subject: Minor cosmetic changes to cleanup some style elements and get rid of some cruft. --- spec/unit/util/storage.rb | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/spec/unit/util/storage.rb b/spec/unit/util/storage.rb index a1f868cf2..55d1d1f61 100755 --- a/spec/unit/util/storage.rb +++ b/spec/unit/util/storage.rb @@ -3,9 +3,10 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'yaml' -require 'sync' require 'tempfile' +require 'puppet/util/storage' + describe Puppet::Util::Storage do before(:all) do Puppet[:statedir] = Dir.tmpdir() @@ -16,17 +17,17 @@ describe Puppet::Util::Storage do end describe "when caching a symbol" do - it "it should return an empty hash" do + it "should return an empty hash" do Puppet::Util::Storage.cache(:yayness).should == {} Puppet::Util::Storage.cache(:more_yayness).should == {} end - it "it should add the symbol to its internal state" do + it "should add the symbol to its internal state" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state().should == {:yayness=>{}} end - it "it should not clobber existing state when caching additional objects" do + it "should not clobber existing state when caching additional objects" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state().should == {:yayness=>{}} Puppet::Util::Storage.cache(:bubblyness) @@ -40,12 +41,12 @@ describe Puppet::Util::Storage do @exec_test = Puppet.type(:exec).create(:name => "/bin/ls /yayness") end - it "it should return an empty hash" do + it "should return an empty hash" do Puppet::Util::Storage.cache(@file_test).should == {} Puppet::Util::Storage.cache(@exec_test).should == {} end - it "it should add the resource ref to its internal state" do + it "should add the resource ref to its internal state" do Puppet::Util::Storage.state().should == {} Puppet::Util::Storage.cache(@file_test) Puppet::Util::Storage.state().should == {"File[/yayness]"=>{}} @@ -59,13 +60,13 @@ describe Puppet::Util::Storage do @bogus_objects = [ {}, [], "foo", 42, nil, Tempfile.new('storage_test') ] end - it "it should raise an ArgumentError" do + it "should raise an ArgumentError" do @bogus_objects.each do |object| proc { Puppet::Util::Storage.cache(object) }.should raise_error() end end - it "it should not add anything to its internal state" do + it "should not add anything to its internal state" do @bogus_objects.each do |object| begin Puppet::Util::Storage.cache(object) @@ -76,7 +77,7 @@ describe Puppet::Util::Storage do end end - it "it should clear its internal state when clear() is called" do + it "should clear its internal state when clear() is called" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state().should == {:yayness=>{}} Puppet::Util::Storage.clear() @@ -91,7 +92,7 @@ describe Puppet::Util::Storage do transient.close!() end - it "it should not fail to load()" do + it "should not fail to load()" do FileTest.exists?(@path).should be_false() Puppet[:statedir] = @path proc { Puppet::Util::Storage.load() }.should_not raise_error() @@ -99,7 +100,7 @@ describe Puppet::Util::Storage do proc { Puppet::Util::Storage.load() }.should_not raise_error() end - it "it should not lose its internal state when load() is called" do + it "should not lose its internal state when load() is called" do FileTest.exists?(@path).should be_false() Puppet::Util::Storage.cache(:yayness) @@ -119,7 +120,7 @@ describe Puppet::Util::Storage do Puppet[:statefile] = @state_file.path() end - it "it should overwrite its internal state if load() is called" do + it "should overwrite its internal state if load() is called" do # Should the state be overwritten even if Puppet[:statefile] is not valid YAML? Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state().should == {:yayness=>{}} @@ -128,7 +129,7 @@ describe Puppet::Util::Storage do Puppet::Util::Storage.state().should == {} end - it "it should restore its internal state if the state file contains valid YAML" do + it "should restore its internal state if the state file contains valid YAML" do test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} YAML.expects(:load).returns(test_yaml) @@ -136,21 +137,21 @@ describe Puppet::Util::Storage do Puppet::Util::Storage.state().should == test_yaml end - it "it should initialize with a clear internal state if the state file does not contain valid YAML" do + it "should initialize with a clear internal state if the state file does not contain valid YAML" do @state_file.write(:booness) proc { Puppet::Util::Storage.load() }.should_not raise_error() Puppet::Util::Storage.state().should == {} end - it "it should raise an error if the state file does not contain valid YAML and cannot be renamed" do + it "should raise an error if the state file does not contain valid YAML and cannot be renamed" do @state_file.write(:booness) File.chmod(0000, @state_file.path()) proc { Puppet::Util::Storage.load() }.should raise_error() end - it "it should attempt to rename the state file if the file is corrupted" do + it "should attempt to rename the state file if the file is corrupted" do # We fake corruption by causing YAML.load to raise an exception YAML.expects(:load).raises(Puppet::Error) File.expects(:rename).at_least_once @@ -158,7 +159,7 @@ describe Puppet::Util::Storage do proc { Puppet::Util::Storage.load() }.should_not raise_error() end - it "it should fail gracefully on load() if the state file is not a regular file" do + it "should fail gracefully on load() if the state file is not a regular file" do @state_file.close!() Dir.mkdir(Puppet[:statefile]) File.expects(:rename).returns(0) @@ -168,7 +169,7 @@ describe Puppet::Util::Storage do Dir.rmdir(Puppet[:statefile]) end - it "it should fail gracefully on load() if it cannot get a read lock on the state file" do + it "should fail gracefully on load() if it cannot get a read lock on the state file" do Puppet::Util.expects(:readlock).yields(false) test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} YAML.expects(:load).returns(test_yaml) @@ -191,7 +192,7 @@ describe Puppet::Util::Storage do Puppet[:statefile] = @state_file.path() end - it "it should create the state file if it does not exist" do + it "should create the state file if it does not exist" do @state_file.close!() FileTest.exists?(Puppet[:statefile]).should be_false() Puppet::Util::Storage.cache(:yayness) @@ -200,7 +201,7 @@ describe Puppet::Util::Storage do FileTest.exists?(Puppet[:statefile]).should be_true() end - it "it should raise an exception if the state file is not a regular file" do + it "should raise an exception if the state file is not a regular file" do @state_file.close!() Dir.mkdir(Puppet[:statefile]) Puppet::Util::Storage.cache(:yayness) @@ -210,14 +211,14 @@ describe Puppet::Util::Storage do Dir.rmdir(Puppet[:statefile]) end - it "it should raise an exception if it cannot get a write lock on the state file" do + 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::Storage.cache(:yayness) proc { Puppet::Util::Storage.store() }.should raise_error() end - it "it should load() the same information that it store()s" do + it "should load() the same information that it store()s" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state().should == {:yayness=>{}} -- cgit From 77ee4ec6ca56973ce315358864722a152557857f Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 19 May 2008 02:05:19 -0500 Subject: Refactoring how the provider confine tests work, again. Now each of the test types is a separate subclass of Confine, so that they can have all of their own logging and summarizing behaviour. Also, added a 'feature' type, which can test for the availability of Puppet features (and log their absence more usefully). --- lib/puppet/provider/confine.rb | 88 ++++++------- lib/puppet/provider/confine/exists.rb | 22 ++++ lib/puppet/provider/confine/facter.rb | 37 ++++++ lib/puppet/provider/confine/false.rb | 19 +++ lib/puppet/provider/confine/feature.rb | 17 +++ lib/puppet/provider/confine/true.rb | 20 +++ lib/puppet/provider/confine_collection.rb | 37 +++--- lib/puppet/provider/confiner.rb | 2 +- lib/puppet/reference/providers.rb | 2 + spec/unit/provider/confine.rb | 200 ++++-------------------------- spec/unit/provider/confine/exists.rb | 80 ++++++++++++ spec/unit/provider/confine/facter.rb | 86 +++++++++++++ spec/unit/provider/confine/false.rb | 52 ++++++++ spec/unit/provider/confine/feature.rb | 59 +++++++++ spec/unit/provider/confine/true.rb | 50 ++++++++ spec/unit/provider/confine_collection.rb | 121 +++++++++--------- spec/unit/provider/confiner.rb | 4 +- 17 files changed, 589 insertions(+), 307 deletions(-) create mode 100644 lib/puppet/provider/confine/exists.rb create mode 100644 lib/puppet/provider/confine/facter.rb create mode 100644 lib/puppet/provider/confine/false.rb create mode 100644 lib/puppet/provider/confine/feature.rb create mode 100644 lib/puppet/provider/confine/true.rb create mode 100755 spec/unit/provider/confine/exists.rb create mode 100755 spec/unit/provider/confine/facter.rb create mode 100755 spec/unit/provider/confine/false.rb create mode 100755 spec/unit/provider/confine/feature.rb create mode 100755 spec/unit/provider/confine/true.rb diff --git a/lib/puppet/provider/confine.rb b/lib/puppet/provider/confine.rb index 227c923e6..35b80fdcf 100644 --- a/lib/puppet/provider/confine.rb +++ b/lib/puppet/provider/confine.rb @@ -5,85 +5,73 @@ require 'puppet/util' class Puppet::Provider::Confine include Puppet::Util - attr_reader :test, :values, :fact + @tests = {} - # Mark that this confine is used for testing binary existence. - attr_accessor :for_binary - def for_binary? - for_binary + class << self + attr_accessor :name end - def exists?(value) - if for_binary? - return false unless value = binary(value) - end - value and FileTest.exist?(value) - end + def self.inherited(klass) + name = klass.to_s.split("::").pop.downcase.to_sym + raise "Test %s is already defined" % name if @tests.include?(name) + + klass.name = name - # Are we a facter comparison? - def facter? - defined?(@facter) + @tests[name] = klass end - # Retrieve the value from facter - def facter_value - unless defined?(@facter_value) and @facter_value - @facter_value = Facter.value(@fact).to_s.downcase + def self.test(name) + unless @tests[name] + begin + require "puppet/provider/confine/%s" % name + rescue LoadError => detail + unless detail.to_s.include?("no such file") + warn "Could not load confine test '%s': %s" % [name, detail] + end + # Could not find file + end end - @facter_value + return @tests[name] end - def false?(value) - ! value + attr_reader :values + + # Mark that this confine is used for testing binary existence. + attr_accessor :for_binary + def for_binary? + for_binary end - def initialize(test, values) + def initialize(values) values = [values] unless values.is_a?(Array) @values = values - - if %w{exists false true}.include?(test.to_s) - @test = test - @method = @test.to_s + "?" - else - @fact = test - @test = :facter - @method = "match?" - end end - def match?(value) - facter_value == value.to_s.downcase + # Provide a hook for the message when there's a failure. + def message(value) + "" end # Collect the results of all of them. def result - values.collect { |value| send(@method, value) } - end - - def true?(value) - # Double negate, so we only get true or false. - ! ! value + values.collect { |value| pass?(value) } end # Test whether our confine matches. def valid? values.each do |value| - unless send(@method, value) - msg = case test - when :false: "false value when expecting true" - when :true: "true value when expecting false" - when :exists: "file %s does not exist" % value - when :facter: "facter value '%s' for '%s' not in required list '%s'" % [value, @fact, values.join(",")] - end - Puppet.debug msg + unless pass?(value) + Puppet.debug message(value) return false end end return true ensure - # Reset the cache. We want to cache it during a given - # run, but across runs. - @facter_value = nil + reset + end + + # Provide a hook for subclasses. + def reset end end diff --git a/lib/puppet/provider/confine/exists.rb b/lib/puppet/provider/confine/exists.rb new file mode 100644 index 000000000..1d1ed8c84 --- /dev/null +++ b/lib/puppet/provider/confine/exists.rb @@ -0,0 +1,22 @@ +require 'puppet/provider/confine' + +class Puppet::Provider::Confine::Exists < Puppet::Provider::Confine + def self.summarize(confines) + confines.inject([]) { |total, confine| total + confine.summary } + end + + def pass?(value) + if for_binary? + return false unless value = binary(value) + end + value and FileTest.exist?(value) + end + + def message(value) + "file %s does not exist" % value + end + + def summary + result.zip(values).inject([]) { |array, args| val, f = args; array << f unless val; array } + end +end diff --git a/lib/puppet/provider/confine/facter.rb b/lib/puppet/provider/confine/facter.rb new file mode 100644 index 000000000..9bb66c058 --- /dev/null +++ b/lib/puppet/provider/confine/facter.rb @@ -0,0 +1,37 @@ +require 'puppet/provider/confine' + +class Puppet::Provider::Confine::Facter < Puppet::Provider::Confine + def self.summarize(confines) + result = Hash.new { |hash, key| hash[key] = [] } + confines.inject(result) { |total, confine| total[confine.fact] += confine.values unless confine.valid?; total } + end + + attr_accessor :fact + + # Are we a facter comparison? + def facter? + defined?(@facter) + end + + # Retrieve the value from facter + def facter_value + unless defined?(@facter_value) and @facter_value + @facter_value = ::Facter.value(@fact).to_s.downcase + end + @facter_value + end + + def message(value) + "facter value '%s' for '%s' not in required list '%s'" % [value, self.fact, values.join(",")] + end + + def pass?(value) + facter_value == value.to_s.downcase + end + + def reset + # Reset the cache. We want to cache it during a given + # run, but across runs. + @facter_value = nil + end +end diff --git a/lib/puppet/provider/confine/false.rb b/lib/puppet/provider/confine/false.rb new file mode 100644 index 000000000..b5b2b51c8 --- /dev/null +++ b/lib/puppet/provider/confine/false.rb @@ -0,0 +1,19 @@ +require 'puppet/provider/confine' + +class Puppet::Provider::Confine::False < Puppet::Provider::Confine + def self.summarize(confines) + confines.inject(0) { |count, confine| count + confine.summary } + end + + def pass?(value) + ! value + end + + def message(value) + "true value when expecting false" + end + + def summary + result.find_all { |v| v == false }.length + end +end diff --git a/lib/puppet/provider/confine/feature.rb b/lib/puppet/provider/confine/feature.rb new file mode 100644 index 000000000..1d92b001a --- /dev/null +++ b/lib/puppet/provider/confine/feature.rb @@ -0,0 +1,17 @@ +require 'puppet/provider/confine' + +class Puppet::Provider::Confine::Feature < Puppet::Provider::Confine + def self.summarize(confines) + confines.collect { |c| c.values }.flatten.uniq.find_all { |value| ! confines[0].pass?(value) } + end + + # Is the named feature available? + def pass?(value) + Puppet.features.send(value.to_s + "?") + end + + def message(value) + "feature %s is missing" % value + end +end + diff --git a/lib/puppet/provider/confine/true.rb b/lib/puppet/provider/confine/true.rb new file mode 100644 index 000000000..86b3b144f --- /dev/null +++ b/lib/puppet/provider/confine/true.rb @@ -0,0 +1,20 @@ +require 'puppet/provider/confine' + +class Puppet::Provider::Confine::True < Puppet::Provider::Confine + def self.summarize(confines) + confines.inject(0) { |count, confine| count + confine.summary } + end + + def pass?(value) + # Double negate, so we only get true or false. + ! ! value + end + + def message(value) + "false value when expecting true" + end + + def summary + result.find_all { |v| v == true }.length + end +end diff --git a/lib/puppet/provider/confine_collection.rb b/lib/puppet/provider/confine_collection.rb index f38035521..0c80086c9 100644 --- a/lib/puppet/provider/confine_collection.rb +++ b/lib/puppet/provider/confine_collection.rb @@ -11,8 +11,14 @@ class Puppet::Provider::ConfineCollection for_binary = false end hash.each do |test, values| - @confines << Puppet::Provider::Confine.new(test, values) - @confines[-1].for_binary = true if for_binary + if klass = Puppet::Provider::Confine.test(test) + @confines << klass.new(values) + @confines[-1].for_binary = true if for_binary + else + confine = Puppet::Provider::Confine.test(:facter).new(values) + confine.fact = test + @confines << confine + end end end @@ -22,24 +28,17 @@ class Puppet::Provider::ConfineCollection # Return a hash of the whole confine set, used for the Provider # reference. - def result - defaults = { - :false => 0, - :true => 0, - :exists => [], - :facter => {} - } - missing = Hash.new { |hash, key| hash[key] = defaults[key] } - @confines.each do |confine| - case confine.test - when :false: missing[confine.test] += confine.result.find_all { |v| v == false }.length - when :true: missing[confine.test] += confine.result.find_all { |v| v == true }.length - when :exists: confine.result.zip(confine.values).each { |val, f| missing[:exists] << f unless val } - when :facter: missing[:facter][confine.fact] = confine.values if confine.result.include?(false) - end - end + def summary + confines = Hash.new { |hash, key| hash[key] = [] } + @confines.each { |confine| confines[confine.class] << confine } + result = {} + confines.each do |klass, list| + value = klass.summarize(list) + next if (value.respond_to?(:length) and value.length == 0) or (value == 0) + result[klass.name] = value - missing + end + result end def valid? diff --git a/lib/puppet/provider/confiner.rb b/lib/puppet/provider/confiner.rb index 3e406873b..4605523e8 100644 --- a/lib/puppet/provider/confiner.rb +++ b/lib/puppet/provider/confiner.rb @@ -15,6 +15,6 @@ module Puppet::Provider::Confiner # Check whether this implementation is suitable for our platform. def suitable?(short = true) return confine_collection.valid? if short - return confine_collection.result + return confine_collection.summary end end diff --git a/lib/puppet/reference/providers.rb b/lib/puppet/reference/providers.rb index da815ddf1..610c7550d 100644 --- a/lib/puppet/reference/providers.rb +++ b/lib/puppet/reference/providers.rb @@ -71,6 +71,8 @@ providers = Puppet::Util::Reference.newreference :providers, :title => "Provider details += " - Got %s true tests that should have been false\n" % values when :false: details += " - Got %s false tests that should have been true\n" % values + when :feature: + details += " - Missing features %s\n" % values.collect { |f| f.to_s }.join(",") end end notes << details diff --git a/spec/unit/provider/confine.rb b/spec/unit/provider/confine.rb index bb2e751d6..6a9214e26 100755 --- a/spec/unit/provider/confine.rb +++ b/spec/unit/provider/confine.rb @@ -5,219 +5,61 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/provider/confine' describe Puppet::Provider::Confine do - it "should require a test" do - lambda { Puppet::Provider::Confine.new }.should raise_error(ArgumentError) - end - it "should require a value" do - lambda { Puppet::Provider::Confine.new(:exists) }.should raise_error(ArgumentError) - end - - it "should have a test" do - Puppet::Provider::Confine.new(:exists, "/some/file").test.should == :exists + lambda { Puppet::Provider::Confine.new() }.should raise_error(ArgumentError) end it "should always convert values to an array" do - Puppet::Provider::Confine.new(:exists, "/some/file").values.should be_instance_of(Array) + Puppet::Provider::Confine.new("/some/file").values.should be_instance_of(Array) end - it "should have an accessor for its fact" do - Puppet::Provider::Confine.new(:foo, :bar).should respond_to(:fact) + it "should have a 'true' test" do + Puppet::Provider::Confine.test(:true).should be_instance_of(Class) end - it "should be possible to mark the confine as a binary test" do - Puppet::Provider::Confine.new(:foo, :bar).should respond_to(:for_binary=) + it "should have a 'false' test" do + Puppet::Provider::Confine.test(:false).should be_instance_of(Class) end - it "should have a boolean method to indicate it's a binary confine" do - Puppet::Provider::Confine.new(:foo, :bar).should respond_to(:for_binary?) + it "should have a 'feature' test" do + Puppet::Provider::Confine.test(:feature).should be_instance_of(Class) end - it "should indicate it's a boolean confine if it has been marked that way" do - confine = Puppet::Provider::Confine.new(:foo, :bar) - confine.for_binary = true - confine.should be_for_binary + it "should have an 'exists' test" do + Puppet::Provider::Confine.test(:exists).should be_instance_of(Class) end - it "should have a method for returning a binary's path" do - Puppet::Provider::Confine.new(:foo, :bar).private_methods.should be_include("binary") - end - - describe "when testing values" do - before { @confine = Puppet::Provider::Confine.new("eh", "foo") } - - describe "and the test is 'false'" do - it "should use the 'false?' method to test validity" do - @confine = Puppet::Provider::Confine.new(:false, "foo") - @confine.expects(:false?).with("foo") - @confine.valid? - end - - it "should return true if the value is false" do - @confine.false?(false).should be_true - end - - it "should return false if the value is not false" do - @confine.false?("else").should be_false - end - - it "should log that a value is false" do - @confine = Puppet::Provider::Confine.new(:false, "foo") - Puppet.expects(:debug).with { |l| l.include?("false") } - @confine.valid? - end - end - - describe "and the test is 'true'" do - it "should use the 'true?' method to test validity" do - @confine = Puppet::Provider::Confine.new(:true, "foo") - @confine.expects(:true?).with("foo") - @confine.valid? - end - - it "should return true if the value is not false" do - @confine.true?("else").should be_true - end - - it "should return false if the value is false" do - @confine.true?(nil).should be_false - end - end - - describe "and the test is 'exists'" do - it "should use the 'exists?' method to test validity" do - @confine = Puppet::Provider::Confine.new(:exists, "foo") - @confine.expects(:exists?).with("foo") - @confine.valid? - end - - it "should return false if the value is false" do - @confine.exists?(false).should be_false - end - - it "should return false if the value does not point to a file" do - FileTest.expects(:exist?).with("/my/file").returns false - @confine.exists?("/my/file").should be_false - end - - it "should return true if the value points to a file" do - FileTest.expects(:exist?).with("/my/file").returns true - @confine.exists?("/my/file").should be_true - end - - it "should log that a value is true" do - @confine = Puppet::Provider::Confine.new(:true, nil) - Puppet.expects(:debug).with { |l| l.include?("true") } - @confine.valid? - end - - describe "and the confine is for binaries" do - before { @confine.stubs(:for_binary).returns true } - it "should use its 'binary' method to look up the full path of the file" do - @confine.expects(:binary).returns nil - @confine.exists?("/my/file") - end - - it "should return false if no binary can be found" do - @confine.expects(:binary).with("/my/file").returns nil - @confine.exists?("/my/file").should be_false - end - - it "should return true if the binary can be found and the file exists" do - @confine.expects(:binary).with("/my/file").returns "/my/file" - FileTest.expects(:exist?).with("/my/file").returns true - @confine.exists?("/my/file").should be_true - end - - it "should return false if the binary can be found but the file does not exist" do - @confine.expects(:binary).with("/my/file").returns "/my/file" - FileTest.expects(:exist?).with("/my/file").returns true - @confine.exists?("/my/file").should be_true - end - end - end - - describe "and the test is not 'true', 'false', or 'exists'" do - it "should use the 'match?' method to test validity" do - @confine = Puppet::Provider::Confine.new("yay", "foo") - @confine.expects(:match?).with("foo") - @confine.valid? - end - - it "should return true if the value matches the facter value" do - Facter.expects(:value).returns("foo") - - @confine.match?("foo").should be_true - end - - it "should return false if the value does not match the facter value" do - Facter.expects(:value).returns("boo") - - @confine.match?("foo").should be_false - end - - it "should be case insensitive" do - Facter.expects(:value).returns("FOO") - - @confine.match?("foo").should be_true - end - - it "should not care whether the value is a string or symbol" do - Facter.expects(:value).returns("FOO") - - @confine.match?(:foo).should be_true - end - - it "should cache the fact during testing" do - Facter.expects(:value).once.returns("FOO") - - @confine.match?(:foo) - @confine.match?(:foo) - end - - it "should log that the fact value is not correct" do - @confine = Puppet::Provider::Confine.new("foo", ["bar", "bee"]) - Facter.expects(:value).with("foo").returns "yayness" - Puppet.expects(:debug).with { |l| l.include?("facter") and l.include?("bar,bee") } - @confine.valid? - end - end + it "should have a 'facter' test" do + Puppet::Provider::Confine.test(:facter).should be_instance_of(Class) end describe "when testing all values" do - before { @confine = Puppet::Provider::Confine.new(:true, %w{a b c}) } + before { @confine = Puppet::Provider::Confine.new(%w{a b c}) } it "should be invalid if any values fail" do - @confine.stubs(:true?).returns true - @confine.expects(:true?).with("b").returns false + @confine.stubs(:pass?).returns true + @confine.expects(:pass?).with("b").returns false @confine.should_not be_valid end it "should be valid if all values pass" do - @confine.stubs(:true?).returns true + @confine.stubs(:pass?).returns true @confine.should be_valid end it "should short-cut at the first failing value" do - @confine.expects(:true?).once.returns false - @confine.valid? - end - - it "should remove the cached facter value" do - @confine = Puppet::Provider::Confine.new(:foo, :bar) - Facter.expects(:value).with(:foo).times(2).returns "eh" - @confine.valid? + @confine.expects(:pass?).once.returns false @confine.valid? end end describe "when testing the result of the values" do - before { @confine = Puppet::Provider::Confine.new(:true, %w{a b c d}) } + before { @confine = Puppet::Provider::Confine.new(%w{a b c d}) } it "should return an array with the result of the test for each value" do - @confine.stubs(:true?).returns true - @confine.expects(:true?).with("b").returns false - @confine.expects(:true?).with("d").returns false + @confine.stubs(:pass?).returns true + @confine.expects(:pass?).with("b").returns false + @confine.expects(:pass?).with("d").returns false @confine.result.should == [true, false, true, false] end diff --git a/spec/unit/provider/confine/exists.rb b/spec/unit/provider/confine/exists.rb new file mode 100755 index 000000000..1ab1d39f7 --- /dev/null +++ b/spec/unit/provider/confine/exists.rb @@ -0,0 +1,80 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/provider/confine/exists' + +describe Puppet::Provider::Confine::Exists do + before do + @confine = Puppet::Provider::Confine::Exists.new("/my/file") + end + + it "should be named :exists" do + Puppet::Provider::Confine::Exists.name.should == :exists + end + + it "should use the 'pass?' method to test validity" do + @confine.expects(:pass?).with("/my/file") + @confine.valid? + end + + it "should return false if the value is false" do + @confine.pass?(false).should be_false + end + + it "should return false if the value does not point to a file" do + FileTest.expects(:exist?).with("/my/file").returns false + @confine.pass?("/my/file").should be_false + end + + it "should return true if the value points to a file" do + FileTest.expects(:exist?).with("/my/file").returns true + @confine.pass?("/my/file").should be_true + end + + it "should produce a message saying that a file is missing" do + @confine.message("/my/file").should be_include("does not exist") + end + + describe "and the confine is for binaries" do + before { @confine.stubs(:for_binary).returns true } + it "should use its 'binary' method to look up the full path of the file" do + @confine.expects(:binary).returns nil + @confine.pass?("/my/file") + end + + it "should return false if no binary can be found" do + @confine.expects(:binary).with("/my/file").returns nil + @confine.pass?("/my/file").should be_false + end + + it "should return true if the binary can be found and the file exists" do + @confine.expects(:binary).with("/my/file").returns "/my/file" + FileTest.expects(:exist?).with("/my/file").returns true + @confine.pass?("/my/file").should be_true + end + + it "should return false if the binary can be found but the file does not exist" do + @confine.expects(:binary).with("/my/file").returns "/my/file" + FileTest.expects(:exist?).with("/my/file").returns true + @confine.pass?("/my/file").should be_true + end + end + + it "should produce a summary containing all missing files" do + FileTest.stubs(:exist?).returns true + FileTest.expects(:exist?).with("/two").returns false + FileTest.expects(:exist?).with("/four").returns false + + confine = Puppet::Provider::Confine::Exists.new %w{/one /two /three /four} + confine.summary.should == %w{/two /four} + end + + it "should summarize multiple instances by returning a flattened array of their summaries" do + c1 = mock '1', :summary => %w{one} + c2 = mock '2', :summary => %w{two} + c3 = mock '3', :summary => %w{three} + + Puppet::Provider::Confine::Exists.summarize([c1, c2, c3]).should == %w{one two three} + end +end diff --git a/spec/unit/provider/confine/facter.rb b/spec/unit/provider/confine/facter.rb new file mode 100755 index 000000000..560263257 --- /dev/null +++ b/spec/unit/provider/confine/facter.rb @@ -0,0 +1,86 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/provider/confine/facter' + +describe Puppet::Provider::Confine::Facter::Facter do + it "should be named :facter" do + Puppet::Provider::Confine::Facter.name.should == :facter + end + + it "should require a value" do + lambda { Puppet::Provider::Confine::Facter.new() }.should raise_error(ArgumentError) + end + + it "should always convert values to an array" do + Puppet::Provider::Confine::Facter.new("/some/file").values.should be_instance_of(Array) + end + + it "should have an accessor for its fact" do + Puppet::Provider::Confine::Facter.new(:bar).should respond_to(:fact) + end + + describe "when testing values" do + before { @confine = Puppet::Provider::Confine::Facter.new("foo") } + it "should use the 'pass?' method to test validity" do + @confine.expects(:pass?).with("foo") + @confine.valid? + end + + it "should return true if the value matches the facter value" do + Facter.expects(:value).returns("foo") + + @confine.pass?("foo").should be_true + end + + it "should return false if the value does not match the facter value" do + Facter.expects(:value).returns("boo") + + @confine.pass?("foo").should be_false + end + + it "should be case insensitive" do + Facter.expects(:value).returns("FOO") + + @confine.pass?("foo").should be_true + end + + it "should not care whether the value is a string or symbol" do + Facter.expects(:value).returns("FOO") + + @confine.pass?(:foo).should be_true + end + + it "should cache the fact during testing" do + Facter.expects(:value).once.returns("FOO") + + @confine.pass?(:foo) + @confine.pass?(:foo) + end + + it "should produce a message that the fact value is not correct" do + @confine = Puppet::Provider::Confine::Facter.new(%w{bar bee}) + message = @confine.message("value") + message.should be_include("facter") + message.should be_include("bar,bee") + end + end + + describe "when summarizing multiple instances" do + it "should return a hash of failing variables and their values" do + c1 = stub '1', :valid? => false, :values => %w{one}, :fact => "uno" + c2 = stub '2', :valid? => true, :values => %w{two}, :fact => "dos" + c3 = stub '3', :valid? => false, :values => %w{three}, :fact => "tres" + + Puppet::Provider::Confine::Facter.summarize([c1, c2, c3]).should == {"uno" => %w{one}, "tres" => %w{three}} + end + + it "should combine the values of multiple confines with the same fact" do + c1 = stub '1', :valid? => false, :values => %w{one}, :fact => "uno" + c2 = stub '2', :valid? => false, :values => %w{two}, :fact => "uno" + + Puppet::Provider::Confine::Facter.summarize([c1, c2]).should == {"uno" => %w{one two}} + end + end +end diff --git a/spec/unit/provider/confine/false.rb b/spec/unit/provider/confine/false.rb new file mode 100755 index 000000000..c6c43e391 --- /dev/null +++ b/spec/unit/provider/confine/false.rb @@ -0,0 +1,52 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/provider/confine/false' + +describe Puppet::Provider::Confine::False do + it "should be named :false" do + Puppet::Provider::Confine::False.name.should == :false + end + + it "should require a value" do + lambda { Puppet::Provider::Confine.new() }.should raise_error(ArgumentError) + end + + describe "when testing values" do + before { @confine = Puppet::Provider::Confine::False.new("foo") } + + it "should use the 'pass?' method to test validity" do + @confine = Puppet::Provider::Confine::False.new("foo") + @confine.expects(:pass?).with("foo") + @confine.valid? + end + + it "should return true if the value is false" do + @confine.pass?(false).should be_true + end + + it "should return false if the value is not false" do + @confine.pass?("else").should be_false + end + + it "should produce a message that a value is true" do + @confine = Puppet::Provider::Confine::False.new("foo") + @confine.message("eh").should be_include("true") + end + end + + it "should be able to produce a summary with the number of incorrectly true values" do + confine = Puppet::Provider::Confine::False.new %w{one two three four} + confine.expects(:pass?).times(4).returns(true).returns(false).returns(true).returns(false) + confine.summary.should == 2 + end + + it "should summarize multiple instances by summing their summaries" do + c1 = mock '1', :summary => 1 + c2 = mock '2', :summary => 2 + c3 = mock '3', :summary => 3 + + Puppet::Provider::Confine::False.summarize([c1, c2, c3]).should == 6 + end +end diff --git a/spec/unit/provider/confine/feature.rb b/spec/unit/provider/confine/feature.rb new file mode 100755 index 000000000..1845c9a47 --- /dev/null +++ b/spec/unit/provider/confine/feature.rb @@ -0,0 +1,59 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/provider/confine/feature' + +describe Puppet::Provider::Confine::Feature do + it "should be named :feature" do + Puppet::Provider::Confine::Feature.name.should == :feature + end + + it "should require a value" do + lambda { Puppet::Provider::Confine::Feature.new() }.should raise_error(ArgumentError) + end + + it "should always convert values to an array" do + Puppet::Provider::Confine::Feature.new("/some/file").values.should be_instance_of(Array) + end + + describe "when testing values" do + before do + @features = mock 'features' + Puppet.stubs(:features).returns @features + @confine = Puppet::Provider::Confine::Feature.new("myfeature") + end + + it "should use the Puppet features instance to test validity" do + @features.expects(:myfeature?) + @confine.valid? + end + + it "should return true if the feature is present" do + @features.expects(:myfeature?).returns true + @confine.pass?("myfeature").should be_true + end + + it "should return false if the value is false" do + @features.expects(:myfeature?).returns false + @confine.pass?("myfeature").should be_false + end + + it "should log that a feature is missing" do + @confine.message("myfeat").should be_include("missing") + end + end + + it "should summarize multiple instances by returning a flattened array of all missing features" do + confines = [] + confines << Puppet::Provider::Confine::Feature.new(%w{one two}) + confines << Puppet::Provider::Confine::Feature.new(%w{two}) + confines << Puppet::Provider::Confine::Feature.new(%w{three four}) + + features = mock 'feature' + features.stub_everything + Puppet.stubs(:features).returns features + + Puppet::Provider::Confine::Feature.summarize(confines).sort.should == %w{one two three four}.sort + end +end diff --git a/spec/unit/provider/confine/true.rb b/spec/unit/provider/confine/true.rb new file mode 100755 index 000000000..c9cc83c9e --- /dev/null +++ b/spec/unit/provider/confine/true.rb @@ -0,0 +1,50 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/provider/confine/true' + +describe Puppet::Provider::Confine::True do + it "should be named :true" do + Puppet::Provider::Confine::True.name.should == :true + end + + it "should require a value" do + lambda { Puppet::Provider::Confine::True.new() }.should raise_error(ArgumentError) + end + + describe "when testing values" do + before { @confine = Puppet::Provider::Confine::True.new("foo") } + + it "should use the 'pass?' method to test validity" do + @confine.expects(:pass?).with("foo") + @confine.valid? + end + + it "should return true if the value is not false" do + @confine.pass?("else").should be_true + end + + it "should return false if the value is false" do + @confine.pass?(nil).should be_false + end + + it "should produce the message that a value is false" do + @confine.message("eh").should be_include("false") + end + end + + it "should produce the number of false values when asked for a summary" do + @confine = Puppet::Provider::Confine::True.new %w{one two three four} + @confine.expects(:pass?).times(4).returns(true).returns(false).returns(true).returns(false) + @confine.summary.should == 2 + end + + it "should summarize multiple instances by summing their summaries" do + c1 = mock '1', :summary => 1 + c2 = mock '2', :summary => 2 + c3 = mock '3', :summary => 3 + + Puppet::Provider::Confine::True.summarize([c1, c2, c3]).should == 6 + end +end diff --git a/spec/unit/provider/confine_collection.rb b/spec/unit/provider/confine_collection.rb index 3430d604f..da4b3fe72 100755 --- a/spec/unit/provider/confine_collection.rb +++ b/spec/unit/provider/confine_collection.rb @@ -9,105 +9,114 @@ describe Puppet::Provider::ConfineCollection do Puppet::Provider::ConfineCollection.new.should respond_to(:confine) end - it "should create a Confine instance for every confine call" do - Puppet::Provider::Confine.expects(:new).with(:foo, :bar).returns "eh" - Puppet::Provider::Confine.expects(:new).with(:baz, :bee).returns "eh" - Puppet::Provider::ConfineCollection.new.confine :foo => :bar, :baz => :bee - end + describe "when creating confine instances" do + it "should create an instance of the named test with the provided values" do + test_class = mock 'test_class' + test_class.expects(:new).with(%w{my values}) + Puppet::Provider::Confine.expects(:test).with(:foo).returns test_class + + Puppet::Provider::ConfineCollection.new.confine :foo => %w{my values} + end + + describe "and the test cannot be found" do + before do + @facter = mock 'facter_test' + + Puppet::Provider::Confine.expects(:test).with(:foo).returns nil + Puppet::Provider::Confine.expects(:test).with(:facter).returns @facter + end + + it "should create a Facter test with the provided values and set the fact to the test name" do + confine = mock 'confine' + confine.expects(:fact=).with(:foo) + @facter.expects(:new).with(%w{my values}).returns confine + Puppet::Provider::ConfineCollection.new.confine :foo => %w{my values} + end + end - it "should mark each confine as a binary confine if :for_binary => true is included" do - confine = mock 'confine' - confine.expects(:for_binary=).with true - Puppet::Provider::Confine.expects(:new).with(:foo, :bar).returns confine - Puppet::Provider::ConfineCollection.new.confine :foo => :bar, :for_binary => true + describe "and the 'for_binary' option was provided" do + it "should mark the test as a binary confine" do + confine = mock 'confine' + confine.expects(:for_binary=).with true + Puppet::Provider::Confine.test(:exists).expects(:new).with(:bar).returns confine + Puppet::Provider::ConfineCollection.new.confine :exists => :bar, :for_binary => true + end + end end it "should be valid if no confines are present" do Puppet::Provider::ConfineCollection.new.should be_valid end - it "should be valid if all confines are valid" do + it "should be valid if all confines pass" do c1 = mock 'c1', :valid? => true c2 = mock 'c2', :valid? => true - Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) + Puppet::Provider::Confine.test(:true).expects(:new).returns(c1) + Puppet::Provider::Confine.test(:false).expects(:new).returns(c2) confiner = Puppet::Provider::ConfineCollection.new - confiner.confine :foo => :bar, :baz => :bee + confiner.confine :true => :bar, :false => :bee confiner.should be_valid end - it "should not be valid if any confines are valid" do - c1 = mock 'c1', :valid? => true - c2 = mock 'c2', :valid? => false + it "should not be valid if any confines fail" do + c1 = stub 'c1', :valid? => true + c2 = stub 'c2', :valid? => false - Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) + Puppet::Provider::Confine.test(:true).expects(:new).returns(c1) + Puppet::Provider::Confine.test(:false).expects(:new).returns(c2) confiner = Puppet::Provider::ConfineCollection.new - confiner.confine :foo => :bar, :baz => :bee + confiner.confine :true => :bar, :false => :bee confiner.should_not be_valid end - describe "when providing a complete result" do + describe "when providing a summary" do before do @confiner = Puppet::Provider::ConfineCollection.new end it "should return a hash" do - @confiner.result.should be_instance_of(Hash) + @confiner.summary.should be_instance_of(Hash) end it "should return an empty hash if the confiner is valid" do - @confiner.result.should == {} + @confiner.summary.should == {} end - it "should contain the number of incorrectly false values" do - c1 = stub 'c1', :result => [true, false, true], :test => :true - c2 = stub 'c2', :result => [false, true, false], :test => :true - - Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) - - confiner = Puppet::Provider::ConfineCollection.new - confiner.confine :foo => :bar, :baz => :bee + it "should add each test type's summary to the hash" do + @confiner.confine :true => :bar, :false => :bee + Puppet::Provider::Confine.test(:true).expects(:summarize).returns :tsumm + Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm - confiner.result[:true].should == 3 + @confiner.summary.should == {:true => :tsumm, :false => :fsumm} end - it "should contain the number of incorrectly true values" do - c1 = stub 'c1', :result => [true, false, true], :test => :false - c2 = stub 'c2', :result => [false, true, false], :test => :false + it "should not include tests that return 0" do + @confiner.confine :true => :bar, :false => :bee + Puppet::Provider::Confine.test(:true).expects(:summarize).returns 0 + Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm - Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) - - confiner = Puppet::Provider::ConfineCollection.new - confiner.confine :foo => :bar, :baz => :bee - - confiner.result[:false].should == 3 + @confiner.summary.should == {:false => :fsumm} end - it "should contain the missing files" do - FileTest.stubs(:exist?).returns true - FileTest.expects(:exist?).with("/two").returns false - FileTest.expects(:exist?).with("/four").returns false - - confiner = Puppet::Provider::ConfineCollection.new - confiner.confine :exists => %w{/one /two} - confiner.confine :exists => %w{/three /four} + it "should not include tests that return empty arrays" do + @confiner.confine :true => :bar, :false => :bee + Puppet::Provider::Confine.test(:true).expects(:summarize).returns [] + Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm - confiner.result[:exists].should == %w{/two /four} + @confiner.summary.should == {:false => :fsumm} end - it "should contain a hash of facts and the allowed values" do - Facter.expects(:value).with(:foo).returns "yay" - Facter.expects(:value).with(:bar).returns "boo" - confiner = Puppet::Provider::ConfineCollection.new - confiner.confine :foo => "yes", :bar => "boo" + it "should not include tests that return empty hashes" do + @confiner.confine :true => :bar, :false => :bee + Puppet::Provider::Confine.test(:true).expects(:summarize).returns({}) + Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm - result = confiner.result - result[:facter][:foo].should == %w{yes} - result[:facter][:bar].should be_nil + @confiner.summary.should == {:false => :fsumm} end end end diff --git a/spec/unit/provider/confiner.rb b/spec/unit/provider/confiner.rb index 38fffc102..078fc4420 100755 --- a/spec/unit/provider/confiner.rb +++ b/spec/unit/provider/confiner.rb @@ -54,8 +54,8 @@ describe Puppet::Provider::Confiner do @object.should_not be_suitable end - it "should return the result of the confine collection if a long result is asked for" do - @coll.expects(:result).returns "myresult" + it "should return the summary of the confine collection if a long result is asked for" do + @coll.expects(:summary).returns "myresult" @object.suitable?(false).should == "myresult" end end -- cgit From b8ce6a1a8c5dc370cae86cc3a40450d472e6843c Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 19 May 2008 22:10:51 -0500 Subject: Mocking Facter in an integration test, so it works with no networking --- spec/integration/node/catalog.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/integration/node/catalog.rb b/spec/integration/node/catalog.rb index ca14c2ea8..941d2cc6c 100755 --- a/spec/integration/node/catalog.rb +++ b/spec/integration/node/catalog.rb @@ -7,6 +7,12 @@ require File.dirname(__FILE__) + '/../../spec_helper' describe Puppet::Node::Catalog do describe "when using the indirector" do + before do + # This is so the tests work w/out networking. + Facter.stubs(:to_hash).returns({"hostname" => "foo.domain.com"}) + Facter.stubs(:value).returns("eh") + end + after { Puppet::Node::Catalog.indirection.clear_cache } it "should be able to delegate to the :yaml terminus" do -- cgit From ee4be4f78f7c904dbe5873ff7b44993d1440da41 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 19 May 2008 22:16:13 -0500 Subject: Removing an unused file. Closes #1229. --- lib/puppet/util/variables.rb | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 lib/puppet/util/variables.rb diff --git a/lib/puppet/util/variables.rb b/lib/puppet/util/variables.rb deleted file mode 100644 index 1a78ef5c1..000000000 --- a/lib/puppet/util/variables.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Puppet::Util::Variables - def inithooks - @instance_init_hooks.dup - end - - def initvars - return unless defined? @class_init_hooks - self.inithooks.each do |var, value| - if value.is_a?(Class) - instance_variable_set("@" + var.to_s, value.new) - else - instance_variable_set("@" + var.to_s, value) - end - end - end - - def instancevar(hash) - @instance_init_hooks ||= {} - - unless method_defined?(:initvars) - define_method(:initvars) do - self.class.inithooks.each do |var, value| - if value.is_a?(Class) - instance_variable_set("@" + var.to_s, value.new) - else - instance_variable_set("@" + var.to_s, value) - end - end - end - end - hash.each do |var, value| - raise("Already initializing %s" % var) if @instance_init_hooks[var] - - @instance_init_hooks[var] = value - end - end -end - -- cgit