diff options
author | Leonel Galán <leonel@getstealz.com> | 2017-04-03 22:59:43 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-04-03 22:59:43 -0400 |
commit | 8648f289f01656b2258687126108c389127108e0 (patch) | |
tree | 1ccb15f72f00d8c30497ad4e48f203545dd3383b /activesupport/test | |
parent | 28b3582aa2cd6007c708134b911b3f360ad5d659 (diff) | |
parent | e8c33349bfabca28996ac74d344d69c7aaffec50 (diff) | |
download | rails-8648f289f01656b2258687126108c389127108e0.tar.gz rails-8648f289f01656b2258687126108c389127108e0.tar.bz2 rails-8648f289f01656b2258687126108c389127108e0.zip |
Merge branch 'master' into bug/filtered_parameters_class
Diffstat (limited to 'activesupport/test')
-rw-r--r-- | activesupport/test/caching_test.rb | 11 | ||||
-rw-r--r-- | activesupport/test/core_ext/date_and_time_compatibility_test.rb | 166 | ||||
-rw-r--r-- | activesupport/test/core_ext/date_time_ext_test.rb | 29 | ||||
-rw-r--r-- | activesupport/test/core_ext/duration_test.rb | 157 | ||||
-rw-r--r-- | activesupport/test/core_ext/hash_ext_test.rb | 679 | ||||
-rw-r--r-- | activesupport/test/core_ext/marshal_test.rb | 13 | ||||
-rw-r--r-- | activesupport/test/core_ext/object/duplicable_test.rb | 5 | ||||
-rw-r--r-- | activesupport/test/core_ext/string_ext_test.rb | 13 | ||||
-rw-r--r-- | activesupport/test/core_ext/time_ext_test.rb | 36 | ||||
-rw-r--r-- | activesupport/test/core_ext/time_with_zone_test.rb | 40 | ||||
-rw-r--r-- | activesupport/test/deprecation_test.rb | 23 | ||||
-rw-r--r-- | activesupport/test/gzip_test.rb | 10 | ||||
-rw-r--r-- | activesupport/test/hash_with_indifferent_access_test.rb | 734 | ||||
-rw-r--r-- | activesupport/test/inflector_test.rb | 13 | ||||
-rw-r--r-- | activesupport/test/inflector_test_cases.rb | 16 | ||||
-rw-r--r-- | activesupport/test/json/encoding_test_cases.rb | 4 | ||||
-rw-r--r-- | activesupport/test/time_zone_test.rb | 186 |
17 files changed, 1444 insertions, 691 deletions
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index c543122d91..c67ffe69b8 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -47,6 +47,17 @@ module ActiveSupport assert_raises(RuntimeError) { middleware.call({}) } assert_nil LocalCacheRegistry.cache_for(key) end + + def test_local_cache_cleared_on_throw + key = "super awesome key" + assert_nil LocalCacheRegistry.cache_for key + middleware = Middleware.new("<3", key).new(->(env) { + assert LocalCacheRegistry.cache_for(key), "should have a cache" + throw :warden + }) + assert_throws(:warden) { middleware.call({}) } + assert_nil LocalCacheRegistry.cache_for(key) + end end end end diff --git a/activesupport/test/core_ext/date_and_time_compatibility_test.rb b/activesupport/test/core_ext/date_and_time_compatibility_test.rb index 4c90460032..6c6205a4d2 100644 --- a/activesupport/test/core_ext/date_and_time_compatibility_test.rb +++ b/activesupport/test/core_ext/date_and_time_compatibility_test.rb @@ -16,11 +16,13 @@ class DateAndTimeCompatibilityTest < ActiveSupport::TestCase def test_time_to_time_preserves_timezone with_preserve_timezone(true) do with_env_tz "US/Eastern" do - time = Time.new(2016, 4, 23, 15, 11, 12, 3600).to_time + source = Time.new(2016, 4, 23, 15, 11, 12, 3600) + time = source.to_time assert_instance_of Time, time assert_equal @utc_time, time.getutc assert_equal @utc_offset, time.utc_offset + assert_equal source.object_id, time.object_id end end end @@ -28,11 +30,43 @@ class DateAndTimeCompatibilityTest < ActiveSupport::TestCase def test_time_to_time_does_not_preserve_time_zone with_preserve_timezone(false) do with_env_tz "US/Eastern" do - time = Time.new(2016, 4, 23, 15, 11, 12, 3600).to_time + source = Time.new(2016, 4, 23, 15, 11, 12, 3600) + time = source.to_time assert_instance_of Time, time assert_equal @utc_time, time.getutc assert_equal @system_offset, time.utc_offset + assert_not_equal source.object_id, time.object_id + end + end + end + + def test_time_to_time_frozen_preserves_timezone + with_preserve_timezone(true) do + with_env_tz "US/Eastern" do + source = Time.new(2016, 4, 23, 15, 11, 12, 3600).freeze + time = source.to_time + + assert_instance_of Time, time + assert_equal @utc_time, time.getutc + assert_equal @utc_offset, time.utc_offset + assert_equal source.object_id, time.object_id + assert_predicate time, :frozen? + end + end + end + + def test_time_to_time_frozen_does_not_preserve_time_zone + with_preserve_timezone(false) do + with_env_tz "US/Eastern" do + source = Time.new(2016, 4, 23, 15, 11, 12, 3600).freeze + time = source.to_time + + assert_instance_of Time, time + assert_equal @utc_time, time.getutc + assert_equal @system_offset, time.utc_offset + assert_not_equal source.object_id, time.object_id + assert_not_predicate time, :frozen? end end end @@ -40,7 +74,8 @@ class DateAndTimeCompatibilityTest < ActiveSupport::TestCase def test_datetime_to_time_preserves_timezone with_preserve_timezone(true) do with_env_tz "US/Eastern" do - time = DateTime.new(2016, 4, 23, 15, 11, 12, Rational(1, 24)).to_time + source = DateTime.new(2016, 4, 23, 15, 11, 12, Rational(1, 24)) + time = source.to_time assert_instance_of Time, time assert_equal @utc_time, time.getutc @@ -52,7 +87,8 @@ class DateAndTimeCompatibilityTest < ActiveSupport::TestCase def test_datetime_to_time_does_not_preserve_time_zone with_preserve_timezone(false) do with_env_tz "US/Eastern" do - time = DateTime.new(2016, 4, 23, 15, 11, 12, Rational(1, 24)).to_time + source = DateTime.new(2016, 4, 23, 15, 11, 12, Rational(1, 24)) + time = source.to_time assert_instance_of Time, time assert_equal @utc_time, time.getutc @@ -61,17 +97,47 @@ class DateAndTimeCompatibilityTest < ActiveSupport::TestCase end end + def test_datetime_to_time_frozen_preserves_timezone + with_preserve_timezone(true) do + with_env_tz "US/Eastern" do + source = DateTime.new(2016, 4, 23, 15, 11, 12, Rational(1, 24)).freeze + time = source.to_time + + assert_instance_of Time, time + assert_equal @utc_time, time.getutc + assert_equal @utc_offset, time.utc_offset + assert_not_predicate time, :frozen? + end + end + end + + def test_datetime_to_time_frozen_does_not_preserve_time_zone + with_preserve_timezone(false) do + with_env_tz "US/Eastern" do + source = DateTime.new(2016, 4, 23, 15, 11, 12, Rational(1, 24)).freeze + time = source.to_time + + assert_instance_of Time, time + assert_equal @utc_time, time.getutc + assert_equal @system_offset, time.utc_offset + assert_not_predicate time, :frozen? + end + end + end + def test_twz_to_time_preserves_timezone with_preserve_timezone(true) do with_env_tz "US/Eastern" do - time = ActiveSupport::TimeWithZone.new(@utc_time, @zone).to_time + source = ActiveSupport::TimeWithZone.new(@utc_time, @zone) + time = source.to_time assert_instance_of Time, time assert_equal @utc_time, time.getutc assert_instance_of Time, time.getutc assert_equal @utc_offset, time.utc_offset - time = ActiveSupport::TimeWithZone.new(@date_time, @zone).to_time + source = ActiveSupport::TimeWithZone.new(@date_time, @zone) + time = source.to_time assert_instance_of Time, time assert_equal @date_time, time.getutc @@ -84,19 +150,69 @@ class DateAndTimeCompatibilityTest < ActiveSupport::TestCase def test_twz_to_time_does_not_preserve_time_zone with_preserve_timezone(false) do with_env_tz "US/Eastern" do - time = ActiveSupport::TimeWithZone.new(@utc_time, @zone).to_time + source = ActiveSupport::TimeWithZone.new(@utc_time, @zone) + time = source.to_time + + assert_instance_of Time, time + assert_equal @utc_time, time.getutc + assert_instance_of Time, time.getutc + assert_equal @system_offset, time.utc_offset + + source = ActiveSupport::TimeWithZone.new(@date_time, @zone) + time = source.to_time + + assert_instance_of Time, time + assert_equal @date_time, time.getutc + assert_instance_of Time, time.getutc + assert_equal @system_offset, time.utc_offset + end + end + end + + def test_twz_to_time_frozen_preserves_timezone + with_preserve_timezone(true) do + with_env_tz "US/Eastern" do + source = ActiveSupport::TimeWithZone.new(@utc_time, @zone).freeze + time = source.to_time + + assert_instance_of Time, time + assert_equal @utc_time, time.getutc + assert_instance_of Time, time.getutc + assert_equal @utc_offset, time.utc_offset + assert_not_predicate time, :frozen? + + source = ActiveSupport::TimeWithZone.new(@date_time, @zone).freeze + time = source.to_time + + assert_instance_of Time, time + assert_equal @date_time, time.getutc + assert_instance_of Time, time.getutc + assert_equal @utc_offset, time.utc_offset + assert_not_predicate time, :frozen? + end + end + end + + def test_twz_to_time_frozen_does_not_preserve_time_zone + with_preserve_timezone(false) do + with_env_tz "US/Eastern" do + source = ActiveSupport::TimeWithZone.new(@utc_time, @zone).freeze + time = source.to_time assert_instance_of Time, time assert_equal @utc_time, time.getutc assert_instance_of Time, time.getutc assert_equal @system_offset, time.utc_offset + assert_not_predicate time, :frozen? - time = ActiveSupport::TimeWithZone.new(@date_time, @zone).to_time + source = ActiveSupport::TimeWithZone.new(@date_time, @zone).freeze + time = source.to_time assert_instance_of Time, time assert_equal @date_time, time.getutc assert_instance_of Time, time.getutc assert_equal @system_offset, time.utc_offset + assert_not_predicate time, :frozen? end end end @@ -104,7 +220,8 @@ class DateAndTimeCompatibilityTest < ActiveSupport::TestCase def test_string_to_time_preserves_timezone with_preserve_timezone(true) do with_env_tz "US/Eastern" do - time = "2016-04-23T15:11:12+01:00".to_time + source = "2016-04-23T15:11:12+01:00" + time = source.to_time assert_instance_of Time, time assert_equal @utc_time, time.getutc @@ -116,11 +233,40 @@ class DateAndTimeCompatibilityTest < ActiveSupport::TestCase def test_string_to_time_does_not_preserve_time_zone with_preserve_timezone(false) do with_env_tz "US/Eastern" do - time = "2016-04-23T15:11:12+01:00".to_time + source = "2016-04-23T15:11:12+01:00" + time = source.to_time + + assert_instance_of Time, time + assert_equal @utc_time, time.getutc + assert_equal @system_offset, time.utc_offset + end + end + end + + def test_string_to_time_frozen_preserves_timezone + with_preserve_timezone(true) do + with_env_tz "US/Eastern" do + source = "2016-04-23T15:11:12+01:00".freeze + time = source.to_time + + assert_instance_of Time, time + assert_equal @utc_time, time.getutc + assert_equal @utc_offset, time.utc_offset + assert_not_predicate time, :frozen? + end + end + end + + def test_string_to_time_frozen_does_not_preserve_time_zone + with_preserve_timezone(false) do + with_env_tz "US/Eastern" do + source = "2016-04-23T15:11:12+01:00".freeze + time = source.to_time assert_instance_of Time, time assert_equal @utc_time, time.getutc assert_equal @system_offset, time.utc_offset + assert_not_predicate time, :frozen? end end end diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index e3b31c10f5..36f0ee22b8 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -4,8 +4,8 @@ require "core_ext/date_and_time_behavior" require "time_zone_test_helpers" class DateTimeExtCalculationsTest < ActiveSupport::TestCase - def date_time_init(year, month, day, hour, minute, second, *args) - DateTime.civil(year, month, day, hour, minute, second) + def date_time_init(year, month, day, hour, minute, second, usec = 0) + DateTime.civil(year, month, day, hour, minute, second + (usec / 1000000)) end include DateAndTimeBehavior @@ -113,7 +113,7 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase end def test_end_of_day - assert_equal DateTime.civil(2005, 2, 4, 23, 59, 59), DateTime.civil(2005, 2, 4, 10, 10, 10).end_of_day + assert_equal DateTime.civil(2005, 2, 4, 23, 59, Rational(59999999999, 1000000000)), DateTime.civil(2005, 2, 4, 10, 10, 10).end_of_day end def test_beginning_of_hour @@ -121,7 +121,7 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase end def test_end_of_hour - assert_equal DateTime.civil(2005, 2, 4, 19, 59, 59), DateTime.civil(2005, 2, 4, 19, 30, 10).end_of_hour + assert_equal DateTime.civil(2005, 2, 4, 19, 59, Rational(59999999999, 1000000000)), DateTime.civil(2005, 2, 4, 19, 30, 10).end_of_hour end def test_beginning_of_minute @@ -129,13 +129,13 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase end def test_end_of_minute - assert_equal DateTime.civil(2005, 2, 4, 19, 30, 59), DateTime.civil(2005, 2, 4, 19, 30, 10).end_of_minute + assert_equal DateTime.civil(2005, 2, 4, 19, 30, Rational(59999999999, 1000000000)), DateTime.civil(2005, 2, 4, 19, 30, 10).end_of_minute end def test_end_of_month - assert_equal DateTime.civil(2005, 3, 31, 23, 59, 59), DateTime.civil(2005, 3, 20, 10, 10, 10).end_of_month - assert_equal DateTime.civil(2005, 2, 28, 23, 59, 59), DateTime.civil(2005, 2, 20, 10, 10, 10).end_of_month - assert_equal DateTime.civil(2005, 4, 30, 23, 59, 59), DateTime.civil(2005, 4, 20, 10, 10, 10).end_of_month + assert_equal DateTime.civil(2005, 3, 31, 23, 59, Rational(59999999999, 1000000000)), DateTime.civil(2005, 3, 20, 10, 10, 10).end_of_month + assert_equal DateTime.civil(2005, 2, 28, 23, 59, Rational(59999999999, 1000000000)), DateTime.civil(2005, 2, 20, 10, 10, 10).end_of_month + assert_equal DateTime.civil(2005, 4, 30, 23, 59, Rational(59999999999, 1000000000)), DateTime.civil(2005, 4, 20, 10, 10, 10).end_of_month end def test_last_year @@ -162,12 +162,19 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase assert_equal DateTime.civil(2006, 2, 22, 15, 15, 10), DateTime.civil(2005, 2, 22, 15, 15, 10).change(year: 2006) assert_equal DateTime.civil(2005, 6, 22, 15, 15, 10), DateTime.civil(2005, 2, 22, 15, 15, 10).change(month: 6) assert_equal DateTime.civil(2012, 9, 22, 15, 15, 10), DateTime.civil(2005, 2, 22, 15, 15, 10).change(year: 2012, month: 9) - assert_equal DateTime.civil(2005, 2, 22, 16), DateTime.civil(2005, 2, 22, 15, 15, 10).change(hour: 16) - assert_equal DateTime.civil(2005, 2, 22, 16, 45), DateTime.civil(2005, 2, 22, 15, 15, 10).change(hour: 16, min: 45) - assert_equal DateTime.civil(2005, 2, 22, 15, 45), DateTime.civil(2005, 2, 22, 15, 15, 10).change(min: 45) + assert_equal DateTime.civil(2005, 2, 22, 16), DateTime.civil(2005, 2, 22, 15, 15, 10).change(hour: 16) + assert_equal DateTime.civil(2005, 2, 22, 16, 45), DateTime.civil(2005, 2, 22, 15, 15, 10).change(hour: 16, min: 45) + assert_equal DateTime.civil(2005, 2, 22, 15, 45), DateTime.civil(2005, 2, 22, 15, 15, 10).change(min: 45) # datetime with fractions of a second assert_equal DateTime.civil(2005, 2, 1, 15, 15, 10.7), DateTime.civil(2005, 2, 22, 15, 15, 10.7).change(day: 1) + assert_equal DateTime.civil(2005, 1, 2, 11, 22, Rational(33000008, 1000000)), DateTime.civil(2005, 1, 2, 11, 22, 33).change(usec: 8) + assert_equal DateTime.civil(2005, 1, 2, 11, 22, Rational(33000008, 1000000)), DateTime.civil(2005, 1, 2, 11, 22, 33).change(nsec: 8000) + assert_raise(ArgumentError) { DateTime.civil(2005, 1, 2, 11, 22, 0).change(usec: 1, nsec: 1) } + assert_raise(ArgumentError) { DateTime.civil(2005, 1, 2, 11, 22, 0).change(usec: 1000000) } + assert_raise(ArgumentError) { DateTime.civil(2005, 1, 2, 11, 22, 0).change(nsec: 1000000000) } + assert_nothing_raised { DateTime.civil(2005, 1, 2, 11, 22, 0).change(usec: 999999) } + assert_nothing_raised { DateTime.civil(2005, 1, 2, 11, 22, 0).change(nsec: 999999999) } end def test_advance diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index 6a275d1d5b..1648a9b270 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -84,6 +84,38 @@ class DurationTest < ActiveSupport::TestCase assert_nothing_raised { Date.today - Date.today } end + def test_plus + assert_equal 2.seconds, 1.second + 1.second + assert_instance_of ActiveSupport::Duration, 1.second + 1.second + assert_equal 2.seconds, 1.second + 1 + assert_instance_of ActiveSupport::Duration, 1.second + 1 + end + + def test_minus + assert_equal 1.second, 2.seconds - 1.second + assert_instance_of ActiveSupport::Duration, 2.seconds - 1.second + assert_equal 1.second, 2.seconds - 1 + assert_instance_of ActiveSupport::Duration, 2.seconds - 1 + assert_equal 1.second, 2 - 1.second + assert_instance_of ActiveSupport::Duration, 2.seconds - 1 + end + + def test_multiply + assert_equal 7.days, 1.day * 7 + assert_instance_of ActiveSupport::Duration, 1.day * 7 + assert_equal 86400, 1.day * 1.second + end + + def test_divide + assert_equal 1.day, 7.days / 7 + assert_instance_of ActiveSupport::Duration, 7.days / 7 + assert_equal 1, 1.day / 1.day + end + + def test_date_added_with_multiplied_duration + assert_equal Date.civil(2017, 1, 3), Date.civil(2017, 1, 1) + 1.day * 2 + end + def test_plus_with_time assert_equal 1 + 1.second, 1.second + 1, "Duration + Numeric should == Numeric + Duration" end @@ -179,6 +211,19 @@ class DurationTest < ActiveSupport::TestCase Time.zone = nil end + def test_before_and_afer + t = Time.local(2000) + assert_equal t + 1, 1.second.after(t) + assert_equal t - 1, 1.second.before(t) + end + + def test_before_and_after_without_argument + Time.stub(:now, Time.local(2000)) do + assert_equal Time.now - 1.second, 1.second.before + assert_equal Time.now + 1.second, 1.second.after + end + end + def test_adding_hours_across_dst_boundary with_env_tz "CET" do assert_equal Time.local(2009, 3, 29, 0, 0, 0) + 24.hours, Time.local(2009, 3, 30, 1, 0, 0) @@ -237,6 +282,118 @@ class DurationTest < ActiveSupport::TestCase assert_equal(1, (61 <=> 1.minute)) end + def test_implicit_coercion + assert_equal 2.days, 2 * 1.day + assert_instance_of ActiveSupport::Duration, 2 * 1.day + assert_equal Time.utc(2017, 1, 3), Time.utc(2017, 1, 1) + 2 * 1.day + assert_equal Date.civil(2017, 1, 3), Date.civil(2017, 1, 1) + 2 * 1.day + end + + def test_scalar_coerce + scalar = ActiveSupport::Duration::Scalar.new(10) + assert_instance_of ActiveSupport::Duration::Scalar, 10 + scalar + assert_instance_of ActiveSupport::Duration, 10.seconds + scalar + end + + def test_scalar_delegations + scalar = ActiveSupport::Duration::Scalar.new(10) + assert_kind_of Float, scalar.to_f + assert_kind_of Integer, scalar.to_i + assert_kind_of String, scalar.to_s + end + + def test_scalar_unary_minus + scalar = ActiveSupport::Duration::Scalar.new(10) + + assert_equal(-10, -scalar) + assert_instance_of ActiveSupport::Duration::Scalar, -scalar + end + + def test_scalar_compare + scalar = ActiveSupport::Duration::Scalar.new(10) + + assert_equal(1, scalar <=> 5) + assert_equal(0, scalar <=> 10) + assert_equal(-1, scalar <=> 15) + assert_equal(nil, scalar <=> "foo") + end + + def test_scalar_plus + scalar = ActiveSupport::Duration::Scalar.new(10) + + assert_equal 20, 10 + scalar + assert_instance_of ActiveSupport::Duration::Scalar, 10 + scalar + assert_equal 20, scalar + 10 + assert_instance_of ActiveSupport::Duration::Scalar, scalar + 10 + assert_equal 20, 10.seconds + scalar + assert_instance_of ActiveSupport::Duration, 10.seconds + scalar + assert_equal 20, scalar + 10.seconds + assert_instance_of ActiveSupport::Duration, scalar + 10.seconds + + exception = assert_raises(TypeError) do + scalar + "foo" + end + + assert_equal "no implicit conversion of String into ActiveSupport::Duration::Scalar", exception.message + end + + def test_scalar_minus + scalar = ActiveSupport::Duration::Scalar.new(10) + + assert_equal 10, 20 - scalar + assert_instance_of ActiveSupport::Duration::Scalar, 20 - scalar + assert_equal 5, scalar - 5 + assert_instance_of ActiveSupport::Duration::Scalar, scalar - 5 + assert_equal 10, 20.seconds - scalar + assert_instance_of ActiveSupport::Duration, 20.seconds - scalar + assert_equal 5, scalar - 5.seconds + assert_instance_of ActiveSupport::Duration, scalar - 5.seconds + + exception = assert_raises(TypeError) do + scalar - "foo" + end + + assert_equal "no implicit conversion of String into ActiveSupport::Duration::Scalar", exception.message + end + + def test_scalar_multiply + scalar = ActiveSupport::Duration::Scalar.new(5) + + assert_equal 10, 2 * scalar + assert_instance_of ActiveSupport::Duration::Scalar, 2 * scalar + assert_equal 10, scalar * 2 + assert_instance_of ActiveSupport::Duration::Scalar, scalar * 2 + assert_equal 10, 2.seconds * scalar + assert_instance_of ActiveSupport::Duration, 2.seconds * scalar + assert_equal 10, scalar * 2.seconds + assert_instance_of ActiveSupport::Duration, scalar * 2.seconds + + exception = assert_raises(TypeError) do + scalar * "foo" + end + + assert_equal "no implicit conversion of String into ActiveSupport::Duration::Scalar", exception.message + end + + def test_scalar_divide + scalar = ActiveSupport::Duration::Scalar.new(10) + + assert_equal 10, 100 / scalar + assert_instance_of ActiveSupport::Duration::Scalar, 100 / scalar + assert_equal 5, scalar / 2 + assert_instance_of ActiveSupport::Duration::Scalar, scalar / 2 + assert_equal 10, 100.seconds / scalar + assert_instance_of ActiveSupport::Duration, 2.seconds * scalar + assert_equal 5, scalar / 2.seconds + assert_instance_of ActiveSupport::Duration, scalar / 2.seconds + + exception = assert_raises(TypeError) do + scalar / "foo" + end + + assert_equal "no implicit conversion of String into ActiveSupport::Duration::Scalar", exception.message + end + def test_twelve_months_equals_one_year assert_equal 12.months, 1.year end diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 05813ad388..18da5fcf5f 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -8,31 +8,6 @@ require "active_support/core_ext/object/deep_dup" require "active_support/inflections" class HashExtTest < ActiveSupport::TestCase - class IndifferentHash < ActiveSupport::HashWithIndifferentAccess - end - - class SubclassingArray < Array - end - - class SubclassingHash < Hash - end - - class NonIndifferentHash < Hash - def nested_under_indifferent_access - self - end - end - - class HashByConversion - def initialize(hash) - @hash = hash - end - - def to_hash - @hash - end - end - def setup @strings = { "a" => 1, "b" => 2 } @nested_strings = { "a" => { "b" => { "c" => 3 } } } @@ -262,461 +237,6 @@ class HashExtTest < ActiveSupport::TestCase assert_equal({ "a" => { b: { "c" => 3 } } }, @nested_mixed) 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 - end - - def test_deep_symbolize_keys_for_hash_with_indifferent_access - assert_instance_of Hash, @nested_symbols.with_indifferent_access.deep_symbolize_keys - assert_equal @nested_symbols, @nested_symbols.with_indifferent_access.deep_symbolize_keys - assert_equal @nested_symbols, @nested_strings.with_indifferent_access.deep_symbolize_keys - assert_equal @nested_symbols, @nested_mixed.with_indifferent_access.deep_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_deep_symbolize_keys_bang_for_hash_with_indifferent_access - assert_raise(NoMethodError) { @nested_symbols.with_indifferent_access.deep_dup.deep_symbolize_keys! } - assert_raise(NoMethodError) { @nested_strings.with_indifferent_access.deep_dup.deep_symbolize_keys! } - assert_raise(NoMethodError) { @nested_mixed.with_indifferent_access.deep_dup.deep_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_deep_symbolize_keys_preserves_keys_that_cant_be_symbolized_for_hash_with_indifferent_access - assert_equal @nested_illegal_symbols, @nested_illegal_symbols.with_indifferent_access.deep_symbolize_keys - assert_raise(NoMethodError) { @nested_illegal_symbols.with_indifferent_access.deep_dup.deep_symbolize_keys! } - end - - def test_symbolize_keys_preserves_integer_keys_for_hash_with_indifferent_access - assert_equal @integers, @integers.with_indifferent_access.symbolize_keys - assert_raise(NoMethodError) { @integers.with_indifferent_access.dup.symbolize_keys! } - end - - def test_deep_symbolize_keys_preserves_integer_keys_for_hash_with_indifferent_access - assert_equal @nested_integers, @nested_integers.with_indifferent_access.deep_symbolize_keys - assert_raise(NoMethodError) { @nested_integers.with_indifferent_access.deep_dup.deep_symbolize_keys! } - 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_deep_stringify_keys_for_hash_with_indifferent_access - assert_instance_of ActiveSupport::HashWithIndifferentAccess, @nested_symbols.with_indifferent_access.deep_stringify_keys - assert_equal @nested_strings, @nested_symbols.with_indifferent_access.deep_stringify_keys - assert_equal @nested_strings, @nested_strings.with_indifferent_access.deep_stringify_keys - assert_equal @nested_strings, @nested_mixed.with_indifferent_access.deep_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! - end - - def test_deep_stringify_keys_bang_for_hash_with_indifferent_access - assert_instance_of ActiveSupport::HashWithIndifferentAccess, @nested_symbols.with_indifferent_access.dup.deep_stringify_keys! - assert_equal @nested_strings, @nested_symbols.with_indifferent_access.deep_dup.deep_stringify_keys! - assert_equal @nested_strings, @nested_strings.with_indifferent_access.deep_dup.deep_stringify_keys! - assert_equal @nested_strings, @nested_mixed.with_indifferent_access.deep_dup.deep_stringify_keys! - end - - def test_nested_under_indifferent_access - foo = { "foo" => SubclassingHash.new.tap { |h| h["bar"] = "baz" } }.with_indifferent_access - assert_kind_of ActiveSupport::HashWithIndifferentAccess, foo["foo"] - - foo = { "foo" => NonIndifferentHash.new.tap { |h| h["bar"] = "baz" } }.with_indifferent_access - assert_kind_of NonIndifferentHash, foo["foo"] - - foo = { "foo" => IndifferentHash.new.tap { |h| h["bar"] = "baz" } }.with_indifferent_access - assert_kind_of IndifferentHash, foo["foo"] - end - - def test_indifferent_assorted - @strings = @strings.with_indifferent_access - @symbols = @symbols.with_indifferent_access - @mixed = @mixed.with_indifferent_access - - assert_equal "a", @strings.__send__(:convert_key, :a) - - assert_equal 1, @strings.fetch("a") - assert_equal 1, @strings.fetch(:a.to_s) - assert_equal 1, @strings.fetch(:a) - - hashes = { :@strings => @strings, :@symbols => @symbols, :@mixed => @mixed } - method_map = { '[]': 1, fetch: 1, values_at: [1], - has_key?: true, include?: true, key?: true, - member?: true } - - hashes.each do |name, hash| - method_map.sort_by(&:to_s).each do |meth, expected| - assert_equal(expected, hash.__send__(meth, "a"), - "Calling #{name}.#{meth} 'a'") - assert_equal(expected, hash.__send__(meth, :a), - "Calling #{name}.#{meth} :a") - end - end - - assert_equal [1, 2], @strings.values_at("a", "b") - assert_equal [1, 2], @strings.values_at(:a, :b) - assert_equal [1, 2], @symbols.values_at("a", "b") - assert_equal [1, 2], @symbols.values_at(:a, :b) - assert_equal [1, 2], @mixed.values_at("a", "b") - assert_equal [1, 2], @mixed.values_at(:a, :b) - end - - def test_indifferent_reading - hash = HashWithIndifferentAccess.new - hash["a"] = 1 - hash["b"] = true - hash["c"] = false - hash["d"] = nil - - assert_equal 1, hash[:a] - assert_equal true, hash[:b] - assert_equal false, hash[:c] - assert_nil hash[:d] - assert_nil hash[:e] - end - - def test_indifferent_reading_with_nonnil_default - hash = HashWithIndifferentAccess.new(1) - hash["a"] = 1 - hash["b"] = true - hash["c"] = false - hash["d"] = nil - - assert_equal 1, hash[:a] - assert_equal true, hash[:b] - assert_equal false, hash[:c] - assert_nil hash[:d] - assert_equal 1, hash[:e] - end - - def test_indifferent_writing - hash = HashWithIndifferentAccess.new - hash[:a] = 1 - hash["b"] = 2 - hash[3] = 3 - - assert_equal 1, hash["a"] - assert_equal 2, hash["b"] - assert_equal 1, hash[:a] - assert_equal 2, hash[:b] - assert_equal 3, hash[3] - end - - def test_indifferent_update - hash = HashWithIndifferentAccess.new - hash[:a] = "a" - hash["b"] = "b" - - updated_with_strings = hash.update(@strings) - updated_with_symbols = hash.update(@symbols) - updated_with_mixed = hash.update(@mixed) - - assert_equal 1, updated_with_strings[:a] - assert_equal 1, updated_with_strings["a"] - assert_equal 2, updated_with_strings["b"] - - assert_equal 1, updated_with_symbols[:a] - assert_equal 2, updated_with_symbols["b"] - assert_equal 2, updated_with_symbols[:b] - - assert_equal 1, updated_with_mixed[:a] - assert_equal 2, updated_with_mixed["b"] - - assert [updated_with_strings, updated_with_symbols, updated_with_mixed].all? { |h| h.keys.size == 2 } - end - - def test_update_with_to_hash_conversion - hash = HashWithIndifferentAccess.new - hash.update HashByConversion.new(a: 1) - assert_equal 1, hash["a"] - end - - def test_indifferent_merging - hash = HashWithIndifferentAccess.new - hash[:a] = "failure" - hash["b"] = "failure" - - other = { "a" => 1, :b => 2 } - - merged = hash.merge(other) - - assert_equal HashWithIndifferentAccess, merged.class - assert_equal 1, merged[:a] - assert_equal 2, merged["b"] - - hash.update(other) - - assert_equal 1, hash[:a] - assert_equal 2, hash["b"] - end - - def test_merge_with_to_hash_conversion - hash = HashWithIndifferentAccess.new - merged = hash.merge HashByConversion.new(a: 1) - assert_equal 1, merged["a"] - end - - def test_indifferent_replace - hash = HashWithIndifferentAccess.new - hash[:a] = 42 - - replaced = hash.replace(b: 12) - - assert hash.key?("b") - assert !hash.key?(:a) - assert_equal 12, hash[:b] - assert_same hash, replaced - end - - def test_replace_with_to_hash_conversion - hash = HashWithIndifferentAccess.new - hash[:a] = 42 - - replaced = hash.replace(HashByConversion.new(b: 12)) - - assert hash.key?("b") - assert !hash.key?(:a) - assert_equal 12, hash[:b] - assert_same hash, replaced - end - - def test_indifferent_merging_with_block - hash = HashWithIndifferentAccess.new - hash[:a] = 1 - hash["b"] = 3 - - other = { "a" => 4, :b => 2, "c" => 10 } - - merged = hash.merge(other) { |key, old, new| old > new ? old : new } - - assert_equal HashWithIndifferentAccess, merged.class - assert_equal 4, merged[:a] - assert_equal 3, merged["b"] - assert_equal 10, merged[:c] - - other_indifferent = HashWithIndifferentAccess.new("a" => 9, :b => 2) - - merged = hash.merge(other_indifferent) { |key, old, new| old + new } - - assert_equal HashWithIndifferentAccess, merged.class - assert_equal 10, merged[:a] - assert_equal 5, merged[:b] - end - - def test_indifferent_reverse_merging - hash = HashWithIndifferentAccess.new key: :old_value - hash.reverse_merge! key: :new_value - assert_equal :old_value, hash[:key] - - hash = HashWithIndifferentAccess.new("some" => "value", "other" => "value") - hash.reverse_merge!(some: "noclobber", another: "clobber") - assert_equal "value", hash[:some] - assert_equal "clobber", hash[:another] - end - - def test_indifferent_deleting - get_hash = proc { { a: "foo" }.with_indifferent_access } - hash = get_hash.call - assert_equal "foo", hash.delete(:a) - assert_nil hash.delete(:a) - hash = get_hash.call - assert_equal "foo", hash.delete("a") - assert_nil hash.delete("a") - end - - def test_indifferent_select - hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).select { |k, v| v == 1 } - - assert_equal({ "a" => 1 }, hash) - assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash - end - - def test_indifferent_select_returns_enumerator - enum = ActiveSupport::HashWithIndifferentAccess.new(@strings).select - assert_instance_of Enumerator, enum - end - - def test_indifferent_select_returns_a_hash_when_unchanged - hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).select { |k, v| true } - - assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash - end - - def test_indifferent_select_bang - indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@strings) - indifferent_strings.select! { |k, v| v == 1 } - - assert_equal({ "a" => 1 }, indifferent_strings) - assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings - end - - def test_indifferent_reject - hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).reject { |k, v| v != 1 } - - assert_equal({ "a" => 1 }, hash) - assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash - end - - def test_indifferent_reject_returns_enumerator - enum = ActiveSupport::HashWithIndifferentAccess.new(@strings).reject - assert_instance_of Enumerator, enum - end - - def test_indifferent_reject_bang - indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@strings) - indifferent_strings.reject! { |k, v| v != 1 } - - assert_equal({ "a" => 1 }, indifferent_strings) - assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings - end - - def test_indifferent_compact - hash_contain_nil_value = @strings.merge("z" => nil) - hash = ActiveSupport::HashWithIndifferentAccess.new(hash_contain_nil_value) - compacted_hash = hash.compact - - assert_equal(@strings, compacted_hash) - assert_equal(hash_contain_nil_value, hash) - assert_instance_of ActiveSupport::HashWithIndifferentAccess, compacted_hash - end - - def test_indifferent_to_hash - # Should convert to a Hash with String keys. - assert_equal @strings, @mixed.with_indifferent_access.to_hash - - # Should preserve the default value. - mixed_with_default = @mixed.dup - mixed_with_default.default = "1234" - roundtrip = mixed_with_default.with_indifferent_access.to_hash - assert_equal @strings, roundtrip - assert_equal "1234", roundtrip.default - - # Ensure nested hashes are not HashWithIndiffereneAccess - new_to_hash = @nested_mixed.with_indifferent_access.to_hash - assert_not new_to_hash.instance_of?(HashWithIndifferentAccess) - assert_not new_to_hash["a"].instance_of?(HashWithIndifferentAccess) - assert_not new_to_hash["a"]["b"].instance_of?(HashWithIndifferentAccess) - end - - def test_lookup_returns_the_same_object_that_is_stored_in_hash_indifferent_access - hash = HashWithIndifferentAccess.new { |h, k| h[k] = [] } - hash[:a] << 1 - - assert_equal [1], hash[:a] - end - - def test_with_indifferent_access_has_no_side_effects_on_existing_hash - hash = { content: [{ :foo => :bar, "bar" => "baz" }] } - hash.with_indifferent_access - - assert_equal [:foo, "bar"], hash[:content].first.keys - end - - def test_indifferent_hash_with_array_of_hashes - hash = { "urls" => { "url" => [ { "address" => "1" }, { "address" => "2" } ] } }.with_indifferent_access - assert_equal "1", hash[:urls][:url].first[:address] - - hash = hash.to_hash - assert_not hash.instance_of?(HashWithIndifferentAccess) - assert_not hash["urls"].instance_of?(HashWithIndifferentAccess) - assert_not hash["urls"]["url"].first.instance_of?(HashWithIndifferentAccess) - end - - def test_should_preserve_array_subclass_when_value_is_array - array = SubclassingArray.new - array << { "address" => "1" } - hash = { "urls" => { "url" => array } }.with_indifferent_access - assert_equal SubclassingArray, hash[:urls][:url].class - end - - def test_should_preserve_array_class_when_hash_value_is_frozen_array - array = SubclassingArray.new - array << { "address" => "1" } - hash = { "urls" => { "url" => array.freeze } }.with_indifferent_access - assert_equal SubclassingArray, hash[:urls][:url].class - end - - def test_stringify_and_symbolize_keys_on_indifferent_preserves_hash - h = HashWithIndifferentAccess.new - h[:first] = 1 - h = h.stringify_keys - assert_equal 1, h["first"] - h = HashWithIndifferentAccess.new - h["first"] = 1 - h = h.symbolize_keys - assert_equal 1, h[:first] - end - - def test_deep_stringify_and_deep_symbolize_keys_on_indifferent_preserves_hash - h = HashWithIndifferentAccess.new - h[:first] = 1 - h = h.deep_stringify_keys - assert_equal 1, h["first"] - h = HashWithIndifferentAccess.new - h["first"] = 1 - h = h.deep_symbolize_keys - assert_equal 1, h[:first] - end - - def test_to_options_on_indifferent_preserves_hash - h = HashWithIndifferentAccess.new - h["first"] = 1 - h.to_options! - assert_equal 1, h["first"] - end - - def test_to_options_on_indifferent_preserves_works_as_hash_with_dup - h = HashWithIndifferentAccess.new(a: { b: "b" }) - dup = h.dup - - dup[:a][:c] = "c" - assert_equal "c", h[:a][:c] - end - - def test_indifferent_sub_hashes - h = { "user" => { "id" => 5 } }.with_indifferent_access - ["user", :user].each { |user| [:id, "id"].each { |id| assert_equal 5, h[user][id], "h[#{user.inspect}][#{id.inspect}] should be 5" } } - - h = { user: { id: 5 } }.with_indifferent_access - ["user", :user].each { |user| [:id, "id"].each { |id| assert_equal 5, h[user][id], "h[#{user.inspect}][#{id.inspect}] should be 5" } } - end - - def test_indifferent_duplication - # Should preserve default value - h = HashWithIndifferentAccess.new - h.default = "1234" - assert_equal h.default, h.dup.default - - # Should preserve class for subclasses - h = IndifferentHash.new - assert_equal h.class, h.dup.class - end - - def test_nested_dig_indifferent_access - skip if RUBY_VERSION < "2.3.0" - data = { "this" => { "views" => 1234 } }.with_indifferent_access - assert_equal 1234, data.dig(:this, :views) - end - def test_assert_valid_keys assert_nothing_raised do { failure: "stuff", funny: "business" }.assert_valid_keys([ :failure, :funny ]) @@ -749,12 +269,6 @@ class HashExtTest < ActiveSupport::TestCase assert_equal "Unknown key: :failore. Valid keys are: :failure", exception.message end - def test_assorted_keys_not_stringified - original = { Object.new => 2, 1 => 2, [] => true } - indiff = original.with_indifferent_access - assert(!indiff.keys.any? { |k| k.kind_of? String }, "A key was converted to a string!") - end - def test_deep_merge hash_1 = { a: "a", b: "b", c: { c1: "c1", c2: "c2", c3: { d1: "d1" } } } hash_2 = { a: 1, c: { c1: 2, c3: { d2: "d2" } } } @@ -785,37 +299,6 @@ class HashExtTest < ActiveSupport::TestCase assert_equal expected, hash_1 end - def test_deep_merge_on_indifferent_access - hash_1 = HashWithIndifferentAccess.new(a: "a", b: "b", c: { c1: "c1", c2: "c2", c3: { d1: "d1" } }) - hash_2 = HashWithIndifferentAccess.new(a: 1, c: { c1: 2, c3: { d2: "d2" } }) - hash_3 = { a: 1, c: { c1: 2, c3: { d2: "d2" } } } - expected = { "a" => 1, "b" => "b", "c" => { "c1" => 2, "c2" => "c2", "c3" => { "d1" => "d1", "d2" => "d2" } } } - assert_equal expected, hash_1.deep_merge(hash_2) - assert_equal expected, hash_1.deep_merge(hash_3) - - hash_1.deep_merge!(hash_2) - assert_equal expected, hash_1 - end - - def test_store_on_indifferent_access - hash = HashWithIndifferentAccess.new - hash.store(:test1, 1) - hash.store("test1", 11) - hash[:test2] = 2 - hash["test2"] = 22 - expected = { "test1" => 11, "test2" => 22 } - assert_equal expected, hash - end - - def test_constructor_on_indifferent_access - hash = HashWithIndifferentAccess[:foo, 1] - assert_equal 1, hash[:foo] - assert_equal 1, hash["foo"] - hash[:foo] = 3 - assert_equal 3, hash[:foo] - assert_equal 3, hash["foo"] - end - def test_reverse_merge defaults = { a: "x", b: "y", c: 10 }.freeze options = { a: 1, b: 2 } @@ -836,6 +319,21 @@ class HashExtTest < ActiveSupport::TestCase assert_equal expected, merged end + def test_with_defaults_aliases_reverse_merge + defaults = { a: "x", b: "y", c: 10 }.freeze + options = { a: 1, b: 2 } + expected = { a: 1, b: 2, c: 10 } + + # Should be an alias for reverse_merge + assert_equal expected, options.with_defaults(defaults) + assert_not_equal expected, options + + # Should be an alias for reverse_merge! + merged = options.dup + assert_equal expected, merged.with_defaults!(defaults) + assert_equal expected, merged + end + def test_slice original = { a: "x", b: "y", c: 10 } expected = { a: "x", b: "y" } @@ -878,38 +376,6 @@ class HashExtTest < ActiveSupport::TestCase assert_equal expected, original.slice(*[:a, :b]) end - def test_indifferent_slice - original = { a: "x", b: "y", c: 10 }.with_indifferent_access - expected = { a: "x", b: "y" }.with_indifferent_access - - [["a", "b"], [:a, :b]].each do |keys| - # Should return a new hash with only the given keys. - assert_equal expected, original.slice(*keys), keys.inspect - assert_not_equal expected, original - end - end - - def test_indifferent_slice_inplace - original = { a: "x", b: "y", c: 10 }.with_indifferent_access - expected = { c: 10 }.with_indifferent_access - - [["a", "b"], [:a, :b]].each do |keys| - # Should replace the hash with only the given keys. - copy = original.dup - assert_equal expected, copy.slice!(*keys) - end - end - - def test_indifferent_slice_access_with_symbols - original = { "login" => "bender", "password" => "shiny", "stuff" => "foo" } - original = original.with_indifferent_access - - slice = original.slice(:login, :password) - - assert_equal "bender", slice[:login] - assert_equal "bender", slice["login"] - end - def test_slice_bang_does_not_override_default hash = Hash.new(0) hash.update(a: 1, b: 2) @@ -947,18 +413,6 @@ class HashExtTest < ActiveSupport::TestCase assert_nil extracted[:x] end - def test_indifferent_extract - original = { :a => 1, "b" => 2, :c => 3, "d" => 4 }.with_indifferent_access - expected = { a: 1, b: 2 }.with_indifferent_access - remaining = { c: 3, d: 4 }.with_indifferent_access - - [["a", "b"], [:a, :b]].each do |keys| - copy = original.dup - assert_equal expected, copy.extract!(*keys) - assert_equal remaining, copy - end - end - def test_except original = { a: "x", b: "y", c: 10 } expected = { a: "x", b: "y" } @@ -1030,54 +484,6 @@ class HashExtTest < ActiveSupport::TestCase assert_nil(h.compact!) assert_equal(@symbols, h) end - - def test_new_with_to_hash_conversion - hash = HashWithIndifferentAccess.new(HashByConversion.new(a: 1)) - assert hash.key?("a") - assert_equal 1, hash[:a] - end - - def test_dup_with_default_proc - hash = HashWithIndifferentAccess.new - hash.default_proc = proc { |h, v| raise "walrus" } - assert_nothing_raised { hash.dup } - end - - def test_dup_with_default_proc_sets_proc - hash = HashWithIndifferentAccess.new - hash.default_proc = proc { |h, k| k + 1 } - new_hash = hash.dup - - assert_equal 3, new_hash[2] - - new_hash.default = 2 - assert_equal 2, new_hash[:non_existent] - end - - def test_to_hash_with_raising_default_proc - hash = HashWithIndifferentAccess.new - hash.default_proc = proc { |h, k| raise "walrus" } - - assert_nothing_raised { hash.to_hash } - end - - def test_new_with_to_hash_conversion_copies_default - normal_hash = Hash.new(3) - normal_hash[:a] = 1 - - hash = HashWithIndifferentAccess.new(HashByConversion.new(normal_hash)) - assert_equal 1, hash[:a] - assert_equal 3, hash[:b] - end - - def test_new_with_to_hash_conversion_copies_default_proc - normal_hash = Hash.new { 1 + 2 } - normal_hash[:a] = 1 - - hash = HashWithIndifferentAccess.new(HashByConversion.new(normal_hash)) - assert_equal 1, hash[:a] - assert_equal 3, hash[:b] - end end class IWriteMyOwnXML @@ -1598,61 +1004,6 @@ class HashToXmlTest < ActiveSupport::TestCase assert_equal expected, Hash.from_trusted_xml('<product><name type="yaml">:value</name></product>') end - def test_should_use_default_proc_for_unknown_key - hash_wia = HashWithIndifferentAccess.new { 1 + 2 } - assert_equal 3, hash_wia[:new_key] - end - - def test_should_return_nil_if_no_key_is_supplied - hash_wia = HashWithIndifferentAccess.new { 1 + 2 } - assert_nil hash_wia.default - end - - def test_should_use_default_value_for_unknown_key - hash_wia = HashWithIndifferentAccess.new(3) - assert_equal 3, hash_wia[:new_key] - end - - def test_should_use_default_value_if_no_key_is_supplied - hash_wia = HashWithIndifferentAccess.new(3) - assert_equal 3, hash_wia.default - end - - def test_should_nil_if_no_default_value_is_supplied - hash_wia = HashWithIndifferentAccess.new - assert_nil hash_wia.default - end - - def test_should_return_dup_for_with_indifferent_access - hash_wia = HashWithIndifferentAccess.new - assert_equal hash_wia, hash_wia.with_indifferent_access - assert_not_same hash_wia, hash_wia.with_indifferent_access - end - - def test_allows_setting_frozen_array_values_with_indifferent_access - value = [1, 2, 3].freeze - hash = HashWithIndifferentAccess.new - hash[:key] = value - assert_equal hash[:key], value - end - - def test_should_copy_the_default_value_when_converting_to_hash_with_indifferent_access - hash = Hash.new(3) - hash_wia = hash.with_indifferent_access - assert_equal 3, hash_wia.default - end - - def test_should_copy_the_default_proc_when_converting_to_hash_with_indifferent_access - hash = Hash.new do - 2 + 1 - end - assert_equal 3, hash[:foo] - - hash_wia = hash.with_indifferent_access - assert_equal 3, hash_wia[:foo] - assert_equal 3, hash_wia[:bar] - end - # The XML builder seems to fail miserably when trying to tag something # with the same name as a Kernel method (throw, test, loop, select ...) def test_kernel_method_names_to_xml diff --git a/activesupport/test/core_ext/marshal_test.rb b/activesupport/test/core_ext/marshal_test.rb index a899f98705..cabeed2fae 100644 --- a/activesupport/test/core_ext/marshal_test.rb +++ b/activesupport/test/core_ext/marshal_test.rb @@ -19,6 +19,19 @@ class MarshalTest < ActiveSupport::TestCase end end + test "that Marshal#load still works when passed a proc" do + example_string = "test" + + example_proc = Proc.new do |o| + if o.is_a?(String) + o.capitalize! + end + end + + dumped = Marshal.dump(example_string) + assert_equal Marshal.load(dumped, example_proc), "Test" + end + test "that a missing class is autoloaded from string" do dumped = nil with_autoloading_fixtures do diff --git a/activesupport/test/core_ext/object/duplicable_test.rb b/activesupport/test/core_ext/object/duplicable_test.rb index e6f3c8aef2..68b0129980 100644 --- a/activesupport/test/core_ext/object/duplicable_test.rb +++ b/activesupport/test/core_ext/object/duplicable_test.rb @@ -4,7 +4,10 @@ require "active_support/core_ext/object/duplicable" require "active_support/core_ext/numeric/time" class DuplicableTest < ActiveSupport::TestCase - if RUBY_VERSION >= "2.4.1" + if RUBY_VERSION >= "2.5.0" + RAISE_DUP = [method(:puts)] + ALLOW_DUP = ["1", "symbol_from_string".to_sym, Object.new, /foo/, [], {}, Time.now, Class.new, Module.new, BigDecimal.new("4.56"), nil, false, true, 1, 2.3, Complex(1), Rational(1)] + elsif RUBY_VERSION >= "2.4.1" RAISE_DUP = [method(:puts), Complex(1), Rational(1)] ALLOW_DUP = ["1", "symbol_from_string".to_sym, Object.new, /foo/, [], {}, Time.now, Class.new, Module.new, BigDecimal.new("4.56"), nil, false, true, 1, 2.3] elsif RUBY_VERSION >= "2.4.0" # Due to 2.4.0 bug. This elsif cannot be removed unless we drop 2.4.0 support... diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index 5d90fa2509..a98951e889 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -1,5 +1,6 @@ require "date" require "abstract_unit" +require "timeout" require "inflector_test_cases" require "constantize_test_cases" @@ -77,6 +78,12 @@ class StringInflectionsTest < ActiveSupport::TestCase end end + def test_titleize_with_keep_id_suffix + MixtureToTitleCaseWithKeepIdSuffix.each do |before, titleized| + assert_equal(titleized, before.titleize(keep_id_suffix: true)) + end + end + def test_upcase_first assert_equal "What a Lovely Day", "what a Lovely Day".upcase_first end @@ -198,6 +205,12 @@ class StringInflectionsTest < ActiveSupport::TestCase end end + def test_humanize_with_keep_id_suffix + UnderscoreToHumanWithKeepIdSuffix.each do |underscore, human| + assert_equal(human, underscore.humanize(keep_id_suffix: true)) + end + end + def test_humanize_with_html_escape assert_equal "Hello", ERB::Util.html_escape("hello").humanize end diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index a399e36dc9..bd644c8457 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -569,6 +569,11 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase Time::DATE_FORMATS.delete(:custom) end + def test_rfc3339_with_fractional_seconds + time = Time.new(1999, 12, 31, 19, 0, Rational(1, 8), -18000) + assert_equal "1999-12-31T19:00:00.125-05:00", time.rfc3339(3) + end + def test_to_date assert_equal Date.new(2005, 2, 21), Time.local(2005, 2, 21, 17, 44, 30).to_date end @@ -910,6 +915,37 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase def test_all_year assert_equal Time.local(2011, 1, 1, 0, 0, 0)..Time.local(2011, 12, 31, 23, 59, 59, Rational(999999999, 1000)), Time.local(2011, 6, 7, 10, 10, 10).all_year end + + def test_rfc3339_parse + time = Time.rfc3339("1999-12-31T19:00:00.125-05:00") + + assert_equal 1999, time.year + assert_equal 12, time.month + assert_equal 31, time.day + assert_equal 19, time.hour + assert_equal 0, time.min + assert_equal 0, time.sec + assert_equal 125000, time.usec + assert_equal(-18000, time.utc_offset) + + exception = assert_raises(ArgumentError) do + Time.rfc3339("1999-12-31") + end + + assert_equal "invalid date", exception.message + + exception = assert_raises(ArgumentError) do + Time.rfc3339("1999-12-31T19:00:00") + end + + assert_equal "invalid date", exception.message + + exception = assert_raises(ArgumentError) do + Time.rfc3339("foobar") + end + + assert_equal "invalid date", exception.message + end end class TimeExtMarshalingTest < ActiveSupport::TestCase diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index ab5ec98642..c3afe68378 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -144,6 +144,16 @@ class TimeWithZoneTest < ActiveSupport::TestCase assert_equal "1999-12-31T19:00:00-05:00", @twz.xmlschema(nil) end + def test_iso8601_with_fractional_seconds + @twz += Rational(1, 8) + assert_equal "1999-12-31T19:00:00.125-05:00", @twz.iso8601(3) + end + + def test_rfc3339_with_fractional_seconds + @twz += Rational(1, 8) + assert_equal "1999-12-31T19:00:00.125-05:00", @twz.rfc3339(3) + end + def test_to_yaml yaml = <<-EOF.strip_heredoc --- !ruby/object:ActiveSupport::TimeWithZone @@ -421,11 +431,29 @@ class TimeWithZoneTest < ActiveSupport::TestCase assert_equal time, Time.at(time) end - def test_to_time - with_env_tz "US/Eastern" do - assert_equal Time, @twz.to_time.class - assert_equal Time.local(1999, 12, 31, 19), @twz.to_time - assert_equal Time.local(1999, 12, 31, 19).utc_offset, @twz.to_time.utc_offset + def test_to_time_with_preserve_timezone + with_preserve_timezone(true) do + with_env_tz "US/Eastern" do + time = @twz.to_time + + assert_equal Time, time.class + assert_equal time.object_id, @twz.to_time.object_id + assert_equal Time.local(1999, 12, 31, 19), time + assert_equal Time.local(1999, 12, 31, 19).utc_offset, time.utc_offset + end + end + end + + def test_to_time_without_preserve_timezone + with_preserve_timezone(false) do + with_env_tz "US/Eastern" do + time = @twz.to_time + + assert_equal Time, time.class + assert_equal time.object_id, @twz.to_time.object_id + assert_equal Time.local(1999, 12, 31, 19), time + assert_equal Time.local(1999, 12, 31, 19).utc_offset, time.utc_offset + end end end @@ -507,6 +535,8 @@ class TimeWithZoneTest < ActiveSupport::TestCase assert_nothing_raised do @twz.period @twz.time + @twz.to_datetime + @twz.to_time end end diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb index 5f72fbf662..36d1ef0849 100644 --- a/activesupport/test/deprecation_test.rb +++ b/activesupport/test/deprecation_test.rb @@ -35,6 +35,18 @@ class Deprecatee A = ActiveSupport::Deprecation::DeprecatedConstantProxy.new("Deprecatee::A", "Deprecatee::B::C") end +class DeprecateeWithAccessor + include ActiveSupport::Deprecation::DeprecatedConstantAccessor + + module B + C = 1 + end + deprecate_constant "A", "DeprecateeWithAccessor::B::C" + + class NewException < StandardError; end + deprecate_constant "OldException", "DeprecateeWithAccessor::NewException" +end + class DeprecationTest < ActiveSupport::TestCase include ActiveSupport::Testing::Stream @@ -162,6 +174,17 @@ class DeprecationTest < ActiveSupport::TestCase assert_not_deprecated { assert_equal Deprecatee::B::C.class, Deprecatee::A.class } end + def test_deprecated_constant_accessor + assert_not_deprecated { DeprecateeWithAccessor::B::C } + assert_deprecated("DeprecateeWithAccessor::A") { assert_equal DeprecateeWithAccessor::B::C, DeprecateeWithAccessor::A } + end + + def test_deprecated_constant_accessor_exception + raise DeprecateeWithAccessor::NewException.new("Test") + rescue DeprecateeWithAccessor::OldException => e + assert_kind_of DeprecateeWithAccessor::NewException, e + end + def test_assert_deprecated_raises_when_method_not_deprecated assert_raises(Minitest::Assertion) { assert_deprecated { @dtc.not } } end diff --git a/activesupport/test/gzip_test.rb b/activesupport/test/gzip_test.rb index f51d3cdf65..33e0cd2a04 100644 --- a/activesupport/test/gzip_test.rb +++ b/activesupport/test/gzip_test.rb @@ -30,4 +30,14 @@ class GzipTest < ActiveSupport::TestCase assert_equal true, (gzipped_by_best_compression.bytesize < gzipped_by_speed.bytesize) end + + def test_decompress_checks_crc + compressed = ActiveSupport::Gzip.compress("Hello World") + first_crc_byte_index = compressed.bytesize - 8 + compressed.setbyte(first_crc_byte_index, compressed.getbyte(first_crc_byte_index) ^ 0xff) + + assert_raises(Zlib::GzipFile::CRCError) do + ActiveSupport::Gzip.decompress(compressed) + end + end end diff --git a/activesupport/test/hash_with_indifferent_access_test.rb b/activesupport/test/hash_with_indifferent_access_test.rb new file mode 100644 index 0000000000..ee862320e8 --- /dev/null +++ b/activesupport/test/hash_with_indifferent_access_test.rb @@ -0,0 +1,734 @@ +require "abstract_unit" +require "active_support/core_ext/hash" +require "bigdecimal" +require "active_support/core_ext/string/access" +require "active_support/ordered_hash" +require "active_support/core_ext/object/conversions" +require "active_support/core_ext/object/deep_dup" +require "active_support/inflections" + +class HashWithIndifferentAccessTest < ActiveSupport::TestCase + HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess + + class IndifferentHash < ActiveSupport::HashWithIndifferentAccess + end + + class SubclassingArray < Array + end + + class SubclassingHash < Hash + end + + class NonIndifferentHash < Hash + def nested_under_indifferent_access + self + end + end + + class HashByConversion + def initialize(hash) + @hash = hash + end + + def to_hash + @hash + end + end + + def setup + @strings = { "a" => 1, "b" => 2 } + @nested_strings = { "a" => { "b" => { "c" => 3 } } } + @symbols = { a: 1, b: 2 } + @nested_symbols = { a: { b: { c: 3 } } } + @mixed = { :a => 1, "b" => 2 } + @nested_mixed = { "a" => { b: { "c" => 3 } } } + @integers = { 0 => 1, 1 => 2 } + @nested_integers = { 0 => { 1 => { 2 => 3 } } } + @illegal_symbols = { [] => 3 } + @nested_illegal_symbols = { [] => { [] => 3 } } + 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 + end + + def test_deep_symbolize_keys_for_hash_with_indifferent_access + assert_instance_of Hash, @nested_symbols.with_indifferent_access.deep_symbolize_keys + assert_equal @nested_symbols, @nested_symbols.with_indifferent_access.deep_symbolize_keys + assert_equal @nested_symbols, @nested_strings.with_indifferent_access.deep_symbolize_keys + assert_equal @nested_symbols, @nested_mixed.with_indifferent_access.deep_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_deep_symbolize_keys_bang_for_hash_with_indifferent_access + assert_raise(NoMethodError) { @nested_symbols.with_indifferent_access.deep_dup.deep_symbolize_keys! } + assert_raise(NoMethodError) { @nested_strings.with_indifferent_access.deep_dup.deep_symbolize_keys! } + assert_raise(NoMethodError) { @nested_mixed.with_indifferent_access.deep_dup.deep_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_deep_symbolize_keys_preserves_keys_that_cant_be_symbolized_for_hash_with_indifferent_access + assert_equal @nested_illegal_symbols, @nested_illegal_symbols.with_indifferent_access.deep_symbolize_keys + assert_raise(NoMethodError) { @nested_illegal_symbols.with_indifferent_access.deep_dup.deep_symbolize_keys! } + end + + def test_symbolize_keys_preserves_integer_keys_for_hash_with_indifferent_access + assert_equal @integers, @integers.with_indifferent_access.symbolize_keys + assert_raise(NoMethodError) { @integers.with_indifferent_access.dup.symbolize_keys! } + end + + def test_deep_symbolize_keys_preserves_integer_keys_for_hash_with_indifferent_access + assert_equal @nested_integers, @nested_integers.with_indifferent_access.deep_symbolize_keys + assert_raise(NoMethodError) { @nested_integers.with_indifferent_access.deep_dup.deep_symbolize_keys! } + 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_deep_stringify_keys_for_hash_with_indifferent_access + assert_instance_of ActiveSupport::HashWithIndifferentAccess, @nested_symbols.with_indifferent_access.deep_stringify_keys + assert_equal @nested_strings, @nested_symbols.with_indifferent_access.deep_stringify_keys + assert_equal @nested_strings, @nested_strings.with_indifferent_access.deep_stringify_keys + assert_equal @nested_strings, @nested_mixed.with_indifferent_access.deep_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! + end + + def test_deep_stringify_keys_bang_for_hash_with_indifferent_access + assert_instance_of ActiveSupport::HashWithIndifferentAccess, @nested_symbols.with_indifferent_access.dup.deep_stringify_keys! + assert_equal @nested_strings, @nested_symbols.with_indifferent_access.deep_dup.deep_stringify_keys! + assert_equal @nested_strings, @nested_strings.with_indifferent_access.deep_dup.deep_stringify_keys! + assert_equal @nested_strings, @nested_mixed.with_indifferent_access.deep_dup.deep_stringify_keys! + end + + def test_nested_under_indifferent_access + foo = { "foo" => SubclassingHash.new.tap { |h| h["bar"] = "baz" } }.with_indifferent_access + assert_kind_of ActiveSupport::HashWithIndifferentAccess, foo["foo"] + + foo = { "foo" => NonIndifferentHash.new.tap { |h| h["bar"] = "baz" } }.with_indifferent_access + assert_kind_of NonIndifferentHash, foo["foo"] + + foo = { "foo" => IndifferentHash.new.tap { |h| h["bar"] = "baz" } }.with_indifferent_access + assert_kind_of IndifferentHash, foo["foo"] + end + + def test_indifferent_assorted + @strings = @strings.with_indifferent_access + @symbols = @symbols.with_indifferent_access + @mixed = @mixed.with_indifferent_access + + assert_equal "a", @strings.__send__(:convert_key, :a) + + assert_equal 1, @strings.fetch("a") + assert_equal 1, @strings.fetch(:a.to_s) + assert_equal 1, @strings.fetch(:a) + + hashes = { :@strings => @strings, :@symbols => @symbols, :@mixed => @mixed } + method_map = { '[]': 1, fetch: 1, values_at: [1], + has_key?: true, include?: true, key?: true, + member?: true } + + hashes.each do |name, hash| + method_map.sort_by(&:to_s).each do |meth, expected| + assert_equal(expected, hash.__send__(meth, "a"), + "Calling #{name}.#{meth} 'a'") + assert_equal(expected, hash.__send__(meth, :a), + "Calling #{name}.#{meth} :a") + end + end + + assert_equal [1, 2], @strings.values_at("a", "b") + assert_equal [1, 2], @strings.values_at(:a, :b) + assert_equal [1, 2], @symbols.values_at("a", "b") + assert_equal [1, 2], @symbols.values_at(:a, :b) + assert_equal [1, 2], @mixed.values_at("a", "b") + assert_equal [1, 2], @mixed.values_at(:a, :b) + end + + def test_indifferent_reading + hash = HashWithIndifferentAccess.new + hash["a"] = 1 + hash["b"] = true + hash["c"] = false + hash["d"] = nil + + assert_equal 1, hash[:a] + assert_equal true, hash[:b] + assert_equal false, hash[:c] + assert_nil hash[:d] + assert_nil hash[:e] + end + + def test_indifferent_reading_with_nonnil_default + hash = HashWithIndifferentAccess.new(1) + hash["a"] = 1 + hash["b"] = true + hash["c"] = false + hash["d"] = nil + + assert_equal 1, hash[:a] + assert_equal true, hash[:b] + assert_equal false, hash[:c] + assert_nil hash[:d] + assert_equal 1, hash[:e] + end + + def test_indifferent_writing + hash = HashWithIndifferentAccess.new + hash[:a] = 1 + hash["b"] = 2 + hash[3] = 3 + + assert_equal 1, hash["a"] + assert_equal 2, hash["b"] + assert_equal 1, hash[:a] + assert_equal 2, hash[:b] + assert_equal 3, hash[3] + end + + def test_indifferent_update + hash = HashWithIndifferentAccess.new + hash[:a] = "a" + hash["b"] = "b" + + updated_with_strings = hash.update(@strings) + updated_with_symbols = hash.update(@symbols) + updated_with_mixed = hash.update(@mixed) + + assert_equal 1, updated_with_strings[:a] + assert_equal 1, updated_with_strings["a"] + assert_equal 2, updated_with_strings["b"] + + assert_equal 1, updated_with_symbols[:a] + assert_equal 2, updated_with_symbols["b"] + assert_equal 2, updated_with_symbols[:b] + + assert_equal 1, updated_with_mixed[:a] + assert_equal 2, updated_with_mixed["b"] + + assert [updated_with_strings, updated_with_symbols, updated_with_mixed].all? { |h| h.keys.size == 2 } + end + + def test_update_with_to_hash_conversion + hash = HashWithIndifferentAccess.new + hash.update HashByConversion.new(a: 1) + assert_equal 1, hash["a"] + end + + def test_indifferent_merging + hash = HashWithIndifferentAccess.new + hash[:a] = "failure" + hash["b"] = "failure" + + other = { "a" => 1, :b => 2 } + + merged = hash.merge(other) + + assert_equal HashWithIndifferentAccess, merged.class + assert_equal 1, merged[:a] + assert_equal 2, merged["b"] + + hash.update(other) + + assert_equal 1, hash[:a] + assert_equal 2, hash["b"] + end + + def test_merge_with_to_hash_conversion + hash = HashWithIndifferentAccess.new + merged = hash.merge HashByConversion.new(a: 1) + assert_equal 1, merged["a"] + end + + def test_indifferent_replace + hash = HashWithIndifferentAccess.new + hash[:a] = 42 + + replaced = hash.replace(b: 12) + + assert hash.key?("b") + assert !hash.key?(:a) + assert_equal 12, hash[:b] + assert_same hash, replaced + end + + def test_replace_with_to_hash_conversion + hash = HashWithIndifferentAccess.new + hash[:a] = 42 + + replaced = hash.replace(HashByConversion.new(b: 12)) + + assert hash.key?("b") + assert !hash.key?(:a) + assert_equal 12, hash[:b] + assert_same hash, replaced + end + + def test_indifferent_merging_with_block + hash = HashWithIndifferentAccess.new + hash[:a] = 1 + hash["b"] = 3 + + other = { "a" => 4, :b => 2, "c" => 10 } + + merged = hash.merge(other) { |key, old, new| old > new ? old : new } + + assert_equal HashWithIndifferentAccess, merged.class + assert_equal 4, merged[:a] + assert_equal 3, merged["b"] + assert_equal 10, merged[:c] + + other_indifferent = HashWithIndifferentAccess.new("a" => 9, :b => 2) + + merged = hash.merge(other_indifferent) { |key, old, new| old + new } + + assert_equal HashWithIndifferentAccess, merged.class + assert_equal 10, merged[:a] + assert_equal 5, merged[:b] + end + + def test_indifferent_reverse_merging + hash = HashWithIndifferentAccess.new key: :old_value + hash.reverse_merge! key: :new_value + assert_equal :old_value, hash[:key] + + hash = HashWithIndifferentAccess.new("some" => "value", "other" => "value") + hash.reverse_merge!(some: "noclobber", another: "clobber") + assert_equal "value", hash[:some] + assert_equal "clobber", hash[:another] + end + + def test_indifferent_with_defaults_aliases_reverse_merge + hash = HashWithIndifferentAccess.new key: :old_value + actual = hash.with_defaults key: :new_value + assert_equal :old_value, actual[:key] + + hash = HashWithIndifferentAccess.new key: :old_value + hash.with_defaults! key: :new_value + assert_equal :old_value, hash[:key] + end + + def test_indifferent_deleting + get_hash = proc { { a: "foo" }.with_indifferent_access } + hash = get_hash.call + assert_equal "foo", hash.delete(:a) + assert_nil hash.delete(:a) + hash = get_hash.call + assert_equal "foo", hash.delete("a") + assert_nil hash.delete("a") + end + + def test_indifferent_select + hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).select { |k, v| v == 1 } + + assert_equal({ "a" => 1 }, hash) + assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash + end + + def test_indifferent_select_returns_enumerator + enum = ActiveSupport::HashWithIndifferentAccess.new(@strings).select + assert_instance_of Enumerator, enum + end + + def test_indifferent_select_returns_a_hash_when_unchanged + hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).select { |k, v| true } + + assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash + end + + def test_indifferent_select_bang + indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@strings) + indifferent_strings.select! { |k, v| v == 1 } + + assert_equal({ "a" => 1 }, indifferent_strings) + assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings + end + + def test_indifferent_reject + hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).reject { |k, v| v != 1 } + + assert_equal({ "a" => 1 }, hash) + assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash + end + + def test_indifferent_reject_returns_enumerator + enum = ActiveSupport::HashWithIndifferentAccess.new(@strings).reject + assert_instance_of Enumerator, enum + end + + def test_indifferent_reject_bang + indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@strings) + indifferent_strings.reject! { |k, v| v != 1 } + + assert_equal({ "a" => 1 }, indifferent_strings) + assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings + end + + def test_indifferent_compact + hash_contain_nil_value = @strings.merge("z" => nil) + hash = ActiveSupport::HashWithIndifferentAccess.new(hash_contain_nil_value) + compacted_hash = hash.compact + + assert_equal(@strings, compacted_hash) + assert_equal(hash_contain_nil_value, hash) + assert_instance_of ActiveSupport::HashWithIndifferentAccess, compacted_hash + + empty_hash = ActiveSupport::HashWithIndifferentAccess.new + compacted_hash = empty_hash.compact + + assert_equal compacted_hash, empty_hash + + non_empty_hash = ActiveSupport::HashWithIndifferentAccess.new(foo: :bar) + compacted_hash = non_empty_hash.compact + + assert_equal compacted_hash, non_empty_hash + end + + def test_indifferent_to_hash + # Should convert to a Hash with String keys. + assert_equal @strings, @mixed.with_indifferent_access.to_hash + + # Should preserve the default value. + mixed_with_default = @mixed.dup + mixed_with_default.default = "1234" + roundtrip = mixed_with_default.with_indifferent_access.to_hash + assert_equal @strings, roundtrip + assert_equal "1234", roundtrip.default + + # Ensure nested hashes are not HashWithIndiffereneAccess + new_to_hash = @nested_mixed.with_indifferent_access.to_hash + assert_not new_to_hash.instance_of?(HashWithIndifferentAccess) + assert_not new_to_hash["a"].instance_of?(HashWithIndifferentAccess) + assert_not new_to_hash["a"]["b"].instance_of?(HashWithIndifferentAccess) + end + + def test_lookup_returns_the_same_object_that_is_stored_in_hash_indifferent_access + hash = HashWithIndifferentAccess.new { |h, k| h[k] = [] } + hash[:a] << 1 + + assert_equal [1], hash[:a] + end + + def test_with_indifferent_access_has_no_side_effects_on_existing_hash + hash = { content: [{ :foo => :bar, "bar" => "baz" }] } + hash.with_indifferent_access + + assert_equal [:foo, "bar"], hash[:content].first.keys + end + + def test_indifferent_hash_with_array_of_hashes + hash = { "urls" => { "url" => [ { "address" => "1" }, { "address" => "2" } ] } }.with_indifferent_access + assert_equal "1", hash[:urls][:url].first[:address] + + hash = hash.to_hash + assert_not hash.instance_of?(HashWithIndifferentAccess) + assert_not hash["urls"].instance_of?(HashWithIndifferentAccess) + assert_not hash["urls"]["url"].first.instance_of?(HashWithIndifferentAccess) + end + + def test_should_preserve_array_subclass_when_value_is_array + array = SubclassingArray.new + array << { "address" => "1" } + hash = { "urls" => { "url" => array } }.with_indifferent_access + assert_equal SubclassingArray, hash[:urls][:url].class + end + + def test_should_preserve_array_class_when_hash_value_is_frozen_array + array = SubclassingArray.new + array << { "address" => "1" } + hash = { "urls" => { "url" => array.freeze } }.with_indifferent_access + assert_equal SubclassingArray, hash[:urls][:url].class + end + + def test_stringify_and_symbolize_keys_on_indifferent_preserves_hash + h = HashWithIndifferentAccess.new + h[:first] = 1 + h = h.stringify_keys + assert_equal 1, h["first"] + h = HashWithIndifferentAccess.new + h["first"] = 1 + h = h.symbolize_keys + assert_equal 1, h[:first] + end + + def test_deep_stringify_and_deep_symbolize_keys_on_indifferent_preserves_hash + h = HashWithIndifferentAccess.new + h[:first] = 1 + h = h.deep_stringify_keys + assert_equal 1, h["first"] + h = HashWithIndifferentAccess.new + h["first"] = 1 + h = h.deep_symbolize_keys + assert_equal 1, h[:first] + end + + def test_to_options_on_indifferent_preserves_hash + h = HashWithIndifferentAccess.new + h["first"] = 1 + h.to_options! + assert_equal 1, h["first"] + end + + def test_to_options_on_indifferent_preserves_works_as_hash_with_dup + h = HashWithIndifferentAccess.new(a: { b: "b" }) + dup = h.dup + + dup[:a][:c] = "c" + assert_equal "c", h[:a][:c] + end + + def test_indifferent_sub_hashes + h = { "user" => { "id" => 5 } }.with_indifferent_access + ["user", :user].each { |user| [:id, "id"].each { |id| assert_equal 5, h[user][id], "h[#{user.inspect}][#{id.inspect}] should be 5" } } + + h = { user: { id: 5 } }.with_indifferent_access + ["user", :user].each { |user| [:id, "id"].each { |id| assert_equal 5, h[user][id], "h[#{user.inspect}][#{id.inspect}] should be 5" } } + end + + def test_indifferent_duplication + # Should preserve default value + h = HashWithIndifferentAccess.new + h.default = "1234" + assert_equal h.default, h.dup.default + + # Should preserve class for subclasses + h = IndifferentHash.new + assert_equal h.class, h.dup.class + end + + def test_nested_dig_indifferent_access + skip if RUBY_VERSION < "2.3.0" + data = { "this" => { "views" => 1234 } }.with_indifferent_access + assert_equal 1234, data.dig(:this, :views) + end + + def test_assorted_keys_not_stringified + original = { Object.new => 2, 1 => 2, [] => true } + indiff = original.with_indifferent_access + assert(!indiff.keys.any? { |k| k.kind_of? String }, "A key was converted to a string!") + end + + def test_deep_merge_on_indifferent_access + hash_1 = HashWithIndifferentAccess.new(a: "a", b: "b", c: { c1: "c1", c2: "c2", c3: { d1: "d1" } }) + hash_2 = HashWithIndifferentAccess.new(a: 1, c: { c1: 2, c3: { d2: "d2" } }) + hash_3 = { a: 1, c: { c1: 2, c3: { d2: "d2" } } } + expected = { "a" => 1, "b" => "b", "c" => { "c1" => 2, "c2" => "c2", "c3" => { "d1" => "d1", "d2" => "d2" } } } + assert_equal expected, hash_1.deep_merge(hash_2) + assert_equal expected, hash_1.deep_merge(hash_3) + + hash_1.deep_merge!(hash_2) + assert_equal expected, hash_1 + end + + def test_store_on_indifferent_access + hash = HashWithIndifferentAccess.new + hash.store(:test1, 1) + hash.store("test1", 11) + hash[:test2] = 2 + hash["test2"] = 22 + expected = { "test1" => 11, "test2" => 22 } + assert_equal expected, hash + end + + def test_constructor_on_indifferent_access + hash = HashWithIndifferentAccess[:foo, 1] + assert_equal 1, hash[:foo] + assert_equal 1, hash["foo"] + hash[:foo] = 3 + assert_equal 3, hash[:foo] + assert_equal 3, hash["foo"] + end + + def test_indifferent_slice + original = { a: "x", b: "y", c: 10 }.with_indifferent_access + expected = { a: "x", b: "y" }.with_indifferent_access + + [["a", "b"], [:a, :b]].each do |keys| + # Should return a new hash with only the given keys. + assert_equal expected, original.slice(*keys), keys.inspect + assert_not_equal expected, original + end + end + + def test_indifferent_slice_inplace + original = { a: "x", b: "y", c: 10 }.with_indifferent_access + expected = { c: 10 }.with_indifferent_access + + [["a", "b"], [:a, :b]].each do |keys| + # Should replace the hash with only the given keys. + copy = original.dup + assert_equal expected, copy.slice!(*keys) + end + end + + def test_indifferent_slice_access_with_symbols + original = { "login" => "bender", "password" => "shiny", "stuff" => "foo" } + original = original.with_indifferent_access + + slice = original.slice(:login, :password) + + assert_equal "bender", slice[:login] + assert_equal "bender", slice["login"] + end + + def test_indifferent_extract + original = { :a => 1, "b" => 2, :c => 3, "d" => 4 }.with_indifferent_access + expected = { a: 1, b: 2 }.with_indifferent_access + remaining = { c: 3, d: 4 }.with_indifferent_access + + [["a", "b"], [:a, :b]].each do |keys| + copy = original.dup + assert_equal expected, copy.extract!(*keys) + assert_equal remaining, copy + end + end + + def test_new_with_to_hash_conversion + hash = HashWithIndifferentAccess.new(HashByConversion.new(a: 1)) + assert hash.key?("a") + assert_equal 1, hash[:a] + end + + def test_dup_with_default_proc + hash = HashWithIndifferentAccess.new + hash.default_proc = proc { |h, v| raise "walrus" } + assert_nothing_raised { hash.dup } + end + + def test_dup_with_default_proc_sets_proc + hash = HashWithIndifferentAccess.new + hash.default_proc = proc { |h, k| k + 1 } + new_hash = hash.dup + + assert_equal 3, new_hash[2] + + new_hash.default = 2 + assert_equal 2, new_hash[:non_existent] + end + + def test_to_hash_with_raising_default_proc + hash = HashWithIndifferentAccess.new + hash.default_proc = proc { |h, k| raise "walrus" } + + assert_nothing_raised { hash.to_hash } + end + + def test_new_with_to_hash_conversion_copies_default + normal_hash = Hash.new(3) + normal_hash[:a] = 1 + + hash = HashWithIndifferentAccess.new(HashByConversion.new(normal_hash)) + assert_equal 1, hash[:a] + assert_equal 3, hash[:b] + end + + def test_new_with_to_hash_conversion_copies_default_proc + normal_hash = Hash.new { 1 + 2 } + normal_hash[:a] = 1 + + hash = HashWithIndifferentAccess.new(HashByConversion.new(normal_hash)) + assert_equal 1, hash[:a] + assert_equal 3, hash[:b] + end + + def test_inheriting_from_top_level_hash_with_indifferent_access_preserves_ancestors_chain + klass = Class.new(::HashWithIndifferentAccess) + assert_equal ActiveSupport::HashWithIndifferentAccess, klass.ancestors[1] + end + + def test_inheriting_from_hash_with_indifferent_access_properly_dumps_ivars + klass = Class.new(::HashWithIndifferentAccess) do + def initialize(*) + @foo = "bar" + super + end + end + + yaml_output = klass.new.to_yaml + + # `hash-with-ivars` was introduced in 2.0.9 (https://git.io/vyUQW) + if Gem::Version.new(Psych::VERSION) >= Gem::Version.new("2.0.9") + assert_includes yaml_output, "hash-with-ivars" + assert_includes yaml_output, "@foo: bar" + else + assert_includes yaml_output, "hash" + end + end + HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess + + def test_should_use_default_proc_for_unknown_key + hash_wia = HashWithIndifferentAccess.new { 1 + 2 } + assert_equal 3, hash_wia[:new_key] + end + + def test_should_return_nil_if_no_key_is_supplied + hash_wia = HashWithIndifferentAccess.new { 1 + 2 } + assert_nil hash_wia.default + end + + def test_should_use_default_value_for_unknown_key + hash_wia = HashWithIndifferentAccess.new(3) + assert_equal 3, hash_wia[:new_key] + end + + def test_should_use_default_value_if_no_key_is_supplied + hash_wia = HashWithIndifferentAccess.new(3) + assert_equal 3, hash_wia.default + end + + def test_should_nil_if_no_default_value_is_supplied + hash_wia = HashWithIndifferentAccess.new + assert_nil hash_wia.default + end + + def test_should_return_dup_for_with_indifferent_access + hash_wia = HashWithIndifferentAccess.new + assert_equal hash_wia, hash_wia.with_indifferent_access + assert_not_same hash_wia, hash_wia.with_indifferent_access + end + + def test_allows_setting_frozen_array_values_with_indifferent_access + value = [1, 2, 3].freeze + hash = HashWithIndifferentAccess.new + hash[:key] = value + assert_equal hash[:key], value + end + + def test_should_copy_the_default_value_when_converting_to_hash_with_indifferent_access + hash = Hash.new(3) + hash_wia = hash.with_indifferent_access + assert_equal 3, hash_wia.default + end + + def test_should_copy_the_default_proc_when_converting_to_hash_with_indifferent_access + hash = Hash.new do + 2 + 1 + end + assert_equal 3, hash[:foo] + + hash_wia = hash.with_indifferent_access + assert_equal 3, hash_wia[:foo] + assert_equal 3, hash_wia[:bar] + end +end diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb index 03d7b3fe94..14bc10513b 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -119,6 +119,13 @@ class InflectorTest < ActiveSupport::TestCase end end + MixtureToTitleCaseWithKeepIdSuffix.each_with_index do |(before, titleized), index| + define_method "test_titleize_with_keep_id_suffix_mixture_to_title_case_#{index}" do + assert_equal(titleized, ActiveSupport::Inflector.titleize(before, keep_id_suffix: true), + "mixture to TitleCase with keep_id_suffix failed for #{before}") + end + end + def test_camelize CamelToUnderscore.each do |camel, underscore| assert_equal(camel, ActiveSupport::Inflector.camelize(underscore)) @@ -324,6 +331,12 @@ class InflectorTest < ActiveSupport::TestCase end end + def test_humanize_with_keep_id_suffix + UnderscoreToHumanWithKeepIdSuffix.each do |underscore, human| + assert_equal(human, ActiveSupport::Inflector.humanize(underscore, keep_id_suffix: true)) + end + end + def test_humanize_by_rule ActiveSupport::Inflector.inflections do |inflect| inflect.human(/_cnt$/i, '\1_count') diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb index b660987d92..d61ca3fc18 100644 --- a/activesupport/test/inflector_test_cases.rb +++ b/activesupport/test/inflector_test_cases.rb @@ -248,12 +248,27 @@ module InflectorTestCases "_external_id" => "External" } + UnderscoreToHumanWithKeepIdSuffix = { + "this_is_a_string_ending_with_id" => "This is a string ending with id", + "employee_id" => "Employee id", + "employee_id_something_else" => "Employee id something else", + "underground" => "Underground", + "_id" => "Id", + "_external_id" => "External id" + } + UnderscoreToHumanWithoutCapitalize = { "employee_salary" => "employee salary", "employee_id" => "employee", "underground" => "underground" } + MixtureToTitleCaseWithKeepIdSuffix = { + "this_is_a_string_ending_with_id" => "This Is A String Ending With Id", + "EmployeeId" => "Employee Id", + "Author Id" => "Author Id" + } + MixtureToTitleCase = { "active_record" => "Active Record", "ActiveRecord" => "Active Record", @@ -271,6 +286,7 @@ module InflectorTestCases "¿por qué?" => "¿Por Qué?", "Fred’s" => "Fred’s", "Fred`s" => "Fred`s", + "this was 'fake news'" => "This Was 'Fake News'", ActiveSupport::SafeBuffer.new("confirmation num") => "Confirmation Num" } diff --git a/activesupport/test/json/encoding_test_cases.rb b/activesupport/test/json/encoding_test_cases.rb index b2f0cf3048..7e4775cec8 100644 --- a/activesupport/test/json/encoding_test_cases.rb +++ b/activesupport/test/json/encoding_test_cases.rb @@ -1,4 +1,8 @@ require "bigdecimal" +require "date" +require "time" +require "pathname" +require "uri" module JSONTest class Foo diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index 4794b55742..de111cc40e 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -215,6 +215,95 @@ class TimeZoneTest < ActiveSupport::TestCase assert_equal secs, twz.to_f end + def test_iso8601 + zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + twz = zone.iso8601("1999-12-31T19:00:00") + assert_equal Time.utc(1999, 12, 31, 19), twz.time + assert_equal Time.utc(2000), twz.utc + assert_equal zone, twz.time_zone + end + + def test_iso8601_with_fractional_seconds + zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + twz = zone.iso8601("1999-12-31T19:00:00.750") + assert_equal 750000, twz.time.usec + assert_equal Time.utc(1999, 12, 31, 19, 0, 0 + Rational(3, 4)), twz.time + assert_equal Time.utc(2000, 1, 1, 0, 0, 0 + Rational(3, 4)), twz.utc + assert_equal zone, twz.time_zone + end + + def test_iso8601_with_zone + zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + twz = zone.iso8601("1999-12-31T14:00:00-10:00") + assert_equal Time.utc(1999, 12, 31, 19), twz.time + assert_equal Time.utc(2000), twz.utc + assert_equal zone, twz.time_zone + end + + def test_iso8601_with_invalid_string + zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + + exception = assert_raises(ArgumentError) do + zone.iso8601("foobar") + end + + assert_equal "invalid date", exception.message + end + + def test_iso8601_with_missing_time_components + zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + twz = zone.iso8601("1999-12-31") + assert_equal Time.utc(1999, 12, 31, 0, 0, 0), twz.time + assert_equal Time.utc(1999, 12, 31, 5, 0, 0), twz.utc + assert_equal zone, twz.time_zone + end + + def test_iso8601_with_old_date + zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + twz = zone.iso8601("1883-12-31T19:00:00") + assert_equal [0, 0, 19, 31, 12, 1883], twz.to_a[0, 6] + assert_equal zone, twz.time_zone + end + + def test_iso8601_far_future_date_with_time_zone_offset_in_string + zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + twz = zone.iso8601("2050-12-31T19:00:00-10:00") # i.e., 2050-01-01 05:00:00 UTC + assert_equal [0, 0, 0, 1, 1, 2051], twz.to_a[0, 6] + assert_equal zone, twz.time_zone + end + + def test_iso8601_should_not_black_out_system_timezone_dst_jump + with_env_tz("EET") do + zone = ActiveSupport::TimeZone["Pacific Time (US & Canada)"] + twz = zone.iso8601("2012-03-25T03:29:00") + assert_equal [0, 29, 3, 25, 3, 2012], twz.to_a[0, 6] + end + end + + def test_iso8601_should_black_out_app_timezone_dst_jump + with_env_tz("EET") do + zone = ActiveSupport::TimeZone["Pacific Time (US & Canada)"] + twz = zone.iso8601("2012-03-11T02:29:00") + assert_equal [0, 29, 3, 11, 3, 2012], twz.to_a[0, 6] + end + end + + def test_iso8601_doesnt_use_local_dst + with_env_tz "US/Eastern" do + zone = ActiveSupport::TimeZone["UTC"] + twz = zone.iso8601("2013-03-10T02:00:00") + assert_equal Time.utc(2013, 3, 10, 2, 0, 0), twz.time + end + end + + def test_iso8601_handles_dst_jump + with_env_tz "US/Eastern" do + zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + twz = zone.iso8601("2013-03-10T02:00:00") + assert_equal Time.utc(2013, 3, 10, 3, 0, 0), twz.time + end + end + def test_parse zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] twz = zone.parse("1999-12-31 19:00:00") @@ -314,6 +403,99 @@ class TimeZoneTest < ActiveSupport::TestCase end end + def test_rfc3339 + zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + twz = zone.rfc3339("1999-12-31T14:00:00-10:00") + assert_equal Time.utc(1999, 12, 31, 19), twz.time + assert_equal Time.utc(2000), twz.utc + assert_equal zone, twz.time_zone + end + + def test_rfc3339_with_fractional_seconds + zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + twz = zone.iso8601("1999-12-31T14:00:00.750-10:00") + assert_equal 750000, twz.time.usec + assert_equal Time.utc(1999, 12, 31, 19, 0, 0 + Rational(3, 4)), twz.time + assert_equal Time.utc(2000, 1, 1, 0, 0, 0 + Rational(3, 4)), twz.utc + assert_equal zone, twz.time_zone + end + + def test_rfc3339_with_missing_time + zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + + exception = assert_raises(ArgumentError) do + zone.rfc3339("1999-12-31") + end + + assert_equal "invalid date", exception.message + end + + def test_rfc3339_with_missing_offset + zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + + exception = assert_raises(ArgumentError) do + zone.rfc3339("1999-12-31T19:00:00") + end + + assert_equal "invalid date", exception.message + end + + def test_rfc3339_with_invalid_string + zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + + exception = assert_raises(ArgumentError) do + zone.rfc3339("foobar") + end + + assert_equal "invalid date", exception.message + end + + def test_rfc3339_with_old_date + zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + twz = zone.rfc3339("1883-12-31T19:00:00-05:00") + assert_equal [0, 0, 19, 31, 12, 1883], twz.to_a[0, 6] + assert_equal zone, twz.time_zone + end + + def test_rfc3339_far_future_date_with_time_zone_offset_in_string + zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + twz = zone.rfc3339("2050-12-31T19:00:00-10:00") # i.e., 2050-01-01 05:00:00 UTC + assert_equal [0, 0, 0, 1, 1, 2051], twz.to_a[0, 6] + assert_equal zone, twz.time_zone + end + + def test_rfc3339_should_not_black_out_system_timezone_dst_jump + with_env_tz("EET") do + zone = ActiveSupport::TimeZone["Pacific Time (US & Canada)"] + twz = zone.rfc3339("2012-03-25T03:29:00-07:00") + assert_equal [0, 29, 3, 25, 3, 2012], twz.to_a[0, 6] + end + end + + def test_rfc3339_should_black_out_app_timezone_dst_jump + with_env_tz("EET") do + zone = ActiveSupport::TimeZone["Pacific Time (US & Canada)"] + twz = zone.rfc3339("2012-03-11T02:29:00-08:00") + assert_equal [0, 29, 3, 11, 3, 2012], twz.to_a[0, 6] + end + end + + def test_rfc3339_doesnt_use_local_dst + with_env_tz "US/Eastern" do + zone = ActiveSupport::TimeZone["UTC"] + twz = zone.rfc3339("2013-03-10T02:00:00Z") + assert_equal Time.utc(2013, 3, 10, 2, 0, 0), twz.time + end + end + + def test_rfc3339_handles_dst_jump + with_env_tz "US/Eastern" do + zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + twz = zone.iso8601("2013-03-10T02:00:00-05:00") + assert_equal Time.utc(2013, 3, 10, 3, 0, 0), twz.time + end + end + def test_strptime zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] twz = zone.strptime("1999-12-31 12:00:00", "%Y-%m-%d %H:%M:%S") @@ -532,6 +714,10 @@ class TimeZoneTest < ActiveSupport::TestCase assert_not_includes ActiveSupport::TimeZone.country_zones(:ru), ActiveSupport::TimeZone["Kuala Lumpur"] end + def test_country_zones_without_mappings + assert_includes ActiveSupport::TimeZone.country_zones(:sv), ActiveSupport::TimeZone["America/El_Salvador"] + end + def test_to_yaml assert_equal("--- !ruby/object:ActiveSupport::TimeZone\nname: Pacific/Honolulu\n", ActiveSupport::TimeZone["Hawaii"].to_yaml) assert_equal("--- !ruby/object:ActiveSupport::TimeZone\nname: Europe/London\n", ActiveSupport::TimeZone["Europe/London"].to_yaml) |