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

5
class CacheKeyTest < ActiveSupport::TestCase
6 7 8 9 10 11 12 13 14 15 16 17 18 19
  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

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

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

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

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

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

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

75 76 77 78 79 80 81 82 83 84 85
  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
86 87 88 89
  
  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
90 91
end

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

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

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

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

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

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

  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
139

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

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

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

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

200
  def test_fetch_with_forced_cache_miss
B
Brian Durand 已提交
201
    @cache.write('foo', 'bar')
202
    @cache.expects(:read).never
B
Brian Durand 已提交
203
    @cache.expects(:write).with('foo', 'bar', @cache.options.merge(:force => true))
204
    @cache.fetch('foo', :force => true) { 'bar' }
205
  end
J
Joshua Peek 已提交
206

B
Brian Durand 已提交
207 208 209 210
  def test_fetch_with_cached_nil
    @cache.write('foo', nil)
    @cache.expects(:write).never
    assert_nil @cache.fetch('foo') { 'baz' }
J
Joshua Peek 已提交
211 212 213
  end

  def test_should_read_and_write_hash
214
    assert @cache.write('foo', {:a => "b"})
J
Joshua Peek 已提交
215 216 217
    assert_equal({:a => "b"}, @cache.read('foo'))
  end

218
  def test_should_read_and_write_integer
219
    assert @cache.write('foo', 1)
220 221 222
    assert_equal 1, @cache.read('foo')
  end

J
Joshua Peek 已提交
223
  def test_should_read_and_write_nil
224
    assert @cache.write('foo', nil)
J
Joshua Peek 已提交
225
    assert_equal nil, @cache.read('foo')
226 227
  end

228
  def test_should_read_and_write_false
229
    assert @cache.write('foo', false)
230
    assert_equal false, @cache.read('foo')
231 232
  end

B
Brian Durand 已提交
233
  def test_read_multi
234
    @cache.write('foo', 'bar')
B
Brian Durand 已提交
235 236 237
    @cache.write('fu', 'baz')
    @cache.write('fud', 'biz')
    assert_equal({"foo" => "bar", "fu" => "baz"}, @cache.read_multi('foo', 'fu'))
238
  end
239

240
  def test_read_multi_with_expires
241 242
    time = Time.now
    @cache.write('foo', 'bar', :expires_in => 10)
243 244
    @cache.write('fu', 'baz')
    @cache.write('fud', 'biz')
245
    Time.stubs(:now).returns(time + 11)
246 247
    assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
  end
248

B
Brian Durand 已提交
249 250 251
  def test_read_and_write_compressed_small_data
    @cache.write('foo', 'bar', :compress => true)
    assert_equal 'bar', @cache.read('foo')
252 253
  end

B
Brian Durand 已提交
254 255 256
  def test_read_and_write_compressed_large_data
    @cache.write('foo', 'bar', :compress => true, :compress_threshold => 2)
    assert_equal 'bar', @cache.read('foo')
257
  end
258

B
Brian Durand 已提交
259 260 261
  def test_read_and_write_compressed_nil
    @cache.write('foo', nil, :compress => true)
    assert_nil @cache.read('foo')
262 263
  end

B
Brian Durand 已提交
264 265 266 267 268 269 270
  def test_cache_key
    obj = Object.new
    def obj.cache_key
      :foo
    end
    @cache.write(obj, "bar")
    assert_equal "bar", @cache.read("foo")
271
  end
272

B
Brian Durand 已提交
273 274 275 276 277 278 279
  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")
280
  end
281

B
Brian Durand 已提交
282 283 284
  def test_array_as_cache_key
    @cache.write([:fu, "foo"], "bar")
    assert_equal "bar", @cache.read("fu/foo")
285 286
  end

B
Brian Durand 已提交
287 288 289
  def test_hash_as_cache_key
    @cache.write({:foo => 1, :fu => 2}, "bar")
    assert_equal "bar", @cache.read("foo=1/fu=2")
290 291
  end

B
Brian Durand 已提交
292 293 294 295
  def test_keys_are_case_sensitive
    @cache.write("foo", "bar")
    assert_nil @cache.read("FOO")
  end
296

B
Brian Durand 已提交
297
  def test_exist
298
    @cache.write('foo', 'bar')
299 300
    assert @cache.exist?('foo')
    assert !@cache.exist?('bar')
301
  end
302

B
Brian Durand 已提交
303 304
  def test_nil_exist
    @cache.write('foo', nil)
305
    assert @cache.exist?('foo')
306 307
  end

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

315 316 317 318 319
  def test_original_store_objects_should_not_be_immutable
    bar = 'bar'
    @cache.write('foo', bar)
    assert_nothing_raised { bar.gsub!(/.*/, 'baz') }
  end
320

B
Brian Durand 已提交
321 322 323 324 325 326 327 328 329 330 331 332
  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')
333
  end
334

B
Brian Durand 已提交
335 336 337 338 339 340 341
  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"
342
    end
B
Brian Durand 已提交
343 344
    assert_equal "baz", result
  end
345

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

B
Brian Durand 已提交
357 358 359 360 361 362
  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
363
        assert_equal 'bar', @cache.read('foo')
B
Brian Durand 已提交
364
        raise ArgumentError.new
365
      end
S
Santiago Pastorino 已提交
366
    rescue ArgumentError
367
    end
B
Brian Durand 已提交
368
    assert_equal "bar", @cache.read('foo')
369
    Time.stubs(:now).returns(time + 91)
B
Brian Durand 已提交
370 371 372 373 374
    assert_nil @cache.read('foo')
  end

  def test_crazy_key_characters
    crazy_key = "#/:*(<+=> )&$%@?;'\"\'`~-"
375
    assert @cache.write(crazy_key, "1", :raw => true)
B
Brian Durand 已提交
376 377
    assert_equal "1", @cache.read(crazy_key)
    assert_equal "1", @cache.fetch(crazy_key)
378
    assert @cache.delete(crazy_key)
B
Brian Durand 已提交
379 380 381 382 383 384 385
    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 = ""
386
    900.times{key << "x"}
387
    assert @cache.write(key, "bar")
B
Brian Durand 已提交
388 389 390 391
    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))
392
    assert @cache.delete(key)
B
Brian Durand 已提交
393 394
  end
end
395

396 397 398 399
# https://rails.lighthouseapp.com/projects/8994/tickets/6225-memcachestore-cant-deal-with-umlauts-and-special-characters
# The error is caused by charcter encodings that can't be compared with ASCII-8BIT regular expressions and by special
# characters like the umlaut in UTF-8.
module EncodedKeyCacheBehavior
400 401 402
  Encoding.list.each do |encoding|
    define_method "test_#{encoding.name.underscore}_encoded_values" do
      key = "foo".force_encoding(encoding)
403
      assert @cache.write(key, "1", :raw => true)
404 405
      assert_equal "1", @cache.read(key)
      assert_equal "1", @cache.fetch(key)
406
      assert @cache.delete(key)
407 408 409 410
      assert_equal "2", @cache.fetch(key, :raw => true) { "2" }
      assert_equal 3, @cache.increment(key)
      assert_equal 2, @cache.decrement(key)
    end
411
  end
412

413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
  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
428 429 430
  end
end

B
Brian Durand 已提交
431 432 433 434
module CacheDeleteMatchedBehavior
  def test_delete_matched
    @cache.write("foo", "bar")
    @cache.write("fu", "baz")
435 436
    @cache.write("foo/bar", "baz")
    @cache.write("fu/baz", "bar")
B
Brian Durand 已提交
437
    @cache.delete_matched(/oo/)
438 439 440 441
    assert !@cache.exist?("foo")
    assert @cache.exist?("fu")
    assert !@cache.exist?("foo/bar")
    assert @cache.exist?("fu/baz")
B
Brian Durand 已提交
442 443 444 445 446 447 448 449 450 451 452
  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
453
    assert_nil @cache.increment('bar')
B
Brian Durand 已提交
454 455 456 457 458 459 460 461 462
  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
463
    assert_nil @cache.decrement('bar')
B
Brian Durand 已提交
464 465 466 467 468 469 470
  end
end

module LocalCacheBehavior
  def test_local_writes_are_persistent_on_the_remote_cache
    retval = @cache.with_local_cache do
      @cache.write('foo', 'bar')
471
    end
472
    assert retval
B
Brian Durand 已提交
473 474
    assert_equal 'bar', @cache.read('foo')
  end
475

B
Brian Durand 已提交
476 477 478 479 480
  def test_clear_also_clears_local_cache
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @cache.clear
      assert_nil @cache.read('foo')
481 482
    end

B
Brian Durand 已提交
483 484
    assert_nil @cache.read('foo')
  end
485

B
Brian Durand 已提交
486 487 488 489
  def test_local_cache_of_write
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @peek.delete('foo')
490 491
      assert_equal 'bar', @cache.read('foo')
    end
B
Brian Durand 已提交
492
  end
493

B
Brian Durand 已提交
494 495 496 497
  def test_local_cache_of_read
    @cache.write('foo', 'bar')
    @cache.with_local_cache do
      assert_equal 'bar', @cache.read('foo')
498
    end
B
Brian Durand 已提交
499
  end
500

B
Brian Durand 已提交
501 502
  def test_local_cache_of_write_nil
    @cache.with_local_cache do
503
      assert @cache.write('foo', nil)
B
Brian Durand 已提交
504 505 506
      assert_nil @cache.read('foo')
      @peek.write('foo', 'bar')
      assert_nil @cache.read('foo')
507
    end
B
Brian Durand 已提交
508
  end
509

B
Brian Durand 已提交
510 511 512 513 514
  def test_local_cache_of_delete
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @cache.delete('foo')
      assert_nil @cache.read('foo')
515
    end
B
Brian Durand 已提交
516
  end
517

B
Brian Durand 已提交
518 519 520 521
  def test_local_cache_of_exist
    @cache.with_local_cache do
      @cache.write('foo', 'bar')
      @peek.delete('foo')
522
      assert @cache.exist?('foo')
523
    end
B
Brian Durand 已提交
524
  end
525

B
Brian Durand 已提交
526 527 528 529 530 531
  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')
532
    end
B
Brian Durand 已提交
533
  end
534

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

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

B
Brian Durand 已提交
555 556 557 558 559
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)
560
    @cache_with_pathname = ActiveSupport::Cache.lookup_store(:file_store, Pathname.new(cache_dir), :expires_in => 60)
561 562 563

    @buffer = StringIO.new
    @cache.logger = ActiveSupport::Logger.new(@buffer)
B
Brian Durand 已提交
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
  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

579 580 581 582 583 584 585
  def test_clear
    filepath = File.join(cache_dir, ".gitkeep")
    FileUtils.touch(filepath)
    @cache.clear
    assert File.exist?(filepath)
  end

586 587 588 589
  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
590 591 592 593 594 595

  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
596

597 598 599 600 601 602
  # Test that generated cache keys are short enough to have Tempfile stuff added to them and 
  # remain valid
  def test_filename_max_size
    key = "#{'A' * ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE}"
    path = @cache.send(:key_file_path, key)
    Dir::Tmpname.create(path) do |tmpname, n, opts|
603
      assert File.basename(tmpname+'.lock').length <= 255, "Temp filename too long: #{File.basename(tmpname+'.lock').length}"
604 605 606
    end
  end

607 608 609 610
  # 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"
611
    path = @cache.send(:key_file_path, key)
612 613 614
    assert path.split('/').all? { |dir_name| dir_name.size <= ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE}
    assert_equal 'B', File.basename(path)
  end
615

616 617 618 619 620
  # 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/)
621 622
    end
  end
623 624 625 626 627 628

  def test_log_exception_when_cache_read_fails
    File.expects(:exist?).raises(StandardError, "failed")
    @cache.send(:read_entry, "winston", {})
    assert_present @buffer.string
  end
B
Brian Durand 已提交
629
end
630

B
Brian Durand 已提交
631 632
class MemoryStoreTest < ActiveSupport::TestCase
  def setup
B
Brian Durand 已提交
633
    @record_size = ActiveSupport::Cache::Entry.new("aaaaaaaaaa").size
634
    @cache = ActiveSupport::Cache.lookup_store(:memory_store, :expires_in => 60, :size => @record_size * 10)
B
Brian Durand 已提交
635 636 637 638 639 640 641 642 643 644 645 646 647 648
  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)
649
    @cache.prune(@record_size * 3)
650 651
    assert @cache.exist?(5)
    assert @cache.exist?(4)
S
Steve Klabnik 已提交
652
    assert !@cache.exist?(3), "no entry"
653
    assert @cache.exist?(2)
S
Steve Klabnik 已提交
654
    assert !@cache.exist?(1), "no entry"
B
Brian Durand 已提交
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
  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")
671 672 673 674 675
    assert @cache.exist?(11)
    assert @cache.exist?(10)
    assert @cache.exist?(9)
    assert @cache.exist?(8)
    assert @cache.exist?(7)
676 677
    assert !@cache.exist?(6), "no entry"
    assert !@cache.exist?(5), "no entry"
678
    assert @cache.exist?(4)
679
    assert !@cache.exist?(3), "no entry"
680
    assert @cache.exist?(2)
681
    assert !@cache.exist?(1), "no entry"
B
Brian Durand 已提交
682 683 684 685 686 687
  end

  def test_pruning_is_capped_at_a_max_time
    def @cache.delete_entry (*args)
      sleep(0.01)
      super
688
    end
B
Brian Durand 已提交
689 690 691 692 693 694
    @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)
695 696 697 698 699
    assert @cache.exist?(5)
    assert @cache.exist?(4)
    assert @cache.exist?(3)
    assert @cache.exist?(2)
    assert !@cache.exist?(1)
B
Brian Durand 已提交
700
  end
701 702 703 704 705 706 707

  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 已提交
708
end
709

710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
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

  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
745

746 747 748 749 750 751
  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 已提交
752

753 754 755 756
  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 已提交
757 758
      cache.write("foo", 2)
      assert_equal "2", cache.read("foo")
759
    end
760
  end
761

762 763 764 765
  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
766
      cache.write("foo", Marshal.dump([]))
767
      assert_equal [], cache.read("foo")
768
    end
769
  end
770 771 772 773 774 775 776 777

  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
778
end
779

780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837
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

838 839 840 841 842
class CacheStoreLoggerTest < ActiveSupport::TestCase
  def setup
    @cache = ActiveSupport::Cache.lookup_store(:memory_store)

    @buffer = StringIO.new
843
    @cache.logger = ActiveSupport::Logger.new(@buffer)
844 845 846 847
  end

  def test_logging
    @cache.fetch('foo') { 'bar' }
848
    assert_present @buffer.string
849 850 851 852
  end

  def test_mute_logging
    @cache.mute { @cache.fetch('foo') { 'bar' } }
853
    assert_blank @buffer.string
854 855
  end
end
B
Brian Durand 已提交
856 857 858 859

class CacheEntryTest < ActiveSupport::TestCase
  def test_expired
    entry = ActiveSupport::Cache::Entry.new("value")
860
    assert !entry.expired?, 'entry not expired'
B
Brian Durand 已提交
861
    entry = ActiveSupport::Cache::Entry.new("value", :expires_in => 60)
862
    assert !entry.expired?, 'entry not expired'
B
Brian Durand 已提交
863 864
    time = Time.now + 61
    Time.stubs(:now).returns(time)
865
    assert entry.expired?, 'entry is expired'
B
Brian Durand 已提交
866 867 868
  end

  def test_compress_values
869 870 871
    value = "value" * 100
    entry = ActiveSupport::Cache::Entry.new(value, :compress => true, :compress_threshold => 1)
    assert_equal value, entry.value
872
    assert(value.bytesize > entry.size, "value is compressed")
B
Brian Durand 已提交
873 874 875
  end

  def test_non_compress_values
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 902 903 904 905 906
    value = "value" * 100
    entry = ActiveSupport::Cache::Entry.new(value)
    assert_equal value, entry.value
    assert_equal value.bytesize, entry.size
  end

  def test_restoring_version_3_entries
    version_3_entry = ActiveSupport::Cache::Entry.allocate
    version_3_entry.instance_variable_set(:@value, "hello")
    version_3_entry.instance_variable_set(:@created_at, Time.now - 60)
    entry = Marshal.load(Marshal.dump(version_3_entry))
    assert_equal "hello", entry.value
    assert_equal false, entry.expired?
  end

  def test_restoring_compressed_version_3_entries
    version_3_entry = ActiveSupport::Cache::Entry.allocate
    version_3_entry.instance_variable_set(:@value, Zlib::Deflate.deflate(Marshal.dump("hello")))
    version_3_entry.instance_variable_set(:@compressed, true)
    entry = Marshal.load(Marshal.dump(version_3_entry))
    assert_equal "hello", entry.value
  end

  def test_restoring_expired_version_3_entries
    version_3_entry = ActiveSupport::Cache::Entry.allocate
    version_3_entry.instance_variable_set(:@value, "hello")
    version_3_entry.instance_variable_set(:@created_at, Time.now - 60)
    version_3_entry.instance_variable_set(:@expires_in, 58.9)
    entry = Marshal.load(Marshal.dump(version_3_entry))
    assert_equal "hello", entry.value
    assert_equal true, entry.expired?
B
Brian Durand 已提交
907 908
  end
end