caching_test.rb 32.6 KB
Newer Older
1
require 'logger'
2
require 'abstract_unit'
J
Jeremy Kemper 已提交
3
require 'active_support/cache'
4
require 'dependencies_test_helpers'
5

6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
module ActiveSupport
  module Cache
    module Strategy
      module LocalCache
        class MiddlewareTest < ActiveSupport::TestCase
          def test_local_cache_cleared_on_close
            key = "super awesome key"
            assert_nil LocalCacheRegistry.cache_for key
            middleware = Middleware.new('<3', key).new(->(env) {
              assert LocalCacheRegistry.cache_for(key), 'should have a cache'
              [200, {}, []]
            })
            _, _, body = middleware.call({})
            assert LocalCacheRegistry.cache_for(key), 'should still have a cache'
            body.each { }
            assert LocalCacheRegistry.cache_for(key), 'should still have a cache'
            body.close
            assert_nil LocalCacheRegistry.cache_for(key)
          end

          def test_local_cache_cleared_on_exception
            key = "super awesome key"
            assert_nil LocalCacheRegistry.cache_for key
            middleware = Middleware.new('<3', key).new(->(env) {
              assert LocalCacheRegistry.cache_for(key), 'should have a cache'
              raise
            })
            assert_raises(RuntimeError) { middleware.call({}) }
            assert_nil LocalCacheRegistry.cache_for(key)
          end
        end
      end
    end
  end
end

42
class CacheKeyTest < ActiveSupport::TestCase
43 44 45 46 47 48 49 50 51 52 53 54 55 56
  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

57
  def test_expand_cache_key
58 59
    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)
60
  end
61 62

  def test_expand_cache_key_with_rails_cache_id
Z
Zuhao Wan 已提交
63
    with_env('RAILS_CACHE_ID' => 'c99') do
64
      assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key(:foo)
65 66
      assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key([:foo])
      assert_equal 'c99/foo/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar])
67
      assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key(:foo, :nm)
68 69
      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)
70
    end
71
  end
72 73

  def test_expand_cache_key_with_rails_app_version
Z
Zuhao Wan 已提交
74
    with_env('RAILS_APP_VERSION' => 'rails3') do
75 76
      assert_equal 'rails3/foo', ActiveSupport::Cache.expand_cache_key(:foo)
    end
77 78 79
  end

  def test_expand_cache_key_rails_cache_id_should_win_over_rails_app_version
Z
Zuhao Wan 已提交
80
    with_env('RAILS_CACHE_ID' => 'c99', 'RAILS_APP_VERSION' => 'rails3') do
81 82 83 84
      assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key(:foo)
    end
  end

85
  def test_expand_cache_key_respond_to_cache_key
86 87 88 89 90
    key = 'foo'
    def key.cache_key
      :foo_key
    end
    assert_equal 'foo_key', ActiveSupport::Cache.expand_cache_key(key)
91 92
  end

93
  def test_expand_cache_key_array_with_something_that_responds_to_cache_key
94 95 96 97
    key = 'foo'
    def key.cache_key
      :foo_key
    end
98
    assert_equal 'foo_key', ActiveSupport::Cache.expand_cache_key([key])
99 100
  end

101 102 103 104 105 106 107 108 109 110 111
  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
112

113 114 115
  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
Z
Zuhao Wan 已提交
116 117 118 119 120 121 122 123 124 125

  private

  def with_env(kv)
    old_values = {}
    kv.each { |key, value| old_values[key], ENV[key] = ENV[key], value }
    yield
  ensure
    old_values.each { |key, value| ENV[key] = value}
  end
126 127
end

128
class CacheStoreSettingTest < ActiveSupport::TestCase
129 130 131 132 133
  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
134

135
  def test_mem_cache_fragment_cache_store
136
    Dalli::Client.expects(:new).with(%w[localhost], {})
137 138
    store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost"
    assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
139 140 141
  end

  def test_mem_cache_fragment_cache_store_with_given_mem_cache
142 143
    mem_cache = Dalli::Client.new
    Dalli::Client.expects(:new).never
144 145
    store = ActiveSupport::Cache.lookup_store :mem_cache_store, mem_cache
    assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
146
  end
147

148 149 150 151 152 153 154 155
  def test_mem_cache_fragment_cache_store_with_not_dalli_client
    Dalli::Client.expects(:new).never
    memcache = Object.new
    assert_raises(ArgumentError) do
      ActiveSupport::Cache.lookup_store :mem_cache_store, memcache
    end
  end

156
  def test_mem_cache_fragment_cache_store_with_multiple_servers
157
    Dalli::Client.expects(:new).with(%w[localhost 192.168.1.1], {})
158 159 160
    store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1'
    assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
  end
161

162
  def test_mem_cache_fragment_cache_store_with_options
163
    Dalli::Client.expects(:new).with(%w[localhost 192.168.1.1], { :timeout => 10 })
B
Brian Durand 已提交
164
    store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1', :namespace => 'foo', :timeout => 10
165
    assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
B
Brian Durand 已提交
166
    assert_equal 'foo', store.options[:namespace]
167
  end
168 169 170 171 172 173 174

  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
175

B
Brian Durand 已提交
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
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/)
198 199
    assert !cache.exist?("foo")
    assert cache.exist?("fu")
B
Brian Durand 已提交
200 201 202 203 204 205 206
  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)
207 208
    assert !cache.exist?("foo")
    assert cache.exist?("fu")
B
Brian Durand 已提交
209 210 211 212 213 214
  end
end

# Tests the base functionality that should be identical across all cache stores.
module CacheStoreBehavior
  def test_should_read_and_write_strings
215
    assert @cache.write('foo', 'bar')
B
Brian Durand 已提交
216 217 218 219 220 221 222
    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')
223
  end
224

225
  def test_fetch_without_cache_miss
B
Brian Durand 已提交
226
    @cache.write('foo', 'bar')
227 228 229
    @cache.expects(:write).never
    assert_equal 'bar', @cache.fetch('foo') { 'baz' }
  end
230

231
  def test_fetch_with_cache_miss
B
Brian Durand 已提交
232
    @cache.expects(:write).with('foo', 'baz', @cache.options)
233 234
    assert_equal 'baz', @cache.fetch('foo') { 'baz' }
  end
235

236
  def test_fetch_with_cache_miss_passes_key_to_block
237 238 239 240 241 242 243
    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
244 245
  end

246
  def test_fetch_with_forced_cache_miss
B
Brian Durand 已提交
247
    @cache.write('foo', 'bar')
248
    @cache.expects(:read).never
B
Brian Durand 已提交
249
    @cache.expects(:write).with('foo', 'bar', @cache.options.merge(:force => true))
250
    @cache.fetch('foo', :force => true) { 'bar' }
251
  end
J
Joshua Peek 已提交
252

B
Brian Durand 已提交
253 254 255 256
  def test_fetch_with_cached_nil
    @cache.write('foo', nil)
    @cache.expects(:write).never
    assert_nil @cache.fetch('foo') { 'baz' }
J
Joshua Peek 已提交
257 258 259
  end

  def test_should_read_and_write_hash
260
    assert @cache.write('foo', {:a => "b"})
J
Joshua Peek 已提交
261 262 263
    assert_equal({:a => "b"}, @cache.read('foo'))
  end

264
  def test_should_read_and_write_integer
265
    assert @cache.write('foo', 1)
266 267 268
    assert_equal 1, @cache.read('foo')
  end

J
Joshua Peek 已提交
269
  def test_should_read_and_write_nil
270
    assert @cache.write('foo', nil)
J
Joshua Peek 已提交
271
    assert_equal nil, @cache.read('foo')
272 273
  end

274
  def test_should_read_and_write_false
275
    assert @cache.write('foo', false)
276
    assert_equal false, @cache.read('foo')
277 278
  end

B
Brian Durand 已提交
279
  def test_read_multi
280
    @cache.write('foo', 'bar')
B
Brian Durand 已提交
281 282 283
    @cache.write('fu', 'baz')
    @cache.write('fud', 'biz')
    assert_equal({"foo" => "bar", "fu" => "baz"}, @cache.read_multi('foo', 'fu'))
284
  end
285

286
  def test_read_multi_with_expires
287 288
    time = Time.now
    @cache.write('foo', 'bar', :expires_in => 10)
289 290
    @cache.write('fu', 'baz')
    @cache.write('fud', 'biz')
291
    Time.stubs(:now).returns(time + 11)
292 293
    assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
  end
294

295 296 297 298
  def test_fetch_multi
    @cache.write('foo', 'bar')
    @cache.write('fud', 'biz')

299
    values = @cache.fetch_multi('foo', 'fu', 'fud') { |value| value * 2 }
300

301 302
    assert_equal({ 'foo' => 'bar', 'fu' => 'fufu', 'fud' => 'biz' }, values)
    assert_equal('fufu', @cache.read('fu'))
303 304 305
  end

  def test_multi_with_objects
306 307
    foo = stub(:title => 'FOO!', :cache_key => 'foo')
    bar = stub(:cache_key => 'bar')
308

309
    @cache.write('bar', 'BAM!')
310

311 312 313
    values = @cache.fetch_multi(foo, bar) { |object| object.title }

    assert_equal({ foo => 'FOO!', bar => 'BAM!' }, values)
314 315
  end

B
Brian Durand 已提交
316 317 318
  def test_read_and_write_compressed_small_data
    @cache.write('foo', 'bar', :compress => true)
    assert_equal 'bar', @cache.read('foo')
319 320
  end

B
Brian Durand 已提交
321 322 323
  def test_read_and_write_compressed_large_data
    @cache.write('foo', 'bar', :compress => true, :compress_threshold => 2)
    assert_equal 'bar', @cache.read('foo')
324
  end
325

B
Brian Durand 已提交
326 327 328
  def test_read_and_write_compressed_nil
    @cache.write('foo', nil, :compress => true)
    assert_nil @cache.read('foo')
329 330
  end

B
Brian Durand 已提交
331 332 333 334 335 336 337
  def test_cache_key
    obj = Object.new
    def obj.cache_key
      :foo
    end
    @cache.write(obj, "bar")
    assert_equal "bar", @cache.read("foo")
338
  end
339

B
Brian Durand 已提交
340 341 342 343 344 345 346
  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")
347
  end
348

B
Brian Durand 已提交
349 350 351
  def test_array_as_cache_key
    @cache.write([:fu, "foo"], "bar")
    assert_equal "bar", @cache.read("fu/foo")
352 353
  end

B
Brian Durand 已提交
354 355 356
  def test_hash_as_cache_key
    @cache.write({:foo => 1, :fu => 2}, "bar")
    assert_equal "bar", @cache.read("foo=1/fu=2")
357 358
  end

B
Brian Durand 已提交
359 360 361 362
  def test_keys_are_case_sensitive
    @cache.write("foo", "bar")
    assert_nil @cache.read("FOO")
  end
363

B
Brian Durand 已提交
364
  def test_exist
365
    @cache.write('foo', 'bar')
366 367
    assert_equal true, @cache.exist?('foo')
    assert_equal false, @cache.exist?('bar')
368
  end
369

B
Brian Durand 已提交
370 371
  def test_nil_exist
    @cache.write('foo', nil)
372
    assert @cache.exist?('foo')
373 374
  end

B
Brian Durand 已提交
375 376 377
  def test_delete
    @cache.write('foo', 'bar')
    assert @cache.exist?('foo')
378
    assert @cache.delete('foo')
B
Brian Durand 已提交
379 380
    assert !@cache.exist?('foo')
  end
381

382 383 384 385 386
  def test_original_store_objects_should_not_be_immutable
    bar = 'bar'
    @cache.write('foo', bar)
    assert_nothing_raised { bar.gsub!(/.*/, 'baz') }
  end
387

B
Brian Durand 已提交
388 389 390 391 392 393 394 395 396 397 398 399
  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')
400
  end
401

B
Brian Durand 已提交
402 403 404 405 406 407 408
  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"
409
    end
B
Brian Durand 已提交
410 411
    assert_equal "baz", result
  end
412

B
Brian Durand 已提交
413 414 415 416 417 418 419 420 421 422
  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
423

B
Brian Durand 已提交
424 425 426 427 428 429
  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
430
        assert_equal 'bar', @cache.read('foo')
B
Brian Durand 已提交
431
        raise ArgumentError.new
432
      end
S
Santiago Pastorino 已提交
433
    rescue ArgumentError
434
    end
B
Brian Durand 已提交
435
    assert_equal "bar", @cache.read('foo')
436
    Time.stubs(:now).returns(time + 91)
B
Brian Durand 已提交
437 438 439 440 441
    assert_nil @cache.read('foo')
  end

  def test_crazy_key_characters
    crazy_key = "#/:*(<+=> )&$%@?;'\"\'`~-"
442
    assert @cache.write(crazy_key, "1", :raw => true)
B
Brian Durand 已提交
443 444
    assert_equal "1", @cache.read(crazy_key)
    assert_equal "1", @cache.fetch(crazy_key)
445
    assert @cache.delete(crazy_key)
B
Brian Durand 已提交
446 447 448 449 450 451 452
    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 = ""
453
    900.times{key << "x"}
454
    assert @cache.write(key, "bar")
B
Brian Durand 已提交
455 456 457 458
    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))
459
    assert @cache.delete(key)
B
Brian Durand 已提交
460 461
  end
end
462

463
# https://rails.lighthouseapp.com/projects/8994/tickets/6225-memcachestore-cant-deal-with-umlauts-and-special-characters
A
Anupam Choudhury 已提交
464
# The error is caused by character encodings that can't be compared with ASCII-8BIT regular expressions and by special
465 466
# characters like the umlaut in UTF-8.
module EncodedKeyCacheBehavior
467 468 469
  Encoding.list.each do |encoding|
    define_method "test_#{encoding.name.underscore}_encoded_values" do
      key = "foo".force_encoding(encoding)
470
      assert @cache.write(key, "1", :raw => true)
471 472
      assert_equal "1", @cache.read(key)
      assert_equal "1", @cache.fetch(key)
473
      assert @cache.delete(key)
474 475 476 477
      assert_equal "2", @cache.fetch(key, :raw => true) { "2" }
      assert_equal 3, @cache.increment(key)
      assert_equal 2, @cache.decrement(key)
    end
478
  end
479

480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
  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
495 496 497
  end
end

B
Brian Durand 已提交
498 499 500 501
module CacheDeleteMatchedBehavior
  def test_delete_matched
    @cache.write("foo", "bar")
    @cache.write("fu", "baz")
502 503
    @cache.write("foo/bar", "baz")
    @cache.write("fu/baz", "bar")
B
Brian Durand 已提交
504
    @cache.delete_matched(/oo/)
505 506 507 508
    assert !@cache.exist?("foo")
    assert @cache.exist?("fu")
    assert !@cache.exist?("foo/bar")
    assert @cache.exist?("fu/baz")
B
Brian Durand 已提交
509 510 511 512 513 514 515 516 517 518 519
  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
520
    assert_nil @cache.increment('bar')
B
Brian Durand 已提交
521 522 523 524 525 526 527 528 529
  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
530
    assert_nil @cache.decrement('bar')
B
Brian Durand 已提交
531 532 533 534 535 536 537
  end
end

module LocalCacheBehavior
  def test_local_writes_are_persistent_on_the_remote_cache
    retval = @cache.with_local_cache do
      @cache.write('foo', 'bar')
538
    end
539
    assert retval
B
Brian Durand 已提交
540 541
    assert_equal 'bar', @cache.read('foo')
  end
542

B
Brian Durand 已提交
543 544 545 546 547
  def test_clear_also_clears_local_cache
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @cache.clear
      assert_nil @cache.read('foo')
548 549
    end

B
Brian Durand 已提交
550 551
    assert_nil @cache.read('foo')
  end
552

B
Brian Durand 已提交
553 554 555 556
  def test_local_cache_of_write
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @peek.delete('foo')
557 558
      assert_equal 'bar', @cache.read('foo')
    end
B
Brian Durand 已提交
559
  end
560

B
Brian Durand 已提交
561 562 563 564
  def test_local_cache_of_read
    @cache.write('foo', 'bar')
    @cache.with_local_cache do
      assert_equal 'bar', @cache.read('foo')
565
    end
B
Brian Durand 已提交
566
  end
567

B
Brian Durand 已提交
568 569
  def test_local_cache_of_write_nil
    @cache.with_local_cache do
570
      assert @cache.write('foo', nil)
B
Brian Durand 已提交
571 572 573
      assert_nil @cache.read('foo')
      @peek.write('foo', 'bar')
      assert_nil @cache.read('foo')
574
    end
B
Brian Durand 已提交
575
  end
576

B
Brian Durand 已提交
577 578 579 580 581
  def test_local_cache_of_delete
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @cache.delete('foo')
      assert_nil @cache.read('foo')
582
    end
B
Brian Durand 已提交
583
  end
584

B
Brian Durand 已提交
585 586 587 588
  def test_local_cache_of_exist
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @peek.delete('foo')
589
      assert @cache.exist?('foo')
590
    end
B
Brian Durand 已提交
591
  end
592

B
Brian Durand 已提交
593 594 595 596 597 598
  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')
599
    end
B
Brian Durand 已提交
600
  end
601

B
Brian Durand 已提交
602 603 604 605 606 607
  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')
608
    end
B
Brian Durand 已提交
609
  end
610

B
Brian Durand 已提交
611 612 613 614 615
  def test_middleware
    app = lambda { |env|
      result = @cache.write('foo', 'bar')
      assert_equal 'bar', @cache.read('foo') # make sure 'foo' was written
      assert result
616
      [200, {}, []]
B
Brian Durand 已提交
617 618 619 620 621
    }
    app = @cache.middleware.new(app)
    app.call({})
  end
end
622

623
module AutoloadingCacheBehavior
624
  include DependenciesTestHelpers
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
  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

B
Brian Durand 已提交
662 663 664 665 666
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)
667
    @cache_with_pathname = ActiveSupport::Cache.lookup_store(:file_store, Pathname.new(cache_dir), :expires_in => 60)
668 669 670

    @buffer = StringIO.new
    @cache.logger = ActiveSupport::Logger.new(@buffer)
B
Brian Durand 已提交
671 672 673 674 675 676 677 678 679 680 681 682 683 684
  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
685
  include AutoloadingCacheBehavior
B
Brian Durand 已提交
686

687 688 689 690 691 692 693
  def test_clear
    filepath = File.join(cache_dir, ".gitkeep")
    FileUtils.touch(filepath)
    @cache.clear
    assert File.exist?(filepath)
  end

694 695 696 697 698
  def test_long_keys
    @cache.write("a"*10000, 1)
    assert_equal 1, @cache.read("a"*10000)
  end

699 700 701 702
  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
703 704 705 706 707 708

  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
709

710
  # Test that generated cache keys are short enough to have Tempfile stuff added to them and
711 712 713 714 715
  # 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|
716
      assert File.basename(tmpname+'.lock').length <= 255, "Temp filename too long: #{File.basename(tmpname+'.lock').length}"
717 718 719
    end
  end

720 721 722 723
  # 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"
724
    path = @cache.send(:key_file_path, key)
725 726 727
    assert path.split('/').all? { |dir_name| dir_name.size <= ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE}
    assert_equal 'B', File.basename(path)
  end
728

729 730 731 732 733
  # 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/)
734 735
    end
  end
736

737 738 739 740 741 742 743 744 745 746 747 748
  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

749 750 751
  def test_log_exception_when_cache_read_fails
    File.expects(:exist?).raises(StandardError, "failed")
    @cache.send(:read_entry, "winston", {})
752
    assert @buffer.string.present?
753
  end
754 755 756 757 758 759 760 761 762 763 764 765

  def test_cleanup_removes_all_expired_entries
    time = Time.now
    @cache.write('foo', 'bar', expires_in: 10)
    @cache.write('baz', 'qux')
    @cache.write('quux', 'corge', expires_in: 20)
    Time.stubs(:now).returns(time + 15)
    @cache.cleanup
    assert_not @cache.exist?('foo')
    assert @cache.exist?('baz')
    assert @cache.exist?('quux')
  end
766

G
grosser 已提交
767 768 769 770 771 772
  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
B
Brian Durand 已提交
773
end
774

B
Brian Durand 已提交
775 776
class MemoryStoreTest < ActiveSupport::TestCase
  def setup
777 778
    @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)
B
Brian Durand 已提交
779 780 781 782 783 784 785 786 787 788 789 790 791 792
  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)
793
    @cache.prune(@record_size * 3)
794 795
    assert @cache.exist?(5)
    assert @cache.exist?(4)
S
Steve Klabnik 已提交
796
    assert !@cache.exist?(3), "no entry"
797
    assert @cache.exist?(2)
S
Steve Klabnik 已提交
798
    assert !@cache.exist?(1), "no entry"
B
Brian Durand 已提交
799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814
  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")
815 816 817 818 819
    assert @cache.exist?(11)
    assert @cache.exist?(10)
    assert @cache.exist?(9)
    assert @cache.exist?(8)
    assert @cache.exist?(7)
820 821
    assert !@cache.exist?(6), "no entry"
    assert !@cache.exist?(5), "no entry"
822
    assert @cache.exist?(4)
823
    assert !@cache.exist?(3), "no entry"
824
    assert @cache.exist?(2)
825
    assert !@cache.exist?(1), "no entry"
B
Brian Durand 已提交
826 827
  end

828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
  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

B
Brian Durand 已提交
852 853 854 855
  def test_pruning_is_capped_at_a_max_time
    def @cache.delete_entry (*args)
      sleep(0.01)
      super
856
    end
B
Brian Durand 已提交
857 858 859 860 861 862
    @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)
863 864 865 866 867
    assert @cache.exist?(5)
    assert @cache.exist?(4)
    assert @cache.exist?(3)
    assert @cache.exist?(2)
    assert !@cache.exist?(1)
B
Brian Durand 已提交
868
  end
869 870 871 872 873 874 875

  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
B
Brian Durand 已提交
876
end
877

878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905
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
906
  include AutoloadingCacheBehavior
907 908 909 910 911 912 913

  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
914

915 916 917 918 919 920
  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
B
Brian Durand 已提交
921

922 923 924 925
  def test_local_cache_raw_values
    cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
    cache.clear
    cache.with_local_cache do
B
Brian Durand 已提交
926 927
      cache.write("foo", 2)
      assert_equal "2", cache.read("foo")
928
    end
929
  end
930

931 932 933 934
  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
935
      cache.write("foo", Marshal.dump([]))
936
      assert_equal [], cache.read("foo")
937
    end
938
  end
939 940 941 942 943 944 945 946

  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
947
end
948

949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006
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

1007 1008 1009 1010 1011
class CacheStoreLoggerTest < ActiveSupport::TestCase
  def setup
    @cache = ActiveSupport::Cache.lookup_store(:memory_store)

    @buffer = StringIO.new
1012
    @cache.logger = ActiveSupport::Logger.new(@buffer)
1013 1014 1015 1016
  end

  def test_logging
    @cache.fetch('foo') { 'bar' }
1017
    assert @buffer.string.present?
1018 1019 1020 1021
  end

  def test_mute_logging
    @cache.mute { @cache.fetch('foo') { 'bar' } }
1022
    assert @buffer.string.blank?
1023 1024
  end
end
B
Brian Durand 已提交
1025 1026 1027 1028

class CacheEntryTest < ActiveSupport::TestCase
  def test_expired
    entry = ActiveSupport::Cache::Entry.new("value")
1029
    assert !entry.expired?, 'entry not expired'
B
Brian Durand 已提交
1030
    entry = ActiveSupport::Cache::Entry.new("value", :expires_in => 60)
1031
    assert !entry.expired?, 'entry not expired'
B
Brian Durand 已提交
1032 1033
    time = Time.now + 61
    Time.stubs(:now).returns(time)
1034
    assert entry.expired?, 'entry is expired'
B
Brian Durand 已提交
1035 1036 1037
  end

  def test_compress_values
1038 1039 1040
    value = "value" * 100
    entry = ActiveSupport::Cache::Entry.new(value, :compress => true, :compress_threshold => 1)
    assert_equal value, entry.value
1041
    assert(value.bytesize > entry.size, "value is compressed")
B
Brian Durand 已提交
1042 1043 1044
  end

  def test_non_compress_values
1045 1046 1047 1048 1049 1050
    value = "value" * 100
    entry = ActiveSupport::Cache::Entry.new(value)
    assert_equal value, entry.value
    assert_equal value.bytesize, entry.size
  end

1051 1052 1053 1054 1055
  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))
1056 1057 1058 1059
    assert_equal "hello", entry.value
    assert_equal false, entry.expired?
  end

1060 1061 1062 1063 1064
  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))
1065 1066 1067
    assert_equal "hello", entry.value
  end

1068 1069 1070 1071 1072
  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))
1073 1074
    assert_equal "hello", entry.value
    assert_equal true, entry.expired?
B
Brian Durand 已提交
1075 1076
  end
end