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

B
Brian Durand 已提交
260 261 262
  def test_read_and_write_compressed_small_data
    @cache.write('foo', 'bar', :compress => true)
    assert_equal 'bar', @cache.read('foo')
263 264
  end

B
Brian Durand 已提交
265 266 267
  def test_read_and_write_compressed_large_data
    @cache.write('foo', 'bar', :compress => true, :compress_threshold => 2)
    assert_equal 'bar', @cache.read('foo')
268
  end
269

B
Brian Durand 已提交
270 271 272
  def test_read_and_write_compressed_nil
    @cache.write('foo', nil, :compress => true)
    assert_nil @cache.read('foo')
273 274
  end

B
Brian Durand 已提交
275 276 277 278 279 280 281
  def test_cache_key
    obj = Object.new
    def obj.cache_key
      :foo
    end
    @cache.write(obj, "bar")
    assert_equal "bar", @cache.read("foo")
282
  end
283

B
Brian Durand 已提交
284 285 286 287 288 289 290
  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")
291
  end
292

B
Brian Durand 已提交
293 294 295
  def test_array_as_cache_key
    @cache.write([:fu, "foo"], "bar")
    assert_equal "bar", @cache.read("fu/foo")
296 297
  end

B
Brian Durand 已提交
298 299 300
  def test_hash_as_cache_key
    @cache.write({:foo => 1, :fu => 2}, "bar")
    assert_equal "bar", @cache.read("foo=1/fu=2")
301 302
  end

B
Brian Durand 已提交
303 304 305 306
  def test_keys_are_case_sensitive
    @cache.write("foo", "bar")
    assert_nil @cache.read("FOO")
  end
307

B
Brian Durand 已提交
308
  def test_exist
309
    @cache.write('foo', 'bar')
310 311
    assert @cache.exist?('foo')
    assert !@cache.exist?('bar')
312
  end
313

B
Brian Durand 已提交
314 315
  def test_nil_exist
    @cache.write('foo', nil)
316
    assert @cache.exist?('foo')
317 318
  end

B
Brian Durand 已提交
319 320 321
  def test_delete
    @cache.write('foo', 'bar')
    assert @cache.exist?('foo')
322
    assert @cache.delete('foo')
B
Brian Durand 已提交
323 324
    assert !@cache.exist?('foo')
  end
325

326 327 328 329 330
  def test_original_store_objects_should_not_be_immutable
    bar = 'bar'
    @cache.write('foo', bar)
    assert_nothing_raised { bar.gsub!(/.*/, 'baz') }
  end
331

B
Brian Durand 已提交
332 333 334 335 336 337 338 339 340 341 342 343
  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')
344
  end
345

B
Brian Durand 已提交
346 347 348 349 350 351 352
  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"
353
    end
B
Brian Durand 已提交
354 355
    assert_equal "baz", result
  end
356

B
Brian Durand 已提交
357 358 359 360 361 362 363 364 365 366
  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
367

B
Brian Durand 已提交
368 369 370 371 372 373
  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
374
        assert_equal 'bar', @cache.read('foo')
B
Brian Durand 已提交
375
        raise ArgumentError.new
376
      end
S
Santiago Pastorino 已提交
377
    rescue ArgumentError
378
    end
B
Brian Durand 已提交
379
    assert_equal "bar", @cache.read('foo')
380
    Time.stubs(:now).returns(time + 91)
B
Brian Durand 已提交
381 382 383 384 385
    assert_nil @cache.read('foo')
  end

  def test_crazy_key_characters
    crazy_key = "#/:*(<+=> )&$%@?;'\"\'`~-"
386
    assert @cache.write(crazy_key, "1", :raw => true)
B
Brian Durand 已提交
387 388
    assert_equal "1", @cache.read(crazy_key)
    assert_equal "1", @cache.fetch(crazy_key)
389
    assert @cache.delete(crazy_key)
B
Brian Durand 已提交
390 391 392 393 394 395 396
    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 = ""
397
    900.times{key << "x"}
398
    assert @cache.write(key, "bar")
B
Brian Durand 已提交
399 400 401 402
    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))
403
    assert @cache.delete(key)
B
Brian Durand 已提交
404 405
  end
end
406

407
# https://rails.lighthouseapp.com/projects/8994/tickets/6225-memcachestore-cant-deal-with-umlauts-and-special-characters
A
Anupam Choudhury 已提交
408
# The error is caused by character encodings that can't be compared with ASCII-8BIT regular expressions and by special
409 410
# characters like the umlaut in UTF-8.
module EncodedKeyCacheBehavior
411 412 413
  Encoding.list.each do |encoding|
    define_method "test_#{encoding.name.underscore}_encoded_values" do
      key = "foo".force_encoding(encoding)
414
      assert @cache.write(key, "1", :raw => true)
415 416
      assert_equal "1", @cache.read(key)
      assert_equal "1", @cache.fetch(key)
417
      assert @cache.delete(key)
418 419 420 421
      assert_equal "2", @cache.fetch(key, :raw => true) { "2" }
      assert_equal 3, @cache.increment(key)
      assert_equal 2, @cache.decrement(key)
    end
422
  end
423

424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
  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
439 440 441
  end
end

B
Brian Durand 已提交
442 443 444 445
module CacheDeleteMatchedBehavior
  def test_delete_matched
    @cache.write("foo", "bar")
    @cache.write("fu", "baz")
446 447
    @cache.write("foo/bar", "baz")
    @cache.write("fu/baz", "bar")
B
Brian Durand 已提交
448
    @cache.delete_matched(/oo/)
449 450 451 452
    assert !@cache.exist?("foo")
    assert @cache.exist?("fu")
    assert !@cache.exist?("foo/bar")
    assert @cache.exist?("fu/baz")
B
Brian Durand 已提交
453 454 455 456 457 458 459 460 461 462 463
  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
464
    assert_nil @cache.increment('bar')
B
Brian Durand 已提交
465 466 467 468 469 470 471 472 473
  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
474
    assert_nil @cache.decrement('bar')
B
Brian Durand 已提交
475 476 477 478 479 480 481
  end
end

module LocalCacheBehavior
  def test_local_writes_are_persistent_on_the_remote_cache
    retval = @cache.with_local_cache do
      @cache.write('foo', 'bar')
482
    end
483
    assert retval
B
Brian Durand 已提交
484 485
    assert_equal 'bar', @cache.read('foo')
  end
486

B
Brian Durand 已提交
487 488 489 490 491
  def test_clear_also_clears_local_cache
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @cache.clear
      assert_nil @cache.read('foo')
492 493
    end

B
Brian Durand 已提交
494 495
    assert_nil @cache.read('foo')
  end
496

B
Brian Durand 已提交
497 498 499 500
  def test_local_cache_of_write
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @peek.delete('foo')
501 502
      assert_equal 'bar', @cache.read('foo')
    end
B
Brian Durand 已提交
503
  end
504

B
Brian Durand 已提交
505 506 507 508
  def test_local_cache_of_read
    @cache.write('foo', 'bar')
    @cache.with_local_cache do
      assert_equal 'bar', @cache.read('foo')
509
    end
B
Brian Durand 已提交
510
  end
511

B
Brian Durand 已提交
512 513
  def test_local_cache_of_write_nil
    @cache.with_local_cache do
514
      assert @cache.write('foo', nil)
B
Brian Durand 已提交
515 516 517
      assert_nil @cache.read('foo')
      @peek.write('foo', 'bar')
      assert_nil @cache.read('foo')
518
    end
B
Brian Durand 已提交
519
  end
520

B
Brian Durand 已提交
521 522 523 524 525
  def test_local_cache_of_delete
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @cache.delete('foo')
      assert_nil @cache.read('foo')
526
    end
B
Brian Durand 已提交
527
  end
528

B
Brian Durand 已提交
529 530 531 532
  def test_local_cache_of_exist
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @peek.delete('foo')
533
      assert @cache.exist?('foo')
534
    end
B
Brian Durand 已提交
535
  end
536

B
Brian Durand 已提交
537 538 539 540 541 542
  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')
543
    end
B
Brian Durand 已提交
544
  end
545

B
Brian Durand 已提交
546 547 548 549 550 551
  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')
552
    end
B
Brian Durand 已提交
553
  end
554

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

566
module AutoloadingCacheBehavior
567
  include DependenciesTestHelpers
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
  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 已提交
605 606 607 608 609
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)
610
    @cache_with_pathname = ActiveSupport::Cache.lookup_store(:file_store, Pathname.new(cache_dir), :expires_in => 60)
611 612 613

    @buffer = StringIO.new
    @cache.logger = ActiveSupport::Logger.new(@buffer)
B
Brian Durand 已提交
614 615 616 617 618 619 620 621 622 623 624 625 626 627
  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
628
  include AutoloadingCacheBehavior
B
Brian Durand 已提交
629

630 631 632 633 634 635 636
  def test_clear
    filepath = File.join(cache_dir, ".gitkeep")
    FileUtils.touch(filepath)
    @cache.clear
    assert File.exist?(filepath)
  end

637 638 639 640
  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
641 642 643 644 645 646

  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
647

648
  # Test that generated cache keys are short enough to have Tempfile stuff added to them and
649 650 651 652 653
  # 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|
654
      assert File.basename(tmpname+'.lock').length <= 255, "Temp filename too long: #{File.basename(tmpname+'.lock').length}"
655 656 657
    end
  end

658 659 660 661
  # 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"
662
    path = @cache.send(:key_file_path, key)
663 664 665
    assert path.split('/').all? { |dir_name| dir_name.size <= ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE}
    assert_equal 'B', File.basename(path)
  end
666

667 668 669 670 671
  # 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/)
672 673
    end
  end
674

675 676 677 678 679 680 681 682 683 684 685 686
  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

687 688 689
  def test_log_exception_when_cache_read_fails
    File.expects(:exist?).raises(StandardError, "failed")
    @cache.send(:read_entry, "winston", {})
690
    assert @buffer.string.present?
691
  end
B
Brian Durand 已提交
692
end
693

B
Brian Durand 已提交
694 695
class MemoryStoreTest < ActiveSupport::TestCase
  def setup
B
Brian Durand 已提交
696
    @record_size = ActiveSupport::Cache::Entry.new("aaaaaaaaaa").size
697
    @cache = ActiveSupport::Cache.lookup_store(:memory_store, :expires_in => 60, :size => @record_size * 10)
B
Brian Durand 已提交
698 699 700 701 702 703 704 705 706 707 708 709 710 711
  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)
712
    @cache.prune(@record_size * 3)
713 714
    assert @cache.exist?(5)
    assert @cache.exist?(4)
S
Steve Klabnik 已提交
715
    assert !@cache.exist?(3), "no entry"
716
    assert @cache.exist?(2)
S
Steve Klabnik 已提交
717
    assert !@cache.exist?(1), "no entry"
B
Brian Durand 已提交
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
  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")
734 735 736 737 738
    assert @cache.exist?(11)
    assert @cache.exist?(10)
    assert @cache.exist?(9)
    assert @cache.exist?(8)
    assert @cache.exist?(7)
739 740
    assert !@cache.exist?(6), "no entry"
    assert !@cache.exist?(5), "no entry"
741
    assert @cache.exist?(4)
742
    assert !@cache.exist?(3), "no entry"
743
    assert @cache.exist?(2)
744
    assert !@cache.exist?(1), "no entry"
B
Brian Durand 已提交
745 746 747 748 749 750
  end

  def test_pruning_is_capped_at_a_max_time
    def @cache.delete_entry (*args)
      sleep(0.01)
      super
751
    end
B
Brian Durand 已提交
752 753 754 755 756 757
    @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)
758 759 760 761 762
    assert @cache.exist?(5)
    assert @cache.exist?(4)
    assert @cache.exist?(3)
    assert @cache.exist?(2)
    assert !@cache.exist?(1)
B
Brian Durand 已提交
763
  end
764 765 766 767 768 769 770

  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 已提交
771
end
772

773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
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
801
  include AutoloadingCacheBehavior
802 803 804 805 806 807 808

  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
809

810 811 812 813 814 815
  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 已提交
816

817 818 819 820
  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 已提交
821 822
      cache.write("foo", 2)
      assert_equal "2", cache.read("foo")
823
    end
824
  end
825

826 827 828 829
  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
830
      cache.write("foo", Marshal.dump([]))
831
      assert_equal [], cache.read("foo")
832
    end
833
  end
834 835 836 837 838 839 840 841

  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
842
end
843

844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 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
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

902 903 904 905 906
class CacheStoreLoggerTest < ActiveSupport::TestCase
  def setup
    @cache = ActiveSupport::Cache.lookup_store(:memory_store)

    @buffer = StringIO.new
907
    @cache.logger = ActiveSupport::Logger.new(@buffer)
908 909 910 911
  end

  def test_logging
    @cache.fetch('foo') { 'bar' }
912
    assert @buffer.string.present?
913 914 915 916
  end

  def test_mute_logging
    @cache.mute { @cache.fetch('foo') { 'bar' } }
917
    assert @buffer.string.blank?
918 919
  end
end
B
Brian Durand 已提交
920 921 922 923

class CacheEntryTest < ActiveSupport::TestCase
  def test_expired
    entry = ActiveSupport::Cache::Entry.new("value")
924
    assert !entry.expired?, 'entry not expired'
B
Brian Durand 已提交
925
    entry = ActiveSupport::Cache::Entry.new("value", :expires_in => 60)
926
    assert !entry.expired?, 'entry not expired'
B
Brian Durand 已提交
927 928
    time = Time.now + 61
    Time.stubs(:now).returns(time)
929
    assert entry.expired?, 'entry is expired'
B
Brian Durand 已提交
930 931 932
  end

  def test_compress_values
933 934 935
    value = "value" * 100
    entry = ActiveSupport::Cache::Entry.new(value, :compress => true, :compress_threshold => 1)
    assert_equal value, entry.value
936
    assert(value.bytesize > entry.size, "value is compressed")
B
Brian Durand 已提交
937 938 939
  end

  def test_non_compress_values
940 941 942 943 944 945
    value = "value" * 100
    entry = ActiveSupport::Cache::Entry.new(value)
    assert_equal value, entry.value
    assert_equal value.bytesize, entry.size
  end

946 947 948 949 950
  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))
951 952 953 954
    assert_equal "hello", entry.value
    assert_equal false, entry.expired?
  end

955 956 957 958 959
  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))
960 961 962
    assert_equal "hello", entry.value
  end

963 964 965 966 967
  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))
968 969
    assert_equal "hello", entry.value
    assert_equal true, entry.expired?
B
Brian Durand 已提交
970 971
  end
end