summaryrefslogtreecommitdiffstats
path: root/spec/unit/util
diff options
context:
space:
mode:
authorNick Lewis <nick@puppetlabs.com>2011-07-21 11:53:06 -0700
committerNick Lewis <nick@puppetlabs.com>2011-07-21 20:10:28 -0700
commitd198fedf65e472b384666fc9ae3bef487852068a (patch)
tree30b0b4175bb52d8442c6899f7463c6fe7aba8199 /spec/unit/util
parent7048b4c4d8c4a8ad45caf6a02b263ac0a9fa333e (diff)
Rework Puppet::Util::Cacher to only expire using TTLs
We have removed every usage of cached_attr in which the attribute needs to be manually expired. Thus, the only meaningful behavior provided by Puppet::Util::Cacher is expiration based on TTLs. This commit reworks the cacher to only support that behavior. Rather than accepting an options hash, of which :ttl is the only available option, cached_attr now requires a second argument, which is the TTL. TTLs are now used to compute expirations, which are stored and used for expiring values. Previously, we stored a timestamp and used it and the TTL to determine whether the attribute was expired. This had the potentially undesirable side effect that the lifetime of a cached attribute could be extended after its insertion by modifying the TTL setting for the cache. Now, the lifetime of an attribute is determined when it is set, and is thereafter immutable, aside from deliberately re-setting the expiration for that particular attribute. Reviewed-By: Jacob Helwig <jacob@puppetlabs.com>
Diffstat (limited to 'spec/unit/util')
-rwxr-xr-xspec/unit/util/cacher_spec.rb205
1 files changed, 64 insertions, 141 deletions
diff --git a/spec/unit/util/cacher_spec.rb b/spec/unit/util/cacher_spec.rb
index fe93afd2b..16414c858 100755
--- a/spec/unit/util/cacher_spec.rb
+++ b/spec/unit/util/cacher_spec.rb
@@ -3,182 +3,105 @@ require 'spec_helper'
require 'puppet/util/cacher'
-class ExpirerTest
- include Puppet::Util::Cacher::Expirer
-end
-
class CacheTest
- @@init_count = 0
-
- include Puppet::Util::Cacher
- cached_attr(:instance_cache) { Time.now }
-end
+ @@count = 0
-describe Puppet::Util::Cacher::Expirer do
- before do
- @expirer = ExpirerTest.new
+ def self.count
+ @@count
end
- it "should be able to test whether a timestamp is expired" do
- @expirer.should respond_to(:dependent_data_expired?)
- end
-
- it "should be able to expire all values" do
- @expirer.should respond_to(:expire)
- end
-
- it "should consider any value to be valid if it has never been expired" do
- @expirer.should_not be_dependent_data_expired(Time.now)
- end
+ include Puppet::Util::Cacher
- it "should consider any value created after expiration to be expired" do
- @expirer.expire
- @expirer.should be_dependent_data_expired(Time.now - 1)
+ cached_attr(:instance_cache, 10) do
+ @@count += 1
+ {:number => @@count}
end
end
describe Puppet::Util::Cacher do
- it "should be extended with the Expirer module" do
- Puppet::Util::Cacher.singleton_class.ancestors.should be_include(Puppet::Util::Cacher::Expirer)
+ before :each do
+ CacheTest.set_attr_ttl(:instance_cache, 10)
+ @object = CacheTest.new
end
- it "should support defining cached attributes", :'fails_on_ruby_1.9.2' => true do
- CacheTest.methods.should be_include("cached_attr")
+ it "should return a value calculated from the provided block" do
+ @object.instance_cache.should == {:number => CacheTest.count}
end
- it "should default to the Cacher module as its expirer" do
- CacheTest.new.expirer.should equal(Puppet::Util::Cacher)
+ it "should return the cached value from the getter every time if the value is not expired" do
+ @object.instance_cache.should equal(@object.instance_cache)
end
- describe "when using cached attributes" do
- before do
- @expirer = ExpirerTest.new
- @object = CacheTest.new
+ it "should regenerate and return a new value using the provided block if the value has expired" do
+ initial = @object.instance_cache
- @object.stubs(:expirer).returns @expirer
- end
-
- it "should create a getter for the cached attribute" do
- @object.should respond_to(:instance_cache)
- end
-
- it "should return a value calculated from the provided block" do
- time = Time.now
- Time.stubs(:now).returns time
- @object.instance_cache.should equal(time)
- end
+ # Ensure the value is expired immediately
+ CacheTest.set_attr_ttl(:instance_cache, -10)
+ @object.send(:set_expiration, :instance_cache)
- it "should return the cached value from the getter every time if the value is not expired" do
- @object.instance_cache.should equal(@object.instance_cache)
- end
-
- it "should regenerate and return a new value using the provided block if the value has been expired" do
- value = @object.instance_cache
- @expirer.expire
- @object.instance_cache.should_not equal(value)
- end
+ @object.instance_cache.should_not equal(initial)
+ end
- it "should be able to trigger expiration on its expirer" do
- @expirer.expects(:expire)
- @object.expire
- end
+ it "should be able to cache false values" do
+ @object.expects(:init_instance_cache).once.returns false
+ @object.instance_cache.should be_false
+ @object.instance_cache.should be_false
+ end
- it "should do nothing when asked to expire when no expirer is available" do
- cacher = CacheTest.new
- class << cacher
- def expirer
- nil
- end
- end
- lambda { cacher.expire }.should_not raise_error
- end
+ it "should cache values again after expiration" do
+ initial = @object.instance_cache
- it "should be able to cache false values" do
- @object.expects(:init_instance_cache).returns false
- @object.instance_cache.should be_false
- @object.instance_cache.should be_false
- end
+ # Ensure the value is expired immediately
+ CacheTest.set_attr_ttl(:instance_cache, -10)
+ @object.send(:set_expiration, :instance_cache)
- it "should cache values again after expiration" do
- @object.instance_cache
- @expirer.expire
- @object.instance_cache.should equal(@object.instance_cache)
- end
+ # Reset ttl so this new value doesn't get expired
+ CacheTest.set_attr_ttl(:instance_cache, 10)
+ after_expiration = @object.instance_cache
- it "should always consider a value expired if it has no expirer" do
- @object.stubs(:expirer).returns nil
- @object.instance_cache.should_not equal(@object.instance_cache)
- end
+ after_expiration.should_not == initial
+ @object.instance_cache.should == after_expiration
+ end
- it "should allow writing of the attribute" do
- @object.should respond_to(:instance_cache=)
- end
+ it "should allow writing of the attribute" do
+ initial = @object.instance_cache
- it "should correctly configure timestamps for expiration when the cached attribute is written to" do
- @object.instance_cache = "foo"
- @expirer.expire
- @object.instance_cache.should_not == "foo"
- end
+ @object.instance_cache = "another value"
+ @object.instance_cache.should == "another value"
+ end
- it "should allow specification of a ttl for cached attributes" do
- klass = Class.new do
- include Puppet::Util::Cacher
- end
+ it "should update the expiration when the cached attribute is set manually" do
+ # Freeze time
+ now = Time.now
+ Time.stubs(:now).returns now
- klass.cached_attr(:myattr, :ttl => 5) { Time.now }
+ @object.instance_cache
- klass.attr_ttl(:myattr).should == 5
- end
+ # Set expiration to something far in the future
+ CacheTest.set_attr_ttl(:instance_cache, 60)
+ @object.send(:set_expiration, :instance_cache)
- it "should allow specification of a ttl as a string" do
- klass = Class.new do
- include Puppet::Util::Cacher
- end
+ CacheTest.set_attr_ttl(:instance_cache, 10)
- klass.cached_attr(:myattr, :ttl => "5") { Time.now }
+ @object.instance_cache = "foo"
+ @object.instance_variable_get(:@attr_expirations)[:instance_cache].should == now + 10
+ end
- klass.attr_ttl(:myattr).should == 5
+ it "should allow specification of a ttl as a string" do
+ klass = Class.new do
+ include Puppet::Util::Cacher
end
- it "should fail helpfully if the ttl cannot be converted to an integer" do
- klass = Class.new do
- include Puppet::Util::Cacher
- end
-
- lambda { klass.cached_attr(:myattr, :ttl => "yep") { Time.now } }.should raise_error(ArgumentError)
- end
+ klass.cached_attr(:myattr, "5") { 10 }
- it "should not check for a ttl expiration if the class does not support that method" do
- klass = Class.new do
- extend Puppet::Util::Cacher
- end
+ klass.attr_ttl(:myattr).should == 5
+ end
- klass.singleton_class.cached_attr(:myattr) { "eh" }
- klass.myattr
+ it "should fail helpfully if the ttl cannot be converted to an integer" do
+ klass = Class.new do
+ include Puppet::Util::Cacher
end
- it "should automatically expire cached attributes whose ttl has expired, even if no expirer is present" do
- klass = Class.new do
- def self.to_s
- "CacheTestClass"
- end
- include Puppet::Util::Cacher
- attr_accessor :value
- end
-
- klass.cached_attr(:myattr, :ttl => 5) { self.value += 1; self.value }
-
- now = Time.now
- later = Time.now + 15
-
- instance = klass.new
- instance.value = 0
- instance.myattr.should == 1
-
- Time.expects(:now).returns later
-
- # This call should get the new Time value, which should expire the old value
- instance.myattr.should == 2
- end
+ lambda { klass.cached_attr(:myattr, "yep") { 10 } }.should raise_error(ArgumentError)
end
end