caching_test.rb 32.1 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

402 403 404 405 406 407 408 409
  def test_race_condition_protection_skipped_if_not_defined
    @cache.write('foo', 'bar')
    time = @cache.send(:read_entry, 'foo', {}).expires_at
    Time.stubs(:now).returns(Time.at(time))

    result = @cache.fetch('foo') do
      assert_equal nil, @cache.read('foo')
      'baz'
410
    end
411
    assert_equal 'baz', result
B
Brian Durand 已提交
412
  end
413

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

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

441 442 443 444 445 446 447 448 449 450 451
  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

B
Brian Durand 已提交
452 453
  def test_crazy_key_characters
    crazy_key = "#/:*(<+=> )&$%@?;'\"\'`~-"
454
    assert @cache.write(crazy_key, "1", :raw => true)
B
Brian Durand 已提交
455 456
    assert_equal "1", @cache.read(crazy_key)
    assert_equal "1", @cache.fetch(crazy_key)
457
    assert @cache.delete(crazy_key)
B
Brian Durand 已提交
458 459 460 461 462 463 464
    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 = ""
465
    900.times{key << "x"}
466
    assert @cache.write(key, "bar")
B
Brian Durand 已提交
467 468 469 470
    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))
471
    assert @cache.delete(key)
B
Brian Durand 已提交
472 473
  end
end
474

475
# https://rails.lighthouseapp.com/projects/8994/tickets/6225-memcachestore-cant-deal-with-umlauts-and-special-characters
A
Anupam Choudhury 已提交
476
# The error is caused by character encodings that can't be compared with ASCII-8BIT regular expressions and by special
477 478
# characters like the umlaut in UTF-8.
module EncodedKeyCacheBehavior
479 480 481
  Encoding.list.each do |encoding|
    define_method "test_#{encoding.name.underscore}_encoded_values" do
      key = "foo".force_encoding(encoding)
482
      assert @cache.write(key, "1", :raw => true)
483 484
      assert_equal "1", @cache.read(key)
      assert_equal "1", @cache.fetch(key)
485
      assert @cache.delete(key)
486 487 488 489
      assert_equal "2", @cache.fetch(key, :raw => true) { "2" }
      assert_equal 3, @cache.increment(key)
      assert_equal 2, @cache.decrement(key)
    end
490
  end
491

492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
  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
507 508 509
  end
end

B
Brian Durand 已提交
510 511 512 513
module CacheDeleteMatchedBehavior
  def test_delete_matched
    @cache.write("foo", "bar")
    @cache.write("fu", "baz")
514 515
    @cache.write("foo/bar", "baz")
    @cache.write("fu/baz", "bar")
B
Brian Durand 已提交
516
    @cache.delete_matched(/oo/)
517 518 519 520
    assert !@cache.exist?("foo")
    assert @cache.exist?("fu")
    assert !@cache.exist?("foo/bar")
    assert @cache.exist?("fu/baz")
B
Brian Durand 已提交
521 522 523 524 525 526 527 528 529 530 531
  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
532
    assert_nil @cache.increment('bar')
B
Brian Durand 已提交
533 534 535 536 537 538 539 540 541
  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
542
    assert_nil @cache.decrement('bar')
B
Brian Durand 已提交
543 544 545 546 547 548 549
  end
end

module LocalCacheBehavior
  def test_local_writes_are_persistent_on_the_remote_cache
    retval = @cache.with_local_cache do
      @cache.write('foo', 'bar')
550
    end
551
    assert retval
B
Brian Durand 已提交
552 553
    assert_equal 'bar', @cache.read('foo')
  end
554

B
Brian Durand 已提交
555 556 557 558 559
  def test_clear_also_clears_local_cache
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @cache.clear
      assert_nil @cache.read('foo')
560 561
    end

B
Brian Durand 已提交
562 563
    assert_nil @cache.read('foo')
  end
564

B
Brian Durand 已提交
565 566 567 568
  def test_local_cache_of_write
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @peek.delete('foo')
569 570
      assert_equal 'bar', @cache.read('foo')
    end
B
Brian Durand 已提交
571
  end
572

B
Brian Durand 已提交
573 574 575 576
  def test_local_cache_of_read
    @cache.write('foo', 'bar')
    @cache.with_local_cache do
      assert_equal 'bar', @cache.read('foo')
577
    end
B
Brian Durand 已提交
578
  end
579

B
Brian Durand 已提交
580 581
  def test_local_cache_of_write_nil
    @cache.with_local_cache do
582
      assert @cache.write('foo', nil)
B
Brian Durand 已提交
583 584 585
      assert_nil @cache.read('foo')
      @peek.write('foo', 'bar')
      assert_nil @cache.read('foo')
586
    end
B
Brian Durand 已提交
587
  end
588

B
Brian Durand 已提交
589 590 591 592 593
  def test_local_cache_of_delete
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @cache.delete('foo')
      assert_nil @cache.read('foo')
594
    end
B
Brian Durand 已提交
595
  end
596

B
Brian Durand 已提交
597 598 599 600
  def test_local_cache_of_exist
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @peek.delete('foo')
601
      assert @cache.exist?('foo')
602
    end
B
Brian Durand 已提交
603
  end
604

B
Brian Durand 已提交
605 606 607 608 609 610
  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')
611
    end
B
Brian Durand 已提交
612
  end
613

B
Brian Durand 已提交
614 615 616 617 618 619
  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')
620
    end
B
Brian Durand 已提交
621
  end
622

B
Brian Durand 已提交
623 624 625 626 627
  def test_middleware
    app = lambda { |env|
      result = @cache.write('foo', 'bar')
      assert_equal 'bar', @cache.read('foo') # make sure 'foo' was written
      assert result
628
      [200, {}, []]
B
Brian Durand 已提交
629 630 631 632 633
    }
    app = @cache.middleware.new(app)
    app.call({})
  end
end
634

635
module AutoloadingCacheBehavior
636
  include DependenciesTestHelpers
637 638
  def test_simple_autoloading
    with_autoloading_fixtures do
639
      @cache.write('foo', EM.new)
640 641
    end

642
    remove_constants(:EM)
643 644 645
    ActiveSupport::Dependencies.clear

    with_autoloading_fixtures do
646
      assert_kind_of EM, @cache.read('foo')
647 648
    end

649
    remove_constants(:EM)
650 651 652 653 654
    ActiveSupport::Dependencies.clear
  end

  def test_two_classes_autoloading
    with_autoloading_fixtures do
655
      @cache.write('foo', [EM.new, ClassFolder.new])
656 657
    end

658
    remove_constants(:EM, :ClassFolder)
659 660 661 662 663 664
    ActiveSupport::Dependencies.clear

    with_autoloading_fixtures do
      loaded = @cache.read('foo')
      assert_kind_of Array, loaded
      assert_equal 2, loaded.size
665
      assert_kind_of EM, loaded[0]
666 667 668
      assert_kind_of ClassFolder, loaded[1]
    end

669
    remove_constants(:EM, :ClassFolder)
670 671 672 673
    ActiveSupport::Dependencies.clear
  end
end

B
Brian Durand 已提交
674 675 676 677 678
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)
679
    @cache_with_pathname = ActiveSupport::Cache.lookup_store(:file_store, Pathname.new(cache_dir), :expires_in => 60)
680 681 682

    @buffer = StringIO.new
    @cache.logger = ActiveSupport::Logger.new(@buffer)
B
Brian Durand 已提交
683 684 685 686
  end

  def teardown
    FileUtils.rm_r(cache_dir)
687
  rescue Errno::ENOENT
B
Brian Durand 已提交
688 689 690 691 692 693 694 695 696 697
  end

  def cache_dir
    File.join(Dir.pwd, 'tmp_cache')
  end

  include CacheStoreBehavior
  include LocalCacheBehavior
  include CacheDeleteMatchedBehavior
  include CacheIncrementDecrementBehavior
698
  include AutoloadingCacheBehavior
B
Brian Durand 已提交
699

700 701 702 703 704 705 706
  def test_clear
    filepath = File.join(cache_dir, ".gitkeep")
    FileUtils.touch(filepath)
    @cache.clear
    assert File.exist?(filepath)
  end

707 708 709 710 711
  def test_clear_without_cache_dir
    FileUtils.rm_r(cache_dir)
    @cache.clear
  end

712 713 714 715 716
  def test_long_keys
    @cache.write("a"*10000, 1)
    assert_equal 1, @cache.read("a"*10000)
  end

717 718 719 720
  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
721 722 723 724 725 726

  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
727

728
  # Test that generated cache keys are short enough to have Tempfile stuff added to them and
729 730 731 732 733
  # 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|
734
      assert File.basename(tmpname+'.lock').length <= 255, "Temp filename too long: #{File.basename(tmpname+'.lock').length}"
735 736 737
    end
  end

738 739 740 741
  # 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"
742
    path = @cache.send(:key_file_path, key)
743 744 745
    assert path.split('/').all? { |dir_name| dir_name.size <= ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE}
    assert_equal 'B', File.basename(path)
  end
746

747 748 749 750 751
  # 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/)
752 753
    end
  end
754

755 756 757 758 759 760 761 762 763 764 765 766
  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

767 768 769
  def test_log_exception_when_cache_read_fails
    File.expects(:exist?).raises(StandardError, "failed")
    @cache.send(:read_entry, "winston", {})
770
    assert @buffer.string.present?
771
  end
772 773 774 775 776 777 778 779 780 781 782 783

  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
784

G
grosser 已提交
785 786 787 788 789 790
  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 已提交
791
end
792

B
Brian Durand 已提交
793 794
class MemoryStoreTest < ActiveSupport::TestCase
  def setup
795 796
    @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 已提交
797 798 799 800 801 802 803 804 805 806 807 808 809 810
  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)
811
    @cache.prune(@record_size * 3)
812 813
    assert @cache.exist?(5)
    assert @cache.exist?(4)
S
Steve Klabnik 已提交
814
    assert !@cache.exist?(3), "no entry"
815
    assert @cache.exist?(2)
S
Steve Klabnik 已提交
816
    assert !@cache.exist?(1), "no entry"
B
Brian Durand 已提交
817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
  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")
833 834 835 836 837
    assert @cache.exist?(11)
    assert @cache.exist?(10)
    assert @cache.exist?(9)
    assert @cache.exist?(8)
    assert @cache.exist?(7)
838 839
    assert !@cache.exist?(6), "no entry"
    assert !@cache.exist?(5), "no entry"
840
    assert @cache.exist?(4)
841
    assert !@cache.exist?(3), "no entry"
842
    assert @cache.exist?(2)
843
    assert !@cache.exist?(1), "no entry"
B
Brian Durand 已提交
844 845
  end

846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869
  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 已提交
870 871 872 873
  def test_pruning_is_capped_at_a_max_time
    def @cache.delete_entry (*args)
      sleep(0.01)
      super
874
    end
B
Brian Durand 已提交
875 876 877 878 879 880
    @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)
881 882 883 884 885
    assert @cache.exist?(5)
    assert @cache.exist?(4)
    assert @cache.exist?(3)
    assert @cache.exist?(2)
    assert !@cache.exist?(1)
B
Brian Durand 已提交
886
  end
887 888 889 890 891 892 893

  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 已提交
894
end
895

896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923
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
924
  include AutoloadingCacheBehavior
925 926 927 928 929 930 931

  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
932

933 934 935 936 937 938
  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 已提交
939

940 941 942 943
  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 已提交
944 945
      cache.write("foo", 2)
      assert_equal "2", cache.read("foo")
946
    end
947
  end
948

949 950 951 952
  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
953
      cache.write("foo", Marshal.dump([]))
954
      assert_equal [], cache.read("foo")
955
    end
956
  end
957 958 959 960

  def test_read_should_return_a_different_object_id_each_time_it_is_called
    @cache.write('foo', 'bar')
    value = @cache.read('foo')
M
Matthew Draper 已提交
961
    assert_not_equal value.object_id, @cache.read('foo').object_id
962 963 964
    value << 'bingo'
    assert_not_equal value, @cache.read('foo')
  end
965
end
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 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
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

1025 1026 1027 1028 1029
class CacheStoreLoggerTest < ActiveSupport::TestCase
  def setup
    @cache = ActiveSupport::Cache.lookup_store(:memory_store)

    @buffer = StringIO.new
1030
    @cache.logger = ActiveSupport::Logger.new(@buffer)
1031 1032 1033 1034
  end

  def test_logging
    @cache.fetch('foo') { 'bar' }
1035
    assert @buffer.string.present?
1036 1037 1038 1039
  end

  def test_mute_logging
    @cache.mute { @cache.fetch('foo') { 'bar' } }
1040
    assert @buffer.string.blank?
1041
  end
K
Kasper Timm Hansen 已提交
1042 1043 1044 1045 1046 1047 1048

  def test_multi_read_loggin
    @cache.write 'hello', 'goodbye'
    @cache.write 'world', 'earth'

    @cache.read_multi('hello', 'world')

A
Arthur Neves 已提交
1049
    assert_match "Caches multi read:\n- hello\n- world", @buffer.string
K
Kasper Timm Hansen 已提交
1050
  end
1051
end
B
Brian Durand 已提交
1052 1053 1054 1055

class CacheEntryTest < ActiveSupport::TestCase
  def test_expired
    entry = ActiveSupport::Cache::Entry.new("value")
1056
    assert !entry.expired?, 'entry not expired'
B
Brian Durand 已提交
1057
    entry = ActiveSupport::Cache::Entry.new("value", :expires_in => 60)
1058
    assert !entry.expired?, 'entry not expired'
B
Brian Durand 已提交
1059 1060
    time = Time.now + 61
    Time.stubs(:now).returns(time)
1061
    assert entry.expired?, 'entry is expired'
B
Brian Durand 已提交
1062 1063 1064
  end

  def test_compress_values
1065 1066 1067
    value = "value" * 100
    entry = ActiveSupport::Cache::Entry.new(value, :compress => true, :compress_threshold => 1)
    assert_equal value, entry.value
1068
    assert(value.bytesize > entry.size, "value is compressed")
B
Brian Durand 已提交
1069 1070 1071
  end

  def test_non_compress_values
1072 1073 1074 1075 1076
    value = "value" * 100
    entry = ActiveSupport::Cache::Entry.new(value)
    assert_equal value, entry.value
    assert_equal value.bytesize, entry.size
  end
B
Brian Durand 已提交
1077
end