diff options
Diffstat (limited to 'activesupport/test')
64 files changed, 2815 insertions, 994 deletions
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb index 7ffcae6007..c0e23e89f7 100644 --- a/activesupport/test/abstract_unit.rb +++ b/activesupport/test/abstract_unit.rb @@ -15,6 +15,7 @@ silence_warnings do end require 'active_support/testing/autorun' +require 'active_support/testing/method_call_assertions' ENV['NO_RELOAD'] = '1' require 'active_support' @@ -37,4 +38,6 @@ def jruby_skip(message = '') skip message if defined?(JRUBY_VERSION) end -require 'mocha/setup' # FIXME: stop using mocha +class ActiveSupport::TestCase + include ActiveSupport::Testing::MethodCallAssertions +end diff --git a/activesupport/test/array_inquirer_test.rb b/activesupport/test/array_inquirer_test.rb index b25e5cca86..263ab3802b 100644 --- a/activesupport/test/array_inquirer_test.rb +++ b/activesupport/test/array_inquirer_test.rb @@ -3,7 +3,7 @@ require 'active_support/core_ext/array' class ArrayInquirerTest < ActiveSupport::TestCase def setup - @array_inquirer = ActiveSupport::ArrayInquirer.new([:mobile, :tablet]) + @array_inquirer = ActiveSupport::ArrayInquirer.new([:mobile, :tablet, 'api']) end def test_individual @@ -18,6 +18,11 @@ class ArrayInquirerTest < ActiveSupport::TestCase assert_not @array_inquirer.any?(:desktop, :watch) end + def test_any_string_symbol_mismatch + assert @array_inquirer.any?('mobile') + assert @array_inquirer.any?(:api) + end + def test_any_with_block assert @array_inquirer.any? { |v| v == :mobile } assert_not @array_inquirer.any? { |v| v == :desktop } @@ -28,7 +33,7 @@ class ArrayInquirerTest < ActiveSupport::TestCase end def test_inquiry - result = [:mobile, :tablet].inquiry + result = [:mobile, :tablet, 'api'].inquiry assert_instance_of ActiveSupport::ArrayInquirer, result assert_equal @array_inquirer, result diff --git a/activesupport/test/autoloading_fixtures/a/c/e/f.rb b/activesupport/test/autoloading_fixtures/a/c/e/f.rb deleted file mode 100644 index 57dba5a307..0000000000 --- a/activesupport/test/autoloading_fixtures/a/c/e/f.rb +++ /dev/null @@ -1,2 +0,0 @@ -class A::C::E::F -end
\ No newline at end of file diff --git a/activesupport/test/autoloading_fixtures/a/c/em/f.rb b/activesupport/test/autoloading_fixtures/a/c/em/f.rb new file mode 100644 index 0000000000..8b28e19148 --- /dev/null +++ b/activesupport/test/autoloading_fixtures/a/c/em/f.rb @@ -0,0 +1,2 @@ +class A::C::EM::F +end
\ No newline at end of file diff --git a/activesupport/test/autoloading_fixtures/d.rb b/activesupport/test/autoloading_fixtures/d.rb new file mode 100644 index 0000000000..45c794d4ca --- /dev/null +++ b/activesupport/test/autoloading_fixtures/d.rb @@ -0,0 +1,2 @@ +class D +end
\ No newline at end of file diff --git a/activesupport/test/autoloading_fixtures/e.rb b/activesupport/test/autoloading_fixtures/e.rb deleted file mode 100644 index 2f59e4fb75..0000000000 --- a/activesupport/test/autoloading_fixtures/e.rb +++ /dev/null @@ -1,2 +0,0 @@ -class E -end
\ No newline at end of file diff --git a/activesupport/test/autoloading_fixtures/em.rb b/activesupport/test/autoloading_fixtures/em.rb new file mode 100644 index 0000000000..16a1838667 --- /dev/null +++ b/activesupport/test/autoloading_fixtures/em.rb @@ -0,0 +1,2 @@ +class EM +end
\ No newline at end of file diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 527538ed9a..4a299429f3 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -133,37 +133,42 @@ class CacheStoreSettingTest < ActiveSupport::TestCase end def test_mem_cache_fragment_cache_store - Dalli::Client.expects(:new).with(%w[localhost], {}) - store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost" - assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) + assert_called_with(Dalli::Client, :new, [%w[localhost], {}]) do + store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost" + assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) + end end def test_mem_cache_fragment_cache_store_with_given_mem_cache mem_cache = Dalli::Client.new - Dalli::Client.expects(:new).never - store = ActiveSupport::Cache.lookup_store :mem_cache_store, mem_cache - assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) + assert_not_called(Dalli::Client, :new) do + store = ActiveSupport::Cache.lookup_store :mem_cache_store, mem_cache + assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) + end end def test_mem_cache_fragment_cache_store_with_not_dalli_client - Dalli::Client.expects(:new).never - memcache = Object.new - assert_raises(ArgumentError) do - ActiveSupport::Cache.lookup_store :mem_cache_store, memcache + assert_not_called(Dalli::Client, :new) do + memcache = Object.new + assert_raises(ArgumentError) do + ActiveSupport::Cache.lookup_store :mem_cache_store, memcache + end end end def test_mem_cache_fragment_cache_store_with_multiple_servers - Dalli::Client.expects(:new).with(%w[localhost 192.168.1.1], {}) - store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1' - assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) + assert_called_with(Dalli::Client, :new, [%w[localhost 192.168.1.1], {}]) do + store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1' + assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) + end end def test_mem_cache_fragment_cache_store_with_options - Dalli::Client.expects(:new).with(%w[localhost 192.168.1.1], { :timeout => 10 }) - store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1', :namespace => 'foo', :timeout => 10 - assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) - assert_equal 'foo', store.options[:namespace] + assert_called_with(Dalli::Client, :new, [%w[localhost 192.168.1.1], { :timeout => 10 }]) do + store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1', :namespace => 'foo', :timeout => 10 + assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) + assert_equal 'foo', store.options[:namespace] + end end def test_object_assigned_fragment_cache_store @@ -224,13 +229,15 @@ module CacheStoreBehavior def test_fetch_without_cache_miss @cache.write('foo', 'bar') - @cache.expects(:write).never - assert_equal 'bar', @cache.fetch('foo') { 'baz' } + assert_not_called(@cache, :write) do + assert_equal 'bar', @cache.fetch('foo') { 'baz' } + end end def test_fetch_with_cache_miss - @cache.expects(:write).with('foo', 'baz', @cache.options) - assert_equal 'baz', @cache.fetch('foo') { 'baz' } + assert_called_with(@cache, :write, ['foo', 'baz', @cache.options]) do + assert_equal 'baz', @cache.fetch('foo') { 'baz' } + end end def test_fetch_with_cache_miss_passes_key_to_block @@ -245,15 +252,18 @@ module CacheStoreBehavior def test_fetch_with_forced_cache_miss @cache.write('foo', 'bar') - @cache.expects(:read).never - @cache.expects(:write).with('foo', 'bar', @cache.options.merge(:force => true)) - @cache.fetch('foo', :force => true) { 'bar' } + assert_not_called(@cache, :read) do + assert_called_with(@cache, :write, ['foo', 'bar', @cache.options.merge(:force => true)]) do + @cache.fetch('foo', :force => true) { 'bar' } + end + end end def test_fetch_with_cached_nil @cache.write('foo', nil) - @cache.expects(:write).never - assert_nil @cache.fetch('foo') { 'baz' } + assert_not_called(@cache, :write) do + assert_nil @cache.fetch('foo') { 'baz' } + end end def test_should_read_and_write_hash @@ -288,8 +298,9 @@ module CacheStoreBehavior @cache.write('foo', 'bar', :expires_in => 10) @cache.write('fu', 'baz') @cache.write('fud', 'biz') - Time.stubs(:now).returns(time + 11) - assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu')) + Time.stub(:now, time + 11) do + assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu')) + end end def test_fetch_multi @@ -303,8 +314,9 @@ module CacheStoreBehavior end def test_multi_with_objects - foo = stub(:title => 'FOO!', :cache_key => 'foo') - bar = stub(:cache_key => 'bar') + cache_struct = Struct.new(:cache_key, :title) + foo = cache_struct.new('foo', 'FOO!') + bar = cache_struct.new('bar') @cache.write('bar', 'BAM!') @@ -387,66 +399,74 @@ module CacheStoreBehavior def test_expires_in time = Time.local(2008, 4, 24) - Time.stubs(:now).returns(time) - @cache.write('foo', 'bar') - assert_equal 'bar', @cache.read('foo') + Time.stub(:now, time) do + @cache.write('foo', 'bar') + assert_equal 'bar', @cache.read('foo') + end - Time.stubs(:now).returns(time + 30) - assert_equal 'bar', @cache.read('foo') + Time.stub(:now, time + 30) do + assert_equal 'bar', @cache.read('foo') + end - Time.stubs(:now).returns(time + 61) - assert_nil @cache.read('foo') + Time.stub(:now, time + 61) do + assert_nil @cache.read('foo') + end end def test_race_condition_protection_skipped_if_not_defined @cache.write('foo', 'bar') - time = @cache.send(:read_entry, 'foo', {}).expires_at - Time.stubs(:now).returns(Time.at(time)) + time = @cache.send(:read_entry, @cache.send(:normalize_key, 'foo', {}), {}).expires_at - result = @cache.fetch('foo') do - assert_equal nil, @cache.read('foo') - 'baz' + Time.stub(:now, Time.at(time)) do + result = @cache.fetch('foo') do + assert_equal nil, @cache.read('foo') + 'baz' + end + assert_equal 'baz', result end - assert_equal 'baz', result end def test_race_condition_protection_is_limited time = Time.now @cache.write('foo', 'bar', :expires_in => 60) - Time.stubs(:now).returns(time + 71) - result = @cache.fetch('foo', :race_condition_ttl => 10) do - assert_equal nil, @cache.read('foo') - "baz" + Time.stub(:now, time + 71) do + result = @cache.fetch('foo', :race_condition_ttl => 10) do + assert_equal nil, @cache.read('foo') + "baz" + end + assert_equal "baz", result end - assert_equal "baz", result end def test_race_condition_protection_is_safe time = Time.now @cache.write('foo', 'bar', :expires_in => 60) - Time.stubs(:now).returns(time + 61) - begin - @cache.fetch('foo', :race_condition_ttl => 10) do - assert_equal 'bar', @cache.read('foo') - raise ArgumentError.new + Time.stub(:now, time + 61) do + begin + @cache.fetch('foo', :race_condition_ttl => 10) do + assert_equal 'bar', @cache.read('foo') + raise ArgumentError.new + end + rescue ArgumentError end - rescue ArgumentError + assert_equal "bar", @cache.read('foo') + end + Time.stub(:now, time + 91) do + assert_nil @cache.read('foo') end - assert_equal "bar", @cache.read('foo') - Time.stubs(:now).returns(time + 91) - assert_nil @cache.read('foo') end def test_race_condition_protection time = Time.now @cache.write('foo', 'bar', :expires_in => 60) - Time.stubs(:now).returns(time + 61) - result = @cache.fetch('foo', :race_condition_ttl => 10) do - assert_equal 'bar', @cache.read('foo') - "baz" + Time.stub(:now, time + 61) do + result = @cache.fetch('foo', :race_condition_ttl => 10) do + assert_equal 'bar', @cache.read('foo') + "baz" + end + assert_equal "baz", result end - assert_equal "baz", result end def test_crazy_key_characters @@ -470,6 +490,44 @@ module CacheStoreBehavior assert_equal({key => "bar"}, @cache.read_multi(key)) assert @cache.delete(key) end + + def test_cache_hit_instrumentation + key = "test_key" + @events = [] + ActiveSupport::Notifications.subscribe "cache_read.active_support" do |*args| + @events << ActiveSupport::Notifications::Event.new(*args) + end + assert @cache.write(key, "1", :raw => true) + assert @cache.fetch(key) {} + assert_equal 1, @events.length + assert_equal 'cache_read.active_support', @events[0].name + assert_equal :fetch, @events[0].payload[:super_operation] + assert @events[0].payload[:hit] + ensure + ActiveSupport::Notifications.unsubscribe "cache_read.active_support" + end + + def test_cache_miss_instrumentation + @events = [] + ActiveSupport::Notifications.subscribe(/^cache_(.*)\.active_support$/) do |*args| + @events << ActiveSupport::Notifications::Event.new(*args) + end + assert_not @cache.fetch("bad_key") {} + assert_equal 3, @events.length + assert_equal 'cache_read.active_support', @events[0].name + assert_equal 'cache_generate.active_support', @events[1].name + assert_equal 'cache_write.active_support', @events[2].name + assert_equal :fetch, @events[0].payload[:super_operation] + assert_not @events[0].payload[:hit] + ensure + ActiveSupport::Notifications.unsubscribe "cache_read.active_support" + end + + def test_can_call_deprecated_namesaced_key + assert_deprecated "`namespaced_key` is deprecated" do + @cache.send(:namespaced_key, 111, {}) + end + end end # https://rails.lighthouseapp.com/projects/8994/tickets/6225-memcachestore-cant-deal-with-umlauts-and-special-characters @@ -577,6 +635,21 @@ module LocalCacheBehavior end end + def test_local_cache_of_read_nil + @cache.with_local_cache do + assert_equal nil, @cache.read('foo') + @cache.send(:bypass_local_cache) { @cache.write 'foo', 'bar' } + assert_equal nil, @cache.read('foo') + end + end + + def test_local_cache_fetch + @cache.with_local_cache do + @cache.send(:local_cache).write 'foo', 'bar' + assert_equal 'bar', @cache.send(:local_cache).fetch('foo') + end + end + def test_local_cache_of_write_nil @cache.with_local_cache do assert @cache.write('foo', nil) @@ -630,43 +703,52 @@ module LocalCacheBehavior app = @cache.middleware.new(app) app.call({}) end + + def test_can_call_deprecated_set_cache_value + @cache.with_local_cache do + assert_deprecated "`set_cache_value` is deprecated" do + @cache.send(:set_cache_value, 1, 'foo', :ignored, {}) + end + assert_equal 1, @cache.read('foo') + end + end end module AutoloadingCacheBehavior include DependenciesTestHelpers def test_simple_autoloading with_autoloading_fixtures do - @cache.write('foo', E.new) + @cache.write('foo', EM.new) end - remove_constants(:E) + remove_constants(:EM) ActiveSupport::Dependencies.clear with_autoloading_fixtures do - assert_kind_of E, @cache.read('foo') + assert_kind_of EM, @cache.read('foo') end - remove_constants(:E) + remove_constants(:EM) ActiveSupport::Dependencies.clear end def test_two_classes_autoloading with_autoloading_fixtures do - @cache.write('foo', [E.new, ClassFolder.new]) + @cache.write('foo', [EM.new, ClassFolder.new]) end - remove_constants(:E, :ClassFolder) + remove_constants(:EM, :ClassFolder) ActiveSupport::Dependencies.clear with_autoloading_fixtures do loaded = @cache.read('foo') assert_kind_of Array, loaded assert_equal 2, loaded.size - assert_kind_of E, loaded[0] + assert_kind_of EM, loaded[0] assert_kind_of ClassFolder, loaded[1] end - remove_constants(:E, :ClassFolder) + remove_constants(:EM, :ClassFolder) ActiveSupport::Dependencies.clear end end @@ -698,10 +780,12 @@ class FileStoreTest < ActiveSupport::TestCase include AutoloadingCacheBehavior def test_clear - filepath = File.join(cache_dir, ".gitkeep") - FileUtils.touch(filepath) + gitkeep = File.join(cache_dir, ".gitkeep") + keep = File.join(cache_dir, ".keep") + FileUtils.touch([gitkeep, keep]) @cache.clear - assert File.exist?(filepath) + assert File.exist?(gitkeep) + assert File.exist?(keep) end def test_clear_without_cache_dir @@ -714,14 +798,19 @@ class FileStoreTest < ActiveSupport::TestCase assert_equal 1, @cache.read("a"*10000) end + def test_long_uri_encoded_keys + @cache.write("%"*870, 1) + assert_equal 1, @cache.read("%"*870) + end + def test_key_transformation - key = @cache.send(:key_file_path, "views/index?id=1") + key = @cache.send(:normalize_key, "views/index?id=1", {}) assert_equal "views/index?id=1", @cache.send(:file_path_key, key) end def test_key_transformation_with_pathname FileUtils.touch(File.join(cache_dir, "foo")) - key = @cache_with_pathname.send(:key_file_path, "views/index?id=1") + key = @cache_with_pathname.send(:normalize_key, "views/index?id=1", {}) assert_equal "views/index?id=1", @cache_with_pathname.send(:file_path_key, key) end @@ -729,7 +818,7 @@ class FileStoreTest < ActiveSupport::TestCase # remain valid def test_filename_max_size key = "#{'A' * ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE}" - path = @cache.send(:key_file_path, key) + path = @cache.send(:normalize_key, key, {}) Dir::Tmpname.create(path) do |tmpname, n, opts| assert File.basename(tmpname+'.lock').length <= 255, "Temp filename too long: #{File.basename(tmpname+'.lock').length}" end @@ -739,7 +828,7 @@ class FileStoreTest < ActiveSupport::TestCase # If filename is 'AAAAB', where max size is 4, the returned path should be AAAA/B def test_key_transformation_max_filename_size key = "#{'A' * ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE}B" - path = @cache.send(:key_file_path, key) + path = @cache.send(:normalize_key, key, {}) assert path.split('/').all? { |dir_name| dir_name.size <= ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE} assert_equal 'B', File.basename(path) end @@ -765,9 +854,10 @@ class FileStoreTest < ActiveSupport::TestCase end def test_log_exception_when_cache_read_fails - File.expects(:exist?).raises(StandardError, "failed") - @cache.send(:read_entry, "winston", {}) - assert @buffer.string.present? + File.stub(:exist?, -> { raise StandardError.new("failed") }) do + @cache.send(:read_entry, "winston", {}) + assert @buffer.string.present? + end end def test_cleanup_removes_all_expired_entries @@ -775,11 +865,12 @@ class FileStoreTest < ActiveSupport::TestCase @cache.write('foo', 'bar', expires_in: 10) @cache.write('baz', 'qux') @cache.write('quux', 'corge', expires_in: 20) - Time.stubs(:now).returns(time + 15) - @cache.cleanup - assert_not @cache.exist?('foo') - assert @cache.exist?('baz') - assert @cache.exist?('quux') + Time.stub(:now, time + 15) do + @cache.cleanup + assert_not @cache.exist?('foo') + assert @cache.exist?('baz') + assert @cache.exist?('quux') + end end def test_write_with_unless_exist @@ -788,6 +879,12 @@ class FileStoreTest < ActiveSupport::TestCase @cache.write(1, nil) assert_equal false, @cache.write(1, "aaaaaaaaaa", unless_exist: true) end + + def test_can_call_deprecated_key_file_path + assert_deprecated "`key_file_path` is deprecated" do + assert_equal 111, @cache.send(:key_file_path, 111) + end + end end class MemoryStoreTest < ActiveSupport::TestCase @@ -962,6 +1059,12 @@ class MemCacheStoreTest < ActiveSupport::TestCase value << 'bingo' assert_not_equal value, @cache.read('foo') end + + def test_can_call_deprecated_escape_key + assert_deprecated "`escape_key` is deprecated" do + assert_equal 111, @cache.send(:escape_key, 111) + end + end end class NullStoreTest < ActiveSupport::TestCase @@ -1016,10 +1119,6 @@ class NullStoreTest < ActiveSupport::TestCase end assert_nil @cache.read("name") end - - def test_setting_nil_cache_store - assert ActiveSupport::Cache.lookup_store.class.name, ActiveSupport::Cache::NullStore.name - end end class CacheStoreLoggerTest < ActiveSupport::TestCase @@ -1035,6 +1134,19 @@ class CacheStoreLoggerTest < ActiveSupport::TestCase assert @buffer.string.present? end + def test_log_with_string_namespace + @cache.fetch('foo', {namespace: 'string_namespace'}) { 'bar' } + assert_match %r{string_namespace:foo}, @buffer.string + end + + def test_log_with_proc_namespace + proc = Proc.new do + "proc_namespace" + end + @cache.fetch('foo', {:namespace => proc}) { 'bar' } + assert_match %r{proc_namespace:foo}, @buffer.string + end + def test_mute_logging @cache.mute { @cache.fetch('foo') { 'bar' } } assert @buffer.string.blank? @@ -1056,9 +1168,9 @@ class CacheEntryTest < ActiveSupport::TestCase assert !entry.expired?, 'entry not expired' entry = ActiveSupport::Cache::Entry.new("value", :expires_in => 60) assert !entry.expired?, 'entry not expired' - time = Time.now + 61 - Time.stubs(:now).returns(time) - assert entry.expired?, 'entry is expired' + Time.stub(:now, Time.now + 61) do + assert entry.expired?, 'entry is expired' + end end def test_compress_values diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb index cda9732cae..a624473f46 100644 --- a/activesupport/test/callbacks_test.rb +++ b/activesupport/test/callbacks_test.rb @@ -59,7 +59,7 @@ module CallbacksTest [:before_save, :after_save].each do |callback_method| callback_method_sym = callback_method.to_sym send(callback_method, callback_symbol(callback_method_sym)) - send(callback_method, callback_string(callback_method_sym)) + ActiveSupport::Deprecation.silence { send(callback_method, callback_string(callback_method_sym)) } send(callback_method, callback_proc(callback_method_sym)) send(callback_method, callback_object(callback_method_sym.to_s.gsub(/_save/, ''))) send(callback_method, CallbackClass) @@ -228,7 +228,7 @@ module CallbacksTest set_callback :save, :before, :nope, :if => :no set_callback :save, :before, :nope, :unless => :yes set_callback :save, :after, :tweedle - set_callback :save, :before, "tweedle_dee" + ActiveSupport::Deprecation.silence { set_callback :save, :before, "tweedle_dee" } set_callback :save, :before, proc {|m| m.history << "yup" } set_callback :save, :before, :nope, :if => proc { false } set_callback :save, :before, :nope, :unless => proc { true } @@ -766,34 +766,30 @@ module CallbacksTest end class CallbackFalseTerminatorWithoutConfigTest < ActiveSupport::TestCase - def test_returning_false_halts_callback_if_config_variable_is_not_set + def test_returning_false_does_not_halt_callback_if_config_variable_is_not_set obj = CallbackFalseTerminator.new - assert_deprecated do - obj.save - assert_equal :second, obj.halted - assert !obj.saved - end + obj.save + assert_equal nil, obj.halted + assert obj.saved end end class CallbackFalseTerminatorWithConfigTrueTest < ActiveSupport::TestCase def setup - ActiveSupport::Callbacks::CallbackChain.halt_and_display_warning_on_return_false = true + ActiveSupport::Callbacks.halt_and_display_warning_on_return_false = true end - def test_returning_false_halts_callback_if_config_variable_is_true + def test_returning_false_does_not_halt_callback_if_config_variable_is_true obj = CallbackFalseTerminator.new - assert_deprecated do - obj.save - assert_equal :second, obj.halted - assert !obj.saved - end + obj.save + assert_equal nil, obj.halted + assert obj.saved end end class CallbackFalseTerminatorWithConfigFalseTest < ActiveSupport::TestCase def setup - ActiveSupport::Callbacks::CallbackChain.halt_and_display_warning_on_return_false = false + ActiveSupport::Callbacks.halt_and_display_warning_on_return_false = false end def test_returning_false_does_not_halt_callback_if_config_variable_is_false @@ -1050,7 +1046,7 @@ module CallbacksTest def test_add_eval calls = [] - klass = build_class("bar") + klass = ActiveSupport::Deprecation.silence { build_class("bar") } klass.class_eval { define_method(:bar) { calls << klass } } klass.new.run assert_equal 1, calls.length @@ -1090,7 +1086,7 @@ module CallbacksTest def test_skip_string # raises error calls = [] - klass = build_class("bar") + klass = ActiveSupport::Deprecation.silence { build_class("bar") } klass.class_eval { define_method(:bar) { calls << klass } } assert_raises(ArgumentError) { klass.skip "bar" } klass.new.run @@ -1115,4 +1111,14 @@ module CallbacksTest assert_equal 1, calls.length end end + + class DeprecatedWarningTest < ActiveSupport::TestCase + def test_deprecate_string_callback + klass = Class.new(Record) + + assert_deprecated do + klass.send :before_save, "tweedle_dee" + end + end + end end diff --git a/activesupport/test/concern_test.rb b/activesupport/test/concern_test.rb index 253c1adc23..8ea701cfb7 100644 --- a/activesupport/test/concern_test.rb +++ b/activesupport/test/concern_test.rb @@ -54,6 +54,11 @@ class ConcernTest < ActiveSupport::TestCase include Bar, Baz end + module Qux + module ClassMethods + end + end + def setup @klass = Class.new end @@ -70,6 +75,26 @@ class ConcernTest < ActiveSupport::TestCase assert_equal ConcernTest::Baz::ClassMethods, (class << @klass; self.included_modules; end)[0] end + def test_class_methods_are_extended_only_on_expected_objects + ::Object.__send__(:include, Qux) + Object.extend(Qux::ClassMethods) + # module needs to be created after Qux is included in Object or bug won't + # be triggered + test_module = Module.new do + extend ActiveSupport::Concern + + class_methods do + def test + end + end + end + @klass.include test_module + assert_equal false, Object.respond_to?(:test) + Qux.class_eval do + remove_const :ClassMethods + end + end + def test_included_block_is_ran @klass.include(Baz) assert_equal true, @klass.included_ran diff --git a/activesupport/test/constantize_test_cases.rb b/activesupport/test/constantize_test_cases.rb index 366e4e5ef0..1115bc0fd8 100644 --- a/activesupport/test/constantize_test_cases.rb +++ b/activesupport/test/constantize_test_cases.rb @@ -100,6 +100,10 @@ module ConstantizeTestCases assert_nil yield("Ace::Gas::ConstantizeTestCases") assert_nil yield("#<Class:0x7b8b718b>::Nested_1") assert_nil yield("Ace::gas") + assert_nil yield('Object::ABC') + assert_nil yield('Object::Object::Object::ABC') + assert_nil yield('A::Object::B') + assert_nil yield('A::Object::Object::Object::B') assert_raises(NameError) do with_autoloading_fixtures do diff --git a/activesupport/test/core_ext/class/delegating_attributes_test.rb b/activesupport/test/core_ext/class/delegating_attributes_test.rb deleted file mode 100644 index 447b1d10ad..0000000000 --- a/activesupport/test/core_ext/class/delegating_attributes_test.rb +++ /dev/null @@ -1,122 +0,0 @@ -require 'abstract_unit' -require 'active_support/core_ext/class/delegating_attributes' - -module DelegatingFixtures - class Parent - end - - class Child < Parent - ActiveSupport::Deprecation.silence do - superclass_delegating_accessor :some_attribute - end - end - - class Mokopuna < Child - end - - class PercysMom - ActiveSupport::Deprecation.silence do - superclass_delegating_accessor :superpower - end - end - - class Percy < PercysMom - end -end - -class DelegatingAttributesTest < ActiveSupport::TestCase - include DelegatingFixtures - attr_reader :single_class - - def setup - @single_class = Class.new(Object) - end - - def test_simple_accessor_declaration - assert_deprecated do - single_class.superclass_delegating_accessor :both - end - - # Class should have accessor and mutator - # the instance should have an accessor only - assert_respond_to single_class, :both - assert_respond_to single_class, :both= - assert single_class.public_instance_methods.map(&:to_s).include?("both") - assert !single_class.public_instance_methods.map(&:to_s).include?("both=") - end - - def test_simple_accessor_declaration_with_instance_reader_false - _instance_methods = single_class.public_instance_methods - - assert_deprecated do - single_class.superclass_delegating_accessor :no_instance_reader, :instance_reader => false - end - - assert_respond_to single_class, :no_instance_reader - assert_respond_to single_class, :no_instance_reader= - assert !_instance_methods.include?(:no_instance_reader) - assert !_instance_methods.include?(:no_instance_reader?) - assert !_instance_methods.include?(:_no_instance_reader) - end - - def test_working_with_simple_attributes - assert_deprecated do - single_class.superclass_delegating_accessor :both - end - - single_class.both = "HMMM" - - assert_equal "HMMM", single_class.both - assert_equal true, single_class.both? - - assert_equal "HMMM", single_class.new.both - assert_equal true, single_class.new.both? - - single_class.both = false - assert_equal false, single_class.both? - end - - def test_child_class_delegates_to_parent_but_can_be_overridden - parent = Class.new - - assert_deprecated do - parent.superclass_delegating_accessor :both - end - - child = Class.new(parent) - parent.both = "1" - assert_equal "1", child.both - - child.both = "2" - assert_equal "1", parent.both - assert_equal "2", child.both - - parent.both = "3" - assert_equal "3", parent.both - assert_equal "2", child.both - end - - def test_delegation_stops_at_the_right_level - assert_nil Percy.superpower - assert_nil PercysMom.superpower - - PercysMom.superpower = :heatvision - assert_equal :heatvision, Percy.superpower - end - - def test_delegation_stops_for_nil - Mokopuna.some_attribute = nil - Child.some_attribute="1" - - assert_equal "1", Child.some_attribute - assert_nil Mokopuna.some_attribute - ensure - Child.some_attribute=nil - end - - def test_deprecation_warning - assert_deprecated(/superclass_delegating_accessor is deprecated/) do - single_class.superclass_delegating_accessor :test_attribute - end - end -end diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index e89be25b53..0fc3f765f5 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -191,8 +191,9 @@ class DateExtCalculationsTest < ActiveSupport::TestCase def test_yesterday_constructor_when_zone_is_set with_env_tz 'UTC' do with_tz_default ActiveSupport::TimeZone['Eastern Time (US & Canada)'] do # UTC -5 - Time.stubs(:now).returns Time.local(2000, 1, 1) - assert_equal Date.new(1999, 12, 30), Date.yesterday + Time.stub(:now, Time.local(2000, 1, 1)) do + assert_equal Date.new(1999, 12, 30), Date.yesterday + end end end end @@ -212,8 +213,9 @@ class DateExtCalculationsTest < ActiveSupport::TestCase def test_tomorrow_constructor_when_zone_is_set with_env_tz 'UTC' do with_tz_default ActiveSupport::TimeZone['Europe/Paris'] do # UTC +1 - Time.stubs(:now).returns Time.local(1999, 12, 31, 23) - assert_equal Date.new(2000, 1, 2), Date.tomorrow + Time.stub(:now, Time.local(1999, 12, 31, 23)) do + assert_equal Date.new(2000, 1, 2), Date.tomorrow + end end end end @@ -317,23 +319,26 @@ class DateExtCalculationsTest < ActiveSupport::TestCase end def test_past - Date.stubs(:current).returns(Date.new(2000, 1, 1)) - assert_equal true, Date.new(1999, 12, 31).past? - assert_equal false, Date.new(2000,1,1).past? - assert_equal false, Date.new(2000,1,2).past? + Date.stub(:current, Date.new(2000, 1, 1)) do + assert_equal true, Date.new(1999, 12, 31).past? + assert_equal false, Date.new(2000,1,1).past? + assert_equal false, Date.new(2000,1,2).past? + end end def test_future - Date.stubs(:current).returns(Date.new(2000, 1, 1)) - assert_equal false, Date.new(1999, 12, 31).future? - assert_equal false, Date.new(2000,1,1).future? - assert_equal true, Date.new(2000,1,2).future? + Date.stub(:current, Date.new(2000, 1, 1)) do + assert_equal false, Date.new(1999, 12, 31).future? + assert_equal false, Date.new(2000,1,1).future? + assert_equal true, Date.new(2000,1,2).future? + end end def test_current_returns_date_today_when_zone_not_set with_env_tz 'US/Central' do - Time.stubs(:now).returns Time.local(1999, 12, 31, 23) - assert_equal Date.today, Date.current + Time.stub(:now, Time.local(1999, 12, 31, 23)) do + assert_equal Date.today, Date.current + 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 74319ecd09..6fe38c45ec 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -204,61 +204,69 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase end def test_today_with_offset - Date.stubs(:current).returns(Date.new(2000, 1, 1)) - assert_equal false, DateTime.civil(1999,12,31,23,59,59, Rational(-18000, 86400)).today? - assert_equal true, DateTime.civil(2000,1,1,0,0,0, Rational(-18000, 86400)).today? - assert_equal true, DateTime.civil(2000,1,1,23,59,59, Rational(-18000, 86400)).today? - assert_equal false, DateTime.civil(2000,1,2,0,0,0, Rational(-18000, 86400)).today? + Date.stub(:current, Date.new(2000, 1, 1)) do + assert_equal false, DateTime.civil(1999,12,31,23,59,59, Rational(-18000, 86400)).today? + assert_equal true, DateTime.civil(2000,1,1,0,0,0, Rational(-18000, 86400)).today? + assert_equal true, DateTime.civil(2000,1,1,23,59,59, Rational(-18000, 86400)).today? + assert_equal false, DateTime.civil(2000,1,2,0,0,0, Rational(-18000, 86400)).today? + end end def test_today_without_offset - Date.stubs(:current).returns(Date.new(2000, 1, 1)) - assert_equal false, DateTime.civil(1999,12,31,23,59,59).today? - assert_equal true, DateTime.civil(2000,1,1,0).today? - assert_equal true, DateTime.civil(2000,1,1,23,59,59).today? - assert_equal false, DateTime.civil(2000,1,2,0).today? + Date.stub(:current, Date.new(2000, 1, 1)) do + assert_equal false, DateTime.civil(1999,12,31,23,59,59).today? + assert_equal true, DateTime.civil(2000,1,1,0).today? + assert_equal true, DateTime.civil(2000,1,1,23,59,59).today? + assert_equal false, DateTime.civil(2000,1,2,0).today? + end end def test_past_with_offset - DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) - assert_equal true, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).past? - assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).past? - assert_equal false, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).past? + DateTime.stub(:current, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) do + assert_equal true, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).past? + assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).past? + assert_equal false, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).past? + end end def test_past_without_offset - DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) - assert_equal true, DateTime.civil(2005,2,10,20,30,44).past? - assert_equal false, DateTime.civil(2005,2,10,20,30,45).past? - assert_equal false, DateTime.civil(2005,2,10,20,30,46).past? + DateTime.stub(:current, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) do + assert_equal true, DateTime.civil(2005,2,10,20,30,44).past? + assert_equal false, DateTime.civil(2005,2,10,20,30,45).past? + assert_equal false, DateTime.civil(2005,2,10,20,30,46).past? + end end def test_future_with_offset - DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) - assert_equal false, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).future? - assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).future? - assert_equal true, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).future? + DateTime.stub(:current, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) do + assert_equal false, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).future? + assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).future? + assert_equal true, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).future? + end end def test_future_without_offset - DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) - assert_equal false, DateTime.civil(2005,2,10,20,30,44).future? - assert_equal false, DateTime.civil(2005,2,10,20,30,45).future? - assert_equal true, DateTime.civil(2005,2,10,20,30,46).future? + DateTime.stub(:current, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) do + assert_equal false, DateTime.civil(2005,2,10,20,30,44).future? + assert_equal false, DateTime.civil(2005,2,10,20,30,45).future? + assert_equal true, DateTime.civil(2005,2,10,20,30,46).future? + end end def test_current_returns_date_today_when_zone_is_not_set with_env_tz 'US/Eastern' do - Time.stubs(:now).returns Time.local(1999, 12, 31, 23, 59, 59) - assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current + Time.stub(:now, Time.local(1999, 12, 31, 23, 59, 59)) do + assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current + end end end def test_current_returns_time_zone_today_when_zone_is_set Time.zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] with_env_tz 'US/Eastern' do - Time.stubs(:now).returns Time.local(1999, 12, 31, 23, 59, 59) - assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current + Time.stub(:now, Time.local(1999, 12, 31, 23, 59, 59)) do + assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current + end end ensure Time.zone = nil @@ -335,6 +343,13 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase assert_equal(-1, DateTime.civil(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 1), ActiveSupport::TimeZone['UTC'] )) end + def test_compare_with_string + assert_equal 1, DateTime.civil(2000) <=> Time.utc(1999, 12, 31, 23, 59, 59).to_s + assert_equal 0, DateTime.civil(2000) <=> Time.utc(2000, 1, 1, 0, 0, 0).to_s + assert_equal( -1, DateTime.civil(2000) <=> Time.utc(2000, 1, 1, 0, 0, 1).to_s) + assert_equal nil, DateTime.civil(2000) <=> "Invalid as Time" + 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 diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index c283b546e6..9e97acaffb 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -140,28 +140,30 @@ class DurationTest < ActiveSupport::TestCase def test_since_and_ago_anchored_to_time_now_when_time_zone_is_not_set Time.zone = nil with_env_tz 'US/Eastern' do - Time.stubs(:now).returns Time.local(2000) - # since - assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.since - assert_equal Time.local(2000,1,1,0,0,5), 5.seconds.since - # ago - assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.ago - assert_equal Time.local(1999,12,31,23,59,55), 5.seconds.ago + Time.stub(:now, Time.local(2000)) do + # since + assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.since + assert_equal Time.local(2000,1,1,0,0,5), 5.seconds.since + # ago + assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.ago + assert_equal Time.local(1999,12,31,23,59,55), 5.seconds.ago + end end end def test_since_and_ago_anchored_to_time_zone_now_when_time_zone_is_set Time.zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] with_env_tz 'US/Eastern' do - Time.stubs(:now).returns Time.local(2000) - # since - assert_instance_of ActiveSupport::TimeWithZone, 5.seconds.since - assert_equal Time.utc(2000,1,1,0,0,5), 5.seconds.since.time - assert_equal 'Eastern Time (US & Canada)', 5.seconds.since.time_zone.name - # ago - assert_instance_of ActiveSupport::TimeWithZone, 5.seconds.ago - assert_equal Time.utc(1999,12,31,23,59,55), 5.seconds.ago.time - assert_equal 'Eastern Time (US & Canada)', 5.seconds.ago.time_zone.name + Time.stub(:now, Time.local(2000)) do + # since + assert_instance_of ActiveSupport::TimeWithZone, 5.seconds.since + assert_equal Time.utc(2000,1,1,0,0,5), 5.seconds.since.time + assert_equal 'Eastern Time (US & Canada)', 5.seconds.since.time_zone.name + # ago + assert_instance_of ActiveSupport::TimeWithZone, 5.seconds.ago + assert_equal Time.utc(1999,12,31,23,59,55), 5.seconds.ago.time + assert_equal 'Eastern Time (US & Canada)', 5.seconds.ago.time_zone.name + end end ensure Time.zone = nil diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb index e5d8ae7882..f09b7d8850 100644 --- a/activesupport/test/core_ext/enumerable_test.rb +++ b/activesupport/test/core_ext/enumerable_test.rb @@ -3,6 +3,8 @@ require 'active_support/core_ext/array' require 'active_support/core_ext/enumerable' Payment = Struct.new(:price) +ExpandedPayment = Struct.new(:dollars, :cents) + class SummablePayment < Payment def +(p) self.class.new(price + p.price) end end @@ -110,4 +112,16 @@ class EnumerableTests < ActiveSupport::TestCase assert_equal [1, 2, 4], (1..5).to_set.without(3, 5) assert_equal({foo: 1, baz: 3}, {foo: 1, bar: 2, baz: 3}.without(:bar)) end + + def test_pluck + payments = GenericEnumerable.new([ Payment.new(5), Payment.new(15), Payment.new(10) ]) + assert_equal [5, 15, 10], payments.pluck(:price) + + payments = GenericEnumerable.new([ + ExpandedPayment.new(5, 99), + ExpandedPayment.new(15, 0), + ExpandedPayment.new(10, 50) + ]) + assert_equal [[5, 99], [15, 0], [10, 50]], payments.pluck(:dollars, :cents) + end end diff --git a/activesupport/test/core_ext/hash/transform_keys_test.rb b/activesupport/test/core_ext/hash/transform_keys_test.rb index a7e12117f3..99af274614 100644 --- a/activesupport/test/core_ext/hash/transform_keys_test.rb +++ b/activesupport/test/core_ext/hash/transform_keys_test.rb @@ -18,9 +18,17 @@ class TransformKeysTest < ActiveSupport::TestCase assert_same original, mapped end - test "transform_keys returns an Enumerator if no block is given" do + test "transform_keys returns a sized Enumerator if no block is given" do original = { a: 'a', b: 'b' } enumerator = original.transform_keys + assert_equal original.size, enumerator.size + assert_equal Enumerator, enumerator.class + end + + test "transform_keys! returns a sized Enumerator if no block is given" do + original = { a: 'a', b: 'b' } + enumerator = original.transform_keys! + assert_equal original.size, enumerator.size assert_equal Enumerator, enumerator.class end @@ -29,4 +37,10 @@ class TransformKeysTest < ActiveSupport::TestCase mapped = original.transform_keys.with_index { |k, i| [k, i].join.to_sym } assert_equal({ a0: 'a', b1: 'b' }, mapped) end + + test "transform_keys! is chainable with Enumerable methods" do + original = { a: 'a', b: 'b' } + original.transform_keys!.with_index { |k, i| [k, i].join.to_sym } + assert_equal({ a0: 'a', b1: 'b' }, original) + end end diff --git a/activesupport/test/core_ext/hash/transform_values_test.rb b/activesupport/test/core_ext/hash/transform_values_test.rb index 45ed11fef7..114022fbaf 100644 --- a/activesupport/test/core_ext/hash/transform_values_test.rb +++ b/activesupport/test/core_ext/hash/transform_values_test.rb @@ -47,9 +47,17 @@ class TransformValuesTest < ActiveSupport::TestCase assert_nil mapped[:b] end - test "transform_values returns an Enumerator if no block is given" do + test "transform_values returns a sized Enumerator if no block is given" do original = { a: 'a', b: 'b' } enumerator = original.transform_values + assert_equal original.size, enumerator.size + assert_equal Enumerator, enumerator.class + end + + test "transform_values! returns a sized Enumerator if no block is given" do + original = { a: 'a', b: 'b' } + enumerator = original.transform_values! + assert_equal original.size, enumerator.size assert_equal Enumerator, enumerator.class end @@ -58,4 +66,10 @@ class TransformValuesTest < ActiveSupport::TestCase mapped = original.transform_values.with_index { |v, i| [v, i].join } assert_equal({ a: 'a0', b: 'b1' }, mapped) end + + test "transform_values! is chainable with Enumerable methods" do + original = { a: 'a', b: 'b' } + original.transform_values!.with_index { |v, i| [v, i].join } + assert_equal({ a: 'a0', b: 'b1' }, original) + end end diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index e10bee5e00..1b66f784e4 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -524,6 +524,10 @@ class HashExtTest < ActiveSupport::TestCase 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] @@ -547,6 +551,11 @@ class HashExtTest < ActiveSupport::TestCase 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} @@ -568,6 +577,11 @@ class HashExtTest < ActiveSupport::TestCase 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} @@ -961,10 +975,11 @@ class HashExtTest < ActiveSupport::TestCase assert_raise(RuntimeError) { original.except!(:a) } end - def test_except_with_mocha_expectation_on_original + def test_except_does_not_delete_values_in_original original = { :a => 'x', :b => 'y' } - original.expects(:delete).never - original.except(:a) + assert_not_called(original, :delete) do + original.except(:a) + end end def test_compact @@ -998,6 +1013,55 @@ class HashExtTest < ActiveSupport::TestCase 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_existant] + 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_from_hash_copying_default_should_not_raise_when_default_proc_does + hash = Hash.new + hash.default_proc = proc { |h, k| raise "walrus" } + + assert_deprecated { HashWithIndifferentAccess.new_from_hash_copying_default(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 @@ -1523,9 +1587,9 @@ class HashToXmlTest < ActiveSupport::TestCase assert_equal 3, hash_wia[:new_key] end - def test_should_use_default_proc_if_no_key_is_supplied + def test_should_return_nil_if_no_key_is_supplied hash_wia = HashWithIndifferentAccess.new { 1 + 2 } - assert_equal 3, hash_wia.default + assert_equal nil, hash_wia.default end def test_should_use_default_value_for_unknown_key @@ -1549,7 +1613,6 @@ class HashToXmlTest < ActiveSupport::TestCase 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 diff --git a/activesupport/test/core_ext/marshal_test.rb b/activesupport/test/core_ext/marshal_test.rb index e49330128b..825df439a5 100644 --- a/activesupport/test/core_ext/marshal_test.rb +++ b/activesupport/test/core_ext/marshal_test.rb @@ -8,7 +8,7 @@ class MarshalTest < ActiveSupport::TestCase def teardown ActiveSupport::Dependencies.clear - remove_constants(:E, :ClassFolder) + remove_constants(:EM, :ClassFolder) end test "that Marshal#load still works" do @@ -22,14 +22,14 @@ class MarshalTest < ActiveSupport::TestCase test "that a missing class is autoloaded from string" do dumped = nil with_autoloading_fixtures do - dumped = Marshal.dump(E.new) + dumped = Marshal.dump(EM.new) end - remove_constants(:E) + remove_constants(:EM) ActiveSupport::Dependencies.clear with_autoloading_fixtures do - assert_kind_of E, Marshal.load(dumped) + assert_kind_of EM, Marshal.load(dumped) end end @@ -50,16 +50,16 @@ class MarshalTest < ActiveSupport::TestCase test "that more than one missing class is autoloaded" do dumped = nil with_autoloading_fixtures do - dumped = Marshal.dump([E.new, ClassFolder.new]) + dumped = Marshal.dump([EM.new, ClassFolder.new]) end - remove_constants(:E, :ClassFolder) + remove_constants(:EM, :ClassFolder) ActiveSupport::Dependencies.clear with_autoloading_fixtures do loaded = Marshal.load(dumped) assert_equal 2, loaded.size - assert_kind_of E, loaded[0] + assert_kind_of EM, loaded[0] assert_kind_of ClassFolder, loaded[1] end end @@ -67,10 +67,10 @@ class MarshalTest < ActiveSupport::TestCase test "that a real missing class is causing an exception" do dumped = nil with_autoloading_fixtures do - dumped = Marshal.dump(E.new) + dumped = Marshal.dump(EM.new) end - remove_constants(:E) + remove_constants(:EM) ActiveSupport::Dependencies.clear assert_raise(NameError) do @@ -84,10 +84,10 @@ class MarshalTest < ActiveSupport::TestCase end with_autoloading_fixtures do - dumped = Marshal.dump([E.new, SomeClass.new]) + dumped = Marshal.dump([EM.new, SomeClass.new]) end - remove_constants(:E) + remove_constants(:EM) self.class.send(:remove_const, :SomeClass) ActiveSupport::Dependencies.clear @@ -96,8 +96,8 @@ class MarshalTest < ActiveSupport::TestCase Marshal.load(dumped) end - assert_nothing_raised("E failed to load while we expect only SomeClass to fail loading") do - E.new + assert_nothing_raised("EM failed to load while we expect only SomeClass to fail loading") do + EM.new end assert_raise(NameError, "We expected SomeClass to not be loaded but it is!") do @@ -109,15 +109,15 @@ class MarshalTest < ActiveSupport::TestCase test "loading classes from files trigger autoloading" do Tempfile.open("object_serializer_test") do |f| with_autoloading_fixtures do - Marshal.dump(E.new, f) + Marshal.dump(EM.new, f) end f.rewind - remove_constants(:E) + remove_constants(:EM) ActiveSupport::Dependencies.clear with_autoloading_fixtures do - assert_kind_of E, Marshal.load(f) + assert_kind_of EM, Marshal.load(f) 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 new file mode 100644 index 0000000000..65fadc5c20 --- /dev/null +++ b/activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb @@ -0,0 +1,109 @@ +require 'abstract_unit' +require 'active_support/core_ext/module/attribute_accessors_per_thread' + +class ModuleAttributeAccessorPerThreadTest < ActiveSupport::TestCase + def setup + @class = Class.new do + thread_mattr_accessor :foo + thread_mattr_accessor :bar, instance_writer: false + thread_mattr_reader :shaq, instance_reader: false + thread_mattr_accessor :camp, instance_accessor: false + end + + @object = @class.new + end + + def test_should_use_mattr_default + Thread.new do + assert_nil @class.foo + assert_nil @object.foo + end.join + end + + def test_should_set_mattr_value + Thread.new do + @class.foo = :test + assert_equal :test, @class.foo + + @class.foo = :test2 + assert_equal :test2, @class.foo + end.join + end + + def test_should_not_create_instance_writer + Thread.new do + assert_respond_to @class, :foo + assert_respond_to @class, :foo= + assert_respond_to @object, :bar + assert !@object.respond_to?(:bar=) + end.join + end + + def test_should_not_create_instance_reader + Thread.new do + assert_respond_to @class, :shaq + assert !@object.respond_to?(:shaq) + end.join + end + + def test_should_not_create_instance_accessors + Thread.new do + assert_respond_to @class, :camp + assert !@object.respond_to?(:camp) + assert !@object.respond_to?(:camp=) + end.join + end + + def test_values_should_not_bleed_between_threads + threads = [] + threads << Thread.new do + @class.foo = 'things' + sleep 1 + assert_equal 'things', @class.foo + end + + threads << Thread.new do + @class.foo = 'other things' + sleep 1 + assert_equal 'other things', @class.foo + end + + threads << Thread.new do + @class.foo = 'really other things' + sleep 1 + assert_equal 'really other things', @class.foo + end + + threads.each { |t| t.join } + end + + def test_should_raise_name_error_if_attribute_name_is_invalid + exception = assert_raises NameError do + Class.new do + thread_cattr_reader "1nvalid" + end + end + assert_equal "invalid attribute name: 1nvalid", exception.message + + exception = assert_raises NameError do + Class.new do + thread_cattr_writer "1nvalid" + end + end + assert_equal "invalid attribute name: 1nvalid", exception.message + + exception = assert_raises NameError do + Class.new do + thread_mattr_reader "1valid_part" + end + end + assert_equal "invalid attribute name: 1valid_part", exception.message + + exception = assert_raises NameError do + Class.new do + thread_mattr_writer "2valid_part" + end + end + assert_equal "invalid attribute name: 2valid_part", exception.message + end +end diff --git a/activesupport/test/core_ext/module/attribute_accessor_test.rb b/activesupport/test/core_ext/module/attribute_accessor_test.rb index 48f3cc579f..0b0f3a2808 100644 --- a/activesupport/test/core_ext/module/attribute_accessor_test.rb +++ b/activesupport/test/core_ext/module/attribute_accessor_test.rb @@ -69,6 +69,20 @@ class ModuleAttributeAccessorTest < ActiveSupport::TestCase end end assert_equal "invalid attribute name: 1nvalid", exception.message + + exception = assert_raises NameError do + Class.new do + mattr_reader "valid_part\ninvalid_part" + end + end + assert_equal "invalid attribute name: valid_part\ninvalid_part", exception.message + + exception = assert_raises NameError do + Class.new do + mattr_writer "valid_part\ninvalid_part" + end + end + assert_equal "invalid attribute name: valid_part\ninvalid_part", exception.message end def test_should_use_default_value_if_block_passed @@ -76,4 +90,10 @@ class ModuleAttributeAccessorTest < ActiveSupport::TestCase assert_equal 'default_reader_value', @module.defr assert_equal 'default_writer_value', @module.class_variable_get('@@defw') end + + def test_should_not_invoke_default_value_block_multiple_times + count = 0 + @module.cattr_accessor(:defcount){ count += 1 } + assert_equal 1, count + end end diff --git a/activesupport/test/core_ext/module/qualified_const_test.rb b/activesupport/test/core_ext/module/qualified_const_test.rb index 37c9228a64..a3146cabe1 100644 --- a/activesupport/test/core_ext/module/qualified_const_test.rb +++ b/activesupport/test/core_ext/module/qualified_const_test.rb @@ -19,84 +19,94 @@ end class QualifiedConstTest < ActiveSupport::TestCase test "Object.qualified_const_defined?" do - assert Object.qualified_const_defined?("QualifiedConstTestMod") - assert !Object.qualified_const_defined?("NonExistingQualifiedConstTestMod") - - assert Object.qualified_const_defined?("QualifiedConstTestMod::X") - assert !Object.qualified_const_defined?("QualifiedConstTestMod::Y") - - assert Object.qualified_const_defined?("QualifiedConstTestMod::M::X") - assert !Object.qualified_const_defined?("QualifiedConstTestMod::M::Y") - - if Module.method(:const_defined?).arity == 1 - assert !Object.qualified_const_defined?("QualifiedConstTestMod::N::X") - else - assert Object.qualified_const_defined?("QualifiedConstTestMod::N::X") - assert !Object.qualified_const_defined?("QualifiedConstTestMod::N::X", false) - assert Object.qualified_const_defined?("QualifiedConstTestMod::N::X", true) + assert_deprecated do + assert Object.qualified_const_defined?("QualifiedConstTestMod") + assert !Object.qualified_const_defined?("NonExistingQualifiedConstTestMod") + + assert Object.qualified_const_defined?("QualifiedConstTestMod::X") + assert !Object.qualified_const_defined?("QualifiedConstTestMod::Y") + + assert Object.qualified_const_defined?("QualifiedConstTestMod::M::X") + assert !Object.qualified_const_defined?("QualifiedConstTestMod::M::Y") + + if Module.method(:const_defined?).arity == 1 + assert !Object.qualified_const_defined?("QualifiedConstTestMod::N::X") + else + assert Object.qualified_const_defined?("QualifiedConstTestMod::N::X") + assert !Object.qualified_const_defined?("QualifiedConstTestMod::N::X", false) + assert Object.qualified_const_defined?("QualifiedConstTestMod::N::X", true) + end end end test "mod.qualified_const_defined?" do - assert QualifiedConstTestMod.qualified_const_defined?("M") - assert !QualifiedConstTestMod.qualified_const_defined?("NonExistingM") - - assert QualifiedConstTestMod.qualified_const_defined?("M::X") - assert !QualifiedConstTestMod.qualified_const_defined?("M::Y") - - assert QualifiedConstTestMod.qualified_const_defined?("M::C::X") - assert !QualifiedConstTestMod.qualified_const_defined?("M::C::Y") - - if Module.method(:const_defined?).arity == 1 - assert !QualifiedConstTestMod.qualified_const_defined?("QualifiedConstTestMod::N::X") - else - assert QualifiedConstTestMod.qualified_const_defined?("N::X") - assert !QualifiedConstTestMod.qualified_const_defined?("N::X", false) - assert QualifiedConstTestMod.qualified_const_defined?("N::X", true) + assert_deprecated do + assert QualifiedConstTestMod.qualified_const_defined?("M") + assert !QualifiedConstTestMod.qualified_const_defined?("NonExistingM") + + assert QualifiedConstTestMod.qualified_const_defined?("M::X") + assert !QualifiedConstTestMod.qualified_const_defined?("M::Y") + + assert QualifiedConstTestMod.qualified_const_defined?("M::C::X") + assert !QualifiedConstTestMod.qualified_const_defined?("M::C::Y") + + if Module.method(:const_defined?).arity == 1 + assert !QualifiedConstTestMod.qualified_const_defined?("QualifiedConstTestMod::N::X") + else + assert QualifiedConstTestMod.qualified_const_defined?("N::X") + assert !QualifiedConstTestMod.qualified_const_defined?("N::X", false) + assert QualifiedConstTestMod.qualified_const_defined?("N::X", true) + end end end test "qualified_const_get" do - assert_equal false, Object.qualified_const_get("QualifiedConstTestMod::X") - assert_equal false, QualifiedConstTestMod.qualified_const_get("X") - assert_equal 1, QualifiedConstTestMod.qualified_const_get("M::X") - assert_equal 1, QualifiedConstTestMod.qualified_const_get("N::X") - assert_equal 2, QualifiedConstTestMod.qualified_const_get("M::C::X") - - assert_raise(NameError) { QualifiedConstTestMod.qualified_const_get("M::C::Y")} + assert_deprecated do + assert_equal false, Object.qualified_const_get("QualifiedConstTestMod::X") + assert_equal false, QualifiedConstTestMod.qualified_const_get("X") + assert_equal 1, QualifiedConstTestMod.qualified_const_get("M::X") + assert_equal 1, QualifiedConstTestMod.qualified_const_get("N::X") + assert_equal 2, QualifiedConstTestMod.qualified_const_get("M::C::X") + + assert_raise(NameError) { QualifiedConstTestMod.qualified_const_get("M::C::Y")} + end end test "qualified_const_set" do - begin - m = Module.new - assert_equal m, Object.qualified_const_set("QualifiedConstTestMod2", m) - assert_equal m, ::QualifiedConstTestMod2 - - # We are going to assign to existing constants on purpose, so silence warnings. - silence_warnings do - assert_equal true, QualifiedConstTestMod.qualified_const_set("QualifiedConstTestMod::X", true) - assert_equal true, QualifiedConstTestMod::X - - assert_equal 10, QualifiedConstTestMod::M.qualified_const_set("X", 10) - assert_equal 10, QualifiedConstTestMod::M::X - end - ensure - silence_warnings do - QualifiedConstTestMod.qualified_const_set('QualifiedConstTestMod::X', false) - QualifiedConstTestMod::M.qualified_const_set('X', 1) + assert_deprecated do + begin + m = Module.new + assert_equal m, Object.qualified_const_set("QualifiedConstTestMod2", m) + assert_equal m, ::QualifiedConstTestMod2 + + # We are going to assign to existing constants on purpose, so silence warnings. + silence_warnings do + assert_equal true, QualifiedConstTestMod.qualified_const_set("QualifiedConstTestMod::X", true) + assert_equal true, QualifiedConstTestMod::X + + assert_equal 10, QualifiedConstTestMod::M.qualified_const_set("X", 10) + assert_equal 10, QualifiedConstTestMod::M::X + end + ensure + silence_warnings do + QualifiedConstTestMod.qualified_const_set('QualifiedConstTestMod::X', false) + QualifiedConstTestMod::M.qualified_const_set('X', 1) + end end end end test "reject absolute paths" do - assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_defined?("::X")} - assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_defined?("::X::Y")} + assert_deprecated do + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_defined?("::X")} + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_defined?("::X::Y")} - assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_get("::X")} - assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_get("::X::Y")} + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_get("::X")} + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_get("::X::Y")} - assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_set("::X", nil)} - assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_set("::X::Y", nil)} + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_set("::X", nil)} + assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_set("::X::Y", nil)} + end end private diff --git a/activesupport/test/core_ext/module/remove_method_test.rb b/activesupport/test/core_ext/module/remove_method_test.rb index 4657f0c175..0d684dc70e 100644 --- a/activesupport/test/core_ext/module/remove_method_test.rb +++ b/activesupport/test/core_ext/module/remove_method_test.rb @@ -6,24 +6,54 @@ module RemoveMethodTests def do_something return 1 end - + + def do_something_protected + return 1 + end + protected :do_something_protected + + def do_something_private + return 1 + end + private :do_something_private + + class << self + def do_something_else + return 2 + end + end end end class RemoveMethodTest < ActiveSupport::TestCase - + def test_remove_method_from_an_object RemoveMethodTests::A.class_eval{ self.remove_possible_method(:do_something) } assert !RemoveMethodTests::A.new.respond_to?(:do_something) end - + + def test_remove_singleton_method_from_an_object + RemoveMethodTests::A.class_eval{ + self.remove_possible_singleton_method(:do_something_else) + } + assert !RemoveMethodTests::A.respond_to?(:do_something_else) + end + def test_redefine_method_in_an_object RemoveMethodTests::A.class_eval{ self.redefine_method(:do_something) { return 100 } + self.redefine_method(:do_something_protected) { return 100 } + self.redefine_method(:do_something_private) { return 100 } } assert_equal 100, RemoveMethodTests::A.new.do_something + assert_equal 100, RemoveMethodTests::A.new.send(:do_something_protected) + assert_equal 100, RemoveMethodTests::A.new.send(:do_something_private) + + assert RemoveMethodTests::A.public_method_defined? :do_something + assert RemoveMethodTests::A.protected_method_defined? :do_something_protected + assert RemoveMethodTests::A.private_method_defined? :do_something_private end -end
\ No newline at end of file +end diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb index bdfbadcf1d..0ed66f8c37 100644 --- a/activesupport/test/core_ext/module_test.rb +++ b/activesupport/test/core_ext/module_test.rb @@ -83,6 +83,16 @@ Product = Struct.new(:name) do end end +class Block + def hello? + true + end +end + +HasBlock = Struct.new(:block) do + delegate :hello?, to: :block +end + class ParameterSet delegate :[], :[]=, :to => :@params @@ -301,6 +311,11 @@ class ModuleTest < ActiveSupport::TestCase assert_raise(NoMethodError) { product.type_name } end + def test_delegation_with_method_arguments + has_block = HasBlock.new(Block.new) + assert has_block.hello? + end + def test_parent assert_equal Yz::Zy, Yz::Zy::Cd.parent assert_equal Yz, Yz::Zy.parent diff --git a/activesupport/test/core_ext/numeric_ext_test.rb b/activesupport/test/core_ext/numeric_ext_test.rb index 2d8796179e..5654aeb4f8 100644 --- a/activesupport/test/core_ext/numeric_ext_test.rb +++ b/activesupport/test/core_ext/numeric_ext_test.rb @@ -143,6 +143,14 @@ class NumericExtFormattingTest < ActiveSupport::TestCase gigabytes(number) * 1024 end + def petabytes(number) + terabytes(number) * 1024 + end + + def exabytes(number) + petabytes(number) * 1024 + end + def test_to_s__phone assert_equal("555-1234", 5551234.to_s(:phone)) assert_equal("800-555-1212", 8005551212.to_s(:phone)) @@ -266,7 +274,9 @@ class NumericExtFormattingTest < ActiveSupport::TestCase assert_equal '1.18 MB', 1234567.to_s(:human_size) assert_equal '1.15 GB', 1234567890.to_s(:human_size) assert_equal '1.12 TB', 1234567890123.to_s(:human_size) - assert_equal '1030 TB', terabytes(1026).to_s(:human_size) + assert_equal '1.1 PB', 1234567890123456.to_s(:human_size) + assert_equal '1.07 EB', 1234567890123456789.to_s(:human_size) + assert_equal '1030 EB', exabytes(1026).to_s(:human_size) assert_equal '444 KB', kilobytes(444).to_s(:human_size) assert_equal '1020 MB', megabytes(1023).to_s(:human_size) assert_equal '3 TB', terabytes(3).to_s(:human_size) @@ -280,14 +290,18 @@ class NumericExtFormattingTest < ActiveSupport::TestCase end def test_to_s__human_size_with_si_prefix - assert_equal '3 Bytes', 3.14159265.to_s(:human_size, :prefix => :si) - assert_equal '123 Bytes', 123.0.to_s(:human_size, :prefix => :si) - assert_equal '123 Bytes', 123.to_s(:human_size, :prefix => :si) - assert_equal '1.23 KB', 1234.to_s(:human_size, :prefix => :si) - assert_equal '12.3 KB', 12345.to_s(:human_size, :prefix => :si) - assert_equal '1.23 MB', 1234567.to_s(:human_size, :prefix => :si) - assert_equal '1.23 GB', 1234567890.to_s(:human_size, :prefix => :si) - assert_equal '1.23 TB', 1234567890123.to_s(:human_size, :prefix => :si) + assert_deprecated do + assert_equal '3 Bytes', 3.14159265.to_s(:human_size, :prefix => :si) + assert_equal '123 Bytes', 123.0.to_s(:human_size, :prefix => :si) + assert_equal '123 Bytes', 123.to_s(:human_size, :prefix => :si) + assert_equal '1.23 KB', 1234.to_s(:human_size, :prefix => :si) + assert_equal '12.3 KB', 12345.to_s(:human_size, :prefix => :si) + assert_equal '1.23 MB', 1234567.to_s(:human_size, :prefix => :si) + assert_equal '1.23 GB', 1234567890.to_s(:human_size, :prefix => :si) + assert_equal '1.23 TB', 1234567890123.to_s(:human_size, :prefix => :si) + assert_equal '1.23 PB', 1234567890123456.to_s(:human_size, :prefix => :si) + assert_equal '1.23 EB', 1234567890123456789.to_s(:human_size, :prefix => :si) + end end def test_to_s__human_size_with_options_hash @@ -386,6 +400,32 @@ class NumericExtFormattingTest < ActiveSupport::TestCase assert_equal '1 Million', BigDecimal("1000010").to_s(:human) end + def test_to_formatted_s_is_deprecated + assert_deprecated do + 5551234.to_formatted_s(:phone) + end + end + + def test_to_s_with_invalid_formatter + assert_equal '123', 123.to_s(:invalid) + assert_equal '2.5', 2.5.to_s(:invalid) + assert_equal '100000000000000000000', (100**10).to_s(:invalid) + assert_equal '1000010.0', BigDecimal("1000010").to_s(:invalid) + end + + def test_default_to_s + assert_equal '123', 123.to_s + assert_equal '1111011', 123.to_s(2) + + assert_equal '2.5', 2.5.to_s + + assert_equal '100000000000000000000', (100**10).to_s + assert_equal '1010110101111000111010111100010110101100011000100000000000000000000', (100**10).to_s(2) + + assert_equal '1000010.0', BigDecimal("1000010").to_s + assert_equal '10000 10.0', BigDecimal("1000010").to_s('5F') + end + def test_in_milliseconds assert_equal 10_000, 10.seconds.in_milliseconds end diff --git a/activesupport/test/core_ext/object/blank_test.rb b/activesupport/test/core_ext/object/blank_test.rb index 8a5e385dd7..a142096993 100644 --- a/activesupport/test/core_ext/object/blank_test.rb +++ b/activesupport/test/core_ext/object/blank_test.rb @@ -1,4 +1,3 @@ - require 'abstract_unit' require 'active_support/core_ext/object/blank' diff --git a/activesupport/test/core_ext/object/json_gem_encoding_test.rb b/activesupport/test/core_ext/object/json_gem_encoding_test.rb new file mode 100644 index 0000000000..02ab17fb64 --- /dev/null +++ b/activesupport/test/core_ext/object/json_gem_encoding_test.rb @@ -0,0 +1,66 @@ +require 'abstract_unit' +require 'json' +require 'json/encoding_test_cases' + +# These test cases were added to test that we do not interfere with json gem's +# output when the AS encoder is loaded, primarily for problems reported in +# #20775. They need to be executed in isolation to reproduce the scenario +# correctly, because other test cases might have already loaded additional +# dependencies. + +# 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 +# ideally we should just fix the BigDecimal core_ext to not change to_s without +# arguments. +require 'active_support/core_ext/big_decimal' + +class JsonGemEncodingTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation + + JSONTest::EncodingTestCases.constants.each_with_index do |name| + JSONTest::EncodingTestCases.const_get(name).each_with_index do |(subject, _), i| + test("#{name[0..-6].underscore} #{i}") do + assert_same_with_or_without_active_support(subject) + end + end + end + + class CustomToJson + def to_json(*) + '"custom"' + end + end + + test "custom to_json" do + assert_same_with_or_without_active_support(CustomToJson.new) + end + + private + def require_or_skip(file) + require(file) || skip("'#{file}' was already loaded") + end + + def assert_same_with_or_without_active_support(subject) + begin + expected = JSON.generate(subject, quirks_mode: true) + rescue JSON::GeneratorError => e + exception = e + end + + require_or_skip 'active_support/core_ext/object/json' + + if exception + assert_raises_with_message JSON::GeneratorError, e.message do + JSON.generate(subject, quirks_mode: true) + end + else + assert_equal expected, JSON.generate(subject, quirks_mode: true) + end + end + + def assert_raises_with_message(exception_class, message, &block) + err = assert_raises(exception_class) { block.call } + assert_match message, err.message + end +end diff --git a/activesupport/test/core_ext/object/try_test.rb b/activesupport/test/core_ext/object/try_test.rb index 5ea0f0eca6..25bf0207b8 100644 --- a/activesupport/test/core_ext/object/try_test.rb +++ b/activesupport/test/core_ext/object/try_test.rb @@ -121,7 +121,7 @@ class ObjectTryTest < ActiveSupport::TestCase assert_equal 5, Decorator.new(@string).size end - def test_try_with_overriden_method_on_delegator + def test_try_with_overridden_method_on_delegator assert_equal 'overridden reverse', Decorator.new(@string).reverse end diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb index f096328cee..f28cebda3d 100644 --- a/activesupport/test/core_ext/range_ext_test.rb +++ b/activesupport/test/core_ext/range_ext_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'active_support/time' +require 'active_support/core_ext/numeric' require 'active_support/core_ext/range' class RangeTest < ActiveSupport::TestCase @@ -13,6 +14,11 @@ class RangeTest < ActiveSupport::TestCase assert_equal "BETWEEN '2005-12-10 15:30:00' AND '2005-12-10 17:30:00'", date_range.to_s(:db) end + def test_to_s_with_numeric + number_range = (1..100) + assert_equal "BETWEEN '1' AND '100'", number_range.to_s(:db) + end + def test_date_range assert_instance_of Range, DateTime.new..DateTime.new assert_instance_of Range, DateTime::Infinity.new..DateTime::Infinity.new diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index cb24147ab3..2e69816364 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -143,15 +143,49 @@ class StringInflectionsTest < ActiveSupport::TestCase end end + def test_string_parameterized_normal_preserve_case + StringToParameterizedPreserveCase.each do |normal, slugged| + assert_equal(normal.parameterize(preserve_case: true), slugged) + end + end + def test_string_parameterized_no_separator StringToParameterizeWithNoSeparator.each do |normal, slugged| - assert_equal(normal.parameterize(''), slugged) + assert_equal(normal.parameterize(separator: ''), slugged) + end + end + + def test_string_parameterized_no_separator_deprecated + StringToParameterizeWithNoSeparator.each do |normal, slugged| + assert_deprecated(/Passing the separator argument as a positional parameter is deprecated and will soon be removed. Use `separator: ''` instead./i) do + assert_equal(normal.parameterize(''), slugged) + end + end + end + + def test_string_parameterized_no_separator_preserve_case + StringToParameterizePreserveCaseWithNoSeparator.each do |normal, slugged| + assert_equal(normal.parameterize(separator: '', preserve_case: true), slugged) end end def test_string_parameterized_underscore StringToParameterizeWithUnderscore.each do |normal, slugged| - assert_equal(normal.parameterize('_'), slugged) + assert_equal(normal.parameterize(separator: '_'), slugged) + end + end + + def test_string_parameterized_underscore_deprecated + StringToParameterizeWithUnderscore.each do |normal, slugged| + assert_deprecated(/Passing the separator argument as a positional parameter is deprecated and will soon be removed. Use `separator: '_'` instead./i) do + assert_equal(normal.parameterize('_'), slugged) + end + end + end + + def test_string_parameterized_underscore_preserve_case + StringToParameterizePreserceCaseWithUnderscore.each do |normal, slugged| + assert_equal(normal.parameterize(separator: '_', preserve_case: true), slugged) end end @@ -435,117 +469,123 @@ class StringConversionsTest < ActiveSupport::TestCase def test_standard_time_string_to_time_when_current_time_is_standard_time with_env_tz "US/Eastern" do - Time.stubs(:now).returns(Time.local(2012, 1, 1)) - assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time - assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time(:utc) - assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 -0800".to_time - assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 -0800".to_time(:utc) - assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 -0500".to_time - assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 -0500".to_time(:utc) - assert_equal Time.local(2012, 1, 1, 5, 0), "2012-01-01 10:00 UTC".to_time - assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00 UTC".to_time(:utc) - assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 PST".to_time - assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 PST".to_time(:utc) - assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 EST".to_time - assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 EST".to_time(:utc) + Time.stub(:now, Time.local(2012, 1, 1)) do + assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time + assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 -0800".to_time + assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 -0800".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 -0500".to_time + assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 -0500".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 5, 0), "2012-01-01 10:00 UTC".to_time + assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00 UTC".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 PST".to_time + assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 PST".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 EST".to_time + assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 EST".to_time(:utc) + end end end def test_standard_time_string_to_time_when_current_time_is_daylight_savings with_env_tz "US/Eastern" do - Time.stubs(:now).returns(Time.local(2012, 7, 1)) - assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time - assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time(:utc) - assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 -0800".to_time - assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 -0800".to_time(:utc) - assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 -0500".to_time - assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 -0500".to_time(:utc) - assert_equal Time.local(2012, 1, 1, 5, 0), "2012-01-01 10:00 UTC".to_time - assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00 UTC".to_time(:utc) - assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 PST".to_time - assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 PST".to_time(:utc) - assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 EST".to_time - assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 EST".to_time(:utc) + Time.stub(:now, Time.local(2012, 7, 1)) do + assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time + assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 -0800".to_time + assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 -0800".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 -0500".to_time + assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 -0500".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 5, 0), "2012-01-01 10:00 UTC".to_time + assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00 UTC".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 PST".to_time + assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 PST".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 EST".to_time + assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 EST".to_time(:utc) + end end end def test_daylight_savings_string_to_time_when_current_time_is_standard_time with_env_tz "US/Eastern" do - Time.stubs(:now).returns(Time.local(2012, 1, 1)) - assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time - assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time(:utc) - assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 -0700".to_time - assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 -0700".to_time(:utc) - assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 -0400".to_time - assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 -0400".to_time(:utc) - assert_equal Time.local(2012, 7, 1, 6, 0), "2012-07-01 10:00 UTC".to_time - assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00 UTC".to_time(:utc) - assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 PDT".to_time - assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 PDT".to_time(:utc) - assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 EDT".to_time - assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 EDT".to_time(:utc) + Time.stub(:now, Time.local(2012, 1, 1)) do + assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time + assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 -0700".to_time + assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 -0700".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 -0400".to_time + assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 -0400".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 6, 0), "2012-07-01 10:00 UTC".to_time + assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00 UTC".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 PDT".to_time + assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 PDT".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 EDT".to_time + assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 EDT".to_time(:utc) + end end end def test_daylight_savings_string_to_time_when_current_time_is_daylight_savings with_env_tz "US/Eastern" do - Time.stubs(:now).returns(Time.local(2012, 7, 1)) - assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time - assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time(:utc) - assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 -0700".to_time - assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 -0700".to_time(:utc) - assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 -0400".to_time - assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 -0400".to_time(:utc) - assert_equal Time.local(2012, 7, 1, 6, 0), "2012-07-01 10:00 UTC".to_time - assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00 UTC".to_time(:utc) - assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 PDT".to_time - assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 PDT".to_time(:utc) - assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 EDT".to_time - assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 EDT".to_time(:utc) + Time.stub(:now, Time.local(2012, 7, 1)) do + assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time + assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 -0700".to_time + assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 -0700".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 -0400".to_time + assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 -0400".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 6, 0), "2012-07-01 10:00 UTC".to_time + assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00 UTC".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 PDT".to_time + assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 PDT".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 EDT".to_time + assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 EDT".to_time(:utc) + end end end def test_partial_string_to_time_when_current_time_is_standard_time with_env_tz "US/Eastern" do - Time.stubs(:now).returns(Time.local(2012, 1, 1)) - assert_equal Time.local(2012, 1, 1, 10, 0), "10:00".to_time - assert_equal Time.utc(2012, 1, 1, 10, 0), "10:00".to_time(:utc) - assert_equal Time.local(2012, 1, 1, 6, 0), "10:00 -0100".to_time - assert_equal Time.utc(2012, 1, 1, 11, 0), "10:00 -0100".to_time(:utc) - assert_equal Time.local(2012, 1, 1, 10, 0), "10:00 -0500".to_time - assert_equal Time.utc(2012, 1, 1, 15, 0), "10:00 -0500".to_time(:utc) - assert_equal Time.local(2012, 1, 1, 5, 0), "10:00 UTC".to_time - assert_equal Time.utc(2012, 1, 1, 10, 0), "10:00 UTC".to_time(:utc) - assert_equal Time.local(2012, 1, 1, 13, 0), "10:00 PST".to_time - assert_equal Time.utc(2012, 1, 1, 18, 0), "10:00 PST".to_time(:utc) - assert_equal Time.local(2012, 1, 1, 12, 0), "10:00 PDT".to_time - assert_equal Time.utc(2012, 1, 1, 17, 0), "10:00 PDT".to_time(:utc) - assert_equal Time.local(2012, 1, 1, 10, 0), "10:00 EST".to_time - assert_equal Time.utc(2012, 1, 1, 15, 0), "10:00 EST".to_time(:utc) - assert_equal Time.local(2012, 1, 1, 9, 0), "10:00 EDT".to_time - assert_equal Time.utc(2012, 1, 1, 14, 0), "10:00 EDT".to_time(:utc) + Time.stub(:now, Time.local(2012, 1, 1)) do + assert_equal Time.local(2012, 1, 1, 10, 0), "10:00".to_time + assert_equal Time.utc(2012, 1, 1, 10, 0), "10:00".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 6, 0), "10:00 -0100".to_time + assert_equal Time.utc(2012, 1, 1, 11, 0), "10:00 -0100".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 10, 0), "10:00 -0500".to_time + assert_equal Time.utc(2012, 1, 1, 15, 0), "10:00 -0500".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 5, 0), "10:00 UTC".to_time + assert_equal Time.utc(2012, 1, 1, 10, 0), "10:00 UTC".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 13, 0), "10:00 PST".to_time + assert_equal Time.utc(2012, 1, 1, 18, 0), "10:00 PST".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 12, 0), "10:00 PDT".to_time + assert_equal Time.utc(2012, 1, 1, 17, 0), "10:00 PDT".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 10, 0), "10:00 EST".to_time + assert_equal Time.utc(2012, 1, 1, 15, 0), "10:00 EST".to_time(:utc) + assert_equal Time.local(2012, 1, 1, 9, 0), "10:00 EDT".to_time + assert_equal Time.utc(2012, 1, 1, 14, 0), "10:00 EDT".to_time(:utc) + end end end def test_partial_string_to_time_when_current_time_is_daylight_savings with_env_tz "US/Eastern" do - Time.stubs(:now).returns(Time.local(2012, 7, 1)) - assert_equal Time.local(2012, 7, 1, 10, 0), "10:00".to_time - assert_equal Time.utc(2012, 7, 1, 10, 0), "10:00".to_time(:utc) - assert_equal Time.local(2012, 7, 1, 7, 0), "10:00 -0100".to_time - assert_equal Time.utc(2012, 7, 1, 11, 0), "10:00 -0100".to_time(:utc) - assert_equal Time.local(2012, 7, 1, 11, 0), "10:00 -0500".to_time - assert_equal Time.utc(2012, 7, 1, 15, 0), "10:00 -0500".to_time(:utc) - assert_equal Time.local(2012, 7, 1, 6, 0), "10:00 UTC".to_time - assert_equal Time.utc(2012, 7, 1, 10, 0), "10:00 UTC".to_time(:utc) - assert_equal Time.local(2012, 7, 1, 14, 0), "10:00 PST".to_time - assert_equal Time.utc(2012, 7, 1, 18, 0), "10:00 PST".to_time(:utc) - assert_equal Time.local(2012, 7, 1, 13, 0), "10:00 PDT".to_time - assert_equal Time.utc(2012, 7, 1, 17, 0), "10:00 PDT".to_time(:utc) - assert_equal Time.local(2012, 7, 1, 11, 0), "10:00 EST".to_time - assert_equal Time.utc(2012, 7, 1, 15, 0), "10:00 EST".to_time(:utc) - assert_equal Time.local(2012, 7, 1, 10, 0), "10:00 EDT".to_time - assert_equal Time.utc(2012, 7, 1, 14, 0), "10:00 EDT".to_time(:utc) + Time.stub(:now, Time.local(2012, 7, 1)) do + assert_equal Time.local(2012, 7, 1, 10, 0), "10:00".to_time + assert_equal Time.utc(2012, 7, 1, 10, 0), "10:00".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 7, 0), "10:00 -0100".to_time + assert_equal Time.utc(2012, 7, 1, 11, 0), "10:00 -0100".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 11, 0), "10:00 -0500".to_time + assert_equal Time.utc(2012, 7, 1, 15, 0), "10:00 -0500".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 6, 0), "10:00 UTC".to_time + assert_equal Time.utc(2012, 7, 1, 10, 0), "10:00 UTC".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 14, 0), "10:00 PST".to_time + assert_equal Time.utc(2012, 7, 1, 18, 0), "10:00 PST".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 13, 0), "10:00 PDT".to_time + assert_equal Time.utc(2012, 7, 1, 17, 0), "10:00 PDT".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 11, 0), "10:00 EST".to_time + assert_equal Time.utc(2012, 7, 1, 15, 0), "10:00 EST".to_time(:utc) + assert_equal Time.local(2012, 7, 1, 10, 0), "10:00 EDT".to_time + assert_equal Time.utc(2012, 7, 1, 14, 0), "10:00 EDT".to_time(:utc) + end end end @@ -776,8 +816,8 @@ class OutputSafetyTest < ActiveSupport::TestCase end test "ERB::Util.html_escape should correctly handle invalid UTF-8 strings" do - string = [192, 60].pack('CC') - expected = 192.chr + "<" + string = "\251 <" + expected = "© <" assert_equal expected, ERB::Util.html_escape(string) end @@ -793,6 +833,12 @@ class OutputSafetyTest < ActiveSupport::TestCase assert_equal escaped_string, ERB::Util.html_escape_once(string) assert_equal escaped_string, ERB::Util.html_escape_once(escaped_string) end + + test "ERB::Util.html_escape_once should correctly handle invalid UTF-8 strings" do + string = "\251 <" + expected = "© <" + assert_equal expected, ERB::Util.html_escape_once(string) + end end class StringExcludeTest < ActiveSupport::TestCase diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index d59775001b..e45df63fd5 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -149,6 +149,9 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal Time.local(2006,3,19,23,59,59,Rational(999999999, 1000)), Time.local(2006,3,19,10,10,10).end_of_day, 'ends DST' assert_equal Time.local(2006,10,1,23,59,59,Rational(999999999, 1000)), Time.local(2006,10,1,10,10,10).end_of_day, 'start DST' end + with_env_tz 'Asia/Yekaterinburg' do + assert_equal Time.local(2015, 2, 8, 23, 59, 59, Rational(999999999, 1000)), Time.new(2015, 2, 8, 8, 0, 0, '+05:00').end_of_day + end end def test_end_of_hour @@ -389,6 +392,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) } end def test_utc_change @@ -530,6 +534,7 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal "17:44", time.to_s(:time) assert_equal "20050221174430", time.to_s(:number) assert_equal "20050221174430123456789", time.to_s(:nsec) + assert_equal "20050221174430123456", time.to_s(:usec) assert_equal "February 21, 2005 17:44", time.to_s(:long) assert_equal "February 21st, 2005 17:44", time.to_s(:long_ordinal) with_env_tz "UTC" do @@ -601,13 +606,34 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase end def test_days_in_month_feb_in_common_year_without_year_arg - Time.stubs(:now).returns(Time.utc(2007)) - assert_equal 28, Time.days_in_month(2) + Time.stub(:now, Time.utc(2007)) do + assert_equal 28, Time.days_in_month(2) + end end def test_days_in_month_feb_in_leap_year_without_year_arg - Time.stubs(:now).returns(Time.utc(2008)) - assert_equal 29, Time.days_in_month(2) + Time.stub(:now, Time.utc(2008)) do + assert_equal 29, Time.days_in_month(2) + end + end + + def test_days_in_year_with_year + assert_equal 365, Time.days_in_year(2005) + assert_equal 366, Time.days_in_year(2004) + assert_equal 366, Time.days_in_year(2000) + assert_equal 365, Time.days_in_year(1900) + end + + def test_days_in_year_in_common_year_without_year_arg + Time.stub(:now, Time.utc(2007)) do + assert_equal 365, Time.days_in_year + end + end + + def test_days_in_year_in_leap_year_without_year_arg + Time.stub(:now, Time.utc(2008)) do + assert_equal 366, Time.days_in_year + end end def test_last_month_on_31st @@ -619,68 +645,74 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase end def test_today_with_time_local - Date.stubs(:current).returns(Date.new(2000, 1, 1)) - assert_equal false, Time.local(1999,12,31,23,59,59).today? - assert_equal true, Time.local(2000,1,1,0).today? - assert_equal true, Time.local(2000,1,1,23,59,59).today? - assert_equal false, Time.local(2000,1,2,0).today? + Date.stub(:current, Date.new(2000, 1, 1)) do + assert_equal false, Time.local(1999,12,31,23,59,59).today? + assert_equal true, Time.local(2000,1,1,0).today? + assert_equal true, Time.local(2000,1,1,23,59,59).today? + assert_equal false, Time.local(2000,1,2,0).today? + end end def test_today_with_time_utc - Date.stubs(:current).returns(Date.new(2000, 1, 1)) - assert_equal false, Time.utc(1999,12,31,23,59,59).today? - assert_equal true, Time.utc(2000,1,1,0).today? - assert_equal true, Time.utc(2000,1,1,23,59,59).today? - assert_equal false, Time.utc(2000,1,2,0).today? + Date.stub(:current, Date.new(2000, 1, 1)) do + assert_equal false, Time.utc(1999,12,31,23,59,59).today? + assert_equal true, Time.utc(2000,1,1,0).today? + assert_equal true, Time.utc(2000,1,1,23,59,59).today? + assert_equal false, Time.utc(2000,1,2,0).today? + end end def test_past_with_time_current_as_time_local with_env_tz 'US/Eastern' do - Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45)) - assert_equal true, Time.local(2005,2,10,15,30,44).past? - assert_equal false, Time.local(2005,2,10,15,30,45).past? - assert_equal false, Time.local(2005,2,10,15,30,46).past? - assert_equal true, Time.utc(2005,2,10,20,30,44).past? - assert_equal false, Time.utc(2005,2,10,20,30,45).past? - assert_equal false, Time.utc(2005,2,10,20,30,46).past? + Time.stub(:current, Time.local(2005,2,10,15,30,45)) do + assert_equal true, Time.local(2005,2,10,15,30,44).past? + assert_equal false, Time.local(2005,2,10,15,30,45).past? + assert_equal false, Time.local(2005,2,10,15,30,46).past? + assert_equal true, Time.utc(2005,2,10,20,30,44).past? + assert_equal false, Time.utc(2005,2,10,20,30,45).past? + assert_equal false, Time.utc(2005,2,10,20,30,46).past? + end end end def test_past_with_time_current_as_time_with_zone with_env_tz 'US/Eastern' do twz = Time.utc(2005,2,10,15,30,45).in_time_zone('Central Time (US & Canada)') - Time.stubs(:current).returns(twz) - assert_equal true, Time.local(2005,2,10,10,30,44).past? - assert_equal false, Time.local(2005,2,10,10,30,45).past? - assert_equal false, Time.local(2005,2,10,10,30,46).past? - assert_equal true, Time.utc(2005,2,10,15,30,44).past? - assert_equal false, Time.utc(2005,2,10,15,30,45).past? - assert_equal false, Time.utc(2005,2,10,15,30,46).past? + Time.stub(:current, twz) do + assert_equal true, Time.local(2005,2,10,10,30,44).past? + assert_equal false, Time.local(2005,2,10,10,30,45).past? + assert_equal false, Time.local(2005,2,10,10,30,46).past? + assert_equal true, Time.utc(2005,2,10,15,30,44).past? + assert_equal false, Time.utc(2005,2,10,15,30,45).past? + assert_equal false, Time.utc(2005,2,10,15,30,46).past? + end end end def test_future_with_time_current_as_time_local with_env_tz 'US/Eastern' do - Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45)) - assert_equal false, Time.local(2005,2,10,15,30,44).future? - assert_equal false, Time.local(2005,2,10,15,30,45).future? - assert_equal true, Time.local(2005,2,10,15,30,46).future? - assert_equal false, Time.utc(2005,2,10,20,30,44).future? - assert_equal false, Time.utc(2005,2,10,20,30,45).future? - assert_equal true, Time.utc(2005,2,10,20,30,46).future? + Time.stub(:current, Time.local(2005,2,10,15,30,45)) do + assert_equal false, Time.local(2005,2,10,15,30,44).future? + assert_equal false, Time.local(2005,2,10,15,30,45).future? + assert_equal true, Time.local(2005,2,10,15,30,46).future? + assert_equal false, Time.utc(2005,2,10,20,30,44).future? + assert_equal false, Time.utc(2005,2,10,20,30,45).future? + assert_equal true, Time.utc(2005,2,10,20,30,46).future? + end end end def test_future_with_time_current_as_time_with_zone with_env_tz 'US/Eastern' do twz = Time.utc(2005,2,10,15,30,45).in_time_zone('Central Time (US & Canada)') - Time.stubs(:current).returns(twz) - assert_equal false, Time.local(2005,2,10,10,30,44).future? - assert_equal false, Time.local(2005,2,10,10,30,45).future? - assert_equal true, Time.local(2005,2,10,10,30,46).future? - assert_equal false, Time.utc(2005,2,10,15,30,44).future? - assert_equal false, Time.utc(2005,2,10,15,30,45).future? - assert_equal true, Time.utc(2005,2,10,15,30,46).future? + Time.stub(:current, twz) do + assert_equal false, Time.local(2005,2,10,10,30,44).future? + assert_equal false, Time.local(2005,2,10,10,30,45).future? + assert_equal true, Time.local(2005,2,10,10,30,46).future? + assert_equal false, Time.utc(2005,2,10,15,30,44).future? + assert_equal false, Time.utc(2005,2,10,15,30,45).future? + assert_equal true, Time.utc(2005,2,10,15,30,46).future? + end end end @@ -721,6 +753,13 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal(-1, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 1), ActiveSupport::TimeZone['UTC'] )) end + def test_compare_with_string + assert_equal 1, Time.utc(2000) <=> Time.utc(1999, 12, 31, 23, 59, 59, 999).to_s + assert_equal 0, Time.utc(2000) <=> Time.utc(2000, 1, 1, 0, 0, 0).to_s + assert_equal( -1, Time.utc(2000) <=> Time.utc(2000, 1, 1, 0, 0, 1, 0).to_s) + assert_equal nil, Time.utc(2000) <=> 'Invalid as Time' + end + def test_at_with_datetime assert_equal Time.utc(2000, 1, 1, 0, 0, 0), Time.at(DateTime.civil(2000, 1, 1, 0, 0, 0)) diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index 79d78c02cd..7acada011d 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -2,6 +2,7 @@ require 'abstract_unit' require 'active_support/time' require 'time_zone_test_helpers' require 'active_support/core_ext/string/strip' +require 'yaml' class TimeWithZoneTest < ActiveSupport::TestCase include TimeZoneTestHelpers @@ -50,7 +51,22 @@ class TimeWithZoneTest < ActiveSupport::TestCase def test_utc? assert_equal false, @twz.utc? + assert_equal true, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['UTC']).utc? + assert_equal true, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['Etc/UTC']).utc? + assert_equal true, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['Universal']).utc? + assert_equal true, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['UCT']).utc? + assert_equal true, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['Etc/UCT']).utc? + assert_equal true, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['Etc/Universal']).utc? + + assert_equal false, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['Africa/Abidjan']).utc? + assert_equal false, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['Africa/Banjul']).utc? + assert_equal false, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['Africa/Freetown']).utc? + assert_equal false, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['GMT']).utc? + assert_equal false, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['GMT0']).utc? + assert_equal false, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['Greenwich']).utc? + assert_equal false, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['Iceland']).utc? + assert_equal false, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['Africa/Monrovia']).utc? end def test_formatted_offset @@ -109,14 +125,14 @@ class TimeWithZoneTest < ActiveSupport::TestCase @twz += 0.1234560001 # advance the time by a fraction of a second assert_equal "1999-12-31T19:00:00.123-05:00", @twz.xmlschema(3) assert_equal "1999-12-31T19:00:00.123456-05:00", @twz.xmlschema(6) - assert_equal "1999-12-31T19:00:00.123456-05:00", @twz.xmlschema(12) + assert_equal "1999-12-31T19:00:00.123456000100-05:00", @twz.xmlschema(12) end def test_xmlschema_with_fractional_seconds_lower_than_hundred_thousand @twz += 0.001234 # advance the time by a fraction assert_equal "1999-12-31T19:00:00.001-05:00", @twz.xmlschema(3) assert_equal "1999-12-31T19:00:00.001234-05:00", @twz.xmlschema(6) - assert_equal "1999-12-31T19:00:00.001234-05:00", @twz.xmlschema(12) + assert_equal "1999-12-31T19:00:00.001234000000-05:00", @twz.xmlschema(12) end def test_xmlschema_with_nil_fractional_seconds @@ -205,52 +221,61 @@ class TimeWithZoneTest < ActiveSupport::TestCase end def test_today - Date.stubs(:current).returns(Date.new(2000, 1, 1)) - assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(1999,12,31,23,59,59) ).today? - assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,0) ).today? - assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,23,59,59) ).today? - assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,2,0) ).today? + Date.stub(:current, Date.new(2000, 1, 1)) do + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(1999,12,31,23,59,59) ).today? + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,0) ).today? + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,23,59,59) ).today? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,2,0) ).today? + end end def test_past_with_time_current_as_time_local with_env_tz 'US/Eastern' do - Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45)) - assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past? - assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past? - assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past? + Time.stub(:current, Time.local(2005,2,10,15,30,45)) do + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past? + end end end def test_past_with_time_current_as_time_with_zone twz = ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45) ) - Time.stubs(:current).returns(twz) - assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past? - assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past? - assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past? + Time.stub(:current, twz) do + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past? + end end def test_future_with_time_current_as_time_local with_env_tz 'US/Eastern' do - Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45)) - assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future? - assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future? - assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future? + Time.stub(:current, Time.local(2005,2,10,15,30,45)) do + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future? + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future? + end end end def test_future_with_time_current_as_time_with_zone twz = ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45) ) - Time.stubs(:current).returns(twz) - assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future? - assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future? - assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future? + Time.stub(:current, twz) do + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future? + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future? + end end def test_eql? + assert_equal true, @twz.eql?(@twz.dup) assert_equal true, @twz.eql?(Time.utc(2000)) assert_equal true, @twz.eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) ) assert_equal false, @twz.eql?( Time.utc(2000, 1, 1, 0, 0, 1) ) assert_equal false, @twz.eql?( DateTime.civil(1999, 12, 31, 23, 59, 59) ) + + other_twz = ActiveSupport::TimeWithZone.new(DateTime.now.utc, @time_zone) + assert_equal true, other_twz.eql?(other_twz.dup) end def test_hash @@ -477,22 +502,24 @@ class TimeWithZoneTest < ActiveSupport::TestCase end def test_method_missing_with_non_time_return_value - @twz.time.expects(:foo).returns('bar') + time = @twz.time + def time.foo; 'bar'; end assert_equal 'bar', @twz.foo end def test_date_part_value_methods twz = ActiveSupport::TimeWithZone.new(Time.utc(1999,12,31,19,18,17,500), @time_zone) - twz.expects(:method_missing).never - assert_equal 1999, twz.year - assert_equal 12, twz.month - assert_equal 31, twz.day - assert_equal 14, twz.hour - assert_equal 18, twz.min - assert_equal 17, twz.sec - assert_equal 500, twz.usec - assert_equal 5, twz.wday - assert_equal 365, twz.yday + assert_not_called(twz, :method_missing) do + assert_equal 1999, twz.year + assert_equal 12, twz.month + assert_equal 31, twz.day + assert_equal 14, twz.hour + assert_equal 18, twz.min + assert_equal 17, twz.sec + assert_equal 500, twz.usec + assert_equal 5, twz.wday + assert_equal 365, twz.yday + end end def test_usec_returns_0_when_datetime_is_wrapped @@ -1033,19 +1060,21 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < ActiveSupport::TestCase def test_current_returns_time_now_when_zone_not_set with_env_tz 'US/Eastern' do - Time.stubs(:now).returns Time.local(2000) - assert_equal false, Time.current.is_a?(ActiveSupport::TimeWithZone) - assert_equal Time.local(2000), Time.current + Time.stub(:now, Time.local(2000)) do + assert_equal false, Time.current.is_a?(ActiveSupport::TimeWithZone) + assert_equal Time.local(2000), Time.current + end end end def test_current_returns_time_zone_now_when_zone_set Time.zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] with_env_tz 'US/Eastern' do - Time.stubs(:now).returns Time.local(2000) - assert_equal true, Time.current.is_a?(ActiveSupport::TimeWithZone) - assert_equal 'Eastern Time (US & Canada)', Time.current.time_zone.name - assert_equal Time.utc(2000), Time.current.time + Time.stub(:now, Time.local(2000)) do + assert_equal true, Time.current.is_a?(ActiveSupport::TimeWithZone) + assert_equal 'Eastern Time (US & Canada)', Time.current.time_zone.name + assert_equal Time.utc(2000), Time.current.time + end end end diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index 4a1d90bfd6..757e600646 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -187,7 +187,7 @@ class DependenciesTest < ActiveSupport::TestCase assert_kind_of Module, A assert_kind_of Class, A::B assert_kind_of Class, A::C::D - assert_kind_of Class, A::C::E::F + assert_kind_of Class, A::C::EM::F end end @@ -552,24 +552,24 @@ class DependenciesTest < ActiveSupport::TestCase def test_const_missing_in_anonymous_modules_loads_top_level_constants with_autoloading_fixtures do # class_eval STRING pushes the class to the nesting of the eval'ed code. - klass = Class.new.class_eval "E" - assert_equal E, klass + klass = Class.new.class_eval "EM" + assert_equal EM, klass end ensure - remove_constants(:E) + remove_constants(:EM) end def test_const_missing_in_anonymous_modules_raises_if_the_constant_belongs_to_Object with_autoloading_fixtures do - require_dependency 'e' + require_dependency 'em' mod = Module.new - e = assert_raise(NameError) { mod::E } - assert_equal 'E cannot be autoloaded from an anonymous class or module', e.message - assert_equal :E, e.name + e = assert_raise(NameError) { mod::EM } + assert_equal 'EM cannot be autoloaded from an anonymous class or module', e.message + assert_equal :EM, e.name end ensure - remove_constants(:E) + remove_constants(:EM) end def test_removal_from_tree_should_be_detected @@ -664,19 +664,19 @@ class DependenciesTest < ActiveSupport::TestCase def test_preexisting_constants_are_not_marked_as_autoloaded with_autoloading_fixtures do - require_dependency 'e' - assert ActiveSupport::Dependencies.autoloaded?(:E) + require_dependency 'em' + assert ActiveSupport::Dependencies.autoloaded?(:EM) ActiveSupport::Dependencies.clear end - Object.const_set :E, Class.new + Object.const_set :EM, Class.new with_autoloading_fixtures do - require_dependency 'e' - assert ! ActiveSupport::Dependencies.autoloaded?(:E), "E shouldn't be marked autoloaded!" + require_dependency 'em' + assert ! ActiveSupport::Dependencies.autoloaded?(:EM), "EM shouldn't be marked autoloaded!" ActiveSupport::Dependencies.clear end ensure - remove_constants(:E) + remove_constants(:EM) end def test_constants_in_capitalized_nesting_marked_as_autoloaded @@ -721,12 +721,13 @@ class DependenciesTest < ActiveSupport::TestCase end def test_unloadable_constants_should_receive_callback - Object.const_set :C, Class.new + Object.const_set :C, Class.new { def self.before_remove_const; end } C.unloadable - C.expects(:before_remove_const).once - assert C.respond_to?(:before_remove_const) - ActiveSupport::Dependencies.clear - assert !defined?(C) + assert_called(C, :before_remove_const, times: 1) do + assert C.respond_to?(:before_remove_const) + ActiveSupport::Dependencies.clear + assert !defined?(C) + end ensure remove_constants(:C) end diff --git a/activesupport/test/deprecation/method_wrappers_test.rb b/activesupport/test/deprecation/method_wrappers_test.rb new file mode 100644 index 0000000000..9a4ca2b217 --- /dev/null +++ b/activesupport/test/deprecation/method_wrappers_test.rb @@ -0,0 +1,34 @@ +require 'abstract_unit' +require 'active_support/deprecation' + +class MethodWrappersTest < ActiveSupport::TestCase + def setup + @klass = Class.new do + def new_method; "abc" end + alias_method :old_method, :new_method + end + end + + def test_deprecate_methods_warning_default + warning = /old_method is deprecated and will be removed from Rails \d.\d \(use new_method instead\)/ + ActiveSupport::Deprecation.deprecate_methods(@klass, :old_method => :new_method) + + assert_deprecated(warning) { assert_equal "abc", @klass.new.old_method } + end + + def test_deprecate_methods_warning_with_optional_deprecator + warning = /old_method is deprecated and will be removed from MyGem next-release \(use new_method instead\)/ + deprecator = ActiveSupport::Deprecation.new("next-release", "MyGem") + ActiveSupport::Deprecation.deprecate_methods(@klass, :old_method => :new_method, :deprecator => deprecator) + + assert_deprecated(warning, deprecator) { assert_equal "abc", @klass.new.old_method } + end + + def test_deprecate_methods_warning_when_deprecated_with_custom_deprecator + warning = /old_method is deprecated and will be removed from MyGem next-release \(use new_method instead\)/ + deprecator = ActiveSupport::Deprecation.new("next-release", "MyGem") + deprecator.deprecate_methods(@klass, :old_method => :new_method) + + assert_deprecated(warning, deprecator) { assert_equal "abc", @klass.new.old_method } + end +end diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb index 20bd8ee5dd..58a0a3964d 100644 --- a/activesupport/test/deprecation_test.rb +++ b/activesupport/test/deprecation_test.rb @@ -163,6 +163,14 @@ class DeprecationTest < ActiveSupport::TestCase assert_not_deprecated { assert_equal Deprecatee::B::C.class, Deprecatee::A.class } end + def test_assert_deprecated_raises_when_method_not_deprecated + assert_raises(Minitest::Assertion) { assert_deprecated { @dtc.not } } + end + + def test_assert_not_deprecated + assert_raises(Minitest::Assertion) { assert_not_deprecated { @dtc.partially } } + end + def test_assert_deprecation_without_match assert_deprecated do @dtc.partially @@ -191,7 +199,7 @@ class DeprecationTest < ActiveSupport::TestCase end def test_assert_deprecated_warn_work_with_default_behavior - ActiveSupport::Deprecation.instance_variable_set('@behavior' , nil) + ActiveSupport::Deprecation.instance_variable_set('@behavior', nil) assert_deprecated('abc') do ActiveSupport::Deprecation.warn 'abc' end @@ -256,17 +264,17 @@ class DeprecationTest < ActiveSupport::TestCase end def test_deprecate_with_custom_deprecator - custom_deprecator = mock('Deprecator') do - expects(:deprecation_warning) - end + custom_deprecator = Struct.new(:deprecation_warning).new - klass = Class.new do - def method + assert_called_with(custom_deprecator, :deprecation_warning, [:method, nil]) do + klass = Class.new do + def method + end + deprecate :method, deprecator: custom_deprecator end - deprecate :method, deprecator: custom_deprecator - end - klass.new.method + klass.new.method + end end def test_deprecated_constant_with_deprecator_given @@ -332,6 +340,10 @@ class DeprecationTest < ActiveSupport::TestCase assert_match(/You are calling deprecated method/, object.last_message) end + def test_default_deprecation_horizon_should_always_bigger_than_current_rails_version + assert ActiveSupport::Deprecation.new.deprecation_horizon > ActiveSupport::VERSION::STRING + end + def test_default_gem_name deprecator = ActiveSupport::Deprecation.new diff --git a/activesupport/test/evented_file_update_checker_test.rb b/activesupport/test/evented_file_update_checker_test.rb new file mode 100644 index 0000000000..bc3f77bd54 --- /dev/null +++ b/activesupport/test/evented_file_update_checker_test.rb @@ -0,0 +1,155 @@ +require 'abstract_unit' +require 'pathname' +require 'file_update_checker_shared_tests' + +class EventedFileUpdateCheckerTest < ActiveSupport::TestCase + include FileUpdateCheckerSharedTests + + def setup + skip if ENV['LISTEN'] == '0' + super + end + + def new_checker(files = [], dirs = {}, &block) + ActiveSupport::EventedFileUpdateChecker.new(files, dirs, &block).tap do + wait + end + end + + def teardown + super + Listen.stop + end + + def wait + sleep 1 + end + + def touch(files) + super + wait # wait for the events to fire + end + + def rm_f(files) + super + wait + end +end + +class EventedFileUpdateCheckerPathHelperTest < ActiveSupport::TestCase + def pn(path) + Pathname.new(path) + end + + setup do + @ph = ActiveSupport::EventedFileUpdateChecker::PathHelper.new + end + + test '#xpath returns the expanded path as a Pathname object' do + assert_equal pn(__FILE__).expand_path, @ph.xpath(__FILE__) + end + + test '#normalize_extension returns a bare extension as is' do + assert_equal 'rb', @ph.normalize_extension('rb') + end + + test '#normalize_extension removes a leading dot' do + assert_equal 'rb', @ph.normalize_extension('.rb') + end + + test '#normalize_extension supports symbols' do + assert_equal 'rb', @ph.normalize_extension(:rb) + end + + test '#longest_common_subpath finds the longest common subpath, if there is one' do + paths = %w( + /foo/bar + /foo/baz + /foo/bar/baz/woo/zoo + ).map { |path| pn(path) } + + assert_equal pn('/foo'), @ph.longest_common_subpath(paths) + end + + test '#longest_common_subpath returns the root directory as an edge case' do + paths = %w( + /foo/bar + /foo/baz + /foo/bar/baz/woo/zoo + /wadus + ).map { |path| pn(path) } + + assert_equal pn('/'), @ph.longest_common_subpath(paths) + end + + test '#longest_common_subpath returns nil for an empty collection' do + assert_nil @ph.longest_common_subpath([]) + end + + test '#existing_parent returns the most specific existing ascendant' do + wd = Pathname.getwd + + assert_equal wd, @ph.existing_parent(wd) + assert_equal wd, @ph.existing_parent(wd.join('non-existing/directory')) + assert_equal pn('/'), @ph.existing_parent(pn('/non-existing/directory')) + end + + test '#filter_out_descendants returns the same collection if there are no descendants (empty)' do + assert_equal [], @ph.filter_out_descendants([]) + end + + test '#filter_out_descendants returns the same collection if there are no descendants (one)' do + assert_equal ['/foo'], @ph.filter_out_descendants(['/foo']) + end + + test '#filter_out_descendants returns the same collection if there are no descendants (several)' do + paths = %w( + /Rails.root/app/controllers + /Rails.root/app/models + /Rails.root/app/helpers + ).map { |path| pn(path) } + + assert_equal paths, @ph.filter_out_descendants(paths) + end + + test '#filter_out_descendants filters out descendants preserving order' do + paths = %w( + /Rails.root/app/controllers + /Rails.root/app/controllers/concerns + /Rails.root/app/models + /Rails.root/app/models/concerns + /Rails.root/app/helpers + ).map { |path| pn(path) } + + assert_equal paths.values_at(0, 2, 4), @ph.filter_out_descendants(paths) + end + + test '#filter_out_descendants works on path units' do + paths = %w( + /foo/bar + /foo/barrrr + ).map { |path| pn(path) } + + assert_equal paths, @ph.filter_out_descendants(paths) + end + + test '#filter_out_descendants deals correctly with the root directory' do + paths = %w( + / + /foo + /foo/bar + ).map { |path| pn(path) } + + assert_equal paths.values_at(0), @ph.filter_out_descendants(paths) + end + + test '#filter_out_descendants preserves duplicates' do + paths = %w( + /foo + /foo/bar + /foo + ).map { |path| pn(path) } + + assert_equal paths.values_at(0, 2), @ph.filter_out_descendants(paths) + end +end diff --git a/activesupport/test/file_update_checker_shared_tests.rb b/activesupport/test/file_update_checker_shared_tests.rb new file mode 100644 index 0000000000..9c07e38fe5 --- /dev/null +++ b/activesupport/test/file_update_checker_shared_tests.rb @@ -0,0 +1,242 @@ +require 'fileutils' + +module FileUpdateCheckerSharedTests + extend ActiveSupport::Testing::Declarative + include FileUtils + + def tmpdir + @tmpdir ||= Dir.mktmpdir(nil, __dir__) + end + + def tmpfile(name) + "#{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 + end + + test 'should not execute the block if no paths are given' do + silence_warnings { require 'listen' } + i = 0 + + checker = new_checker { i += 1 } + + assert !checker.execute_if_updated + assert_equal 0, i + end + + test 'should not execute the block if no files change' do + i = 0 + + FileUtils.touch(tmpfiles) + + checker = new_checker(tmpfiles) { i += 1 } + + assert !checker.execute_if_updated + assert_equal 0, i + end + + test 'should execute the block once when files are created' do + i = 0 + + checker = new_checker(tmpfiles) { i += 1 } + + touch(tmpfiles) + + assert checker.execute_if_updated + assert_equal 1, i + end + + test 'should execute the block once when files are modified' do + i = 0 + + FileUtils.touch(tmpfiles) + + checker = new_checker(tmpfiles) { i += 1 } + + touch(tmpfiles) + + assert checker.execute_if_updated + assert_equal 1, i + end + + test 'should execute the block once when files are deleted' do + i = 0 + + FileUtils.touch(tmpfiles) + + checker = new_checker(tmpfiles) { i += 1 } + + rm_f(tmpfiles) + + assert checker.execute_if_updated + assert_equal 1, i + end + + + test 'updated should become true when watched files are created' do + i = 0 + + checker = new_checker(tmpfiles) { i += 1 } + assert !checker.updated? + + touch(tmpfiles) + + assert checker.updated? + end + + + test 'updated should become true when watched files are modified' do + i = 0 + + FileUtils.touch(tmpfiles) + + checker = new_checker(tmpfiles) { i += 1 } + assert !checker.updated? + + touch(tmpfiles) + + assert checker.updated? + end + + test 'updated should become true when watched files are deleted' do + i = 0 + + FileUtils.touch(tmpfiles) + + checker = new_checker(tmpfiles) { i += 1 } + assert !checker.updated? + + rm_f(tmpfiles) + + assert checker.updated? + end + + test 'should be robust to handle files with wrong modified time' do + i = 0 + + FileUtils.touch(tmpfiles) + + now = Time.now + time = Time.mktime(now.year + 1, now.month, now.day) # 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 + + checker = new_checker(tmpfiles) { i += 1 } + assert !checker.updated? + + touch(tmpfiles) + + assert checker.updated? + checker.execute + assert !checker.updated? + end + + test 'should execute the block if files change in a watched directory one extension' do + i = 0 + + checker = new_checker([], tmpdir => :rb) { i += 1 } + + touch(tmpfile('foo.rb')) + + assert checker.execute_if_updated + assert_equal 1, i + end + + test 'should execute the block if files change in a watched directory several extensions' do + i = 0 + + checker = new_checker([], tmpdir => [:rb, :txt]) { i += 1 } + + touch(tmpfile('foo.rb')) + + assert checker.execute_if_updated + assert_equal 1, i + + touch(tmpfile('foo.txt')) + + assert checker.execute_if_updated + assert_equal 2, i + end + + test 'should not execute the block if the file extension is not watched' do + i = 0 + + checker = new_checker([], tmpdir => :txt) { i += 1 } + + touch(tmpfile('foo.rb')) + + assert !checker.execute_if_updated + assert_equal 0, i + end + + test 'does not assume files exist on instantiation' do + i = 0 + + non_existing = tmpfile('non_existing.rb') + checker = new_checker([non_existing]) { i += 1 } + + touch(non_existing) + + assert checker.execute_if_updated + assert_equal 1, i + end + + test 'detects files in new subdirectories' do + i = 0 + + checker = new_checker([], tmpdir => :rb) { i += 1 } + + subdir = tmpfile('subdir') + mkdir(subdir) + wait + + assert !checker.execute_if_updated + assert_equal 0, i + + touch("#{subdir}/nested.rb") + + assert checker.execute_if_updated + assert_equal 1, i + end + + test 'looked up extensions are inherited in subdirectories not listening to them' do + i = 0 + + subdir = tmpfile('subdir') + mkdir(subdir) + + checker = new_checker([], tmpdir => :rb, subdir => :txt) { i += 1 } + + touch(tmpfile('new.txt')) + + assert !checker.execute_if_updated + assert_equal 0, i + + # subdir does not look for Ruby files, but its parent tmpdir does. + touch("#{subdir}/nested.rb") + + assert checker.execute_if_updated + assert_equal 1, i + + touch("#{subdir}/nested.txt") + + assert checker.execute_if_updated + assert_equal 2, i + end +end diff --git a/activesupport/test/file_update_checker_test.rb b/activesupport/test/file_update_checker_test.rb index bd1df0f858..752f7836cd 100644 --- a/activesupport/test/file_update_checker_test.rb +++ b/activesupport/test/file_update_checker_test.rb @@ -1,112 +1,19 @@ require 'abstract_unit' -require 'fileutils' -require 'thread' +require 'file_update_checker_shared_tests' -MTIME_FIXTURES_PATH = File.expand_path("../fixtures", __FILE__) +class FileUpdateCheckerTest < ActiveSupport::TestCase + include FileUpdateCheckerSharedTests -class FileUpdateCheckerWithEnumerableTest < ActiveSupport::TestCase - FILES = %w(1.txt 2.txt 3.txt) - - def setup - FileUtils.mkdir_p("tmp_watcher") - FileUtils.touch(FILES) - end - - def teardown - FileUtils.rm_rf("tmp_watcher") - FileUtils.rm_rf(FILES) - end - - def test_should_not_execute_the_block_if_no_paths_are_given - i = 0 - checker = ActiveSupport::FileUpdateChecker.new([]){ i += 1 } - checker.execute_if_updated - assert_equal 0, i - end - - def test_should_not_invoke_the_block_if_no_file_has_changed - i = 0 - checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 } - 5.times { assert !checker.execute_if_updated } - assert_equal 0, i - end - - def test_should_invoke_the_block_if_a_file_has_changed - i = 0 - checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 } - sleep(1) - FileUtils.touch(FILES) - assert checker.execute_if_updated - assert_equal 1, i - end - - def test_should_be_robust_enough_to_handle_deleted_files - i = 0 - checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 } - FileUtils.rm(FILES) - assert checker.execute_if_updated - assert_equal 1, i - end - - def test_should_be_robust_to_handle_files_with_wrong_modified_time - i = 0 - now = Time.now - time = Time.mktime(now.year + 1, now.month, now.day) # wrong mtime from the future - File.utime time, time, FILES[2] - - checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 } - - sleep(1) - FileUtils.touch(FILES[0..1]) - - assert checker.execute_if_updated - assert_equal 1, i - end - - def test_should_cache_updated_result_until_execute - i = 0 - checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 } - assert !checker.updated? - - sleep(1) - FileUtils.touch(FILES) - - assert checker.updated? - checker.execute - assert !checker.updated? - end - - def test_should_invoke_the_block_if_a_watched_dir_changed_its_glob - i = 0 - checker = ActiveSupport::FileUpdateChecker.new([], "tmp_watcher" => [:txt]){ i += 1 } - FileUtils.cd "tmp_watcher" do - FileUtils.touch(FILES) - end - assert checker.execute_if_updated - assert_equal 1, i + def new_checker(files = [], dirs = {}, &block) + ActiveSupport::FileUpdateChecker.new(files, dirs, &block) end - def test_should_not_invoke_the_block_if_a_watched_dir_changed_its_glob - i = 0 - checker = ActiveSupport::FileUpdateChecker.new([], "tmp_watcher" => :rb){ i += 1 } - FileUtils.cd "tmp_watcher" do - FileUtils.touch(FILES) - end - assert !checker.execute_if_updated - assert_equal 0, i + def wait + # noop end - def test_should_not_block_if_a_strange_filename_used - FileUtils.mkdir_p("tmp_watcher/valid,yetstrange,path,") - FileUtils.touch(FILES.map { |file_name| "tmp_watcher/valid,yetstrange,path,/#{file_name}" }) - - test = Thread.new do - ActiveSupport::FileUpdateChecker.new([],"tmp_watcher/valid,yetstrange,path," => :txt) { i += 1 } - Thread.exit - end - test.priority = -1 - test.join(5) - - assert !test.alive? + def touch(files) + sleep 1 # let's wait a bit to ensure there's a new mtime + super end end diff --git a/activesupport/test/hash_with_indifferent_access_test.rb b/activesupport/test/hash_with_indifferent_access_test.rb deleted file mode 100644 index 1facd691fa..0000000000 --- a/activesupport/test/hash_with_indifferent_access_test.rb +++ /dev/null @@ -1,11 +0,0 @@ -require 'abstract_unit' -require 'active_support/hash_with_indifferent_access' - -class HashWithIndifferentAccessTest < ActiveSupport::TestCase - def test_reverse_merge - hash = HashWithIndifferentAccess.new key: :old_value - hash.reverse_merge! key: :new_value - assert_equal :old_value, hash[:key] - end - -end diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb index be68bb2e2e..06cd41c86d 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -8,6 +8,20 @@ class InflectorTest < ActiveSupport::TestCase include InflectorTestCases include ConstantizeTestCases + def setup + # Dups the singleton before each test, restoring the original inflections later. + # + # This helper is implemented by setting @__instance__ because in some tests + # there are module functions that access ActiveSupport::Inflector.inflections, + # so we need to replace the singleton itself. + @original_inflections = ActiveSupport::Inflector::Inflections.instance_variable_get(:@__instance__)[:en] + ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: @original_inflections.dup) + end + + def teardown + ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: @original_inflections) + end + def test_pluralize_plurals assert_equal "plurals", ActiveSupport::Inflector.pluralize("plurals") assert_equal "Plurals", ActiveSupport::Inflector.pluralize("Plurals") @@ -26,20 +40,18 @@ class InflectorTest < ActiveSupport::TestCase end def test_uncountable_word_is_not_greedy - with_dup do - uncountable_word = "ors" - countable_word = "sponsor" + uncountable_word = "ors" + countable_word = "sponsor" - ActiveSupport::Inflector.inflections.uncountable << uncountable_word + ActiveSupport::Inflector.inflections.uncountable << uncountable_word - assert_equal uncountable_word, ActiveSupport::Inflector.singularize(uncountable_word) - assert_equal uncountable_word, ActiveSupport::Inflector.pluralize(uncountable_word) - assert_equal ActiveSupport::Inflector.pluralize(uncountable_word), ActiveSupport::Inflector.singularize(uncountable_word) + assert_equal uncountable_word, ActiveSupport::Inflector.singularize(uncountable_word) + assert_equal uncountable_word, ActiveSupport::Inflector.pluralize(uncountable_word) + assert_equal ActiveSupport::Inflector.pluralize(uncountable_word), ActiveSupport::Inflector.singularize(uncountable_word) - assert_equal "sponsor", ActiveSupport::Inflector.singularize(countable_word) - assert_equal "sponsors", ActiveSupport::Inflector.pluralize(countable_word) - assert_equal "sponsor", ActiveSupport::Inflector.singularize(ActiveSupport::Inflector.pluralize(countable_word)) - end + assert_equal "sponsor", ActiveSupport::Inflector.singularize(countable_word) + assert_equal "sponsors", ActiveSupport::Inflector.pluralize(countable_word) + assert_equal "sponsor", ActiveSupport::Inflector.singularize(ActiveSupport::Inflector.pluralize(countable_word)) end SingularToPlural.each do |singular, plural| @@ -70,11 +82,9 @@ class InflectorTest < ActiveSupport::TestCase def test_overwrite_previous_inflectors - with_dup do - assert_equal("series", ActiveSupport::Inflector.singularize("series")) - ActiveSupport::Inflector.inflections.singular "series", "serie" - assert_equal("serie", ActiveSupport::Inflector.singularize("series")) - end + assert_equal("series", ActiveSupport::Inflector.singularize("series")) + ActiveSupport::Inflector.inflections.singular "series", "serie" + assert_equal("serie", ActiveSupport::Inflector.singularize("series")) end MixtureToTitleCase.each_with_index do |(before, titleized), index| @@ -259,14 +269,32 @@ class InflectorTest < ActiveSupport::TestCase def test_parameterize_with_custom_separator jruby_skip "UTF-8 to UTF8-MAC Converter is unavailable" StringToParameterizeWithUnderscore.each do |some_string, parameterized_string| - assert_equal(parameterized_string, ActiveSupport::Inflector.parameterize(some_string, '_')) + assert_equal(parameterized_string, ActiveSupport::Inflector.parameterize(some_string, separator: '_')) + end + end + + def test_parameterize_with_custom_separator_deprecated + jruby_skip "UTF-8 to UTF8-MAC Converter is unavailable" + StringToParameterizeWithUnderscore.each do |some_string, parameterized_string| + assert_deprecated(/Passing the separator argument as a positional parameter is deprecated and will soon be removed. Use `separator: '_'` instead./i) do + assert_equal(parameterized_string, ActiveSupport::Inflector.parameterize(some_string, '_')) + end end end def test_parameterize_with_multi_character_separator jruby_skip "UTF-8 to UTF8-MAC Converter is unavailable" StringToParameterized.each do |some_string, parameterized_string| - assert_equal(parameterized_string.gsub('-', '__sep__'), ActiveSupport::Inflector.parameterize(some_string, '__sep__')) + assert_equal(parameterized_string.gsub('-', '__sep__'), ActiveSupport::Inflector.parameterize(some_string, separator: '__sep__')) + end + end + + def test_parameterize_with_multi_character_separator_deprecated + jruby_skip "UTF-8 to UTF8-MAC Converter is unavailable" + StringToParameterized.each do |some_string, parameterized_string| + assert_deprecated(/Passing the separator argument as a positional parameter is deprecated and will soon be removed. Use `separator: '__sep__'` instead./i) do + assert_equal(parameterized_string.gsub('-', '__sep__'), ActiveSupport::Inflector.parameterize(some_string, '__sep__')) + end end end @@ -367,10 +395,8 @@ class InflectorTest < ActiveSupport::TestCase %w{plurals singulars uncountables humans}.each do |inflection_type| class_eval <<-RUBY, __FILE__, __LINE__ + 1 def test_clear_#{inflection_type} - with_dup do - ActiveSupport::Inflector.inflections.clear :#{inflection_type} - assert ActiveSupport::Inflector.inflections.#{inflection_type}.empty?, \"#{inflection_type} inflections should be empty after clear :#{inflection_type}\" - end + ActiveSupport::Inflector.inflections.clear :#{inflection_type} + assert ActiveSupport::Inflector.inflections.#{inflection_type}.empty?, \"#{inflection_type} inflections should be empty after clear :#{inflection_type}\" end RUBY end @@ -405,73 +431,63 @@ class InflectorTest < ActiveSupport::TestCase end def test_clear_all - with_dup do - ActiveSupport::Inflector.inflections do |inflect| - # ensure any data is present - inflect.plural(/(quiz)$/i, '\1zes') - inflect.singular(/(database)s$/i, '\1') - inflect.uncountable('series') - inflect.human("col_rpted_bugs", "Reported bugs") - - inflect.clear :all - - assert inflect.plurals.empty? - assert inflect.singulars.empty? - assert inflect.uncountables.empty? - assert inflect.humans.empty? - end + ActiveSupport::Inflector.inflections do |inflect| + # ensure any data is present + inflect.plural(/(quiz)$/i, '\1zes') + inflect.singular(/(database)s$/i, '\1') + inflect.uncountable('series') + inflect.human("col_rpted_bugs", "Reported bugs") + + inflect.clear :all + + assert inflect.plurals.empty? + assert inflect.singulars.empty? + assert inflect.uncountables.empty? + assert inflect.humans.empty? end end def test_clear_with_default - with_dup do - ActiveSupport::Inflector.inflections do |inflect| - # ensure any data is present - inflect.plural(/(quiz)$/i, '\1zes') - inflect.singular(/(database)s$/i, '\1') - inflect.uncountable('series') - inflect.human("col_rpted_bugs", "Reported bugs") - - inflect.clear - - assert inflect.plurals.empty? - assert inflect.singulars.empty? - assert inflect.uncountables.empty? - assert inflect.humans.empty? - end + ActiveSupport::Inflector.inflections do |inflect| + # ensure any data is present + inflect.plural(/(quiz)$/i, '\1zes') + inflect.singular(/(database)s$/i, '\1') + inflect.uncountable('series') + inflect.human("col_rpted_bugs", "Reported bugs") + + inflect.clear + + assert inflect.plurals.empty? + assert inflect.singulars.empty? + assert inflect.uncountables.empty? + assert inflect.humans.empty? end end Irregularities.each do |singular, plural| define_method("test_irregularity_between_#{singular}_and_#{plural}") do - with_dup do - ActiveSupport::Inflector.inflections do |inflect| - inflect.irregular(singular, plural) - assert_equal singular, ActiveSupport::Inflector.singularize(plural) - assert_equal plural, ActiveSupport::Inflector.pluralize(singular) - end + ActiveSupport::Inflector.inflections do |inflect| + inflect.irregular(singular, plural) + assert_equal singular, ActiveSupport::Inflector.singularize(plural) + assert_equal plural, ActiveSupport::Inflector.pluralize(singular) end end end Irregularities.each do |singular, plural| define_method("test_pluralize_of_irregularity_#{plural}_should_be_the_same") do - with_dup do - ActiveSupport::Inflector.inflections do |inflect| - inflect.irregular(singular, plural) - assert_equal plural, ActiveSupport::Inflector.pluralize(plural) - end + ActiveSupport::Inflector.inflections do |inflect| + inflect.irregular(singular, plural) + assert_equal plural, ActiveSupport::Inflector.pluralize(plural) end end end Irregularities.each do |singular, plural| define_method("test_singularize_of_irregularity_#{singular}_should_be_the_same") do - with_dup do - ActiveSupport::Inflector.inflections do |inflect| - inflect.irregular(singular, plural) - assert_equal singular, ActiveSupport::Inflector.singularize(singular) - end + ActiveSupport::Inflector.inflections do |inflect| + inflect.irregular(singular, plural) + assert_equal singular, ActiveSupport::Inflector.singularize(singular) end end end @@ -503,12 +519,10 @@ class InflectorTest < ActiveSupport::TestCase %w(plurals singulars uncountables humans acronyms).each do |scope| define_method("test_clear_inflections_with_#{scope}") do - with_dup do - # clear the inflections - ActiveSupport::Inflector.inflections do |inflect| - inflect.clear(scope) - assert_equal [], inflect.send(scope) - end + # clear the inflections + ActiveSupport::Inflector.inflections do |inflect| + inflect.clear(scope) + assert_equal [], inflect.send(scope) end end end @@ -520,18 +534,4 @@ class InflectorTest < ActiveSupport::TestCase assert_equal "HTTP", ActiveSupport::Inflector.pluralize("HTTP") end - - # Dups the singleton and yields, restoring the original inflections later. - # Use this in tests what modify the state of the singleton. - # - # This helper is implemented by setting @__instance__ because in some tests - # there are module functions that access ActiveSupport::Inflector.inflections, - # so we need to replace the singleton itself. - def with_dup - original = ActiveSupport::Inflector::Inflections.instance_variable_get(:@__instance__)[:en] - ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: original.dup) - yield - ensure - ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: original) - end end diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb index 18a8b92eb9..14fe97a986 100644 --- a/activesupport/test/inflector_test_cases.rb +++ b/activesupport/test/inflector_test_cases.rb @@ -1,4 +1,3 @@ - module InflectorTestCases SingularToPlural = { "search" => "searches", @@ -175,6 +174,17 @@ module InflectorTestCases "Test with malformed utf8 \251" => "test-with-malformed-utf8" } + StringToParameterizedPreserveCase = { + "Donald E. Knuth" => "Donald-E-Knuth", + "Random text with *(bad)* characters" => "Random-text-with-bad-characters", + "Allow_Under_Scores" => "Allow_Under_Scores", + "Trailing bad characters!@#" => "Trailing-bad-characters", + "!@#Leading bad characters" => "Leading-bad-characters", + "Squeeze separators" => "Squeeze-separators", + "Test with + sign" => "Test-with-sign", + "Test with malformed utf8 \xA9" => "Test-with-malformed-utf8" + } + StringToParameterizeWithNoSeparator = { "Donald E. Knuth" => "donaldeknuth", "With-some-dashes" => "with-some-dashes", @@ -186,6 +196,17 @@ module InflectorTestCases "Test with malformed utf8 \251" => "testwithmalformedutf8" } + StringToParameterizePreserveCaseWithNoSeparator = { + "Donald E. Knuth" => "DonaldEKnuth", + "With-some-dashes" => "With-some-dashes", + "Random text with *(bad)* characters" => "Randomtextwithbadcharacters", + "Trailing bad characters!@#" => "Trailingbadcharacters", + "!@#Leading bad characters" => "Leadingbadcharacters", + "Squeeze separators" => "Squeezeseparators", + "Test with + sign" => "Testwithsign", + "Test with malformed utf8 \xA9" => "Testwithmalformedutf8" + } + StringToParameterizeWithUnderscore = { "Donald E. Knuth" => "donald_e_knuth", "Random text with *(bad)* characters" => "random_text_with_bad_characters", @@ -198,6 +219,18 @@ module InflectorTestCases "Test with malformed utf8 \251" => "test_with_malformed_utf8" } + StringToParameterizePreserceCaseWithUnderscore = { + "Donald E. Knuth" => "Donald_E_Knuth", + "Random text with *(bad)* characters" => "Random_text_with_bad_characters", + "With-some-dashes" => "With-some-dashes", + "Allow_Under_Scores" => "Allow_Under_Scores", + "Trailing bad characters!@#" => "Trailing_bad_characters", + "!@#Leading bad characters" => "Leading_bad_characters", + "Squeeze separators" => "Squeeze_separators", + "Test with + sign" => "Test_with_sign", + "Test with malformed utf8 \xA9" => "Test_with_malformed_utf8" + } + StringToParameterizedAndNormalized = { "Malmö" => "malmo", "Garçons" => "garcons", diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index 2f269a66f0..9f4b62fd8b 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -4,122 +4,24 @@ require 'active_support/core_ext/string/inflections' require 'active_support/json' require 'active_support/time' require 'time_zone_test_helpers' +require 'json/encoding_test_cases' class TestJSONEncoding < ActiveSupport::TestCase include TimeZoneTestHelpers - class Foo - def initialize(a, b) - @a, @b = a, b - end - end - - class Hashlike - def to_hash - { :foo => "hello", :bar => "world" } - end - end - - class Custom - def initialize(serialized) - @serialized = serialized - end - - def as_json(options = nil) - @serialized - end - end - - class CustomWithOptions - attr_accessor :foo, :bar - - def as_json(options={}) - options[:only] = %w(foo bar) - super(options) - end - end - - class OptionsTest - def as_json(options = :default) - options - end - end - - class HashWithAsJson < Hash - attr_accessor :as_json_called - - def initialize(*) - super - end - - def as_json(options={}) - @as_json_called = true - super - end - end - - TrueTests = [[ true, %(true) ]] - FalseTests = [[ false, %(false) ]] - NilTests = [[ nil, %(null) ]] - NumericTests = [[ 1, %(1) ], - [ 2.5, %(2.5) ], - [ 0.0/0.0, %(null) ], - [ 1.0/0.0, %(null) ], - [ -1.0/0.0, %(null) ], - [ BigDecimal('0.0')/BigDecimal('0.0'), %(null) ], - [ BigDecimal('2.5'), %("#{BigDecimal('2.5')}") ]] - - StringTests = [[ 'this is the <string>', %("this is the \\u003cstring\\u003e")], - [ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ], - [ 'http://test.host/posts/1', %("http://test.host/posts/1")], - [ "Control characters: \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\u2028\u2029", - %("Control characters: \\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f\\u2028\\u2029") ]] - - ArrayTests = [[ ['a', 'b', 'c'], %([\"a\",\"b\",\"c\"]) ], - [ [1, 'a', :b, nil, false], %([1,\"a\",\"b\",null,false]) ]] - - RangeTests = [[ 1..2, %("1..2")], - [ 1...2, %("1...2")], - [ 1.5..2.5, %("1.5..2.5")]] - - SymbolTests = [[ :a, %("a") ], - [ :this, %("this") ], - [ :"a b", %("a b") ]] - - ObjectTests = [[ Foo.new(1, 2), %({\"a\":1,\"b\":2}) ]] - HashlikeTests = [[ Hashlike.new, %({\"bar\":\"world\",\"foo\":\"hello\"}) ]] - CustomTests = [[ Custom.new("custom"), '"custom"' ], - [ Custom.new(nil), 'null' ], - [ Custom.new(:a), '"a"' ], - [ Custom.new([ :foo, "bar" ]), '["foo","bar"]' ], - [ Custom.new({ :foo => "hello", :bar => "world" }), '{"bar":"world","foo":"hello"}' ], - [ Custom.new(Hashlike.new), '{"bar":"world","foo":"hello"}' ], - [ Custom.new(Custom.new(Custom.new(:a))), '"a"' ]] - - RegexpTests = [[ /^a/, '"(?-mix:^a)"' ], [/^\w{1,2}[a-z]+/ix, '"(?ix-m:^\\\\w{1,2}[a-z]+)"']] - - DateTests = [[ Date.new(2005,2,1), %("2005/02/01") ]] - TimeTests = [[ Time.utc(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]] - DateTimeTests = [[ DateTime.civil(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]] - - StandardDateTests = [[ Date.new(2005,2,1), %("2005-02-01") ]] - StandardTimeTests = [[ Time.utc(2005,2,1,15,15,10), %("2005-02-01T15:15:10.000Z") ]] - StandardDateTimeTests = [[ DateTime.civil(2005,2,1,15,15,10), %("2005-02-01T15:15:10.000+00:00") ]] - StandardStringTests = [[ 'this is the <string>', %("this is the <string>")]] - def sorted_json(json) return json unless json =~ /^\{.*\}$/ '{' + json[1..-2].split(',').sort.join(',') + '}' end - constants.grep(/Tests$/).each do |class_tests| + JSONTest::EncodingTestCases.constants.each do |class_tests| define_method("test_#{class_tests[0..-6].underscore}") do begin prev = ActiveSupport.use_standard_json_time_format ActiveSupport.escape_html_entities_in_json = class_tests !~ /^Standard/ ActiveSupport.use_standard_json_time_format = class_tests =~ /^Standard/ - self.class.const_get(class_tests).each do |pair| + JSONTest::EncodingTestCases.const_get(class_tests).each do |pair| assert_equal pair.last, sorted_json(ActiveSupport::JSON.encode(pair.first)) end ensure @@ -147,6 +49,13 @@ class TestJSONEncoding < ActiveSupport::TestCase assert_equal %({\"a\":\"b\",\"c\":\"d\"}), sorted_json(ActiveSupport::JSON.encode(:a => :b, :c => :d)) end + def test_hash_keys_encoding + ActiveSupport.escape_html_entities_in_json = true + assert_equal "{\"\\u003c\\u003e\":\"\\u003c\\u003e\"}", ActiveSupport::JSON.encode("<>" => "<>") + ensure + ActiveSupport.escape_html_entities_in_json = false + end + def test_utf8_string_encoded_properly result = ActiveSupport::JSON.encode('€2.99') assert_equal '"€2.99"', result @@ -217,7 +126,7 @@ class TestJSONEncoding < ActiveSupport::TestCase end def test_hash_like_with_options - h = Hashlike.new + h = JSONTest::Hashlike.new json = h.to_json :only => [:foo] assert_equal({"foo"=>"hello"}, JSON.parse(json)) @@ -338,6 +247,15 @@ class TestJSONEncoding < ActiveSupport::TestCase assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json) end + class CustomWithOptions + attr_accessor :foo, :bar + + def as_json(options={}) + options[:only] = %w(foo bar) + super(options) + end + end + def test_hash_to_json_should_not_keep_options_around f = CustomWithOptions.new f.foo = "hello" @@ -358,6 +276,12 @@ class TestJSONEncoding < ActiveSupport::TestCase {"foo"=>"other_foo","test"=>"other_test"}], ActiveSupport::JSON.decode(array.to_json)) end + class OptionsTest + def as_json(options = :default) + options + end + end + def test_hash_as_json_without_options json = { foo: OptionsTest.new }.as_json assert_equal({"foo" => :default}, json) @@ -405,6 +329,19 @@ class TestJSONEncoding < ActiveSupport::TestCase assert_equal false, false.as_json end + class HashWithAsJson < Hash + attr_accessor :as_json_called + + def initialize(*) + super + end + + def as_json(options={}) + @as_json_called = true + super + end + end + def test_json_gem_dump_by_passing_active_support_encoder h = HashWithAsJson.new h[:foo] = "hello" diff --git a/activesupport/test/json/encoding_test_cases.rb b/activesupport/test/json/encoding_test_cases.rb new file mode 100644 index 0000000000..0159ba8606 --- /dev/null +++ b/activesupport/test/json/encoding_test_cases.rb @@ -0,0 +1,88 @@ +require 'bigdecimal' + +module JSONTest + class Foo + def initialize(a, b) + @a, @b = a, b + end + end + + class Hashlike + def to_hash + { :foo => "hello", :bar => "world" } + end + end + + class Custom + def initialize(serialized) + @serialized = serialized + end + + def as_json(options = nil) + @serialized + end + end + + class MyStruct < Struct.new(:name, :value) + def initialize(*) + @unused = "unused instance variable" + super + end + end + + module EncodingTestCases + TrueTests = [[ true, %(true) ]] + FalseTests = [[ false, %(false) ]] + NilTests = [[ nil, %(null) ]] + NumericTests = [[ 1, %(1) ], + [ 2.5, %(2.5) ], + [ 0.0/0.0, %(null) ], + [ 1.0/0.0, %(null) ], + [ -1.0/0.0, %(null) ], + [ BigDecimal('0.0')/BigDecimal('0.0'), %(null) ], + [ BigDecimal('2.5'), %("#{BigDecimal('2.5')}") ]] + + StringTests = [[ 'this is the <string>', %("this is the \\u003cstring\\u003e")], + [ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ], + [ 'http://test.host/posts/1', %("http://test.host/posts/1")], + [ "Control characters: \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\u2028\u2029", + %("Control characters: \\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f\\u2028\\u2029") ]] + + ArrayTests = [[ ['a', 'b', 'c'], %([\"a\",\"b\",\"c\"]) ], + [ [1, 'a', :b, nil, false], %([1,\"a\",\"b\",null,false]) ]] + + HashTests = [[ {foo: "bar"}, %({\"foo\":\"bar\"}) ], + [ {1 => 1, 2 => 'a', 3 => :b, 4 => nil, 5 => false}, %({\"1\":1,\"2\":\"a\",\"3\":\"b\",\"4\":null,\"5\":false}) ]] + + RangeTests = [[ 1..2, %("1..2")], + [ 1...2, %("1...2")], + [ 1.5..2.5, %("1.5..2.5")]] + + SymbolTests = [[ :a, %("a") ], + [ :this, %("this") ], + [ :"a b", %("a b") ]] + + ObjectTests = [[ Foo.new(1, 2), %({\"a\":1,\"b\":2}) ]] + HashlikeTests = [[ Hashlike.new, %({\"bar\":\"world\",\"foo\":\"hello\"}) ]] + StructTests = [[ MyStruct.new(:foo, "bar"), %({\"name\":\"foo\",\"value\":\"bar\"}) ], + [ MyStruct.new(nil, nil), %({\"name\":null,\"value\":null}) ]] + CustomTests = [[ Custom.new("custom"), '"custom"' ], + [ Custom.new(nil), 'null' ], + [ Custom.new(:a), '"a"' ], + [ Custom.new([ :foo, "bar" ]), '["foo","bar"]' ], + [ Custom.new({ :foo => "hello", :bar => "world" }), '{"bar":"world","foo":"hello"}' ], + [ Custom.new(Hashlike.new), '{"bar":"world","foo":"hello"}' ], + [ Custom.new(Custom.new(Custom.new(:a))), '"a"' ]] + + RegexpTests = [[ /^a/, '"(?-mix:^a)"' ], [/^\w{1,2}[a-z]+/ix, '"(?ix-m:^\\\\w{1,2}[a-z]+)"']] + + DateTests = [[ Date.new(2005,2,1), %("2005/02/01") ]] + TimeTests = [[ Time.utc(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]] + DateTimeTests = [[ DateTime.civil(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]] + + StandardDateTests = [[ Date.new(2005,2,1), %("2005-02-01") ]] + StandardTimeTests = [[ Time.utc(2005,2,1,15,15,10), %("2005-02-01T15:15:10.000Z") ]] + StandardDateTimeTests = [[ DateTime.civil(2005,2,1,15,15,10), %("2005-02-01T15:15:10.000+00:00") ]] + StandardStringTests = [[ 'this is the <string>', %("this is the <string>")]] + end +end diff --git a/activesupport/test/log_subscriber_test.rb b/activesupport/test/log_subscriber_test.rb index 2a0e8d20ed..998a6887c5 100644 --- a/activesupport/test/log_subscriber_test.rb +++ b/activesupport/test/log_subscriber_test.rb @@ -82,10 +82,11 @@ class SyncLogSubscriberTest < ActiveSupport::TestCase def test_does_not_send_the_event_if_logger_is_nil ActiveSupport::LogSubscriber.logger = nil - @log_subscriber.expects(:some_event).never - ActiveSupport::LogSubscriber.attach_to :my_log_subscriber, @log_subscriber - instrument "some_event.my_log_subscriber" - wait + assert_not_called(@log_subscriber, :some_event) do + ActiveSupport::LogSubscriber.attach_to :my_log_subscriber, @log_subscriber + instrument "some_event.my_log_subscriber" + wait + end end def test_does_not_fail_with_non_namespaced_events diff --git a/activesupport/test/logger_test.rb b/activesupport/test/logger_test.rb index d2801849ca..317e09b7f2 100644 --- a/activesupport/test/logger_test.rb +++ b/activesupport/test/logger_test.rb @@ -3,6 +3,7 @@ require 'multibyte_test_helpers' require 'stringio' require 'fileutils' require 'tempfile' +require 'concurrent/atomics' class LoggerTest < ActiveSupport::TestCase include MultibyteTestHelpers @@ -16,6 +17,14 @@ class LoggerTest < ActiveSupport::TestCase @logger = Logger.new(@output) end + def test_log_outputs_to + assert Logger.logger_outputs_to?(@logger, @output), "Expected logger_outputs_to? @output to return true but was false" + assert Logger.logger_outputs_to?(@logger, @output, STDOUT), "Expected logger_outputs_to? @output or STDOUT to return true but was false" + + assert_not Logger.logger_outputs_to?(@logger, STDOUT), "Expected logger_outputs_to? to STDOUT to return false, but was true" + assert_not Logger.logger_outputs_to?(@logger, STDOUT, STDERR), "Expected logger_outputs_to? to STDOUT or STDERR to return false, but was true" + end + def test_write_binary_data_to_existing_file t = Tempfile.new ['development', 'log'] t.binmode @@ -64,7 +73,7 @@ class LoggerTest < ActiveSupport::TestCase def test_should_not_log_debug_messages_when_log_level_is_info @logger.level = Logger::INFO @logger.add(Logger::DEBUG, @message) - assert ! @output.string.include?(@message) + assert_not @output.string.include?(@message) end def test_should_add_message_passed_as_block_when_using_add @@ -113,6 +122,7 @@ class LoggerTest < ActiveSupport::TestCase end def test_buffer_multibyte + @logger.level = Logger::INFO @logger.info(UNICODE_STRING) @logger.info(BYTE_STRING) assert @output.string.include?(UNICODE_STRING) @@ -120,14 +130,93 @@ class LoggerTest < ActiveSupport::TestCase byte_string.force_encoding("ASCII-8BIT") assert byte_string.include?(BYTE_STRING) end - + def test_silencing_everything_but_errors @logger.silence do @logger.debug "NOT THERE" @logger.error "THIS IS HERE" end - - assert !@output.string.include?("NOT THERE") + + assert_not @output.string.include?("NOT THERE") assert @output.string.include?("THIS IS HERE") end + + def test_logger_level_per_object_thread_safety + logger1 = Logger.new(StringIO.new) + logger2 = Logger.new(StringIO.new) + + level = Logger::DEBUG + assert_equal level, logger1.level, "Expected level #{level_name(level)}, got #{level_name(logger1.level)}" + assert_equal level, logger2.level, "Expected level #{level_name(level)}, got #{level_name(logger2.level)}" + + logger1.level = Logger::ERROR + assert_equal level, logger2.level, "Expected level #{level_name(level)}, got #{level_name(logger2.level)}" + end + + def test_logger_level_main_thread_safety + @logger.level = Logger::INFO + assert_level(Logger::INFO) + + latch = Concurrent::CountDownLatch.new + latch2 = Concurrent::CountDownLatch.new + + t = Thread.new do + latch.wait + assert_level(Logger::INFO) + latch2.count_down + end + + @logger.silence(Logger::ERROR) do + assert_level(Logger::ERROR) + latch.count_down + latch2.wait + end + + t.join + end + + def test_logger_level_local_thread_safety + @logger.level = Logger::INFO + assert_level(Logger::INFO) + + thread_1_latch = Concurrent::CountDownLatch.new + thread_2_latch = Concurrent::CountDownLatch.new + + threads = (1..2).collect do |thread_number| + Thread.new do + # force thread 2 to wait until thread 1 is already in @logger.silence + thread_2_latch.wait if thread_number == 2 + + @logger.silence(Logger::ERROR) do + assert_level(Logger::ERROR) + @logger.silence(Logger::DEBUG) do + # allow thread 2 to finish but hold thread 1 + if thread_number == 1 + thread_2_latch.count_down + thread_1_latch.wait + end + assert_level(Logger::DEBUG) + end + end + + # allow thread 1 to finish + assert_level(Logger::INFO) + thread_1_latch.count_down if thread_number == 2 + end + end + + threads.each(&:join) + assert_level(Logger::INFO) + end + + private + def level_name(level) + ::Logger::Severity.constants.find do |severity| + Logger.const_get(severity) == level + end.to_s + end + + def assert_level(level) + assert_equal level, @logger.level, "Expected level #{level_name(level)}, got #{level_name(@logger.level)}" + end end diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb index 6c3519df9a..668d78492e 100644 --- a/activesupport/test/message_verifier_test.rb +++ b/activesupport/test/message_verifier_test.rb @@ -24,6 +24,7 @@ class MessageVerifierTest < ActiveSupport::TestCase data, hash = @verifier.generate(@data).split("--") assert !@verifier.valid_message?(nil) assert !@verifier.valid_message?("") + assert !@verifier.valid_message?("\xff") # invalid encoding assert !@verifier.valid_message?("#{data.reverse}--#{hash}") assert !@verifier.valid_message?("#{data}--#{hash.reverse}") assert !@verifier.valid_message?("purejunk") diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb index e1c4b705f8..c1e0b19248 100644 --- a/activesupport/test/multibyte_chars_test.rb +++ b/activesupport/test/multibyte_chars_test.rb @@ -413,12 +413,24 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase assert_equal 'にち', @chars.slice!(1..2) end + def test_slice_bang_returns_nil_on_out_of_bound_arguments + assert_equal nil, @chars.mb_chars.slice!(9..10) + end + def test_slice_bang_removes_the_slice_from_the_receiver chars = 'úüù'.mb_chars chars.slice!(0,2) assert_equal 'ù', chars end + def test_slice_bang_returns_nil_and_does_not_modify_receiver_if_out_of_bounds + string = 'úüù' + chars = string.mb_chars + assert_nil chars.slice!(4, 5) + assert_equal 'úüù', chars + assert_equal 'úüù', string + end + def test_slice_should_throw_exceptions_on_invalid_arguments assert_raise(TypeError) { @chars.slice(2..3, 1) } assert_raise(TypeError) { @chars.slice(1, 2..3) } @@ -600,28 +612,54 @@ class MultibyteCharsExtrasTest < ActiveSupport::TestCase ['abc', 3], ['こにちわ', 4], [[0x0924, 0x094D, 0x0930].pack('U*'), 2], + # GB3 [%w(cr lf), 1], + # GB4 + [%w(cr n), 2], + [%w(lf n), 2], + [%w(control n), 2], + [%w(cr extend), 2], + [%w(lf extend), 2], + [%w(control extend), 2], + # GB 5 + [%w(n cr), 2], + [%w(n lf), 2], + [%w(n control), 2], + [%w(extend cr), 2], + [%w(extend lf), 2], + [%w(extend control), 2], + # GB 6 [%w(l l), 1], [%w(l v), 1], [%w(l lv), 1], [%w(l lvt), 1], + # GB7 [%w(lv v), 1], [%w(lv t), 1], [%w(v v), 1], [%w(v t), 1], + # GB8 [%w(lvt t), 1], [%w(t t), 1], + # GB8a + [%w(r r), 1], + # GB9 [%w(n extend), 1], + # GB9a + [%w(n spacingmark), 1], + # GB10 [%w(n n), 2], + # Other [%w(n cr lf n), 3], - [%w(n l v t), 2] + [%w(n l v t), 2], + [%w(cr extend n), 3], ].each do |input, expected_length| if input.kind_of?(Array) str = string_from_classes(input) else str = input end - assert_equal expected_length, chars(str).grapheme_length + assert_equal expected_length, chars(str).grapheme_length, input.inspect end end @@ -686,7 +724,7 @@ class MultibyteCharsExtrasTest < ActiveSupport::TestCase # Characters from the character classes as described in UAX #29 character_from_class = { :l => 0x1100, :v => 0x1160, :t => 0x11A8, :lv => 0xAC00, :lvt => 0xAC01, :cr => 0x000D, :lf => 0x000A, - :extend => 0x094D, :n => 0x64 + :extend => 0x094D, :n => 0x64, :spacingmark => 0x0903, :r => 0x1F1E6, :control => 0x0001 } classes.collect do |k| character_from_class[k.intern] diff --git a/activesupport/test/multibyte_conformance_test.rb b/activesupport/test/multibyte_conformance_test.rb index d8704716e7..d493a48fe4 100644 --- a/activesupport/test/multibyte_conformance_test.rb +++ b/activesupport/test/multibyte_conformance_test.rb @@ -1,4 +1,3 @@ - require 'abstract_unit' require 'multibyte_test_helpers' @@ -105,11 +104,8 @@ class MultibyteConformanceTest < ActiveSupport::TestCase protected def each_line_of_norm_tests(&block) - lines = 0 - max_test_lines = 0 # Don't limit below 38, because that's the header of the testfile File.open(File.join(CACHE_DIR, UNIDATA_FILE), 'r') do | f | - until f.eof? || (max_test_lines > 38 and lines > max_test_lines) - lines += 1 + until f.eof? line = f.gets.chomp! next if (line.empty? || line =~ /^\#/) diff --git a/activesupport/test/multibyte_grapheme_break_conformance.rb b/activesupport/test/multibyte_grapheme_break_conformance.rb new file mode 100644 index 0000000000..7d185e2cae --- /dev/null +++ b/activesupport/test/multibyte_grapheme_break_conformance.rb @@ -0,0 +1,76 @@ +# encoding: utf-8 + +require 'abstract_unit' + +require 'fileutils' +require 'open-uri' +require 'tmpdir' + +class Downloader + def self.download(from, to) + unless File.exist?(to) + $stderr.puts "Downloading #{from} to #{to}" + unless File.exist?(File.dirname(to)) + system "mkdir -p #{File.dirname(to)}" + end + open(from) do |source| + File.open(to, 'w') do |target| + source.each_line do |l| + target.write l + end + end + end + end + end +end + +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') + + def setup + FileUtils.mkdir_p(CACHE_DIR) + Downloader.download(TEST_DATA_URL + TEST_DATA_FILE, CACHE_DIR + TEST_DATA_FILE) + end + + def test_breaks + each_line_of_break_tests do |*cols| + *clusters, comment = *cols + packed = ActiveSupport::Multibyte::Unicode.pack_graphemes(clusters) + assert_equal clusters, ActiveSupport::Multibyte::Unicode.unpack_graphemes(packed), comment + end + end + + protected + def each_line_of_break_tests(&block) + lines = 0 + max_test_lines = 0 # Don't limit below 21, because that's the header of the testfile + File.open(File.join(CACHE_DIR, TEST_DATA_FILE), 'r') do | f | + until f.eof? || (max_test_lines > 21 and lines > max_test_lines) + lines += 1 + line = f.gets.chomp! + next if (line.empty? || line =~ /^\#/) + + cols, comment = line.split("#") + # Cluster breaks are represented by ÷ + clusters = cols.split("÷").map{|e| e.strip}.reject{|e| e.empty? } + clusters = clusters.map do |cluster| + # Codepoints within each cluster are separated by × + codepoints = cluster.split("×").map{|e| e.strip}.reject{|e| e.empty? } + # codepoints are in hex in the test suite, pack wants them as integers + codepoints.map{|codepoint| codepoint.to_i(16)} + end + + # The tests contain a solitary U+D800 <Non Private Use High + # Surrogate, First> character, which Ruby does not allow to stand + # alone in a UTF-8 string. So we'll just skip it. + next if clusters.flatten.include?(0xd800) + + clusters << comment.strip + + yield(*clusters) + end + end + end +end diff --git a/activesupport/test/multibyte_normalization_conformance.rb b/activesupport/test/multibyte_normalization_conformance.rb new file mode 100644 index 0000000000..839aec7fa8 --- /dev/null +++ b/activesupport/test/multibyte_normalization_conformance.rb @@ -0,0 +1,129 @@ +# encoding: utf-8 + +require 'abstract_unit' +require 'multibyte_test_helpers' + +require 'fileutils' +require 'open-uri' +require 'tmpdir' + +class Downloader + def self.download(from, to) + unless File.exist?(to) + $stderr.puts "Downloading #{from} to #{to}" + unless File.exist?(File.dirname(to)) + system "mkdir -p #{File.dirname(to)}" + end + open(from) do |source| + File.open(to, 'w') do |target| + source.each_line do |l| + target.write l + end + end + end + end + end +end + +class MultibyteNormalizationConformanceTest < ActiveSupport::TestCase + include MultibyteTestHelpers + + UNIDATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}/ucd" + UNIDATA_FILE = '/NormalizationTest.txt' + CACHE_DIR = File.join(Dir.tmpdir, 'cache') + + def setup + FileUtils.mkdir_p(CACHE_DIR) + Downloader.download(UNIDATA_URL + UNIDATA_FILE, CACHE_DIR + UNIDATA_FILE) + @proxy = ActiveSupport::Multibyte::Chars + end + + def test_normalizations_C + each_line_of_norm_tests do |*cols| + col1, col2, col3, col4, col5, comment = *cols + + # CONFORMANCE: + # 1. The following invariants must be true for all conformant implementations + # + # NFC + # c2 == NFC(c1) == NFC(c2) == NFC(c3) + assert_equal_codepoints col2, @proxy.new(col1).normalize(:c), "Form C - Col 2 has to be NFC(1) - #{comment}" + assert_equal_codepoints col2, @proxy.new(col2).normalize(:c), "Form C - Col 2 has to be NFC(2) - #{comment}" + assert_equal_codepoints col2, @proxy.new(col3).normalize(:c), "Form C - Col 2 has to be NFC(3) - #{comment}" + # + # c4 == NFC(c4) == NFC(c5) + assert_equal_codepoints col4, @proxy.new(col4).normalize(:c), "Form C - Col 4 has to be C(4) - #{comment}" + assert_equal_codepoints col4, @proxy.new(col5).normalize(:c), "Form C - Col 4 has to be C(5) - #{comment}" + end + end + + def test_normalizations_D + each_line_of_norm_tests do |*cols| + col1, col2, col3, col4, col5, comment = *cols + # + # NFD + # c3 == NFD(c1) == NFD(c2) == NFD(c3) + assert_equal_codepoints col3, @proxy.new(col1).normalize(:d), "Form D - Col 3 has to be NFD(1) - #{comment}" + assert_equal_codepoints col3, @proxy.new(col2).normalize(:d), "Form D - Col 3 has to be NFD(2) - #{comment}" + assert_equal_codepoints col3, @proxy.new(col3).normalize(:d), "Form D - Col 3 has to be NFD(3) - #{comment}" + # c5 == NFD(c4) == NFD(c5) + assert_equal_codepoints col5, @proxy.new(col4).normalize(:d), "Form D - Col 5 has to be NFD(4) - #{comment}" + assert_equal_codepoints col5, @proxy.new(col5).normalize(:d), "Form D - Col 5 has to be NFD(5) - #{comment}" + end + end + + def test_normalizations_KC + each_line_of_norm_tests do | *cols | + col1, col2, col3, col4, col5, comment = *cols + # + # NFKC + # c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5) + assert_equal_codepoints col4, @proxy.new(col1).normalize(:kc), "Form D - Col 4 has to be NFKC(1) - #{comment}" + assert_equal_codepoints col4, @proxy.new(col2).normalize(:kc), "Form D - Col 4 has to be NFKC(2) - #{comment}" + assert_equal_codepoints col4, @proxy.new(col3).normalize(:kc), "Form D - Col 4 has to be NFKC(3) - #{comment}" + assert_equal_codepoints col4, @proxy.new(col4).normalize(:kc), "Form D - Col 4 has to be NFKC(4) - #{comment}" + assert_equal_codepoints col4, @proxy.new(col5).normalize(:kc), "Form D - Col 4 has to be NFKC(5) - #{comment}" + end + end + + def test_normalizations_KD + each_line_of_norm_tests do | *cols | + col1, col2, col3, col4, col5, comment = *cols + # + # NFKD + # c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5) + assert_equal_codepoints col5, @proxy.new(col1).normalize(:kd), "Form KD - Col 5 has to be NFKD(1) - #{comment}" + assert_equal_codepoints col5, @proxy.new(col2).normalize(:kd), "Form KD - Col 5 has to be NFKD(2) - #{comment}" + assert_equal_codepoints col5, @proxy.new(col3).normalize(:kd), "Form KD - Col 5 has to be NFKD(3) - #{comment}" + assert_equal_codepoints col5, @proxy.new(col4).normalize(:kd), "Form KD - Col 5 has to be NFKD(4) - #{comment}" + assert_equal_codepoints col5, @proxy.new(col5).normalize(:kd), "Form KD - Col 5 has to be NFKD(5) - #{comment}" + end + end + + protected + def each_line_of_norm_tests(&block) + lines = 0 + max_test_lines = 0 # Don't limit below 38, because that's the header of the testfile + File.open(File.join(CACHE_DIR, UNIDATA_FILE), 'r') do | f | + until f.eof? || (max_test_lines > 38 and lines > max_test_lines) + lines += 1 + line = f.gets.chomp! + next if (line.empty? || line =~ /^\#/) + + cols, comment = line.split("#") + cols = cols.split(";").map{|e| e.strip}.reject{|e| e.empty? } + next unless cols.length == 5 + + # codepoints are in hex in the test suite, pack wants them as integers + cols.map!{|c| c.split.map{|codepoint| codepoint.to_i(16)}.pack("U*") } + cols << comment + + yield(*cols) + end + end + end + + def inspect_codepoints(str) + str.to_s.unpack("U*").map{|cp| cp.to_s(16) }.join(' ') + end +end diff --git a/activesupport/test/multibyte_proxy_test.rb b/activesupport/test/multibyte_proxy_test.rb index 11f5374017..360cf57302 100644 --- a/activesupport/test/multibyte_proxy_test.rb +++ b/activesupport/test/multibyte_proxy_test.rb @@ -1,4 +1,3 @@ - require 'abstract_unit' class MultibyteProxyText < ActiveSupport::TestCase diff --git a/activesupport/test/multibyte_test_helpers.rb b/activesupport/test/multibyte_test_helpers.rb index 2e4b5cc873..58cf5488cd 100644 --- a/activesupport/test/multibyte_test_helpers.rb +++ b/activesupport/test/multibyte_test_helpers.rb @@ -1,4 +1,3 @@ - module MultibyteTestHelpers UNICODE_STRING = 'こにちわ'.freeze ASCII_STRING = 'ohayo'.freeze diff --git a/activesupport/test/multibyte_unicode_database_test.rb b/activesupport/test/multibyte_unicode_database_test.rb index 52ae266f01..dd33641ec2 100644 --- a/activesupport/test/multibyte_unicode_database_test.rb +++ b/activesupport/test/multibyte_unicode_database_test.rb @@ -11,8 +11,9 @@ class MultibyteUnicodeDatabaseTest < ActiveSupport::TestCase UnicodeDatabase::ATTRIBUTES.each do |attribute| define_method "test_lazy_loading_on_attribute_access_of_#{attribute}" do - @ucd.expects(:load) - @ucd.send(attribute) + assert_called(@ucd, :load) do + @ucd.send(attribute) + end end end diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb index f729f0a95b..1cb17e6197 100644 --- a/activesupport/test/notifications_test.rb +++ b/activesupport/test/notifications_test.rb @@ -42,6 +42,21 @@ module Notifications ActiveSupport::Notifications.instrument(name) assert_equal expected, events end + + def test_subsribing_to_instrumentation_while_inside_it + # the repro requires that there are no evented subscribers for the "foo" event, + # so we have to duplicate some of the setup code + old_notifier = ActiveSupport::Notifications.notifier + ActiveSupport::Notifications.notifier = ActiveSupport::Notifications::Fanout.new + + ActiveSupport::Notifications.subscribe('foo', TestSubscriber.new) + + ActiveSupport::Notifications.instrument('foo') do + ActiveSupport::Notifications.subscribe('foo') {} + end + ensure + ActiveSupport::Notifications.notifier = old_notifier + end end class UnsubscribeTest < TestCase @@ -217,7 +232,7 @@ module Notifications assert_equal 1, @events.size assert_equal Hash[:payload => "notifications", - :exception => ["RuntimeError", "FAIL"]], @events.last.payload + :exception => ["RuntimeError", "FAIL"], :exception_object => e], @events.last.payload end def test_event_is_pushed_even_without_block diff --git a/activesupport/test/number_helper_test.rb b/activesupport/test/number_helper_test.rb index 83efbffdfb..b3464462c8 100644 --- a/activesupport/test/number_helper_test.rb +++ b/activesupport/test/number_helper_test.rb @@ -34,6 +34,14 @@ module ActiveSupport gigabytes(number) * 1024 end + def petabytes(number) + terabytes(number) * 1024 + end + + def exabytes(number) + petabytes(number) * 1024 + end + def test_number_to_phone [@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper| assert_equal("555-1234", number_helper.number_to_phone(5551234)) @@ -106,6 +114,7 @@ module ActiveSupport assert_equal("123,456,789.78901", number_helper.number_to_delimited(123456789.78901)) assert_equal("0.78901", number_helper.number_to_delimited(0.78901)) assert_equal("123,456.78", number_helper.number_to_delimited("123456.78")) + assert_equal("1,23,456.78", number_helper.number_to_delimited("123456.78", delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/)) assert_equal("123,456.78", number_helper.number_to_delimited("123456.78".html_safe)) end end @@ -218,7 +227,9 @@ module ActiveSupport assert_equal '1.18 MB', number_helper.number_to_human_size(1234567) assert_equal '1.15 GB', number_helper.number_to_human_size(1234567890) assert_equal '1.12 TB', number_helper.number_to_human_size(1234567890123) - assert_equal '1030 TB', number_helper.number_to_human_size(terabytes(1026)) + assert_equal '1.1 PB', number_helper.number_to_human_size(1234567890123456) + assert_equal '1.07 EB', number_helper.number_to_human_size(1234567890123456789) + assert_equal '1030 EB', number_helper.number_to_human_size(exabytes(1026)) assert_equal '444 KB', number_helper.number_to_human_size(kilobytes(444)) assert_equal '1020 MB', number_helper.number_to_human_size(megabytes(1023)) assert_equal '3 TB', number_helper.number_to_human_size(terabytes(3)) @@ -234,15 +245,19 @@ module ActiveSupport end def test_number_to_human_size_with_si_prefix - [@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper| - assert_equal '3 Bytes', number_helper.number_to_human_size(3.14159265, :prefix => :si) - assert_equal '123 Bytes', number_helper.number_to_human_size(123.0, :prefix => :si) - assert_equal '123 Bytes', number_helper.number_to_human_size(123, :prefix => :si) - assert_equal '1.23 KB', number_helper.number_to_human_size(1234, :prefix => :si) - assert_equal '12.3 KB', number_helper.number_to_human_size(12345, :prefix => :si) - assert_equal '1.23 MB', number_helper.number_to_human_size(1234567, :prefix => :si) - assert_equal '1.23 GB', number_helper.number_to_human_size(1234567890, :prefix => :si) - assert_equal '1.23 TB', number_helper.number_to_human_size(1234567890123, :prefix => :si) + assert_deprecated do + [@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper| + assert_equal '3 Bytes', number_helper.number_to_human_size(3.14159265, :prefix => :si) + assert_equal '123 Bytes', number_helper.number_to_human_size(123.0, :prefix => :si) + assert_equal '123 Bytes', number_helper.number_to_human_size(123, :prefix => :si) + assert_equal '1.23 KB', number_helper.number_to_human_size(1234, :prefix => :si) + assert_equal '12.3 KB', number_helper.number_to_human_size(12345, :prefix => :si) + assert_equal '1.23 MB', number_helper.number_to_human_size(1234567, :prefix => :si) + assert_equal '1.23 GB', number_helper.number_to_human_size(1234567890, :prefix => :si) + assert_equal '1.23 TB', number_helper.number_to_human_size(1234567890123, :prefix => :si) + assert_equal '1.23 PB', number_helper.number_to_human_size(1234567890123456, :prefix => :si) + assert_equal '1.23 EB', number_helper.number_to_human_size(1234567890123456789, :prefix => :si) + end end end @@ -293,6 +308,8 @@ module ActiveSupport assert_equal '1.2346 Million', number_helper.number_to_human(1234567, :precision => 4, :significant => false) assert_equal '1,2 Million', number_helper.number_to_human(1234567, :precision => 1, :significant => false, :separator => ',') assert_equal '1 Million', number_helper.number_to_human(1234567, :precision => 0, :significant => true, :separator => ',') #significant forced to false + assert_equal '1 Million', number_helper.number_to_human(999999) + assert_equal '1 Billion', number_helper.number_to_human(999999999) end end diff --git a/activesupport/test/ordered_options_test.rb b/activesupport/test/ordered_options_test.rb index fdc745b23b..18767a3536 100644 --- a/activesupport/test/ordered_options_test.rb +++ b/activesupport/test/ordered_options_test.rb @@ -85,4 +85,19 @@ class OrderedOptionsTest < ActiveSupport::TestCase assert_equal 42, a.method(:blah=).call(42) assert_equal 42, a.method(:blah).call end + + def test_raises_with_bang + a = ActiveSupport::OrderedOptions.new + a[:foo] = :bar + assert a.respond_to?(:foo!) + + assert_nothing_raised { a.foo! } + assert_equal a.foo, a.foo! + + assert_raises(KeyError) do + a.foo = nil + a.foo! + end + assert_raises(KeyError) { a.non_existing_key! } + end end diff --git a/activesupport/test/share_lock_test.rb b/activesupport/test/share_lock_test.rb new file mode 100644 index 0000000000..465a657308 --- /dev/null +++ b/activesupport/test/share_lock_test.rb @@ -0,0 +1,333 @@ +require 'abstract_unit' +require 'concurrent/atomic/count_down_latch' +require 'active_support/concurrency/share_lock' + +class ShareLockTest < ActiveSupport::TestCase + def setup + @lock = ActiveSupport::Concurrency::ShareLock.new + end + + def test_reentrancy + thread = Thread.new do + @lock.sharing { @lock.sharing {} } + @lock.exclusive { @lock.exclusive {} } + end + assert_threads_not_stuck thread + end + + def test_sharing_doesnt_block + with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_latch| + assert_threads_not_stuck(Thread.new {@lock.sharing {} }) + end + end + + def test_sharing_blocks_exclusive + with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch| + @lock.exclusive(no_wait: true) { flunk } # polling should fail + exclusive_thread = Thread.new { @lock.exclusive {} } + assert_threads_stuck_but_releasable_by_latch exclusive_thread, sharing_thread_release_latch + end + end + + def test_exclusive_blocks_sharing + with_thread_waiting_in_lock_section(:exclusive) do |exclusive_thread_release_latch| + sharing_thread = Thread.new { @lock.sharing {} } + assert_threads_stuck_but_releasable_by_latch sharing_thread, exclusive_thread_release_latch + end + end + + def test_multiple_exlusives_are_able_to_progress + with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch| + exclusive_threads = (1..2).map do + Thread.new do + @lock.exclusive {} + end + end + + assert_threads_stuck_but_releasable_by_latch exclusive_threads, sharing_thread_release_latch + end + end + + def test_sharing_is_upgradeable_to_exclusive + upgrading_thread = Thread.new do + @lock.sharing do + @lock.exclusive {} + end + end + assert_threads_not_stuck upgrading_thread + end + + def test_exclusive_upgrade_waits_for_other_sharers_to_leave + with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch| + in_sharing = Concurrent::CountDownLatch.new + + upgrading_thread = Thread.new do + @lock.sharing do + in_sharing.count_down + @lock.exclusive {} + end + end + + in_sharing.wait + assert_threads_stuck_but_releasable_by_latch upgrading_thread, sharing_thread_release_latch + end + end + + def test_exclusive_matching_purpose + [true, false].each do |use_upgrading| + with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch| + exclusive_threads = (1..2).map do + Thread.new do + @lock.send(use_upgrading ? :sharing : :tap) do + @lock.exclusive(purpose: :load, compatible: [:load, :unload]) {} + end + end + end + + assert_threads_stuck_but_releasable_by_latch exclusive_threads, sharing_thread_release_latch + end + end + end + + def test_killed_thread_loses_lock + with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch| + thread = Thread.new do + @lock.sharing do + @lock.exclusive {} + end + end + + assert_threads_stuck thread + thread.kill + + sharing_thread_release_latch.count_down + + thread = Thread.new do + @lock.exclusive {} + end + + assert_threads_not_stuck thread + end + end + + def test_exclusive_conflicting_purpose + [true, false].each do |use_upgrading| + with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch| + begin + conflicting_exclusive_threads = [ + Thread.new do + @lock.send(use_upgrading ? :sharing : :tap) do + @lock.exclusive(purpose: :red, compatible: [:green, :purple]) {} + end + end, + Thread.new do + @lock.send(use_upgrading ? :sharing : :tap) do + @lock.exclusive(purpose: :blue, compatible: [:green]) {} + end + end + ] + + assert_threads_stuck conflicting_exclusive_threads # wait for threads to get into their respective `exclusive {}` blocks + + # This thread will be stuck as long as any other thread is in + # a sharing block. While it's blocked, it holds no lock, so it + # doesn't interfere with any other attempts. + no_purpose_thread = Thread.new do + @lock.exclusive {} + end + assert_threads_stuck no_purpose_thread + + # This thread is compatible with both of the "primary" + # attempts above. It's initially stuck on the outer share + # lock, but as soon as that's released, it can run -- + # regardless of whether those threads hold share locks. + compatible_thread = Thread.new do + @lock.exclusive(purpose: :green, compatible: []) {} + end + assert_threads_stuck compatible_thread + + assert_threads_stuck conflicting_exclusive_threads + + sharing_thread_release_latch.count_down + + assert_threads_not_stuck compatible_thread # compatible thread is now able to squeak through + + if use_upgrading + # The "primary" threads both each hold a share lock, and are + # mutually incompatible; they're still stuck. + assert_threads_stuck conflicting_exclusive_threads + + # The thread without a specified purpose is also stuck; it's + # not compatible with anything. + assert_threads_stuck no_purpose_thread + else + # As the primaries didn't hold a share lock, as soon as the + # outer one was released, all the exclusive locks are free + # to be acquired in turn. + + assert_threads_not_stuck conflicting_exclusive_threads + assert_threads_not_stuck no_purpose_thread + end + ensure + conflicting_exclusive_threads.each(&:kill) + no_purpose_thread.kill + end + end + end + end + + def test_exclusive_ordering + scratch_pad = [] + scratch_pad_mutex = Mutex.new + + load_params = [:load, [:load]] + unload_params = [:unload, [:unload, :load]] + + [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 + @lock.exclusive(purpose: purpose, compatible: compatible) do + scratch_pad_mutex.synchronize { scratch_pad << purpose } + end + end + end + end + + sleep(0.01) + scratch_pad_mutex.synchronize { assert_empty scratch_pad } + + sharing_thread_release_latch.count_down + + assert_threads_not_stuck threads + scratch_pad_mutex.synchronize do + assert_equal [:load, :load, :unload, :unload], scratch_pad + scratch_pad.clear + end + end + end + end + + def test_in_shared_section_incompatible_non_upgrading_threads_cannot_preempt_upgrading_threads + scratch_pad = [] + scratch_pad_mutex = Mutex.new + + upgrading_load_params = [:load, [:load], true] + non_upgrading_unload_params = [:unload, [:load, :unload], false] + + [upgrading_load_params, non_upgrading_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, use_upgrading| + Thread.new do + @lock.send(use_upgrading ? :sharing : :tap) do + @lock.exclusive(purpose: purpose, compatible: compatible) do + scratch_pad_mutex.synchronize { scratch_pad << purpose } + end + end + end + end + + assert_threads_stuck threads + scratch_pad_mutex.synchronize { assert_empty scratch_pad } + + sharing_thread_release_latch.count_down + + assert_threads_not_stuck threads + scratch_pad_mutex.synchronize do + assert_equal [:load, :unload], scratch_pad + scratch_pad.clear + end + end + end + end + + private + + module CustomAssertions + SUFFICIENT_TIMEOUT = 0.2 + + private + + def assert_threads_stuck_but_releasable_by_latch(threads, latch) + assert_threads_stuck threads + latch.count_down + assert_threads_not_stuck threads + end + + def assert_threads_stuck(threads) + sleep(SUFFICIENT_TIMEOUT) # give threads time to do their business + assert(Array(threads).all? { |t| t.join(0.001).nil? }) + end + + def assert_threads_not_stuck(threads) + assert(Array(threads).all? { |t| t.join(SUFFICIENT_TIMEOUT) }) + end + end + + class CustomAssertionsTest < ActiveSupport::TestCase + include CustomAssertions + + def setup + @latch = Concurrent::CountDownLatch.new + @thread = Thread.new { @latch.wait } + end + + def teardown + @latch.count_down + @thread.join + end + + def test_happy_path + assert_threads_stuck_but_releasable_by_latch @thread, @latch + end + + def test_detects_stuck_thread + assert_raises(Minitest::Assertion) do + assert_threads_not_stuck @thread + end + end + + def test_detects_free_thread + @latch.count_down + assert_raises(Minitest::Assertion) do + assert_threads_stuck @thread + end + end + + def test_detects_already_released + @latch.count_down + assert_raises(Minitest::Assertion) do + assert_threads_stuck_but_releasable_by_latch @thread, @latch + end + end + + def test_detects_remains_latched + another_latch = Concurrent::CountDownLatch.new + assert_raises(Minitest::Assertion) do + assert_threads_stuck_but_releasable_by_latch @thread, another_latch + end + end + end + + include CustomAssertions + + def with_thread_waiting_in_lock_section(lock_section) + in_section = Concurrent::CountDownLatch.new + section_release = Concurrent::CountDownLatch.new + + stuck_thread = Thread.new do + @lock.send(lock_section) do + in_section.count_down + section_release.wait + end + end + + in_section.wait + + yield section_release + ensure + section_release.count_down + stuck_thread.join # clean up + end +end diff --git a/activesupport/test/tagged_logging_test.rb b/activesupport/test/tagged_logging_test.rb index 03a63e94e8..917fa46c96 100644 --- a/activesupport/test/tagged_logging_test.rb +++ b/activesupport/test/tagged_logging_test.rb @@ -72,11 +72,11 @@ class TaggedLoggingTest < ActiveSupport::TestCase test "keeps each tag in their own thread" do @logger.tagged("BCX") do Thread.new do - @logger.tagged("OMG") { @logger.info "Cool story bro" } + @logger.tagged("OMG") { @logger.info "Cool story" } end.join @logger.info "Funky time" end - assert_equal "[OMG] Cool story bro\n[BCX] Funky time\n", @output.string + assert_equal "[OMG] Cool story\n[BCX] Funky time\n", @output.string end test "keeps each tag in their own instance" do @@ -84,11 +84,11 @@ class TaggedLoggingTest < ActiveSupport::TestCase @other_logger = ActiveSupport::TaggedLogging.new(MyLogger.new(@other_output)) @logger.tagged("OMG") do @other_logger.tagged("BCX") do - @logger.info "Cool story bro" + @logger.info "Cool story" @other_logger.info "Funky time" end end - assert_equal "[OMG] Cool story bro\n", @output.string + assert_equal "[OMG] Cool story\n", @output.string assert_equal "[BCX] Funky time\n", @other_output.string end @@ -97,11 +97,11 @@ class TaggedLoggingTest < ActiveSupport::TestCase Thread.new do @logger.tagged("OMG") do @logger.flush - @logger.info "Cool story bro" + @logger.info "Cool story" end end.join end - assert_equal "[FLUSHED]\nCool story bro\n", @output.string + assert_equal "[FLUSHED]\nCool story\n", @output.string end test "mixed levels of tagging" do diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb index 9e6d1a91d0..18228a2ac5 100644 --- a/activesupport/test/test_case_test.rb +++ b/activesupport/test/test_case_test.rb @@ -56,6 +56,14 @@ class AssertDifferenceTest < ActiveSupport::TestCase end end + def test_assert_difference_retval + incremented = assert_difference '@object.num', +1 do + @object.increment + end + + assert_equal incremented, 1 + end + def test_assert_difference_with_implicit_difference assert_difference '@object.num' do @object.increment diff --git a/activesupport/test/testing/method_call_assertions_test.rb b/activesupport/test/testing/method_call_assertions_test.rb new file mode 100644 index 0000000000..3e5ba7c079 --- /dev/null +++ b/activesupport/test/testing/method_call_assertions_test.rb @@ -0,0 +1,123 @@ +require 'abstract_unit' +require 'active_support/testing/method_call_assertions' + +class MethodCallAssertionsTest < ActiveSupport::TestCase + include ActiveSupport::Testing::MethodCallAssertions + + class Level + def increment; 1; end + def decrement; end + def <<(arg); end + end + + setup do + @object = Level.new + end + + def test_assert_called_with_defaults_to_expect_once + assert_called @object, :increment do + @object.increment + end + end + + def test_assert_called_more_than_once + assert_called(@object, :increment, times: 2) do + @object.increment + @object.increment + end + end + + def test_assert_called_method_with_arguments + assert_called(@object, :<<) do + @object << 2 + end + end + + def test_assert_called_returns + assert_called(@object, :increment, returns: 10) do + assert_equal 10, @object.increment + end + end + + def test_assert_called_failure + error = assert_raises(Minitest::Assertion) do + assert_called(@object, :increment) do + # Call nothing... + end + end + + assert_equal "Expected increment to be called 1 times, but was called 0 times.\nExpected: 1\n Actual: 0", error.message + end + + def test_assert_called_with_message + error = assert_raises(Minitest::Assertion) do + assert_called(@object, :increment, 'dang it') do + # Call nothing... + end + end + + assert_match(/dang it.\nExpected increment/, error.message) + end + + def test_assert_called_with + assert_called_with(@object, :increment) do + @object.increment + end + end + + def test_assert_called_with_arguments + assert_called_with(@object, :<<, [ 2 ]) do + @object << 2 + end + end + + def test_assert_called_with_failure + assert_raises(MockExpectationError) do + assert_called_with(@object, :<<, [ 4567 ]) do + @object << 2 + end + end + end + + def test_assert_called_with_returns + assert_called_with(@object, :increment, returns: 1) do + @object.increment + end + end + + def test_assert_called_with_multiple_expected_arguments + assert_called_with(@object, :<<, [ [ 1 ], [ 2 ] ]) do + @object << 1 + @object << 2 + end + end + + def test_assert_not_called + assert_not_called(@object, :decrement) do + @object.increment + end + end + + def test_assert_not_called_failure + error = assert_raises(Minitest::Assertion) do + assert_not_called(@object, :increment) do + @object.increment + end + end + + assert_equal "Expected increment to be called 0 times, but was called 1 times.\nExpected: 0\n Actual: 1", error.message + end + + def test_stub_any_instance + stub_any_instance(Level) do |instance| + assert_equal instance, Level.new + end + end + + def test_stub_any_instance_with_instance + stub_any_instance(Level, instance: @object) do |instance| + assert_equal @object, instance + assert_equal instance, Level.new + end + end +end diff --git a/activesupport/test/time_travel_test.rb b/activesupport/test/time_travel_test.rb index 869bc09991..59c3e52c2f 100644 --- a/activesupport/test/time_travel_test.rb +++ b/activesupport/test/time_travel_test.rb @@ -3,80 +3,88 @@ require 'active_support/core_ext/date_time' require 'active_support/core_ext/numeric/time' class TimeTravelTest < ActiveSupport::TestCase - setup do - Time.stubs now: Time.now - end - teardown do travel_back end def test_time_helper_travel - expected_time = Time.now + 1.day - travel 1.day - - assert_equal expected_time.to_s(:db), Time.now.to_s(:db) - assert_equal expected_time.to_date, Date.today - assert_equal expected_time.to_datetime.to_s(:db), DateTime.now.to_s(:db) - end - - def test_time_helper_travel_with_block - expected_time = Time.now + 1.day + Time.stub(:now, Time.now) do + expected_time = Time.now + 1.day + travel 1.day - travel 1.day do assert_equal expected_time.to_s(:db), Time.now.to_s(:db) assert_equal expected_time.to_date, Date.today assert_equal expected_time.to_datetime.to_s(:db), DateTime.now.to_s(:db) end - - assert_not_equal expected_time.to_s(:db), Time.now.to_s(:db) - assert_not_equal expected_time.to_date, Date.today - assert_not_equal expected_time.to_datetime.to_s(:db), DateTime.now.to_s(:db) end - def test_time_helper_travel_to - expected_time = Time.new(2004, 11, 24, 01, 04, 44) - travel_to expected_time - - assert_equal expected_time, Time.now - assert_equal Date.new(2004, 11, 24), Date.today - assert_equal expected_time.to_datetime, DateTime.now + def test_time_helper_travel_with_block + Time.stub(:now, Time.now) do + expected_time = Time.now + 1.day + + travel 1.day do + assert_equal expected_time.to_s(:db), Time.now.to_s(:db) + assert_equal expected_time.to_date, Date.today + assert_equal expected_time.to_datetime.to_s(:db), DateTime.now.to_s(:db) + end + + assert_not_equal expected_time.to_s(:db), Time.now.to_s(:db) + assert_not_equal expected_time.to_date, Date.today + assert_not_equal expected_time.to_datetime.to_s(:db), DateTime.now.to_s(:db) + end end - def test_time_helper_travel_to_with_block - expected_time = Time.new(2004, 11, 24, 01, 04, 44) + def test_time_helper_travel_to + Time.stub(:now, Time.now) do + expected_time = Time.new(2004, 11, 24, 01, 04, 44) + travel_to expected_time - travel_to expected_time do assert_equal expected_time, Time.now assert_equal Date.new(2004, 11, 24), Date.today assert_equal expected_time.to_datetime, DateTime.now end + end - assert_not_equal expected_time, Time.now - assert_not_equal Date.new(2004, 11, 24), Date.today - assert_not_equal expected_time.to_datetime, DateTime.now + def test_time_helper_travel_to_with_block + Time.stub(:now, Time.now) do + expected_time = Time.new(2004, 11, 24, 01, 04, 44) + + travel_to expected_time do + assert_equal expected_time, Time.now + assert_equal Date.new(2004, 11, 24), Date.today + assert_equal expected_time.to_datetime, DateTime.now + end + + assert_not_equal expected_time, Time.now + assert_not_equal Date.new(2004, 11, 24), Date.today + assert_not_equal expected_time.to_datetime, DateTime.now + end end def test_time_helper_travel_back - expected_time = Time.new(2004, 11, 24, 01, 04, 44) + Time.stub(:now, Time.now) do + expected_time = Time.new(2004, 11, 24, 01, 04, 44) - travel_to expected_time - assert_equal expected_time, Time.now - assert_equal Date.new(2004, 11, 24), Date.today - assert_equal expected_time.to_datetime, DateTime.now - travel_back + travel_to expected_time + assert_equal expected_time, Time.now + assert_equal Date.new(2004, 11, 24), Date.today + assert_equal expected_time.to_datetime, DateTime.now + travel_back - assert_not_equal expected_time, Time.now - assert_not_equal Date.new(2004, 11, 24), Date.today - assert_not_equal expected_time.to_datetime, DateTime.now + assert_not_equal expected_time, Time.now + assert_not_equal Date.new(2004, 11, 24), Date.today + assert_not_equal expected_time.to_datetime, DateTime.now + end end def test_travel_to_will_reset_the_usec_to_avoid_mysql_rouding - travel_to Time.utc(2014, 10, 10, 10, 10, 50, 999999) do - assert_equal 50, Time.now.sec - assert_equal 0, Time.now.usec - assert_equal 50, DateTime.now.sec - assert_equal 0, DateTime.now.usec + Time.stub(:now, Time.now) do + travel_to Time.utc(2014, 10, 10, 10, 10, 50, 999999) do + assert_equal 50, Time.now.sec + assert_equal 0, Time.now.usec + assert_equal 50, DateTime.now.sec + assert_equal 0, DateTime.now.usec + end end end end diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index 5e0474f449..00d40c4497 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -1,6 +1,7 @@ require 'abstract_unit' require 'active_support/time' require 'time_zone_test_helpers' +require 'yaml' class TimeZoneTest < ActiveSupport::TestCase include TimeZoneTestHelpers @@ -252,9 +253,10 @@ class TimeZoneTest < ActiveSupport::TestCase def test_parse_with_incomplete_date zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] - zone.stubs(:now).returns zone.local(1999,12,31) - twz = zone.parse('19:00:00') - assert_equal Time.utc(1999,12,31,19), twz.time + zone.stub(:now, zone.local(1999,12,31)) do + twz = zone.parse('19:00:00') + assert_equal Time.utc(1999,12,31,19), twz.time + end end def test_parse_with_day_omitted @@ -284,9 +286,10 @@ class TimeZoneTest < ActiveSupport::TestCase def test_parse_with_missing_time_components zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] - zone.stubs(:now).returns zone.local(1999, 12, 31, 12, 59, 59) - twz = zone.parse('2012-12-01') - assert_equal Time.utc(2012, 12, 1), twz.time + zone.stub(:now, zone.local(1999, 12, 31, 12, 59, 59)) do + twz = zone.parse('2012-12-01') + assert_equal Time.utc(2012, 12, 1), twz.time + end end def test_parse_with_javascript_date diff --git a/activesupport/test/xml_mini_test.rb b/activesupport/test/xml_mini_test.rb index 0e4e7427d2..55e8181b54 100644 --- a/activesupport/test/xml_mini_test.rb +++ b/activesupport/test/xml_mini_test.rb @@ -3,6 +3,7 @@ require 'active_support/xml_mini' require 'active_support/builder' require 'active_support/core_ext/hash' require 'active_support/core_ext/big_decimal' +require 'yaml' module XmlMiniTest class RenameKeyTest < ActiveSupport::TestCase |