summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Turnbull <james@lovedthanlost.net>2008-05-20 19:06:45 +1000
committerJames Turnbull <james@lovedthanlost.net>2008-05-20 19:06:45 +1000
commit84a787a2a764a5035f7cbb8d30f94fc601bed154 (patch)
treeec46287a86ce738fec5774347ea4f9afcf804953
parent390db8061ae81174ee9d42e26a6ebfe7182ea529 (diff)
parentee4be4f78f7c904dbe5873ff7b44993d1440da41 (diff)
downloadpuppet-84a787a2a764a5035f7cbb8d30f94fc601bed154.tar.gz
puppet-84a787a2a764a5035f7cbb8d30f94fc601bed154.tar.xz
puppet-84a787a2a764a5035f7cbb8d30f94fc601bed154.zip
Merge branch '0.24.x' of git://github.com/lak/puppet into 0.24.x
Conflicts: CHANGELOG
-rw-r--r--CHANGELOG4
-rw-r--r--lib/puppet/defaults.rb8
-rw-r--r--lib/puppet/provider/confine.rb88
-rw-r--r--lib/puppet/provider/confine/exists.rb22
-rw-r--r--lib/puppet/provider/confine/facter.rb37
-rw-r--r--lib/puppet/provider/confine/false.rb19
-rw-r--r--lib/puppet/provider/confine/feature.rb17
-rw-r--r--lib/puppet/provider/confine/true.rb20
-rw-r--r--lib/puppet/provider/confine_collection.rb37
-rw-r--r--lib/puppet/provider/confiner.rb2
-rw-r--r--lib/puppet/reference/providers.rb2
-rw-r--r--lib/puppet/util/storage.rb4
-rw-r--r--lib/puppet/util/variables.rb38
-rwxr-xr-xspec/integration/defaults.rb17
-rwxr-xr-xspec/integration/node/catalog.rb6
-rwxr-xr-xspec/unit/provider/confine.rb200
-rwxr-xr-xspec/unit/provider/confine/exists.rb80
-rwxr-xr-xspec/unit/provider/confine/facter.rb86
-rwxr-xr-xspec/unit/provider/confine/false.rb52
-rwxr-xr-xspec/unit/provider/confine/feature.rb59
-rwxr-xr-xspec/unit/provider/confine/true.rb50
-rwxr-xr-xspec/unit/provider/confine_collection.rb121
-rwxr-xr-xspec/unit/provider/confiner.rb4
-rwxr-xr-xspec/unit/provider/interface/redhat.rb4
-rwxr-xr-xspec/unit/util/storage.rb237
25 files changed, 864 insertions, 350 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 870afb92b..5b5148ff4 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,6 +2,10 @@
Created a Confiner module for the Provider class methods, enhanced
the interface between it and the Confine class to make sure binary
paths are searched for fresh each time.
+
+ Modified the 'factpath' setting to automatically configure
+ Facter to load facts there if a new enough version of
+ Facter is used.
Crontab provider: fix a parse error when a line begins with a space
character (fixes #1216)
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/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/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/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
-
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
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
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
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
diff --git a/spec/unit/util/storage.rb b/spec/unit/util/storage.rb
new file mode 100755
index 000000000..55d1d1f61
--- /dev/null
+++ b/spec/unit/util/storage.rb
@@ -0,0 +1,237 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'yaml'
+require 'tempfile'
+
+require 'puppet/util/storage'
+
+describe Puppet::Util::Storage do
+ before(:all) do
+ Puppet[:statedir] = Dir.tmpdir()
+ end
+
+ before(:each) do
+ Puppet::Util::Storage.clear()
+ end
+
+ describe "when caching a symbol" do
+ it "should return an empty hash" do
+ Puppet::Util::Storage.cache(:yayness).should == {}
+ Puppet::Util::Storage.cache(:more_yayness).should == {}
+ end
+
+ it "should add the symbol to its internal state" do
+ Puppet::Util::Storage.cache(:yayness)
+ Puppet::Util::Storage.state().should == {:yayness=>{}}
+ end
+
+ 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.state().should == {:yayness=>{},:bubblyness=>{}}
+ end
+ 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 "should return an empty hash" do
+ Puppet::Util::Storage.cache(@file_test).should == {}
+ Puppet::Util::Storage.cache(@exec_test).should == {}
+ end
+
+ 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]"=>{}}
+ Puppet::Util::Storage.cache(@exec_test)
+ Puppet::Util::Storage.state().should == {"File[/yayness]"=>{}, "Exec[/bin/ls /yayness]"=>{}}
+ end
+ end
+
+ describe "when caching invalid objects" do
+ before(:all) do
+ @bogus_objects = [ {}, [], "foo", 42, nil, Tempfile.new('storage_test') ]
+ end
+
+ it "should raise an ArgumentError" do
+ @bogus_objects.each do |object|
+ proc { Puppet::Util::Storage.cache(object) }.should raise_error()
+ end
+ end
+
+ 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.state().should == {}
+ end
+ end
+ end
+ end
+
+ 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()
+ Puppet::Util::Storage.state().should == {}
+ end
+
+ 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 "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 "should not lose its internal state when load() is called" do
+ FileTest.exists?(@path).should be_false()
+
+ Puppet::Util::Storage.cache(:yayness)
+ Puppet::Util::Storage.state().should == {:yayness=>{}}
+
+ Puppet[:statefile] = @path
+ proc { Puppet::Util::Storage.load() }.should_not raise_error()
+
+ Puppet::Util::Storage.state().should == {:yayness=>{}}
+ end
+ end
+
+ 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
+
+ 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=>{}}
+
+ proc { Puppet::Util::Storage.load() }.should_not raise_error()
+ Puppet::Util::Storage.state().should == {}
+ end
+
+ 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_not raise_error()
+ Puppet::Util::Storage.state().should == test_yaml
+ end
+
+ 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 "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 "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
+
+ proc { Puppet::Util::Storage.load() }.should_not raise_error()
+ end
+
+ 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)
+
+ proc { Puppet::Util::Storage.load() }.should_not raise_error()
+
+ Dir.rmdir(Puppet[:statefile])
+ 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)
+ 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.state().should == test_yaml
+ end
+
+ after(:each) do
+ @state_file.close!()
+ Puppet[:statefile] = @saved_statefile
+ end
+ end
+ end
+
+ 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
+
+ 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)
+
+ proc { Puppet::Util::Storage.store() }.should_not raise_error()
+ FileTest.exists?(Puppet[:statefile]).should be_true()
+ end
+
+ 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)
+
+ proc { Puppet::Util::Storage.store() }.should raise_error()
+
+ Dir.rmdir(Puppet[:statefile])
+ 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::Storage.cache(:yayness)
+
+ proc { Puppet::Util::Storage.store() }.should raise_error()
+ end
+
+ it "should load() the same information that it store()s" do
+ Puppet::Util::Storage.cache(:yayness)
+
+ Puppet::Util::Storage.state().should == {:yayness=>{}}
+ proc { Puppet::Util::Storage.store() }.should_not raise_error()
+ Puppet::Util::Storage.clear()
+ Puppet::Util::Storage.state().should == {}
+ proc { Puppet::Util::Storage.load() }.should_not raise_error()
+ Puppet::Util::Storage.state().should == {:yayness=>{}}
+ end
+
+ after(:each) do
+ @state_file.close!()
+ Puppet[:statefile] = @saved_statefile
+ end
+ end
+end