caching_test.rb 30.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
class CacheKeyTest < ActiveSupport::TestCase
7 8 9 10 11 12 13 14 15 16 17 18 19 20
  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

21
  def test_expand_cache_key
22 23
    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)
24
  end
25 26

  def test_expand_cache_key_with_rails_cache_id
27 28 29
    begin
      ENV['RAILS_CACHE_ID'] = 'c99'
      assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key(:foo)
30 31
      assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key([:foo])
      assert_equal 'c99/foo/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar])
32
      assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key(:foo, :nm)
33 34
      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)
35 36 37
    ensure
      ENV['RAILS_CACHE_ID'] = nil
    end
38
  end
39 40

  def test_expand_cache_key_with_rails_app_version
41 42 43 44 45 46
    begin
      ENV['RAILS_APP_VERSION'] = 'rails3'
      assert_equal 'rails3/foo', ActiveSupport::Cache.expand_cache_key(:foo)
    ensure
      ENV['RAILS_APP_VERSION'] = nil
    end
47 48 49
  end

  def test_expand_cache_key_rails_cache_id_should_win_over_rails_app_version
50 51 52 53 54 55 56 57 58 59
    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

60
  def test_expand_cache_key_respond_to_cache_key
61 62 63 64 65
    key = 'foo'
    def key.cache_key
      :foo_key
    end
    assert_equal 'foo_key', ActiveSupport::Cache.expand_cache_key(key)
66 67
  end

68
  def test_expand_cache_key_array_with_something_that_responds_to_cache_key
69 70 71 72
    key = 'foo'
    def key.cache_key
      :foo_key
    end
73
    assert_equal 'foo_key', ActiveSupport::Cache.expand_cache_key([key])
74 75
  end

76 77 78 79 80 81 82 83 84 85 86
  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
87

88 89 90
  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
91 92
end

93
class CacheStoreSettingTest < ActiveSupport::TestCase
94 95 96 97 98
  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
99

100
  def test_mem_cache_fragment_cache_store
101
    Dalli::Client.expects(:new).with(%w[localhost], {})
102 103
    store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost"
    assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
104 105 106
  end

  def test_mem_cache_fragment_cache_store_with_given_mem_cache
107 108
    mem_cache = Dalli::Client.new
    Dalli::Client.expects(:new).never
109 110
    store = ActiveSupport::Cache.lookup_store :mem_cache_store, mem_cache
    assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
111
  end
112

113
  def test_mem_cache_fragment_cache_store_with_given_mem_cache_like_object
114
    Dalli::Client.expects(:new).never
115 116 117
    memcache = Object.new
    def memcache.get() true end
    store = ActiveSupport::Cache.lookup_store :mem_cache_store, memcache
118 119 120
    assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
  end

121
  def test_mem_cache_fragment_cache_store_with_multiple_servers
122
    Dalli::Client.expects(:new).with(%w[localhost 192.168.1.1], {})
123 124 125
    store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1'
    assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
  end
126

127
  def test_mem_cache_fragment_cache_store_with_options
128
    Dalli::Client.expects(:new).with(%w[localhost 192.168.1.1], { :timeout => 10 })
B
Brian Durand 已提交
129
    store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1', :namespace => 'foo', :timeout => 10
130
    assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
B
Brian Durand 已提交
131
    assert_equal 'foo', store.options[:namespace]
132
  end
133 134 135 136 137 138 139

  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
140

B
Brian Durand 已提交
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
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/)
163 164
    assert !cache.exist?("foo")
    assert cache.exist?("fu")
B
Brian Durand 已提交
165 166 167 168 169 170 171
  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)
172 173
    assert !cache.exist?("foo")
    assert cache.exist?("fu")
B
Brian Durand 已提交
174 175 176 177 178 179
  end
end

# Tests the base functionality that should be identical across all cache stores.
module CacheStoreBehavior
  def test_should_read_and_write_strings
180
    assert @cache.write('foo', 'bar')
B
Brian Durand 已提交
181 182 183 184 185 186 187
    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')
188
  end
189

190
  def test_fetch_without_cache_miss
B
Brian Durand 已提交
191
    @cache.write('foo', 'bar')
192 193 194
    @cache.expects(:write).never
    assert_equal 'bar', @cache.fetch('foo') { 'baz' }
  end
195

196
  def test_fetch_with_cache_miss
B
Brian Durand 已提交
197
    @cache.expects(:write).with('foo', 'baz', @cache.options)
198 199
    assert_equal 'baz', @cache.fetch('foo') { 'baz' }
  end
200

201
  def test_fetch_with_cache_miss_passes_key_to_block
202 203 204 205 206 207 208
    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
209 210
  end

211
  def test_fetch_with_forced_cache_miss
B
Brian Durand 已提交
212
    @cache.write('foo', 'bar')
213
    @cache.expects(:read).never
B
Brian Durand 已提交
214
    @cache.expects(:write).with('foo', 'bar', @cache.options.merge(:force => true))
215
    @cache.fetch('foo', :force => true) { 'bar' }
216
  end
J
Joshua Peek 已提交
217

B
Brian Durand 已提交
218 219 220 221
  def test_fetch_with_cached_nil
    @cache.write('foo', nil)
    @cache.expects(:write).never
    assert_nil @cache.fetch('foo') { 'baz' }
J
Joshua Peek 已提交
222 223 224
  end

  def test_should_read_and_write_hash
225
    assert @cache.write('foo', {:a => "b"})
J
Joshua Peek 已提交
226 227 228
    assert_equal({:a => "b"}, @cache.read('foo'))
  end

229
  def test_should_read_and_write_integer
230
    assert @cache.write('foo', 1)
231 232 233
    assert_equal 1, @cache.read('foo')
  end

J
Joshua Peek 已提交
234
  def test_should_read_and_write_nil
235
    assert @cache.write('foo', nil)
J
Joshua Peek 已提交
236
    assert_equal nil, @cache.read('foo')
237 238
  end

239
  def test_should_read_and_write_false
240
    assert @cache.write('foo', false)
241
    assert_equal false, @cache.read('foo')
242 243
  end

B
Brian Durand 已提交
244
  def test_read_multi
245
    @cache.write('foo', 'bar')
B
Brian Durand 已提交
246 247 248
    @cache.write('fu', 'baz')
    @cache.write('fud', 'biz')
    assert_equal({"foo" => "bar", "fu" => "baz"}, @cache.read_multi('foo', 'fu'))
249
  end
250

251
  def test_read_multi_with_expires
252 253
    time = Time.now
    @cache.write('foo', 'bar', :expires_in => 10)
254 255
    @cache.write('fu', 'baz')
    @cache.write('fud', 'biz')
256
    Time.stubs(:now).returns(time + 11)
257 258
    assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
  end
259

260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
  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

B
Brian Durand 已提交
280 281 282
  def test_read_and_write_compressed_small_data
    @cache.write('foo', 'bar', :compress => true)
    assert_equal 'bar', @cache.read('foo')
283 284
  end

B
Brian Durand 已提交
285 286 287
  def test_read_and_write_compressed_large_data
    @cache.write('foo', 'bar', :compress => true, :compress_threshold => 2)
    assert_equal 'bar', @cache.read('foo')
288
  end
289

B
Brian Durand 已提交
290 291 292
  def test_read_and_write_compressed_nil
    @cache.write('foo', nil, :compress => true)
    assert_nil @cache.read('foo')
293 294
  end

B
Brian Durand 已提交
295 296 297 298 299 300 301
  def test_cache_key
    obj = Object.new
    def obj.cache_key
      :foo
    end
    @cache.write(obj, "bar")
    assert_equal "bar", @cache.read("foo")
302
  end
303

B
Brian Durand 已提交
304 305 306 307 308 309 310
  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")
311
  end
312

B
Brian Durand 已提交
313 314 315
  def test_array_as_cache_key
    @cache.write([:fu, "foo"], "bar")
    assert_equal "bar", @cache.read("fu/foo")
316 317
  end

B
Brian Durand 已提交
318 319 320
  def test_hash_as_cache_key
    @cache.write({:foo => 1, :fu => 2}, "bar")
    assert_equal "bar", @cache.read("foo=1/fu=2")
321 322
  end

B
Brian Durand 已提交
323 324 325 326
  def test_keys_are_case_sensitive
    @cache.write("foo", "bar")
    assert_nil @cache.read("FOO")
  end
327

B
Brian Durand 已提交
328
  def test_exist
329
    @cache.write('foo', 'bar')
330 331
    assert_equal true, @cache.exist?('foo')
    assert_equal false, @cache.exist?('bar')
332
  end
333

B
Brian Durand 已提交
334 335
  def test_nil_exist
    @cache.write('foo', nil)
336
    assert @cache.exist?('foo')
337 338
  end

B
Brian Durand 已提交
339 340 341
  def test_delete
    @cache.write('foo', 'bar')
    assert @cache.exist?('foo')
342
    assert @cache.delete('foo')
B
Brian Durand 已提交
343 344
    assert !@cache.exist?('foo')
  end
345

346 347 348 349 350
  def test_original_store_objects_should_not_be_immutable
    bar = 'bar'
    @cache.write('foo', bar)
    assert_nothing_raised { bar.gsub!(/.*/, 'baz') }
  end
351

B
Brian Durand 已提交
352 353 354 355 356 357 358 359 360 361 362 363
  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')
364
  end
365

B
Brian Durand 已提交
366 367 368 369 370 371 372
  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"
373
    end
B
Brian Durand 已提交
374 375
    assert_equal "baz", result
  end
376

B
Brian Durand 已提交
377 378 379 380 381 382 383 384 385 386
  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
387

B
Brian Durand 已提交
388 389 390 391 392 393
  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
394
        assert_equal 'bar', @cache.read('foo')
B
Brian Durand 已提交
395
        raise ArgumentError.new
396
      end
S
Santiago Pastorino 已提交
397
    rescue ArgumentError
398
    end
B
Brian Durand 已提交
399
    assert_equal "bar", @cache.read('foo')
400
    Time.stubs(:now).returns(time + 91)
B
Brian Durand 已提交
401 402 403 404 405
    assert_nil @cache.read('foo')
  end

  def test_crazy_key_characters
    crazy_key = "#/:*(<+=> )&$%@?;'\"\'`~-"
406
    assert @cache.write(crazy_key, "1", :raw => true)
B
Brian Durand 已提交
407 408
    assert_equal "1", @cache.read(crazy_key)
    assert_equal "1", @cache.fetch(crazy_key)
409
    assert @cache.delete(crazy_key)
B
Brian Durand 已提交
410 411 412 413 414 415 416
    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 = ""
417
    900.times{key << "x"}
418
    assert @cache.write(key, "bar")
B
Brian Durand 已提交
419 420 421 422
    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))
423
    assert @cache.delete(key)
B
Brian Durand 已提交
424 425
  end
end
426

427
# https://rails.lighthouseapp.com/projects/8994/tickets/6225-memcachestore-cant-deal-with-umlauts-and-special-characters
A
Anupam Choudhury 已提交
428
# The error is caused by character encodings that can't be compared with ASCII-8BIT regular expressions and by special
429 430
# characters like the umlaut in UTF-8.
module EncodedKeyCacheBehavior
431 432 433
  Encoding.list.each do |encoding|
    define_method "test_#{encoding.name.underscore}_encoded_values" do
      key = "foo".force_encoding(encoding)
434
      assert @cache.write(key, "1", :raw => true)
435 436
      assert_equal "1", @cache.read(key)
      assert_equal "1", @cache.fetch(key)
437
      assert @cache.delete(key)
438 439 440 441
      assert_equal "2", @cache.fetch(key, :raw => true) { "2" }
      assert_equal 3, @cache.increment(key)
      assert_equal 2, @cache.decrement(key)
    end
442
  end
443

444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
  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
459 460 461
  end
end

B
Brian Durand 已提交
462 463 464 465
module CacheDeleteMatchedBehavior
  def test_delete_matched
    @cache.write("foo", "bar")
    @cache.write("fu", "baz")
466 467
    @cache.write("foo/bar", "baz")
    @cache.write("fu/baz", "bar")
B
Brian Durand 已提交
468
    @cache.delete_matched(/oo/)
469 470 471 472
    assert !@cache.exist?("foo")
    assert @cache.exist?("fu")
    assert !@cache.exist?("foo/bar")
    assert @cache.exist?("fu/baz")
B
Brian Durand 已提交
473 474 475 476 477 478 479 480 481 482 483
  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
484
    assert_nil @cache.increment('bar')
B
Brian Durand 已提交
485 486 487 488 489 490 491 492 493
  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
494
    assert_nil @cache.decrement('bar')
B
Brian Durand 已提交
495 496 497 498 499 500 501
  end
end

module LocalCacheBehavior
  def test_local_writes_are_persistent_on_the_remote_cache
    retval = @cache.with_local_cache do
      @cache.write('foo', 'bar')
502
    end
503
    assert retval
B
Brian Durand 已提交
504 505
    assert_equal 'bar', @cache.read('foo')
  end
506

B
Brian Durand 已提交
507 508 509 510 511
  def test_clear_also_clears_local_cache
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @cache.clear
      assert_nil @cache.read('foo')
512 513
    end

B
Brian Durand 已提交
514 515
    assert_nil @cache.read('foo')
  end
516

B
Brian Durand 已提交
517 518 519 520
  def test_local_cache_of_write
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @peek.delete('foo')
521 522
      assert_equal 'bar', @cache.read('foo')
    end
B
Brian Durand 已提交
523
  end
524

B
Brian Durand 已提交
525 526 527 528
  def test_local_cache_of_read
    @cache.write('foo', 'bar')
    @cache.with_local_cache do
      assert_equal 'bar', @cache.read('foo')
529
    end
B
Brian Durand 已提交
530
  end
531

B
Brian Durand 已提交
532 533
  def test_local_cache_of_write_nil
    @cache.with_local_cache do
534
      assert @cache.write('foo', nil)
B
Brian Durand 已提交
535 536 537
      assert_nil @cache.read('foo')
      @peek.write('foo', 'bar')
      assert_nil @cache.read('foo')
538
    end
B
Brian Durand 已提交
539
  end
540

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

B
Brian Durand 已提交
549 550 551 552
  def test_local_cache_of_exist
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @peek.delete('foo')
553
      assert @cache.exist?('foo')
554
    end
B
Brian Durand 已提交
555
  end
556

B
Brian Durand 已提交
557 558 559 560 561 562
  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')
563
    end
B
Brian Durand 已提交
564
  end
565

B
Brian Durand 已提交
566 567 568 569 570 571
  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')
572
    end
B
Brian Durand 已提交
573
  end
574

B
Brian Durand 已提交
575 576 577 578 579 580 581 582 583 584
  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
585

586
module AutoloadingCacheBehavior
587
  include DependenciesTestHelpers
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
  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 已提交
625 626 627 628 629
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)
630
    @cache_with_pathname = ActiveSupport::Cache.lookup_store(:file_store, Pathname.new(cache_dir), :expires_in => 60)
631 632 633

    @buffer = StringIO.new
    @cache.logger = ActiveSupport::Logger.new(@buffer)
B
Brian Durand 已提交
634 635 636 637 638 639 640 641 642 643 644 645 646 647
  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
648
  include AutoloadingCacheBehavior
B
Brian Durand 已提交
649

650 651 652 653 654 655 656
  def test_clear
    filepath = File.join(cache_dir, ".gitkeep")
    FileUtils.touch(filepath)
    @cache.clear
    assert File.exist?(filepath)
  end

657 658 659 660
  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
661 662 663 664 665 666

  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
667

668
  # Test that generated cache keys are short enough to have Tempfile stuff added to them and
669 670 671 672 673
  # 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|
674
      assert File.basename(tmpname+'.lock').length <= 255, "Temp filename too long: #{File.basename(tmpname+'.lock').length}"
675 676 677
    end
  end

678 679 680 681
  # 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"
682
    path = @cache.send(:key_file_path, key)
683 684 685
    assert path.split('/').all? { |dir_name| dir_name.size <= ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE}
    assert_equal 'B', File.basename(path)
  end
686

687 688 689 690 691
  # 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/)
692 693
    end
  end
694

695 696 697 698 699 700 701 702 703 704 705 706
  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

707 708 709
  def test_log_exception_when_cache_read_fails
    File.expects(:exist?).raises(StandardError, "failed")
    @cache.send(:read_entry, "winston", {})
710
    assert @buffer.string.present?
711
  end
B
Brian Durand 已提交
712
end
713

B
Brian Durand 已提交
714 715
class MemoryStoreTest < ActiveSupport::TestCase
  def setup
716 717
    @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 已提交
718 719 720 721 722 723 724 725 726 727 728 729 730 731
  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)
732
    @cache.prune(@record_size * 3)
733 734
    assert @cache.exist?(5)
    assert @cache.exist?(4)
S
Steve Klabnik 已提交
735
    assert !@cache.exist?(3), "no entry"
736
    assert @cache.exist?(2)
S
Steve Klabnik 已提交
737
    assert !@cache.exist?(1), "no entry"
B
Brian Durand 已提交
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
  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")
754 755 756 757 758
    assert @cache.exist?(11)
    assert @cache.exist?(10)
    assert @cache.exist?(9)
    assert @cache.exist?(8)
    assert @cache.exist?(7)
759 760
    assert !@cache.exist?(6), "no entry"
    assert !@cache.exist?(5), "no entry"
761
    assert @cache.exist?(4)
762
    assert !@cache.exist?(3), "no entry"
763
    assert @cache.exist?(2)
764
    assert !@cache.exist?(1), "no entry"
B
Brian Durand 已提交
765 766
  end

767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
  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 已提交
791 792 793 794
  def test_pruning_is_capped_at_a_max_time
    def @cache.delete_entry (*args)
      sleep(0.01)
      super
795
    end
B
Brian Durand 已提交
796 797 798 799 800 801
    @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)
802 803 804 805 806
    assert @cache.exist?(5)
    assert @cache.exist?(4)
    assert @cache.exist?(3)
    assert @cache.exist?(2)
    assert !@cache.exist?(1)
B
Brian Durand 已提交
807
  end
808 809 810 811 812 813 814

  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 已提交
815
end
816

817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
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
845
  include AutoloadingCacheBehavior
846 847 848 849 850 851 852

  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
853

854 855 856 857 858 859
  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 已提交
860

861 862 863 864
  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 已提交
865 866
      cache.write("foo", 2)
      assert_equal "2", cache.read("foo")
867
    end
868
  end
869

870 871 872 873
  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
874
      cache.write("foo", Marshal.dump([]))
875
      assert_equal [], cache.read("foo")
876
    end
877
  end
878 879 880 881 882 883 884 885

  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
886
end
887

888 889 890 891 892 893 894 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 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945
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

946 947 948 949 950
class CacheStoreLoggerTest < ActiveSupport::TestCase
  def setup
    @cache = ActiveSupport::Cache.lookup_store(:memory_store)

    @buffer = StringIO.new
951
    @cache.logger = ActiveSupport::Logger.new(@buffer)
952 953 954 955
  end

  def test_logging
    @cache.fetch('foo') { 'bar' }
956
    assert @buffer.string.present?
957 958 959 960
  end

  def test_mute_logging
    @cache.mute { @cache.fetch('foo') { 'bar' } }
961
    assert @buffer.string.blank?
962 963
  end
end
B
Brian Durand 已提交
964 965 966 967

class CacheEntryTest < ActiveSupport::TestCase
  def test_expired
    entry = ActiveSupport::Cache::Entry.new("value")
968
    assert !entry.expired?, 'entry not expired'
B
Brian Durand 已提交
969
    entry = ActiveSupport::Cache::Entry.new("value", :expires_in => 60)
970
    assert !entry.expired?, 'entry not expired'
B
Brian Durand 已提交
971 972
    time = Time.now + 61
    Time.stubs(:now).returns(time)
973
    assert entry.expired?, 'entry is expired'
B
Brian Durand 已提交
974 975 976
  end

  def test_compress_values
977 978 979
    value = "value" * 100
    entry = ActiveSupport::Cache::Entry.new(value, :compress => true, :compress_threshold => 1)
    assert_equal value, entry.value
980
    assert(value.bytesize > entry.size, "value is compressed")
B
Brian Durand 已提交
981 982 983
  end

  def test_non_compress_values
984 985 986 987 988 989
    value = "value" * 100
    entry = ActiveSupport::Cache::Entry.new(value)
    assert_equal value, entry.value
    assert_equal value.bytesize, entry.size
  end

990 991 992 993 994
  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))
995 996 997 998
    assert_equal "hello", entry.value
    assert_equal false, entry.expired?
  end

999 1000 1001 1002 1003
  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))
1004 1005 1006
    assert_equal "hello", entry.value
  end

1007 1008 1009 1010 1011
  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))
1012 1013
    assert_equal "hello", entry.value
    assert_equal true, entry.expired?
B
Brian Durand 已提交
1014 1015
  end
end