From 9934738ed5f05fe78cdf2c4cf5fcaae7227d5e1e Mon Sep 17 00:00:00 2001
From: Jeremy Kemper <jeremy@bitsweat.net>
Date: Tue, 20 Apr 2010 10:54:28 -0700
Subject: Switch to TimezoneProxy for later support of deferred TZ loading

---
 activesupport/test/core_ext/time_with_zone_test.rb | 7 +++++--
 activesupport/test/time_zone_test.rb               | 8 ++++----
 2 files changed, 9 insertions(+), 6 deletions(-)

(limited to 'activesupport/test')

diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index d88f79ae4f..a808a25821 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -823,9 +823,12 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase
     assert_equal(-18_000, Time.zone.utc_offset)
   end
 
-  def test_time_zone_setter_with_non_identifying_argument_returns_nil
+  def test_time_zone_setter_with_invalid_zone
     Time.zone = 'foo'
-    assert_equal nil, Time.zone
+    assert_not_nil Time.zone
+    assert_equal 'foo', Time.zone.name
+    assert_raise(TZInfo::InvalidTimezoneIdentifier) { Time.zone.utc_offset }
+
     Time.zone = -15.hours
     assert_equal nil, Time.zone
   end
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index 3b7fbb7808..516da7a14c 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -268,16 +268,16 @@ class TimeZoneTest < Test::Unit::TestCase
   end
 
   def test_index
-    assert_nil ActiveSupport::TimeZone["bogus"]
+    assert_not_nil ActiveSupport::TimeZone["bogus"]
     assert_instance_of ActiveSupport::TimeZone, ActiveSupport::TimeZone["Central Time (US & Canada)"]
     assert_instance_of ActiveSupport::TimeZone, ActiveSupport::TimeZone[8]
     assert_raise(ArgumentError) { ActiveSupport::TimeZone[false] }
   end
 
-  def test_unknown_zone_shouldnt_have_tzinfo_nor_utc_offset
+  def test_unknown_zone_should_have_tzinfo_but_exception_on_utc_offset
     zone = ActiveSupport::TimeZone.create("bogus")
-    assert_nil zone.tzinfo
-    assert_nil zone.utc_offset
+    assert_instance_of TZInfo::TimezoneProxy, zone.tzinfo
+    assert_raise(TZInfo::InvalidTimezoneIdentifier) { zone.utc_offset }
   end
 
   def test_unknown_zone_with_utc_offset
-- 
cgit v1.2.3


From 726b5d79845c0ef50db64e2991bfec4e108faf4d Mon Sep 17 00:00:00 2001
From: Daniel Neighman <has.sox@gmail.com>
Date: Thu, 22 Apr 2010 09:43:33 +1000
Subject: updates String#to_(date|date_time|time) to return nil for blank
 strings

---
 activesupport/test/core_ext/string_ext_test.rb | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

(limited to 'activesupport/test')

diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index 58ca215970..97b08da0e4 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -117,17 +117,20 @@ class StringInflectionsTest < Test::Unit::TestCase
     assert_equal Time.local(2005, 2, 27, 23, 50, 19, 275038), "2005-02-27T23:50:19.275038".to_time(:local)
     assert_equal DateTime.civil(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_time
     assert_equal Time.local_time(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_time(:local)
+    assert_equal nil, "".to_time
   end
-  
+
   def test_string_to_datetime
     assert_equal DateTime.civil(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_datetime
     assert_equal 0, "2039-02-27 23:50".to_datetime.offset # use UTC offset
     assert_equal ::Date::ITALY, "2039-02-27 23:50".to_datetime.start # use Ruby's default start value
     assert_equal DateTime.civil(2039, 2, 27, 23, 50, 19 + Rational(275038, 1000000), "-04:00"), "2039-02-27T23:50:19.275038-04:00".to_datetime
+    assert_equal nil, "".to_datetime
   end
-  
+
   def test_string_to_date
     assert_equal Date.new(2005, 2, 27), "2005-02-27".to_date
+    assert_equal nil, "".to_date
   end
 
   def test_access
@@ -255,7 +258,7 @@ end
   string.rb - Interpolation for String.
 
   Copyright (C) 2005-2009 Masao Mutoh
- 
+
   You may redistribute it and/or modify it under the same
   license terms as Ruby.
 =end
-- 
cgit v1.2.3


From 4163ccec2343ee66e2488f067eab2a15260e1219 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Valim?= <jose.valim@gmail.com>
Date: Thu, 22 Apr 2010 12:00:13 +0200
Subject: Clean up the config object in ActionPack. Create config_accessor
 which just delegates to the config object, reducing the number of
 deprecations and add specific tests.

---
 activesupport/test/configurable_test.rb | 42 +++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 activesupport/test/configurable_test.rb

(limited to 'activesupport/test')

diff --git a/activesupport/test/configurable_test.rb b/activesupport/test/configurable_test.rb
new file mode 100644
index 0000000000..cef67e3cf9
--- /dev/null
+++ b/activesupport/test/configurable_test.rb
@@ -0,0 +1,42 @@
+require 'abstract_unit'
+require 'active_support/configurable'
+
+class ConfigurableActiveSupport < ActiveSupport::TestCase
+  class Parent
+    include ActiveSupport::Configurable
+    config_accessor :foo
+  end
+
+  class Child < Parent
+  end
+
+  setup do
+    Parent.config.clear
+    Parent.config.foo = :bar
+
+    Child.config.clear
+  end
+
+  test "adds a configuration hash" do
+    assert_equal({ :foo => :bar }, Parent.config)
+  end
+
+  test "configuration hash is inheritable" do
+    assert_equal :bar, Child.config.foo
+    assert_equal :bar, Parent.config.foo
+
+    Child.config.foo = :baz
+    assert_equal :baz, Child.config.foo
+    assert_equal :bar, Parent.config.foo
+  end
+
+  test "configuration hash is available on instance" do
+    instance = Parent.new
+    assert_equal :bar, instance.config.foo
+    assert_equal :bar, Parent.config.foo
+
+    instance.config.foo = :baz
+    assert_equal :baz, instance.config.foo
+    assert_equal :bar, Parent.config.foo
+  end
+end
\ No newline at end of file
-- 
cgit v1.2.3


From 2472f1026a7d3b3309937568204a5b46b60c2c3c Mon Sep 17 00:00:00 2001
From: Santiago Pastorino <santiago@wyeworks.com>
Date: Thu, 22 Apr 2010 13:02:55 -0300
Subject: HWIA delegates to to_hash symbolize_keys and stringify_keys and bang
 methods are not in the api

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
---
 activesupport/test/core_ext/hash_ext_test.rb | 38 ++++++++++++++++++++++++++--
 1 file changed, 36 insertions(+), 2 deletions(-)

(limited to 'activesupport/test')

diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index 86272a28c1..bac6d16ac5 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -60,6 +60,40 @@ class HashExtTest < Test::Unit::TestCase
     assert_equal @strings, @mixed.dup.stringify_keys!
   end
 
+  def test_symbolize_keys_for_hash_with_indifferent_access
+    assert_equal @symbols, @symbols.with_indifferent_access.symbolize_keys
+    assert_equal @symbols, @strings.with_indifferent_access.symbolize_keys
+    assert_equal @symbols, @mixed.with_indifferent_access.symbolize_keys
+  end
+
+  def test_symbolize_keys_bang_for_hash_with_indifferent_access
+    assert_raise(NoMethodError) { @symbols.with_indifferent_access.dup.symbolize_keys! }
+    assert_raise(NoMethodError) { @strings.with_indifferent_access.dup.symbolize_keys! }
+    assert_raise(NoMethodError) { @mixed.with_indifferent_access.dup.symbolize_keys! }
+  end
+
+  def test_symbolize_keys_preserves_keys_that_cant_be_symbolized_for_hash_with_indifferent_access
+    assert_equal @illegal_symbols, @illegal_symbols.with_indifferent_access.symbolize_keys
+    assert_raise(NoMethodError) { @illegal_symbols.with_indifferent_access.dup.symbolize_keys! }
+  end
+
+  def test_symbolize_keys_preserves_fixnum_keys_for_hash_with_indifferent_access
+    assert_equal @fixnums, @fixnums.with_indifferent_access.symbolize_keys
+    assert_raise(NoMethodError) { @fixnums.with_indifferent_access.dup.symbolize_keys! }
+  end
+
+  def test_stringify_keys_for_hash_with_indifferent_access
+    assert_equal @strings, @symbols.with_indifferent_access.stringify_keys
+    assert_equal @strings, @strings.with_indifferent_access.stringify_keys
+    assert_equal @strings, @mixed.with_indifferent_access.stringify_keys
+  end
+
+  def test_stringify_keys_bang_for_hash_with_indifferent_access
+    assert_raise(NoMethodError) { @symbols.with_indifferent_access.dup.stringify_keys! }
+    assert_raise(NoMethodError) { @strings.with_indifferent_access.dup.stringify_keys! }
+    assert_raise(NoMethodError) { @mixed.with_indifferent_access.dup.stringify_keys! }
+  end
+
   def test_indifferent_assorted
     @strings = @strings.with_indifferent_access
     @symbols = @symbols.with_indifferent_access
@@ -213,11 +247,11 @@ class HashExtTest < Test::Unit::TestCase
   def test_stringify_and_symbolize_keys_on_indifferent_preserves_hash
     h = HashWithIndifferentAccess.new
     h[:first] = 1
-    h.stringify_keys!
+    h = h.stringify_keys
     assert_equal 1, h['first']
     h = HashWithIndifferentAccess.new
     h['first'] = 1
-    h.symbolize_keys!
+    h = h.symbolize_keys
     assert_equal 1, h[:first]
   end
 
-- 
cgit v1.2.3


From d692e6be3091dd114afa0cce2778787d3af93e83 Mon Sep 17 00:00:00 2001
From: Jeremy Kemper <jeremy@bitsweat.net>
Date: Thu, 22 Apr 2010 09:47:40 -0700
Subject: Restore HWIA#stringify_keys! and update changelog

---
 activesupport/test/core_ext/hash_ext_test.rb | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'activesupport/test')

diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index bac6d16ac5..1dcde4b665 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -89,9 +89,9 @@ class HashExtTest < Test::Unit::TestCase
   end
 
   def test_stringify_keys_bang_for_hash_with_indifferent_access
-    assert_raise(NoMethodError) { @symbols.with_indifferent_access.dup.stringify_keys! }
-    assert_raise(NoMethodError) { @strings.with_indifferent_access.dup.stringify_keys! }
-    assert_raise(NoMethodError) { @mixed.with_indifferent_access.dup.stringify_keys! }
+    assert_equal @strings, @symbols.with_indifferent_access.dup.stringify_keys!
+    assert_equal @strings, @strings.with_indifferent_access.dup.stringify_keys!
+    assert_equal @strings, @mixed.with_indifferent_access.dup.stringify_keys!
   end
 
   def test_indifferent_assorted
-- 
cgit v1.2.3


From c976784381d694df8090ad8a16c2648e93a2bd12 Mon Sep 17 00:00:00 2001
From: Jeremy Kemper <jeremy@bitsweat.net>
Date: Thu, 22 Apr 2010 10:07:33 -0700
Subject: Change HWIA#stringify_keys to return a HWIA not a Hash

---
 activesupport/test/core_ext/hash_ext_test.rb | 3 +++
 1 file changed, 3 insertions(+)

(limited to 'activesupport/test')

diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index 1dcde4b665..b2a9731578 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -61,6 +61,7 @@ class HashExtTest < Test::Unit::TestCase
   end
 
   def test_symbolize_keys_for_hash_with_indifferent_access
+    assert_instance_of Hash, @symbols.with_indifferent_access.symbolize_keys
     assert_equal @symbols, @symbols.with_indifferent_access.symbolize_keys
     assert_equal @symbols, @strings.with_indifferent_access.symbolize_keys
     assert_equal @symbols, @mixed.with_indifferent_access.symbolize_keys
@@ -83,12 +84,14 @@ class HashExtTest < Test::Unit::TestCase
   end
 
   def test_stringify_keys_for_hash_with_indifferent_access
+    assert_instance_of ActiveSupport::HashWithIndifferentAccess, @symbols.with_indifferent_access.stringify_keys
     assert_equal @strings, @symbols.with_indifferent_access.stringify_keys
     assert_equal @strings, @strings.with_indifferent_access.stringify_keys
     assert_equal @strings, @mixed.with_indifferent_access.stringify_keys
   end
 
   def test_stringify_keys_bang_for_hash_with_indifferent_access
+    assert_instance_of ActiveSupport::HashWithIndifferentAccess, @symbols.with_indifferent_access.dup.stringify_keys!
     assert_equal @strings, @symbols.with_indifferent_access.dup.stringify_keys!
     assert_equal @strings, @strings.with_indifferent_access.dup.stringify_keys!
     assert_equal @strings, @mixed.with_indifferent_access.dup.stringify_keys!
-- 
cgit v1.2.3


From 864bd9c21fdc8ce265c48fdfa2d4d7917032e717 Mon Sep 17 00:00:00 2001
From: David Chelimsky <dchelimsky@gmail.com>
Date: Sat, 24 Apr 2010 21:12:02 -0500
Subject: allow unsubscribe by name or subscription [#4433 state:resolved]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
---
 activesupport/test/notifications_test.rb | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

(limited to 'activesupport/test')

diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb
index 92fbe5b92f..c2e1c588f0 100644
--- a/activesupport/test/notifications_test.rb
+++ b/activesupport/test/notifications_test.rb
@@ -6,7 +6,9 @@ module Notifications
       ActiveSupport::Notifications.notifier = nil
       @notifier = ActiveSupport::Notifications.notifier
       @events = []
+      @named_events = []
       @subscription = @notifier.subscribe { |*args| @events << event(*args) }
+      @named_subscription = @notifier.subscribe("named.subscription") { |*args| @named_events << event(*args) }
     end
 
     private
@@ -30,6 +32,26 @@ module Notifications
       assert_equal [[:foo]], @events
     end
 
+    def test_unsubscribing_by_name_removes_a_subscription
+      @notifier.publish "named.subscription", :foo
+      @notifier.wait
+      assert_equal [["named.subscription", :foo]], @named_events
+      @notifier.unsubscribe("named.subscription")
+      @notifier.publish "named.subscription", :foo
+      @notifier.wait
+      assert_equal [["named.subscription", :foo]], @named_events
+    end
+
+    def test_unsubscribing_by_name_leaves_the_other_subscriptions
+      @notifier.publish "named.subscription", :foo
+      @notifier.wait
+      assert_equal [["named.subscription", :foo]], @events
+      @notifier.unsubscribe("named.subscription")
+      @notifier.publish "named.subscription", :foo
+      @notifier.wait
+      assert_equal [["named.subscription", :foo], ["named.subscription", :foo]], @events
+    end
+
   private
     def event(*args)
       args
-- 
cgit v1.2.3


From 76d6a993647f3bbe39f31faa0b8ed8767a228301 Mon Sep 17 00:00:00 2001
From: Santiago Pastorino <santiago@wyeworks.com>
Date: Mon, 26 Apr 2010 11:50:18 -0300
Subject: Use explicit source encoding rather than forced UTF-8 from US-ASCII.

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
---
 activesupport/test/core_ext/uri_ext_test.rb | 2 +-
 activesupport/test/multibyte_chars_test.rb  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'activesupport/test')

diff --git a/activesupport/test/core_ext/uri_ext_test.rb b/activesupport/test/core_ext/uri_ext_test.rb
index e4a242abc4..d988837603 100644
--- a/activesupport/test/core_ext/uri_ext_test.rb
+++ b/activesupport/test/core_ext/uri_ext_test.rb
@@ -1,3 +1,4 @@
+# encoding: utf-8
 require 'abstract_unit'
 require 'uri'
 require 'active_support/core_ext/uri'
@@ -5,7 +6,6 @@ require 'active_support/core_ext/uri'
 class URIExtTest < Test::Unit::TestCase
   def test_uri_decode_handle_multibyte
     str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
-    str.force_encoding(Encoding::UTF_8) if str.respond_to?(:force_encoding)
 
     if URI.const_defined?(:Parser)
       parser = URI::Parser.new
diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb
index 1b8d13c024..caf50aa1c9 100644
--- a/activesupport/test/multibyte_chars_test.rb
+++ b/activesupport/test/multibyte_chars_test.rb
@@ -105,7 +105,7 @@ class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase
       @whitespace = "\n\t#{[32, 8195].pack('U*')}"
     else
       # Ruby 1.9 only supports basic whitespace
-      @whitespace = "\n\t ".force_encoding(Encoding::UTF_8)
+      @whitespace = "\n\t "
     end
 
     @byte_order_mark = [65279].pack('U')
-- 
cgit v1.2.3


From c1d73270717f30498f8f4d55d6695509107c2834 Mon Sep 17 00:00:00 2001
From: Jeremy Kemper <jeremy@bitsweat.net>
Date: Mon, 26 Apr 2010 19:32:23 -0700
Subject: JSON: encode objects that don't have a native JSON representation
 using to_hash, if available, instead of instance_values (the old fallback) or
 to_s (other encoders' default). Encode BigDecimal and Regexp encode as
 strings to conform with other encoders. Try to transcode non-UTF-8 strings.

---
 activesupport/test/json/encoding_test.rb | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

(limited to 'activesupport/test')

diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index 188b799f3f..ff95c0ca18 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -9,6 +9,12 @@ class TestJSONEncoding < Test::Unit::TestCase
     end
   end
 
+  class Hashlike
+    def to_hash
+      { :a => 1 }
+    end
+  end
+
   class Custom
     def as_json(options)
       'custom'
@@ -19,7 +25,8 @@ class TestJSONEncoding < Test::Unit::TestCase
   FalseTests    = [[ false, %(false) ]]
   NilTests      = [[ nil,   %(null)  ]]
   NumericTests  = [[ 1,     %(1)     ],
-                   [ 2.5,   %(2.5)   ]]
+                   [ 2.5,   %(2.5)   ],
+                   [ BigDecimal('2.5'), %("#{BigDecimal('2.5').to_s}") ]]
 
   StringTests   = [[ 'this is the <string>',     %("this is the \\u003Cstring\\u003E")],
                    [ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ],
@@ -35,11 +42,12 @@ class TestJSONEncoding < Test::Unit::TestCase
                    [ :"a b", %("a b")  ]]
 
   ObjectTests   = [[ Foo.new(1, 2), %({\"a\":1,\"b\":2}) ]]
+  HashlikeTests = [[ Hashlike.new, %({\"a\":1}) ]]
   CustomTests   = [[ Custom.new, '"custom"' ]]
 
   VariableTests = [[ ActiveSupport::JSON::Variable.new('foo'), 'foo'],
                    [ ActiveSupport::JSON::Variable.new('alert("foo")'), 'alert("foo")']]
-  RegexpTests   = [[ /^a/, '/^a/' ], [/^\w{1,2}[a-z]+/ix, '/^\\w{1,2}[a-z]+/ix']]
+  RegexpTests   = [[ /^a/, '"(?-mix:^a)"' ], [/^\w{1,2}[a-z]+/ix, '"(?ix-m:^\\\\w{1,2}[a-z]+)"']]
 
   DateTests     = [[ Date.new(2005,2,1), %("2005/02/01") ]]
   TimeTests     = [[ Time.utc(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]]
@@ -91,6 +99,15 @@ class TestJSONEncoding < Test::Unit::TestCase
     end
   end
 
+  if '1.9'.respond_to?(:force_encoding)
+    def test_non_utf8_string_transcodes
+      s = '二'.encode('Shift_JIS')
+      result = ActiveSupport::JSON.encode(s)
+      assert_equal '"\\u4e8c"', result
+      assert_equal Encoding::UTF_8, result.encoding
+    end
+  end
+
   def test_exception_raised_when_encoding_circular_reference
     a = [1]
     a << a
-- 
cgit v1.2.3


From ee51b51b60f9e6cce9babed2c8a65a14d87790c8 Mon Sep 17 00:00:00 2001
From: Brian Durand <bdurand@bdurand.local>
Date: Wed, 21 Apr 2010 23:22:05 -0500
Subject: ActiveSupport::Cache refactoring

All Caches

* Add default options to initializer that will be sent to all read, write, fetch, exist?, increment, and decrement
* Add support for the :expires_in option to fetch and write for all caches. Cache entries are stored with the create timestamp and a ttl so that expiration can be handled independently of the implementation.
* Add support for a :namespace option. This can be used to set a global prefix for cache entries.
* Deprecate expand_cache_key on ActiveSupport::Cache and move it to ActionController::Caching and ActionDispatch::Http::Cache since the logic in the method used some Rails specific environment variables and was only used by ActionPack classes. Not very DRY but there didn't seem to be a good shared spot and ActiveSupport really shouldn't be Rails specific.
* Add support for :race_condition_ttl to fetch. This setting can prevent race conditions on fetch calls where several processes try to regenerate a recently expired entry at once.
* Add support for :compress option to fetch and write which will compress any data over a configurable threshold.
* Nil values can now be stored in the cache and are distinct from cache misses for fetch.
* Easier API to create new implementations. Just need to implement the methods read_entry, write_entry, and delete_entry instead of overwriting existing methods.
* Since all cache implementations support storing objects, update the docs to state that ActiveCache::Cache::Store implementations should store objects. Keys, however, must be strings since some implementations require that.
* Increase test coverage.
* Document methods which are provided as convenience but which may not be universally available.

MemoryStore

* MemoryStore can now safely be used as the cache for single server sites.
* Make thread safe so that the default cache implementation used by Rails is thread safe. The overhead is minimal and it is still the fastest store available.
* Provide :size initialization option indicating the maximum size of the cache in memory (defaults to 32Mb).
* Add prune logic that removes the least recently used cache entries to keep the cache size from exceeding the max.
* Deprecated SynchronizedMemoryStore since it isn't needed anymore.

FileStore

* Escape key values so they will work as file names on all file systems, be consistent, and case sensitive
* Use a hash algorithm to segment the cache into sub directories so that a large cache doesn't exceed file system limits.
* FileStore can be slow so implement the LocalCache strategy to cache reads for the duration of a request.
* Add cleanup method to keep the disk from filling up with expired entries.
* Fix increment and decrement to use file system locks so they are consistent between processes.

MemCacheStore

* Support all keys. Previously keys with spaces in them would fail
* Deprecate CompressedMemCacheStore since it isn't needed anymore (use :compress => true)

[#4452 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
---
 activesupport/test/caching_test.rb | 649 ++++++++++++++++++++++++++-----------
 1 file changed, 461 insertions(+), 188 deletions(-)

(limited to 'activesupport/test')

diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index e62e7ef9aa..d9ff1207e7 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -4,6 +4,7 @@ require 'active_support/cache'
 
 class CacheKeyTest < ActiveSupport::TestCase
   def test_expand_cache_key
+    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)
   end
 end
@@ -43,9 +44,10 @@ class CacheStoreSettingTest < ActiveSupport::TestCase
   end
 
   def test_mem_cache_fragment_cache_store_with_options
-    MemCache.expects(:new).with(%w[localhost 192.168.1.1], { :namespace => "foo" })
-    store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1', :namespace => 'foo'
+    MemCache.expects(:new).with(%w[localhost 192.168.1.1], { :timeout => 10 })
+    store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1', :namespace => 'foo', :timeout => 10
     assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
+    assert_equal 'foo', store.options[:namespace]
   end
 
   def test_object_assigned_fragment_cache_store
@@ -55,124 +57,170 @@ class CacheStoreSettingTest < ActiveSupport::TestCase
   end
 end
 
-class CacheStoreTest < ActiveSupport::TestCase
-  def setup
-    @cache = ActiveSupport::Cache.lookup_store(:memory_store)
+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/)
+    assert_equal false, cache.exist?("foo")
+    assert_equal true, cache.exist?("fu")
+  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)
+    assert_equal false, cache.exist?("foo")
+    assert_equal true, cache.exist?("fu")
+  end
+end
+
+# Tests the base functionality that should be identical across all cache stores.
+module CacheStoreBehavior
+  def test_should_read_and_write_strings
+    assert_equal true, @cache.write('foo', 'bar')
+    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')
   end
 
   def test_fetch_without_cache_miss
-    @cache.stubs(:read).with('foo', {}).returns('bar')
+    @cache.write('foo', 'bar')
     @cache.expects(:write).never
     assert_equal 'bar', @cache.fetch('foo') { 'baz' }
   end
 
   def test_fetch_with_cache_miss
-    @cache.stubs(:read).with('foo', {}).returns(nil)
-    @cache.expects(:write).with('foo', 'baz', {})
+    @cache.expects(:write).with('foo', 'baz', @cache.options)
     assert_equal 'baz', @cache.fetch('foo') { 'baz' }
   end
 
   def test_fetch_with_forced_cache_miss
+    @cache.write('foo', 'bar')
     @cache.expects(:read).never
-    @cache.expects(:write).with('foo', 'bar', :force => true)
+    @cache.expects(:write).with('foo', 'bar', @cache.options.merge(:force => true))
     @cache.fetch('foo', :force => true) { 'bar' }
   end
-end
 
-# Tests the base functionality that should be identical across all cache stores.
-module CacheStoreBehavior
-  def test_should_read_and_write_strings
-    @cache.write('foo', 'bar')
-    assert_equal 'bar', @cache.read('foo')
+  def test_fetch_with_cached_nil
+    @cache.write('foo', nil)
+    @cache.expects(:write).never
+    assert_nil @cache.fetch('foo') { 'baz' }
   end
 
   def test_should_read_and_write_hash
-    @cache.write('foo', {:a => "b"})
+    assert_equal true, @cache.write('foo', {:a => "b"})
     assert_equal({:a => "b"}, @cache.read('foo'))
   end
 
   def test_should_read_and_write_integer
-    @cache.write('foo', 1)
+    assert_equal true, @cache.write('foo', 1)
     assert_equal 1, @cache.read('foo')
   end
 
   def test_should_read_and_write_nil
-    @cache.write('foo', nil)
+    assert_equal true, @cache.write('foo', nil)
     assert_equal nil, @cache.read('foo')
   end
 
-  def test_fetch_without_cache_miss
+  def test_read_multi
     @cache.write('foo', 'bar')
-    assert_equal 'bar', @cache.fetch('foo') { 'baz' }
+    @cache.write('fu', 'baz')
+    @cache.write('fud', 'biz')
+    assert_equal({"foo" => "bar", "fu" => "baz"}, @cache.read_multi('foo', 'fu'))
   end
 
-  def test_fetch_with_cache_miss
-    assert_equal 'baz', @cache.fetch('foo') { 'baz' }
+  def test_read_and_write_compressed_small_data
+    @cache.write('foo', 'bar', :compress => true)
+    raw_value = @cache.send(:read_entry, 'foo', {}).raw_value
+    assert_equal 'bar', @cache.read('foo')
+    assert_equal 'bar', raw_value
   end
 
-  def test_fetch_with_forced_cache_miss
-    @cache.fetch('foo', :force => true) { 'bar' }
+  def test_read_and_write_compressed_large_data
+    @cache.write('foo', 'bar', :compress => true, :compress_threshold => 2)
+    raw_value = @cache.send(:read_entry, 'foo', {}).raw_value
+    assert_equal 'bar', @cache.read('foo')
+    assert_equal 'bar', Marshal.load(Zlib::Inflate.inflate(raw_value))
   end
 
-  def test_increment
-    @cache.write('foo', 1, :raw => true)
-    assert_equal 1, @cache.read('foo', :raw => true).to_i
-    assert_equal 2, @cache.increment('foo')
-    assert_equal 2, @cache.read('foo', :raw => true).to_i
-    assert_equal 3, @cache.increment('foo')
-    assert_equal 3, @cache.read('foo', :raw => true).to_i
+  def test_read_and_write_compressed_nil
+    @cache.write('foo', nil, :compress => true)
+    assert_nil @cache.read('foo')
   end
 
-  def test_decrement
-    @cache.write('foo', 3, :raw => true)
-    assert_equal 3, @cache.read('foo', :raw => true).to_i
-    assert_equal 2, @cache.decrement('foo')
-    assert_equal 2, @cache.read('foo', :raw => true).to_i
-    assert_equal 1, @cache.decrement('foo')
-    assert_equal 1, @cache.read('foo', :raw => true).to_i
+  def test_cache_key
+    obj = Object.new
+    def obj.cache_key
+      :foo
+    end
+    @cache.write(obj, "bar")
+    assert_equal "bar", @cache.read("foo")
   end
 
-  def test_exist
-    @cache.write('foo', 'bar')
-    assert @cache.exist?('foo')
-    assert !@cache.exist?('bar')
+  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")
   end
-end
 
-class FileStoreTest < ActiveSupport::TestCase
-  def setup
-    @cache = ActiveSupport::Cache.lookup_store(:file_store, Dir.pwd)
+  def test_array_as_cache_key
+    @cache.write([:fu, "foo"], "bar")
+    assert_equal "bar", @cache.read("fu/foo")
   end
 
-  def teardown
-    File.delete("foo.cache")
+  def test_hash_as_cache_key
+    @cache.write({:foo => 1, :fu => 2}, "bar")
+    assert_equal "bar", @cache.read("foo=1/fu=2")
   end
 
-  include CacheStoreBehavior
-
-  def test_expires_in
-    time = Time.local(2008, 4, 24)
-    Time.stubs(:now).returns(time)
-    File.stubs(:mtime).returns(time)
+  def test_keys_are_case_sensitive
+    @cache.write("foo", "bar")
+    assert_nil @cache.read("FOO")
+  end
 
+  def test_exist
     @cache.write('foo', 'bar')
-    cache_read = lambda { @cache.read('foo', :expires_in => 60) }
-    assert_equal 'bar', cache_read.call
-
-    Time.stubs(:now).returns(time + 30)
-    assert_equal 'bar', cache_read.call
-
-    Time.stubs(:now).returns(time + 120)
-    assert_nil cache_read.call
+    assert_equal true, @cache.exist?('foo')
+    assert_equal false, @cache.exist?('bar')
   end
-end
 
-class MemoryStoreTest < ActiveSupport::TestCase
-  def setup
-    @cache = ActiveSupport::Cache.lookup_store(:memory_store)
+  def test_nil_exist
+    @cache.write('foo', nil)
+    assert_equal true, @cache.exist?('foo')
   end
 
-  include CacheStoreBehavior
+  def test_delete
+    @cache.write('foo', 'bar')
+    assert @cache.exist?('foo')
+    assert_equal true, @cache.delete('foo')
+    assert !@cache.exist?('foo')
+  end
 
   def test_store_objects_should_be_immutable
     @cache.write('foo', 'bar')
@@ -186,175 +234,365 @@ class MemoryStoreTest < ActiveSupport::TestCase
     assert_nothing_raised { bar.gsub!(/.*/, 'baz') }
   end
 
-  def test_multi_get
-    @cache.write('foo', 1)
-    @cache.write('goo', 2)
-    result = @cache.read_multi('foo', 'goo')
-    assert_equal({'foo' => 1, 'goo' => 2}, result)
+  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')
   end
-end
 
-uses_memcached 'memcached backed store' do
-  class MemCacheStoreTest < ActiveSupport::TestCase
-    def setup
-      @cache = ActiveSupport::Cache.lookup_store(:mem_cache_store)
-      @data = @cache.instance_variable_get(:@data)
-      @cache.clear
-      @cache.silence!
-      @cache.logger = Logger.new("/dev/null")
+  def test_race_condition_protection
+    time = Time.now
+    @cache.write('foo', 'bar', :expires_in => 60)
+    Time.stubs(:now).returns(time + 61)
+    result = @cache.fetch('foo', :race_condition_ttl => 10) do
+      assert_equal 'bar', @cache.read('foo')
+      "baz"
     end
+    assert_equal "baz", result
+  end
 
-    include CacheStoreBehavior
+  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
 
-    def test_store_objects_should_be_immutable
-      @cache.with_local_cache do
-        @cache.write('foo', 'bar')
-        @cache.read('foo').gsub!(/.*/, 'baz')
+  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
         assert_equal 'bar', @cache.read('foo')
+        raise ArgumentError.new
       end
+    rescue ArgumentError => e
     end
+    assert_equal "bar", @cache.read('foo')
+    Time.stubs(:now).returns(time + 71)
+    assert_nil @cache.read('foo')
+  end
+
+  def test_crazy_key_characters
+    crazy_key = "#/:*(<+=> )&$%@?;'\"\'`~-"
+    assert_equal true, @cache.write(crazy_key, "1", :raw => true)
+    assert_equal "1", @cache.read(crazy_key)
+    assert_equal "1", @cache.fetch(crazy_key)
+    assert_equal true, @cache.delete(crazy_key)
+    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 = ""
+    1000.times{key << "x"}
+    assert_equal true, @cache.write(key, "bar")
+    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))
+    assert_equal true, @cache.delete(key)
+  end
+end
 
-    def test_stored_objects_should_not_be_frozen
-      @cache.with_local_cache do
-        @cache.write('foo', 'bar')
-      end
-      @cache.with_local_cache do
-        assert !@cache.read('foo').frozen?
-      end
+module CacheDeleteMatchedBehavior
+  def test_delete_matched
+    @cache.write("foo", "bar")
+    @cache.write("fu", "baz")
+    @cache.delete_matched(/oo/)
+    assert_equal false, @cache.exist?("foo")
+    assert_equal true, @cache.exist?("fu")
+  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
+  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
+  end
+end
+
+module LocalCacheBehavior
+  def test_local_writes_are_persistent_on_the_remote_cache
+    retval = @cache.with_local_cache do
+      @cache.write('foo', 'bar')
     end
+    assert_equal true, retval
+    assert_equal 'bar', @cache.read('foo')
+  end
 
-    def test_write_should_return_true_on_success
-      @cache.with_local_cache do
-        result = @cache.write('foo', 'bar')
-        assert_equal 'bar', @cache.read('foo') # make sure 'foo' was written
-        assert result
-      end
+  def test_clear_also_clears_local_cache
+    @cache.with_local_cache do
+      @cache.write('foo', 'bar')
+      @cache.clear
+      assert_nil @cache.read('foo')
     end
 
-    def test_local_writes_are_persistent_on_the_remote_cache
-      @cache.with_local_cache do
-        @cache.write('foo', 'bar')
-      end
+    assert_nil @cache.read('foo')
+  end
 
+  def test_local_cache_of_write
+    @cache.with_local_cache do
+      @cache.write('foo', 'bar')
+      @peek.delete('foo')
       assert_equal 'bar', @cache.read('foo')
     end
+  end
 
-    def test_clear_also_clears_local_cache
-      @cache.with_local_cache do
-        @cache.write('foo', 'bar')
-        @cache.clear
-        assert_nil @cache.read('foo')
-      end
+  def test_local_cache_of_read
+    @cache.write('foo', 'bar')
+    @cache.with_local_cache do
+      assert_equal 'bar', @cache.read('foo')
     end
+  end
 
-    def test_local_cache_of_read_and_write
-      @cache.with_local_cache do
-        @cache.write('foo', 'bar')
-        @data.flush_all # Clear remote cache
-        assert_equal 'bar', @cache.read('foo')
-      end
+  def test_local_cache_of_write_nil
+    @cache.with_local_cache do
+      assert true, @cache.write('foo', nil)
+      assert_nil @cache.read('foo')
+      @peek.write('foo', 'bar')
+      assert_nil @cache.read('foo')
     end
+  end
 
-    def test_local_cache_should_read_and_write_integer
-      @cache.with_local_cache do
-        @cache.write('foo', 1)
-        assert_equal 1, @cache.read('foo')
-      end
+  def test_local_cache_of_delete
+    @cache.with_local_cache do
+      @cache.write('foo', 'bar')
+      @cache.delete('foo')
+      assert_nil @cache.read('foo')
     end
+  end
 
-    def test_local_cache_of_delete
-      @cache.with_local_cache do
-        @cache.write('foo', 'bar')
-        @cache.delete('foo')
-        @data.flush_all # Clear remote cache
-        assert_nil @cache.read('foo')
-      end
+  def test_local_cache_of_exist
+    @cache.with_local_cache do
+      @cache.write('foo', 'bar')
+      @peek.delete('foo')
+      assert true, @cache.exist?('foo')
     end
+  end
 
-    def test_local_cache_of_exist
-      @cache.with_local_cache do
-        @cache.write('foo', 'bar')
-        @cache.instance_variable_set(:@data, nil)
-        @data.flush_all # Clear remote cache
-        assert @cache.exist?('foo')
-      end
+  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')
     end
+  end
 
-    def test_local_cache_of_increment
-      @cache.with_local_cache do
-        @cache.write('foo', 1, :raw => true)
-        @cache.increment('foo')
-        @data.flush_all # Clear remote cache
-        assert_equal 2, @cache.read('foo', :raw => true).to_i
-      end
+  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')
     end
+  end
 
-    def test_local_cache_of_decrement
-      @cache.with_local_cache do
-        @cache.write('foo', 1, :raw => true)
-        @cache.decrement('foo')
-        @data.flush_all # Clear remote cache
-        assert_equal 0, @cache.read('foo', :raw => true).to_i
-      end
-    end
+  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
 
-    def test_exist_with_nulls_cached_locally
-      @cache.with_local_cache do
-        @cache.write('foo', 'bar')
-        @cache.delete('foo')
-        assert !@cache.exist?('foo')
-      end
+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)
+  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
+
+  def test_deprecated_expires_in_on_read
+    ActiveSupport::Deprecation.silence do
+      old_cache = ActiveSupport::Cache.lookup_store(:file_store, cache_dir)
+
+      time = Time.local(2008, 4, 24)
+      Time.stubs(:now).returns(time)
+
+      old_cache.write("foo", "bar")
+      assert_equal 'bar', old_cache.read('foo', :expires_in => 60)
+
+      Time.stubs(:now).returns(time + 30)
+      assert_equal 'bar', old_cache.read('foo', :expires_in => 60)
+
+      Time.stubs(:now).returns(time + 61)
+      assert_equal 'bar', old_cache.read('foo')
+      assert_nil old_cache.read('foo', :expires_in => 60)
+      assert_nil old_cache.read('foo')
     end
+  end
+end
 
-    def test_multi_get
-      @cache.with_local_cache do
-        @cache.write('foo', 1)
-        @cache.write('goo', 2)
-        result = @cache.read_multi('foo', 'goo')
-        assert_equal({'foo' => 1, 'goo' => 2}, result)
-      end
+class MemoryStoreTest < ActiveSupport::TestCase
+  def setup
+    @cache = ActiveSupport::Cache.lookup_store(:memory_store, :expires_in => 60, :size => 100)
+  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)
+    @cache.prune(30)
+    assert_equal true, @cache.exist?(5)
+    assert_equal true, @cache.exist?(4)
+    assert_equal false, @cache.exist?(3)
+    assert_equal true, @cache.exist?(2)
+    assert_equal false, @cache.exist?(1)
+  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")
+    assert_equal true, @cache.exist?(11)
+    assert_equal true, @cache.exist?(10)
+    assert_equal true, @cache.exist?(9)
+    assert_equal true, @cache.exist?(8)
+    assert_equal true, @cache.exist?(7)
+    assert_equal false, @cache.exist?(6)
+    assert_equal false, @cache.exist?(5)
+    assert_equal true, @cache.exist?(4)
+    assert_equal false, @cache.exist?(3)
+    assert_equal true, @cache.exist?(2)
+    assert_equal false, @cache.exist?(1)
+  end
+
+  def test_pruning_is_capped_at_a_max_time
+    def @cache.delete_entry (*args)
+      sleep(0.01)
+      super
     end
+    @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)
+    assert_equal true, @cache.exist?(5)
+    assert_equal true, @cache.exist?(4)
+    assert_equal true, @cache.exist?(3)
+    assert_equal true, @cache.exist?(2)
+    assert_equal false, @cache.exist?(1)
+  end
+end
 
-    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({})
+class SynchronizedStoreTest < ActiveSupport::TestCase
+  def setup
+    ActiveSupport::Deprecation.silence do
+      @cache = ActiveSupport::Cache.lookup_store(:memory_store, :expires_in => 60)
     end
+  end
 
-    def test_expires_in
-      result = @cache.write('foo', 'bar', :expires_in => 1)
-      assert_equal 'bar', @cache.read('foo')
-      sleep 2
-      assert_equal nil, @cache.read('foo')
+  include CacheStoreBehavior
+  include CacheDeleteMatchedBehavior
+  include CacheIncrementDecrementBehavior
+end
+
+uses_memcached 'memcached backed store' do
+  class MemCacheStoreTest < ActiveSupport::TestCase
+    def setup
+      @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 = Logger.new("/dev/null")
     end
 
-    def test_expires_in_with_invalid_value
-      @cache.write('baz', 'bat')
-      assert_raise(RuntimeError) do
-        @cache.write('foo', 'bar', :expires_in => 'Mon Jun 29 13:10:40 -0700 2150')
-      end
-      assert_equal 'bat', @cache.read('baz')
-      assert_equal nil, @cache.read('foo')
+    include CacheStoreBehavior
+    include LocalCacheBehavior
+    include CacheIncrementDecrementBehavior
+
+    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
 
-    def test_delete_should_only_pass_key_to_data
-      key = 'foo'
-      @data.expects(:delete).with(key)
-      @cache.delete(key)
+    def test_local_cache_raw_values
+      cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
+      cache.clear
+      cache.with_local_cache do
+        cache.write("foo", 2)
+        assert_equal "2", cache.read("foo")
+      end
     end
   end
 
   class CompressedMemCacheStore < ActiveSupport::TestCase
     def setup
-      @cache = ActiveSupport::Cache.lookup_store(:compressed_mem_cache_store)
-      @cache.clear
+      ActiveSupport::Deprecation.silence do
+        @cache = ActiveSupport::Cache.lookup_store(:compressed_mem_cache_store, :expires_in => 60)
+        @cache.clear
+      end
     end
 
     include CacheStoreBehavior
+    include CacheIncrementDecrementBehavior
   end
 end
 
@@ -376,3 +614,38 @@ class CacheStoreLoggerTest < ActiveSupport::TestCase
     assert @buffer.string.blank?
   end
 end
+
+class CacheEntryTest < ActiveSupport::TestCase
+  def test_create_raw_entry
+    time = Time.now
+    entry = ActiveSupport::Cache::Entry.create("raw", time, :compress => false, :expires_in => 300)
+    assert_equal "raw", entry.raw_value
+    assert_equal time.to_f, entry.created_at
+    assert_equal false, entry.compressed?
+    assert_equal 300, entry.expires_in
+  end
+
+  def test_expired
+    entry = ActiveSupport::Cache::Entry.new("value")
+    assert_equal false, entry.expired?
+    entry = ActiveSupport::Cache::Entry.new("value", :expires_in => 60)
+    assert_equal false, entry.expired?
+    time = Time.now + 61
+    Time.stubs(:now).returns(time)
+    assert_equal true, entry.expired?
+  end
+
+  def test_compress_values
+    entry = ActiveSupport::Cache::Entry.new("value", :compress => true, :compress_threshold => 1)
+    assert_equal "value", entry.value
+    assert_equal true, entry.compressed?
+    assert_equal "value", Marshal.load(Zlib::Inflate.inflate(entry.raw_value))
+  end
+
+  def test_non_compress_values
+    entry = ActiveSupport::Cache::Entry.new("value")
+    assert_equal "value", entry.value
+    assert_equal "value", entry.raw_value
+    assert_equal false, entry.compressed?
+  end
+end
-- 
cgit v1.2.3


From 1067a7be52ec8deb208ce46a82f16ed8d0199bd4 Mon Sep 17 00:00:00 2001
From: Cezary Baginski <cezary.baginski@gmail.com>
Date: Wed, 28 Apr 2010 12:24:53 +0200
Subject: Fix BigDecimal JSON encoding test. [#4495 state:resolved]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
---
 activesupport/test/json/encoding_test.rb | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'activesupport/test')

diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index ff95c0ca18..ac7ca96c4d 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -26,7 +26,7 @@ class TestJSONEncoding < Test::Unit::TestCase
   NilTests      = [[ nil,   %(null)  ]]
   NumericTests  = [[ 1,     %(1)     ],
                    [ 2.5,   %(2.5)   ],
-                   [ BigDecimal('2.5'), %("#{BigDecimal('2.5').to_s}") ]]
+                   [ BigDecimal('2.5'), %("#{BigDecimal('2.5').to_s('F')}") ]]
 
   StringTests   = [[ 'this is the <string>',     %("this is the \\u003Cstring\\u003E")],
                    [ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ],
@@ -126,7 +126,7 @@ class TestJSONEncoding < Test::Unit::TestCase
   def test_hash_should_allow_key_filtering_with_except
     assert_equal %({"b":2}), ActiveSupport::JSON.encode({'foo' => 'bar', :b => 2, :c => 3}, :except => ['foo', :c])
   end
-  
+
   def test_time_to_json_includes_local_offset
     ActiveSupport.use_standard_json_time_format = true
     with_env_tz 'US/Eastern' do
@@ -153,7 +153,7 @@ class TestJSONEncoding < Test::Unit::TestCase
     def object_keys(json_object)
       json_object[1..-2].scan(/([^{}:,\s]+):/).flatten.sort
     end
-    
+
     def with_env_tz(new_tz = 'US/Eastern')
       old_tz, ENV['TZ'] = ENV['TZ'], new_tz
       yield
-- 
cgit v1.2.3