aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/test
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/test')
-rw-r--r--activesupport/test/abstract_unit.rb10
-rw-r--r--activesupport/test/autoloading_fixtures/raises_arbitrary_exception.rb4
-rw-r--r--activesupport/test/autoloading_fixtures/throws.rb4
-rw-r--r--activesupport/test/benchmarkable_test.rb14
-rw-r--r--activesupport/test/broadcast_logger_test.rb134
-rw-r--r--activesupport/test/caching_test.rb27
-rw-r--r--activesupport/test/core_ext/array/access_test.rb2
-rw-r--r--activesupport/test/core_ext/array/conversions_test.rb6
-rw-r--r--activesupport/test/core_ext/array/grouping_test.rb13
-rw-r--r--activesupport/test/core_ext/bigdecimal_test.rb2
-rw-r--r--activesupport/test/core_ext/date_and_time_behavior.rb10
-rw-r--r--activesupport/test/core_ext/date_and_time_compatibility_test.rb127
-rw-r--r--activesupport/test/core_ext/date_ext_test.rb32
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb58
-rw-r--r--activesupport/test/core_ext/duration_test.rb101
-rw-r--r--activesupport/test/core_ext/enumerable_test.rb102
-rw-r--r--activesupport/test/core_ext/hash/transform_keys_test.rb16
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb50
-rw-r--r--activesupport/test/core_ext/marshal_test.rb34
-rw-r--r--activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb6
-rw-r--r--activesupport/test/core_ext/module_test.rb67
-rw-r--r--activesupport/test/core_ext/numeric_ext_test.rb7
-rw-r--r--activesupport/test/core_ext/object/deep_dup_test.rb2
-rw-r--r--activesupport/test/core_ext/object/json_gem_encoding_test.rb2
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb51
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb16
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb5
-rw-r--r--activesupport/test/dependencies_test.rb35
-rw-r--r--activesupport/test/deprecation_test.rb8
-rw-r--r--activesupport/test/evented_file_update_checker_test.rb44
-rw-r--r--activesupport/test/executor_test.rb183
-rw-r--r--activesupport/test/file_update_checker_shared_tests.rb32
-rw-r--r--activesupport/test/inflector_test_cases.rb3
-rw-r--r--activesupport/test/json/encoding_test.rb5
-rw-r--r--activesupport/test/logger_test.rb46
-rw-r--r--activesupport/test/multibyte_conformance_test.rb2
-rw-r--r--activesupport/test/multibyte_grapheme_break_conformance_test.rb2
-rw-r--r--activesupport/test/multibyte_normalization_conformance_test.rb2
-rw-r--r--activesupport/test/number_helper_test.rb3
-rw-r--r--activesupport/test/reloader_test.rb85
-rw-r--r--activesupport/test/rescuable_test.rb21
-rw-r--r--activesupport/test/share_lock_test.rb245
-rw-r--r--activesupport/test/time_zone_test.rb16
-rw-r--r--activesupport/test/time_zone_test_helpers.rb8
-rw-r--r--activesupport/test/xml_mini/rexml_engine_test.rb26
45 files changed, 1518 insertions, 150 deletions
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb
index c0e23e89f7..bab7440397 100644
--- a/activesupport/test/abstract_unit.rb
+++ b/activesupport/test/abstract_unit.rb
@@ -1,12 +1,5 @@
ORIG_ARGV = ARGV.dup
-begin
- old, $VERBOSE = $VERBOSE, nil
- require File.expand_path('../../../load_paths', __FILE__)
-ensure
- $VERBOSE = old
-end
-
require 'active_support/core_ext/kernel/reporting'
silence_warnings do
@@ -25,6 +18,9 @@ Thread.abort_on_exception = true
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
+# Default to old to_time behavior but allow running tests with new behavior
+ActiveSupport.to_time_preserves_timezone = ENV['PRESERVE_TIMEZONES'] == '1'
+
# Disable available locale checks to avoid warnings running the test suite.
I18n.enforce_available_locales = false
diff --git a/activesupport/test/autoloading_fixtures/raises_arbitrary_exception.rb b/activesupport/test/autoloading_fixtures/raises_arbitrary_exception.rb
new file mode 100644
index 0000000000..3ca4213c71
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/raises_arbitrary_exception.rb
@@ -0,0 +1,4 @@
+RaisesArbitraryException = 1
+_ = A::B # Autoloading recursion, also expected to be watched and discarded.
+
+raise Exception, 'arbitray exception message'
diff --git a/activesupport/test/autoloading_fixtures/throws.rb b/activesupport/test/autoloading_fixtures/throws.rb
new file mode 100644
index 0000000000..e1d96cc512
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/throws.rb
@@ -0,0 +1,4 @@
+Throws = 1
+_ = A::B # Autoloading recursion, expected to be discarded.
+
+throw :t
diff --git a/activesupport/test/benchmarkable_test.rb b/activesupport/test/benchmarkable_test.rb
index 04d4f5e503..5af041f458 100644
--- a/activesupport/test/benchmarkable_test.rb
+++ b/activesupport/test/benchmarkable_test.rb
@@ -41,6 +41,20 @@ class BenchmarkableTest < ActiveSupport::TestCase
assert_last_logged 'test_run'
end
+ def test_with_silence
+ assert_difference 'buffer.count', +2 do
+ benchmark('test_run') do
+ logger.info "SOMETHING"
+ end
+ end
+
+ assert_difference 'buffer.count', +1 do
+ benchmark('test_run', silence: true) do
+ logger.info "NOTHING"
+ end
+ end
+ end
+
def test_within_level
logger.level = ActiveSupport::Logger::DEBUG
benchmark('included_debug_run', :level => :debug) { }
diff --git a/activesupport/test/broadcast_logger_test.rb b/activesupport/test/broadcast_logger_test.rb
index 6d4e3b74f7..f95b3e5ad3 100644
--- a/activesupport/test/broadcast_logger_test.rb
+++ b/activesupport/test/broadcast_logger_test.rb
@@ -3,75 +3,147 @@ require 'abstract_unit'
module ActiveSupport
class BroadcastLoggerTest < TestCase
attr_reader :logger, :log1, :log2
- def setup
+
+ setup do
@log1 = FakeLogger.new
@log2 = FakeLogger.new
@log1.extend Logger.broadcast @log2
@logger = @log1
end
- def test_debug
- logger.debug "foo"
- assert_equal 'foo', log1.adds.first[2]
- assert_equal 'foo', log2.adds.first[2]
+ Logger::Severity.constants.each do |level_name|
+ method = level_name.downcase
+ level = Logger::Severity.const_get(level_name)
+
+ test "##{method} adds the message to all loggers" do
+ logger.send(method, "msg")
+
+ assert_equal [level, "msg", nil], log1.adds.first
+ assert_equal [level, "msg", nil], log2.adds.first
+ end
end
- def test_close
+ test "#close broadcasts to all loggers" do
logger.close
+
assert log1.closed, 'should be closed'
assert log2.closed, 'should be closed'
end
- def test_chevrons
+ test "#<< shovels the value into all loggers" do
logger << "foo"
+
assert_equal %w{ foo }, log1.chevrons
assert_equal %w{ foo }, log2.chevrons
end
- def test_level
- assert_nil logger.level
- logger.level = 10
- assert_equal 10, log1.level
- assert_equal 10, log2.level
+ test "#level= assigns the level to all loggers" do
+ assert_equal ::Logger::DEBUG, logger.level
+ logger.level = ::Logger::FATAL
+
+ assert_equal ::Logger::FATAL, log1.level
+ assert_equal ::Logger::FATAL, log2.level
end
- def test_progname
+ test "#progname= assigns to all the loggers" do
assert_nil logger.progname
- logger.progname = 10
- assert_equal 10, log1.progname
- assert_equal 10, log2.progname
+ logger.progname = ::Logger::FATAL
+
+ assert_equal ::Logger::FATAL, log1.progname
+ assert_equal ::Logger::FATAL, log2.progname
end
- def test_formatter
+ test "#formatter= assigns to all the loggers" do
assert_nil logger.formatter
- logger.formatter = 10
- assert_equal 10, log1.formatter
- assert_equal 10, log2.formatter
+ logger.formatter = ::Logger::FATAL
+
+ assert_equal ::Logger::FATAL, log1.formatter
+ assert_equal ::Logger::FATAL, log2.formatter
+ end
+
+ test "#local_level= assigns the local_level to all loggers" do
+ assert_equal ::Logger::DEBUG, logger.local_level
+ logger.local_level = ::Logger::FATAL
+
+ assert_equal ::Logger::FATAL, log1.local_level
+ assert_equal ::Logger::FATAL, log2.local_level
+ end
+
+ test "#silence silences all loggers below the default level of ERROR" do
+ logger.silence do
+ logger.debug "test"
+ end
+
+ assert_equal [], log1.adds
+ assert_equal [], log2.adds
+ end
+
+ test "#silence does not silence at or above ERROR" do
+ logger.silence do
+ logger.error "from error"
+ logger.unknown "from unknown"
+ end
+
+ assert_equal [[::Logger::ERROR, "from error", nil], [::Logger::UNKNOWN, "from unknown", nil]], log1.adds
+ assert_equal [[::Logger::ERROR, "from error", nil], [::Logger::UNKNOWN, "from unknown", nil]], log2.adds
+ end
+
+ test "#silence allows you to override the silence level" do
+ logger.silence(::Logger::FATAL) do
+ logger.error "unseen"
+ logger.fatal "seen"
+ end
+
+ assert_equal [[::Logger::FATAL, "seen", nil]], log1.adds
+ assert_equal [[::Logger::FATAL, "seen", nil]], log2.adds
end
class FakeLogger
+ include LoggerSilence
+
attr_reader :adds, :closed, :chevrons
- attr_accessor :level, :progname, :formatter
+ attr_accessor :level, :progname, :formatter, :local_level
def initialize
- @adds = []
- @closed = false
- @chevrons = []
- @level = nil
- @progname = nil
- @formatter = nil
+ @adds = []
+ @closed = false
+ @chevrons = []
+ @level = ::Logger::DEBUG
+ @local_level = ::Logger::DEBUG
+ @progname = nil
+ @formatter = nil
+ end
+
+ def debug(message, &block)
+ add(::Logger::DEBUG, message, &block)
+ end
+
+ def info(message, &block)
+ add(::Logger::INFO, message, &block)
+ end
+
+ def warn(message, &block)
+ add(::Logger::WARN, message, &block)
+ end
+
+ def error(message, &block)
+ add(::Logger::ERROR, message, &block)
+ end
+
+ def fatal(message, &block)
+ add(::Logger::FATAL, message, &block)
end
- def debug msg, &block
- add(:omg, nil, msg, &block)
+ def unknown(message, &block)
+ add(::Logger::UNKNOWN, message, &block)
end
def << x
@chevrons << x
end
- def add(*args)
- @adds << args
+ def add(message_level, message=nil, progname=nil, &block)
+ @adds << [message_level, message, progname] if message_level >= local_level
end
def close
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index 4a299429f3..ec7d028d7e 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -266,6 +266,20 @@ module CacheStoreBehavior
end
end
+ def test_fetch_with_forced_cache_miss_with_block
+ @cache.write('foo', 'bar')
+ assert_equal 'foo_bar', @cache.fetch('foo', force: true) { 'foo_bar' }
+ end
+
+ def test_fetch_with_forced_cache_miss_without_block
+ @cache.write('foo', 'bar')
+ assert_raises(ArgumentError) do
+ @cache.fetch('foo', force: true)
+ end
+
+ assert_equal 'bar', @cache.read('foo')
+ end
+
def test_should_read_and_write_hash
assert @cache.write('foo', {:a => "b"})
assert_equal({:a => "b"}, @cache.read('foo'))
@@ -836,7 +850,7 @@ class FileStoreTest < ActiveSupport::TestCase
# If nothing has been stored in the cache, there is a chance the cache directory does not yet exist
# Ensure delete_matched gracefully handles this case
def test_delete_matched_when_cache_directory_does_not_exist
- assert_nothing_raised(Exception) do
+ assert_nothing_raised do
ActiveSupport::Cache::FileStore.new('/test/cache/directory').delete_matched(/does_not_exist/)
end
end
@@ -844,7 +858,7 @@ class FileStoreTest < ActiveSupport::TestCase
def test_delete_does_not_delete_empty_parent_dir
sub_cache_dir = File.join(cache_dir, 'subdir/')
sub_cache_store = ActiveSupport::Cache::FileStore.new(sub_cache_dir)
- assert_nothing_raised(Exception) do
+ assert_nothing_raised do
assert sub_cache_store.write('foo', 'bar')
assert sub_cache_store.delete('foo')
end
@@ -1151,15 +1165,6 @@ class CacheStoreLoggerTest < ActiveSupport::TestCase
@cache.mute { @cache.fetch('foo') { 'bar' } }
assert @buffer.string.blank?
end
-
- def test_multi_read_loggin
- @cache.write 'hello', 'goodbye'
- @cache.write 'world', 'earth'
-
- @cache.read_multi('hello', 'world')
-
- assert_match "Caches multi read:\n- hello\n- world", @buffer.string
- end
end
class CacheEntryTest < ActiveSupport::TestCase
diff --git a/activesupport/test/core_ext/array/access_test.rb b/activesupport/test/core_ext/array/access_test.rb
index 3f1e0c4cb4..1d834667f0 100644
--- a/activesupport/test/core_ext/array/access_test.rb
+++ b/activesupport/test/core_ext/array/access_test.rb
@@ -26,6 +26,8 @@ class AccessTest < ActiveSupport::TestCase
assert_equal array[3], array.fourth
assert_equal array[4], array.fifth
assert_equal array[41], array.forty_two
+ assert_equal array[-3], array.third_to_last
+ assert_equal array[-2], array.second_to_last
end
def test_without
diff --git a/activesupport/test/core_ext/array/conversions_test.rb b/activesupport/test/core_ext/array/conversions_test.rb
index 507e13f968..de36e2026d 100644
--- a/activesupport/test/core_ext/array/conversions_test.rb
+++ b/activesupport/test/core_ext/array/conversions_test.rb
@@ -101,10 +101,10 @@ class ToXmlTest < ActiveSupport::TestCase
end
def test_to_xml_with_non_hash_elements
- xml = [1, 2, 3].to_xml(skip_instruct: true, indent: 0)
+ xml = %w[1 2 3].to_xml(skip_instruct: true, indent: 0)
- assert_equal '<fixnums type="array"><fixnum', xml.first(29)
- assert xml.include?(%(<fixnum type="integer">2</fixnum>)), xml
+ assert_equal '<strings type="array"><string', xml.first(29)
+ assert xml.include?(%(<string>2</string>)), xml
end
def test_to_xml_with_non_hash_different_type_elements
diff --git a/activesupport/test/core_ext/array/grouping_test.rb b/activesupport/test/core_ext/array/grouping_test.rb
index 2eb0f05141..0682241f0b 100644
--- a/activesupport/test/core_ext/array/grouping_test.rb
+++ b/activesupport/test/core_ext/array/grouping_test.rb
@@ -3,11 +3,12 @@ require 'active_support/core_ext/array'
class GroupingTest < ActiveSupport::TestCase
def setup
- Fixnum.send :private, :/ # test we avoid Integer#/ (redefined by mathn)
+ # In Ruby < 2.4, test we avoid Integer#/ (redefined by mathn)
+ Fixnum.send :private, :/ unless Fixnum == Integer
end
def teardown
- Fixnum.send :public, :/
+ Fixnum.send :public, :/ unless Fixnum == Integer
end
def test_in_groups_of_with_perfect_fit
@@ -123,4 +124,12 @@ class SplitTest < ActiveSupport::TestCase
assert_equal [[], [2, 3, 4], []], a.split { |i| i == 1 || i == 5 }
assert_equal [1, 2, 3, 4, 5], a
end
+
+ def test_split_with_repeated_values
+ a = [1, 2, 3, 5, 5, 3, 4, 6, 2, 1, 3]
+ assert_equal [[1, 2], [5, 5], [4, 6, 2, 1], []], a.split(3)
+ assert_equal [[1, 2, 3], [], [3, 4, 6, 2, 1, 3]], a.split(5)
+ assert_equal [[1, 2], [], [], [], [4, 6, 2, 1], []], a.split { |i| i == 3 || i == 5 }
+ assert_equal [1, 2, 3, 5, 5, 3, 4, 6, 2, 1, 3], a
+ end
end
diff --git a/activesupport/test/core_ext/bigdecimal_test.rb b/activesupport/test/core_ext/bigdecimal_test.rb
index 423a3f2e9d..6e82e3892b 100644
--- a/activesupport/test/core_ext/bigdecimal_test.rb
+++ b/activesupport/test/core_ext/bigdecimal_test.rb
@@ -5,5 +5,7 @@ class BigDecimalTest < ActiveSupport::TestCase
def test_to_s
bd = BigDecimal.new '0.01'
assert_equal '0.01', bd.to_s
+ assert_equal '+0.01', bd.to_s('+F')
+ assert_equal '+0.0 1', bd.to_s('+1F')
end
end
diff --git a/activesupport/test/core_ext/date_and_time_behavior.rb b/activesupport/test/core_ext/date_and_time_behavior.rb
index 784547bdf8..54df87def8 100644
--- a/activesupport/test/core_ext/date_and_time_behavior.rb
+++ b/activesupport/test/core_ext/date_and_time_behavior.rb
@@ -301,6 +301,16 @@ module DateAndTimeBehavior
assert_not date_time_init(2015,1,5,15,15,10).on_weekend?
end
+ def test_on_weekday_on_sunday
+ assert_not date_time_init(2015,1,4,0,0,0).on_weekday?
+ assert_not date_time_init(2015,1,4,15,15,10).on_weekday?
+ end
+
+ def test_on_weekday_on_monday
+ assert date_time_init(2015,1,5,0,0,0).on_weekday?
+ assert date_time_init(2015,1,5,15,15,10).on_weekday?
+ end
+
def with_bw_default(bw = :monday)
old_bw = Date.beginning_of_week
Date.beginning_of_week = bw
diff --git a/activesupport/test/core_ext/date_and_time_compatibility_test.rb b/activesupport/test/core_ext/date_and_time_compatibility_test.rb
new file mode 100644
index 0000000000..11cb1469da
--- /dev/null
+++ b/activesupport/test/core_ext/date_and_time_compatibility_test.rb
@@ -0,0 +1,127 @@
+require 'abstract_unit'
+require 'active_support/time'
+require 'time_zone_test_helpers'
+
+class DateAndTimeCompatibilityTest < ActiveSupport::TestCase
+ include TimeZoneTestHelpers
+
+ def setup
+ @utc_time = Time.utc(2016, 4, 23, 14, 11, 12)
+ @date_time = DateTime.new(2016, 4, 23, 14, 11, 12, 0)
+ @utc_offset = 3600
+ @system_offset = -14400
+ @zone = ActiveSupport::TimeZone['London']
+ end
+
+ 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
+
+ assert_instance_of Time, time
+ assert_equal @utc_time, time.getutc
+ assert_equal @utc_offset, time.utc_offset
+ end
+ end
+ end
+
+ 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
+
+ assert_instance_of Time, time
+ assert_equal @utc_time, time.getutc
+ assert_equal @system_offset, time.utc_offset
+ end
+ end
+ end
+
+ 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
+
+ assert_instance_of Time, time
+ assert_equal @utc_time, time.getutc
+ assert_equal @utc_offset, time.utc_offset
+ end
+ end
+ end
+
+ 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
+
+ assert_instance_of Time, time
+ assert_equal @utc_time, time.getutc
+ assert_equal @system_offset, time.utc_offset
+ 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
+
+ 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
+
+ assert_instance_of Time, time
+ assert_equal @date_time, time.getutc
+ assert_instance_of Time, time.getutc
+ assert_equal @utc_offset, time.utc_offset
+ end
+ end
+ end
+
+ 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
+
+ assert_instance_of Time, time
+ assert_equal @utc_time, time.getutc
+ assert_instance_of Time, time.getutc
+ assert_equal @system_offset, time.utc_offset
+
+ time = ActiveSupport::TimeWithZone.new(@date_time, @zone).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_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
+
+ assert_instance_of Time, time
+ assert_equal @utc_time, time.getutc
+ assert_equal @utc_offset, time.utc_offset
+ end
+ end
+ end
+
+ 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
+
+ assert_instance_of Time, time
+ assert_equal @utc_time, time.getutc
+ assert_equal @system_offset, time.utc_offset
+ end
+ end
+ end
+end
diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb
index 0fc3f765f5..a7219eee31 100644
--- a/activesupport/test/core_ext/date_ext_test.rb
+++ b/activesupport/test/core_ext/date_ext_test.rb
@@ -30,6 +30,17 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
assert_equal "2005-02-21", date.to_s(:iso8601)
end
+ def test_to_s_with_single_digit_day
+ date = Date.new(2005, 2, 1)
+ assert_equal "2005-02-01", date.to_s
+ assert_equal "01 Feb", date.to_s(:short)
+ assert_equal "February 01, 2005", date.to_s(:long)
+ assert_equal "February 1st, 2005", date.to_s(:long_ordinal)
+ assert_equal "2005-02-01", date.to_s(:db)
+ assert_equal "01 Feb 2005", date.to_s(:rfc822)
+ assert_equal "2005-02-01", date.to_s(:iso8601)
+ end
+
def test_readable_inspect
assert_equal "Mon, 21 Feb 2005", Date.new(2005, 2, 21).readable_inspect
assert_equal Date.new(2005, 2, 21).readable_inspect, Date.new(2005, 2, 21).inspect
@@ -49,6 +60,10 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
end
end
end
+
+ assert_raise(ArgumentError) do
+ Date.new(2005, 2, 21).to_time(:tokyo)
+ end
end
def test_compare_to_time
@@ -280,6 +295,23 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
end
end
+ def test_all_day
+ beginning_of_day = Time.local(2011,6,7,0,0,0)
+ end_of_day = Time.local(2011,6,7,23,59,59,Rational(999999999, 1000))
+ assert_equal beginning_of_day..end_of_day, Date.new(2011,6,7).all_day
+ end
+
+ def test_all_day_when_zone_is_set
+ zone = ActiveSupport::TimeZone["Hawaii"]
+ with_env_tz "UTC" do
+ with_tz_default zone do
+ beginning_of_day = zone.local(2011,6,7,0,0,0)
+ end_of_day = zone.local(2011,6,7,23,59,59,Rational(999999999, 1000))
+ assert_equal beginning_of_day..end_of_day, Date.new(2011,6,7).all_day
+ end
+ end
+ end
+
def test_all_week
assert_equal Date.new(2011,6,6)..Date.new(2011,6,12), Date.new(2011,6,7).all_week
assert_equal Date.new(2011,6,5)..Date.new(2011,6,11), Date.new(2011,6,7).all_week(:sunday)
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index 6fe38c45ec..306316efcd 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -40,6 +40,24 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
Time::DATE_FORMATS.delete(:custom)
end
+ def test_localtime
+ with_env_tz 'US/Eastern' do
+ assert_instance_of Time, DateTime.new(2016, 3, 11, 15, 11, 12, 0).localtime
+ assert_equal Time.local(2016, 3, 11, 10, 11, 12), DateTime.new(2016, 3, 11, 15, 11, 12, 0).localtime
+ assert_equal Time.local(2016, 3, 21, 11, 11, 12), DateTime.new(2016, 3, 21, 15, 11, 12, 0).localtime
+ assert_equal Time.local(2016, 4, 1, 11, 11, 12), DateTime.new(2016, 4, 1, 16, 11, 12, Rational(1,24)).localtime
+ end
+ end
+
+ def test_getlocal
+ with_env_tz 'US/Eastern' do
+ assert_instance_of Time, DateTime.new(2016, 3, 11, 15, 11, 12, 0).getlocal
+ assert_equal Time.local(2016, 3, 11, 10, 11, 12), DateTime.new(2016, 3, 11, 15, 11, 12, 0).getlocal
+ assert_equal Time.local(2016, 3, 21, 11, 11, 12), DateTime.new(2016, 3, 21, 15, 11, 12, 0).getlocal
+ assert_equal Time.local(2016, 4, 1, 11, 11, 12), DateTime.new(2016, 4, 1, 16, 11, 12, Rational(1,24)).getlocal
+ end
+ end
+
def test_to_date
assert_equal Date.new(2005, 2, 21), DateTime.new(2005, 2, 21, 14, 30, 0).to_date
end
@@ -50,9 +68,15 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
def test_to_time
with_env_tz 'US/Eastern' do
- assert_equal Time, DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time.class
- assert_equal Time.local(2005, 2, 21, 5, 11, 12), DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time
- assert_equal Time.local(2005, 2, 21, 5, 11, 12).utc_offset, DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time.utc_offset
+ assert_instance_of Time, DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time
+
+ if ActiveSupport.to_time_preserves_timezone
+ assert_equal Time.local(2005, 2, 21, 5, 11, 12).getlocal(0), DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time
+ assert_equal Time.local(2005, 2, 21, 5, 11, 12).getlocal(0).utc_offset, DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time.utc_offset
+ else
+ assert_equal Time.local(2005, 2, 21, 5, 11, 12), DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time
+ assert_equal Time.local(2005, 2, 21, 5, 11, 12).utc_offset, DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time.utc_offset
+ end
end
end
@@ -186,6 +210,10 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal DateTime.civil(2006,11,15), DateTime.civil(2006,11,23,0,0,0).last_week(:wednesday)
end
+ def test_date_time_should_have_correct_last_week_for_leap_year
+ assert_equal DateTime.civil(2016, 2, 29), DateTime.civil(2016, 3, 7).last_week
+ end
+
def test_last_month_on_31st
assert_equal DateTime.civil(2004, 2, 29), DateTime.civil(2004, 3, 31).last_month
end
@@ -306,6 +334,7 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_utc
+ assert_instance_of Time, DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc
assert_equal DateTime.civil(2005, 2, 21, 16, 11, 12, 0), DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc
assert_equal DateTime.civil(2005, 2, 21, 15, 11, 12, 0), DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-5, 24)).utc
assert_equal DateTime.civil(2005, 2, 21, 10, 11, 12, 0), DateTime.civil(2005, 2, 21, 10, 11, 12, 0).utc
@@ -350,6 +379,24 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal nil, DateTime.civil(2000) <=> "Invalid as Time"
end
+ def test_compare_with_integer
+ assert_equal 1, DateTime.civil(1970, 1, 1, 12, 0, 0) <=> 2440587
+ assert_equal 0, DateTime.civil(1970, 1, 1, 12, 0, 0) <=> 2440588
+ assert_equal(-1, DateTime.civil(1970, 1, 1, 12, 0, 0) <=> 2440589)
+ end
+
+ def test_compare_with_float
+ assert_equal 1, DateTime.civil(1970) <=> 2440586.5
+ assert_equal 0, DateTime.civil(1970) <=> 2440587.5
+ assert_equal(-1, DateTime.civil(1970) <=> 2440588.5)
+ end
+
+ def test_compare_with_rational
+ assert_equal 1, DateTime.civil(1970) <=> Rational(4881173, 2)
+ assert_equal 0, DateTime.civil(1970) <=> Rational(4881175, 2)
+ assert_equal(-1, DateTime.civil(1970) <=> Rational(4881177, 2))
+ end
+
def test_to_f
assert_equal 946684800.0, DateTime.civil(2000).to_f
assert_equal 946684800.0, DateTime.civil(1999,12,31,19,0,0,Rational(-5,24)).to_f
@@ -370,4 +417,9 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal 0, DateTime.civil(2000).nsec
assert_equal 500000000, DateTime.civil(2000, 1, 1, 0, 0, Rational(1,2)).nsec
end
+
+ def test_subsec
+ assert_equal 0, DateTime.civil(2000).subsec
+ assert_equal Rational(1,2), DateTime.civil(2000, 1, 1, 0, 0, Rational(1,2)).subsec
+ end
end
diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb
index 9e97acaffb..502e2811fa 100644
--- a/activesupport/test/core_ext/duration_test.rb
+++ b/activesupport/test/core_ext/duration_test.rb
@@ -12,7 +12,7 @@ class DurationTest < ActiveSupport::TestCase
assert d.is_a?(ActiveSupport::Duration)
assert_kind_of ActiveSupport::Duration, d
assert_kind_of Numeric, d
- assert_kind_of Fixnum, d
+ assert_kind_of Integer, d
assert !d.is_a?(Hash)
k = Class.new
@@ -66,8 +66,9 @@ class DurationTest < ActiveSupport::TestCase
assert_equal '10 years, 2 months, and 1 day', (10.years + 2.months + 1.day).inspect
assert_equal '10 years, 2 months, and 1 day', (10.years + 1.month + 1.day + 1.month).inspect
assert_equal '10 years, 2 months, and 1 day', (1.day + 10.years + 2.months).inspect
- assert_equal '7 days', 1.week.inspect
- assert_equal '14 days', 1.fortnight.inspect
+ assert_equal '7 days', 7.days.inspect
+ assert_equal '1 week', 1.week.inspect
+ assert_equal '2 weeks', 1.fortnight.inspect
end
def test_inspect_locale
@@ -87,6 +88,15 @@ class DurationTest < ActiveSupport::TestCase
assert_equal 1 + 1.second, 1.second + 1, "Duration + Numeric should == Numeric + Duration"
end
+ def test_time_plus_duration_returns_same_time_datatype
+ twz = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone['Moscow'] , Time.utc(2016,4,28,00,45))
+ now = Time.now.utc
+ %w( second minute hour day week month year ).each do |unit|
+ assert_equal((now + 1.send(unit)).class, Time, "Time + 1.#{unit} must be Time")
+ assert_equal((twz + 1.send(unit)).class, ActiveSupport::TimeWithZone, "TimeWithZone + 1.#{unit} must be TimeWithZone")
+ end
+ end
+
def test_argument_error
e = assert_raise ArgumentError do
1.second.ago('')
@@ -222,4 +232,89 @@ class DurationTest < ActiveSupport::TestCase
assert_equal(1, (1.minute <=> 1.second))
assert_equal(1, (61 <=> 1.minute))
end
+
+ # ISO8601 string examples are taken from ISO8601 gem at https://github.com/arnau/ISO8601/blob/b93d466840/spec/iso8601/duration_spec.rb
+ # published under the conditions of MIT license at https://github.com/arnau/ISO8601/blob/b93d466840/LICENSE
+ #
+ # Copyright (c) 2012-2014 Arnau Siches
+ #
+ # MIT License
+ #
+ # Permission is hereby granted, free of charge, to any person obtaining
+ # a copy of this software and associated documentation files (the
+ # "Software"), to deal in the Software without restriction, including
+ # without limitation the rights to use, copy, modify, merge, publish,
+ # distribute, sublicense, and/or sell copies of the Software, and to
+ # permit persons to whom the Software is furnished to do so, subject to
+ # the following conditions:
+ #
+ # The above copyright notice and this permission notice shall be
+ # included in all copies or substantial portions of the Software.
+ #
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ def test_iso8601_parsing_wrong_patterns_with_raise
+ invalid_patterns = ['', 'P', 'PT', 'P1YT', 'T', 'PW', 'P1Y1W', '~P1Y', '.P1Y', 'P1.5Y0.5M', 'P1.5Y1M', 'P1.5MT10.5S']
+ invalid_patterns.each do |pattern|
+ assert_raise ActiveSupport::Duration::ISO8601Parser::ParsingError, pattern.inspect do
+ ActiveSupport::Duration.parse(pattern)
+ end
+ end
+ end
+
+ def test_iso8601_output
+ expectations = [
+ ['P1Y', 1.year ],
+ ['P1W', 1.week ],
+ ['P1Y1M', 1.year + 1.month ],
+ ['P1Y1M1D', 1.year + 1.month + 1.day ],
+ ['-P1Y1D', -1.year - 1.day ],
+ ['P1Y-1DT-1S', 1.year - 1.day - 1.second ], # Parts with different signs are exists in PostgreSQL interval datatype.
+ ['PT1S', 1.second ],
+ ['PT1.4S', (1.4).seconds ],
+ ['P1Y1M1DT1H', 1.year + 1.month + 1.day + 1.hour],
+ ]
+ expectations.each do |expected_output, duration|
+ assert_equal expected_output, duration.iso8601, expected_output.inspect
+ end
+ end
+
+ def test_iso8601_output_precision
+ expectations = [
+ [nil, 'P1Y1MT5.55S', 1.year + 1.month + (5.55).seconds ],
+ [0, 'P1Y1MT6S', 1.year + 1.month + (5.55).seconds ],
+ [1, 'P1Y1MT5.5S', 1.year + 1.month + (5.55).seconds ],
+ [2, 'P1Y1MT5.55S', 1.year + 1.month + (5.55).seconds ],
+ [3, 'P1Y1MT5.550S', 1.year + 1.month + (5.55).seconds ],
+ [nil, 'PT1S', 1.second ],
+ [2, 'PT1.00S', 1.second ],
+ [nil, 'PT1.4S', (1.4).seconds ],
+ [0, 'PT1S', (1.4).seconds ],
+ [1, 'PT1.4S', (1.4).seconds ],
+ [5, 'PT1.40000S', (1.4).seconds ],
+ ]
+ expectations.each do |precision, expected_output, duration|
+ assert_equal expected_output, duration.iso8601(precision: precision), expected_output.inspect
+ end
+ end
+
+ def test_iso8601_output_and_reparsing
+ patterns = %w[
+ P1Y P0.5Y P0,5Y P1Y1M P1Y0.5M P1Y0,5M P1Y1M1D P1Y1M0.5D P1Y1M0,5D P1Y1M1DT1H P1Y1M1DT0.5H P1Y1M1DT0,5H P1W +P1Y -P1Y
+ P1Y1M1DT1H1M P1Y1M1DT1H0.5M P1Y1M1DT1H0,5M P1Y1M1DT1H1M1S P1Y1M1DT1H1M1.0S P1Y1M1DT1H1M1,0S P-1Y-2M3DT-4H-5M-6S
+ ]
+ # That could be weird, but if we parse P1Y1M0.5D and output it to ISO 8601, we'll get P1Y1MT12.0H.
+ # So we check that initially parsed and reparsed duration added to time will result in the same time.
+ time = Time.current
+ patterns.each do |pattern|
+ duration = ActiveSupport::Duration.parse(pattern)
+ assert_equal time+duration, time+ActiveSupport::Duration.parse(duration.iso8601), pattern.inspect
+ end
+ end
end
diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb
index f09b7d8850..99c3236c35 100644
--- a/activesupport/test/core_ext/enumerable_test.rb
+++ b/activesupport/test/core_ext/enumerable_test.rb
@@ -10,22 +10,27 @@ class SummablePayment < Payment
end
class EnumerableTests < ActiveSupport::TestCase
-
class GenericEnumerable
include Enumerable
+
def initialize(values = [1, 2, 3])
@values = values
end
def each
- @values.each{|v| yield v}
+ @values.each { |v| yield v }
end
end
+ def assert_typed_equal(e, v, cls, msg=nil)
+ assert_kind_of(cls, v, msg)
+ assert_equal(e, v, msg)
+ end
+
def test_sums
enum = GenericEnumerable.new([5, 15, 10])
assert_equal 30, enum.sum
- assert_equal 60, enum.sum { |i| i * 2}
+ assert_equal 60, enum.sum { |i| i * 2 }
enum = GenericEnumerable.new(%w(a b c))
assert_equal 'abc', enum.sum
@@ -38,6 +43,40 @@ class EnumerableTests < ActiveSupport::TestCase
payments = GenericEnumerable.new([ SummablePayment.new(5), SummablePayment.new(15) ])
assert_equal SummablePayment.new(20), payments.sum
assert_equal SummablePayment.new(20), payments.sum { |p| p }
+
+ sum = GenericEnumerable.new([3, 5.quo(1)]).sum
+ assert_typed_equal(8, sum, Rational)
+
+ sum = GenericEnumerable.new([3, 5.quo(1)]).sum(0.0)
+ assert_typed_equal(8.0, sum, Float)
+
+ sum = GenericEnumerable.new([3, 5.quo(1), 7.0]).sum
+ assert_typed_equal(15.0, sum, Float)
+
+ sum = GenericEnumerable.new([3, 5.quo(1), Complex(7)]).sum
+ assert_typed_equal(Complex(15), sum, Complex)
+ assert_typed_equal(15, sum.real, Rational)
+ assert_typed_equal(0, sum.imag, Integer)
+
+ sum = GenericEnumerable.new([3.5, 5]).sum
+ assert_typed_equal(8.5, sum, Float)
+
+ sum = GenericEnumerable.new([2, 8.5]).sum
+ assert_typed_equal(10.5, sum, Float)
+
+ sum = GenericEnumerable.new([1.quo(2), 1]).sum
+ assert_typed_equal(3.quo(2), sum, Rational)
+
+ sum = GenericEnumerable.new([1.quo(2), 1.quo(3)]).sum
+ assert_typed_equal(5.quo(6), sum, Rational)
+
+ sum = GenericEnumerable.new([2.0, 3.0*Complex::I]).sum
+ assert_typed_equal(Complex(2.0, 3.0), sum, Complex)
+ assert_typed_equal(2.0, sum.real, Float)
+ assert_typed_equal(3.0, sum.imag, Float)
+
+ sum = GenericEnumerable.new([1, 2]).sum(10) {|v| v * 2 }
+ assert_typed_equal(16, sum, Integer)
end
def test_nil_sums
@@ -55,6 +94,7 @@ class EnumerableTests < ActiveSupport::TestCase
assert_equal 0, GenericEnumerable.new([]).sum
assert_equal 0, GenericEnumerable.new([]).sum { |i| i + 10 }
assert_equal Payment.new(0), GenericEnumerable.new([]).sum(Payment.new(0))
+ assert_typed_equal 0.0, GenericEnumerable.new([]).sum(0.0), Float
end
def test_range_sums
@@ -68,6 +108,62 @@ class EnumerableTests < ActiveSupport::TestCase
assert_equal 5, (10..0).sum(5)
assert_equal 10, (10..10).sum
assert_equal 42, (10...10).sum(42)
+ assert_typed_equal 20.0, (1..4).sum(0.0) { |i| i * 2 }, Float
+ assert_typed_equal 10.0, (1..4).sum(0.0), Float
+ assert_typed_equal 20.0, (1..4).sum(10.0), Float
+ assert_typed_equal 5.0, (10..0).sum(5.0), Float
+ end
+
+ def test_array_sums
+ enum = [5, 15, 10]
+ assert_equal 30, enum.sum
+ assert_equal 60, enum.sum { |i| i * 2 }
+
+ enum = %w(a b c)
+ assert_equal 'abc', enum.sum
+ assert_equal 'aabbcc', enum.sum { |i| i * 2 }
+
+ payments = [ Payment.new(5), Payment.new(15), Payment.new(10) ]
+ assert_equal 30, payments.sum(&:price)
+ assert_equal 60, payments.sum { |p| p.price * 2 }
+
+ payments = [ SummablePayment.new(5), SummablePayment.new(15) ]
+ assert_equal SummablePayment.new(20), payments.sum
+ assert_equal SummablePayment.new(20), payments.sum { |p| p }
+
+ sum = [3, 5.quo(1)].sum
+ assert_typed_equal(8, sum, Rational)
+
+ sum = [3, 5.quo(1)].sum(0.0)
+ assert_typed_equal(8.0, sum, Float)
+
+ sum = [3, 5.quo(1), 7.0].sum
+ assert_typed_equal(15.0, sum, Float)
+
+ sum = [3, 5.quo(1), Complex(7)].sum
+ assert_typed_equal(Complex(15), sum, Complex)
+ assert_typed_equal(15, sum.real, Rational)
+ assert_typed_equal(0, sum.imag, Integer)
+
+ sum = [3.5, 5].sum
+ assert_typed_equal(8.5, sum, Float)
+
+ sum = [2, 8.5].sum
+ assert_typed_equal(10.5, sum, Float)
+
+ sum = [1.quo(2), 1].sum
+ assert_typed_equal(3.quo(2), sum, Rational)
+
+ sum = [1.quo(2), 1.quo(3)].sum
+ assert_typed_equal(5.quo(6), sum, Rational)
+
+ sum = [2.0, 3.0*Complex::I].sum
+ assert_typed_equal(Complex(2.0, 3.0), sum, Complex)
+ assert_typed_equal(2.0, sum.real, Float)
+ assert_typed_equal(3.0, sum.imag, Float)
+
+ sum = [1, 2].sum(10) {|v| v * 2 }
+ assert_typed_equal(16, sum, Integer)
end
def test_index_by
diff --git a/activesupport/test/core_ext/hash/transform_keys_test.rb b/activesupport/test/core_ext/hash/transform_keys_test.rb
index 99af274614..962d3a30b6 100644
--- a/activesupport/test/core_ext/hash/transform_keys_test.rb
+++ b/activesupport/test/core_ext/hash/transform_keys_test.rb
@@ -43,4 +43,20 @@ class TransformKeysTest < ActiveSupport::TestCase
original.transform_keys!.with_index { |k, i| [k, i].join.to_sym }
assert_equal({ a0: 'a', b1: 'b' }, original)
end
+
+ test "transform_keys returns a Hash instance when self is inherited from Hash" do
+ class HashDescendant < ::Hash
+ def initialize(elements = nil)
+ super(elements)
+ (elements || {}).each_pair{ |key, value| self[key] = value }
+ end
+ end
+
+ original = HashDescendant.new({ a: 'a', b: 'b' })
+ mapped = original.transform_keys { |k| "#{k}!".to_sym }
+
+ assert_equal({ a: 'a', b: 'b' }, original)
+ assert_equal({ a!: 'a', b!: 'b' }, mapped)
+ assert_equal(::Hash, mapped.class)
+ end
end
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index 1b66f784e4..e8099baa35 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -36,12 +36,12 @@ class HashExtTest < ActiveSupport::TestCase
def setup
@strings = { 'a' => 1, 'b' => 2 }
@nested_strings = { 'a' => { 'b' => { 'c' => 3 } } }
- @symbols = { :a => 1, :b => 2 }
+ @symbols = { :a => 1, :b => 2 }
@nested_symbols = { :a => { :b => { :c => 3 } } }
- @mixed = { :a => 1, 'b' => 2 }
- @nested_mixed = { 'a' => { :b => { 'c' => 3 } } }
- @fixnums = { 0 => 1, 1 => 2 }
- @nested_fixnums = { 0 => { 1 => { 2 => 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} }
@upcase_strings = { 'A' => 1, 'B' => 2 }
@@ -196,14 +196,14 @@ class HashExtTest < ActiveSupport::TestCase
assert_equal @nested_illegal_symbols, @nested_illegal_symbols.deep_dup.deep_symbolize_keys!
end
- def test_symbolize_keys_preserves_fixnum_keys
- assert_equal @fixnums, @fixnums.symbolize_keys
- assert_equal @fixnums, @fixnums.dup.symbolize_keys!
+ def test_symbolize_keys_preserves_integer_keys
+ assert_equal @integers, @integers.symbolize_keys
+ assert_equal @integers, @integers.dup.symbolize_keys!
end
- def test_deep_symbolize_keys_preserves_fixnum_keys
- assert_equal @nested_fixnums, @nested_fixnums.deep_symbolize_keys
- assert_equal @nested_fixnums, @nested_fixnums.deep_dup.deep_symbolize_keys!
+ def test_deep_symbolize_keys_preserves_integer_keys
+ assert_equal @nested_integers, @nested_integers.deep_symbolize_keys
+ assert_equal @nested_integers, @nested_integers.deep_dup.deep_symbolize_keys!
end
def test_stringify_keys
@@ -299,14 +299,14 @@ class HashExtTest < ActiveSupport::TestCase
assert_raise(NoMethodError) { @nested_illegal_symbols.with_indifferent_access.deep_dup.deep_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! }
+ 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_fixnum_keys_for_hash_with_indifferent_access
- assert_equal @nested_fixnums, @nested_fixnums.with_indifferent_access.deep_symbolize_keys
- assert_raise(NoMethodError) { @nested_fixnums.with_indifferent_access.deep_dup.deep_symbolize_keys! }
+ 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
@@ -702,6 +702,12 @@ class HashExtTest < ActiveSupport::TestCase
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 ])
@@ -993,6 +999,10 @@ class HashExtTest < ActiveSupport::TestCase
h = hash_with_only_nil_values.dup
assert_equal({}, h.compact)
assert_equal(hash_with_only_nil_values, h)
+
+ h = @symbols.dup
+ assert_equal(@symbols, h.compact)
+ assert_equal(@symbols, h)
end
def test_compact!
@@ -1006,6 +1016,10 @@ class HashExtTest < ActiveSupport::TestCase
h = hash_with_only_nil_values.dup
assert_equal({}, h.compact!)
assert_equal({}, h)
+
+ h = @symbols.dup
+ assert_equal(nil, h.compact!)
+ assert_equal(@symbols, h)
end
def test_new_with_to_hash_conversion
@@ -1028,7 +1042,7 @@ class HashExtTest < ActiveSupport::TestCase
assert_equal 3, new_hash[2]
new_hash.default = 2
- assert_equal 2, new_hash[:non_existant]
+ assert_equal 2, new_hash[:non_existent]
end
def test_to_hash_with_raising_default_proc
diff --git a/activesupport/test/core_ext/marshal_test.rb b/activesupport/test/core_ext/marshal_test.rb
index 825df439a5..380f64c6fd 100644
--- a/activesupport/test/core_ext/marshal_test.rb
+++ b/activesupport/test/core_ext/marshal_test.rb
@@ -29,7 +29,12 @@ class MarshalTest < ActiveSupport::TestCase
ActiveSupport::Dependencies.clear
with_autoloading_fixtures do
- assert_kind_of EM, Marshal.load(dumped)
+ object = nil
+ assert_nothing_raised do
+ object = Marshal.load(dumped)
+ end
+
+ assert_kind_of EM, object
end
end
@@ -43,7 +48,12 @@ class MarshalTest < ActiveSupport::TestCase
ActiveSupport::Dependencies.clear
with_autoloading_fixtures do
- assert_kind_of ClassFolder::ClassFolderSubclass, Marshal.load(dumped)
+ object = nil
+ assert_nothing_raised do
+ object = Marshal.load(dumped)
+ end
+
+ assert_kind_of ClassFolder::ClassFolderSubclass, object
end
end
@@ -64,6 +74,17 @@ class MarshalTest < ActiveSupport::TestCase
end
end
+ test "when one constant resolves to another" do
+ class Parent; C = Class.new; end
+ class Child < Parent; C = Class.new; end
+
+ dump = Marshal.dump(Child::C.new)
+
+ Child.send(:remove_const, :C)
+
+ assert_raise(ArgumentError) { Marshal.load(dump) }
+ end
+
test "that a real missing class is causing an exception" do
dumped = nil
with_autoloading_fixtures do
@@ -96,7 +117,7 @@ class MarshalTest < ActiveSupport::TestCase
Marshal.load(dumped)
end
- assert_nothing_raised("EM failed to load while we expect only SomeClass to fail loading") do
+ assert_nothing_raised do
EM.new
end
@@ -117,7 +138,12 @@ class MarshalTest < ActiveSupport::TestCase
ActiveSupport::Dependencies.clear
with_autoloading_fixtures do
- assert_kind_of EM, Marshal.load(f)
+ object = nil
+ assert_nothing_raised do
+ object = Marshal.load(f)
+ end
+
+ assert_kind_of EM, object
end
end
end
diff --git a/activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb b/activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb
index 65fadc5c20..a9fd878b80 100644
--- a/activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb
+++ b/activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb
@@ -106,4 +106,10 @@ class ModuleAttributeAccessorPerThreadTest < ActiveSupport::TestCase
end
assert_equal "invalid attribute name: 2valid_part", exception.message
end
+
+ def test_should_return_same_value_by_class_or_instance_accessor
+ @class.foo = 'fries'
+
+ assert_equal @class.foo, @object.foo
+ end
end
diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb
index 0ed66f8c37..566f29b470 100644
--- a/activesupport/test/core_ext/module_test.rb
+++ b/activesupport/test/core_ext/module_test.rb
@@ -40,6 +40,12 @@ class Someone < Struct.new(:name, :place)
FAILED_DELEGATE_LINE_2 = __LINE__ + 1
delegate :bar, :to => :place, :allow_nil => true
+
+ private
+
+ def private_name
+ "Private"
+ end
end
Invoice = Struct.new(:client) do
@@ -83,6 +89,20 @@ Product = Struct.new(:name) do
end
end
+DecoratedTester = Struct.new(:client) do
+ delegate_missing_to :client
+end
+
+class DecoratedReserved
+ delegate_missing_to :case
+
+ attr_reader :case
+
+ def initialize(kase)
+ @case = kase
+ end
+end
+
class Block
def hello?
true
@@ -173,6 +193,21 @@ class ModuleTest < ActiveSupport::TestCase
end
end
+ def test_delegation_target_when_prefix_is_true
+ assert_nothing_raised do
+ Name.send :delegate, :go, to: :you, prefix: true
+ end
+ assert_nothing_raised do
+ Name.send :delegate, :go, to: :_you, prefix: true
+ end
+ assert_raise(ArgumentError) do
+ Name.send :delegate, :go, to: :You, prefix: true
+ end
+ assert_raise(ArgumentError) do
+ Name.send :delegate, :go, to: :@you, prefix: true
+ end
+ end
+
def test_delegation_prefix
invoice = Invoice.new(@david)
assert_equal invoice.client_name, "David"
@@ -316,6 +351,30 @@ class ModuleTest < ActiveSupport::TestCase
assert has_block.hello?
end
+ def test_delegate_to_missing_with_method
+ assert_equal "David", DecoratedTester.new(@david).name
+ end
+
+ def test_delegate_to_missing_with_reserved_methods
+ assert_equal "David", DecoratedReserved.new(@david).name
+ end
+
+ def test_delegate_to_missing_does_not_delegate_to_private_methods
+ e = assert_raises(NoMethodError) do
+ DecoratedReserved.new(@david).private_name
+ end
+
+ assert_match(/undefined method `private_name' for/, e.message)
+ end
+
+ def test_delegate_to_missing_does_not_delegate_to_fake_methods
+ e = assert_raises(NoMethodError) do
+ DecoratedReserved.new(@david).my_fake_method
+ end
+
+ assert_match(/undefined method `my_fake_method' for/, e.message)
+ end
+
def test_parent
assert_equal Yz::Zy, Yz::Zy::Cd.parent
assert_equal Yz, Yz::Zy.parent
@@ -328,7 +387,13 @@ class ModuleTest < ActiveSupport::TestCase
end
def test_local_constants
- assert_equal %w(Constant1 Constant3), Ab.local_constants.sort.map(&:to_s)
+ ActiveSupport::Deprecation.silence do
+ assert_equal %w(Constant1 Constant3), Ab.local_constants.sort.map(&:to_s)
+ end
+ end
+
+ def test_local_constants_is_deprecated
+ assert_deprecated { Ab.local_constants.sort.map(&:to_s) }
end
end
diff --git a/activesupport/test/core_ext/numeric_ext_test.rb b/activesupport/test/core_ext/numeric_ext_test.rb
index 5654aeb4f8..69c30a8a9e 100644
--- a/activesupport/test/core_ext/numeric_ext_test.rb
+++ b/activesupport/test/core_ext/numeric_ext_test.rb
@@ -387,16 +387,9 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
end
def test_to_s__injected_on_proper_types
- assert_equal Fixnum, 1230.class
assert_equal '1.23 Thousand', 1230.to_s(:human)
-
- assert_equal Float, Float(1230).class
assert_equal '1.23 Thousand', Float(1230).to_s(:human)
-
- assert_equal Bignum, (100**10).class
assert_equal '100000 Quadrillion', (100**10).to_s(:human)
-
- assert_equal BigDecimal, BigDecimal("1000010").class
assert_equal '1 Million', BigDecimal("1000010").to_s(:human)
end
diff --git a/activesupport/test/core_ext/object/deep_dup_test.rb b/activesupport/test/core_ext/object/deep_dup_test.rb
index 791b5e7172..aa839201ea 100644
--- a/activesupport/test/core_ext/object/deep_dup_test.rb
+++ b/activesupport/test/core_ext/object/deep_dup_test.rb
@@ -51,7 +51,7 @@ class DeepDupTest < ActiveSupport::TestCase
end
def test_deep_dup_with_hash_class_key
- hash = { Fixnum => 1 }
+ hash = { Integer => 1 }
dup = hash.deep_dup
assert_equal 1, dup.keys.length
end
diff --git a/activesupport/test/core_ext/object/json_gem_encoding_test.rb b/activesupport/test/core_ext/object/json_gem_encoding_test.rb
index 02ab17fb64..2cbb1d590f 100644
--- a/activesupport/test/core_ext/object/json_gem_encoding_test.rb
+++ b/activesupport/test/core_ext/object/json_gem_encoding_test.rb
@@ -10,7 +10,7 @@ require 'json/encoding_test_cases'
# The AS::JSON encoder requires the BigDecimal core_ext, which, unfortunately,
# changes the BigDecimal#to_s output, and consequently the JSON gem output. So
-# we need to require this unfront to ensure we don't get a false failure, but
+# we need to require this upfront to ensure we don't get a false failure, but
# ideally we should just fix the BigDecimal core_ext to not change to_s without
# arguments.
require 'active_support/core_ext/big_decimal'
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index 2e69816364..d68a77680b 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -10,6 +10,7 @@ require 'active_support/core_ext/string/strip'
require 'active_support/core_ext/string/output_safety'
require 'active_support/core_ext/string/indent'
require 'time_zone_test_helpers'
+require 'yaml'
class StringInflectionsTest < ActiveSupport::TestCase
include InflectorTestCases
@@ -76,6 +77,18 @@ class StringInflectionsTest < ActiveSupport::TestCase
end
end
+ def test_upcase_first
+ assert_equal "What a Lovely Day", "what a Lovely Day".upcase_first
+ end
+
+ def test_upcase_first_with_one_char
+ assert_equal "W", "w".upcase_first
+ end
+
+ def test_upcase_first_with_empty_string
+ assert_equal "", "".upcase_first
+ end
+
def test_camelize
CamelToUnderscore.each do |camel, underscore|
assert_equal(camel, underscore.camelize)
@@ -332,7 +345,7 @@ class StringInflectionsTest < ActiveSupport::TestCase
end
class StringAccessTest < ActiveSupport::TestCase
- test "#at with Fixnum, returns a substring of one character at that position" do
+ test "#at with Integer, returns a substring of one character at that position" do
assert_equal "h", "hello".at(0)
end
@@ -345,19 +358,19 @@ class StringAccessTest < ActiveSupport::TestCase
assert_equal nil, "hello".at(/nonexisting/)
end
- test "#from with positive Fixnum, returns substring from the given position to the end" do
+ test "#from with positive Integer, returns substring from the given position to the end" do
assert_equal "llo", "hello".from(2)
end
- test "#from with negative Fixnum, position is counted from the end" do
+ test "#from with negative Integer, position is counted from the end" do
assert_equal "lo", "hello".from(-2)
end
- test "#to with positive Fixnum, substring from the beginning to the given position" do
+ test "#to with positive Integer, substring from the beginning to the given position" do
assert_equal "hel", "hello".to(2)
end
- test "#to with negative Fixnum, position is counted from the end" do
+ test "#to with negative Integer, position is counted from the end" do
assert_equal "hell", "hello".to(-2)
end
@@ -371,14 +384,14 @@ class StringAccessTest < ActiveSupport::TestCase
assert_equal 'x', 'x'.first
end
- test "#first with Fixnum, returns a substring from the beginning to position" do
+ test "#first with Integer, returns a substring from the beginning to position" do
assert_equal "he", "hello".first(2)
assert_equal "", "hello".first(0)
assert_equal "hello", "hello".first(10)
assert_equal 'x', 'x'.first(4)
end
- test "#first with Fixnum >= string length still returns a new string" do
+ test "#first with Integer >= string length still returns a new string" do
string = "hello"
different_string = string.first(5)
assert_not_same different_string, string
@@ -389,14 +402,14 @@ class StringAccessTest < ActiveSupport::TestCase
assert_equal 'x', 'x'.last
end
- test "#last with Fixnum, returns a substring from the end to position" do
+ test "#last with Integer, returns a substring from the end to position" do
assert_equal "llo", "hello".last(3)
assert_equal "hello", "hello".last(10)
assert_equal "", "hello".last(0)
assert_equal 'x', 'x'.last(4)
end
- test "#last with Fixnum >= string length still returns a new string" do
+ test "#last with Integer >= string length still returns a new string" do
string = "hello"
different_string = string.last(5)
assert_not_same different_string, string
@@ -444,16 +457,24 @@ class StringConversionsTest < ActiveSupport::TestCase
assert_equal Time.local(2011, 2, 27, 17, 50), "2011-02-27 13:50 -0100".to_time
assert_equal Time.utc(2011, 2, 27, 23, 50), "2011-02-27 22:50 -0100".to_time(:utc)
assert_equal Time.local(2005, 2, 27, 22, 50), "2005-02-27 14:50 -0500".to_time
+ assert_nil "010".to_time
assert_nil "".to_time
end
end
def test_string_to_time_utc_offset
with_env_tz "US/Eastern" do
- assert_equal 0, "2005-02-27 23:50".to_time(:utc).utc_offset
- assert_equal(-18000, "2005-02-27 23:50".to_time.utc_offset)
- assert_equal 0, "2005-02-27 22:50 -0100".to_time(:utc).utc_offset
- assert_equal(-18000, "2005-02-27 22:50 -0100".to_time.utc_offset)
+ if ActiveSupport.to_time_preserves_timezone
+ assert_equal 0, "2005-02-27 23:50".to_time(:utc).utc_offset
+ assert_equal(-18000, "2005-02-27 23:50".to_time.utc_offset)
+ assert_equal 0, "2005-02-27 22:50 -0100".to_time(:utc).utc_offset
+ assert_equal(-3600, "2005-02-27 22:50 -0100".to_time.utc_offset)
+ else
+ assert_equal 0, "2005-02-27 23:50".to_time(:utc).utc_offset
+ assert_equal(-18000, "2005-02-27 23:50".to_time.utc_offset)
+ assert_equal 0, "2005-02-27 22:50 -0100".to_time(:utc).utc_offset
+ assert_equal(-18000, "2005-02-27 22:50 -0100".to_time.utc_offset)
+ end
end
end
@@ -661,7 +682,7 @@ class OutputSafetyTest < ActiveSupport::TestCase
assert_equal @string, @string.html_safe
end
- test "A fixnum is safe by default" do
+ test "An integer is safe by default" do
assert 5.html_safe?
end
@@ -792,7 +813,7 @@ class OutputSafetyTest < ActiveSupport::TestCase
assert_equal ["<p>", "<b>", "<h1>"], @other_string
end
- test "Concatting a fixnum to safe always yields safe" do
+ test "Concatting an integer to safe always yields safe" do
string = @string.html_safe
string = string.concat(13)
assert_equal "hello".concat(13), string
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index e45df63fd5..1205797fac 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -107,6 +107,20 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
end
+ def test_sec_fraction
+ time = Time.utc(2016, 4, 23, 0, 0, Rational(1,10000000000))
+ assert_equal Rational(1,10000000000), time.sec_fraction
+
+ time = Time.utc(2016, 4, 23, 0, 0, 0.0000000001)
+ assert_equal 0.0000000001.to_r, time.sec_fraction
+
+ time = Time.utc(2016, 4, 23, 0, 0, 0, Rational(1,10000))
+ assert_equal Rational(1,10000000000), time.sec_fraction
+
+ time = Time.utc(2016, 4, 23, 0, 0, 0, 0.0001)
+ assert_equal 0.0001.to_r / 1000000, time.sec_fraction
+ end
+
def test_beginning_of_day
assert_equal Time.local(2005,2,4,0,0,0), Time.local(2005,2,4,10,10,10).beginning_of_day
with_env_tz 'US/Eastern' do
@@ -392,7 +406,7 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal Time.local(2005,1,2,11,22,33, 8), Time.local(2005,1,2,11,22,33,44).change(:usec => 8)
assert_equal Time.local(2005,1,2,11,22,33, 8), Time.local(2005,1,2,11,22,33,2).change(:nsec => 8000)
assert_raise(ArgumentError) { Time.local(2005,1,2,11,22,33, 8).change(:usec => 1, :nsec => 1) }
- assert_nothing_raised(ArgumentError) { Time.new(2015, 5, 9, 10, 00, 00, '+03:00').change(nsec: 999999999) }
+ assert_nothing_raised { Time.new(2015, 5, 9, 10, 00, 00, '+03:00').change(nsec: 999999999) }
end
def test_utc_change
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index 7acada011d..d90714acdb 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -11,10 +11,13 @@ class TimeWithZoneTest < ActiveSupport::TestCase
@utc = Time.utc(2000, 1, 1, 0)
@time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
@twz = ActiveSupport::TimeWithZone.new(@utc, @time_zone)
+ @dt_twz = ActiveSupport::TimeWithZone.new(@utc.to_datetime, @time_zone)
end
def test_utc
assert_equal @utc, @twz.utc
+ assert_instance_of Time, @twz.utc
+ assert_instance_of Time, @dt_twz.utc
end
def test_time
@@ -47,6 +50,8 @@ class TimeWithZoneTest < ActiveSupport::TestCase
def test_localtime
assert_equal @twz.localtime, @twz.utc.getlocal
+ assert_instance_of Time, @twz.localtime
+ assert_instance_of Time, @dt_twz.localtime
end
def test_utc?
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index 757e600646..04e7b24d30 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -76,6 +76,7 @@ class DependenciesTest < ActiveSupport::TestCase
def test_dependency_which_raises_exception_isnt_added_to_loaded_set
with_loading do
filename = 'dependencies/raises_exception'
+ expanded = File.expand_path(filename)
$raises_exception_load_count = 0
5.times do |count|
@@ -86,8 +87,8 @@ class DependenciesTest < ActiveSupport::TestCase
assert_equal 'Loading me failed, so do not add to loaded or history.', e.message
assert_equal count + 1, $raises_exception_load_count
- assert_not ActiveSupport::Dependencies.loaded.include?(filename)
- assert_not ActiveSupport::Dependencies.history.include?(filename)
+ assert_not ActiveSupport::Dependencies.loaded.include?(expanded)
+ assert_not ActiveSupport::Dependencies.history.include?(expanded)
end
end
end
@@ -268,6 +269,28 @@ class DependenciesTest < ActiveSupport::TestCase
remove_constants(:ModuleFolder)
end
+ def test_raising_discards_autoloaded_constants
+ with_autoloading_fixtures do
+ assert_raises(Exception, 'arbitray exception message') { RaisesArbitraryException }
+ assert_not defined?(A)
+ assert_not defined?(RaisesArbitraryException)
+ end
+ ensure
+ remove_constants(:A, :RaisesArbitraryException)
+ end
+
+ def test_throwing_discards_autoloaded_constants
+ with_autoloading_fixtures do
+ catch :t do
+ Throws
+ end
+ assert_not defined?(A)
+ assert_not defined?(Throws)
+ end
+ ensure
+ remove_constants(:A, :Throws)
+ end
+
def test_doesnt_break_normal_require
path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
original_path = $:.dup
@@ -1047,12 +1070,4 @@ class DependenciesTest < ActiveSupport::TestCase
ensure
ActiveSupport::Dependencies.hook!
end
-
- def test_unhook
- ActiveSupport::Dependencies.unhook!
- assert !Module.new.respond_to?(:const_missing_without_dependencies)
- assert !Module.new.respond_to?(:load_without_new_constant_marking)
- ensure
- ActiveSupport::Dependencies.hook!
- end
end
diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb
index 58a0a3964d..dbde3d2e15 100644
--- a/activesupport/test/deprecation_test.rb
+++ b/activesupport/test/deprecation_test.rb
@@ -105,13 +105,13 @@ class DeprecationTest < ActiveSupport::TestCase
ActiveSupport::Deprecation.behavior = :raise
message = 'Revise this deprecated stuff now!'
- callstack = %w(foo bar baz)
+ callstack = caller_locations
e = assert_raise ActiveSupport::DeprecationException do
ActiveSupport::Deprecation.behavior.first.call(message, callstack)
end
assert_equal message, e.message
- assert_equal callstack, e.backtrace
+ assert_equal callstack.map(&:to_s), e.backtrace.map(&:to_s)
end
def test_default_stderr_behavior
@@ -143,7 +143,7 @@ class DeprecationTest < ActiveSupport::TestCase
stderr_output = capture(:stderr) {
assert_nil behavior.call('Some error!', ['call stack!'])
}
- assert stderr_output.blank?
+ assert stderr_output.empty?
end
def test_deprecated_instance_variable_proxy
@@ -341,7 +341,7 @@ class DeprecationTest < ActiveSupport::TestCase
end
def test_default_deprecation_horizon_should_always_bigger_than_current_rails_version
- assert ActiveSupport::Deprecation.new.deprecation_horizon > ActiveSupport::VERSION::STRING
+ assert_operator ActiveSupport::Deprecation.new.deprecation_horizon, :>, ActiveSupport::VERSION::STRING
end
def test_default_gem_name
diff --git a/activesupport/test/evented_file_update_checker_test.rb b/activesupport/test/evented_file_update_checker_test.rb
index bc3f77bd54..2cb2d8167f 100644
--- a/activesupport/test/evented_file_update_checker_test.rb
+++ b/activesupport/test/evented_file_update_checker_test.rb
@@ -11,7 +11,7 @@ class EventedFileUpdateCheckerTest < ActiveSupport::TestCase
end
def new_checker(files = [], dirs = {}, &block)
- ActiveSupport::EventedFileUpdateChecker.new(files, dirs, &block).tap do
+ ActiveSupport::EventedFileUpdateChecker.new(files, dirs, &block).tap do |c|
wait
end
end
@@ -34,6 +34,48 @@ class EventedFileUpdateCheckerTest < ActiveSupport::TestCase
super
wait
end
+
+ test 'notifies forked processes' do
+ FileUtils.touch(tmpfiles)
+
+ checker = new_checker(tmpfiles) { }
+ assert !checker.updated?
+
+ # Pipes used for flow controll across fork.
+ boot_reader, boot_writer = IO.pipe
+ touch_reader, touch_writer = IO.pipe
+
+ pid = fork do
+ assert checker.updated?
+
+ # Clear previous check value.
+ checker.execute
+ assert !checker.updated?
+
+ # Fork is booted, ready for file to be touched
+ # notify parent process.
+ boot_writer.write("booted")
+
+ # Wait for parent process to signal that file
+ # has been touched.
+ IO.select([touch_reader])
+
+ assert checker.updated?
+ end
+
+ assert pid
+
+ # Wait for fork to be booted before touching files.
+ IO.select([boot_reader])
+ touch(tmpfiles)
+
+ # Notify fork that files have been touched.
+ touch_writer.write("touched")
+
+ assert checker.updated?
+
+ Process.wait(pid)
+ end
end
class EventedFileUpdateCheckerPathHelperTest < ActiveSupport::TestCase
diff --git a/activesupport/test/executor_test.rb b/activesupport/test/executor_test.rb
new file mode 100644
index 0000000000..d9b389461a
--- /dev/null
+++ b/activesupport/test/executor_test.rb
@@ -0,0 +1,183 @@
+require 'abstract_unit'
+
+class ExecutorTest < ActiveSupport::TestCase
+ class DummyError < RuntimeError
+ end
+
+ def test_wrap_invokes_callbacks
+ called = []
+ executor.to_run { called << :run }
+ executor.to_complete { called << :complete }
+
+ executor.wrap do
+ called << :body
+ end
+
+ assert_equal [:run, :body, :complete], called
+ end
+
+ def test_callbacks_share_state
+ result = false
+ executor.to_run { @foo = true }
+ executor.to_complete { result = @foo }
+
+ executor.wrap { }
+
+ assert result
+ end
+
+ def test_separated_calls_invoke_callbacks
+ called = []
+ executor.to_run { called << :run }
+ executor.to_complete { called << :complete }
+
+ state = executor.run!
+ called << :body
+ state.complete!
+
+ assert_equal [:run, :body, :complete], called
+ end
+
+ def test_exceptions_unwind
+ called = []
+ executor.to_run { called << :run_1 }
+ executor.to_run { raise DummyError }
+ executor.to_run { called << :run_2 }
+ executor.to_complete { called << :complete }
+
+ assert_raises(DummyError) do
+ executor.wrap { called << :body }
+ end
+
+ assert_equal [:run_1, :complete], called
+ end
+
+ def test_avoids_double_wrapping
+ called = []
+ executor.to_run { called << :run }
+ executor.to_complete { called << :complete }
+
+ executor.wrap do
+ called << :early
+ executor.wrap do
+ called << :body
+ end
+ called << :late
+ end
+
+ assert_equal [:run, :early, :body, :late, :complete], called
+ end
+
+ def test_hooks_carry_state
+ supplied_state = :none
+
+ hook = Class.new do
+ define_method(:run) do
+ :some_state
+ end
+
+ define_method(:complete) do |state|
+ supplied_state = state
+ end
+ end.new
+
+ executor.register_hook(hook)
+
+ executor.wrap { }
+
+ assert_equal :some_state, supplied_state
+ end
+
+ def test_nil_state_is_sufficient
+ supplied_state = :none
+
+ hook = Class.new do
+ define_method(:run) do
+ nil
+ end
+
+ define_method(:complete) do |state|
+ supplied_state = state
+ end
+ end.new
+
+ executor.register_hook(hook)
+
+ executor.wrap { }
+
+ assert_equal nil, supplied_state
+ end
+
+ def test_exception_skips_uninvoked_hook
+ supplied_state = :none
+
+ hook = Class.new do
+ define_method(:run) do
+ :some_state
+ end
+
+ define_method(:complete) do |state|
+ supplied_state = state
+ end
+ end.new
+
+ executor.to_run do
+ raise DummyError
+ end
+ executor.register_hook(hook)
+
+ assert_raises(DummyError) do
+ executor.wrap { }
+ end
+
+ assert_equal :none, supplied_state
+ end
+
+ def test_exception_unwinds_invoked_hook
+ supplied_state = :none
+
+ hook = Class.new do
+ define_method(:run) do
+ :some_state
+ end
+
+ define_method(:complete) do |state|
+ supplied_state = state
+ end
+ end.new
+
+ executor.register_hook(hook)
+ executor.to_run do
+ raise DummyError
+ end
+
+ assert_raises(DummyError) do
+ executor.wrap { }
+ end
+
+ assert_equal :some_state, supplied_state
+ end
+
+ def test_separate_classes_can_wrap
+ other_executor = Class.new(ActiveSupport::Executor)
+
+ called = []
+ executor.to_run { called << :run }
+ executor.to_complete { called << :complete }
+ other_executor.to_run { called << :other_run }
+ other_executor.to_complete { called << :other_complete }
+
+ executor.wrap do
+ other_executor.wrap do
+ called << :body
+ end
+ end
+
+ assert_equal [:run, :other_run, :body, :other_complete, :complete], called
+ end
+
+ private
+ def executor
+ @executor ||= Class.new(ActiveSupport::Executor)
+ end
+end
diff --git a/activesupport/test/file_update_checker_shared_tests.rb b/activesupport/test/file_update_checker_shared_tests.rb
index 9c07e38fe5..40ae0c7617 100644
--- a/activesupport/test/file_update_checker_shared_tests.rb
+++ b/activesupport/test/file_update_checker_shared_tests.rb
@@ -5,19 +5,21 @@ module FileUpdateCheckerSharedTests
include FileUtils
def tmpdir
- @tmpdir ||= Dir.mktmpdir(nil, __dir__)
+ @tmpdir
end
def tmpfile(name)
- "#{tmpdir}/#{name}"
+ File.join(tmpdir, name)
end
def tmpfiles
@tmpfiles ||= %w(foo.rb bar.rb baz.rb).map { |f| tmpfile(f) }
end
- def teardown
- FileUtils.rm_rf(@tmpdir) if defined? @tmpdir
+ def run(*args)
+ capture_exceptions do
+ Dir.mktmpdir(nil, __dir__) { |dir| @tmpdir = dir; super }
+ end
end
test 'should not execute the block if no paths are given' do
@@ -134,6 +136,22 @@ module FileUpdateCheckerSharedTests
assert_equal 1, i
end
+ test 'should return max_time for files with mtime = Time.at(0)' do
+ i = 0
+
+ FileUtils.touch(tmpfiles)
+
+ time = Time.at(0) # wrong mtime from the future
+ File.utime(time, time, tmpfiles[0])
+
+ checker = new_checker(tmpfiles) { i += 1 }
+
+ touch(tmpfiles[1..-1])
+
+ assert checker.execute_if_updated
+ assert_equal 1, i
+ end
+
test 'should cache updated result until execute' do
i = 0
@@ -209,7 +227,7 @@ module FileUpdateCheckerSharedTests
assert !checker.execute_if_updated
assert_equal 0, i
- touch("#{subdir}/nested.rb")
+ touch(File.join(subdir, "nested.rb"))
assert checker.execute_if_updated
assert_equal 1, i
@@ -229,12 +247,12 @@ module FileUpdateCheckerSharedTests
assert_equal 0, i
# subdir does not look for Ruby files, but its parent tmpdir does.
- touch("#{subdir}/nested.rb")
+ touch(File.join(subdir, "nested.rb"))
assert checker.execute_if_updated
assert_equal 1, i
- touch("#{subdir}/nested.txt")
+ touch(File.join(subdir, "nested.txt"))
assert checker.execute_if_updated
assert_equal 2, i
diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb
index 14fe97a986..c7dc1dadb6 100644
--- a/activesupport/test/inflector_test_cases.rb
+++ b/activesupport/test/inflector_test_cases.rb
@@ -270,7 +270,8 @@ module InflectorTestCases
"maybe you'll be there" => "Maybe You'll Be There",
"¿por qué?" => '¿Por Qué?',
"Fred’s" => "Fred’s",
- "Fred`s" => "Fred`s"
+ "Fred`s" => "Fred`s",
+ ActiveSupport::SafeBuffer.new("confirmation num") => "Confirmation Num"
}
OrdinalNumbers = {
diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index 9f4b62fd8b..5fc2e16336 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -422,6 +422,11 @@ EXPECTED
assert_equal '"1999-12-31T19:00:00.000-05:00"', ActiveSupport::JSON.encode(time)
end
+ def test_exception_to_json
+ exception = Exception.new("foo")
+ assert_equal '"foo"', ActiveSupport::JSON.encode(exception)
+ end
+
protected
def object_keys(json_object)
diff --git a/activesupport/test/logger_test.rb b/activesupport/test/logger_test.rb
index 317e09b7f2..dfc5f3fdf4 100644
--- a/activesupport/test/logger_test.rb
+++ b/activesupport/test/logger_test.rb
@@ -141,6 +141,52 @@ class LoggerTest < ActiveSupport::TestCase
assert @output.string.include?("THIS IS HERE")
end
+ def test_logger_silencing_works_for_broadcast
+ another_output = StringIO.new
+ another_logger = ActiveSupport::Logger.new(another_output)
+
+ @logger.extend ActiveSupport::Logger.broadcast(another_logger)
+
+ @logger.debug "CORRECT DEBUG"
+ @logger.silence do |logger|
+ assert_kind_of ActiveSupport::Logger, logger
+ @logger.debug "FAILURE"
+ @logger.error "CORRECT ERROR"
+ end
+
+ assert @output.string.include?("CORRECT DEBUG")
+ assert @output.string.include?("CORRECT ERROR")
+ assert_not @output.string.include?("FAILURE")
+
+ assert another_output.string.include?("CORRECT DEBUG")
+ assert another_output.string.include?("CORRECT ERROR")
+ assert_not another_output.string.include?("FAILURE")
+ end
+
+ def test_broadcast_silencing_does_not_break_plain_ruby_logger
+ another_output = StringIO.new
+ another_logger = ::Logger.new(another_output)
+
+ @logger.extend ActiveSupport::Logger.broadcast(another_logger)
+
+ @logger.debug "CORRECT DEBUG"
+ @logger.silence do |logger|
+ assert_kind_of ActiveSupport::Logger, logger
+ @logger.debug "FAILURE"
+ @logger.error "CORRECT ERROR"
+ end
+
+ assert @output.string.include?("CORRECT DEBUG")
+ assert @output.string.include?("CORRECT ERROR")
+ assert_not @output.string.include?("FAILURE")
+
+ assert another_output.string.include?("CORRECT DEBUG")
+ assert another_output.string.include?("CORRECT ERROR")
+ assert another_output.string.include?("FAILURE")
+ # We can't silence plain ruby Logger cause with thread safety
+ # but at least we don't break it
+ end
+
def test_logger_level_per_object_thread_safety
logger1 = Logger.new(StringIO.new)
logger2 = Logger.new(StringIO.new)
diff --git a/activesupport/test/multibyte_conformance_test.rb b/activesupport/test/multibyte_conformance_test.rb
index 5df8f32e46..9fca47a985 100644
--- a/activesupport/test/multibyte_conformance_test.rb
+++ b/activesupport/test/multibyte_conformance_test.rb
@@ -28,7 +28,7 @@ class MultibyteConformanceTest < ActiveSupport::TestCase
UNIDATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}/ucd"
UNIDATA_FILE = '/NormalizationTest.txt'
- CACHE_DIR = File.join(Dir.tmpdir, 'cache')
+ CACHE_DIR = "#{Dir.tmpdir}/cache/unicode_conformance"
FileUtils.mkdir_p(CACHE_DIR)
RUN_P = begin
Downloader.download(UNIDATA_URL + UNIDATA_FILE, CACHE_DIR + UNIDATA_FILE)
diff --git a/activesupport/test/multibyte_grapheme_break_conformance_test.rb b/activesupport/test/multibyte_grapheme_break_conformance_test.rb
index 229f24990e..6e2f02abed 100644
--- a/activesupport/test/multibyte_grapheme_break_conformance_test.rb
+++ b/activesupport/test/multibyte_grapheme_break_conformance_test.rb
@@ -27,7 +27,7 @@ class MultibyteGraphemeBreakConformanceTest < ActiveSupport::TestCase
TEST_DATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}/ucd/auxiliary"
TEST_DATA_FILE = '/GraphemeBreakTest.txt'
- CACHE_DIR = File.join(Dir.tmpdir, 'cache')
+ CACHE_DIR = "#{Dir.tmpdir}/cache/unicode_conformance"
def setup
FileUtils.mkdir_p(CACHE_DIR)
diff --git a/activesupport/test/multibyte_normalization_conformance_test.rb b/activesupport/test/multibyte_normalization_conformance_test.rb
index 8bc91ef708..0d31c9520f 100644
--- a/activesupport/test/multibyte_normalization_conformance_test.rb
+++ b/activesupport/test/multibyte_normalization_conformance_test.rb
@@ -30,7 +30,7 @@ class MultibyteNormalizationConformanceTest < ActiveSupport::TestCase
UNIDATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}/ucd"
UNIDATA_FILE = '/NormalizationTest.txt'
- CACHE_DIR = File.join(Dir.tmpdir, 'cache')
+ CACHE_DIR = "#{Dir.tmpdir}/cache/unicode_conformance"
def setup
FileUtils.mkdir_p(CACHE_DIR)
diff --git a/activesupport/test/number_helper_test.rb b/activesupport/test/number_helper_test.rb
index b3464462c8..074c872efc 100644
--- a/activesupport/test/number_helper_test.rb
+++ b/activesupport/test/number_helper_test.rb
@@ -57,6 +57,8 @@ module ActiveSupport
assert_equal("+18005551212", number_helper.number_to_phone(8005551212, :country_code => 1, :delimiter => ''))
assert_equal("22-555-1212", number_helper.number_to_phone(225551212))
assert_equal("+45-22-555-1212", number_helper.number_to_phone(225551212, :country_code => 45))
+ assert_equal("(755) 6123-4567", number_helper.number_to_phone(75561234567, pattern: /(\d{3,4})(\d{4})(\d{4})/, area_code: true))
+ assert_equal("133-1234-5678", number_helper.number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})/))
end
end
@@ -74,7 +76,6 @@ module ActiveSupport
assert_equal("1,234,567,890.50 K&#269;", number_helper.number_to_currency("1234567890.50", {:unit => "K&#269;", :format => "%n %u"}))
assert_equal("1,234,567,890.50 - K&#269;", number_helper.number_to_currency("-1234567890.50", {:unit => "K&#269;", :format => "%n %u", :negative_format => "%n - %u"}))
assert_equal("0.00", number_helper.number_to_currency(+0.0, {:unit => "", :negative_format => "(%n)"}))
- assert_equal("(0.00)", number_helper.number_to_currency(-0.0, {:unit => "", :negative_format => "(%n)"}))
end
end
diff --git a/activesupport/test/reloader_test.rb b/activesupport/test/reloader_test.rb
new file mode 100644
index 0000000000..958cb49993
--- /dev/null
+++ b/activesupport/test/reloader_test.rb
@@ -0,0 +1,85 @@
+require 'abstract_unit'
+
+class ReloaderTest < ActiveSupport::TestCase
+ def test_prepare_callback
+ prepared = false
+ reloader.to_prepare { prepared = true }
+
+ assert !prepared
+ reloader.prepare!
+ assert prepared
+
+ prepared = false
+ reloader.wrap do
+ assert prepared
+ prepared = false
+ end
+ assert !prepared
+ end
+
+ def test_only_run_when_check_passes
+ r = new_reloader { true }
+ invoked = false
+ r.to_run { invoked = true }
+ r.wrap { }
+ assert invoked
+
+ r = new_reloader { false }
+ invoked = false
+ r.to_run { invoked = true }
+ r.wrap { }
+ assert !invoked
+ end
+
+ def test_full_reload_sequence
+ called = []
+ reloader.to_prepare { called << :prepare }
+ reloader.to_run { called << :reloader_run }
+ reloader.to_complete { called << :reloader_complete }
+ reloader.executor.to_run { called << :executor_run }
+ reloader.executor.to_complete { called << :executor_complete }
+
+ reloader.wrap { }
+ assert_equal [:executor_run, :reloader_run, :prepare, :reloader_complete, :executor_complete], called
+
+ called = []
+ reloader.reload!
+ assert_equal [:executor_run, :reloader_run, :prepare, :reloader_complete, :executor_complete, :prepare], called
+
+ reloader.check = lambda { false }
+
+ called = []
+ reloader.wrap { }
+ assert_equal [:executor_run, :executor_complete], called
+
+ called = []
+ reloader.reload!
+ assert_equal [:executor_run, :reloader_run, :prepare, :reloader_complete, :executor_complete, :prepare], called
+ end
+
+ def test_class_unload_block
+ called = []
+ reloader.before_class_unload { called << :before_unload }
+ reloader.after_class_unload { called << :after_unload }
+ reloader.to_run do
+ class_unload! do
+ called << :unload
+ end
+ end
+ reloader.wrap { called << :body }
+
+ assert_equal [:before_unload, :unload, :after_unload, :body], called
+ end
+
+ private
+ def new_reloader(&check)
+ Class.new(ActiveSupport::Reloader).tap do |r|
+ r.check = check
+ r.executor = Class.new(ActiveSupport::Executor)
+ end
+ end
+
+ def reloader
+ @reloader ||= new_reloader { true }
+ end
+end
diff --git a/activesupport/test/rescuable_test.rb b/activesupport/test/rescuable_test.rb
index bd43ad0797..e42e6d2973 100644
--- a/activesupport/test/rescuable_test.rb
+++ b/activesupport/test/rescuable_test.rb
@@ -3,9 +3,6 @@ require 'abstract_unit'
class WraithAttack < StandardError
end
-class NuclearExplosion < StandardError
-end
-
class MadRonon < StandardError
end
@@ -19,6 +16,10 @@ module WeirdError
end
class Stargate
+ # Nest this so the 'NuclearExplosion' handler needs a lexical const_get
+ # to find it.
+ class NuclearExplosion < StandardError; end
+
attr_accessor :result
include ActiveSupport::Rescuable
@@ -57,6 +58,14 @@ class Stargate
raise MadRonon.new("dex")
end
+ def fall_back_to_cause
+ # This exception is the cause and has a handler.
+ ronanize
+ rescue
+ # This is the exception we'll handle that doesn't have a cause.
+ raise 'unhandled RuntimeError with a handleable cause'
+ end
+
def weird
StandardError.new.tap do |exc|
def exc.weird?
@@ -74,7 +83,6 @@ class Stargate
def sos_first
@result = 'sos_first'
end
-
end
class CoolStargate < Stargate
@@ -127,4 +135,9 @@ class RescuableTest < ActiveSupport::TestCase
result = @cool_stargate.send(:rescue_handlers).collect(&:first)
assert_equal expected, result
end
+
+ def test_rescue_falls_back_to_exception_cause
+ @stargate.dispatch :fall_back_to_cause
+ assert_equal 'unhandled RuntimeError with a handleable cause', @stargate.result
+ end
end
diff --git a/activesupport/test/share_lock_test.rb b/activesupport/test/share_lock_test.rb
index 465a657308..acefa185a8 100644
--- a/activesupport/test/share_lock_test.rb
+++ b/activesupport/test/share_lock_test.rb
@@ -114,14 +114,17 @@ class ShareLockTest < ActiveSupport::TestCase
[true, false].each do |use_upgrading|
with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
begin
+ together = Concurrent::CyclicBarrier.new(2)
conflicting_exclusive_threads = [
Thread.new do
@lock.send(use_upgrading ? :sharing : :tap) do
+ together.wait
@lock.exclusive(purpose: :red, compatible: [:green, :purple]) {}
end
end,
Thread.new do
@lock.send(use_upgrading ? :sharing : :tap) do
+ together.wait
@lock.exclusive(purpose: :blue, compatible: [:green]) {}
end
end
@@ -183,11 +186,14 @@ class ShareLockTest < ActiveSupport::TestCase
load_params = [:load, [:load]]
unload_params = [:unload, [:unload, :load]]
+ all_sharing = Concurrent::CyclicBarrier.new(4)
+
[load_params, load_params, unload_params, unload_params].permutation do |thread_params|
with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
threads = thread_params.map do |purpose, compatible|
Thread.new do
@lock.sharing do
+ all_sharing.wait
@lock.exclusive(purpose: purpose, compatible: compatible) do
scratch_pad_mutex.synchronize { scratch_pad << purpose }
end
@@ -209,6 +215,245 @@ class ShareLockTest < ActiveSupport::TestCase
end
end
+ def test_new_share_attempts_block_on_waiting_exclusive
+ with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
+ release_exclusive = Concurrent::CountDownLatch.new
+
+ waiting_exclusive = Thread.new do
+ @lock.sharing do
+ @lock.exclusive do
+ release_exclusive.wait
+ end
+ end
+ end
+ assert_threads_stuck waiting_exclusive
+
+ late_share_attempt = Thread.new do
+ @lock.sharing {}
+ end
+ assert_threads_stuck late_share_attempt
+
+ sharing_thread_release_latch.count_down
+ assert_threads_stuck late_share_attempt
+
+ release_exclusive.count_down
+ assert_threads_not_stuck late_share_attempt
+ end
+ end
+
+ def test_share_remains_reentrant_ignoring_a_waiting_exclusive
+ with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
+ ready = Concurrent::CyclicBarrier.new(2)
+ attempt_reentrancy = Concurrent::CountDownLatch.new
+
+ sharer = Thread.new do
+ @lock.sharing do
+ ready.wait
+ attempt_reentrancy.wait
+ @lock.sharing {}
+ end
+ end
+
+ exclusive = Thread.new do
+ @lock.sharing do
+ ready.wait
+ @lock.exclusive {}
+ end
+ end
+
+ assert_threads_stuck exclusive
+
+ attempt_reentrancy.count_down
+
+ assert_threads_not_stuck sharer
+ assert_threads_stuck exclusive
+ end
+ end
+
+ def test_compatible_exclusives_cooperate_to_both_proceed
+ ready = Concurrent::CyclicBarrier.new(2)
+ done = Concurrent::CyclicBarrier.new(2)
+
+ threads = 2.times.map do
+ Thread.new do
+ @lock.sharing do
+ ready.wait
+ @lock.exclusive(purpose: :x, compatible: [:x], after_compatible: [:x]) {}
+ done.wait
+ end
+ end
+ end
+
+ assert_threads_not_stuck threads
+ end
+
+ def test_manual_yield
+ ready = Concurrent::CyclicBarrier.new(2)
+ done = Concurrent::CyclicBarrier.new(2)
+
+ threads = [
+ Thread.new do
+ @lock.sharing do
+ ready.wait
+ @lock.exclusive(purpose: :x) {}
+ done.wait
+ end
+ end,
+
+ Thread.new do
+ @lock.sharing do
+ ready.wait
+ @lock.yield_shares(compatible: [:x]) do
+ done.wait
+ end
+ end
+ end,
+ ]
+
+ assert_threads_not_stuck threads
+ end
+
+ def test_manual_incompatible_yield
+ ready = Concurrent::CyclicBarrier.new(2)
+ done = Concurrent::CyclicBarrier.new(2)
+
+ threads = [
+ Thread.new do
+ @lock.sharing do
+ ready.wait
+ @lock.exclusive(purpose: :x) {}
+ done.wait
+ end
+ end,
+
+ Thread.new do
+ @lock.sharing do
+ ready.wait
+ @lock.yield_shares(compatible: [:y]) do
+ done.wait
+ end
+ end
+ end,
+ ]
+
+ assert_threads_stuck threads
+ ensure
+ threads.each(&:kill) if threads
+ end
+
+ def test_manual_recursive_yield
+ ready = Concurrent::CyclicBarrier.new(2)
+ done = Concurrent::CyclicBarrier.new(2)
+ do_nesting = Concurrent::CountDownLatch.new
+
+ threads = [
+ Thread.new do
+ @lock.sharing do
+ ready.wait
+ @lock.exclusive(purpose: :x) {}
+ done.wait
+ end
+ end,
+
+ Thread.new do
+ @lock.sharing do
+ @lock.yield_shares(compatible: [:x]) do
+ @lock.sharing do
+ ready.wait
+ do_nesting.wait
+ @lock.yield_shares(compatible: [:x, :y]) do
+ done.wait
+ end
+ end
+ end
+ end
+ end
+ ]
+
+ assert_threads_stuck threads
+ do_nesting.count_down
+
+ assert_threads_not_stuck threads
+ end
+
+ def test_manual_recursive_yield_cannot_expand_outer_compatible
+ ready = Concurrent::CyclicBarrier.new(2)
+ do_compatible_nesting = Concurrent::CountDownLatch.new
+ in_compatible_nesting = Concurrent::CountDownLatch.new
+
+ incompatible_thread = Thread.new do
+ @lock.sharing do
+ ready.wait
+ @lock.exclusive(purpose: :x) {}
+ end
+ end
+
+ yield_shares_thread = Thread.new do
+ @lock.sharing do
+ ready.wait
+ @lock.yield_shares(compatible: [:y]) do
+ do_compatible_nesting.wait
+ @lock.sharing do
+ @lock.yield_shares(compatible: [:x, :y]) do
+ in_compatible_nesting.wait
+ end
+ end
+ end
+ end
+ end
+
+ assert_threads_stuck incompatible_thread
+ do_compatible_nesting.count_down
+ assert_threads_stuck incompatible_thread
+ in_compatible_nesting.count_down
+ assert_threads_not_stuck [yield_shares_thread, incompatible_thread]
+ end
+
+ def test_manual_recursive_yield_restores_previous_compatible
+ ready = Concurrent::CyclicBarrier.new(2)
+ do_nesting = Concurrent::CountDownLatch.new
+ after_nesting = Concurrent::CountDownLatch.new
+
+ incompatible_thread = Thread.new do
+ ready.wait
+ @lock.exclusive(purpose: :z) {}
+ end
+
+ recursive_yield_shares_thread = Thread.new do
+ @lock.sharing do
+ ready.wait
+ @lock.yield_shares(compatible: [:y]) do
+ do_nesting.wait
+ @lock.sharing do
+ @lock.yield_shares(compatible: [:x, :y]) {}
+ end
+ after_nesting.wait
+ end
+ end
+ end
+
+ assert_threads_stuck incompatible_thread
+ do_nesting.count_down
+ assert_threads_stuck incompatible_thread
+
+ compatible_thread = Thread.new do
+ @lock.exclusive(purpose: :y) {}
+ end
+ assert_threads_not_stuck compatible_thread
+
+ post_nesting_incompatible_thread = Thread.new do
+ @lock.exclusive(purpose: :x) {}
+ end
+ assert_threads_stuck post_nesting_incompatible_thread
+
+ after_nesting.count_down
+ assert_threads_not_stuck recursive_yield_shares_thread
+ # post_nesting_incompatible_thread can now proceed
+ assert_threads_not_stuck post_nesting_incompatible_thread
+ # assert_threads_not_stuck can now proceed
+ assert_threads_not_stuck incompatible_thread
+ end
+
def test_in_shared_section_incompatible_non_upgrading_threads_cannot_preempt_upgrading_threads
scratch_pad = []
scratch_pad_mutex = Mutex.new
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index 00d40c4497..a15d5c6a0e 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -395,6 +395,17 @@ class TimeZoneTest < ActiveSupport::TestCase
assert_equal(-18_000, zone.utc_offset)
end
+ def test_utc_offset_is_not_cached_when_current_period_gets_stale
+ tz = ActiveSupport::TimeZone.create('Moscow')
+ travel_to(Time.utc(2014, 10, 25, 21)) do # 1 hour before TZ change
+ assert_equal 14400, tz.utc_offset, 'utc_offset should be initialized according to current_period'
+ end
+
+ travel_to(Time.utc(2014, 10, 25, 22)) do # after TZ change
+ assert_equal 10800, tz.utc_offset, 'utc_offset should not be cached when current_period gets stale'
+ end
+ end
+
def test_seconds_to_utc_offset_with_colon
assert_equal "-06:00", ActiveSupport::TimeZone.seconds_to_utc_offset(-21_600)
assert_equal "+00:00", ActiveSupport::TimeZone.seconds_to_utc_offset(0)
@@ -491,6 +502,11 @@ class TimeZoneTest < ActiveSupport::TestCase
assert !ActiveSupport::TimeZone.us_zones.include?(ActiveSupport::TimeZone["Kuala Lumpur"])
end
+ def test_country_zones
+ assert ActiveSupport::TimeZone.country_zones("ru").include?(ActiveSupport::TimeZone["Moscow"])
+ assert !ActiveSupport::TimeZone.country_zones(:ru).include?(ActiveSupport::TimeZone["Kuala Lumpur"])
+ 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)
diff --git a/activesupport/test/time_zone_test_helpers.rb b/activesupport/test/time_zone_test_helpers.rb
index 9632b89d09..eb6f7d0f85 100644
--- a/activesupport/test/time_zone_test_helpers.rb
+++ b/activesupport/test/time_zone_test_helpers.rb
@@ -13,4 +13,12 @@ module TimeZoneTestHelpers
ensure
old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
end
+
+ def with_preserve_timezone(value)
+ old_preserve_tz = ActiveSupport.to_time_preserves_timezone
+ ActiveSupport.to_time_preserves_timezone = value
+ yield
+ ensure
+ ActiveSupport.to_time_preserves_timezone = old_preserve_tz
+ end
end
diff --git a/activesupport/test/xml_mini/rexml_engine_test.rb b/activesupport/test/xml_mini/rexml_engine_test.rb
index f0067ca656..6e9ce7ac11 100644
--- a/activesupport/test/xml_mini/rexml_engine_test.rb
+++ b/activesupport/test/xml_mini/rexml_engine_test.rb
@@ -22,14 +22,24 @@ class REXMLEngineTest < ActiveSupport::TestCase
morning
</root>
eoxml
- assert_equal_rexml(io)
+ hash = ActiveSupport::XmlMini.parse(io)
+ assert hash.has_key?('root')
+ assert hash['root'].has_key?('products')
+ assert_match "good", hash['root']['__content__']
+ products = hash['root']['products']
+ assert products.has_key?("__content__")
+ assert_match 'hello everyone', products['__content__']
+ end
+
+ def test_parse_from_empty_string
+ ActiveSupport::XmlMini.backend = 'REXML'
+ assert_equal({}, ActiveSupport::XmlMini.parse(""))
+ end
+
+ def test_parse_from_frozen_string
+ ActiveSupport::XmlMini.backend = 'REXML'
+ xml_string = "<root></root>".freeze
+ assert_equal({"root" => {}}, ActiveSupport::XmlMini.parse(xml_string))
end
- private
- def assert_equal_rexml(xml)
- parsed_xml = ActiveSupport::XmlMini.parse(xml)
- xml.rewind if xml.respond_to?(:rewind)
- hash = ActiveSupport::XmlMini.with_backend('REXML') { ActiveSupport::XmlMini.parse(xml) }
- assert_equal(hash, parsed_xml)
- end
end