rails/activesupport/test/caching_test.rb
Simeon Simeonov 51d9b9a821 [Fixes #11512] improves cache size calculation in ActiveSupport::Cache::MemoryStore
Previously, the cache size of `ActiveSupport::Cache::MemoryStore` was calculated
as the sum of the size of its entries, ignoring the size of keys and any data
structure overhead. This could lead to the calculated cache size sometimes being
10-100x smaller than the memory used, e.g., in the case of small values.

The size of a key/entry pair is now calculated via `#cached_size`:

    def cached_size(key, entry)
      key.to_s.bytesize + entry.size + PER_ENTRY_OVERHEAD
    end

The value of `PER_ENTRY_OVERHEAD` is 240 bytes based on an [empirical
estimation](https://gist.github.com/ssimeonov/6047200) for 64-bit MRI on
1.9.3 and 2.0.

Fixes GH#11512 https://github.com/rails/rails/issues/11512
2013-07-22 09:53:03 -04:00

1016 lines
31 KiB
Ruby

require 'logger'
require 'abstract_unit'
require 'active_support/cache'
require 'dependencies_test_helpers'
class CacheKeyTest < ActiveSupport::TestCase
def test_entry_legacy_optional_ivars
legacy = Class.new(ActiveSupport::Cache::Entry) do
def initialize(value, options = {})
@value = value
@expires_in = nil
@created_at = nil
super
end
end
entry = legacy.new 'foo'
assert_equal 'foo', entry.value
end
def test_expand_cache_key
assert_equal '1/2/true', ActiveSupport::Cache.expand_cache_key([1, '2', true])
assert_equal 'name/1/2/true', ActiveSupport::Cache.expand_cache_key([1, '2', true], :name)
end
def test_expand_cache_key_with_rails_cache_id
begin
ENV['RAILS_CACHE_ID'] = 'c99'
assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key(:foo)
assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key([:foo])
assert_equal 'c99/foo/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar])
assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key(:foo, :nm)
assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key([:foo], :nm)
assert_equal 'nm/c99/foo/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar], :nm)
ensure
ENV['RAILS_CACHE_ID'] = nil
end
end
def test_expand_cache_key_with_rails_app_version
begin
ENV['RAILS_APP_VERSION'] = 'rails3'
assert_equal 'rails3/foo', ActiveSupport::Cache.expand_cache_key(:foo)
ensure
ENV['RAILS_APP_VERSION'] = nil
end
end
def test_expand_cache_key_rails_cache_id_should_win_over_rails_app_version
begin
ENV['RAILS_CACHE_ID'] = 'c99'
ENV['RAILS_APP_VERSION'] = 'rails3'
assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key(:foo)
ensure
ENV['RAILS_CACHE_ID'] = nil
ENV['RAILS_APP_VERSION'] = nil
end
end
def test_expand_cache_key_respond_to_cache_key
key = 'foo'
def key.cache_key
:foo_key
end
assert_equal 'foo_key', ActiveSupport::Cache.expand_cache_key(key)
end
def test_expand_cache_key_array_with_something_that_responds_to_cache_key
key = 'foo'
def key.cache_key
:foo_key
end
assert_equal 'foo_key', ActiveSupport::Cache.expand_cache_key([key])
end
def test_expand_cache_key_of_nil
assert_equal '', ActiveSupport::Cache.expand_cache_key(nil)
end
def test_expand_cache_key_of_false
assert_equal 'false', ActiveSupport::Cache.expand_cache_key(false)
end
def test_expand_cache_key_of_true
assert_equal 'true', ActiveSupport::Cache.expand_cache_key(true)
end
def test_expand_cache_key_of_array_like_object
assert_equal 'foo/bar/baz', ActiveSupport::Cache.expand_cache_key(%w{foo bar baz}.to_enum)
end
end
class CacheStoreSettingTest < ActiveSupport::TestCase
def test_file_fragment_cache_store
store = ActiveSupport::Cache.lookup_store :file_store, "/path/to/cache/directory"
assert_kind_of(ActiveSupport::Cache::FileStore, store)
assert_equal "/path/to/cache/directory", store.cache_path
end
def test_mem_cache_fragment_cache_store
Dalli::Client.expects(:new).with(%w[localhost], {})
store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost"
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
end
def test_mem_cache_fragment_cache_store_with_given_mem_cache
mem_cache = Dalli::Client.new
Dalli::Client.expects(:new).never
store = ActiveSupport::Cache.lookup_store :mem_cache_store, mem_cache
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
end
def test_mem_cache_fragment_cache_store_with_given_mem_cache_like_object
Dalli::Client.expects(:new).never
memcache = Object.new
def memcache.get() true end
store = ActiveSupport::Cache.lookup_store :mem_cache_store, memcache
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
end
def test_mem_cache_fragment_cache_store_with_multiple_servers
Dalli::Client.expects(:new).with(%w[localhost 192.168.1.1], {})
store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1'
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
end
def test_mem_cache_fragment_cache_store_with_options
Dalli::Client.expects(:new).with(%w[localhost 192.168.1.1], { :timeout => 10 })
store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1', :namespace => 'foo', :timeout => 10
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
assert_equal 'foo', store.options[:namespace]
end
def test_object_assigned_fragment_cache_store
store = ActiveSupport::Cache.lookup_store ActiveSupport::Cache::FileStore.new("/path/to/cache/directory")
assert_kind_of(ActiveSupport::Cache::FileStore, store)
assert_equal "/path/to/cache/directory", store.cache_path
end
end
class CacheStoreNamespaceTest < ActiveSupport::TestCase
def test_static_namespace
cache = ActiveSupport::Cache.lookup_store(:memory_store, :namespace => "tester")
cache.write("foo", "bar")
assert_equal "bar", cache.read("foo")
assert_equal "bar", cache.instance_variable_get(:@data)["tester:foo"].value
end
def test_proc_namespace
test_val = "tester"
proc = lambda{test_val}
cache = ActiveSupport::Cache.lookup_store(:memory_store, :namespace => proc)
cache.write("foo", "bar")
assert_equal "bar", cache.read("foo")
assert_equal "bar", cache.instance_variable_get(:@data)["tester:foo"].value
end
def test_delete_matched_key_start
cache = ActiveSupport::Cache.lookup_store(:memory_store, :namespace => "tester")
cache.write("foo", "bar")
cache.write("fu", "baz")
cache.delete_matched(/^fo/)
assert !cache.exist?("foo")
assert cache.exist?("fu")
end
def test_delete_matched_key
cache = ActiveSupport::Cache.lookup_store(:memory_store, :namespace => "foo")
cache.write("foo", "bar")
cache.write("fu", "baz")
cache.delete_matched(/OO/i)
assert !cache.exist?("foo")
assert cache.exist?("fu")
end
end
# Tests the base functionality that should be identical across all cache stores.
module CacheStoreBehavior
def test_should_read_and_write_strings
assert @cache.write('foo', 'bar')
assert_equal 'bar', @cache.read('foo')
end
def test_should_overwrite
@cache.write('foo', 'bar')
@cache.write('foo', 'baz')
assert_equal 'baz', @cache.read('foo')
end
def test_fetch_without_cache_miss
@cache.write('foo', 'bar')
@cache.expects(:write).never
assert_equal 'bar', @cache.fetch('foo') { 'baz' }
end
def test_fetch_with_cache_miss
@cache.expects(:write).with('foo', 'baz', @cache.options)
assert_equal 'baz', @cache.fetch('foo') { 'baz' }
end
def test_fetch_with_cache_miss_passes_key_to_block
cache_miss = false
assert_equal 3, @cache.fetch('foo') { |key| cache_miss = true; key.length }
assert cache_miss
cache_miss = false
assert_equal 3, @cache.fetch('foo') { |key| cache_miss = true; key.length }
assert !cache_miss
end
def test_fetch_with_forced_cache_miss
@cache.write('foo', 'bar')
@cache.expects(:read).never
@cache.expects(:write).with('foo', 'bar', @cache.options.merge(:force => true))
@cache.fetch('foo', :force => true) { 'bar' }
end
def test_fetch_with_cached_nil
@cache.write('foo', nil)
@cache.expects(:write).never
assert_nil @cache.fetch('foo') { 'baz' }
end
def test_should_read_and_write_hash
assert @cache.write('foo', {:a => "b"})
assert_equal({:a => "b"}, @cache.read('foo'))
end
def test_should_read_and_write_integer
assert @cache.write('foo', 1)
assert_equal 1, @cache.read('foo')
end
def test_should_read_and_write_nil
assert @cache.write('foo', nil)
assert_equal nil, @cache.read('foo')
end
def test_should_read_and_write_false
assert @cache.write('foo', false)
assert_equal false, @cache.read('foo')
end
def test_read_multi
@cache.write('foo', 'bar')
@cache.write('fu', 'baz')
@cache.write('fud', 'biz')
assert_equal({"foo" => "bar", "fu" => "baz"}, @cache.read_multi('foo', 'fu'))
end
def test_read_multi_with_expires
time = Time.now
@cache.write('foo', 'bar', :expires_in => 10)
@cache.write('fu', 'baz')
@cache.write('fud', 'biz')
Time.stubs(:now).returns(time + 11)
assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
end
def test_fetch_multi
@cache.write('foo', 'bar')
@cache.write('fud', 'biz')
values = @cache.fetch_multi('foo', 'fu', 'fud') {|value| value * 2 }
assert_equal(["bar", "fufu", "biz"], values)
assert_equal("fufu", @cache.read('fu'))
end
def test_multi_with_objects
foo = stub(:title => "FOO!", :cache_key => "foo")
bar = stub(:cache_key => "bar")
@cache.write('bar', "BAM!")
values = @cache.fetch_multi(foo, bar) {|object| object.title }
assert_equal(["FOO!", "BAM!"], values)
end
def test_read_and_write_compressed_small_data
@cache.write('foo', 'bar', :compress => true)
assert_equal 'bar', @cache.read('foo')
end
def test_read_and_write_compressed_large_data
@cache.write('foo', 'bar', :compress => true, :compress_threshold => 2)
assert_equal 'bar', @cache.read('foo')
end
def test_read_and_write_compressed_nil
@cache.write('foo', nil, :compress => true)
assert_nil @cache.read('foo')
end
def test_cache_key
obj = Object.new
def obj.cache_key
:foo
end
@cache.write(obj, "bar")
assert_equal "bar", @cache.read("foo")
end
def test_param_as_cache_key
obj = Object.new
def obj.to_param
"foo"
end
@cache.write(obj, "bar")
assert_equal "bar", @cache.read("foo")
end
def test_array_as_cache_key
@cache.write([:fu, "foo"], "bar")
assert_equal "bar", @cache.read("fu/foo")
end
def test_hash_as_cache_key
@cache.write({:foo => 1, :fu => 2}, "bar")
assert_equal "bar", @cache.read("foo=1/fu=2")
end
def test_keys_are_case_sensitive
@cache.write("foo", "bar")
assert_nil @cache.read("FOO")
end
def test_exist
@cache.write('foo', 'bar')
assert_equal true, @cache.exist?('foo')
assert_equal false, @cache.exist?('bar')
end
def test_nil_exist
@cache.write('foo', nil)
assert @cache.exist?('foo')
end
def test_delete
@cache.write('foo', 'bar')
assert @cache.exist?('foo')
assert @cache.delete('foo')
assert !@cache.exist?('foo')
end
def test_original_store_objects_should_not_be_immutable
bar = 'bar'
@cache.write('foo', bar)
assert_nothing_raised { bar.gsub!(/.*/, 'baz') }
end
def test_expires_in
time = Time.local(2008, 4, 24)
Time.stubs(:now).returns(time)
@cache.write('foo', 'bar')
assert_equal 'bar', @cache.read('foo')
Time.stubs(:now).returns(time + 30)
assert_equal 'bar', @cache.read('foo')
Time.stubs(:now).returns(time + 61)
assert_nil @cache.read('foo')
end
def test_race_condition_protection
time = Time.now
@cache.write('foo', 'bar', :expires_in => 60)
Time.stubs(:now).returns(time + 61)
result = @cache.fetch('foo', :race_condition_ttl => 10) do
assert_equal 'bar', @cache.read('foo')
"baz"
end
assert_equal "baz", result
end
def test_race_condition_protection_is_limited
time = Time.now
@cache.write('foo', 'bar', :expires_in => 60)
Time.stubs(:now).returns(time + 71)
result = @cache.fetch('foo', :race_condition_ttl => 10) do
assert_equal nil, @cache.read('foo')
"baz"
end
assert_equal "baz", result
end
def test_race_condition_protection_is_safe
time = Time.now
@cache.write('foo', 'bar', :expires_in => 60)
Time.stubs(:now).returns(time + 61)
begin
@cache.fetch('foo', :race_condition_ttl => 10) do
assert_equal 'bar', @cache.read('foo')
raise ArgumentError.new
end
rescue ArgumentError
end
assert_equal "bar", @cache.read('foo')
Time.stubs(:now).returns(time + 91)
assert_nil @cache.read('foo')
end
def test_crazy_key_characters
crazy_key = "#/:*(<+=> )&$%@?;'\"\'`~-"
assert @cache.write(crazy_key, "1", :raw => true)
assert_equal "1", @cache.read(crazy_key)
assert_equal "1", @cache.fetch(crazy_key)
assert @cache.delete(crazy_key)
assert_equal "2", @cache.fetch(crazy_key, :raw => true) { "2" }
assert_equal 3, @cache.increment(crazy_key)
assert_equal 2, @cache.decrement(crazy_key)
end
def test_really_long_keys
key = ""
900.times{key << "x"}
assert @cache.write(key, "bar")
assert_equal "bar", @cache.read(key)
assert_equal "bar", @cache.fetch(key)
assert_nil @cache.read("#{key}x")
assert_equal({key => "bar"}, @cache.read_multi(key))
assert @cache.delete(key)
end
end
# https://rails.lighthouseapp.com/projects/8994/tickets/6225-memcachestore-cant-deal-with-umlauts-and-special-characters
# The error is caused by character encodings that can't be compared with ASCII-8BIT regular expressions and by special
# characters like the umlaut in UTF-8.
module EncodedKeyCacheBehavior
Encoding.list.each do |encoding|
define_method "test_#{encoding.name.underscore}_encoded_values" do
key = "foo".force_encoding(encoding)
assert @cache.write(key, "1", :raw => true)
assert_equal "1", @cache.read(key)
assert_equal "1", @cache.fetch(key)
assert @cache.delete(key)
assert_equal "2", @cache.fetch(key, :raw => true) { "2" }
assert_equal 3, @cache.increment(key)
assert_equal 2, @cache.decrement(key)
end
end
def test_common_utf8_values
key = "\xC3\xBCmlaut".force_encoding(Encoding::UTF_8)
assert @cache.write(key, "1", :raw => true)
assert_equal "1", @cache.read(key)
assert_equal "1", @cache.fetch(key)
assert @cache.delete(key)
assert_equal "2", @cache.fetch(key, :raw => true) { "2" }
assert_equal 3, @cache.increment(key)
assert_equal 2, @cache.decrement(key)
end
def test_retains_encoding
key = "\xC3\xBCmlaut".force_encoding(Encoding::UTF_8)
assert @cache.write(key, "1", :raw => true)
assert_equal Encoding::UTF_8, key.encoding
end
end
module CacheDeleteMatchedBehavior
def test_delete_matched
@cache.write("foo", "bar")
@cache.write("fu", "baz")
@cache.write("foo/bar", "baz")
@cache.write("fu/baz", "bar")
@cache.delete_matched(/oo/)
assert !@cache.exist?("foo")
assert @cache.exist?("fu")
assert !@cache.exist?("foo/bar")
assert @cache.exist?("fu/baz")
end
end
module CacheIncrementDecrementBehavior
def test_increment
@cache.write('foo', 1, :raw => true)
assert_equal 1, @cache.read('foo').to_i
assert_equal 2, @cache.increment('foo')
assert_equal 2, @cache.read('foo').to_i
assert_equal 3, @cache.increment('foo')
assert_equal 3, @cache.read('foo').to_i
assert_nil @cache.increment('bar')
end
def test_decrement
@cache.write('foo', 3, :raw => true)
assert_equal 3, @cache.read('foo').to_i
assert_equal 2, @cache.decrement('foo')
assert_equal 2, @cache.read('foo').to_i
assert_equal 1, @cache.decrement('foo')
assert_equal 1, @cache.read('foo').to_i
assert_nil @cache.decrement('bar')
end
end
module LocalCacheBehavior
def test_local_writes_are_persistent_on_the_remote_cache
retval = @cache.with_local_cache do
@cache.write('foo', 'bar')
end
assert retval
assert_equal 'bar', @cache.read('foo')
end
def test_clear_also_clears_local_cache
@cache.with_local_cache do
@cache.write('foo', 'bar')
@cache.clear
assert_nil @cache.read('foo')
end
assert_nil @cache.read('foo')
end
def test_local_cache_of_write
@cache.with_local_cache do
@cache.write('foo', 'bar')
@peek.delete('foo')
assert_equal 'bar', @cache.read('foo')
end
end
def test_local_cache_of_read
@cache.write('foo', 'bar')
@cache.with_local_cache do
assert_equal 'bar', @cache.read('foo')
end
end
def test_local_cache_of_write_nil
@cache.with_local_cache do
assert @cache.write('foo', nil)
assert_nil @cache.read('foo')
@peek.write('foo', 'bar')
assert_nil @cache.read('foo')
end
end
def test_local_cache_of_delete
@cache.with_local_cache do
@cache.write('foo', 'bar')
@cache.delete('foo')
assert_nil @cache.read('foo')
end
end
def test_local_cache_of_exist
@cache.with_local_cache do
@cache.write('foo', 'bar')
@peek.delete('foo')
assert @cache.exist?('foo')
end
end
def test_local_cache_of_increment
@cache.with_local_cache do
@cache.write('foo', 1, :raw => true)
@peek.write('foo', 2, :raw => true)
@cache.increment('foo')
assert_equal 3, @cache.read('foo')
end
end
def test_local_cache_of_decrement
@cache.with_local_cache do
@cache.write('foo', 1, :raw => true)
@peek.write('foo', 3, :raw => true)
@cache.decrement('foo')
assert_equal 2, @cache.read('foo')
end
end
def test_middleware
app = lambda { |env|
result = @cache.write('foo', 'bar')
assert_equal 'bar', @cache.read('foo') # make sure 'foo' was written
assert result
}
app = @cache.middleware.new(app)
app.call({})
end
end
module AutoloadingCacheBehavior
include DependenciesTestHelpers
def test_simple_autoloading
with_autoloading_fixtures do
@cache.write('foo', E.new)
end
remove_constants(:E)
ActiveSupport::Dependencies.clear
with_autoloading_fixtures do
assert_kind_of E, @cache.read('foo')
end
remove_constants(:E)
ActiveSupport::Dependencies.clear
end
def test_two_classes_autoloading
with_autoloading_fixtures do
@cache.write('foo', [E.new, ClassFolder.new])
end
remove_constants(:E, :ClassFolder)
ActiveSupport::Dependencies.clear
with_autoloading_fixtures do
loaded = @cache.read('foo')
assert_kind_of Array, loaded
assert_equal 2, loaded.size
assert_kind_of E, loaded[0]
assert_kind_of ClassFolder, loaded[1]
end
remove_constants(:E, :ClassFolder)
ActiveSupport::Dependencies.clear
end
end
class FileStoreTest < ActiveSupport::TestCase
def setup
Dir.mkdir(cache_dir) unless File.exist?(cache_dir)
@cache = ActiveSupport::Cache.lookup_store(:file_store, cache_dir, :expires_in => 60)
@peek = ActiveSupport::Cache.lookup_store(:file_store, cache_dir, :expires_in => 60)
@cache_with_pathname = ActiveSupport::Cache.lookup_store(:file_store, Pathname.new(cache_dir), :expires_in => 60)
@buffer = StringIO.new
@cache.logger = ActiveSupport::Logger.new(@buffer)
end
def teardown
FileUtils.rm_r(cache_dir)
end
def cache_dir
File.join(Dir.pwd, 'tmp_cache')
end
include CacheStoreBehavior
include LocalCacheBehavior
include CacheDeleteMatchedBehavior
include CacheIncrementDecrementBehavior
include AutoloadingCacheBehavior
def test_clear
filepath = File.join(cache_dir, ".gitkeep")
FileUtils.touch(filepath)
@cache.clear
assert File.exist?(filepath)
end
def test_key_transformation
key = @cache.send(:key_file_path, "views/index?id=1")
assert_equal "views/index?id=1", @cache.send(:file_path_key, key)
end
def test_key_transformation_with_pathname
FileUtils.touch(File.join(cache_dir, "foo"))
key = @cache_with_pathname.send(:key_file_path, "views/index?id=1")
assert_equal "views/index?id=1", @cache_with_pathname.send(:file_path_key, key)
end
# Test that generated cache keys are short enough to have Tempfile stuff added to them and
# remain valid
def test_filename_max_size
key = "#{'A' * ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE}"
path = @cache.send(:key_file_path, key)
Dir::Tmpname.create(path) do |tmpname, n, opts|
assert File.basename(tmpname+'.lock').length <= 255, "Temp filename too long: #{File.basename(tmpname+'.lock').length}"
end
end
# Because file systems have a maximum filename size, filenames > max size should be split in to directories
# If filename is 'AAAAB', where max size is 4, the returned path should be AAAA/B
def test_key_transformation_max_filename_size
key = "#{'A' * ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE}B"
path = @cache.send(:key_file_path, key)
assert path.split('/').all? { |dir_name| dir_name.size <= ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE}
assert_equal 'B', File.basename(path)
end
# If nothing has been stored in the cache, there is a chance the cache directory does not yet exist
# Ensure delete_matched gracefully handles this case
def test_delete_matched_when_cache_directory_does_not_exist
assert_nothing_raised(Exception) do
ActiveSupport::Cache::FileStore.new('/test/cache/directory').delete_matched(/does_not_exist/)
end
end
def test_delete_does_not_delete_empty_parent_dir
sub_cache_dir = File.join(cache_dir, 'subdir/')
sub_cache_store = ActiveSupport::Cache::FileStore.new(sub_cache_dir)
assert_nothing_raised(Exception) do
assert sub_cache_store.write('foo', 'bar')
assert sub_cache_store.delete('foo')
end
assert File.exist?(cache_dir), "Parent of top level cache dir was deleted!"
assert File.exist?(sub_cache_dir), "Top level cache dir was deleted!"
assert Dir.entries(sub_cache_dir).reject {|f| ActiveSupport::Cache::FileStore::EXCLUDED_DIRS.include?(f)}.empty?
end
def test_log_exception_when_cache_read_fails
File.expects(:exist?).raises(StandardError, "failed")
@cache.send(:read_entry, "winston", {})
assert @buffer.string.present?
end
end
class MemoryStoreTest < ActiveSupport::TestCase
def setup
@record_size = ActiveSupport::Cache.lookup_store(:memory_store).send(:cached_size, 1, ActiveSupport::Cache::Entry.new("aaaaaaaaaa"))
@cache = ActiveSupport::Cache.lookup_store(:memory_store, :expires_in => 60, :size => @record_size * 10 + 1)
end
include CacheStoreBehavior
include CacheDeleteMatchedBehavior
include CacheIncrementDecrementBehavior
def test_prune_size
@cache.write(1, "aaaaaaaaaa") && sleep(0.001)
@cache.write(2, "bbbbbbbbbb") && sleep(0.001)
@cache.write(3, "cccccccccc") && sleep(0.001)
@cache.write(4, "dddddddddd") && sleep(0.001)
@cache.write(5, "eeeeeeeeee") && sleep(0.001)
@cache.read(2) && sleep(0.001)
@cache.read(4)
@cache.prune(@record_size * 3)
assert @cache.exist?(5)
assert @cache.exist?(4)
assert !@cache.exist?(3), "no entry"
assert @cache.exist?(2)
assert !@cache.exist?(1), "no entry"
end
def test_prune_size_on_write
@cache.write(1, "aaaaaaaaaa") && sleep(0.001)
@cache.write(2, "bbbbbbbbbb") && sleep(0.001)
@cache.write(3, "cccccccccc") && sleep(0.001)
@cache.write(4, "dddddddddd") && sleep(0.001)
@cache.write(5, "eeeeeeeeee") && sleep(0.001)
@cache.write(6, "ffffffffff") && sleep(0.001)
@cache.write(7, "gggggggggg") && sleep(0.001)
@cache.write(8, "hhhhhhhhhh") && sleep(0.001)
@cache.write(9, "iiiiiiiiii") && sleep(0.001)
@cache.write(10, "kkkkkkkkkk") && sleep(0.001)
@cache.read(2) && sleep(0.001)
@cache.read(4) && sleep(0.001)
@cache.write(11, "llllllllll")
assert @cache.exist?(11)
assert @cache.exist?(10)
assert @cache.exist?(9)
assert @cache.exist?(8)
assert @cache.exist?(7)
assert !@cache.exist?(6), "no entry"
assert !@cache.exist?(5), "no entry"
assert @cache.exist?(4)
assert !@cache.exist?(3), "no entry"
assert @cache.exist?(2)
assert !@cache.exist?(1), "no entry"
end
def test_prune_size_on_write_based_on_key_length
@cache.write(1, "aaaaaaaaaa") && sleep(0.001)
@cache.write(2, "bbbbbbbbbb") && sleep(0.001)
@cache.write(3, "cccccccccc") && sleep(0.001)
@cache.write(4, "dddddddddd") && sleep(0.001)
@cache.write(5, "eeeeeeeeee") && sleep(0.001)
@cache.write(6, "ffffffffff") && sleep(0.001)
@cache.write(7, "gggggggggg") && sleep(0.001)
@cache.write(8, "hhhhhhhhhh") && sleep(0.001)
@cache.write(9, "iiiiiiiiii") && sleep(0.001)
long_key = '*' * 2 * @record_size
@cache.write(long_key, "llllllllll")
assert @cache.exist?(long_key)
assert @cache.exist?(9)
assert @cache.exist?(8)
assert @cache.exist?(7)
assert @cache.exist?(6)
assert !@cache.exist?(5), "no entry"
assert !@cache.exist?(4), "no entry"
assert !@cache.exist?(3), "no entry"
assert !@cache.exist?(2), "no entry"
assert !@cache.exist?(1), "no entry"
end
def test_pruning_is_capped_at_a_max_time
def @cache.delete_entry (*args)
sleep(0.01)
super
end
@cache.write(1, "aaaaaaaaaa") && sleep(0.001)
@cache.write(2, "bbbbbbbbbb") && sleep(0.001)
@cache.write(3, "cccccccccc") && sleep(0.001)
@cache.write(4, "dddddddddd") && sleep(0.001)
@cache.write(5, "eeeeeeeeee") && sleep(0.001)
@cache.prune(30, 0.001)
assert @cache.exist?(5)
assert @cache.exist?(4)
assert @cache.exist?(3)
assert @cache.exist?(2)
assert !@cache.exist?(1)
end
def test_write_with_unless_exist
assert_equal true, @cache.write(1, "aaaaaaaaaa")
assert_equal false, @cache.write(1, "aaaaaaaaaa", :unless_exist => true)
@cache.write(1, nil)
assert_equal false, @cache.write(1, "aaaaaaaaaa", :unless_exist => true)
end
end
class MemCacheStoreTest < ActiveSupport::TestCase
require 'dalli'
begin
ss = Dalli::Client.new('localhost:11211').stats
raise Dalli::DalliError unless ss['localhost:11211']
MEMCACHE_UP = true
rescue Dalli::DalliError
$stderr.puts "Skipping memcached tests. Start memcached and try again."
MEMCACHE_UP = false
end
def setup
skip "memcache server is not up" unless MEMCACHE_UP
@cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :expires_in => 60)
@peek = ActiveSupport::Cache.lookup_store(:mem_cache_store)
@data = @cache.instance_variable_get(:@data)
@cache.clear
@cache.silence!
@cache.logger = ActiveSupport::Logger.new("/dev/null")
end
include CacheStoreBehavior
include LocalCacheBehavior
include CacheIncrementDecrementBehavior
include EncodedKeyCacheBehavior
include AutoloadingCacheBehavior
def test_raw_values
cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
cache.clear
cache.write("foo", 2)
assert_equal "2", cache.read("foo")
end
def test_raw_values_with_marshal
cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
cache.clear
cache.write("foo", Marshal.dump([]))
assert_equal [], cache.read("foo")
end
def test_local_cache_raw_values
cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
cache.clear
cache.with_local_cache do
cache.write("foo", 2)
assert_equal "2", cache.read("foo")
end
end
def test_local_cache_raw_values_with_marshal
cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
cache.clear
cache.with_local_cache do
cache.write("foo", Marshal.dump([]))
assert_equal [], cache.read("foo")
end
end
def test_read_should_return_a_different_object_id_each_time_it_is_called
@cache.write('foo', 'bar')
assert_not_equal @cache.read('foo').object_id, @cache.read('foo').object_id
value = @cache.read('foo')
value << 'bingo'
assert_not_equal value, @cache.read('foo')
end
end
class NullStoreTest < ActiveSupport::TestCase
def setup
@cache = ActiveSupport::Cache.lookup_store(:null_store)
end
def test_clear
@cache.clear
end
def test_cleanup
@cache.cleanup
end
def test_write
assert_equal true, @cache.write("name", "value")
end
def test_read
@cache.write("name", "value")
assert_nil @cache.read("name")
end
def test_delete
@cache.write("name", "value")
assert_equal false, @cache.delete("name")
end
def test_increment
@cache.write("name", 1, :raw => true)
assert_nil @cache.increment("name")
end
def test_decrement
@cache.write("name", 1, :raw => true)
assert_nil @cache.increment("name")
end
def test_delete_matched
@cache.write("name", "value")
@cache.delete_matched(/name/)
end
def test_local_store_strategy
@cache.with_local_cache do
@cache.write("name", "value")
assert_equal "value", @cache.read("name")
@cache.delete("name")
assert_nil @cache.read("name")
@cache.write("name", "value")
end
assert_nil @cache.read("name")
end
def test_setting_nil_cache_store
assert ActiveSupport::Cache.lookup_store.class.name, ActiveSupport::Cache::NullStore.name
end
end
class CacheStoreLoggerTest < ActiveSupport::TestCase
def setup
@cache = ActiveSupport::Cache.lookup_store(:memory_store)
@buffer = StringIO.new
@cache.logger = ActiveSupport::Logger.new(@buffer)
end
def test_logging
@cache.fetch('foo') { 'bar' }
assert @buffer.string.present?
end
def test_mute_logging
@cache.mute { @cache.fetch('foo') { 'bar' } }
assert @buffer.string.blank?
end
end
class CacheEntryTest < ActiveSupport::TestCase
def test_expired
entry = ActiveSupport::Cache::Entry.new("value")
assert !entry.expired?, 'entry not expired'
entry = ActiveSupport::Cache::Entry.new("value", :expires_in => 60)
assert !entry.expired?, 'entry not expired'
time = Time.now + 61
Time.stubs(:now).returns(time)
assert entry.expired?, 'entry is expired'
end
def test_compress_values
value = "value" * 100
entry = ActiveSupport::Cache::Entry.new(value, :compress => true, :compress_threshold => 1)
assert_equal value, entry.value
assert(value.bytesize > entry.size, "value is compressed")
end
def test_non_compress_values
value = "value" * 100
entry = ActiveSupport::Cache::Entry.new(value)
assert_equal value, entry.value
assert_equal value.bytesize, entry.size
end
def test_restoring_version_4beta1_entries
version_4beta1_entry = ActiveSupport::Cache::Entry.allocate
version_4beta1_entry.instance_variable_set(:@v, "hello")
version_4beta1_entry.instance_variable_set(:@x, Time.now.to_i + 60)
entry = Marshal.load(Marshal.dump(version_4beta1_entry))
assert_equal "hello", entry.value
assert_equal false, entry.expired?
end
def test_restoring_compressed_version_4beta1_entries
version_4beta1_entry = ActiveSupport::Cache::Entry.allocate
version_4beta1_entry.instance_variable_set(:@v, Zlib::Deflate.deflate(Marshal.dump("hello")))
version_4beta1_entry.instance_variable_set(:@c, true)
entry = Marshal.load(Marshal.dump(version_4beta1_entry))
assert_equal "hello", entry.value
end
def test_restoring_expired_version_4beta1_entries
version_4beta1_entry = ActiveSupport::Cache::Entry.allocate
version_4beta1_entry.instance_variable_set(:@v, "hello")
version_4beta1_entry.instance_variable_set(:@x, Time.now.to_i - 1)
entry = Marshal.load(Marshal.dump(version_4beta1_entry))
assert_equal "hello", entry.value
assert_equal true, entry.expired?
end
end