diff options
Diffstat (limited to 'activesupport/test')
114 files changed, 4521 insertions, 2328 deletions
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb index 90e50f235b..f65ec962f9 100644 --- a/activesupport/test/abstract_unit.rb +++ b/activesupport/test/abstract_unit.rb @@ -8,7 +8,6 @@ ensure end require 'active_support/core_ext/kernel/reporting' -require 'active_support/core_ext/string/encoding' silence_warnings do Encoding.default_internal = "UTF-8" @@ -16,10 +15,31 @@ silence_warnings do end require 'active_support/testing/autorun' -require 'empty_bool' ENV['NO_RELOAD'] = '1' require 'active_support' +Thread.abort_on_exception = true + # Show backtraces for deprecated behavior for quicker cleanup. ActiveSupport::Deprecation.debug = true + +# Disable available locale checks to avoid warnings running the test suite. +I18n.enforce_available_locales = false + +# Skips the current run on Rubinius using Minitest::Assertions#skip +def rubinius_skip(message = '') + skip message if RUBY_ENGINE == 'rbx' +end + +# Skips the current run on JRuby using Minitest::Assertions#skip +def jruby_skip(message = '') + skip message if defined?(JRUBY_VERSION) +end + +require 'mocha/setup' # FIXME: stop using mocha + +# FIXME: we have tests that depend on run order, we should fix that and +# remove this method call. +require 'active_support/test_case' +ActiveSupport::TestCase.test_order = :sorted diff --git a/activesupport/test/autoload_test.rb b/activesupport/test/autoload_test.rb index 7d02d835a8..c18b007612 100644 --- a/activesupport/test/autoload_test.rb +++ b/activesupport/test/autoload_test.rb @@ -11,6 +11,11 @@ class TestAutoloadModule < ActiveSupport::TestCase end end + def setup + @some_class_path = File.expand_path("test/fixtures/autoload/some_class.rb") + @another_class_path = File.expand_path("test/fixtures/autoload/another_class.rb") + end + test "the autoload module works like normal autoload" do module ::Fixtures::Autoload autoload :SomeClass, "fixtures/autoload/some_class" @@ -21,10 +26,12 @@ class TestAutoloadModule < ActiveSupport::TestCase test "when specifying an :eager constant it still works like normal autoload by default" do module ::Fixtures::Autoload - autoload :SomeClass, "fixtures/autoload/some_class" + eager_autoload do + autoload :SomeClass, "fixtures/autoload/some_class" + end end - assert !$LOADED_FEATURES.include?("fixtures/autoload/some_class.rb") + assert !$LOADED_FEATURES.include?(@some_class_path) assert_nothing_raised { ::Fixtures::Autoload::SomeClass } end @@ -33,16 +40,20 @@ class TestAutoloadModule < ActiveSupport::TestCase autoload :SomeClass end - assert !$LOADED_FEATURES.include?("fixtures/autoload/some_class.rb") + assert !$LOADED_FEATURES.include?(@some_class_path) assert_nothing_raised { ::Fixtures::Autoload::SomeClass } end test "the location of :eager autoloaded constants defaults to :name.underscore" do module ::Fixtures::Autoload - autoload :SomeClass + eager_autoload do + autoload :SomeClass + end end + assert !$LOADED_FEATURES.include?(@some_class_path) ::Fixtures::Autoload.eager_load! + assert $LOADED_FEATURES.include?(@some_class_path) assert_nothing_raised { ::Fixtures::Autoload::SomeClass } end @@ -53,7 +64,7 @@ class TestAutoloadModule < ActiveSupport::TestCase end end - assert !$LOADED_FEATURES.include?("fixtures/autoload/another_class.rb") + assert !$LOADED_FEATURES.include?(@another_class_path) assert_nothing_raised { ::Fixtures::AnotherClass } end @@ -64,7 +75,7 @@ class TestAutoloadModule < ActiveSupport::TestCase end end - assert !$LOADED_FEATURES.include?("fixtures/autoload/another_class.rb") + assert !$LOADED_FEATURES.include?(@another_class_path) assert_nothing_raised { ::Fixtures::AnotherClass } end end
\ No newline at end of file diff --git a/activesupport/test/autoloading_fixtures/html/some_class.rb b/activesupport/test/autoloading_fixtures/html/some_class.rb new file mode 100644 index 0000000000..b43d15d891 --- /dev/null +++ b/activesupport/test/autoloading_fixtures/html/some_class.rb @@ -0,0 +1,4 @@ +module HTML + class SomeClass + end +end diff --git a/activesupport/test/autoloading_fixtures/typo.rb b/activesupport/test/autoloading_fixtures/typo.rb new file mode 100644 index 0000000000..8e047f5fd4 --- /dev/null +++ b/activesupport/test/autoloading_fixtures/typo.rb @@ -0,0 +1,2 @@ +TypO = 1 + diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 5158bbc196..98f3ea7a14 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -1,7 +1,43 @@ require 'logger' require 'abstract_unit' require 'active_support/cache' -require 'dependecies_test_helpers' +require 'dependencies_test_helpers' + +module ActiveSupport + module Cache + module Strategy + module LocalCache + class MiddlewareTest < ActiveSupport::TestCase + def test_local_cache_cleared_on_close + key = "super awesome key" + assert_nil LocalCacheRegistry.cache_for key + middleware = Middleware.new('<3', key).new(->(env) { + assert LocalCacheRegistry.cache_for(key), 'should have a cache' + [200, {}, []] + }) + _, _, body = middleware.call({}) + assert LocalCacheRegistry.cache_for(key), 'should still have a cache' + body.each { } + assert LocalCacheRegistry.cache_for(key), 'should still have a cache' + body.close + assert_nil LocalCacheRegistry.cache_for(key) + end + + def test_local_cache_cleared_on_exception + key = "super awesome key" + assert_nil LocalCacheRegistry.cache_for key + middleware = Middleware.new('<3', key).new(->(env) { + assert LocalCacheRegistry.cache_for(key), 'should have a cache' + raise + }) + assert_raises(RuntimeError) { middleware.call({}) } + assert_nil LocalCacheRegistry.cache_for(key) + end + end + end + end + end +end class CacheKeyTest < ActiveSupport::TestCase def test_entry_legacy_optional_ivars @@ -24,36 +60,25 @@ class CacheKeyTest < ActiveSupport::TestCase end def test_expand_cache_key_with_rails_cache_id - begin - ENV['RAILS_CACHE_ID'] = 'c99' + with_env('RAILS_CACHE_ID' => 'c99') do assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key(:foo) assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key([:foo]) assert_equal 'c99/foo/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar]) assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key(:foo, :nm) assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key([:foo], :nm) assert_equal 'nm/c99/foo/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar], :nm) - ensure - ENV['RAILS_CACHE_ID'] = nil end end def test_expand_cache_key_with_rails_app_version - begin - ENV['RAILS_APP_VERSION'] = 'rails3' + with_env('RAILS_APP_VERSION' => 'rails3') do assert_equal 'rails3/foo', ActiveSupport::Cache.expand_cache_key(:foo) - ensure - ENV['RAILS_APP_VERSION'] = nil end end def test_expand_cache_key_rails_cache_id_should_win_over_rails_app_version - begin - ENV['RAILS_CACHE_ID'] = 'c99' - ENV['RAILS_APP_VERSION'] = 'rails3' + with_env('RAILS_CACHE_ID' => 'c99', 'RAILS_APP_VERSION' => 'rails3') do assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key(:foo) - ensure - ENV['RAILS_CACHE_ID'] = nil - ENV['RAILS_APP_VERSION'] = nil end end @@ -88,6 +113,16 @@ class CacheKeyTest < ActiveSupport::TestCase def test_expand_cache_key_of_array_like_object assert_equal 'foo/bar/baz', ActiveSupport::Cache.expand_cache_key(%w{foo bar baz}.to_enum) end + + private + + def with_env(kv) + old_values = {} + kv.each { |key, value| old_values[key], ENV[key] = ENV[key], value } + yield + ensure + old_values.each { |key, value| ENV[key] = value} + end end class CacheStoreSettingTest < ActiveSupport::TestCase @@ -110,12 +145,12 @@ class CacheStoreSettingTest < ActiveSupport::TestCase assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) end - def test_mem_cache_fragment_cache_store_with_given_mem_cache_like_object + def test_mem_cache_fragment_cache_store_with_not_dalli_client Dalli::Client.expects(:new).never memcache = Object.new - def memcache.get() true end - store = ActiveSupport::Cache.lookup_store :mem_cache_store, memcache - assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) + assert_raises(ArgumentError) do + ActiveSupport::Cache.lookup_store :mem_cache_store, memcache + end end def test_mem_cache_fragment_cache_store_with_multiple_servers @@ -257,6 +292,27 @@ module CacheStoreBehavior assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu')) end + def test_fetch_multi + @cache.write('foo', 'bar') + @cache.write('fud', 'biz') + + values = @cache.fetch_multi('foo', 'fu', 'fud') { |value| value * 2 } + + assert_equal({ 'foo' => 'bar', 'fu' => 'fufu', 'fud' => 'biz' }, values) + assert_equal('fufu', @cache.read('fu')) + end + + def test_multi_with_objects + foo = stub(:title => 'FOO!', :cache_key => 'foo') + bar = stub(:cache_key => 'bar') + + @cache.write('bar', 'BAM!') + + values = @cache.fetch_multi(foo, bar) { |object| object.title } + + assert_equal({ foo => 'FOO!', bar => 'BAM!' }, values) + end + def test_read_and_write_compressed_small_data @cache.write('foo', 'bar', :compress => true) assert_equal 'bar', @cache.read('foo') @@ -307,8 +363,8 @@ module CacheStoreBehavior def test_exist @cache.write('foo', 'bar') - assert @cache.exist?('foo') - assert !@cache.exist?('bar') + assert_equal true, @cache.exist?('foo') + assert_equal false, @cache.exist?('bar') end def test_nil_exist @@ -405,7 +461,7 @@ module CacheStoreBehavior end # https://rails.lighthouseapp.com/projects/8994/tickets/6225-memcachestore-cant-deal-with-umlauts-and-special-characters -# The error is caused by charcter encodings that can't be compared with ASCII-8BIT regular expressions and by special +# The error is caused by character encodings that can't be compared with ASCII-8BIT regular expressions and by special # characters like the umlaut in UTF-8. module EncodedKeyCacheBehavior Encoding.list.each do |encoding| @@ -557,6 +613,7 @@ module LocalCacheBehavior result = @cache.write('foo', 'bar') assert_equal 'bar', @cache.read('foo') # make sure 'foo' was written assert result + [200, {}, []] } app = @cache.middleware.new(app) app.call({}) @@ -564,7 +621,7 @@ module LocalCacheBehavior end module AutoloadingCacheBehavior - include DependeciesTestHelpers + include DependenciesTestHelpers def test_simple_autoloading with_autoloading_fixtures do @cache.write('foo', E.new) @@ -634,6 +691,11 @@ class FileStoreTest < ActiveSupport::TestCase assert File.exist?(filepath) end + def test_long_keys + @cache.write("a"*10000, 1) + assert_equal 1, @cache.read("a"*10000) + end + def test_key_transformation key = @cache.send(:key_file_path, "views/index?id=1") assert_equal "views/index?id=1", @cache.send(:file_path_key, key) @@ -672,17 +734,48 @@ class FileStoreTest < ActiveSupport::TestCase end end + def test_delete_does_not_delete_empty_parent_dir + sub_cache_dir = File.join(cache_dir, 'subdir/') + sub_cache_store = ActiveSupport::Cache::FileStore.new(sub_cache_dir) + assert_nothing_raised(Exception) do + assert sub_cache_store.write('foo', 'bar') + assert sub_cache_store.delete('foo') + end + assert File.exist?(cache_dir), "Parent of top level cache dir was deleted!" + assert File.exist?(sub_cache_dir), "Top level cache dir was deleted!" + assert Dir.entries(sub_cache_dir).reject {|f| ActiveSupport::Cache::FileStore::EXCLUDED_DIRS.include?(f)}.empty? + end + def test_log_exception_when_cache_read_fails File.expects(:exist?).raises(StandardError, "failed") @cache.send(:read_entry, "winston", {}) assert @buffer.string.present? end + + def test_cleanup_removes_all_expired_entries + time = Time.now + @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') + end + + def test_write_with_unless_exist + assert_equal true, @cache.write(1, "aaaaaaaaaa") + assert_equal false, @cache.write(1, "aaaaaaaaaa", unless_exist: true) + @cache.write(1, nil) + assert_equal false, @cache.write(1, "aaaaaaaaaa", unless_exist: true) + end end class MemoryStoreTest < ActiveSupport::TestCase def setup - @record_size = ActiveSupport::Cache::Entry.new("aaaaaaaaaa").size - @cache = ActiveSupport::Cache.lookup_store(:memory_store, :expires_in => 60, :size => @record_size * 10) + @record_size = ActiveSupport::Cache.lookup_store(:memory_store).send(:cached_size, 1, ActiveSupport::Cache::Entry.new("aaaaaaaaaa")) + @cache = ActiveSupport::Cache.lookup_store(:memory_store, :expires_in => 60, :size => @record_size * 10 + 1) end include CacheStoreBehavior @@ -732,6 +825,30 @@ class MemoryStoreTest < ActiveSupport::TestCase assert !@cache.exist?(1), "no entry" end + def test_prune_size_on_write_based_on_key_length + @cache.write(1, "aaaaaaaaaa") && sleep(0.001) + @cache.write(2, "bbbbbbbbbb") && sleep(0.001) + @cache.write(3, "cccccccccc") && sleep(0.001) + @cache.write(4, "dddddddddd") && sleep(0.001) + @cache.write(5, "eeeeeeeeee") && sleep(0.001) + @cache.write(6, "ffffffffff") && sleep(0.001) + @cache.write(7, "gggggggggg") && sleep(0.001) + @cache.write(8, "hhhhhhhhhh") && sleep(0.001) + @cache.write(9, "iiiiiiiiii") && sleep(0.001) + long_key = '*' * 2 * @record_size + @cache.write(long_key, "llllllllll") + assert @cache.exist?(long_key) + assert @cache.exist?(9) + assert @cache.exist?(8) + assert @cache.exist?(7) + assert @cache.exist?(6) + assert !@cache.exist?(5), "no entry" + assert !@cache.exist?(4), "no entry" + assert !@cache.exist?(3), "no entry" + assert !@cache.exist?(2), "no entry" + assert !@cache.exist?(1), "no entry" + end + def test_pruning_is_capped_at_a_max_time def @cache.delete_entry (*args) sleep(0.01) @@ -822,8 +939,8 @@ class MemCacheStoreTest < ActiveSupport::TestCase def test_read_should_return_a_different_object_id_each_time_it_is_called @cache.write('foo', 'bar') - assert_not_equal @cache.read('foo').object_id, @cache.read('foo').object_id value = @cache.read('foo') + assert_not_equal value.object_id, @cache.read('foo').object_id value << 'bingo' assert_not_equal value, @cache.read('foo') end @@ -930,31 +1047,4 @@ class CacheEntryTest < ActiveSupport::TestCase assert_equal value, entry.value assert_equal value.bytesize, entry.size end - - def test_restoring_version_3_entries - version_3_entry = ActiveSupport::Cache::Entry.allocate - version_3_entry.instance_variable_set(:@value, "hello") - version_3_entry.instance_variable_set(:@created_at, Time.now - 60) - entry = Marshal.load(Marshal.dump(version_3_entry)) - assert_equal "hello", entry.value - assert_equal false, entry.expired? - end - - def test_restoring_compressed_version_3_entries - version_3_entry = ActiveSupport::Cache::Entry.allocate - version_3_entry.instance_variable_set(:@value, Zlib::Deflate.deflate(Marshal.dump("hello"))) - version_3_entry.instance_variable_set(:@compressed, true) - entry = Marshal.load(Marshal.dump(version_3_entry)) - assert_equal "hello", entry.value - end - - def test_restoring_expired_version_3_entries - version_3_entry = ActiveSupport::Cache::Entry.allocate - version_3_entry.instance_variable_set(:@value, "hello") - version_3_entry.instance_variable_set(:@created_at, Time.now - 60) - version_3_entry.instance_variable_set(:@expires_in, 58.9) - entry = Marshal.load(Marshal.dump(version_3_entry)) - assert_equal "hello", entry.value - assert_equal true, entry.expired? - end end diff --git a/activesupport/test/callback_inheritance_test.rb b/activesupport/test/callback_inheritance_test.rb index 6be8ea8b84..1adfe4edf4 100644 --- a/activesupport/test/callback_inheritance_test.rb +++ b/activesupport/test/callback_inheritance_test.rb @@ -109,7 +109,6 @@ class BasicCallbacksTest < ActiveSupport::TestCase @index = GrandParent.new("index").dispatch @update = GrandParent.new("update").dispatch @delete = GrandParent.new("delete").dispatch - @unknown = GrandParent.new("unknown").dispatch end def test_basic_conditional_callback1 @@ -130,7 +129,6 @@ class InheritedCallbacksTest < ActiveSupport::TestCase @index = Parent.new("index").dispatch @update = Parent.new("update").dispatch @delete = Parent.new("delete").dispatch - @unknown = Parent.new("unknown").dispatch end def test_inherited_excluded diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb index 13f2e3cdaf..d19e5fd6e7 100644 --- a/activesupport/test/callbacks_test.rb +++ b/activesupport/test/callbacks_test.rb @@ -1,27 +1,6 @@ require 'abstract_unit' module CallbacksTest - class Phone - include ActiveSupport::Callbacks - define_callbacks :save - - set_callback :save, :before, :before_save1 - set_callback :save, :after, :after_save1 - - def before_save1; self.history << :before; end - def after_save1; self.history << :after; end - - def save - run_callbacks :save do - raise 'boom' - end - end - - def history - @history ||= [] - end - end - class Record include ActiveSupport::Callbacks @@ -66,6 +45,16 @@ module CallbacksTest end end + class CallbackClass + def self.before(model) + model.history << [:before_save, :class] + end + + def self.after(model) + model.history << [:after_save, :class] + end + end + class Person < Record [:before_save, :after_save].each do |callback_method| callback_method_sym = callback_method.to_sym @@ -73,6 +62,7 @@ module CallbacksTest 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) send(callback_method) { |model| model.history << [callback_method_sym, :block] } end @@ -86,10 +76,14 @@ module CallbacksTest skip_callback :save, :after, :before_save_method, :unless => :yes skip_callback :save, :after, :before_save_method, :if => :no skip_callback :save, :before, :before_save_method, :unless => :no + skip_callback :save, :before, CallbackClass , :if => :yes def yes; true; end def no; false; end end + class PersonForProgrammaticSkipping < Person + end + class ParentController include ActiveSupport::Callbacks @@ -430,6 +424,26 @@ module CallbacksTest [:before_save, :object], [:before_save, :block], [:after_save, :block], + [:after_save, :class], + [:after_save, :object], + [:after_save, :proc], + [:after_save, :string], + [:after_save, :symbol] + ], person.history + end + + def test_skip_person_programmatically + PersonForProgrammaticSkipping._save_callbacks.each do |save_callback| + if "before" == save_callback.kind.to_s + PersonForProgrammaticSkipping.skip_callback("save", save_callback.kind, save_callback.filter) + end + end + person = PersonForProgrammaticSkipping.new + assert_equal [], person.history + person.save + assert_equal [ + [:after_save, :block], + [:after_save, :class], [:after_save, :object], [:after_save, :proc], [:after_save, :string], @@ -449,8 +463,10 @@ module CallbacksTest [:before_save, :string], [:before_save, :proc], [:before_save, :object], + [:before_save, :class], [:before_save, :block], [:after_save, :block], + [:after_save, :class], [:after_save, :object], [:after_save, :proc], [:after_save, :string], @@ -485,21 +501,20 @@ module CallbacksTest end end - class CallbackTerminator + class AbstractCallbackTerminator include ActiveSupport::Callbacks - define_callbacks :save, :terminator => "result == :halt" - - set_callback :save, :before, :first - set_callback :save, :before, :second - set_callback :save, :around, :around_it - set_callback :save, :before, :third - set_callback :save, :after, :first - set_callback :save, :around, :around_it - set_callback :save, :after, :second - set_callback :save, :around, :around_it - set_callback :save, :after, :third - + def self.set_save_callbacks + set_callback :save, :before, :first + set_callback :save, :before, :second + set_callback :save, :around, :around_it + set_callback :save, :before, :third + set_callback :save, :after, :first + set_callback :save, :around, :around_it + set_callback :save, :after, :second + set_callback :save, :around, :around_it + set_callback :save, :after, :third + end attr_reader :history, :saved, :halted def initialize @@ -536,6 +551,17 @@ module CallbacksTest end end + class CallbackTerminator < AbstractCallbackTerminator + define_callbacks :save, terminator: ->(_,result) { result == :halt } + set_save_callbacks + end + + class CallbackTerminatorSkippingAfterCallbacks < AbstractCallbackTerminator + define_callbacks :save, terminator: ->(_,result) { result == :halt }, + skip_after_callbacks_if_terminated: true + set_save_callbacks + end + class CallbackObject def before(caller) caller.record << "before" @@ -672,7 +698,7 @@ module CallbacksTest end class CallbackTerminatorTest < ActiveSupport::TestCase - def test_termination + def test_termination_skips_following_before_and_around_callbacks terminator = CallbackTerminator.new terminator.save assert_equal ["first", "second", "third", "second", "first"], terminator.history @@ -681,7 +707,7 @@ module CallbacksTest def test_termination_invokes_hook terminator = CallbackTerminator.new terminator.save - assert_equal ":second", terminator.halted + assert_equal :second, terminator.halted end def test_block_never_called_if_terminated @@ -691,6 +717,14 @@ module CallbacksTest end end + class CallbackTerminatorSkippingAfterCallbacksTest < ActiveSupport::TestCase + def test_termination_skips_after_callbacks + terminator = CallbackTerminatorSkippingAfterCallbacks.new + terminator.save + assert_equal ["first", "second"], terminator.history + end + end + class HyphenatedKeyTest < ActiveSupport::TestCase def test_save obj = HyphenatedCallbacks.new @@ -715,8 +749,10 @@ module CallbacksTest [:before_save, :string], [:before_save, :proc], [:before_save, :object], + [:before_save, :class], [:before_save, :block], [:after_save, :block], + [:after_save, :class], [:after_save, :object], [:after_save, :proc], [:after_save, :string], @@ -733,22 +769,6 @@ module CallbacksTest end end - class PerKeyOptionDeprecationTest < ActiveSupport::TestCase - - def test_per_key_option_deprecaton - assert_raise NotImplementedError do - Phone.class_eval do - set_callback :save, :before, :before_save1, :per_key => {:if => "true"} - end - end - assert_raise NotImplementedError do - Phone.class_eval do - skip_callback :save, :before, :before_save1, :per_key => {:if => "true"} - end - end - end - end - class ExcludingDuplicatesCallbackTest < ActiveSupport::TestCase def test_excludes_duplicates_in_separate_calls model = DuplicatingCallbacks.new @@ -762,4 +782,240 @@ module CallbacksTest assert_equal ["two", "one", "three", "yielded"], model.record end end + + class CallbackProcTest < ActiveSupport::TestCase + def build_class(callback) + Class.new { + include ActiveSupport::Callbacks + define_callbacks :foo + set_callback :foo, :before, callback + def run; run_callbacks :foo; end + } + end + + def test_proc_arity_0 + calls = [] + klass = build_class(->() { calls << :foo }) + klass.new.run + assert_equal [:foo], calls + end + + def test_proc_arity_1 + calls = [] + klass = build_class(->(o) { calls << o }) + instance = klass.new + instance.run + assert_equal [instance], calls + end + + def test_proc_arity_2 + assert_raises(ArgumentError) do + klass = build_class(->(x,y) { }) + klass.new.run + end + end + + def test_proc_negative_called_with_empty_list + calls = [] + klass = build_class(->(*args) { calls << args }) + klass.new.run + assert_equal [[]], calls + end + end + + class ConditionalTests < ActiveSupport::TestCase + def build_class(callback) + Class.new { + include ActiveSupport::Callbacks + define_callbacks :foo + set_callback :foo, :before, :foo, :if => callback + def foo; end + def run; run_callbacks :foo; end + } + end + + # FIXME: do we really want to support classes as conditionals? There were + # no tests for it previous to this. + def test_class_conditional_with_scope + z = [] + callback = Class.new { + define_singleton_method(:foo) { |o| z << o } + } + klass = Class.new { + include ActiveSupport::Callbacks + define_callbacks :foo, :scope => [:name] + set_callback :foo, :before, :foo, :if => callback + def run; run_callbacks :foo; end + private + def foo; end + } + object = klass.new + object.run + assert_equal [object], z + end + + # FIXME: do we really want to support classes as conditionals? There were + # no tests for it previous to this. + def test_class + z = [] + klass = build_class Class.new { + define_singleton_method(:before) { |o| z << o } + } + object = klass.new + object.run + assert_equal [object], z + end + + def test_proc_negative_arity # passes an empty list if *args + z = [] + object = build_class(->(*args) { z << args }).new + object.run + assert_equal [], z.flatten + end + + def test_proc_arity0 + z = [] + object = build_class(->() { z << 0 }).new + object.run + assert_equal [0], z + end + + def test_proc_arity1 + z = [] + object = build_class(->(x) { z << x }).new + object.run + assert_equal [object], z + end + + def test_proc_arity2 + assert_raises(ArgumentError) do + object = build_class(->(a,b) { }).new + object.run + end + end + end + + class ResetCallbackTest < ActiveSupport::TestCase + def build_class(memo) + klass = Class.new { + include ActiveSupport::Callbacks + define_callbacks :foo + set_callback :foo, :before, :hello + def run; run_callbacks :foo; end + } + klass.class_eval { + define_method(:hello) { memo << :hi } + } + klass + end + + def test_reset_callbacks + events = [] + klass = build_class events + klass.new.run + assert_equal 1, events.length + + klass.reset_callbacks :foo + klass.new.run + assert_equal 1, events.length + end + + def test_reset_impacts_subclasses + events = [] + klass = build_class events + subclass = Class.new(klass) { set_callback :foo, :before, :world } + subclass.class_eval { define_method(:world) { events << :world } } + + subclass.new.run + assert_equal 2, events.length + + klass.reset_callbacks :foo + subclass.new.run + assert_equal 3, events.length + end + end + + class CallbackTypeTest < ActiveSupport::TestCase + def build_class(callback, n = 10) + Class.new { + include ActiveSupport::Callbacks + define_callbacks :foo + n.times { set_callback :foo, :before, callback } + def run; run_callbacks :foo; end + def self.skip(thing); skip_callback :foo, :before, thing; end + } + end + + def test_add_class + calls = [] + callback = Class.new { + define_singleton_method(:before) { |o| calls << o } + } + build_class(callback).new.run + assert_equal 10, calls.length + end + + def test_add_lambda + calls = [] + build_class(->(o) { calls << o }).new.run + assert_equal 10, calls.length + end + + def test_add_symbol + calls = [] + klass = build_class(:bar) + klass.class_eval { define_method(:bar) { calls << klass } } + klass.new.run + assert_equal 1, calls.length + end + + def test_add_eval + calls = [] + klass = build_class("bar") + klass.class_eval { define_method(:bar) { calls << klass } } + klass.new.run + assert_equal 1, calls.length + end + + def test_skip_class # removes one at a time + calls = [] + callback = Class.new { + define_singleton_method(:before) { |o| calls << o } + } + klass = build_class(callback) + 9.downto(0) { |i| + klass.skip callback + klass.new.run + assert_equal i, calls.length + calls.clear + } + end + + def test_skip_lambda # removes nothing + calls = [] + callback = ->(o) { calls << o } + klass = build_class(callback) + 10.times { klass.skip callback } + klass.new.run + assert_equal 10, calls.length + end + + def test_skip_symbol # removes all + calls = [] + klass = build_class(:bar) + klass.class_eval { define_method(:bar) { calls << klass } } + klass.skip :bar + klass.new.run + assert_equal 0, calls.length + end + + def test_skip_eval # removes nothing + calls = [] + klass = build_class("bar") + klass.class_eval { define_method(:bar) { calls << klass } } + klass.skip "bar" + klass.new.run + assert_equal 1, calls.length + end + end end diff --git a/activesupport/test/clean_backtrace_test.rb b/activesupport/test/clean_backtrace_test.rb index b14950acb3..05580352a9 100644 --- a/activesupport/test/clean_backtrace_test.rb +++ b/activesupport/test/clean_backtrace_test.rb @@ -34,6 +34,32 @@ class BacktraceCleanerSilencerTest < ActiveSupport::TestCase [ "/other/class.rb" ], @bc.clean([ "/mongrel/class.rb", "/other/class.rb", "/mongrel/stuff.rb" ]) end + + test "backtrace cleaner should allow removing silencer" do + @bc.remove_silencers! + assert_equal ["/mongrel/stuff.rb"], @bc.clean(["/mongrel/stuff.rb"]) + end +end + +class BacktraceCleanerMultipleSilencersTest < ActiveSupport::TestCase + def setup + @bc = ActiveSupport::BacktraceCleaner.new + @bc.add_silencer { |line| line =~ /mongrel/ } + @bc.add_silencer { |line| line =~ /yolo/ } + end + + test "backtrace should not contain lines that match the silencers" do + assert_equal \ + [ "/other/class.rb" ], + @bc.clean([ "/mongrel/class.rb", "/other/class.rb", "/mongrel/stuff.rb", "/other/yolo.rb" ]) + end + + test "backtrace should only contain lines that match the silencers" do + assert_equal \ + [ "/mongrel/class.rb", "/mongrel/stuff.rb", "/other/yolo.rb" ], + @bc.clean([ "/mongrel/class.rb", "/other/class.rb", "/mongrel/stuff.rb", "/other/yolo.rb" ], + :noise) + end end class BacktraceCleanerFilterAndSilencerTest < ActiveSupport::TestCase diff --git a/activesupport/test/concern_test.rb b/activesupport/test/concern_test.rb index 912ce30c29..60bd8a06aa 100644 --- a/activesupport/test/concern_test.rb +++ b/activesupport/test/concern_test.rb @@ -5,7 +5,7 @@ class ConcernTest < ActiveSupport::TestCase module Baz extend ActiveSupport::Concern - module ClassMethods + class_methods do def baz "baz" end @@ -33,6 +33,12 @@ class ConcernTest < ActiveSupport::TestCase include Baz + module ClassMethods + def baz + "bar's baz + " + super + end + end + def bar "bar" end @@ -56,10 +62,6 @@ class ConcernTest < ActiveSupport::TestCase @klass.send(:include, Baz) assert_equal "baz", @klass.new.baz assert @klass.included_modules.include?(ConcernTest::Baz) - - @klass.send(:include, Baz) - assert_equal "baz", @klass.new.baz - assert @klass.included_modules.include?(ConcernTest::Baz) end def test_class_methods_are_extended @@ -68,12 +70,6 @@ class ConcernTest < ActiveSupport::TestCase assert_equal ConcernTest::Baz::ClassMethods, (class << @klass; self.included_modules; end)[0] end - def test_instance_methods_are_included - @klass.send(:include, Baz) - assert_equal "baz", @klass.new.baz - assert @klass.included_modules.include?(ConcernTest::Baz) - end - def test_included_block_is_ran @klass.send(:include, Baz) assert_equal true, @klass.included_ran @@ -83,7 +79,7 @@ class ConcernTest < ActiveSupport::TestCase @klass.send(:include, Bar) assert_equal "bar", @klass.new.bar assert_equal "bar+baz", @klass.new.baz - assert_equal "baz", @klass.baz + assert_equal "bar's baz + baz", @klass.baz assert @klass.included_modules.include?(ConcernTest::Bar) end @@ -91,4 +87,18 @@ class ConcernTest < ActiveSupport::TestCase @klass.send(:include, Foo) assert_equal [ConcernTest::Foo, ConcernTest::Bar, ConcernTest::Baz], @klass.included_modules[0..2] end + + def test_raise_on_multiple_included_calls + assert_raises(ActiveSupport::Concern::MultipleIncludedBlocks) do + Module.new do + extend ActiveSupport::Concern + + included do + end + + included do + end + end + end + end end diff --git a/activesupport/test/configurable_test.rb b/activesupport/test/configurable_test.rb index d00273a028..ef847fc557 100644 --- a/activesupport/test/configurable_test.rb +++ b/activesupport/test/configurable_test.rb @@ -95,6 +95,20 @@ class ConfigurableActiveSupport < ActiveSupport::TestCase config_accessor "invalid attribute name" end end + + assert_raises NameError do + Class.new do + include ActiveSupport::Configurable + config_accessor "invalid\nattribute" + end + end + + assert_raises NameError do + Class.new do + include ActiveSupport::Configurable + config_accessor "invalid\n" + end + end end def assert_method_defined(object, method) diff --git a/activesupport/test/constantize_test_cases.rb b/activesupport/test/constantize_test_cases.rb index 9b62295c96..366e4e5ef0 100644 --- a/activesupport/test/constantize_test_cases.rb +++ b/activesupport/test/constantize_test_cases.rb @@ -1,3 +1,5 @@ +require 'dependencies_test_helpers' + module Ace module Base class Case @@ -23,19 +25,30 @@ class Object end module ConstantizeTestCases + include DependenciesTestHelpers + def run_constantize_tests_on assert_equal Ace::Base::Case, yield("Ace::Base::Case") assert_equal Ace::Base::Case, yield("::Ace::Base::Case") assert_equal Ace::Base::Case::Dice, yield("Ace::Base::Case::Dice") + assert_equal Ace::Base::Case::Dice, yield("Ace::Base::Fase::Dice") assert_equal Ace::Base::Fase::Dice, yield("Ace::Base::Fase::Dice") + assert_equal Ace::Gas::Case, yield("Ace::Gas::Case") assert_equal Ace::Gas::Case::Dice, yield("Ace::Gas::Case::Dice") + assert_equal Ace::Base::Case::Dice, yield("Ace::Gas::Case::Dice") + assert_equal Case::Dice, yield("Case::Dice") + assert_equal AddtlGlobalConstants::Case::Dice, yield("Case::Dice") + assert_equal Object::AddtlGlobalConstants::Case::Dice, yield("Case::Dice") + assert_equal Case::Dice, yield("Object::Case::Dice") + assert_equal AddtlGlobalConstants::Case::Dice, yield("Object::Case::Dice") + assert_equal Object::AddtlGlobalConstants::Case::Dice, yield("Case::Dice") + assert_equal ConstantizeTestCases, yield("ConstantizeTestCases") assert_equal ConstantizeTestCases, yield("::ConstantizeTestCases") - assert_equal Object, yield("") - assert_equal Object, yield("::") + assert_raises(NameError) { yield("UnknownClass") } assert_raises(NameError) { yield("UnknownClass::Ace") } assert_raises(NameError) { yield("UnknownClass::Ace::Base") } @@ -45,6 +58,21 @@ module ConstantizeTestCases assert_raises(NameError) { yield("Ace::Base::ConstantizeTestCases") } assert_raises(NameError) { yield("Ace::Gas::Base") } assert_raises(NameError) { yield("Ace::Gas::ConstantizeTestCases") } + assert_raises(NameError) { yield("") } + assert_raises(NameError) { yield("::") } + assert_raises(NameError) { yield("Ace::gas") } + + assert_raises(NameError) do + with_autoloading_fixtures do + yield("RaisesNameError") + end + end + + assert_raises(NoMethodError) do + with_autoloading_fixtures do + yield("RaisesNoMethodError") + end + end end def run_safe_constantize_tests_on @@ -58,8 +86,8 @@ module ConstantizeTestCases assert_equal Case::Dice, yield("Object::Case::Dice") assert_equal ConstantizeTestCases, yield("ConstantizeTestCases") assert_equal ConstantizeTestCases, yield("::ConstantizeTestCases") - assert_equal Object, yield("") - assert_equal Object, yield("::") + assert_nil yield("") + assert_nil yield("::") assert_nil yield("UnknownClass") assert_nil yield("UnknownClass::Ace") assert_nil yield("UnknownClass::Ace::Base") @@ -71,5 +99,18 @@ module ConstantizeTestCases assert_nil yield("Ace::Gas::Base") assert_nil yield("Ace::Gas::ConstantizeTestCases") assert_nil yield("#<Class:0x7b8b718b>::Nested_1") + assert_nil yield("Ace::gas") + + assert_raises(NameError) do + with_autoloading_fixtures do + yield("RaisesNameError") + end + end + + assert_raises(NoMethodError) do + with_autoloading_fixtures do + yield("RaisesNoMethodError") + end + end end end diff --git a/activesupport/test/core_ext/array/access_test.rb b/activesupport/test/core_ext/array/access_test.rb new file mode 100644 index 0000000000..f14f64421d --- /dev/null +++ b/activesupport/test/core_ext/array/access_test.rb @@ -0,0 +1,30 @@ +require 'abstract_unit' +require 'active_support/core_ext/array' + +class AccessTest < ActiveSupport::TestCase + def test_from + assert_equal %w( a b c d ), %w( a b c d ).from(0) + assert_equal %w( c d ), %w( a b c d ).from(2) + assert_equal %w(), %w( a b c d ).from(10) + assert_equal %w( d e ), %w( a b c d e ).from(-2) + assert_equal %w(), %w( a b c d e ).from(-10) + end + + def test_to + assert_equal %w( a ), %w( a b c d ).to(0) + assert_equal %w( a b c ), %w( a b c d ).to(2) + assert_equal %w( a b c d ), %w( a b c d ).to(10) + assert_equal %w( a b c ), %w( a b c d ).to(-2) + assert_equal %w(), %w( a b c ).to(-10) + end + + def test_specific_accessor + array = (1..42).to_a + + assert_equal array[1], array.second + assert_equal array[2], array.third + assert_equal array[3], array.fourth + assert_equal array[4], array.fifth + assert_equal array[41], array.forty_two + end +end diff --git a/activesupport/test/core_ext/array/conversions_test.rb b/activesupport/test/core_ext/array/conversions_test.rb new file mode 100644 index 0000000000..577b889410 --- /dev/null +++ b/activesupport/test/core_ext/array/conversions_test.rb @@ -0,0 +1,197 @@ +require 'abstract_unit' +require 'active_support/core_ext/array' +require 'active_support/core_ext/big_decimal' +require 'active_support/core_ext/hash' +require 'active_support/core_ext/string' + +class ToSentenceTest < ActiveSupport::TestCase + def test_plain_array_to_sentence + assert_equal "", [].to_sentence + assert_equal "one", ['one'].to_sentence + assert_equal "one and two", ['one', 'two'].to_sentence + assert_equal "one, two, and three", ['one', 'two', 'three'].to_sentence + end + + def test_to_sentence_with_words_connector + assert_equal "one two, and three", ['one', 'two', 'three'].to_sentence(words_connector: ' ') + assert_equal "one & two, and three", ['one', 'two', 'three'].to_sentence(words_connector: ' & ') + assert_equal "onetwo, and three", ['one', 'two', 'three'].to_sentence(words_connector: nil) + end + + def test_to_sentence_with_last_word_connector + assert_equal "one, two, and also three", ['one', 'two', 'three'].to_sentence(last_word_connector: ', and also ') + assert_equal "one, twothree", ['one', 'two', 'three'].to_sentence(last_word_connector: nil) + assert_equal "one, two three", ['one', 'two', 'three'].to_sentence(last_word_connector: ' ') + assert_equal "one, two and three", ['one', 'two', 'three'].to_sentence(last_word_connector: ' and ') + end + + def test_two_elements + assert_equal "one and two", ['one', 'two'].to_sentence + assert_equal "one two", ['one', 'two'].to_sentence(two_words_connector: ' ') + end + + def test_one_element + assert_equal "one", ['one'].to_sentence + end + + def test_one_element_not_same_object + elements = ["one"] + assert_not_equal elements[0].object_id, elements.to_sentence.object_id + end + + def test_one_non_string_element + assert_equal '1', [1].to_sentence + end + + def test_does_not_modify_given_hash + options = { words_connector: ' ' } + assert_equal "one two, and three", ['one', 'two', 'three'].to_sentence(options) + assert_equal({ words_connector: ' ' }, options) + end + + def test_with_blank_elements + assert_equal ", one, , two, and three", [nil, 'one', '', 'two', 'three'].to_sentence + end + + def test_with_invalid_options + exception = assert_raise ArgumentError do + ['one', 'two'].to_sentence(passing: 'invalid option') + end + + assert_equal exception.message, "Unknown key: :passing. Valid keys are: :words_connector, :two_words_connector, :last_word_connector, :locale" + end +end + +class ToSTest < ActiveSupport::TestCase + class TestDB + @@counter = 0 + def id + @@counter += 1 + end + end + + def test_to_s_db + collection = [TestDB.new, TestDB.new, TestDB.new] + + assert_equal "null", [].to_s(:db) + assert_equal "1,2,3", collection.to_s(:db) + end +end + +class ToXmlTest < ActiveSupport::TestCase + def test_to_xml_with_hash_elements + xml = [ + { name: "David", age: 26, age_in_millis: 820497600000 }, + { name: "Jason", age: 31, age_in_millis: BigDecimal.new('1.0') } + ].to_xml(skip_instruct: true, indent: 0) + + assert_equal '<objects type="array"><object>', xml.first(30) + assert xml.include?(%(<age type="integer">26</age>)), xml + assert xml.include?(%(<age-in-millis type="integer">820497600000</age-in-millis>)), xml + assert xml.include?(%(<name>David</name>)), xml + assert xml.include?(%(<age type="integer">31</age>)), xml + assert xml.include?(%(<age-in-millis type="decimal">1.0</age-in-millis>)), xml + assert xml.include?(%(<name>Jason</name>)), xml + end + + def test_to_xml_with_non_hash_elements + xml = [1, 2, 3].to_xml(skip_instruct: true, indent: 0) + + assert_equal '<fixnums type="array"><fixnum', xml.first(29) + assert xml.include?(%(<fixnum type="integer">2</fixnum>)), xml + end + + def test_to_xml_with_non_hash_different_type_elements + xml = [1, 2.0, '3'].to_xml(skip_instruct: true, indent: 0) + + assert_equal '<objects type="array"><object', xml.first(29) + assert xml.include?(%(<object type="integer">1</object>)), xml + assert xml.include?(%(<object type="float">2.0</object>)), xml + assert xml.include?(%(object>3</object>)), xml + end + + def test_to_xml_with_dedicated_name + xml = [ + { name: "David", age: 26, age_in_millis: 820497600000 }, { name: "Jason", age: 31 } + ].to_xml(skip_instruct: true, indent: 0, root: "people") + + assert_equal '<people type="array"><person>', xml.first(29) + end + + def test_to_xml_with_options + xml = [ + { name: "David", street_address: "Paulina" }, { name: "Jason", street_address: "Evergreen" } + ].to_xml(skip_instruct: true, skip_types: true, indent: 0) + + assert_equal "<objects><object>", xml.first(17) + assert xml.include?(%(<street-address>Paulina</street-address>)) + assert xml.include?(%(<name>David</name>)) + assert xml.include?(%(<street-address>Evergreen</street-address>)) + assert xml.include?(%(<name>Jason</name>)) + end + + def test_to_xml_with_indent_set + xml = [ + { name: "David", street_address: "Paulina" }, { name: "Jason", street_address: "Evergreen" } + ].to_xml(skip_instruct: true, skip_types: true, indent: 4) + + assert_equal "<objects>\n <object>", xml.first(22) + assert xml.include?(%(\n <street-address>Paulina</street-address>)) + assert xml.include?(%(\n <name>David</name>)) + assert xml.include?(%(\n <street-address>Evergreen</street-address>)) + assert xml.include?(%(\n <name>Jason</name>)) + end + + def test_to_xml_with_dasherize_false + xml = [ + { name: "David", street_address: "Paulina" }, { name: "Jason", street_address: "Evergreen" } + ].to_xml(skip_instruct: true, skip_types: true, indent: 0, dasherize: false) + + assert_equal "<objects><object>", xml.first(17) + assert xml.include?(%(<street_address>Paulina</street_address>)) + assert xml.include?(%(<street_address>Evergreen</street_address>)) + end + + def test_to_xml_with_dasherize_true + xml = [ + { name: "David", street_address: "Paulina" }, { name: "Jason", street_address: "Evergreen" } + ].to_xml(skip_instruct: true, skip_types: true, indent: 0, dasherize: true) + + assert_equal "<objects><object>", xml.first(17) + assert xml.include?(%(<street-address>Paulina</street-address>)) + assert xml.include?(%(<street-address>Evergreen</street-address>)) + end + + def test_to_xml_with_instruct + xml = [ + { name: "David", age: 26, age_in_millis: 820497600000 }, + { name: "Jason", age: 31, age_in_millis: BigDecimal.new('1.0') } + ].to_xml(skip_instruct: false, indent: 0) + + assert_match(/^<\?xml [^>]*/, xml) + assert_equal 0, xml.rindex(/<\?xml /) + end + + def test_to_xml_with_block + xml = [ + { name: "David", age: 26, age_in_millis: 820497600000 }, + { name: "Jason", age: 31, age_in_millis: BigDecimal.new('1.0') } + ].to_xml(skip_instruct: true, indent: 0) do |builder| + builder.count 2 + end + + assert xml.include?(%(<count>2</count>)), xml + end + + def test_to_xml_with_empty + xml = [].to_xml + assert_match(/type="array"\/>/, xml) + end + + def test_to_xml_dups_options + options = { skip_instruct: true } + [].to_xml(options) + # :builder, etc, shouldn't be added to options + assert_equal({ skip_instruct: true }, options) + end +end diff --git a/activesupport/test/core_ext/array/extract_options_test.rb b/activesupport/test/core_ext/array/extract_options_test.rb new file mode 100644 index 0000000000..0481a507cf --- /dev/null +++ b/activesupport/test/core_ext/array/extract_options_test.rb @@ -0,0 +1,45 @@ +require 'abstract_unit' +require 'active_support/core_ext/array' +require 'active_support/core_ext/hash' + +class ExtractOptionsTest < ActiveSupport::TestCase + class HashSubclass < Hash + end + + class ExtractableHashSubclass < Hash + def extractable_options? + true + end + end + + def test_extract_options + assert_equal({}, [].extract_options!) + assert_equal({}, [1].extract_options!) + assert_equal({ a: :b }, [{ a: :b }].extract_options!) + assert_equal({ a: :b }, [1, { a: :b }].extract_options!) + end + + def test_extract_options_doesnt_extract_hash_subclasses + hash = HashSubclass.new + hash[:foo] = 1 + array = [hash] + options = array.extract_options! + assert_equal({}, options) + assert_equal([hash], array) + end + + def test_extract_options_extracts_extractable_subclass + hash = ExtractableHashSubclass.new + hash[:foo] = 1 + array = [hash] + options = array.extract_options! + assert_equal({ foo: 1 }, options) + assert_equal([], array) + end + + def test_extract_options_extracts_hash_with_indifferent_access + array = [{ foo: 1 }.with_indifferent_access] + options = array.extract_options! + assert_equal(1, options[:foo]) + end +end diff --git a/activesupport/test/core_ext/array/grouping_test.rb b/activesupport/test/core_ext/array/grouping_test.rb new file mode 100644 index 0000000000..2eb0f05141 --- /dev/null +++ b/activesupport/test/core_ext/array/grouping_test.rb @@ -0,0 +1,126 @@ +require 'abstract_unit' +require 'active_support/core_ext/array' + +class GroupingTest < ActiveSupport::TestCase + def setup + Fixnum.send :private, :/ # test we avoid Integer#/ (redefined by mathn) + end + + def teardown + Fixnum.send :public, :/ + end + + def test_in_groups_of_with_perfect_fit + groups = [] + ('a'..'i').to_a.in_groups_of(3) do |group| + groups << group + end + + assert_equal [%w(a b c), %w(d e f), %w(g h i)], groups + assert_equal [%w(a b c), %w(d e f), %w(g h i)], ('a'..'i').to_a.in_groups_of(3) + end + + def test_in_groups_of_with_padding + groups = [] + ('a'..'g').to_a.in_groups_of(3) do |group| + groups << group + end + + assert_equal [%w(a b c), %w(d e f), ['g', nil, nil]], groups + end + + def test_in_groups_of_pads_with_specified_values + groups = [] + + ('a'..'g').to_a.in_groups_of(3, 'foo') do |group| + groups << group + end + + assert_equal [%w(a b c), %w(d e f), %w(g foo foo)], groups + end + + def test_in_groups_of_without_padding + groups = [] + + ('a'..'g').to_a.in_groups_of(3, false) do |group| + groups << group + end + + assert_equal [%w(a b c), %w(d e f), %w(g)], groups + end + + def test_in_groups_returned_array_size + array = (1..7).to_a + + 1.upto(array.size + 1) do |number| + assert_equal number, array.in_groups(number).size + end + end + + def test_in_groups_with_empty_array + assert_equal [[], [], []], [].in_groups(3) + end + + def test_in_groups_with_block + array = (1..9).to_a + groups = [] + + array.in_groups(3) do |group| + groups << group + end + + assert_equal array.in_groups(3), groups + end + + def test_in_groups_with_perfect_fit + assert_equal [[1, 2, 3], [4, 5, 6], [7, 8, 9]], + (1..9).to_a.in_groups(3) + end + + def test_in_groups_with_padding + array = (1..7).to_a + + assert_equal [[1, 2, 3], [4, 5, nil], [6, 7, nil]], + array.in_groups(3) + assert_equal [[1, 2, 3], [4, 5, 'foo'], [6, 7, 'foo']], + array.in_groups(3, 'foo') + end + + def test_in_groups_without_padding + assert_equal [[1, 2, 3], [4, 5], [6, 7]], + (1..7).to_a.in_groups(3, false) + end + + def test_in_groups_invalid_argument + assert_raises(ArgumentError) { [].in_groups_of(0) } + assert_raises(ArgumentError) { [].in_groups_of(-1) } + assert_raises(ArgumentError) { [].in_groups_of(nil) } + end +end + +class SplitTest < ActiveSupport::TestCase + def test_split_with_empty_array + assert_equal [[]], [].split(0) + end + + def test_split_with_argument + a = [1, 2, 3, 4, 5] + assert_equal [[1, 2], [4, 5]], a.split(3) + assert_equal [[1, 2, 3, 4, 5]], a.split(0) + assert_equal [1, 2, 3, 4, 5], a + end + + def test_split_with_block + a = (1..10).to_a + assert_equal [[1, 2], [4, 5], [7, 8], [10]], a.split { |i| i % 3 == 0 } + assert_equal [1, 2, 3, 4, 5, 6, 7, 8, 9 ,10], a + end + + def test_split_with_edge_values + a = [1, 2, 3, 4, 5] + assert_equal [[], [2, 3, 4, 5]], a.split(1) + assert_equal [[1, 2, 3, 4], []], a.split(5) + assert_equal [[], [2, 3, 4], []], a.split { |i| i == 1 || i == 5 } + assert_equal [1, 2, 3, 4, 5], a + end +end diff --git a/activesupport/test/core_ext/array/prepend_append_test.rb b/activesupport/test/core_ext/array/prepend_append_test.rb new file mode 100644 index 0000000000..762aa69b2b --- /dev/null +++ b/activesupport/test/core_ext/array/prepend_append_test.rb @@ -0,0 +1,12 @@ +require 'abstract_unit' +require 'active_support/core_ext/array' + +class PrependAppendTest < ActiveSupport::TestCase + def test_append + assert_equal [1, 2], [1].append(2) + end + + def test_prepend + assert_equal [2, 1], [1].prepend(2) + end +end diff --git a/activesupport/test/core_ext/array/wrap_test.rb b/activesupport/test/core_ext/array/wrap_test.rb new file mode 100644 index 0000000000..baf426506f --- /dev/null +++ b/activesupport/test/core_ext/array/wrap_test.rb @@ -0,0 +1,77 @@ +require 'abstract_unit' +require 'active_support/core_ext/array' + +class WrapTest < ActiveSupport::TestCase + class FakeCollection + def to_ary + ["foo", "bar"] + end + end + + class Proxy + def initialize(target) @target = target end + def method_missing(*a) @target.send(*a) end + end + + class DoubtfulToAry + def to_ary + :not_an_array + end + end + + class NilToAry + def to_ary + nil + end + end + + def test_array + ary = %w(foo bar) + assert_same ary, Array.wrap(ary) + end + + def test_nil + assert_equal [], Array.wrap(nil) + end + + def test_object + o = Object.new + assert_equal [o], Array.wrap(o) + end + + def test_string + assert_equal ["foo"], Array.wrap("foo") + end + + def test_string_with_newline + assert_equal ["foo\nbar"], Array.wrap("foo\nbar") + end + + def test_object_with_to_ary + assert_equal ["foo", "bar"], Array.wrap(FakeCollection.new) + end + + def test_proxy_object + p = Proxy.new(Object.new) + assert_equal [p], Array.wrap(p) + end + + def test_proxy_to_object_with_to_ary + p = Proxy.new(FakeCollection.new) + assert_equal [p], Array.wrap(p) + end + + def test_struct + o = Struct.new(:foo).new(123) + assert_equal [o], Array.wrap(o) + end + + def test_wrap_returns_wrapped_if_to_ary_returns_nil + o = NilToAry.new + assert_equal [o], Array.wrap(o) + end + + def test_wrap_does_not_complain_if_to_ary_does_not_return_an_array + assert_equal DoubtfulToAry.new.to_ary, Array.wrap(DoubtfulToAry.new) + end +end diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb deleted file mode 100644 index efa7582ab0..0000000000 --- a/activesupport/test/core_ext/array_ext_test.rb +++ /dev/null @@ -1,471 +0,0 @@ -require 'abstract_unit' -require 'active_support/core_ext/array' -require 'active_support/core_ext/big_decimal' -require 'active_support/core_ext/object/conversions' - -require 'active_support/core_ext' # FIXME: pulling in all to_xml extensions -require 'active_support/hash_with_indifferent_access' - -class ArrayExtAccessTests < ActiveSupport::TestCase - def test_from - assert_equal %w( a b c d ), %w( a b c d ).from(0) - assert_equal %w( c d ), %w( a b c d ).from(2) - assert_equal %w(), %w( a b c d ).from(10) - end - - def test_to - assert_equal %w( a ), %w( a b c d ).to(0) - assert_equal %w( a b c ), %w( a b c d ).to(2) - assert_equal %w( a b c d ), %w( a b c d ).to(10) - end - - def test_second_through_tenth - array = (1..42).to_a - - assert_equal array[1], array.second - assert_equal array[2], array.third - assert_equal array[3], array.fourth - assert_equal array[4], array.fifth - assert_equal array[41], array.forty_two - end -end - -class ArrayExtToParamTests < ActiveSupport::TestCase - class ToParam < String - def to_param - "#{self}1" - end - end - - def test_string_array - assert_equal '', %w().to_param - assert_equal 'hello/world', %w(hello world).to_param - assert_equal 'hello/10', %w(hello 10).to_param - end - - def test_number_array - assert_equal '10/20', [10, 20].to_param - end - - def test_to_param_array - assert_equal 'custom1/param1', [ToParam.new('custom'), ToParam.new('param')].to_param - end -end - -class ArrayExtToSentenceTests < ActiveSupport::TestCase - def test_plain_array_to_sentence - assert_equal "", [].to_sentence - assert_equal "one", ['one'].to_sentence - assert_equal "one and two", ['one', 'two'].to_sentence - assert_equal "one, two, and three", ['one', 'two', 'three'].to_sentence - end - - def test_to_sentence_with_words_connector - assert_equal "one two, and three", ['one', 'two', 'three'].to_sentence(:words_connector => ' ') - assert_equal "one & two, and three", ['one', 'two', 'three'].to_sentence(:words_connector => ' & ') - assert_equal "onetwo, and three", ['one', 'two', 'three'].to_sentence(:words_connector => nil) - end - - def test_to_sentence_with_last_word_connector - assert_equal "one, two, and also three", ['one', 'two', 'three'].to_sentence(:last_word_connector => ', and also ') - assert_equal "one, twothree", ['one', 'two', 'three'].to_sentence(:last_word_connector => nil) - assert_equal "one, two three", ['one', 'two', 'three'].to_sentence(:last_word_connector => ' ') - assert_equal "one, two and three", ['one', 'two', 'three'].to_sentence(:last_word_connector => ' and ') - end - - def test_two_elements - assert_equal "one and two", ['one', 'two'].to_sentence - assert_equal "one two", ['one', 'two'].to_sentence(:two_words_connector => ' ') - end - - def test_one_element - assert_equal "one", ['one'].to_sentence - end - - def test_one_element_not_same_object - elements = ["one"] - assert_not_equal elements[0].object_id, elements.to_sentence.object_id - end - - def test_one_non_string_element - assert_equal '1', [1].to_sentence - end - - def test_does_not_modify_given_hash - options = { words_connector: ' ' } - assert_equal "one two, and three", ['one', 'two', 'three'].to_sentence(options) - assert_equal({ words_connector: ' ' }, options) - end -end - -class ArrayExtToSTests < ActiveSupport::TestCase - def test_to_s_db - collection = [ - Class.new { def id() 1 end }.new, - Class.new { def id() 2 end }.new, - Class.new { def id() 3 end }.new - ] - - assert_equal "null", [].to_s(:db) - assert_equal "1,2,3", collection.to_s(:db) - end -end - -class ArrayExtGroupingTests < ActiveSupport::TestCase - def setup - Fixnum.send :private, :/ # test we avoid Integer#/ (redefined by mathn) - end - - def teardown - Fixnum.send :public, :/ - end - - def test_in_groups_of_with_perfect_fit - groups = [] - ('a'..'i').to_a.in_groups_of(3) do |group| - groups << group - end - - assert_equal [%w(a b c), %w(d e f), %w(g h i)], groups - assert_equal [%w(a b c), %w(d e f), %w(g h i)], ('a'..'i').to_a.in_groups_of(3) - end - - def test_in_groups_of_with_padding - groups = [] - ('a'..'g').to_a.in_groups_of(3) do |group| - groups << group - end - - assert_equal [%w(a b c), %w(d e f), ['g', nil, nil]], groups - end - - def test_in_groups_of_pads_with_specified_values - groups = [] - - ('a'..'g').to_a.in_groups_of(3, 'foo') do |group| - groups << group - end - - assert_equal [%w(a b c), %w(d e f), ['g', 'foo', 'foo']], groups - end - - def test_in_groups_of_without_padding - groups = [] - - ('a'..'g').to_a.in_groups_of(3, false) do |group| - groups << group - end - - assert_equal [%w(a b c), %w(d e f), ['g']], groups - end - - def test_in_groups_returned_array_size - array = (1..7).to_a - - 1.upto(array.size + 1) do |number| - assert_equal number, array.in_groups(number).size - end - end - - def test_in_groups_with_empty_array - assert_equal [[], [], []], [].in_groups(3) - end - - def test_in_groups_with_block - array = (1..9).to_a - groups = [] - - array.in_groups(3) do |group| - groups << group - end - - assert_equal array.in_groups(3), groups - end - - def test_in_groups_with_perfect_fit - assert_equal [[1, 2, 3], [4, 5, 6], [7, 8, 9]], - (1..9).to_a.in_groups(3) - end - - def test_in_groups_with_padding - array = (1..7).to_a - - assert_equal [[1, 2, 3], [4, 5, nil], [6, 7, nil]], - array.in_groups(3) - assert_equal [[1, 2, 3], [4, 5, 'foo'], [6, 7, 'foo']], - array.in_groups(3, 'foo') - end - - def test_in_groups_without_padding - assert_equal [[1, 2, 3], [4, 5], [6, 7]], - (1..7).to_a.in_groups(3, false) - end -end - -class ArraySplitTests < ActiveSupport::TestCase - def test_split_with_empty_array - assert_equal [[]], [].split(0) - end - - def test_split_with_argument - assert_equal [[1, 2], [4, 5]], [1, 2, 3, 4, 5].split(3) - assert_equal [[1, 2, 3, 4, 5]], [1, 2, 3, 4, 5].split(0) - end - - def test_split_with_block - assert_equal [[1, 2], [4, 5], [7, 8], [10]], (1..10).to_a.split { |i| i % 3 == 0 } - end - - def test_split_with_edge_values - assert_equal [[], [2, 3, 4, 5]], [1, 2, 3, 4, 5].split(1) - assert_equal [[1, 2, 3, 4], []], [1, 2, 3, 4, 5].split(5) - assert_equal [[], [2, 3, 4], []], [1, 2, 3, 4, 5].split { |i| i == 1 || i == 5 } - end -end - -class ArrayToXmlTests < ActiveSupport::TestCase - def test_to_xml - xml = [ - { :name => "David", :age => 26, :age_in_millis => 820497600000 }, - { :name => "Jason", :age => 31, :age_in_millis => BigDecimal.new('1.0') } - ].to_xml(:skip_instruct => true, :indent => 0) - - assert_equal '<objects type="array"><object>', xml.first(30) - assert xml.include?(%(<age type="integer">26</age>)), xml - assert xml.include?(%(<age-in-millis type="integer">820497600000</age-in-millis>)), xml - assert xml.include?(%(<name>David</name>)), xml - assert xml.include?(%(<age type="integer">31</age>)), xml - assert xml.include?(%(<age-in-millis type="decimal">1.0</age-in-millis>)), xml - assert xml.include?(%(<name>Jason</name>)), xml - end - - def test_to_xml_with_dedicated_name - xml = [ - { :name => "David", :age => 26, :age_in_millis => 820497600000 }, { :name => "Jason", :age => 31 } - ].to_xml(:skip_instruct => true, :indent => 0, :root => "people") - - assert_equal '<people type="array"><person>', xml.first(29) - end - - def test_to_xml_with_options - xml = [ - { :name => "David", :street_address => "Paulina" }, { :name => "Jason", :street_address => "Evergreen" } - ].to_xml(:skip_instruct => true, :skip_types => true, :indent => 0) - - assert_equal "<objects><object>", xml.first(17) - assert xml.include?(%(<street-address>Paulina</street-address>)) - assert xml.include?(%(<name>David</name>)) - assert xml.include?(%(<street-address>Evergreen</street-address>)) - assert xml.include?(%(<name>Jason</name>)) - end - - def test_to_xml_with_dasherize_false - xml = [ - { :name => "David", :street_address => "Paulina" }, { :name => "Jason", :street_address => "Evergreen" } - ].to_xml(:skip_instruct => true, :skip_types => true, :indent => 0, :dasherize => false) - - assert_equal "<objects><object>", xml.first(17) - assert xml.include?(%(<street_address>Paulina</street_address>)) - assert xml.include?(%(<street_address>Evergreen</street_address>)) - end - - def test_to_xml_with_dasherize_true - xml = [ - { :name => "David", :street_address => "Paulina" }, { :name => "Jason", :street_address => "Evergreen" } - ].to_xml(:skip_instruct => true, :skip_types => true, :indent => 0, :dasherize => true) - - assert_equal "<objects><object>", xml.first(17) - assert xml.include?(%(<street-address>Paulina</street-address>)) - assert xml.include?(%(<street-address>Evergreen</street-address>)) - end - - def test_to_with_instruct - xml = [ - { :name => "David", :age => 26, :age_in_millis => 820497600000 }, - { :name => "Jason", :age => 31, :age_in_millis => BigDecimal.new('1.0') } - ].to_xml(:skip_instruct => false, :indent => 0) - - assert_match(/^<\?xml [^>]*/, xml) - assert_equal 0, xml.rindex(/<\?xml /) - end - - def test_to_xml_with_block - xml = [ - { :name => "David", :age => 26, :age_in_millis => 820497600000 }, - { :name => "Jason", :age => 31, :age_in_millis => BigDecimal.new('1.0') } - ].to_xml(:skip_instruct => true, :indent => 0) do |builder| - builder.count 2 - end - - assert xml.include?(%(<count>2</count>)), xml - end - - def test_to_xml_with_empty - xml = [].to_xml - assert_match(/type="array"\/>/, xml) - end - - def test_to_xml_dups_options - options = {:skip_instruct => true} - [].to_xml(options) - # :builder, etc, shouldn't be added to options - assert_equal({:skip_instruct => true}, options) - end -end - -class ArrayExtractOptionsTests < ActiveSupport::TestCase - class HashSubclass < Hash - end - - class ExtractableHashSubclass < Hash - def extractable_options? - true - end - end - - def test_extract_options - assert_equal({}, [].extract_options!) - assert_equal({}, [1].extract_options!) - assert_equal({:a=>:b}, [{:a=>:b}].extract_options!) - assert_equal({:a=>:b}, [1, {:a=>:b}].extract_options!) - end - - def test_extract_options_doesnt_extract_hash_subclasses - hash = HashSubclass.new - hash[:foo] = 1 - array = [hash] - options = array.extract_options! - assert_equal({}, options) - assert_equal [hash], array - end - - def test_extract_options_extracts_extractable_subclass - hash = ExtractableHashSubclass.new - hash[:foo] = 1 - array = [hash] - options = array.extract_options! - assert_equal({:foo => 1}, options) - assert_equal [], array - end - - def test_extract_options_extracts_hwia - hash = [{:foo => 1}.with_indifferent_access] - options = hash.extract_options! - assert_equal 1, options[:foo] - end -end - -class ArrayUniqByTests < ActiveSupport::TestCase - def test_uniq_by - ActiveSupport::Deprecation.silence do - assert_equal [1,2], [1,2,3,4].uniq_by { |i| i.odd? } - assert_equal [1,2], [1,2,3,4].uniq_by(&:even?) - assert_equal((-5..0).to_a, (-5..5).to_a.uniq_by{ |i| i**2 }) - end - end - - def test_uniq_by! - a = [1,2,3,4] - ActiveSupport::Deprecation.silence do - a.uniq_by! { |i| i.odd? } - end - assert_equal [1,2], a - - a = [1,2,3,4] - ActiveSupport::Deprecation.silence do - a.uniq_by! { |i| i.even? } - end - assert_equal [1,2], a - - a = (-5..5).to_a - ActiveSupport::Deprecation.silence do - a.uniq_by! { |i| i**2 } - end - assert_equal((-5..0).to_a, a) - end -end - -class ArrayWrapperTests < ActiveSupport::TestCase - class FakeCollection - def to_ary - ["foo", "bar"] - end - end - - class Proxy - def initialize(target) @target = target end - def method_missing(*a) @target.send(*a) end - end - - class DoubtfulToAry - def to_ary - :not_an_array - end - end - - class NilToAry - def to_ary - nil - end - end - - def test_array - ary = %w(foo bar) - assert_same ary, Array.wrap(ary) - end - - def test_nil - assert_equal [], Array.wrap(nil) - end - - def test_object - o = Object.new - assert_equal [o], Array.wrap(o) - end - - def test_string - assert_equal ["foo"], Array.wrap("foo") - end - - def test_string_with_newline - assert_equal ["foo\nbar"], Array.wrap("foo\nbar") - end - - def test_object_with_to_ary - assert_equal ["foo", "bar"], Array.wrap(FakeCollection.new) - end - - def test_proxy_object - p = Proxy.new(Object.new) - assert_equal [p], Array.wrap(p) - end - - def test_proxy_to_object_with_to_ary - p = Proxy.new(FakeCollection.new) - assert_equal [p], Array.wrap(p) - end - - def test_struct - o = Struct.new(:foo).new(123) - assert_equal [o], Array.wrap(o) - end - - def test_wrap_returns_wrapped_if_to_ary_returns_nil - o = NilToAry.new - assert_equal [o], Array.wrap(o) - end - - def test_wrap_does_not_complain_if_to_ary_does_not_return_an_array - assert_equal DoubtfulToAry.new.to_ary, Array.wrap(DoubtfulToAry.new) - end -end - -class ArrayPrependAppendTest < ActiveSupport::TestCase - def test_append - assert_equal [1, 2], [1].append(2) - end - - def test_prepend - assert_equal [2, 1], [1].prepend(2) - end -end diff --git a/activesupport/test/core_ext/big_decimal/yaml_conversions_test.rb b/activesupport/test/core_ext/big_decimal/yaml_conversions_test.rb new file mode 100644 index 0000000000..e634679d20 --- /dev/null +++ b/activesupport/test/core_ext/big_decimal/yaml_conversions_test.rb @@ -0,0 +1,11 @@ +require 'abstract_unit' + +class BigDecimalYamlConversionsTest < ActiveSupport::TestCase + def test_to_yaml + assert_deprecated { require 'active_support/core_ext/big_decimal/yaml_conversions' } + assert_match("--- 100000.30020320320000000000000000000000000000001\n", BigDecimal.new('100000.30020320320000000000000000000000000000001').to_yaml) + assert_match("--- .Inf\n", BigDecimal.new('Infinity').to_yaml) + assert_match("--- .NaN\n", BigDecimal.new('NaN').to_yaml) + assert_match("--- -.Inf\n", BigDecimal.new('-Infinity').to_yaml) + end +end diff --git a/activesupport/test/core_ext/bigdecimal_test.rb b/activesupport/test/core_ext/bigdecimal_test.rb index a5987044b9..423a3f2e9d 100644 --- a/activesupport/test/core_ext/bigdecimal_test.rb +++ b/activesupport/test/core_ext/bigdecimal_test.rb @@ -1,20 +1,7 @@ require 'abstract_unit' -require 'bigdecimal' require 'active_support/core_ext/big_decimal' class BigDecimalTest < ActiveSupport::TestCase - def test_to_yaml - assert_match("--- 100000.30020320320000000000000000000000000000001\n", BigDecimal.new('100000.30020320320000000000000000000000000000001').to_yaml) - assert_match("--- .Inf\n", BigDecimal.new('Infinity').to_yaml) - assert_match("--- .NaN\n", BigDecimal.new('NaN').to_yaml) - assert_match("--- -.Inf\n", BigDecimal.new('-Infinity').to_yaml) - end - - def test_to_d - bd = BigDecimal.new '10' - assert_equal bd, bd.to_d - end - def test_to_s bd = BigDecimal.new '0.01' assert_equal '0.01', bd.to_s diff --git a/activesupport/test/core_ext/class/attribute_accessor_test.rb b/activesupport/test/core_ext/class/attribute_accessor_test.rb deleted file mode 100644 index 8d827f054e..0000000000 --- a/activesupport/test/core_ext/class/attribute_accessor_test.rb +++ /dev/null @@ -1,59 +0,0 @@ -require 'abstract_unit' -require 'active_support/core_ext/class/attribute_accessors' - -class ClassAttributeAccessorTest < ActiveSupport::TestCase - def setup - @class = Class.new do - cattr_accessor :foo - cattr_accessor :bar, :instance_writer => false - cattr_reader :shaq, :instance_reader => false - cattr_accessor :camp, :instance_accessor => false - end - @object = @class.new - end - - def test_should_use_mattr_default - assert_nil @class.foo - assert_nil @object.foo - end - - def test_should_set_mattr_value - @class.foo = :test - assert_equal :test, @object.foo - - @object.foo = :test2 - assert_equal :test2, @class.foo - end - - def test_should_not_create_instance_writer - assert_respond_to @class, :foo - assert_respond_to @class, :foo= - assert_respond_to @object, :bar - assert !@object.respond_to?(:bar=) - end - - def test_should_not_create_instance_reader - assert_respond_to @class, :shaq - assert !@object.respond_to?(:shaq) - end - - def test_should_not_create_instance_accessors - assert_respond_to @class, :camp - assert !@object.respond_to?(:camp) - assert !@object.respond_to?(:camp=) - end - - def test_should_raise_name_error_if_attribute_name_is_invalid - assert_raises NameError do - Class.new do - cattr_reader "invalid attribute name" - end - end - - assert_raises NameError do - Class.new do - cattr_writer "invalid attribute name" - end - end - end -end diff --git a/activesupport/test/core_ext/class/attribute_test.rb b/activesupport/test/core_ext/class/attribute_test.rb index 1c3ba8a7a0..e7a1334db3 100644 --- a/activesupport/test/core_ext/class/attribute_test.rb +++ b/activesupport/test/core_ext/class/attribute_test.rb @@ -27,7 +27,7 @@ class ClassAttributeTest < ActiveSupport::TestCase assert_equal 1, Class.new(@sub).setting end - test 'query method' do + test 'predicate method' do assert_equal false, @klass.setting? @klass.setting = 1 assert_equal true, @klass.setting? @@ -48,7 +48,7 @@ class ClassAttributeTest < ActiveSupport::TestCase assert_equal 1, object.setting end - test 'instance query' do + test 'instance predicate' do object = @klass.new assert_equal false, object.setting? object.setting = 1 @@ -73,6 +73,11 @@ class ClassAttributeTest < ActiveSupport::TestCase assert_raise(NoMethodError) { object.setting = 'boom' } end + test 'disabling instance predicate' do + object = Class.new { class_attribute :setting, instance_predicate: false }.new + assert_raise(NoMethodError) { object.setting? } + end + test 'works well with singleton classes' do object = @klass.new object.singleton_class.setting = 'foo' diff --git a/activesupport/test/core_ext/class/delegating_attributes_test.rb b/activesupport/test/core_ext/class/delegating_attributes_test.rb index 148f82946c..447b1d10ad 100644 --- a/activesupport/test/core_ext/class/delegating_attributes_test.rb +++ b/activesupport/test/core_ext/class/delegating_attributes_test.rb @@ -6,14 +6,18 @@ module DelegatingFixtures end class Child < Parent - superclass_delegating_accessor :some_attribute + ActiveSupport::Deprecation.silence do + superclass_delegating_accessor :some_attribute + end end class Mokopuna < Child end class PercysMom - superclass_delegating_accessor :superpower + ActiveSupport::Deprecation.silence do + superclass_delegating_accessor :superpower + end end class Percy < PercysMom @@ -29,7 +33,10 @@ class DelegatingAttributesTest < ActiveSupport::TestCase end def test_simple_accessor_declaration - single_class.superclass_delegating_accessor :both + 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 @@ -39,14 +46,23 @@ class DelegatingAttributesTest < ActiveSupport::TestCase end def test_simple_accessor_declaration_with_instance_reader_false - single_class.superclass_delegating_accessor :no_instance_reader, :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 !single_class.public_instance_methods.map(&:to_s).include?("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 - single_class.superclass_delegating_accessor :both + assert_deprecated do + single_class.superclass_delegating_accessor :both + end single_class.both = "HMMM" @@ -62,7 +78,11 @@ class DelegatingAttributesTest < ActiveSupport::TestCase def test_child_class_delegates_to_parent_but_can_be_overridden parent = Class.new - parent.superclass_delegating_accessor :both + + assert_deprecated do + parent.superclass_delegating_accessor :both + end + child = Class.new(parent) parent.both = "1" assert_equal "1", child.both @@ -94,4 +114,9 @@ class DelegatingAttributesTest < ActiveSupport::TestCase 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_and_time_behavior.rb b/activesupport/test/core_ext/date_and_time_behavior.rb index 9927856aa2..b4ef5a0597 100644 --- a/activesupport/test/core_ext/date_and_time_behavior.rb +++ b/activesupport/test/core_ext/date_and_time_behavior.rb @@ -95,6 +95,11 @@ module DateAndTimeBehavior end def test_next_week + # M | T | W | T | F | S | S # M | T | W | T | F | S | S # + # | 22/2 | | | | | # 28/2 | | | | | | # monday in next week `next_week` + # | 22/2 | | | | | # | | | | 4/3 | | # friday in next week `next_week(:friday)` + # 23/10 | | | | | | # 30/10 | | | | | | # monday in next week `next_week` + # 23/10 | | | | | | # | | 1/11 | | | | # wednesday in next week `next_week(:wednesday)` assert_equal date_time_init(2005,2,28,0,0,0), date_time_init(2005,2,22,15,15,10).next_week assert_equal date_time_init(2005,3,4,0,0,0), date_time_init(2005,2,22,15,15,10).next_week(:friday) assert_equal date_time_init(2006,10,30,0,0,0), date_time_init(2006,10,23,0,0,0).next_week diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index f3fa96ec6f..e89be25b53 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -1,6 +1,7 @@ require 'abstract_unit' require 'active_support/time' require 'core_ext/date_and_time_behavior' +require 'time_zone_test_helpers' class DateExtCalculationsTest < ActiveSupport::TestCase def date_time_init(year,month,day,*args) @@ -8,6 +9,7 @@ class DateExtCalculationsTest < ActiveSupport::TestCase end include DateAndTimeBehavior + include TimeZoneTestHelpers def test_yesterday_in_calendar_reform assert_equal Date.new(1582,10,4), Date.new(1582,10,15).yesterday @@ -25,6 +27,7 @@ class DateExtCalculationsTest < ActiveSupport::TestCase assert_equal "February 21st, 2005", date.to_s(:long_ordinal) assert_equal "2005-02-21", date.to_s(:db) assert_equal "21 Feb 2005", date.to_s(:rfc822) + assert_equal "2005-02-21", date.to_s(:iso8601) end def test_readable_inspect @@ -48,6 +51,10 @@ class DateExtCalculationsTest < ActiveSupport::TestCase end end + def test_compare_to_time + assert Date.yesterday < Time.now + end + def test_to_datetime assert_equal DateTime.civil(2005, 2, 21), Date.new(2005, 2, 21).to_datetime assert_equal 0, Date.new(2005, 2, 21).to_datetime.offset # use UTC offset @@ -243,6 +250,10 @@ class DateExtCalculationsTest < ActiveSupport::TestCase assert_equal Time.local(2005,2,21,0,0,0), Date.new(2005,2,21).beginning_of_day end + def test_middle_of_day + assert_equal Time.local(2005,2,21,12,0,0), Date.new(2005,2,21).middle_of_day + end + def test_beginning_of_day_when_zone_is_set zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] with_env_tz 'UTC' do @@ -267,6 +278,23 @@ class DateExtCalculationsTest < ActiveSupport::TestCase end end + def test_all_week + assert_equal Date.new(2011,6,6)..Date.new(2011,6,12), Date.new(2011,6,7).all_week + assert_equal Date.new(2011,6,5)..Date.new(2011,6,11), Date.new(2011,6,7).all_week(:sunday) + end + + def test_all_month + assert_equal Date.new(2011,6,1)..Date.new(2011,6,30), Date.new(2011,6,7).all_month + end + + def test_all_quarter + assert_equal Date.new(2011,4,1)..Date.new(2011,6,30), Date.new(2011,6,7).all_quarter + end + + def test_all_year + assert_equal Date.new(2011,1,1)..Date.new(2011,12,31), Date.new(2011,6,7).all_year + end + def test_xmlschema with_env_tz 'US/Eastern' do assert_match(/^1980-02-28T00:00:00-05:?00$/, Date.new(1980, 2, 28).xmlschema) @@ -323,22 +351,6 @@ class DateExtCalculationsTest < ActiveSupport::TestCase Date.new(2005,2,28).advance(options) assert_equal({ :years => 3, :months => 11, :days => 2 }, options) end - - protected - def with_env_tz(new_tz = 'US/Eastern') - old_tz, ENV['TZ'] = ENV['TZ'], new_tz - yield - ensure - old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') - end - - def with_tz_default(tz = nil) - old_tz = Time.zone - Time.zone = tz - yield - ensure - Time.zone = old_tz - end end class DateExtBehaviorTest < ActiveSupport::TestCase @@ -357,17 +369,5 @@ class DateExtBehaviorTest < ActiveSupport::TestCase Date.today.freeze.freeze end end - - def test_compare_with_infinity - assert_equal(-1, Date.today <=> Float::INFINITY) - assert_equal(1, Date.today <=> -Float::INFINITY) - end end -class DateExtConversionsTest < ActiveSupport::TestCase - def test_to_time_in_current_zone_is_deprecated - assert_deprecated(/to_time_in_current_zone/) do - Date.new(2012,6,7).to_time_in_current_zone - 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 24e62cc2b9..74319ecd09 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -1,6 +1,7 @@ require 'abstract_unit' require 'active_support/time' require 'core_ext/date_and_time_behavior' +require 'time_zone_test_helpers' class DateTimeExtCalculationsTest < ActiveSupport::TestCase def date_time_init(year,month,day,hour,minute,second,*args) @@ -8,6 +9,7 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase end include DateAndTimeBehavior + include TimeZoneTestHelpers def test_to_s datetime = DateTime.new(2005, 2, 21, 14, 30, 0, 0) @@ -18,6 +20,12 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase assert_equal "Mon, 21 Feb 2005 14:30:00 +0000", datetime.to_s(:rfc822) assert_equal "February 21st, 2005 14:30", datetime.to_s(:long_ordinal) assert_match(/^2005-02-21T14:30:00(Z|\+00:00)$/, datetime.to_s) + + with_env_tz "US/Central" do + assert_equal "2009-02-05T14:30:05-06:00", DateTime.civil(2009, 2, 5, 14, 30, 5, Rational(-21600, 86400)).to_s(:iso8601) + assert_equal "2008-06-09T04:05:01-05:00", DateTime.civil(2008, 6, 9, 4, 5, 1, Rational(-18000, 86400)).to_s(:iso8601) + assert_equal "2009-02-05T14:30:05+00:00", DateTime.civil(2009, 2, 5, 14, 30, 5).to_s(:iso8601) + end end def test_readable_inspect @@ -76,6 +84,10 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase assert_equal DateTime.civil(2005,2,4,0,0,0), DateTime.civil(2005,2,4,10,10,10).beginning_of_day end + def test_middle_of_day + assert_equal DateTime.civil(2005,2,4,12,0,0), DateTime.civil(2005,2,4,10,10,10).middle_of_day + end + def test_end_of_day assert_equal DateTime.civil(2005,2,4,23,59,59), DateTime.civil(2005,2,4,10,10,10).end_of_day end @@ -88,6 +100,14 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase assert_equal DateTime.civil(2005,2,4,19,59,59), DateTime.civil(2005,2,4,19,30,10).end_of_hour end + def test_beginning_of_minute + assert_equal DateTime.civil(2005,2,4,19,30,0), DateTime.civil(2005,2,4,19,30,10).beginning_of_minute + end + + def test_end_of_minute + assert_equal DateTime.civil(2005,2,4,19,30,59), DateTime.civil(2005,2,4,19,30,10).end_of_minute + end + def test_end_of_month assert_equal DateTime.civil(2005,3,31,23,59,59), DateTime.civil(2005,3,20,10,10,10).end_of_month assert_equal DateTime.civil(2005,2,28,23,59,59), DateTime.civil(2005,2,20,10,10,10).end_of_month @@ -121,6 +141,9 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase assert_equal DateTime.civil(2005,2,22,16), DateTime.civil(2005,2,22,15,15,10).change(:hour => 16) assert_equal DateTime.civil(2005,2,22,16,45), DateTime.civil(2005,2,22,15,15,10).change(:hour => 16, :min => 45) assert_equal DateTime.civil(2005,2,22,15,45), DateTime.civil(2005,2,22,15,15,10).change(:min => 45) + + # datetime with fractions of a second + assert_equal DateTime.civil(2005,2,1,15,15,10.7), DateTime.civil(2005,2,22,15,15,10.7).change(:day => 1) end def test_advance @@ -141,6 +164,12 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase assert_equal DateTime.civil(2013,10,17,20,22,19), DateTime.civil(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5, :hours => 5, :minutes => 7, :seconds => 9) end + def test_advance_partial_days + assert_equal DateTime.civil(2012,9,29,13,15,10), DateTime.civil(2012,9,28,1,15,10).advance(:days => 1.5) + assert_equal DateTime.civil(2012,9,28,13,15,10), DateTime.civil(2012,9,28,1,15,10).advance(:days => 0.5) + assert_equal DateTime.civil(2012,10,29,13,15,10), DateTime.civil(2012,9,28,1,15,10).advance(:days => 1.5, :months => 1) + end + def test_advanced_processes_first_the_date_deltas_and_then_the_time_deltas # If the time deltas were processed first, the following datetimes would be advanced to 2010/04/01 instead. assert_equal DateTime.civil(2010, 3, 29), DateTime.civil(2010, 2, 28, 23, 59, 59).advance(:months => 1, :seconds => 1) @@ -309,6 +338,7 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase 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 + assert_equal 946684800.5, DateTime.civil(1999,12,31,19,0,0.5,Rational(-5,24)).to_f end def test_to_i @@ -316,18 +346,13 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase assert_equal 946684800, DateTime.civil(1999,12,31,19,0,0,Rational(-5,24)).to_i end - protected - def with_env_tz(new_tz = 'US/Eastern') - old_tz, ENV['TZ'] = ENV['TZ'], new_tz - yield - ensure - old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') - end -end + def test_usec + assert_equal 0, DateTime.civil(2000).usec + assert_equal 500000, DateTime.civil(2000, 1, 1, 0, 0, Rational(1,2)).usec + end -class DateTimeExtBehaviorTest < ActiveSupport::TestCase - def test_compare_with_infinity - assert_equal(-1, DateTime.now <=> Float::INFINITY) - assert_equal(1, DateTime.now <=> -Float::INFINITY) + def test_nsec + assert_equal 0, DateTime.civil(2000).nsec + assert_equal 500000000, DateTime.civil(2000, 1, 1, 0, 0, Rational(1,2)).nsec end end diff --git a/activesupport/test/core_ext/digest/uuid_test.rb b/activesupport/test/core_ext/digest/uuid_test.rb new file mode 100644 index 0000000000..08e0a1d6e1 --- /dev/null +++ b/activesupport/test/core_ext/digest/uuid_test.rb @@ -0,0 +1,24 @@ +require 'abstract_unit' +require 'active_support/core_ext/digest/uuid' + +class DigestUUIDExt < ActiveSupport::TestCase + def test_v3_uuids + assert_equal "3d813cbb-47fb-32ba-91df-831e1593ac29", Digest::UUID.uuid_v3(Digest::UUID::DNS_NAMESPACE, "www.widgets.com") + assert_equal "86df55fb-428e-3843-8583-ba3c05f290bc", Digest::UUID.uuid_v3(Digest::UUID::URL_NAMESPACE, "http://www.widgets.com") + assert_equal "8c29ab0e-a2dc-3482-b5eb-20cb2e2387a1", Digest::UUID.uuid_v3(Digest::UUID::OID_NAMESPACE, "1.2.3") + assert_equal "ee49149d-53a4-304a-890b-468229f6afc3", Digest::UUID.uuid_v3(Digest::UUID::X500_NAMESPACE, "cn=John Doe, ou=People, o=Acme, Inc., c=US") + end + + def test_v5_uuids + assert_equal "21f7f8de-8051-5b89-8680-0195ef798b6a", Digest::UUID.uuid_v5(Digest::UUID::DNS_NAMESPACE, "www.widgets.com") + assert_equal "4e570fd8-186d-5a74-90f0-4d28e34673a1", Digest::UUID.uuid_v5(Digest::UUID::URL_NAMESPACE, "http://www.widgets.com") + assert_equal "42d5e23b-3a02-5135-85c6-52d1102f1f00", Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, "1.2.3") + assert_equal "fd5b2ddf-bcfe-58b6-90d6-db50f74db527", Digest::UUID.uuid_v5(Digest::UUID::X500_NAMESPACE, "cn=John Doe, ou=People, o=Acme, Inc., c=US") + end + + def test_invalid_hash_class + assert_raise ArgumentError do + Digest::UUID.uuid_from_hash(Digest::SHA2, Digest::UUID::OID_NAMESPACE, '1.2.3') + end + end +end diff --git a/activesupport/test/core_ext/duplicable_test.rb b/activesupport/test/core_ext/duplicable_test.rb deleted file mode 100644 index e0566e012c..0000000000 --- a/activesupport/test/core_ext/duplicable_test.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'abstract_unit' -require 'bigdecimal' -require 'active_support/core_ext/object/duplicable' -require 'active_support/core_ext/numeric/time' - -class DuplicableTest < ActiveSupport::TestCase - RAISE_DUP = [nil, false, true, :symbol, 1, 2.3, 5.seconds] - YES = ['1', Object.new, /foo/, [], {}, Time.now, Class.new, Module.new] - NO = [] - - begin - bd = BigDecimal.new('4.56') - YES << bd.dup - rescue TypeError - RAISE_DUP << bd - end - - - def test_duplicable - (RAISE_DUP + NO).each do |v| - assert !v.duplicable? - end - - YES.each do |v| - assert v.duplicable?, "#{v.class} should be duplicable" - end - - (YES + NO).each do |v| - assert_nothing_raised { v.dup } - end - - RAISE_DUP.each do |v| - assert_raises(TypeError, v.class.name) do - v.dup - end - end - end -end diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index 5e3987265b..2b893c7cd0 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -2,8 +2,11 @@ require 'abstract_unit' require 'active_support/inflector' require 'active_support/time' require 'active_support/json' +require 'time_zone_test_helpers' class DurationTest < ActiveSupport::TestCase + include TimeZoneTestHelpers + def test_is_a d = 1.day assert d.is_a?(ActiveSupport::Duration) @@ -17,19 +20,43 @@ class DurationTest < ActiveSupport::TestCase assert !d.is_a?(k) end + def test_instance_of + assert 1.minute.instance_of?(Fixnum) + assert 2.days.instance_of?(ActiveSupport::Duration) + assert !3.second.instance_of?(Numeric) + end + def test_threequals assert ActiveSupport::Duration === 1.day assert !(ActiveSupport::Duration === 1.day.to_i) assert !(ActiveSupport::Duration === 'foo') - assert !(ActiveSupport::Duration === ActiveSupport::ProxyObject.new) end def test_equals assert 1.day == 1.day assert 1.day == 1.day.to_i + assert 1.day.to_i == 1.day assert !(1.day == 'foo') end + def test_to_s + assert_equal "1", 1.second.to_s + end + + def test_eql + rubinius_skip "Rubinius' #eql? definition relies on #instance_of? " \ + "which behaves oddly for the sake of backward-compatibility." + + assert 1.minute.eql?(1.minute) + assert 1.minute.eql?(60.seconds) + assert 2.days.eql?(48.hours) + assert !1.second.eql?(1) + assert !1.eql?(1.second) + assert 1.minute.eql?(180.seconds - 2.minutes) + assert !1.minute.eql?(60) + assert !1.minute.eql?('foo') + end + def test_inspect assert_equal '0 seconds', 0.seconds.inspect assert_equal '1 month', 1.month.inspect @@ -37,6 +64,8 @@ class DurationTest < ActiveSupport::TestCase assert_equal '6 months and -2 days', (6.months - 2.days).inspect assert_equal '10 seconds', 10.seconds.inspect assert_equal '10 years, 2 months, and 1 day', (10.years + 2.months + 1.day).inspect + assert_equal '10 years, 2 months, and 1 day', (10.years + 1.month + 1.day + 1.month).inspect + assert_equal '10 years, 2 months, and 1 day', (1.day + 10.years + 2.months).inspect assert_equal '7 days', 1.week.inspect assert_equal '14 days', 1.fortnight.inspect end @@ -50,12 +79,10 @@ class DurationTest < ActiveSupport::TestCase end def test_argument_error - 1.second.ago('') - flunk("no exception was raised") - rescue ArgumentError => e + e = assert_raise ArgumentError do + 1.second.ago('') + end assert_equal 'expected a time or date, got ""', e.message, "ensure ArgumentError is not being raised by dependencies.rb" - rescue Exception => e - flunk("ArgumentError should be raised, but we got #{e.class} instead") end def test_fractional_weeks @@ -68,6 +95,19 @@ class DurationTest < ActiveSupport::TestCase assert_equal 86400 * 1.7, 1.7.days end + def test_since_and_ago + t = Time.local(2000) + assert_equal t + 1, 1.second.since(t) + assert_equal t - 1, 1.second.ago(t) + end + + def test_since_and_ago_without_argument + now = Time.now + assert 1.second.since >= now + 1 + now = Time.now + assert 1.second.ago >= now - 1 + end + def test_since_and_ago_with_fractional_days t = Time.local(2000) # since @@ -93,10 +133,10 @@ class DurationTest < ActiveSupport::TestCase with_env_tz 'US/Eastern' do Time.stubs(:now).returns Time.local(2000) # since - assert_equal false, 5.seconds.since.is_a?(ActiveSupport::TimeWithZone) + assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.since assert_equal Time.local(2000,1,1,0,0,5), 5.seconds.since # ago - assert_equal false, 5.seconds.ago.is_a?(ActiveSupport::TimeWithZone) + assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.ago assert_equal Time.local(1999,12,31,23,59,55), 5.seconds.ago end end @@ -106,11 +146,11 @@ class DurationTest < ActiveSupport::TestCase with_env_tz 'US/Eastern' do Time.stubs(:now).returns Time.local(2000) # since - assert_equal true, 5.seconds.since.is_a?(ActiveSupport::TimeWithZone) + 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_equal true, 5.seconds.ago.is_a?(ActiveSupport::TimeWithZone) + 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 @@ -138,15 +178,37 @@ class DurationTest < ActiveSupport::TestCase assert_equal counter, 60 end + def test_as_json + assert_equal 172800, 2.days.as_json + end + def test_to_json assert_equal '172800', 2.days.to_json end - protected - def with_env_tz(new_tz = 'US/Eastern') - old_tz, ENV['TZ'] = ENV['TZ'], new_tz - yield - ensure - old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') - end + def test_case_when + cased = case 1.day when 1.day then "ok" end + assert_equal cased, "ok" + end + + def test_respond_to + assert_respond_to 1.day, :since + assert_respond_to 1.day, :zero? + end + + def test_hash + assert_equal 1.minute.hash, 60.seconds.hash + end + + def test_comparable + assert_equal(-1, (0.seconds <=> 1.second)) + assert_equal(-1, (1.second <=> 1.minute)) + assert_equal(-1, (1 <=> 1.minute)) + assert_equal(0, (0.seconds <=> 0.seconds)) + assert_equal(0, (0.seconds <=> 0.minutes)) + assert_equal(0, (1.second <=> 1.second)) + assert_equal(1, (1.second <=> 0.second)) + assert_equal(1, (1.minute <=> 1.second)) + assert_equal(1, (61 <=> 1.minute)) + end end diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb index 0a1abac767..346dc3d208 100644 --- a/activesupport/test/core_ext/enumerable_test.rb +++ b/activesupport/test/core_ext/enumerable_test.rb @@ -8,7 +8,6 @@ class SummablePayment < Payment end class EnumerableTests < ActiveSupport::TestCase - Enumerator = [].each.class class GenericEnumerable include Enumerable @@ -21,28 +20,6 @@ class EnumerableTests < ActiveSupport::TestCase end end - def test_group_by - names = %w(marcel sam david jeremy) - klass = Struct.new(:name) - objects = (1..50).inject([]) do |people,| - p = klass.new - p.name = names.sort_by { rand }.first - people << p - end - - enum = GenericEnumerable.new(objects) - grouped = enum.group_by { |object| object.name } - - grouped.each do |name, group| - assert group.all? { |person| person.name == name } - end - - assert_equal objects.uniq.map(&:name), grouped.keys - assert({}.merge(grouped), "Could not convert ActiveSupport::OrderedHash into Hash") - assert_equal Enumerator, enum.group_by.class - assert_equal grouped, enum.group_by.each(&:name) - end - def test_sums enum = GenericEnumerable.new([5, 15, 10]) assert_equal 30, enum.sum @@ -94,10 +71,14 @@ class EnumerableTests < ActiveSupport::TestCase def test_index_by payments = GenericEnumerable.new([ Payment.new(5), Payment.new(15), Payment.new(10) ]) assert_equal({ 5 => Payment.new(5), 15 => Payment.new(15), 10 => Payment.new(10) }, - payments.index_by { |p| p.price }) + payments.index_by(&:price)) assert_equal Enumerator, payments.index_by.class + if Enumerator.method_defined? :size + assert_equal nil, payments.index_by.size + assert_equal 42, (1..42).index_by.size + end assert_equal({ 5 => Payment.new(5), 15 => Payment.new(15), 10 => Payment.new(10) }, - payments.index_by.each { |p| p.price }) + payments.index_by.each(&:price)) end def test_many diff --git a/activesupport/test/core_ext/hash/transform_keys_test.rb b/activesupport/test/core_ext/hash/transform_keys_test.rb new file mode 100644 index 0000000000..a7e12117f3 --- /dev/null +++ b/activesupport/test/core_ext/hash/transform_keys_test.rb @@ -0,0 +1,32 @@ +require 'abstract_unit' +require 'active_support/core_ext/hash/keys' + +class TransformKeysTest < ActiveSupport::TestCase + test "transform_keys returns a new hash with the keys computed from the block" do + original = { a: 'a', b: 'b' } + mapped = original.transform_keys { |k| "#{k}!".to_sym } + + assert_equal({ a: 'a', b: 'b' }, original) + assert_equal({ a!: 'a', b!: 'b' }, mapped) + end + + test "transform_keys! modifies the keys of the original" do + original = { a: 'a', b: 'b' } + mapped = original.transform_keys! { |k| "#{k}!".to_sym } + + assert_equal({ a!: 'a', b!: 'b' }, original) + assert_same original, mapped + end + + test "transform_keys returns an Enumerator if no block is given" do + original = { a: 'a', b: 'b' } + enumerator = original.transform_keys + assert_equal Enumerator, enumerator.class + end + + test "transform_keys is chainable with Enumerable methods" do + original = { a: 'a', b: 'b' } + mapped = original.transform_keys.with_index { |k, i| [k, i].join.to_sym } + assert_equal({ a0: 'a', b1: 'b' }, mapped) + end +end diff --git a/activesupport/test/core_ext/hash/transform_values_test.rb b/activesupport/test/core_ext/hash/transform_values_test.rb new file mode 100644 index 0000000000..45ed11fef7 --- /dev/null +++ b/activesupport/test/core_ext/hash/transform_values_test.rb @@ -0,0 +1,61 @@ +require 'abstract_unit' +require 'active_support/core_ext/hash/indifferent_access' +require 'active_support/core_ext/hash/transform_values' + +class TransformValuesTest < ActiveSupport::TestCase + test "transform_values returns a new hash with the values computed from the block" do + original = { a: 'a', b: 'b' } + mapped = original.transform_values { |v| v + '!' } + + assert_equal({ a: 'a', b: 'b' }, original) + assert_equal({ a: 'a!', b: 'b!' }, mapped) + end + + test "transform_values! modifies the values of the original" do + original = { a: 'a', b: 'b' } + mapped = original.transform_values! { |v| v + '!' } + + assert_equal({ a: 'a!', b: 'b!' }, original) + assert_same original, mapped + end + + test "indifferent access is still indifferent after mapping values" do + original = { a: 'a', b: 'b' }.with_indifferent_access + mapped = original.transform_values { |v| v + '!' } + + assert_equal 'a!', mapped[:a] + assert_equal 'a!', mapped['a'] + end + + # This is to be consistent with the behavior of Ruby's built in methods + # (e.g. #select, #reject) as of 2.2 + test "default values do not persist during mapping" do + original = Hash.new('foo') + original[:a] = 'a' + mapped = original.transform_values { |v| v + '!' } + + assert_equal 'a!', mapped[:a] + assert_nil mapped[:b] + end + + test "default procs do not persist after mapping" do + original = Hash.new { 'foo' } + original[:a] = 'a' + mapped = original.transform_values { |v| v + '!' } + + assert_equal 'a!', mapped[:a] + assert_nil mapped[:b] + end + + test "transform_values returns an Enumerator if no block is given" do + original = { a: 'a', b: 'b' } + enumerator = original.transform_values + assert_equal Enumerator, enumerator.class + end + + test "transform_values is chainable with Enumerable methods" do + original = { a: 'a', b: 'b' } + mapped = original.transform_values.with_index { |v, i| [v, i].join } + assert_equal({ a: 'a0', b: 'b1' }, mapped) + end +end diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 30d95b75bc..3d2f50ce49 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -23,6 +23,16 @@ class HashExtTest < ActiveSupport::TestCase end end + class HashByConversion + def initialize(hash) + @hash = hash + end + + def to_hash + @hash + end + end + def setup @strings = { 'a' => 1, 'b' => 2 } @nested_strings = { 'a' => { 'b' => { 'c' => 3 } } } @@ -36,6 +46,10 @@ class HashExtTest < ActiveSupport::TestCase @nested_illegal_symbols = { [] => { [] => 3} } @upcase_strings = { 'A' => 1, 'B' => 2 } @nested_upcase_strings = { 'A' => { 'B' => { 'C' => 3 } } } + @string_array_of_hashes = { 'a' => [ { 'b' => 2 }, { 'c' => 3 }, 4 ] } + @symbol_array_of_hashes = { :a => [ { :b => 2 }, { :c => 3 }, 4 ] } + @mixed_array_of_hashes = { :a => [ { :b => 2 }, { 'c' => 3 }, 4 ] } + @upcase_array_of_hashes = { 'A' => [ { 'B' => 2 }, { 'C' => 3 }, 4 ] } end def test_methods @@ -54,6 +68,10 @@ class HashExtTest < ActiveSupport::TestCase assert_respond_to h, :deep_stringify_keys! assert_respond_to h, :to_options assert_respond_to h, :to_options! + assert_respond_to h, :compact + assert_respond_to h, :compact! + assert_respond_to h, :except + assert_respond_to h, :except! end def test_transform_keys @@ -72,6 +90,9 @@ class HashExtTest < ActiveSupport::TestCase assert_equal @nested_upcase_strings, @nested_symbols.deep_transform_keys{ |key| key.to_s.upcase } assert_equal @nested_upcase_strings, @nested_strings.deep_transform_keys{ |key| key.to_s.upcase } assert_equal @nested_upcase_strings, @nested_mixed.deep_transform_keys{ |key| key.to_s.upcase } + assert_equal @upcase_array_of_hashes, @string_array_of_hashes.deep_transform_keys{ |key| key.to_s.upcase } + assert_equal @upcase_array_of_hashes, @symbol_array_of_hashes.deep_transform_keys{ |key| key.to_s.upcase } + assert_equal @upcase_array_of_hashes, @mixed_array_of_hashes.deep_transform_keys{ |key| key.to_s.upcase } end def test_deep_transform_keys_not_mutates @@ -97,6 +118,9 @@ class HashExtTest < ActiveSupport::TestCase assert_equal @nested_upcase_strings, @nested_symbols.deep_dup.deep_transform_keys!{ |key| key.to_s.upcase } assert_equal @nested_upcase_strings, @nested_strings.deep_dup.deep_transform_keys!{ |key| key.to_s.upcase } assert_equal @nested_upcase_strings, @nested_mixed.deep_dup.deep_transform_keys!{ |key| key.to_s.upcase } + assert_equal @upcase_array_of_hashes, @string_array_of_hashes.deep_dup.deep_transform_keys!{ |key| key.to_s.upcase } + assert_equal @upcase_array_of_hashes, @symbol_array_of_hashes.deep_dup.deep_transform_keys!{ |key| key.to_s.upcase } + assert_equal @upcase_array_of_hashes, @mixed_array_of_hashes.deep_dup.deep_transform_keys!{ |key| key.to_s.upcase } end def test_deep_transform_keys_with_bang_mutates @@ -122,6 +146,9 @@ class HashExtTest < ActiveSupport::TestCase assert_equal @nested_symbols, @nested_symbols.deep_symbolize_keys assert_equal @nested_symbols, @nested_strings.deep_symbolize_keys assert_equal @nested_symbols, @nested_mixed.deep_symbolize_keys + assert_equal @symbol_array_of_hashes, @string_array_of_hashes.deep_symbolize_keys + assert_equal @symbol_array_of_hashes, @symbol_array_of_hashes.deep_symbolize_keys + assert_equal @symbol_array_of_hashes, @mixed_array_of_hashes.deep_symbolize_keys end def test_deep_symbolize_keys_not_mutates @@ -147,6 +174,9 @@ class HashExtTest < ActiveSupport::TestCase assert_equal @nested_symbols, @nested_symbols.deep_dup.deep_symbolize_keys! assert_equal @nested_symbols, @nested_strings.deep_dup.deep_symbolize_keys! assert_equal @nested_symbols, @nested_mixed.deep_dup.deep_symbolize_keys! + assert_equal @symbol_array_of_hashes, @string_array_of_hashes.deep_dup.deep_symbolize_keys! + assert_equal @symbol_array_of_hashes, @symbol_array_of_hashes.deep_dup.deep_symbolize_keys! + assert_equal @symbol_array_of_hashes, @mixed_array_of_hashes.deep_dup.deep_symbolize_keys! end def test_deep_symbolize_keys_with_bang_mutates @@ -192,6 +222,9 @@ class HashExtTest < ActiveSupport::TestCase assert_equal @nested_strings, @nested_symbols.deep_stringify_keys assert_equal @nested_strings, @nested_strings.deep_stringify_keys assert_equal @nested_strings, @nested_mixed.deep_stringify_keys + assert_equal @string_array_of_hashes, @string_array_of_hashes.deep_stringify_keys + assert_equal @string_array_of_hashes, @symbol_array_of_hashes.deep_stringify_keys + assert_equal @string_array_of_hashes, @mixed_array_of_hashes.deep_stringify_keys end def test_deep_stringify_keys_not_mutates @@ -217,6 +250,9 @@ class HashExtTest < ActiveSupport::TestCase assert_equal @nested_strings, @nested_symbols.deep_dup.deep_stringify_keys! assert_equal @nested_strings, @nested_strings.deep_dup.deep_stringify_keys! assert_equal @nested_strings, @nested_mixed.deep_dup.deep_stringify_keys! + assert_equal @string_array_of_hashes, @string_array_of_hashes.deep_dup.deep_stringify_keys! + assert_equal @string_array_of_hashes, @symbol_array_of_hashes.deep_dup.deep_stringify_keys! + assert_equal @string_array_of_hashes, @mixed_array_of_hashes.deep_dup.deep_stringify_keys! end def test_deep_stringify_keys_with_bang_mutates @@ -329,7 +365,7 @@ class HashExtTest < ActiveSupport::TestCase :member? => true } hashes.each do |name, hash| - method_map.sort_by { |m| m.to_s }.each do |meth, expected| + method_map.sort_by(&:to_s).each do |meth, expected| assert_equal(expected, hash.__send__(meth, 'a'), "Calling #{name}.#{meth} 'a'") assert_equal(expected, hash.__send__(meth, :a), @@ -409,6 +445,12 @@ class HashExtTest < ActiveSupport::TestCase assert [updated_with_strings, updated_with_symbols, updated_with_mixed].all? { |h| h.keys.size == 2 } end + def test_update_with_to_hash_conversion + hash = HashWithIndifferentAccess.new + hash.update HashByConversion.new({ :a => 1 }) + assert_equal hash['a'], 1 + end + def test_indifferent_merging hash = HashWithIndifferentAccess.new hash[:a] = 'failure' @@ -428,6 +470,12 @@ class HashExtTest < ActiveSupport::TestCase assert_equal 2, hash['b'] end + def test_merge_with_to_hash_conversion + hash = HashWithIndifferentAccess.new + merged = hash.merge HashByConversion.new({ :a => 1 }) + assert_equal merged['a'], 1 + end + def test_indifferent_replace hash = HashWithIndifferentAccess.new hash[:a] = 42 @@ -440,6 +488,18 @@ class HashExtTest < ActiveSupport::TestCase assert_same hash, replaced end + def test_replace_with_to_hash_conversion + hash = HashWithIndifferentAccess.new + hash[:a] = 42 + + replaced = hash.replace(HashByConversion.new(b: 12)) + + assert hash.key?('b') + assert !hash.key?(:a) + assert_equal 12, hash[:b] + assert_same hash, replaced + end + def test_indifferent_merging_with_block hash = HashWithIndifferentAccess.new hash[:a] = 1 @@ -480,6 +540,42 @@ class HashExtTest < ActiveSupport::TestCase assert_equal hash.delete('a'), nil end + def test_indifferent_select + hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).select {|k,v| v == 1} + + assert_equal({ 'a' => 1 }, hash) + assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash + end + + def test_indifferent_select_returns_a_hash_when_unchanged + hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).select {|k,v| true} + + assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash + end + + def test_indifferent_select_bang + indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@strings) + indifferent_strings.select! {|k,v| v == 1} + + assert_equal({ 'a' => 1 }, indifferent_strings) + assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings + end + + def test_indifferent_reject + hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).reject {|k,v| v != 1} + + assert_equal({ 'a' => 1 }, hash) + assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash + end + + def test_indifferent_reject_bang + indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@strings) + indifferent_strings.reject! {|k,v| v != 1} + + assert_equal({ 'a' => 1 }, indifferent_strings) + assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings + end + def test_indifferent_to_hash # Should convert to a Hash with String keys. assert_equal @strings, @mixed.with_indifferent_access.to_hash @@ -490,6 +586,12 @@ class HashExtTest < ActiveSupport::TestCase roundtrip = mixed_with_default.with_indifferent_access.to_hash assert_equal @strings, roundtrip assert_equal '1234', roundtrip.default + + # Ensure nested hashes are not HashWithIndiffereneAccess + new_to_hash = @nested_mixed.with_indifferent_access.to_hash + assert_not new_to_hash.instance_of?(HashWithIndifferentAccess) + assert_not new_to_hash["a"].instance_of?(HashWithIndifferentAccess) + assert_not new_to_hash["a"]["b"].instance_of?(HashWithIndifferentAccess) end def test_lookup_returns_the_same_object_that_is_stored_in_hash_indifferent_access @@ -499,9 +601,21 @@ class HashExtTest < ActiveSupport::TestCase assert_equal [1], hash[:a] end + def test_with_indifferent_access_has_no_side_effects_on_existing_hash + hash = {content: [{:foo => :bar, 'bar' => 'baz'}]} + hash.with_indifferent_access + + assert_equal [:foo, "bar"], hash[:content].first.keys + end + def test_indifferent_hash_with_array_of_hashes hash = { "urls" => { "url" => [ { "address" => "1" }, { "address" => "2" } ] }}.with_indifferent_access assert_equal "1", hash[:urls][:url].first[:address] + + hash = hash.to_hash + assert_not hash.instance_of?(HashWithIndifferentAccess) + assert_not hash["urls"].instance_of?(HashWithIndifferentAccess) + assert_not hash["urls"]["url"].first.instance_of?(HashWithIndifferentAccess) end def test_should_preserve_array_subclass_when_value_is_array @@ -547,7 +661,15 @@ class HashExtTest < ActiveSupport::TestCase assert_equal 1, h['first'] end - def test_indifferent_subhashes + def test_to_options_on_indifferent_preserves_works_as_hash_with_dup + h = HashWithIndifferentAccess.new({ a: { b: 'b' } }) + dup = h.dup + + dup[:a][:c] = 'c' + assert_equal 'c', h[:a][:c] + end + + def test_indifferent_sub_hashes h = {'user' => {'id' => 5}}.with_indifferent_access ['user', :user].each {|user| [:id, 'id'].each {|id| assert_equal 5, h[user][id], "h[#{user.inspect}][#{id.inspect}] should be 5"}} @@ -571,11 +693,31 @@ class HashExtTest < ActiveSupport::TestCase { :failure => "stuff", :funny => "business" }.assert_valid_keys([ :failure, :funny ]) { :failure => "stuff", :funny => "business" }.assert_valid_keys(:failure, :funny) end + # not all valid keys are required to be present + assert_nothing_raised do + { :failure => "stuff", :funny => "business" }.assert_valid_keys([ :failure, :funny, :sunny ]) + { :failure => "stuff", :funny => "business" }.assert_valid_keys(:failure, :funny, :sunny) + end - assert_raise(ArgumentError, "Unknown key: failore") do + exception = assert_raise ArgumentError do { :failore => "stuff", :funny => "business" }.assert_valid_keys([ :failure, :funny ]) + end + assert_equal "Unknown key: :failore. Valid keys are: :failure, :funny", exception.message + + exception = assert_raise ArgumentError do { :failore => "stuff", :funny => "business" }.assert_valid_keys(:failure, :funny) end + assert_equal "Unknown key: :failore. Valid keys are: :failure, :funny", exception.message + + exception = assert_raise ArgumentError do + { :failore => "stuff", :funny => "business" }.assert_valid_keys([ :failure ]) + end + assert_equal "Unknown key: :failore. Valid keys are: :failure", exception.message + + exception = assert_raise ArgumentError do + { :failore => "stuff", :funny => "business" }.assert_valid_keys(:failure) + end + assert_equal "Unknown key: :failore. Valid keys are: :failure", exception.message end def test_assorted_keys_not_stringified @@ -604,6 +746,16 @@ class HashExtTest < ActiveSupport::TestCase assert_equal expected, hash_1 end + def test_deep_merge_with_falsey_values + hash_1 = { e: false } + hash_2 = { e: 'e' } + expected = { e: [:e, false, 'e'] } + assert_equal(expected, hash_1.deep_merge(hash_2) { |k, o, n| [k, o, n] }) + + hash_1.deep_merge!(hash_2) { |k, o, n| [k, o, n] } + assert_equal expected, hash_1 + end + def test_deep_merge_on_indifferent_access hash_1 = HashWithIndifferentAccess.new({ :a => "a", :b => "b", :c => { :c1 => "c1", :c2 => "c2", :c3 => { :d1 => "d1" } } }) hash_2 = HashWithIndifferentAccess.new({ :a => 1, :c => { :c1 => 2, :c3 => { :d2 => "d2" } } }) @@ -655,12 +807,6 @@ class HashExtTest < ActiveSupport::TestCase assert_equal expected, merged end - def test_diff - assert_deprecated do - assert_equal({ :a => 2 }, { :a => 2, :b => 5 }.diff({ :a => 1, :b => 5 })) - end - end - def test_slice original = { :a => 'x', :b => 'y', :c => 10 } expected = { :a => 'x', :b => 'y' } @@ -735,6 +881,24 @@ class HashExtTest < ActiveSupport::TestCase assert_equal 'bender', slice['login'] end + def test_slice_bang_does_not_override_default + hash = Hash.new(0) + hash.update(a: 1, b: 2) + + hash.slice!(:a) + + assert_equal 0, hash[:c] + end + + def test_slice_bang_does_not_override_default_proc + hash = Hash.new { |h, k| h[k] = [] } + hash.update(a: 1, b: 2) + + hash.slice!(:a) + + assert_equal [], hash[:c] + end + def test_extract original = {:a => 1, :b => 2, :c => 3, :d => 4} expected = {:a => 1, :b => 2} @@ -782,13 +946,19 @@ class HashExtTest < ActiveSupport::TestCase def test_except_with_more_than_one_argument original = { :a => 'x', :b => 'y', :c => 10 } expected = { :a => 'x' } + assert_equal expected, original.except(:b, :c) + + assert_equal expected, original.except!(:b, :c) + assert_equal expected, original end def test_except_with_original_frozen original = { :a => 'x', :b => 'y' } original.freeze assert_nothing_raised { original.except(:a) } + + assert_raise(RuntimeError) { original.except!(:a) } end def test_except_with_mocha_expectation_on_original @@ -796,6 +966,38 @@ class HashExtTest < ActiveSupport::TestCase original.expects(:delete).never original.except(:a) end + + def test_compact + hash_contain_nil_value = @symbols.merge(z: nil) + hash_with_only_nil_values = { a: nil, b: nil } + + h = hash_contain_nil_value.dup + assert_equal(@symbols, h.compact) + assert_equal(hash_contain_nil_value, h) + + h = hash_with_only_nil_values.dup + assert_equal({}, h.compact) + assert_equal(hash_with_only_nil_values, h) + end + + def test_compact! + hash_contain_nil_value = @symbols.merge(z: nil) + hash_with_only_nil_values = { a: nil, b: nil } + + h = hash_contain_nil_value.dup + assert_equal(@symbols, h.compact!) + assert_equal(@symbols, h) + + h = hash_with_only_nil_values.dup + assert_equal({}, h.compact!) + assert_equal({}, h) + end + + def test_new_with_to_hash_conversion + hash = HashWithIndifferentAccess.new(HashByConversion.new(a: 1)) + assert hash.key?('a') + assert_equal 1, hash[:a] + end end class IWriteMyOwnXML @@ -1300,12 +1502,32 @@ class HashToXmlTest < ActiveSupport::TestCase end end + def test_from_xml_array_one + expected = { 'numbers' => { 'type' => 'Array', 'value' => '1' }} + assert_equal expected, Hash.from_xml('<numbers type="Array"><value>1</value></numbers>') + end + + def test_from_xml_array_many + expected = { 'numbers' => { 'type' => 'Array', 'value' => [ '1', '2' ] }} + assert_equal expected, Hash.from_xml('<numbers type="Array"><value>1</value><value>2</value></numbers>') + end + def test_from_trusted_xml_allows_symbol_and_yaml_types expected = { 'product' => { 'name' => :value }} assert_equal expected, Hash.from_trusted_xml('<product><name type="symbol">value</name></product>') assert_equal expected, Hash.from_trusted_xml('<product><name type="yaml">:value</name></product>') end + def test_should_use_default_proc_for_unknown_key + hash_wia = HashWithIndifferentAccess.new { 1 + 2 } + assert_equal 3, hash_wia[:new_key] + end + + def test_should_use_default_proc_if_no_key_is_supplied + hash_wia = HashWithIndifferentAccess.new { 1 + 2 } + assert_equal 3, hash_wia.default + end + def test_should_use_default_value_for_unknown_key hash_wia = HashWithIndifferentAccess.new(3) assert_equal 3, hash_wia[:new_key] @@ -1333,6 +1555,17 @@ class HashToXmlTest < ActiveSupport::TestCase assert_equal 3, hash_wia.default end + def test_should_copy_the_default_proc_when_converting_to_hash_with_indifferent_access + hash = Hash.new do + 2 + 1 + end + assert_equal 3, hash[:foo] + + hash_wia = hash.with_indifferent_access + assert_equal 3, hash_wia[:foo] + assert_equal 3, hash_wia[:bar] + end + # The XML builder seems to fail miserably when trying to tag something # with the same name as a Kernel method (throw, test, loop, select ...) def test_kernel_method_names_to_xml diff --git a/activesupport/test/core_ext/kernel/concern_test.rb b/activesupport/test/core_ext/kernel/concern_test.rb new file mode 100644 index 0000000000..478a00d2d2 --- /dev/null +++ b/activesupport/test/core_ext/kernel/concern_test.rb @@ -0,0 +1,13 @@ +require 'abstract_unit' +require 'active_support/core_ext/kernel/concern' + +class KernelConcernTest < ActiveSupport::TestCase + def test_may_be_defined_at_toplevel + mod = ::TOPLEVEL_BINDING.eval 'concern(:ToplevelConcern) { }' + assert_equal mod, ::ToplevelConcern + assert_kind_of ActiveSupport::Concern, ::ToplevelConcern + assert_not Object.ancestors.include?(::ToplevelConcern), mod.ancestors.inspect + ensure + Object.send :remove_const, :ToplevelConcern + end +end diff --git a/activesupport/test/core_ext/kernel_test.rb b/activesupport/test/core_ext/kernel_test.rb index b8951de402..a87af0007c 100644 --- a/activesupport/test/core_ext/kernel_test.rb +++ b/activesupport/test/core_ext/kernel_test.rb @@ -30,19 +30,29 @@ class KernelTest < ActiveSupport::TestCase end - def test_silence_stderr - old_stderr_position = STDERR.tell - silence_stderr { STDERR.puts 'hello world' } - assert_equal old_stderr_position, STDERR.tell + def test_silence_stream + old_stream_position = STDOUT.tell + silence_stream(STDOUT) { STDOUT.puts 'hello world' } + assert_equal old_stream_position, STDOUT.tell rescue Errno::ESPIPE - # Skip if we can't STDERR.tell + # Skip if we can't stream.tell + end + + def test_silence_stream_closes_file_descriptors + stream = StringIO.new + dup_stream = StringIO.new + stream.stubs(:dup).returns(dup_stream) + dup_stream.expects(:close) + silence_stream(stream) { stream.puts 'hello world' } end def test_quietly old_stdout_position, old_stderr_position = STDOUT.tell, STDERR.tell - quietly do - puts 'see me, feel me' - STDERR.puts 'touch me, heal me' + assert_deprecated do + quietly do + puts 'see me, feel me' + STDERR.puts 'touch me, heal me' + end end assert_equal old_stdout_position, STDOUT.tell assert_equal old_stderr_position, STDERR.tell @@ -50,10 +60,6 @@ class KernelTest < ActiveSupport::TestCase # Skip if we can't STDERR.tell end - def test_silence_stderr_with_return_value - assert_equal 1, silence_stderr { 1 } - end - def test_class_eval o = Object.new class << o; @x = 1; end @@ -61,10 +67,18 @@ class KernelTest < ActiveSupport::TestCase end def test_capture - assert_equal 'STDERR', capture(:stderr) { $stderr.print 'STDERR' } - assert_equal 'STDOUT', capture(:stdout) { print 'STDOUT' } - assert_equal "STDERR\n", capture(:stderr) { system('echo STDERR 1>&2') } - assert_equal "STDOUT\n", capture(:stdout) { system('echo STDOUT') } + assert_deprecated do + assert_equal 'STDERR', capture(:stderr) { $stderr.print 'STDERR' } + end + assert_deprecated do + assert_equal 'STDOUT', capture(:stdout) { print 'STDOUT' } + end + assert_deprecated do + assert_equal "STDERR\n", capture(:stderr) { system('echo STDERR 1>&2') } + end + assert_deprecated do + assert_equal "STDOUT\n", capture(:stdout) { system('echo STDOUT') } + end end end @@ -121,4 +135,4 @@ class KernelDebuggerTest < ActiveSupport::TestCase ensure Object.send(:remove_const, :Rails) end -end +end if RUBY_VERSION < '2.0.0' diff --git a/activesupport/test/core_ext/load_error_test.rb b/activesupport/test/core_ext/load_error_test.rb index 31863d0aca..b2a75a2bcc 100644 --- a/activesupport/test/core_ext/load_error_test.rb +++ b/activesupport/test/core_ext/load_error_test.rb @@ -1,17 +1,11 @@ require 'abstract_unit' require 'active_support/core_ext/load_error' + class TestMissingSourceFile < ActiveSupport::TestCase - def test_with_require - assert_raise(MissingSourceFile) { require 'no_this_file_don\'t_exist' } - end - def test_with_load - assert_raise(MissingSourceFile) { load 'nor_does_this_one' } - end - def test_path - begin load 'nor/this/one.rb' - rescue MissingSourceFile => e - assert_equal 'nor/this/one.rb', e.path + def test_it_is_deprecated + assert_deprecated do + MissingSourceFile.new end end end @@ -29,4 +23,4 @@ class TestLoadError < ActiveSupport::TestCase assert_equal 'nor/this/one.rb', e.path end end -end
\ No newline at end of file +end diff --git a/activesupport/test/core_ext/marshal_test.rb b/activesupport/test/core_ext/marshal_test.rb index ac79b15fa8..8f3f710dfd 100644 --- a/activesupport/test/core_ext/marshal_test.rb +++ b/activesupport/test/core_ext/marshal_test.rb @@ -1,10 +1,10 @@ require 'abstract_unit' require 'active_support/core_ext/marshal' -require 'dependecies_test_helpers' +require 'dependencies_test_helpers' class MarshalTest < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation - include DependeciesTestHelpers + include DependenciesTestHelpers def teardown ActiveSupport::Dependencies.clear diff --git a/activesupport/test/core_ext/module/attribute_accessor_test.rb b/activesupport/test/core_ext/module/attribute_accessor_test.rb index a577f90bdd..48f3cc579f 100644 --- a/activesupport/test/core_ext/module/attribute_accessor_test.rb +++ b/activesupport/test/core_ext/module/attribute_accessor_test.rb @@ -8,6 +8,11 @@ class ModuleAttributeAccessorTest < ActiveSupport::TestCase mattr_accessor :bar, :instance_writer => false mattr_reader :shaq, :instance_reader => false mattr_accessor :camp, :instance_accessor => false + + cattr_accessor(:defa) { 'default_accessor_value' } + cattr_reader(:defr) { 'default_reader_value' } + cattr_writer(:defw) { 'default_writer_value' } + cattr_accessor(:quux) { :quux } end @class = Class.new @class.instance_eval { include m } @@ -27,6 +32,11 @@ class ModuleAttributeAccessorTest < ActiveSupport::TestCase assert_equal :test2, @module.foo end + def test_cattr_accessor_default_value + assert_equal :quux, @module.quux + assert_equal :quux, @object.quux + end + def test_should_not_create_instance_writer assert_respond_to @module, :foo assert_respond_to @module, :foo= @@ -46,16 +56,24 @@ class ModuleAttributeAccessorTest < ActiveSupport::TestCase end def test_should_raise_name_error_if_attribute_name_is_invalid - assert_raises NameError do + exception = assert_raises NameError do Class.new do - mattr_reader "invalid attribute name" + cattr_reader "1nvalid" end end + assert_equal "invalid attribute name: 1nvalid", exception.message - assert_raises NameError do + exception = assert_raises NameError do Class.new do - mattr_writer "invalid attribute name" + cattr_writer "1nvalid" end end + assert_equal "invalid attribute name: 1nvalid", exception.message + end + + def test_should_use_default_value_if_block_passed + assert_equal 'default_accessor_value', @module.defa + assert_equal 'default_reader_value', @module.defr + assert_equal 'default_writer_value', @module.class_variable_get('@@defw') end end diff --git a/activesupport/test/core_ext/module/concerning_test.rb b/activesupport/test/core_ext/module/concerning_test.rb new file mode 100644 index 0000000000..07d860b71c --- /dev/null +++ b/activesupport/test/core_ext/module/concerning_test.rb @@ -0,0 +1,65 @@ +require 'abstract_unit' +require 'active_support/core_ext/module/concerning' + +class ModuleConcerningTest < ActiveSupport::TestCase + def test_concerning_declares_a_concern_and_includes_it_immediately + klass = Class.new { concerning(:Foo) { } } + assert klass.ancestors.include?(klass::Foo), klass.ancestors.inspect + end +end + +class ModuleConcernTest < ActiveSupport::TestCase + def test_concern_creates_a_module_extended_with_active_support_concern + klass = Class.new do + concern :Baz do + included { @foo = 1 } + def should_be_public; end + end + end + + # Declares a concern but doesn't include it + assert klass.const_defined?(:Baz, false) + assert !ModuleConcernTest.const_defined?(:Baz) + assert_kind_of ActiveSupport::Concern, klass::Baz + assert !klass.ancestors.include?(klass::Baz), klass.ancestors.inspect + + # Public method visibility by default + assert klass::Baz.public_instance_methods.map(&:to_s).include?('should_be_public') + + # Calls included hook + assert_equal 1, Class.new { include klass::Baz }.instance_variable_get('@foo') + end + + class Foo + concerning :Bar do + module ClassMethods + def will_be_orphaned; end + end + + const_set :ClassMethods, Module.new { + def hacked_on; end + } + + # Doesn't overwrite existing ClassMethods module. + class_methods do + def nicer_dsl; end + end + + # Doesn't overwrite previous class_methods definitions. + class_methods do + def doesnt_clobber; end + end + end + end + + def test_using_class_methods_blocks_instead_of_ClassMethods_module + assert !Foo.respond_to?(:will_be_orphaned) + assert Foo.respond_to?(:hacked_on) + assert Foo.respond_to?(:nicer_dsl) + assert Foo.respond_to?(:doesnt_clobber) + + # Orphan in Foo::ClassMethods, not Bar::ClassMethods. + assert Foo.const_defined?(:ClassMethods) + assert Foo::ClassMethods.method_defined?(:will_be_orphaned) + end +end diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb index 82249ddd1b..3c49c4d14f 100644 --- a/activesupport/test/core_ext/module_test.rb +++ b/activesupport/test/core_ext/module_test.rb @@ -12,12 +12,6 @@ class Ab Constant3 = "Goodbye World" end -module Xy - class Bc - include One - end -end - module Yz module Zy class Cd @@ -62,8 +56,31 @@ Developer = Struct.new(:client) do delegate :name, :to => :client, :prefix => nil end +Event = Struct.new(:case) do + delegate :foo, :to => :case +end + Tester = Struct.new(:client) do delegate :name, :to => :client, :prefix => false + + def foo; 1; end +end + +Product = Struct.new(:name) do + delegate :name, :to => :manufacturer, :prefix => true + delegate :name, :to => :type, :prefix => true + + def manufacturer + @manufacturer ||= begin + nil.unknown_method + end + end + + def type + @type ||= begin + :thing_without_same_method_name_as_delegated.name + end + end end class ParameterSet @@ -82,6 +99,21 @@ class Name end end +class SideEffect + attr_reader :ints + + delegate :to_i, :to => :shift, :allow_nil => true + delegate :to_s, :to => :shift + + def initialize + @ints = [1, 2, 3] + end + + def shift + @ints.shift + end +end + class ModuleTest < ActiveSupport::TestCase def setup @david = Someone.new("David", Somewhere.new("Paulina", "Chicago")) @@ -171,6 +203,17 @@ class ModuleTest < ActiveSupport::TestCase assert_nil rails.name end + # Ensures with check for nil, not for a falseish target. + def test_delegation_with_allow_nil_and_false_value + project = Project.new(false, false) + assert_raise(NoMethodError) { project.name } + end + + def test_delegation_with_allow_nil_and_invalid_value + rails = Project.new("Rails", "David") + assert_raise(NoMethodError) { rails.name } + end + def test_delegation_with_allow_nil_and_nil_value_and_prefix Project.class_eval do delegate :name, :to => :person, :allow_nil => true, :prefix => true @@ -181,7 +224,7 @@ class ModuleTest < ActiveSupport::TestCase def test_delegation_without_allow_nil_and_nil_value david = Someone.new("David") - assert_raise(RuntimeError) { david.street } + assert_raise(Module::DelegationError) { david.street } end def test_delegation_to_method_that_exists_on_nil @@ -208,6 +251,16 @@ class ModuleTest < ActiveSupport::TestCase end end + def test_delegation_line_number + _, line = Someone.instance_method(:foo).source_location + assert_equal Someone::FAILED_DELEGATE_LINE, line + end + + def test_delegate_line_with_nil + _, line = Someone.instance_method(:bar).source_location + assert_equal Someone::FAILED_DELEGATE_LINE_2, line + end + def test_delegation_exception_backtrace someone = Someone.new("foo", "bar") someone.foo @@ -228,6 +281,26 @@ class ModuleTest < ActiveSupport::TestCase "[#{e.backtrace.inspect}] did not include [#{file_and_line}]" end + def test_delegation_invokes_the_target_exactly_once + se = SideEffect.new + + assert_equal 1, se.to_i + assert_equal [2, 3], se.ints + + assert_equal '2', se.to_s + assert_equal [3], se.ints + end + + def test_delegation_doesnt_mask_nested_no_method_error_on_nil_receiver + product = Product.new('Widget') + + # Nested NoMethodError is a different name from the delegation + assert_raise(NoMethodError) { product.manufacturer_name } + + # Nested NoMethodError is the same name as the delegation + assert_raise(NoMethodError) { product.type_name } + end + def test_parent assert_equal Yz::Zy, Yz::Zy::Cd.parent assert_equal Yz, Yz::Zy.parent @@ -242,12 +315,6 @@ class ModuleTest < ActiveSupport::TestCase def test_local_constants assert_equal %w(Constant1 Constant3), Ab.local_constants.sort.map(&:to_s) end - - def test_local_constant_names - ActiveSupport::Deprecation.silence do - assert_equal %w(Constant1 Constant3), Ab.local_constant_names.sort.map(&:to_s) - end - end end module BarMethodAliaser @@ -434,4 +501,9 @@ class MethodAliasingTest < ActiveSupport::TestCase assert_equal 'duck_with_orange', @instance.duck assert FooClassWithBarMethod.public_method_defined?(:duck) end + + def test_delegate_with_case + event = Event.new(Tester.new) + assert_equal 1, event.foo + end end diff --git a/activesupport/test/core_ext/name_error_test.rb b/activesupport/test/core_ext/name_error_test.rb index 03ce09f22a..7525f80cf0 100644 --- a/activesupport/test/core_ext/name_error_test.rb +++ b/activesupport/test/core_ext/name_error_test.rb @@ -3,18 +3,18 @@ require 'active_support/core_ext/name_error' class NameErrorTest < ActiveSupport::TestCase def test_name_error_should_set_missing_name - SomeNameThatNobodyWillUse____Really ? 1 : 0 - flunk "?!?!" - rescue NameError => exc + exc = assert_raise NameError do + SomeNameThatNobodyWillUse____Really ? 1 : 0 + end assert_equal "NameErrorTest::SomeNameThatNobodyWillUse____Really", exc.missing_name assert exc.missing_name?(:SomeNameThatNobodyWillUse____Really) assert exc.missing_name?("NameErrorTest::SomeNameThatNobodyWillUse____Really") end def test_missing_method_should_ignore_missing_name - some_method_that_does_not_exist - flunk "?!?!" - rescue NameError => exc + exc = assert_raise NameError do + some_method_that_does_not_exist + end assert !exc.missing_name?(:Foo) assert_nil exc.missing_name end diff --git a/activesupport/test/core_ext/numeric_ext_test.rb b/activesupport/test/core_ext/numeric_ext_test.rb index 8c7d00cae1..b82448458d 100644 --- a/activesupport/test/core_ext/numeric_ext_test.rb +++ b/activesupport/test/core_ext/numeric_ext_test.rb @@ -22,23 +22,6 @@ class NumericExtTimeAndDateTimeTest < ActiveSupport::TestCase end end - def test_intervals - @seconds.values.each do |seconds| - assert_equal seconds.since(@now), @now + seconds - assert_equal seconds.until(@now), @now - seconds - end - end - - # Test intervals based from Time.now - def test_now - @seconds.values.each do |seconds| - now = Time.now - assert seconds.ago >= now - seconds - now = Time.now - assert seconds.from_now >= now + seconds - end - end - def test_irregular_durations assert_equal @now.advance(:days => 3000), 3000.days.since(@now) assert_equal @now.advance(:months => 1), 1.month.since(@now) @@ -77,55 +60,17 @@ class NumericExtTimeAndDateTimeTest < ActiveSupport::TestCase assert_equal @dtnow.advance(:days => 1).advance(:months => 2), @dtnow + 1.day + 2.months end - def test_duration_after_convertion_is_no_longer_accurate - assert_equal 30.days.to_i.since(@now), 1.month.to_i.since(@now) - assert_equal 365.25.days.to_f.since(@now), 1.year.to_f.since(@now) - assert_equal 30.days.to_i.since(@dtnow), 1.month.to_i.since(@dtnow) - assert_equal 365.25.days.to_f.since(@dtnow), 1.year.to_f.since(@dtnow) + def test_duration_after_conversion_is_no_longer_accurate + assert_equal 30.days.to_i.seconds.since(@now), 1.month.to_i.seconds.since(@now) + assert_equal 365.25.days.to_f.seconds.since(@now), 1.year.to_f.seconds.since(@now) + assert_equal 30.days.to_i.seconds.since(@dtnow), 1.month.to_i.seconds.since(@dtnow) + assert_equal 365.25.days.to_f.seconds.since(@dtnow), 1.year.to_f.seconds.since(@dtnow) end def test_add_one_year_to_leap_day assert_equal Time.utc(2005,2,28,15,15,10), Time.utc(2004,2,29,15,15,10) + 1.year assert_equal DateTime.civil(2005,2,28,15,15,10), DateTime.civil(2004,2,29,15,15,10) + 1.year end - - 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_equal false, 5.since.is_a?(ActiveSupport::TimeWithZone) - assert_equal Time.local(2000,1,1,0,0,5), 5.since - # ago - assert_equal false, 5.ago.is_a?(ActiveSupport::TimeWithZone) - assert_equal Time.local(1999,12,31,23,59,55), 5.ago - 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_equal true, 5.since.is_a?(ActiveSupport::TimeWithZone) - assert_equal Time.utc(2000,1,1,0,0,5), 5.since.time - assert_equal 'Eastern Time (US & Canada)', 5.since.time_zone.name - # ago - assert_equal true, 5.ago.is_a?(ActiveSupport::TimeWithZone) - assert_equal Time.utc(1999,12,31,23,59,55), 5.ago.time - assert_equal 'Eastern Time (US & Canada)', 5.ago.time_zone.name - end - ensure - Time.zone = nil - end - - protected - def with_env_tz(new_tz = 'US/Eastern') - old_tz, ENV['TZ'] = ENV['TZ'], new_tz - yield - ensure - old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') - end end class NumericExtDateTest < ActiveSupport::TestCase @@ -153,22 +98,16 @@ end class NumericExtSizeTest < ActiveSupport::TestCase def test_unit_in_terms_of_another - relationships = { - 1024.bytes => 1.kilobyte, - 1024.kilobytes => 1.megabyte, - 3584.0.kilobytes => 3.5.megabytes, - 3584.0.megabytes => 3.5.gigabytes, - 1.kilobyte ** 4 => 1.terabyte, - 1024.kilobytes + 2.megabytes => 3.megabytes, - 2.gigabytes / 4 => 512.megabytes, - 256.megabytes * 20 + 5.gigabytes => 10.gigabytes, - 1.kilobyte ** 5 => 1.petabyte, - 1.kilobyte ** 6 => 1.exabyte - } - - relationships.each do |left, right| - assert_equal right, left - end + assert_equal 1024.bytes, 1.kilobyte + assert_equal 1024.kilobytes, 1.megabyte + assert_equal 3584.0.kilobytes, 3.5.megabytes + assert_equal 3584.0.megabytes, 3.5.gigabytes + assert_equal 1.kilobyte ** 4, 1.terabyte + assert_equal 1024.kilobytes + 2.megabytes, 3.megabytes + assert_equal 2.gigabytes / 4, 512.megabytes + assert_equal 256.megabytes * 20 + 5.gigabytes, 10.gigabytes + assert_equal 1.kilobyte ** 5, 1.petabyte + assert_equal 1.kilobyte ** 6, 1.exabyte end def test_units_as_bytes_independently @@ -446,58 +385,8 @@ class NumericExtFormattingTest < ActiveSupport::TestCase assert_equal BigDecimal, BigDecimal("1000010").class assert_equal '1 Million', BigDecimal("1000010").to_s(:human) end -end - -class NumericExtBehaviorTest < ActiveSupport::TestCase - def setup - @inf = BigDecimal.new('Infinity') - end - - def test_compare_infinity_with_date - assert_equal(-1, -Float::INFINITY <=> Date.today) - assert_equal(1, Float::INFINITY <=> Date.today) - assert_equal(-1, -@inf <=> Date.today) - assert_equal(1, @inf <=> Date.today) - end - - def test_compare_infinty_with_infinty - assert_equal(-1, -Float::INFINITY <=> Float::INFINITY) - assert_equal(1, Float::INFINITY <=> -Float::INFINITY) - assert_equal(0, Float::INFINITY <=> Float::INFINITY) - assert_equal(0, -Float::INFINITY <=> -Float::INFINITY) - - assert_equal(-1, -Float::INFINITY <=> BigDecimal::INFINITY) - assert_equal(1, Float::INFINITY <=> -BigDecimal::INFINITY) - assert_equal(0, Float::INFINITY <=> BigDecimal::INFINITY) - assert_equal(0, -Float::INFINITY <=> -BigDecimal::INFINITY) - - assert_equal(-1, -BigDecimal::INFINITY <=> Float::INFINITY) - assert_equal(1, BigDecimal::INFINITY <=> -Float::INFINITY) - assert_equal(0, BigDecimal::INFINITY <=> Float::INFINITY) - assert_equal(0, -BigDecimal::INFINITY <=> -Float::INFINITY) - end - - def test_compare_infinity_with_time - assert_equal(-1, -Float::INFINITY <=> Time.now) - assert_equal(1, Float::INFINITY <=> Time.now) - assert_equal(-1, -@inf <=> Time.now) - assert_equal(1, @inf <=> Time.now) - end - - def test_compare_infinity_with_datetime - assert_equal(-1, -Float::INFINITY <=> DateTime.now) - assert_equal(1, Float::INFINITY <=> DateTime.now) - assert_equal(-1, -@inf <=> DateTime.now) - assert_equal(1, @inf <=> DateTime.now) - end - - def test_compare_infinity_with_twz - time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] - twz = ActiveSupport::TimeWithZone.new(Time.now, time_zone) - assert_equal(-1, -Float::INFINITY <=> twz) - assert_equal(1, Float::INFINITY <=> twz) - assert_equal(-1, -@inf <=> twz) - assert_equal(1, @inf <=> twz) + def test_in_milliseconds + assert_equal 10_000, 10.seconds.in_milliseconds end end diff --git a/activesupport/test/core_ext/object/acts_like_test.rb b/activesupport/test/core_ext/object/acts_like_test.rb new file mode 100644 index 0000000000..e68b1d23cb --- /dev/null +++ b/activesupport/test/core_ext/object/acts_like_test.rb @@ -0,0 +1,33 @@ +require 'abstract_unit' +require 'active_support/core_ext/object' + +class ObjectTests < ActiveSupport::TestCase + class DuckTime + def acts_like_time? + true + end + end + + def test_duck_typing + object = Object.new + time = Time.now + date = Date.today + dt = DateTime.new + duck = DuckTime.new + + assert !object.acts_like?(:time) + assert !object.acts_like?(:date) + + assert time.acts_like?(:time) + assert !time.acts_like?(:date) + + assert !date.acts_like?(:time) + assert date.acts_like?(:date) + + assert dt.acts_like?(:time) + assert dt.acts_like?(:date) + + assert duck.acts_like?(:time) + assert !duck.acts_like?(:date) + end +end diff --git a/activesupport/test/core_ext/blank_test.rb b/activesupport/test/core_ext/object/blank_test.rb index a68c074777..246bc7fa61 100644 --- a/activesupport/test/core_ext/blank_test.rb +++ b/activesupport/test/core_ext/object/blank_test.rb @@ -4,17 +4,29 @@ require 'abstract_unit' require 'active_support/core_ext/object/blank' class BlankTest < ActiveSupport::TestCase - BLANK = [ EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", ' ', [], {} ] + class EmptyTrue + def empty? + 0 + end + end + + class EmptyFalse + def empty? + nil + end + end + + BLANK = [ EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", ' ', "\u00a0", [], {} ] NOT = [ EmptyFalse.new, Object.new, true, 0, 1, 'a', [nil], { nil => 0 } ] def test_blank - BLANK.each { |v| assert v.blank?, "#{v.inspect} should be blank" } - NOT.each { |v| assert !v.blank?, "#{v.inspect} should not be blank" } + BLANK.each { |v| assert_equal true, v.blank?, "#{v.inspect} should be blank" } + NOT.each { |v| assert_equal false, v.blank?, "#{v.inspect} should not be blank" } end def test_present - BLANK.each { |v| assert !v.present?, "#{v.inspect} should not be present" } - NOT.each { |v| assert v.present?, "#{v.inspect} should be present" } + BLANK.each { |v| assert_equal false, v.present?, "#{v.inspect} should not be present" } + NOT.each { |v| assert_equal true, v.present?, "#{v.inspect} should be present" } end def test_presence diff --git a/activesupport/test/core_ext/deep_dup_test.rb b/activesupport/test/core_ext/object/deep_dup_test.rb index 91d558dbb5..91d558dbb5 100644 --- a/activesupport/test/core_ext/deep_dup_test.rb +++ b/activesupport/test/core_ext/object/deep_dup_test.rb diff --git a/activesupport/test/core_ext/object/duplicable_test.rb b/activesupport/test/core_ext/object/duplicable_test.rb new file mode 100644 index 0000000000..8cc39ae7b9 --- /dev/null +++ b/activesupport/test/core_ext/object/duplicable_test.rb @@ -0,0 +1,31 @@ +require 'abstract_unit' +require 'bigdecimal' +require 'active_support/core_ext/object/duplicable' +require 'active_support/core_ext/numeric/time' + +class DuplicableTest < ActiveSupport::TestCase + RAISE_DUP = [nil, false, true, :symbol, 1, 2.3, method(:puts)] + ALLOW_DUP = ['1', Object.new, /foo/, [], {}, Time.now, Class.new, Module.new] + + # Needed to support Ruby 1.9.x, as it doesn't allow dup on BigDecimal, instead + # raises TypeError exception. Checking here on the runtime whether BigDecimal + # will allow dup or not. + begin + bd = BigDecimal.new('4.56') + ALLOW_DUP << bd.dup + rescue TypeError + RAISE_DUP << bd + end + + def test_duplicable + RAISE_DUP.each do |v| + assert !v.duplicable? + assert_raises(TypeError, v.class.name) { v.dup } + end + + ALLOW_DUP.each do |v| + assert v.duplicable?, "#{ v.class } should be duplicable" + assert_nothing_raised { v.dup } + end + end +end diff --git a/activesupport/test/core_ext/object/inclusion_test.rb b/activesupport/test/core_ext/object/inclusion_test.rb index 22888333f5..32d512eca3 100644 --- a/activesupport/test/core_ext/object/inclusion_test.rb +++ b/activesupport/test/core_ext/object/inclusion_test.rb @@ -2,16 +2,6 @@ require 'abstract_unit' require 'active_support/core_ext/object/inclusion' class InTest < ActiveSupport::TestCase - def test_in_multiple_args - assert :b.in?(:a,:b) - assert !:c.in?(:a,:b) - end - - def test_in_multiple_arrays - assert [1,2].in?([1,2],[2,3]) - assert ![1,2].in?([1,3],[2,1]) - end - def test_in_array assert 1.in?([1,2]) assert !3.in?([1,2]) @@ -47,14 +37,23 @@ class InTest < ActiveSupport::TestCase end class C < B end + class D + end def test_in_module assert A.in?(B) assert A.in?(C) assert !A.in?(A) + assert !A.in?(D) end def test_no_method_catching assert_raise(ArgumentError) { 1.in?(1) } end + + def test_presence_in + assert_equal "stuff", "stuff".presence_in(%w( lots of stuff )) + assert_nil "stuff".presence_in(%w( lots of crap )) + assert_raise(ArgumentError) { 1.presence_in(1) } + end end diff --git a/activesupport/test/core_ext/object/instance_variables_test.rb b/activesupport/test/core_ext/object/instance_variables_test.rb new file mode 100644 index 0000000000..9f4c5dc4f1 --- /dev/null +++ b/activesupport/test/core_ext/object/instance_variables_test.rb @@ -0,0 +1,31 @@ +require 'abstract_unit' +require 'active_support/core_ext/object' + +class ObjectInstanceVariableTest < ActiveSupport::TestCase + def setup + @source, @dest = Object.new, Object.new + @source.instance_variable_set(:@bar, 'bar') + @source.instance_variable_set(:@baz, 'baz') + end + + def test_instance_variable_names + assert_equal %w(@bar @baz), @source.instance_variable_names.sort + end + + def test_instance_values + assert_equal({'bar' => 'bar', 'baz' => 'baz'}, @source.instance_values) + end + + def test_instance_exec_passes_arguments_to_block + assert_equal %w(hello goodbye), 'hello'.instance_exec('goodbye') { |v| [self, v] } + end + + def test_instance_exec_with_frozen_obj + assert_equal %w(olleh goodbye), 'hello'.freeze.instance_exec('goodbye') { |v| [reverse, v] } + end + + def test_instance_exec_nested + assert_equal %w(goodbye olleh bar), 'hello'.instance_exec('goodbye') { |arg| + [arg] + instance_exec('bar') { |v| [reverse, v] } } + end +end diff --git a/activesupport/test/core_ext/object/json_cherry_pick_test.rb b/activesupport/test/core_ext/object/json_cherry_pick_test.rb new file mode 100644 index 0000000000..2f7ea3a497 --- /dev/null +++ b/activesupport/test/core_ext/object/json_cherry_pick_test.rb @@ -0,0 +1,42 @@ +require 'abstract_unit' + +# These test cases were added to test that cherry-picking the json extensions +# works correctly, primarily for dependencies problems reported in #16131. They +# need to be executed in isolation to reproduce the scenario correctly, because +# other test cases might have already loaded additional dependencies. + +class JsonCherryPickTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation + + def test_time_as_json + require_or_skip 'active_support/core_ext/object/json' + + expected = Time.new(2004, 7, 25) + actual = Time.parse(expected.as_json) + + assert_equal expected, actual + end + + def test_date_as_json + require_or_skip 'active_support/core_ext/object/json' + + expected = Date.new(2004, 7, 25) + actual = Date.parse(expected.as_json) + + assert_equal expected, actual + end + + def test_datetime_as_json + require_or_skip 'active_support/core_ext/object/json' + + expected = DateTime.new(2004, 7, 25) + actual = DateTime.parse(expected.as_json) + + assert_equal expected, actual + end + + private + def require_or_skip(file) + require(file) || skip("'#{file}' was already loaded") + end +end diff --git a/activesupport/test/core_ext/object/to_param_test.rb b/activesupport/test/core_ext/object/to_param_test.rb index bd7c6c422a..30a7557dc2 100644 --- a/activesupport/test/core_ext/object/to_param_test.rb +++ b/activesupport/test/core_ext/object/to_param_test.rb @@ -2,6 +2,12 @@ require 'abstract_unit' require 'active_support/core_ext/object/to_param' class ToParamTest < ActiveSupport::TestCase + class CustomString < String + def to_param + "custom-#{ self }" + end + end + def test_object foo = Object.new def foo.to_s; 'foo' end @@ -16,4 +22,16 @@ class ToParamTest < ActiveSupport::TestCase assert_equal true, true.to_param assert_equal false, false.to_param end + + def test_array + # Empty Array + assert_equal '', [].to_param + + array = [1, 2, 3, 4] + assert_equal "1/2/3/4", array.to_param + + # Array of different objects + array = [1, '3', { a: 1, b: 2 }, nil, true, false, CustomString.new('object')] + assert_equal "1/3/a=1&b=2//true/false/custom-object", array.to_param + end end diff --git a/activesupport/test/core_ext/object/to_query_test.rb b/activesupport/test/core_ext/object/to_query_test.rb index 8d1a8c628c..09cab3ed35 100644 --- a/activesupport/test/core_ext/object/to_query_test.rb +++ b/activesupport/test/core_ext/object/to_query_test.rb @@ -46,8 +46,37 @@ class ToQueryTest < ActiveSupport::TestCase :person => {:id => [20, 10]} end + def test_empty_array + assert_equal "person%5B%5D=", [].to_query('person') + end + + def test_nested_empty_hash + assert_equal '', + {}.to_query + assert_query_equal 'a=1&b%5Bc%5D=3', + { a: 1, b: { c: 3, d: {} } } + assert_query_equal '', + { a: {b: {c: {}}} } + assert_query_equal 'b%5Bc%5D=false&b%5Be%5D=&b%5Bf%5D=&p=12', + { p: 12, b: { c: false, e: nil, f: '' } } + assert_query_equal 'b%5Bc%5D=3&b%5Bf%5D=', + { b: { c: 3, k: {}, f: '' } } + assert_query_equal 'b=3', + {a: [], b: 3} + end + + def test_hash_with_namespace + hash = { name: 'Nakshay', nationality: 'Indian' } + assert_equal "user%5Bname%5D=Nakshay&user%5Bnationality%5D=Indian", hash.to_query('user') + end + + def test_hash_sorted_lexicographically + hash = { type: 'human', name: 'Nakshay' } + assert_equal "name=Nakshay&type=human", hash.to_query + end + private - def assert_query_equal(expected, actual, message = nil) + def assert_query_equal(expected, actual) assert_equal expected.split('&'), actual.to_query.split('&') end end diff --git a/activesupport/test/core_ext/object/try_test.rb b/activesupport/test/core_ext/object/try_test.rb new file mode 100644 index 0000000000..89438675c1 --- /dev/null +++ b/activesupport/test/core_ext/object/try_test.rb @@ -0,0 +1,99 @@ +require 'abstract_unit' +require 'active_support/core_ext/object' + +class ObjectTryTest < ActiveSupport::TestCase + def setup + @string = "Hello" + end + + def test_nonexisting_method + method = :undefined_method + assert !@string.respond_to?(method) + assert_nil @string.try(method) + end + + def test_nonexisting_method_with_arguments + method = :undefined_method + assert !@string.respond_to?(method) + assert_nil @string.try(method, 'llo', 'y') + end + + def test_nonexisting_method_bang + method = :undefined_method + assert !@string.respond_to?(method) + assert_raise(NoMethodError) { @string.try!(method) } + end + + def test_nonexisting_method_with_arguments_bang + method = :undefined_method + assert !@string.respond_to?(method) + assert_raise(NoMethodError) { @string.try!(method, 'llo', 'y') } + end + + def test_valid_method + assert_equal 5, @string.try(:size) + end + + def test_argument_forwarding + assert_equal 'Hey', @string.try(:sub, 'llo', 'y') + end + + def test_block_forwarding + assert_equal 'Hey', @string.try(:sub, 'llo') { |match| 'y' } + end + + def test_nil_to_type + assert_nil nil.try(:to_s) + assert_nil nil.try(:to_i) + end + + def test_false_try + assert_equal 'false', false.try(:to_s) + end + + def test_try_only_block + assert_equal @string.reverse, @string.try(&:reverse) + end + + def test_try_only_block_bang + assert_equal @string.reverse, @string.try!(&:reverse) + end + + def test_try_only_block_nil + ran = false + nil.try { ran = true } + assert_equal false, ran + end + + def test_try_with_instance_eval_block + assert_equal @string.reverse, @string.try { reverse } + end + + def test_try_with_instance_eval_block_bang + assert_equal @string.reverse, @string.try! { reverse } + end + + def test_try_with_private_method_bang + klass = Class.new do + private + + def private_method + 'private method' + end + end + + assert_raise(NoMethodError) { klass.new.try!(:private_method) } + end + + def test_try_with_private_method + klass = Class.new do + private + + def private_method + 'private method' + end + end + + assert_nil klass.new.try(:private_method) + end +end diff --git a/activesupport/test/core_ext/object_and_class_ext_test.rb b/activesupport/test/core_ext/object_and_class_ext_test.rb deleted file mode 100644 index 8d748791e3..0000000000 --- a/activesupport/test/core_ext/object_and_class_ext_test.rb +++ /dev/null @@ -1,181 +0,0 @@ -require 'abstract_unit' -require 'active_support/time' -require 'active_support/core_ext/object' -require 'active_support/core_ext/class/subclasses' - -class ClassA; end -class ClassB < ClassA; end -class ClassC < ClassB; end -class ClassD < ClassA; end - -class ClassI; end -class ClassJ < ClassI; end - -class ClassK -end -module Nested - class << self - def on_const_missing(&callback) - @on_const_missing = callback - end - private - def const_missing(mod_id) - @on_const_missing[mod_id] if @on_const_missing - super - end - end - class ClassL < ClassK - end -end - -class ObjectTests < ActiveSupport::TestCase - class DuckTime - def acts_like_time? - true - end - end - - def test_duck_typing - object = Object.new - time = Time.now - date = Date.today - dt = DateTime.new - duck = DuckTime.new - - assert !object.acts_like?(:time) - assert !object.acts_like?(:date) - - assert time.acts_like?(:time) - assert !time.acts_like?(:date) - - assert !date.acts_like?(:time) - assert date.acts_like?(:date) - - assert dt.acts_like?(:time) - assert dt.acts_like?(:date) - - assert duck.acts_like?(:time) - assert !duck.acts_like?(:date) - end -end - -class ObjectInstanceVariableTest < ActiveSupport::TestCase - def setup - @source, @dest = Object.new, Object.new - @source.instance_variable_set(:@bar, 'bar') - @source.instance_variable_set(:@baz, 'baz') - end - - def test_instance_variable_names - assert_equal %w(@bar @baz), @source.instance_variable_names.sort - end - - def test_instance_values - object = Object.new - object.instance_variable_set :@a, 1 - object.instance_variable_set :@b, 2 - assert_equal({'a' => 1, 'b' => 2}, object.instance_values) - end - - def test_instance_exec_passes_arguments_to_block - assert_equal %w(hello goodbye), 'hello'.instance_exec('goodbye') { |v| [self, v] } - end - - def test_instance_exec_with_frozen_obj - assert_equal %w(olleh goodbye), 'hello'.freeze.instance_exec('goodbye') { |v| [reverse, v] } - end - - def test_instance_exec_nested - assert_equal %w(goodbye olleh bar), 'hello'.instance_exec('goodbye') { |arg| - [arg] + instance_exec('bar') { |v| [reverse, v] } } - end -end - -class ObjectTryTest < ActiveSupport::TestCase - def setup - @string = "Hello" - end - - def test_nonexisting_method - method = :undefined_method - assert !@string.respond_to?(method) - assert_nil @string.try(method) - end - - def test_nonexisting_method_with_arguments - method = :undefined_method - assert !@string.respond_to?(method) - assert_nil @string.try(method, 'llo', 'y') - end - - def test_nonexisting_method_bang - method = :undefined_method - assert !@string.respond_to?(method) - assert_raise(NoMethodError) { @string.try!(method) } - end - - def test_nonexisting_method_with_arguments_bang - method = :undefined_method - assert !@string.respond_to?(method) - assert_raise(NoMethodError) { @string.try!(method, 'llo', 'y') } - end - - def test_try_only_block_bang - assert_equal @string.reverse, @string.try! { |s| s.reverse } - end - - def test_valid_method - assert_equal 5, @string.try(:size) - end - - def test_argument_forwarding - assert_equal 'Hey', @string.try(:sub, 'llo', 'y') - end - - def test_block_forwarding - assert_equal 'Hey', @string.try(:sub, 'llo') { |match| 'y' } - end - - def test_nil_to_type - assert_nil nil.try(:to_s) - assert_nil nil.try(:to_i) - end - - def test_false_try - assert_equal 'false', false.try(:to_s) - end - - def test_try_only_block - assert_equal @string.reverse, @string.try { |s| s.reverse } - end - - def test_try_only_block_nil - ran = false - nil.try { ran = true } - assert_equal false, ran - end - - def test_try_with_private_method_bang - klass = Class.new do - private - - def private_method - 'private method' - end - end - - assert_raise(NoMethodError) { klass.new.try!(:private_method) } - end - - def test_try_with_private_method - klass = Class.new do - private - - def private_method - 'private method' - end - end - - assert_nil klass.new.try(:private_method) - end -end diff --git a/activesupport/test/core_ext/proc_test.rb b/activesupport/test/core_ext/proc_test.rb deleted file mode 100644 index c4d5592196..0000000000 --- a/activesupport/test/core_ext/proc_test.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'abstract_unit' -require 'active_support/core_ext/proc' - -class ProcTests < ActiveSupport::TestCase - def test_bind_returns_method_with_changed_self - assert_deprecated do - block = Proc.new { self } - assert_equal self, block.call - bound_block = block.bind("hello") - assert_not_equal block, bound_block - assert_equal "hello", bound_block.call - end - end -end diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb index 6e94d5e10d..f096328cee 100644 --- a/activesupport/test/core_ext/range_ext_test.rb +++ b/activesupport/test/core_ext/range_ext_test.rb @@ -1,7 +1,6 @@ require 'abstract_unit' require 'active_support/time' require 'active_support/core_ext/range' -require 'active_support/core_ext/numeric' class RangeTest < ActiveSupport::TestCase def test_to_s_from_dates @@ -13,7 +12,7 @@ class RangeTest < ActiveSupport::TestCase date_range = Time.utc(2005, 12, 10, 15, 30)..Time.utc(2005, 12, 10, 17, 30) assert_equal "BETWEEN '2005-12-10 15:30:00' AND '2005-12-10 17:30:00'", date_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 @@ -44,7 +43,7 @@ class RangeTest < ActiveSupport::TestCase assert((1...10).include?(1...10)) end - def test_should_include_other_with_exlusive_end + def test_should_include_other_with_exclusive_end assert((1..10).include?(1...10)) end @@ -56,7 +55,7 @@ class RangeTest < ActiveSupport::TestCase assert((1...10) === (1...10)) end - def test_should_compare_other_with_exlusive_end + def test_should_compare_other_with_exclusive_end assert((1..10) === (1...10)) end @@ -93,27 +92,34 @@ class RangeTest < ActiveSupport::TestCase assert !time_range_1.overlaps?(time_range_2) end - def test_infinite_bounds - time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + def test_each_on_time_with_zone + twz = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone['Eastern Time (US & Canada)'] , Time.utc(2006,11,28,10,30)) + assert_raises TypeError do + ((twz - 1.hour)..twz).each {} + end + end - time = Time.now - date = Date.today - datetime = DateTime.now - twz = ActiveSupport::TimeWithZone.new(time, time_zone) + def test_step_on_time_with_zone + twz = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone['Eastern Time (US & Canada)'] , Time.utc(2006,11,28,10,30)) + assert_raises TypeError do + ((twz - 1.hour)..twz).step(1) {} + end + end - infinity1 = Float::INFINITY - infinity2 = BigDecimal.new('Infinity') + def test_include_on_time_with_zone + twz = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone['Eastern Time (US & Canada)'] , Time.utc(2006,11,28,10,30)) + assert_raises TypeError do + ((twz - 1.hour)..twz).include?(twz) + end + end - [infinity1, infinity2].each do |infinity| - [time, date, datetime, twz].each do |bound| - [time, date, datetime, twz].each do |value| - assert Range.new(bound, infinity).include?(value + 10.years) - assert Range.new(-infinity, bound).include?(value - 10.years) + def test_date_time_with_each + datetime = DateTime.now + assert(((datetime - 1.hour)..datetime).each {}) + end - assert !Range.new(bound, infinity).include?(value - 10.years) - assert !Range.new(-infinity, bound).include?(value + 10.years) - end - end - end + def test_date_time_with_step + datetime = DateTime.now + assert(((datetime - 1.hour)..datetime).step(1) {}) end end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index 9b22a305c8..0af207fae9 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -10,17 +10,12 @@ require 'active_support/time' require 'active_support/core_ext/string/strip' require 'active_support/core_ext/string/output_safety' require 'active_support/core_ext/string/indent' - -module Ace - module Base - class Case - end - end -end +require 'time_zone_test_helpers' class StringInflectionsTest < ActiveSupport::TestCase include InflectorTestCases include ConstantizeTestCases + include TimeZoneTestHelpers def test_strip_heredoc_on_an_empty_string assert_equal '', ''.strip_heredoc @@ -65,6 +60,11 @@ class StringInflectionsTest < ActiveSupport::TestCase assert_equal("blargles", "blargle".pluralize(2)) end + test 'pluralize with count = 1 still returns new string' do + name = "Kuldeep" + assert_not_same name.pluralize(1), name + end + def test_singularize SingularToPlural.each do |singular, plural| assert_equal(singular, plural.singularize) @@ -162,59 +162,19 @@ class StringInflectionsTest < ActiveSupport::TestCase end end - def test_ord - assert_equal 97, 'a'.ord - assert_equal 97, 'abc'.ord + def test_humanize_without_capitalize + UnderscoreToHumanWithoutCapitalize.each do |underscore, human| + assert_equal(human, underscore.humanize(capitalize: false)) + end end - def test_access - s = "hello" - assert_equal "h", s.at(0) - - assert_equal "llo", s.from(2) - assert_equal "hel", s.to(2) - - assert_equal "h", s.first - assert_equal "he", s.first(2) - assert_equal "", s.first(0) - - assert_equal "o", s.last - assert_equal "llo", s.last(3) - assert_equal "hello", s.last(10) - assert_equal "", s.last(0) - - assert_equal 'x', 'x'.first - assert_equal 'x', 'x'.first(4) - - assert_equal 'x', 'x'.last - assert_equal 'x', 'x'.last(4) + def test_humanize_with_html_escape + assert_equal 'Hello', ERB::Util.html_escape("hello").humanize end - def test_access_returns_a_real_string - hash = {} - hash["h"] = true - hash["hello123".at(0)] = true - assert_equal %w(h), hash.keys - - hash = {} - hash["llo"] = true - hash["hello".from(2)] = true - assert_equal %w(llo), hash.keys - - hash = {} - hash["hel"] = true - hash["hello".to(2)] = true - assert_equal %w(hel), hash.keys - - hash = {} - hash["hello"] = true - hash["123hello".last(5)] = true - assert_equal %w(hello), hash.keys - - hash = {} - hash["hello"] = true - hash["hello123".first(5)] = true - assert_equal %w(hello), hash.keys + def test_ord + assert_equal 97, 'a'.ord + assert_equal 97, 'abc'.ord end def test_starts_ends_with_alias @@ -229,20 +189,21 @@ class StringInflectionsTest < ActiveSupport::TestCase end def test_string_squish - original = %{ A string with tabs(\t\t), newlines(\n\n), and - many spaces( ). } + original = %{\u205f\u3000 A string surrounded by various unicode spaces, + with tabs(\t\t), newlines(\n\n), unicode nextlines(\u0085\u0085) and many spaces( ). \u00a0\u2007} - expected = "A string with tabs( ), newlines( ), and many spaces( )." + expected = "A string surrounded by various unicode spaces, " + + "with tabs( ), newlines( ), unicode nextlines( ) and many spaces( )." # Make sure squish returns what we expect: - assert_equal original.squish, expected + assert_equal expected, original.squish # But doesn't modify the original string: - assert_not_equal original, expected + assert_not_equal expected, original # Make sure squish! returns what we expect: - assert_equal original.squish!, expected + assert_equal expected, original.squish! # And changes the original string: - assert_equal original, expected + assert_equal expected, original end def test_string_inquiry @@ -255,53 +216,192 @@ class StringInflectionsTest < ActiveSupport::TestCase assert_equal "Hello Wor...", "Hello World!!".truncate(12) end - def test_truncate_with_omission_and_seperator + def test_truncate_with_omission_and_separator assert_equal "Hello[...]", "Hello World!".truncate(10, :omission => "[...]") assert_equal "Hello[...]", "Hello Big World!".truncate(13, :omission => "[...]", :separator => ' ') assert_equal "Hello Big[...]", "Hello Big World!".truncate(14, :omission => "[...]", :separator => ' ') assert_equal "Hello Big[...]", "Hello Big World!".truncate(15, :omission => "[...]", :separator => ' ') end - def test_truncate_with_omission_and_regexp_seperator + def test_truncate_with_omission_and_regexp_separator assert_equal "Hello[...]", "Hello Big World!".truncate(13, :omission => "[...]", :separator => /\s/) assert_equal "Hello Big[...]", "Hello Big World!".truncate(14, :omission => "[...]", :separator => /\s/) assert_equal "Hello Big[...]", "Hello Big World!".truncate(15, :omission => "[...]", :separator => /\s/) end + def test_truncate_words + assert_equal "Hello Big World!", "Hello Big World!".truncate_words(3) + assert_equal "Hello Big...", "Hello Big World!".truncate_words(2) + end + + def test_truncate_words_with_omission + assert_equal "Hello Big World!", "Hello Big World!".truncate_words(3, :omission => "[...]") + assert_equal "Hello Big[...]", "Hello Big World!".truncate_words(2, :omission => "[...]") + end + + def test_truncate_words_with_separator + assert_equal "Hello<br>Big<br>World!...", "Hello<br>Big<br>World!<br>".truncate_words(3, :separator => '<br>') + assert_equal "Hello<br>Big<br>World!", "Hello<br>Big<br>World!".truncate_words(3, :separator => '<br>') + assert_equal "Hello\n<br>Big...", "Hello\n<br>Big<br>Wide<br>World!".truncate_words(2, :separator => '<br>') + end + + def test_truncate_words_with_separator_and_omission + assert_equal "Hello<br>Big<br>World![...]", "Hello<br>Big<br>World!<br>".truncate_words(3, :omission => "[...]", :separator => '<br>') + assert_equal "Hello<br>Big<br>World!", "Hello<br>Big<br>World!".truncate_words(3, :omission => "[...]", :separator => '<br>') + end + def test_truncate_multibyte - assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'), - "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8').truncate(10) + assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding(Encoding::UTF_8), + "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding(Encoding::UTF_8).truncate(10) end def test_truncate_should_not_be_html_safe assert !"Hello World!".truncate(12).html_safe? end + def test_remove + original = "This is a good day to die" + assert_equal "This is a good day", original.remove(" to die") + assert_equal "This is a good day", original.remove(" to ", /die/) + assert_equal "This is a good day to die", original + end + + def test_remove_for_multiple_occurrences + original = "This is a good day to die to die" + assert_equal "This is a good day", original.remove(" to die") + assert_equal "This is a good day to die to die", original + end + + def test_remove! + original = "This is a very good day to die" + assert_equal "This is a good day to die", original.remove!(" very") + assert_equal "This is a good day to die", original + assert_equal "This is a good day", original.remove!(" to ", /die/) + assert_equal "This is a good day", original + end + def test_constantize - run_constantize_tests_on do |string| - string.constantize - end + run_constantize_tests_on(&:constantize) end def test_safe_constantize - run_safe_constantize_tests_on do |string| - string.safe_constantize - end + run_safe_constantize_tests_on(&:safe_constantize) + end +end + +class StringAccessTest < ActiveSupport::TestCase + test "#at with Fixnum, returns a substring of one character at that position" do + assert_equal "h", "hello".at(0) + end + + test "#at with Range, returns a substring containing characters at offsets" do + assert_equal "lo", "hello".at(-2..-1) + end + + test "#at with Regex, returns the matching portion of the string" do + assert_equal "lo", "hello".at(/lo/) + assert_equal nil, "hello".at(/nonexisting/) + end + + test "#from with positive Fixnum, returns substring from the given position to the end" do + assert_equal "llo", "hello".from(2) + end + + test "#from with negative Fixnum, position is counted from the end" do + assert_equal "lo", "hello".from(-2) + end + + test "#to with positive Fixnum, substring from the beginning to the given position" do + assert_equal "hel", "hello".to(2) + end + + test "#to with negative Fixnum, position is counted from the end" do + assert_equal "hell", "hello".to(-2) + end + + test "#from and #to can be combined" do + assert_equal "hello", "hello".from(0).to(-1) + assert_equal "ell", "hello".from(1).to(-2) + end + + test "#first returns the first character" do + assert_equal "h", "hello".first + assert_equal 'x', 'x'.first + end + + test "#first with Fixnum, returns a substring from the beginning to position" do + assert_equal "he", "hello".first(2) + assert_equal "", "hello".first(0) + assert_equal "hello", "hello".first(10) + assert_equal 'x', 'x'.first(4) + end + + test "#first with Fixnum >= string length still returns a new string" do + string = "hello" + different_string = string.first(5) + assert_not_same different_string, string + end + + test "#last returns the last character" do + assert_equal "o", "hello".last + assert_equal 'x', 'x'.last + end + + test "#last with Fixnum, returns a substring from the end to position" do + assert_equal "llo", "hello".last(3) + assert_equal "hello", "hello".last(10) + assert_equal "", "hello".last(0) + assert_equal 'x', 'x'.last(4) + end + + test "#last with Fixnum >= string length still returns a new string" do + string = "hello" + different_string = string.last(5) + assert_not_same different_string, string + end + + test "access returns a real string" do + hash = {} + hash["h"] = true + hash["hello123".at(0)] = true + assert_equal %w(h), hash.keys + + hash = {} + hash["llo"] = true + hash["hello".from(2)] = true + assert_equal %w(llo), hash.keys + + hash = {} + hash["hel"] = true + hash["hello".to(2)] = true + assert_equal %w(hel), hash.keys + + hash = {} + hash["hello"] = true + hash["123hello".last(5)] = true + assert_equal %w(hello), hash.keys + + hash = {} + hash["hello"] = true + hash["hello123".first(5)] = true + assert_equal %w(hello), hash.keys end end class StringConversionsTest < ActiveSupport::TestCase + include TimeZoneTestHelpers + def test_string_to_time - with_env_tz "US/Eastern" do + with_env_tz "Europe/Moscow" do assert_equal Time.utc(2005, 2, 27, 23, 50), "2005-02-27 23:50".to_time(:utc) assert_equal Time.local(2005, 2, 27, 23, 50), "2005-02-27 23:50".to_time assert_equal Time.utc(2005, 2, 27, 23, 50, 19, 275038), "2005-02-27T23:50:19.275038".to_time(:utc) assert_equal Time.local(2005, 2, 27, 23, 50, 19, 275038), "2005-02-27T23:50:19.275038".to_time assert_equal Time.utc(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_time(:utc) assert_equal Time.local(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_time - assert_equal Time.local(2011, 2, 27, 18, 50), "2011-02-27 22:50 -0100".to_time + assert_equal Time.local(2011, 2, 27, 17, 50), "2011-02-27 13:50 -0100".to_time assert_equal Time.utc(2011, 2, 27, 23, 50), "2011-02-27 22:50 -0100".to_time(:utc) - assert_equal Time.local(2005, 2, 27, 23, 50), "2005-02-27 23:50 -0500".to_time + assert_equal Time.local(2005, 2, 27, 22, 50), "2005-02-27 14:50 -0500".to_time assert_nil "".to_time end end @@ -316,15 +416,131 @@ class StringConversionsTest < ActiveSupport::TestCase end def test_partial_string_to_time - with_env_tz "US/Eastern" do + with_env_tz "Europe/Moscow" do # use timezone which does not observe DST. now = Time.now assert_equal Time.local(now.year, now.month, now.day, 23, 50), "23:50".to_time assert_equal Time.utc(now.year, now.month, now.day, 23, 50), "23:50".to_time(:utc) - assert_equal Time.local(now.year, now.month, now.day, 18, 50), "22:50 -0100".to_time + assert_equal Time.local(now.year, now.month, now.day, 17, 50), "13:50 -0100".to_time assert_equal Time.utc(now.year, now.month, now.day, 23, 50), "22:50 -0100".to_time(:utc) end end + 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) + 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) + 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) + 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) + 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) + 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) + end + end + def test_string_to_datetime assert_equal DateTime.civil(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_datetime assert_equal 0, "2039-02-27 23:50".to_datetime.offset # use UTC offset @@ -344,14 +560,6 @@ class StringConversionsTest < ActiveSupport::TestCase assert_nil "".to_date assert_equal Date.new(Date.today.year, 2, 3), "Feb 3rd".to_date end - - protected - def with_env_tz(new_tz = 'US/Eastern') - old_tz, ENV['TZ'] = ENV['TZ'], new_tz - yield - ensure - old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') - end end class StringBehaviourTest < ActiveSupport::TestCase @@ -446,6 +654,29 @@ class OutputSafetyTest < ActiveSupport::TestCase assert !@other_combination.html_safe? end + test "Prepending safe onto unsafe yields unsafe" do + @string.prepend "other".html_safe + assert !@string.html_safe? + assert_equal @string, "otherhello" + end + + test "Prepending unsafe onto safe yields escaped safe" do + other = "other".html_safe + other.prepend "<foo>" + assert other.html_safe? + assert_equal other, "<foo>other" + end + + test "Deprecated #prepend! method is still present" do + other = "other".html_safe + + assert_deprecated do + other.prepend! "<foo>" + end + + assert_equal other, "<foo>other" + end + test "Concatting safe onto unsafe yields unsafe" do @other_string = "other" @@ -534,12 +765,6 @@ class OutputSafetyTest < ActiveSupport::TestCase assert_equal 'foo'.to_yaml, 'foo'.html_safe.to_yaml(:foo => 1) end - test 'knows whether it is encoding aware' do - assert_deprecated do - assert 'ruby'.encoding_aware? - end - end - test "call to_param returns a normal string" do string = @string.html_safe assert string.html_safe? @@ -562,6 +787,14 @@ class OutputSafetyTest < ActiveSupport::TestCase string = "<b>hello</b>".html_safe assert_equal string, ERB::Util.html_escape(string) end + + test "ERB::Util.html_escape_once only escapes once" do + string = '1 < 2 & 3' + escaped_string = "1 < 2 & 3" + + assert_equal escaped_string, ERB::Util.html_escape_once(string) + assert_equal escaped_string, ERB::Util.html_escape_once(escaped_string) + end end class StringExcludeTest < ActiveSupport::TestCase diff --git a/activesupport/test/core_ext/thread_test.rb b/activesupport/test/core_ext/thread_test.rb deleted file mode 100644 index 230c1203ad..0000000000 --- a/activesupport/test/core_ext/thread_test.rb +++ /dev/null @@ -1,77 +0,0 @@ -require 'abstract_unit' -require 'active_support/core_ext/thread' - -class ThreadExt < ActiveSupport::TestCase - def test_main_thread_variable_in_enumerator - assert_equal Thread.main, Thread.current - - Thread.current.thread_variable_set :foo, "bar" - - thread, value = Fiber.new { - Fiber.yield [Thread.current, Thread.current.thread_variable_get(:foo)] - }.resume - - assert_equal Thread.current, thread - assert_equal Thread.current.thread_variable_get(:foo), value - end - - def test_thread_variable_in_enumerator - Thread.new { - Thread.current.thread_variable_set :foo, "bar" - - thread, value = Fiber.new { - Fiber.yield [Thread.current, Thread.current.thread_variable_get(:foo)] - }.resume - - assert_equal Thread.current, thread - assert_equal Thread.current.thread_variable_get(:foo), value - }.join - end - - def test_thread_variables - assert_equal [], Thread.new { Thread.current.thread_variables }.join.value - - t = Thread.new { - Thread.current.thread_variable_set(:foo, "bar") - Thread.current.thread_variables - } - assert_equal [:foo], t.join.value - end - - def test_thread_variable? - assert_not Thread.new { Thread.current.thread_variable?("foo") }.join.value - t = Thread.new { - Thread.current.thread_variable_set("foo", "bar") - }.join - - assert t.thread_variable?("foo") - assert t.thread_variable?(:foo) - assert_not t.thread_variable?(:bar) - end - - def test_thread_variable_strings_and_symbols_are_the_same_key - t = Thread.new {}.join - t.thread_variable_set("foo", "bar") - assert_equal "bar", t.thread_variable_get(:foo) - end - - def test_thread_variable_frozen - t = Thread.new { }.join - t.freeze - assert_raises(RuntimeError) do - t.thread_variable_set(:foo, "bar") - end - end - - def test_thread_variable_security - t = Thread.new { sleep } - - assert_raises(SecurityError) do - Thread.new { $SAFE = 4; t.thread_variable_get(:foo) }.join - end - - assert_raises(SecurityError) do - Thread.new { $SAFE = 4; t.thread_variable_set(:foo, :baz) }.join - end - end -end diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index 43c92003dc..d59775001b 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -1,6 +1,7 @@ require 'abstract_unit' require 'active_support/time' require 'core_ext/date_and_time_behavior' +require 'time_zone_test_helpers' class TimeExtCalculationsTest < ActiveSupport::TestCase def date_time_init(year,month,day,hour,minute,second,usec=0) @@ -8,6 +9,7 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase end include DateAndTimeBehavior + include TimeZoneTestHelpers def test_seconds_since_midnight assert_equal 1,Time.local(2005,1,1,0,0,1).seconds_since_midnight @@ -117,10 +119,26 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase end end + def test_middle_of_day + assert_equal Time.local(2005,2,4,12,0,0), Time.local(2005,2,4,10,10,10).middle_of_day + with_env_tz 'US/Eastern' do + assert_equal Time.local(2006,4,2,12,0,0), Time.local(2006,4,2,10,10,10).middle_of_day, 'start DST' + assert_equal Time.local(2006,10,29,12,0,0), Time.local(2006,10,29,10,10,10).middle_of_day, 'ends DST' + end + with_env_tz 'NZ' do + assert_equal Time.local(2006,3,19,12,0,0), Time.local(2006,3,19,10,10,10).middle_of_day, 'ends DST' + assert_equal Time.local(2006,10,1,12,0,0), Time.local(2006,10,1,10,10,10).middle_of_day, 'start DST' + end + end + def test_beginning_of_hour assert_equal Time.local(2005,2,4,19,0,0), Time.local(2005,2,4,19,30,10).beginning_of_hour end + def test_beginning_of_minute + assert_equal Time.local(2005,2,4,19,30,0), Time.local(2005,2,4,19,30,10).beginning_of_minute + end + def test_end_of_day assert_equal Time.local(2007,8,12,23,59,59,Rational(999999999, 1000)), Time.local(2007,8,12,10,10,10).end_of_day with_env_tz 'US/Eastern' do @@ -137,6 +155,10 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal Time.local(2005,2,4,19,59,59,Rational(999999999, 1000)), Time.local(2005,2,4,19,30,10).end_of_hour end + def test_end_of_minute + assert_equal Time.local(2005,2,4,19,30,59,Rational(999999999, 1000)), Time.local(2005,2,4,19,30,10).end_of_minute + end + def test_last_year assert_equal Time.local(2004,6,5,10), Time.local(2005,6,5,10,0,0).last_year end @@ -365,6 +387,8 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal Time.local(2005,1,2,11, 6, 0, 0), Time.local(2005,1,2,11,22,33,44).change(:min => 6) assert_equal Time.local(2005,1,2,11,22, 7, 0), Time.local(2005,1,2,11,22,33,44).change(:sec => 7) 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) } end def test_utc_change @@ -374,6 +398,7 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal Time.utc(2005,2,22,16), Time.utc(2005,2,22,15,15,10).change(:hour => 16) assert_equal Time.utc(2005,2,22,16,45), Time.utc(2005,2,22,15,15,10).change(:hour => 16, :min => 45) assert_equal Time.utc(2005,2,22,15,45), Time.utc(2005,2,22,15,15,10).change(:min => 45) + assert_equal Time.utc(2005,1,2,11,22,33,8), Time.utc(2005,1,2,11,22,33,2).change(:nsec => 8000) end def test_offset_change @@ -383,6 +408,11 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal Time.new(2005,2,22,16,0,0,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").change(:hour => 16) assert_equal Time.new(2005,2,22,16,45,0,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").change(:hour => 16, :min => 45) assert_equal Time.new(2005,2,22,15,45,0,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").change(:min => 45) + assert_equal Time.new(2005,2,22,15,15,10,"-08:00"), Time.new(2005,2,22,15,15,0,"-08:00").change(:sec => 10) + assert_equal 10, Time.new(2005,2,22,15,15,0,"-08:00").change(:usec => 10).usec + assert_equal 10, Time.new(2005,2,22,15,15,0,"-08:00").change(:nsec => 10).nsec + assert_raise(ArgumentError) { Time.new(2005, 2, 22, 15, 15, 45, "-08:00").change(:usec => 1000000) } + assert_raise(ArgumentError) { Time.new(2005, 2, 22, 15, 15, 45, "-08:00").change(:nsec => 1000000000) } end def test_advance @@ -456,6 +486,13 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal t, t.advance(:months => 0) end + def test_advance_gregorian_proleptic + assert_equal Time.local(1582,10,14,15,15,10), Time.local(1582,10,15,15,15,10).advance(:days => -1) + assert_equal Time.local(1582,10,15,15,15,10), Time.local(1582,10,14,15,15,10).advance(:days => 1) + assert_equal Time.local(1582,10,5,15,15,10), Time.local(1582,10,4,15,15,10).advance(:days => 1) + assert_equal Time.local(1582,10,4,15,15,10), Time.local(1582,10,5,15,15,10).advance(:days => -1) + end + def test_last_week with_env_tz 'US/Eastern' do assert_equal Time.local(2005,2,21), Time.local(2005,3,1,15,15,10).last_week @@ -501,6 +538,9 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase with_env_tz "US/Central" do assert_equal "Thu, 05 Feb 2009 14:30:05 -0600", Time.local(2009, 2, 5, 14, 30, 5).to_s(:rfc822) assert_equal "Mon, 09 Jun 2008 04:05:01 -0500", Time.local(2008, 6, 9, 4, 5, 1).to_s(:rfc822) + assert_equal "2009-02-05T14:30:05-06:00", Time.local(2009, 2, 5, 14, 30, 5).to_s(:iso8601) + assert_equal "2008-06-09T04:05:01-05:00", Time.local(2008, 6, 9, 4, 5, 1).to_s(:iso8601) + assert_equal "2009-02-05T14:30:05Z", Time.utc(2009, 2, 5, 14, 30, 5).to_s(:iso8601) end end @@ -570,58 +610,6 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal 29, Time.days_in_month(2) end - def test_time_with_datetime_fallback - ActiveSupport::Deprecation.silence do - assert_equal Time.time_with_datetime_fallback(:utc, 2005, 2, 21, 17, 44, 30), Time.utc(2005, 2, 21, 17, 44, 30) - assert_equal Time.time_with_datetime_fallback(:local, 2005, 2, 21, 17, 44, 30), Time.local(2005, 2, 21, 17, 44, 30) - assert_equal Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30), DateTime.civil(2039, 2, 21, 17, 44, 30, 0) - assert_equal Time.time_with_datetime_fallback(:local, 2039, 2, 21, 17, 44, 30), DateTime.civil_from_format(:local, 2039, 2, 21, 17, 44, 30) - assert_equal Time.time_with_datetime_fallback(:utc, 1900, 2, 21, 17, 44, 30), DateTime.civil(1900, 2, 21, 17, 44, 30, 0) - assert_equal Time.time_with_datetime_fallback(:utc, 2005), Time.utc(2005) - assert_equal Time.time_with_datetime_fallback(:utc, 2039), DateTime.civil(2039, 1, 1, 0, 0, 0, 0) - assert_equal Time.time_with_datetime_fallback(:utc, 2005, 2, 21, 17, 44, 30, 1), Time.utc(2005, 2, 21, 17, 44, 30, 1) #with usec - # This won't overflow on 64bit linux - unless time_is_64bits? - assert_equal Time.time_with_datetime_fallback(:local, 1900, 2, 21, 17, 44, 30), DateTime.civil_from_format(:local, 1900, 2, 21, 17, 44, 30) - assert_equal Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1), - DateTime.civil(2039, 2, 21, 17, 44, 30, 0, 0) - assert_equal ::Date::ITALY, Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1).start # use Ruby's default start value - end - silence_warnings do - 0.upto(138) do |year| - [:utc, :local].each do |format| - assert_equal year, Time.time_with_datetime_fallback(format, year).year - end - end - end - end - end - - def test_utc_time - ActiveSupport::Deprecation.silence do - assert_equal Time.utc_time(2005, 2, 21, 17, 44, 30), Time.utc(2005, 2, 21, 17, 44, 30) - assert_equal Time.utc_time(2039, 2, 21, 17, 44, 30), DateTime.civil(2039, 2, 21, 17, 44, 30, 0) - assert_equal Time.utc_time(1901, 2, 21, 17, 44, 30), DateTime.civil(1901, 2, 21, 17, 44, 30, 0) - end - end - - def test_local_time - ActiveSupport::Deprecation.silence do - assert_equal Time.local_time(2005, 2, 21, 17, 44, 30), Time.local(2005, 2, 21, 17, 44, 30) - assert_equal Time.local_time(2039, 2, 21, 17, 44, 30), DateTime.civil_from_format(:local, 2039, 2, 21, 17, 44, 30) - - unless time_is_64bits? - assert_equal Time.local_time(1901, 2, 21, 17, 44, 30), DateTime.civil_from_format(:local, 1901, 2, 21, 17, 44, 30) - end - end - end - - def test_time_with_datetime_fallback_deprecations - assert_deprecated(/time_with_datetime_fallback/) { Time.time_with_datetime_fallback(:utc, 2012, 6, 7) } - assert_deprecated(/utc_time/) { Time.utc_time(2012, 6, 7) } - assert_deprecated(/local_time/) { Time.local_time(2012, 6, 7) } - end - def test_last_month_on_31st assert_equal Time.local(2004, 2, 29), Time.local(2004, 3, 31).last_month end @@ -733,6 +721,82 @@ 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_at_with_datetime + assert_equal Time.utc(2000, 1, 1, 0, 0, 0), Time.at(DateTime.civil(2000, 1, 1, 0, 0, 0)) + + # Only test this if the underlying Time.at raises a TypeError + begin + Time.at_without_coercion(Time.now, 0) + rescue TypeError + assert_raise(TypeError) { assert_equal(Time.utc(2000, 1, 1, 0, 0, 0), Time.at(DateTime.civil(2000, 1, 1, 0, 0, 0), 0)) } + end + end + + def test_at_with_datetime_returns_local_time + with_env_tz 'US/Eastern' do + dt = DateTime.civil(2000, 1, 1, 0, 0, 0, '+0') + assert_equal Time.local(1999, 12, 31, 19, 0, 0), Time.at(dt) + assert_equal 'EST', Time.at(dt).zone + assert_equal(-18000, Time.at(dt).utc_offset) + + # Daylight savings + dt = DateTime.civil(2000, 7, 1, 1, 0, 0, '+1') + assert_equal Time.local(2000, 6, 30, 20, 0, 0), Time.at(dt) + assert_equal 'EDT', Time.at(dt).zone + assert_equal(-14400, Time.at(dt).utc_offset) + end + end + + def test_at_with_time_with_zone + assert_equal Time.utc(2000, 1, 1, 0, 0, 0), Time.at(ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone['UTC'])) + + # Only test this if the underlying Time.at raises a TypeError + begin + Time.at_without_coercion(Time.now, 0) + rescue TypeError + assert_raise(TypeError) { assert_equal(Time.utc(2000, 1, 1, 0, 0, 0), Time.at(ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone['UTC']), 0)) } + end + end + + def test_at_with_time_with_zone_returns_local_time + with_env_tz 'US/Eastern' do + twz = ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone['London']) + assert_equal Time.local(1999, 12, 31, 19, 0, 0), Time.at(twz) + assert_equal 'EST', Time.at(twz).zone + assert_equal(-18000, Time.at(twz).utc_offset) + + # Daylight savings + twz = ActiveSupport::TimeWithZone.new(Time.utc(2000, 7, 1, 0, 0, 0), ActiveSupport::TimeZone['London']) + assert_equal Time.local(2000, 6, 30, 20, 0, 0), Time.at(twz) + assert_equal 'EDT', Time.at(twz).zone + assert_equal(-14400, Time.at(twz).utc_offset) + end + end + + def test_at_with_time_microsecond_precision + assert_equal Time.at(Time.utc(2000, 1, 1, 0, 0, 0, 111)).to_f, Time.utc(2000, 1, 1, 0, 0, 0, 111).to_f + end + + def test_at_with_utc_time + with_env_tz 'US/Eastern' do + assert_equal Time.utc(2000), Time.at(Time.utc(2000)) + assert_equal 'UTC', Time.at(Time.utc(2000)).zone + assert_equal(0, Time.at(Time.utc(2000)).utc_offset) + end + end + + def test_at_with_local_time + with_env_tz 'US/Eastern' do + assert_equal Time.local(2000), Time.at(Time.local(2000)) + assert_equal 'EST', Time.at(Time.local(2000)).zone + assert_equal(-18000, Time.at(Time.local(2000)).utc_offset) + + assert_equal Time.local(2000, 7, 1), Time.at(Time.local(2000, 7, 1)) + assert_equal 'EDT', Time.at(Time.local(2000, 7, 1)).zone + assert_equal(-14400, Time.at(Time.local(2000, 7, 1)).utc_offset) + end + end + def test_eql? assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['UTC']) ) assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) ) @@ -793,18 +857,6 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase def test_all_year assert_equal Time.local(2011,1,1,0,0,0)..Time.local(2011,12,31,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_year end - - protected - def with_env_tz(new_tz = 'US/Eastern') - old_tz, ENV['TZ'] = ENV['TZ'], new_tz - yield - ensure - old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') - end - - def time_is_64bits? - Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1).is_a?(Time) - end end class TimeExtMarshalingTest < ActiveSupport::TestCase @@ -847,10 +899,3 @@ class TimeExtMarshalingTest < ActiveSupport::TestCase assert_equal Time.local(2004, 2, 29), Time.local(2004, 5, 31).last_quarter end end - -class TimeExtBehaviorTest < ActiveSupport::TestCase - def test_compare_with_infinity - assert_equal(-1, Time.now <=> Float::INFINITY) - assert_equal(1, Time.now <=> -Float::INFINITY) - end -end diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index 18eca4cd8b..92c233d567 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -1,8 +1,9 @@ require 'abstract_unit' require 'active_support/time' -require 'active_support/json' +require 'time_zone_test_helpers' class TimeWithZoneTest < ActiveSupport::TestCase + include TimeZoneTestHelpers def setup @utc = Time.utc(2000, 1, 1, 0) @@ -66,20 +67,6 @@ class TimeWithZoneTest < ActiveSupport::TestCase assert_equal 'EDT', ActiveSupport::TimeWithZone.new(Time.utc(2000, 6), @time_zone).zone #dst end - def test_to_json_with_use_standard_json_time_format_config_set_to_false - old, ActiveSupport.use_standard_json_time_format = ActiveSupport.use_standard_json_time_format, false - assert_equal "\"1999/12/31 19:00:00 -0500\"", ActiveSupport::JSON.encode(@twz) - ensure - ActiveSupport.use_standard_json_time_format = old - end - - def test_to_json_with_use_standard_json_time_format_config_set_to_true - old, ActiveSupport.use_standard_json_time_format = ActiveSupport.use_standard_json_time_format, true - assert_equal "\"1999-12-31T19:00:00-05:00\"", ActiveSupport::JSON.encode(@twz) - ensure - ActiveSupport.use_standard_json_time_format = old - end - def test_nsec local = Time.local(2011,6,7,23,59,59,Rational(999999999, 1000)) with_zone = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Hawaii"], local) @@ -92,6 +79,11 @@ class TimeWithZoneTest < ActiveSupport::TestCase assert_equal '1999-12-31 19:00:00 EST -0500', @twz.strftime('%Y-%m-%d %H:%M:%S %Z %z') end + def test_strftime_with_escaping + assert_equal '%Z %z', @twz.strftime('%%Z %%z') + assert_equal '%EST %-0500', @twz.strftime('%%%Z %%%z') + end + def test_inspect assert_equal 'Fri, 31 Dec 1999 19:00:00 EST -05:00', @twz.inspect end @@ -126,6 +118,10 @@ class TimeWithZoneTest < ActiveSupport::TestCase assert_equal "1999-12-31T19:00:00.001234-05:00", @twz.xmlschema(12) end + def test_xmlschema_with_nil_fractional_seconds + assert_equal "1999-12-31T19:00:00-05:00", @twz.xmlschema(nil) + end + def test_to_yaml assert_match(/^--- 2000-01-01 00:00:00(\.0+)?\s*Z\n/, @twz.to_yaml) end @@ -257,16 +253,31 @@ class TimeWithZoneTest < ActiveSupport::TestCase assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), ActiveSupport::TimeZone['Hawaii'] ) - Time.utc(2000, 1, 1) end + def test_minus_with_time_precision + assert_equal 86_399.999999998, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2, 23, 59, 59, Rational(999999999, 1000)), ActiveSupport::TimeZone['UTC'] ) - Time.utc(2000, 1, 2, 0, 0, 0, Rational(1, 1000)) + assert_equal 86_399.999999998, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2, 23, 59, 59, Rational(999999999, 1000)), ActiveSupport::TimeZone['Hawaii'] ) - Time.utc(2000, 1, 2, 0, 0, 0, Rational(1, 1000)) + end + def test_minus_with_time_with_zone twz1 = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1), ActiveSupport::TimeZone['UTC'] ) twz2 = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), ActiveSupport::TimeZone['UTC'] ) assert_equal 86_400.0, twz2 - twz1 end + def test_minus_with_time_with_zone_precision + twz1 = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 0, Rational(1, 1000)), ActiveSupport::TimeZone['UTC'] ) + twz2 = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 23, 59, 59, Rational(999999999, 1000)), ActiveSupport::TimeZone['UTC'] ) + assert_equal 86_399.999999998, twz2 - twz1 + end + def test_minus_with_datetime assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), ActiveSupport::TimeZone['UTC'] ) - DateTime.civil(2000, 1, 1) end + def test_minus_with_datetime_precision + assert_equal 86_399.999999999, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 23, 59, 59, Rational(999999999, 1000)), ActiveSupport::TimeZone['UTC'] ) - DateTime.civil(2000, 1, 1) + end + def test_minus_with_wrapped_datetime assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( DateTime.civil(2000, 1, 2), ActiveSupport::TimeZone['UTC'] ) - Time.utc(2000, 1, 1) assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( DateTime.civil(2000, 1, 2), ActiveSupport::TimeZone['UTC'] ) - DateTime.civil(2000, 1, 1) @@ -326,6 +337,17 @@ class TimeWithZoneTest < ActiveSupport::TestCase assert_equal 946684800, twz.to_i end + def test_to_r + result = ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1), ActiveSupport::TimeZone['Hawaii']).to_r + assert_equal Rational(946684800, 1), result + assert_kind_of Rational, result + end + + def test_time_at + time = ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1), ActiveSupport::TimeZone['Hawaii']) + assert_equal time, Time.at(time) + end + def test_to_time with_env_tz 'US/Eastern' do assert_equal Time, @twz.to_time.class @@ -350,6 +372,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase end def test_acts_like_time + assert @twz.acts_like_time? assert @twz.acts_like?(:time) assert ActiveSupport::TimeWithZone.new(DateTime.civil(2000), @time_zone).acts_like?(:time) end @@ -434,6 +457,16 @@ class TimeWithZoneTest < ActiveSupport::TestCase assert_equal 0, twz.usec end + def test_usec_returns_sec_fraction_when_datetime_is_wrapped + twz = ActiveSupport::TimeWithZone.new(DateTime.civil(2000, 1, 1, 0, 0, Rational(1,2)), @time_zone) + assert_equal 500000, twz.usec + end + + def test_nsec_returns_sec_fraction_when_datetime_is_wrapped + twz = ActiveSupport::TimeWithZone.new(DateTime.civil(2000, 1, 1, 0, 0, Rational(1,2)), @time_zone) + assert_equal 500000000, twz.nsec + end + def test_utc_to_local_conversion_saves_period_in_instance_variable assert_nil @twz.instance_variable_get('@period') @twz.time @@ -485,6 +518,16 @@ class TimeWithZoneTest < ActiveSupport::TestCase assert_equal "Fri, 31 Dec 1999 19:00:30 EST -05:00", @twz.change(:sec => 30).inspect end + def test_change_at_dst_boundary + twz = ActiveSupport::TimeWithZone.new(Time.at(1319936400).getutc, ActiveSupport::TimeZone['Madrid']) + assert_equal twz, twz.change(:min => 0) + end + + def test_round_at_dst_boundary + twz = ActiveSupport::TimeWithZone.new(Time.at(1319936400).getutc, ActiveSupport::TimeZone['Madrid']) + assert_equal twz, twz.round + end + def test_advance assert_equal "Fri, 31 Dec 1999 19:00:00 EST -05:00", @twz.inspect assert_equal "Mon, 31 Dec 2001 19:00:00 EST -05:00", @twz.advance(:years => 2).inspect @@ -539,6 +582,20 @@ class TimeWithZoneTest < ActiveSupport::TestCase assert_equal "Fri, 31 Dec 1999 19:59:59 EST -05:00", twz.end_of_hour.inspect end + def test_beginning_of_minute + utc = Time.utc(2000, 1, 1, 0, 30, 10) + twz = ActiveSupport::TimeWithZone.new(utc, @time_zone) + assert_equal "Fri, 31 Dec 1999 19:30:10 EST -05:00", twz.inspect + assert_equal "Fri, 31 Dec 1999 19:00:00 EST -05:00", twz.beginning_of_hour.inspect + end + + def test_end_of_minute + utc = Time.utc(2000, 1, 1, 0, 30, 10) + twz = ActiveSupport::TimeWithZone.new(utc, @time_zone) + assert_equal "Fri, 31 Dec 1999 19:30:10 EST -05:00", twz.inspect + assert_equal "Fri, 31 Dec 1999 19:30:59 EST -05:00", twz.end_of_minute.inspect + end + def test_since assert_equal "Fri, 31 Dec 1999 19:00:01 EST -05:00", @twz.since(1).inspect end @@ -754,22 +811,26 @@ class TimeWithZoneTest < ActiveSupport::TestCase assert_equal "Sun, 15 Jul 2007 10:30:00 EDT -04:00", (twz - 1.year).inspect end - protected - def with_env_tz(new_tz = 'US/Eastern') - old_tz, ENV['TZ'] = ENV['TZ'], new_tz - yield - ensure - old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') - end + def test_no_method_error_has_proper_context + rubinius_skip "Error message inconsistency" + + e = assert_raises(NoMethodError) { + @twz.this_method_does_not_exist + } + assert_equal "undefined method `this_method_does_not_exist' for Fri, 31 Dec 1999 19:00:00 EST -05:00:Time", e.message + assert_no_match "rescue", e.backtrace.first + end end class TimeWithZoneMethodsForTimeAndDateTimeTest < ActiveSupport::TestCase + include TimeZoneTestHelpers + def setup - @t, @dt = Time.utc(2000), DateTime.civil(2000) + @t, @dt, @zone = Time.utc(2000), DateTime.civil(2000), Time.zone end def teardown - Time.zone = nil + Time.zone = @zone end def test_in_time_zone @@ -825,8 +886,6 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < ActiveSupport::TestCase def test_localtime Time.zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] assert_equal @dt.in_time_zone.localtime, @dt.in_time_zone.utc.to_time.getlocal - ensure - Time.zone = nil end def test_use_zone @@ -865,6 +924,7 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < ActiveSupport::TestCase end def test_time_zone_getter_and_setter_with_zone_default_set + old_zone_default = Time.zone_default Time.zone_default = ActiveSupport::TimeZone['Alaska'] assert_equal ActiveSupport::TimeZone['Alaska'], Time.zone Time.zone = ActiveSupport::TimeZone['Hawaii'] @@ -872,8 +932,7 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < ActiveSupport::TestCase Time.zone = nil assert_equal ActiveSupport::TimeZone['Alaska'], Time.zone ensure - Time.zone = nil - Time.zone_default = nil + Time.zone_default = old_zone_default end def test_time_zone_setter_is_thread_safe @@ -945,28 +1004,25 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < ActiveSupport::TestCase assert_equal 'Eastern Time (US & Canada)', Time.current.time_zone.name assert_equal Time.utc(2000), Time.current.time end - ensure - Time.zone = nil end - protected - def with_env_tz(new_tz = 'US/Eastern') - old_tz, ENV['TZ'] = ENV['TZ'], new_tz - yield - ensure - old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') + def test_time_in_time_zone_doesnt_affect_receiver + with_env_tz 'Europe/London' do + time = Time.local(2000, 7, 1) + time_with_zone = time.in_time_zone('Eastern Time (US & Canada)') + assert_equal Time.utc(2000, 6, 30, 23, 0, 0), time_with_zone + assert_not time.utc?, 'time expected to be local, but is UTC' end + end end class TimeWithZoneMethodsForDate < ActiveSupport::TestCase + include TimeZoneTestHelpers + def setup @d = Date.civil(2000) end - def teardown - Time.zone = nil - end - def test_in_time_zone with_tz_default 'Alaska' do assert_equal 'Sat, 01 Jan 2000 00:00:00 AKST -09:00', @d.in_time_zone.inspect @@ -999,28 +1055,17 @@ class TimeWithZoneMethodsForDate < ActiveSupport::TestCase assert_raise(ArgumentError) { @d.in_time_zone(-15.hours) } assert_raise(ArgumentError) { @d.in_time_zone(Object.new) } end - - protected - def with_tz_default(tz = nil) - old_tz = Time.zone - Time.zone = tz - yield - ensure - Time.zone = old_tz - end end class TimeWithZoneMethodsForString < ActiveSupport::TestCase + include TimeZoneTestHelpers + def setup @s = "Sat, 01 Jan 2000 00:00:00" @u = "Sat, 01 Jan 2000 00:00:00 UTC +00:00" @z = "Fri, 31 Dec 1999 19:00:00 EST -05:00" end - def teardown - Time.zone = nil - end - def test_in_time_zone with_tz_default 'Alaska' do assert_equal 'Sat, 01 Jan 2000 00:00:00 AKST -09:00', @s.in_time_zone.inspect @@ -1075,23 +1120,4 @@ class TimeWithZoneMethodsForString < ActiveSupport::TestCase assert_raise(ArgumentError) { @u.in_time_zone(Object.new) } assert_raise(ArgumentError) { @z.in_time_zone(Object.new) } end - - protected - def with_tz_default(tz = nil) - old_tz = Time.zone - Time.zone = tz - yield - ensure - Time.zone = old_tz - end -end - -class TimeWithZoneExtBehaviorTest < ActiveSupport::TestCase - def test_compare_with_infinity - time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] - twz = ActiveSupport::TimeWithZone.new(Time.now, time_zone) - - assert_equal(-1, twz <=> Float::INFINITY) - assert_equal(1, twz <=> -Float::INFINITY) - end end diff --git a/activesupport/test/core_ext/uri_ext_test.rb b/activesupport/test/core_ext/uri_ext_test.rb index 03e388dd7a..43a5997ddd 100644 --- a/activesupport/test/core_ext/uri_ext_test.rb +++ b/activesupport/test/core_ext/uri_ext_test.rb @@ -7,7 +7,7 @@ class URIExtTest < ActiveSupport::TestCase def test_uri_decode_handle_multibyte str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese. - parser = URI::Parser.new + parser = URI.parser assert_equal str, parser.unescape(parser.escape(str)) end end diff --git a/activesupport/test/dependencies/raises_exception_without_blame_file.rb b/activesupport/test/dependencies/raises_exception_without_blame_file.rb new file mode 100644 index 0000000000..4b2da6ff30 --- /dev/null +++ b/activesupport/test/dependencies/raises_exception_without_blame_file.rb @@ -0,0 +1,5 @@ +exception = Exception.new('I am not blamable!') +class << exception + undef_method(:blame_file!) +end +raise exception diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index 615808090d..d780acf5f9 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -1,7 +1,7 @@ require 'abstract_unit' require 'pp' require 'active_support/dependencies' -require 'dependecies_test_helpers' +require 'dependencies_test_helpers' module ModuleWithMissing mattr_accessor :missing_count @@ -16,11 +16,16 @@ module ModuleWithConstant end class DependenciesTest < ActiveSupport::TestCase - def teardown - ActiveSupport::Dependencies.clear + include DependenciesTestHelpers + + setup do + @loaded_features_copy = $LOADED_FEATURES.dup end - include DependeciesTestHelpers + teardown do + ActiveSupport::Dependencies.clear + $LOADED_FEATURES.replace(@loaded_features_copy) + end def test_depend_on_path skip "LoadError#path does not exist" if RUBY_VERSION < '2.0.0' @@ -35,25 +40,39 @@ class DependenciesTest < ActiveSupport::TestCase assert_equal expected.path, e.path end + def test_require_dependency_accepts_an_object_which_implements_to_path + o = Object.new + def o.to_path; 'dependencies/service_one'; end + assert_nothing_raised { + require_dependency o + } + assert defined?(ServiceOne) + ensure + remove_constants(:ServiceOne) + end + def test_tracking_loaded_files - require_dependency 'dependencies/service_one' - require_dependency 'dependencies/service_two' - assert_equal 2, ActiveSupport::Dependencies.loaded.size + with_loading do + require_dependency 'dependencies/service_one' + require_dependency 'dependencies/service_two' + assert_equal 2, ActiveSupport::Dependencies.loaded.size + end ensure - Object.send(:remove_const, :ServiceOne) if Object.const_defined?(:ServiceOne) - Object.send(:remove_const, :ServiceTwo) if Object.const_defined?(:ServiceTwo) + remove_constants(:ServiceOne, :ServiceTwo) end def test_tracking_identical_loaded_files - require_dependency 'dependencies/service_one' - require_dependency 'dependencies/service_one' - assert_equal 1, ActiveSupport::Dependencies.loaded.size + with_loading do + require_dependency 'dependencies/service_one' + require_dependency 'dependencies/service_one' + assert_equal 1, ActiveSupport::Dependencies.loaded.size + end ensure - Object.send(:remove_const, :ServiceOne) if Object.const_defined?(:ServiceOne) + remove_constants(:ServiceOne) end def test_missing_dependency_raises_missing_source_file - assert_raise(MissingSourceFile) { require_dependency("missing_service") } + assert_raise(LoadError) { require_dependency("missing_service") } end def test_dependency_which_raises_exception_isnt_added_to_loaded_set @@ -62,30 +81,35 @@ class DependenciesTest < ActiveSupport::TestCase $raises_exception_load_count = 0 5.times do |count| - begin + e = assert_raise Exception, 'should have loaded dependencies/raises_exception which raises an exception' do require_dependency filename - flunk 'should have loaded dependencies/raises_exception which raises an exception' - rescue Exception => e - assert_equal 'Loading me failed, so do not add to loaded or history.', e.message end + + assert_equal 'Loading me failed, so do not add to loaded or history.', e.message assert_equal count + 1, $raises_exception_load_count - assert !ActiveSupport::Dependencies.loaded.include?(filename) - assert !ActiveSupport::Dependencies.history.include?(filename) + assert_not ActiveSupport::Dependencies.loaded.include?(filename) + assert_not ActiveSupport::Dependencies.history.include?(filename) end end end + def test_dependency_which_raises_doesnt_blindly_call_blame_file! + with_loading do + filename = 'dependencies/raises_exception_without_blame_file' + assert_raises(Exception) { require_dependency filename } + end + end + def test_warnings_should_be_enabled_on_first_load with_loading 'dependencies' do old_warnings, ActiveSupport::Dependencies.warnings_on_first_load = ActiveSupport::Dependencies.warnings_on_first_load, true - filename = "check_warnings" expanded = File.expand_path("#{File.dirname(__FILE__)}/dependencies/#{filename}") $check_warnings_load_count = 0 - assert !ActiveSupport::Dependencies.loaded.include?(expanded) - assert !ActiveSupport::Dependencies.history.include?(expanded) + assert_not ActiveSupport::Dependencies.loaded.include?(expanded) + assert_not ActiveSupport::Dependencies.history.include?(expanded) silence_warnings { require_dependency filename } assert_equal 1, $check_warnings_load_count @@ -93,7 +117,7 @@ class DependenciesTest < ActiveSupport::TestCase assert ActiveSupport::Dependencies.loaded.include?(expanded) ActiveSupport::Dependencies.clear - assert !ActiveSupport::Dependencies.loaded.include?(expanded) + assert_not ActiveSupport::Dependencies.loaded.include?(expanded) assert ActiveSupport::Dependencies.history.include?(expanded) silence_warnings { require_dependency filename } @@ -102,7 +126,7 @@ class DependenciesTest < ActiveSupport::TestCase assert ActiveSupport::Dependencies.loaded.include?(expanded) ActiveSupport::Dependencies.clear - assert !ActiveSupport::Dependencies.loaded.include?(expanded) + assert_not ActiveSupport::Dependencies.loaded.include?(expanded) assert ActiveSupport::Dependencies.history.include?(expanded) enable_warnings { require_dependency filename } @@ -135,6 +159,31 @@ class DependenciesTest < ActiveSupport::TestCase end end + def test_ensures_the_expected_constant_is_defined + with_autoloading_fixtures do + e = assert_raise(LoadError) { Typo } + assert_match %r{Unable to autoload constant Typo, expected .*activesupport/test/autoloading_fixtures/typo.rb to define it}, e.message + end + end + + def test_require_dependency_does_not_assume_any_particular_constant_is_defined + with_autoloading_fixtures do + require_dependency 'typo' + assert_equal 1, TypO + end + end + + # Regression, see https://github.com/rails/rails/issues/16468. + def test_require_dependency_interaction_with_autoloading + with_autoloading_fixtures do + require_dependency 'typo' + assert_equal 1, TypO + + e = assert_raise(LoadError) { Typo } + assert_match %r{Unable to autoload constant Typo, expected .*activesupport/test/autoloading_fixtures/typo.rb to define it}, e.message + end + end + def test_module_loading with_autoloading_fixtures do assert_kind_of Module, A @@ -156,43 +205,49 @@ class DependenciesTest < ActiveSupport::TestCase def test_directories_manifest_as_modules_unless_const_defined with_autoloading_fixtures do assert_kind_of Module, ModuleFolder - Object.__send__ :remove_const, :ModuleFolder end + ensure + remove_constants(:ModuleFolder) end def test_module_with_nested_class with_autoloading_fixtures do assert_kind_of Class, ModuleFolder::NestedClass - Object.__send__ :remove_const, :ModuleFolder end + ensure + remove_constants(:ModuleFolder) end def test_module_with_nested_inline_class with_autoloading_fixtures do assert_kind_of Class, ModuleFolder::InlineClass - Object.__send__ :remove_const, :ModuleFolder end + ensure + remove_constants(:ModuleFolder) end def test_directories_may_manifest_as_nested_classes with_autoloading_fixtures do assert_kind_of Class, ClassFolder - Object.__send__ :remove_const, :ClassFolder end + ensure + remove_constants(:ClassFolder) end def test_class_with_nested_class with_autoloading_fixtures do assert_kind_of Class, ClassFolder::NestedClass - Object.__send__ :remove_const, :ClassFolder end + ensure + remove_constants(:ClassFolder) end def test_class_with_nested_inline_class with_autoloading_fixtures do assert_kind_of Class, ClassFolder::InlineClass - Object.__send__ :remove_const, :ClassFolder end + ensure + remove_constants(:ClassFolder) end def test_class_with_nested_inline_subclass_of_parent @@ -200,8 +255,9 @@ class DependenciesTest < ActiveSupport::TestCase assert_kind_of Class, ClassFolder::ClassFolderSubclass assert_kind_of Class, ClassFolder assert_equal 'indeed', ClassFolder::ClassFolderSubclass::ConstantInClassFolder - Object.__send__ :remove_const, :ClassFolder end + ensure + remove_constants(:ClassFolder) end def test_nested_class_can_access_sibling @@ -209,16 +265,15 @@ class DependenciesTest < ActiveSupport::TestCase sibling = ModuleFolder::NestedClass.class_eval "NestedSibling" assert defined?(ModuleFolder::NestedSibling) assert_equal ModuleFolder::NestedSibling, sibling - Object.__send__ :remove_const, :ModuleFolder end + ensure + remove_constants(:ModuleFolder) end def test_doesnt_break_normal_require path = File.expand_path("../autoloading_fixtures/load_path", __FILE__) original_path = $:.dup - original_features = $".dup $:.push(path) - with_autoloading_fixtures do # The _ = assignments are to prevent warnings _ = RequiresConstant @@ -230,15 +285,13 @@ class DependenciesTest < ActiveSupport::TestCase assert defined?(LoadedConstant) end ensure - remove_constants(:RequiresConstant, :LoadedConstant, :LoadsConstant) - $".replace(original_features) + remove_constants(:RequiresConstant, :LoadedConstant) $:.replace(original_path) end def test_doesnt_break_normal_require_nested path = File.expand_path("../autoloading_fixtures/load_path", __FILE__) original_path = $:.dup - original_features = $".dup $:.push(path) with_autoloading_fixtures do @@ -253,14 +306,12 @@ class DependenciesTest < ActiveSupport::TestCase end ensure remove_constants(:RequiresConstant, :LoadedConstant, :LoadsConstant) - $".replace(original_features) $:.replace(original_path) end def test_require_returns_true_when_file_not_yet_required path = File.expand_path("../autoloading_fixtures/load_path", __FILE__) original_path = $:.dup - original_features = $".dup $:.push(path) with_loading do @@ -268,14 +319,12 @@ class DependenciesTest < ActiveSupport::TestCase end ensure remove_constants(:LoadedConstant) - $".replace(original_features) $:.replace(original_path) end def test_require_returns_true_when_file_not_yet_required_even_when_no_new_constants_added path = File.expand_path("../autoloading_fixtures/load_path", __FILE__) original_path = $:.dup - original_features = $".dup $:.push(path) with_loading do @@ -284,14 +333,12 @@ class DependenciesTest < ActiveSupport::TestCase end ensure remove_constants(:LoadedConstant) - $".replace(original_features) $:.replace(original_path) end def test_require_returns_false_when_file_already_required path = File.expand_path("../autoloading_fixtures/load_path", __FILE__) original_path = $:.dup - original_features = $".dup $:.push(path) with_loading do @@ -300,7 +347,6 @@ class DependenciesTest < ActiveSupport::TestCase end ensure remove_constants(:LoadedConstant) - $".replace(original_features) $:.replace(original_path) end @@ -308,14 +354,11 @@ class DependenciesTest < ActiveSupport::TestCase with_loading do assert_raise(LoadError) { require 'this_file_dont_exist_dude' } end - ensure - remove_constants(:LoadedConstant) end def test_load_returns_true_when_file_found path = File.expand_path("../autoloading_fixtures/load_path", __FILE__) original_path = $:.dup - original_features = $".dup $:.push(path) with_loading do @@ -324,7 +367,6 @@ class DependenciesTest < ActiveSupport::TestCase end ensure remove_constants(:LoadedConstant) - $".replace(original_features) $:.replace(original_path) end @@ -332,41 +374,37 @@ class DependenciesTest < ActiveSupport::TestCase with_loading do assert_raise(LoadError) { load 'this_file_dont_exist_dude.rb' } end - ensure - remove_constants(:LoadedConstant) end def failing_test_access_thru_and_upwards_fails with_autoloading_fixtures do - assert ! defined?(ModuleFolder) + assert_not defined?(ModuleFolder) assert_raise(NameError) { ModuleFolder::Object } assert_raise(NameError) { ModuleFolder::NestedClass::Object } - Object.__send__ :remove_const, :ModuleFolder end + ensure + remove_constants(:ModuleFolder) end def test_non_existing_const_raises_name_error_with_fully_qualified_name with_autoloading_fixtures do - begin - A::DoesNotExist.nil? - flunk "No raise!!" - rescue NameError => e - assert_equal "uninitialized constant A::DoesNotExist", e.message - end - begin - A::B::DoesNotExist.nil? - flunk "No raise!!" - rescue NameError => e - assert_equal "uninitialized constant A::B::DoesNotExist", e.message - end + e = assert_raise(NameError) { A::DoesNotExist.nil? } + assert_equal "uninitialized constant A::DoesNotExist", e.message + assert_equal :DoesNotExist, e.name + + e = assert_raise(NameError) { A::B::DoesNotExist.nil? } + assert_equal "uninitialized constant A::B::DoesNotExist", e.message + assert_equal :DoesNotExist, e.name end + ensure + remove_constants(:A) end def test_smart_name_error_strings - Object.module_eval "ImaginaryObject" - flunk "No raise!!" - rescue NameError => e - assert e.message.include?("uninitialized constant ImaginaryObject") + e = assert_raise NameError do + Object.module_eval "ImaginaryObject" + end + assert_includes "uninitialized constant ImaginaryObject", e.message end def test_loadable_constants_for_path_should_handle_empty_autoloads @@ -449,9 +487,9 @@ class DependenciesTest < ActiveSupport::TestCase nil_name = Module.new def nil_name.name() nil end assert !ActiveSupport::Dependencies.autoloaded?(nil_name) - - Object.class_eval { remove_const :ModuleFolder } end + ensure + remove_constants(:ModuleFolder) end def test_qualified_name_for @@ -509,47 +547,45 @@ class DependenciesTest < ActiveSupport::TestCase assert_kind_of Module, ::ModuleWithCustomConstMissing::A assert_kind_of String, ::ModuleWithCustomConstMissing::A::B end + ensure + remove_constants(:ModuleWithCustomConstMissing) end - def test_const_missing_should_not_double_load - $counting_loaded_times = 0 + def test_const_missing_in_anonymous_modules_loads_top_level_constants with_autoloading_fixtures do - require_dependency '././counting_loader' - assert_equal 1, $counting_loaded_times - assert_raise(NameError) { ActiveSupport::Dependencies.load_missing_constant Object, :CountingLoader } - assert_equal 1, $counting_loaded_times + # class_eval STRING pushes the class to the nesting of the eval'ed code. + klass = Class.new.class_eval "E" + assert_equal E, klass end + ensure + remove_constants(:E) end - def test_const_missing_within_anonymous_module - $counting_loaded_times = 0 - m = Module.new - m.module_eval "def a() CountingLoader; end" - extend m - kls = nil + def test_const_missing_in_anonymous_modules_raises_if_the_constant_belongs_to_Object with_autoloading_fixtures do - kls = nil - assert_nothing_raised { kls = a } - assert_equal "CountingLoader", kls.name - assert_equal 1, $counting_loaded_times + require_dependency 'e' - assert_nothing_raised { kls = a } - assert_equal 1, $counting_loaded_times + 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 end + ensure + remove_constants(:E) end def test_removal_from_tree_should_be_detected with_loading 'dependencies' do c = ServiceOne ActiveSupport::Dependencies.clear - assert ! defined?(ServiceOne) - begin + assert_not defined?(ServiceOne) + e = assert_raise ArgumentError do ActiveSupport::Dependencies.load_missing_constant(c, :FakeMissing) - flunk "Expected exception" - rescue ArgumentError => e - assert_match %r{ServiceOne has been removed from the module tree}i, e.message end + assert_match %r{ServiceOne has been removed from the module tree}i, e.message end + ensure + remove_constants(:ServiceOne) end def test_references_should_work @@ -558,42 +594,46 @@ class DependenciesTest < ActiveSupport::TestCase service_one_first = ServiceOne assert_equal service_one_first, c.get("ServiceOne") ActiveSupport::Dependencies.clear - assert ! defined?(ServiceOne) - + assert_not defined?(ServiceOne) service_one_second = ServiceOne assert_not_equal service_one_first, c.get("ServiceOne") assert_equal service_one_second, c.get("ServiceOne") end + ensure + remove_constants(:ServiceOne) end def test_constantize_shortcut_for_cached_constant_lookups with_loading 'dependencies' do assert_equal ServiceOne, ActiveSupport::Dependencies.constantize("ServiceOne") end + ensure + remove_constants(:ServiceOne) end def test_nested_load_error_isnt_rescued with_loading 'dependencies' do - assert_raise(MissingSourceFile) do + assert_raise(LoadError) do RequiresNonexistent1 end end end def test_autoload_once_paths_do_not_add_to_autoloaded_constants + old_path = ActiveSupport::Dependencies.autoload_once_paths with_autoloading_fixtures do ActiveSupport::Dependencies.autoload_once_paths = ActiveSupport::Dependencies.autoload_paths.dup - assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder") - assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass") - assert ! ActiveSupport::Dependencies.autoloaded?(ModuleFolder) + assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder") + assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass") + assert_not ActiveSupport::Dependencies.autoloaded?(ModuleFolder) 1 if ModuleFolder::NestedClass # 1 if to avoid warning - assert ! ActiveSupport::Dependencies.autoloaded?(ModuleFolder::NestedClass) + assert_not ActiveSupport::Dependencies.autoloaded?(ModuleFolder::NestedClass) end ensure - Object.class_eval { remove_const :ModuleFolder } - ActiveSupport::Dependencies.autoload_once_paths = [] + remove_constants(:ModuleFolder) + ActiveSupport::Dependencies.autoload_once_paths = old_path end def test_autoload_once_pathnames_do_not_add_to_autoloaded_constants @@ -602,15 +642,15 @@ class DependenciesTest < ActiveSupport::TestCase ActiveSupport::Dependencies.autoload_paths = pathnames ActiveSupport::Dependencies.autoload_once_paths = pathnames - assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder") - assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass") - assert ! ActiveSupport::Dependencies.autoloaded?(ModuleFolder) + assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder") + assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass") + assert_not ActiveSupport::Dependencies.autoloaded?(ModuleFolder) 1 if ModuleFolder::NestedClass # 1 if to avoid warning - assert ! ActiveSupport::Dependencies.autoloaded?(ModuleFolder::NestedClass) + assert_not ActiveSupport::Dependencies.autoloaded?(ModuleFolder::NestedClass) end ensure - Object.class_eval { remove_const :ModuleFolder } + remove_constants(:ModuleFolder) ActiveSupport::Dependencies.autoload_once_paths = [] end @@ -620,6 +660,8 @@ class DependenciesTest < ActiveSupport::TestCase assert_equal 10, ApplicationController assert ActiveSupport::Dependencies.autoloaded?(:ApplicationController) end + ensure + remove_constants(:ApplicationController) end def test_preexisting_constants_are_not_marked_as_autoloaded @@ -635,9 +677,18 @@ class DependenciesTest < ActiveSupport::TestCase assert ! ActiveSupport::Dependencies.autoloaded?(:E), "E shouldn't be marked autoloaded!" ActiveSupport::Dependencies.clear end + ensure + remove_constants(:E) + end + + def test_constants_in_capitalized_nesting_marked_as_autoloaded + with_autoloading_fixtures do + ActiveSupport::Dependencies.load_missing_constant(HTML, "SomeClass") + assert ActiveSupport::Dependencies.autoloaded?("HTML::SomeClass") + end ensure - Object.class_eval { remove_const :E } + remove_constants(:HTML) end def test_unloadable @@ -668,7 +719,7 @@ class DependenciesTest < ActiveSupport::TestCase assert_equal false, M.unloadable end ensure - Object.class_eval { remove_const :M } + remove_constants(:M) end def test_unloadable_constants_should_receive_callback @@ -679,7 +730,7 @@ class DependenciesTest < ActiveSupport::TestCase ActiveSupport::Dependencies.clear assert !defined?(C) ensure - Object.class_eval { remove_const :C } if defined?(C) + remove_constants(:C) end def test_new_contants_in_without_constants @@ -693,7 +744,7 @@ class DependenciesTest < ActiveSupport::TestCase }.map(&:to_s) assert ActiveSupport::Dependencies.constant_watch_stack.all? {|k,v| v.empty? } ensure - Object.class_eval { remove_const :Hello } + remove_constants(:Hello) end def test_new_constants_in_with_nesting @@ -710,9 +761,7 @@ class DependenciesTest < ActiveSupport::TestCase assert_equal ["OuterAfter", "OuterBefore"], outer.sort.map(&:to_s) assert ActiveSupport::Dependencies.constant_watch_stack.all? {|k,v| v.empty? } ensure - %w(OuterBefore Inner OuterAfter).each do |name| - Object.class_eval { remove_const name if const_defined?(name) } - end + remove_constants(:OuterBefore, :Inner, :OuterAfter) end def test_new_constants_in_module @@ -731,7 +780,7 @@ class DependenciesTest < ActiveSupport::TestCase assert_equal ["M::OuterAfter", "M::OuterBefore"], outer.sort assert ActiveSupport::Dependencies.constant_watch_stack.all? {|k,v| v.empty? } ensure - Object.class_eval { remove_const :M } + remove_constants(:M) end def test_new_constants_in_module_using_name @@ -749,7 +798,7 @@ class DependenciesTest < ActiveSupport::TestCase assert_equal ["M::OuterAfter", "M::OuterBefore"], outer.sort assert ActiveSupport::Dependencies.constant_watch_stack.all? {|k,v| v.empty? } ensure - Object.class_eval { remove_const :M } + remove_constants(:M) end def test_new_constants_in_with_inherited_constants @@ -767,26 +816,27 @@ class DependenciesTest < ActiveSupport::TestCase def test_file_with_multiple_constants_and_require_dependency with_autoloading_fixtures do - assert ! defined?(MultipleConstantFile) - assert ! defined?(SiblingConstant) + assert_not defined?(MultipleConstantFile) + assert_not defined?(SiblingConstant) require_dependency 'multiple_constant_file' assert defined?(MultipleConstantFile) assert defined?(SiblingConstant) assert ActiveSupport::Dependencies.autoloaded?(:MultipleConstantFile) assert ActiveSupport::Dependencies.autoloaded?(:SiblingConstant) - ActiveSupport::Dependencies.clear - assert ! defined?(MultipleConstantFile) - assert ! defined?(SiblingConstant) + assert_not defined?(MultipleConstantFile) + assert_not defined?(SiblingConstant) end + ensure + remove_constants(:MultipleConstantFile, :SiblingConstant) end def test_file_with_multiple_constants_and_auto_loading with_autoloading_fixtures do - assert ! defined?(MultipleConstantFile) - assert ! defined?(SiblingConstant) + assert_not defined?(MultipleConstantFile) + assert_not defined?(SiblingConstant) assert_equal 10, MultipleConstantFile @@ -797,15 +847,17 @@ class DependenciesTest < ActiveSupport::TestCase ActiveSupport::Dependencies.clear - assert ! defined?(MultipleConstantFile) - assert ! defined?(SiblingConstant) + assert_not defined?(MultipleConstantFile) + assert_not defined?(SiblingConstant) end + ensure + remove_constants(:MultipleConstantFile, :SiblingConstant) end def test_nested_file_with_multiple_constants_and_require_dependency with_autoloading_fixtures do - assert ! defined?(ClassFolder::NestedClass) - assert ! defined?(ClassFolder::SiblingClass) + assert_not defined?(ClassFolder::NestedClass) + assert_not defined?(ClassFolder::SiblingClass) require_dependency 'class_folder/nested_class' @@ -816,15 +868,17 @@ class DependenciesTest < ActiveSupport::TestCase ActiveSupport::Dependencies.clear - assert ! defined?(ClassFolder::NestedClass) - assert ! defined?(ClassFolder::SiblingClass) + assert_not defined?(ClassFolder::NestedClass) + assert_not defined?(ClassFolder::SiblingClass) end + ensure + remove_constants(:ClassFolder) end def test_nested_file_with_multiple_constants_and_auto_loading with_autoloading_fixtures do - assert ! defined?(ClassFolder::NestedClass) - assert ! defined?(ClassFolder::SiblingClass) + assert_not defined?(ClassFolder::NestedClass) + assert_not defined?(ClassFolder::SiblingClass) assert_kind_of Class, ClassFolder::NestedClass @@ -835,9 +889,11 @@ class DependenciesTest < ActiveSupport::TestCase ActiveSupport::Dependencies.clear - assert ! defined?(ClassFolder::NestedClass) - assert ! defined?(ClassFolder::SiblingClass) + assert_not defined?(ClassFolder::NestedClass) + assert_not defined?(ClassFolder::SiblingClass) end + ensure + remove_constants(:ClassFolder) end def test_autoload_doesnt_shadow_no_method_error_with_relative_constant @@ -848,9 +904,8 @@ class DependenciesTest < ActiveSupport::TestCase assert !defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it should have failed!" end end - ensure - Object.class_eval { remove_const :RaisesNoMethodError if const_defined?(:RaisesNoMethodError) } + remove_constants(:RaisesNoMethodError) end def test_autoload_doesnt_shadow_no_method_error_with_absolute_constant @@ -861,9 +916,8 @@ class DependenciesTest < ActiveSupport::TestCase assert !defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it should have failed!" end end - ensure - Object.class_eval { remove_const :RaisesNoMethodError if const_defined?(:RaisesNoMethodError) } + remove_constants(:RaisesNoMethodError) end def test_autoload_doesnt_shadow_error_when_mechanism_not_set_to_load @@ -873,18 +927,17 @@ class DependenciesTest < ActiveSupport::TestCase assert_raise(NameError) { assert_equal 123, ::RaisesNameError::FooBarBaz } end end + ensure + remove_constants(:RaisesNameError) end def test_autoload_doesnt_shadow_name_error with_autoloading_fixtures do - Object.send(:remove_const, :RaisesNameError) if defined?(::RaisesNameError) - 2.times do |i| - begin + 2.times do + e = assert_raise NameError do ::RaisesNameError::FooBarBaz.object_id - flunk 'should have raised NameError when autoloaded file referenced FooBarBaz' - rescue NameError => e - assert_equal 'uninitialized constant RaisesNameError::FooBarBaz', e.message end + assert_equal 'uninitialized constant RaisesNameError::FooBarBaz', e.message assert !defined?(::RaisesNameError), "::RaisesNameError is defined but it should have failed!" end @@ -894,19 +947,20 @@ class DependenciesTest < ActiveSupport::TestCase assert !defined?(::RaisesNameError), "::RaisesNameError is defined but it should have failed!" end end - ensure - Object.class_eval { remove_const :RaisesNoMethodError if const_defined?(:RaisesNoMethodError) } + remove_constants(:RaisesNameError) end def test_remove_constant_handles_double_colon_at_start Object.const_set 'DeleteMe', Module.new DeleteMe.const_set 'OrMe', Module.new ActiveSupport::Dependencies.remove_constant "::DeleteMe::OrMe" - assert ! defined?(DeleteMe::OrMe) + assert_not defined?(DeleteMe::OrMe) assert defined?(DeleteMe) ActiveSupport::Dependencies.remove_constant "::DeleteMe" - assert ! defined?(DeleteMe) + assert_not defined?(DeleteMe) + ensure + remove_constants(:DeleteMe) end def test_remove_constant_does_not_trigger_loading_autoloads @@ -916,7 +970,9 @@ class DependenciesTest < ActiveSupport::TestCase end assert_nil ActiveSupport::Dependencies.remove_constant(constant), "Kernel#autoload has been triggered by remove_constant" - assert !defined?(ShouldNotBeAutoloaded) + assert_not defined?(ShouldNotBeAutoloaded) + ensure + remove_constants(constant) end def test_remove_constant_does_not_autoload_already_removed_parents_as_a_side_effect @@ -925,11 +981,14 @@ class DependenciesTest < ActiveSupport::TestCase _ = ::A::B # assignment to silence parse-time warning "possibly useless use of :: in void context" ActiveSupport::Dependencies.remove_constant('A') ActiveSupport::Dependencies.remove_constant('A::B') - assert !defined?(A) + assert_not defined?(A) end + ensure + remove_constants(:A) end def test_load_once_constants_should_not_be_unloaded + old_path = ActiveSupport::Dependencies.autoload_once_paths with_autoloading_fixtures do ActiveSupport::Dependencies.autoload_once_paths = ActiveSupport::Dependencies.autoload_paths _ = ::A # assignment to silence parse-time warning "possibly useless use of :: in void context" @@ -938,30 +997,58 @@ class DependenciesTest < ActiveSupport::TestCase assert defined?(A) end ensure - ActiveSupport::Dependencies.autoload_once_paths = [] - Object.class_eval { remove_const :A if const_defined?(:A) } + ActiveSupport::Dependencies.autoload_once_paths = old_path + remove_constants(:A) + end + + def test_access_unloaded_constants_for_reload + with_autoloading_fixtures do + assert_kind_of Module, A + assert_kind_of Class, A::B # Necessary to load A::B for the test + ActiveSupport::Dependencies.mark_for_unload(A::B) + ActiveSupport::Dependencies.remove_unloadable_constants! + + A::B # Make sure no circular dependency error + end + ensure + remove_constants(:A) end + def test_autoload_once_paths_should_behave_when_recursively_loading + old_path = ActiveSupport::Dependencies.autoload_once_paths with_loading 'dependencies', 'autoloading_fixtures' do ActiveSupport::Dependencies.autoload_once_paths = [ActiveSupport::Dependencies.autoload_paths.last] - assert !defined?(CrossSiteDependency) + assert_not defined?(CrossSiteDependency) assert_nothing_raised { CrossSiteDepender.nil? } assert defined?(CrossSiteDependency) - assert !ActiveSupport::Dependencies.autoloaded?(CrossSiteDependency), + assert_not ActiveSupport::Dependencies.autoloaded?(CrossSiteDependency), "CrossSiteDependency shouldn't be marked as autoloaded!" ActiveSupport::Dependencies.clear assert defined?(CrossSiteDependency), "CrossSiteDependency shouldn't have been unloaded!" end ensure - ActiveSupport::Dependencies.autoload_once_paths = [] + ActiveSupport::Dependencies.autoload_once_paths = old_path + remove_constants(:CrossSiteDependency) end def test_hook_called_multiple_times assert_nothing_raised { ActiveSupport::Dependencies.hook! } end + def test_load_and_require_stay_private + assert Object.private_methods.include?(:load) + assert Object.private_methods.include?(:require) + + ActiveSupport::Dependencies.unhook! + + assert Object.private_methods.include?(:load) + assert Object.private_methods.include?(:require) + ensure + ActiveSupport::Dependencies.hook! + end + def test_unhook ActiveSupport::Dependencies.unhook! assert !Module.new.respond_to?(:const_missing_without_dependencies) @@ -969,11 +1056,4 @@ class DependenciesTest < ActiveSupport::TestCase ensure ActiveSupport::Dependencies.hook! end - -private - def remove_constants(*constants) - constants.each do |constant| - Object.send(:remove_const, constant) if Object.const_defined?(constant) - end - end end diff --git a/activesupport/test/dependecies_test_helpers.rb b/activesupport/test/dependencies_test_helpers.rb index 4b46d32fb4..e4d5197112 100644 --- a/activesupport/test/dependecies_test_helpers.rb +++ b/activesupport/test/dependencies_test_helpers.rb @@ -1,4 +1,4 @@ -module DependeciesTestHelpers +module DependenciesTestHelpers def with_loading(*from) old_mechanism, ActiveSupport::Dependencies.mechanism = ActiveSupport::Dependencies.mechanism, :load this_dir = File.dirname(__FILE__) @@ -13,6 +13,7 @@ module DependeciesTestHelpers ActiveSupport::Dependencies.autoload_paths = prior_autoload_paths ActiveSupport::Dependencies.mechanism = old_mechanism ActiveSupport::Dependencies.explicitly_unloadable_constants = [] + ActiveSupport::Dependencies.clear end def with_autoloading_fixtures(&block) diff --git a/activesupport/test/deprecation/basic_object_test.rb b/activesupport/test/deprecation/basic_object_test.rb deleted file mode 100644 index 4b5bed9eb1..0000000000 --- a/activesupport/test/deprecation/basic_object_test.rb +++ /dev/null @@ -1,12 +0,0 @@ -require 'abstract_unit' -require 'active_support/deprecation' -require 'active_support/basic_object' - - -class BasicObjectTest < ActiveSupport::TestCase - test 'BasicObject warns about deprecation when inherited from' do - warn = 'ActiveSupport::BasicObject is deprecated! Use ActiveSupport::ProxyObject instead.' - ActiveSupport::Deprecation.expects(:warn).with(warn).once - Class.new(ActiveSupport::BasicObject) - end -end
\ No newline at end of file diff --git a/activesupport/test/deprecation/buffered_logger_test.rb b/activesupport/test/deprecation/buffered_logger_test.rb deleted file mode 100644 index bf11a4732c..0000000000 --- a/activesupport/test/deprecation/buffered_logger_test.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'abstract_unit' -require 'active_support/buffered_logger' - -class BufferedLoggerTest < ActiveSupport::TestCase - - def test_can_be_subclassed - warn = 'ActiveSupport::BufferedLogger is deprecated! Use ActiveSupport::Logger instead.' - - ActiveSupport::Deprecation.expects(:warn).with(warn).once - - Class.new(ActiveSupport::BufferedLogger) - end - - def test_issues_deprecation_when_instantiated - warn = 'ActiveSupport::BufferedLogger is deprecated! Use ActiveSupport::Logger instead.' - - ActiveSupport::Deprecation.expects(:warn).with(warn).once - - ActiveSupport::BufferedLogger.new(STDOUT) - end - -end diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb index 9616e42f44..7aff56cbad 100644 --- a/activesupport/test/deprecation_test.rb +++ b/activesupport/test/deprecation_test.rb @@ -98,6 +98,19 @@ class DeprecationTest < ActiveSupport::TestCase assert_match(/foo=nil/, @b) end + def test_raise_behaviour + ActiveSupport::Deprecation.behavior = :raise + + message = 'Revise this deprecated stuff now!' + callstack = %w(foo bar baz) + + e = assert_raise ActiveSupport::DeprecationException do + ActiveSupport::Deprecation.behavior.first.call(message, callstack) + end + assert_equal message, e.message + assert_equal callstack, e.backtrace + end + def test_default_stderr_behavior ActiveSupport::Deprecation.behavior = :stderr behavior = ActiveSupport::Deprecation.behavior.first @@ -158,7 +171,7 @@ class DeprecationTest < ActiveSupport::TestCase ActiveSupport::Deprecation.warn 'abc' ActiveSupport::Deprecation.warn 'def' end - rescue MiniTest::Assertion + rescue Minitest::Assertion flunk 'assert_deprecated should match any warning in block, not just the last one' end @@ -342,4 +355,21 @@ class DeprecationTest < ActiveSupport::TestCase end deprecator end + + def capture(stream) + stream = stream.to_s + captured_stream = Tempfile.new(stream) + stream_io = eval("$#{stream}") + origin_stream = stream_io.dup + stream_io.reopen(captured_stream) + + yield + + stream_io.rewind + return captured_stream.read + ensure + captured_stream.close + captured_stream.unlink + stream_io.reopen(origin_stream) + end end diff --git a/activesupport/test/descendants_tracker_with_autoloading_test.rb b/activesupport/test/descendants_tracker_with_autoloading_test.rb index ab1be296f8..a2ae066a21 100644 --- a/activesupport/test/descendants_tracker_with_autoloading_test.rb +++ b/activesupport/test/descendants_tracker_with_autoloading_test.rb @@ -6,7 +6,7 @@ require 'descendants_tracker_test_cases' class DescendantsTrackerWithAutoloadingTest < ActiveSupport::TestCase include DescendantsTrackerTestCases - def test_clear_with_autoloaded_parent_children_and_granchildren + def test_clear_with_autoloaded_parent_children_and_grandchildren mark_as_autoloaded(*ALL) do ActiveSupport::DescendantsTracker.clear ALL.each do |k| @@ -15,7 +15,7 @@ class DescendantsTrackerWithAutoloadingTest < ActiveSupport::TestCase end end - def test_clear_with_autoloaded_children_and_granchildren + def test_clear_with_autoloaded_children_and_grandchildren mark_as_autoloaded Child1, Grandchild1, Grandchild2 do ActiveSupport::DescendantsTracker.clear assert_equal_sets [Child2], Parent.descendants @@ -23,7 +23,7 @@ class DescendantsTrackerWithAutoloadingTest < ActiveSupport::TestCase end end - def test_clear_with_autoloaded_granchildren + def test_clear_with_autoloaded_grandchildren mark_as_autoloaded Grandchild1, Grandchild2 do ActiveSupport::DescendantsTracker.clear assert_equal_sets [Child1, Child2], Parent.descendants diff --git a/activesupport/test/descendants_tracker_without_autoloading_test.rb b/activesupport/test/descendants_tracker_without_autoloading_test.rb index 74669aaca1..00b449af51 100644 --- a/activesupport/test/descendants_tracker_without_autoloading_test.rb +++ b/activesupport/test/descendants_tracker_without_autoloading_test.rb @@ -4,4 +4,14 @@ require 'descendants_tracker_test_cases' class DescendantsTrackerWithoutAutoloadingTest < ActiveSupport::TestCase include DescendantsTrackerTestCases + + # Regression test for #8422. https://github.com/rails/rails/issues/8442 + def test_clear_without_autoloaded_singleton_parent + mark_as_autoloaded do + parent_instance = Parent.new + parent_instance.singleton_class.descendants + ActiveSupport::DescendantsTracker.clear + assert !ActiveSupport::DescendantsTracker.class_variable_get(:@@direct_descendants).key?(parent_instance.singleton_class) + end + end end diff --git a/activesupport/test/empty_bool.rb b/activesupport/test/empty_bool.rb deleted file mode 100644 index 005b3523ef..0000000000 --- a/activesupport/test/empty_bool.rb +++ /dev/null @@ -1,7 +0,0 @@ -class EmptyTrue - def empty?() true; end -end - -class EmptyFalse - def empty?() false; end -end diff --git a/activesupport/test/fixtures/custom.rb b/activesupport/test/fixtures/custom.rb deleted file mode 100644 index 0eefce0c25..0000000000 --- a/activesupport/test/fixtures/custom.rb +++ /dev/null @@ -1,2 +0,0 @@ -class Custom -end
\ No newline at end of file diff --git a/activesupport/test/fixtures/xml/jdom_doctype.dtd b/activesupport/test/fixtures/xml/jdom_doctype.dtd new file mode 100644 index 0000000000..89480496ef --- /dev/null +++ b/activesupport/test/fixtures/xml/jdom_doctype.dtd @@ -0,0 +1 @@ +<!ENTITY a "external entity"> diff --git a/activesupport/test/fixtures/xml/jdom_entities.txt b/activesupport/test/fixtures/xml/jdom_entities.txt new file mode 100644 index 0000000000..0337fdaa08 --- /dev/null +++ b/activesupport/test/fixtures/xml/jdom_entities.txt @@ -0,0 +1 @@ +<!ENTITY a "hello"> diff --git a/activesupport/test/fixtures/xml/jdom_include.txt b/activesupport/test/fixtures/xml/jdom_include.txt new file mode 100644 index 0000000000..239ca3afaf --- /dev/null +++ b/activesupport/test/fixtures/xml/jdom_include.txt @@ -0,0 +1 @@ +include me diff --git a/activesupport/test/gzip_test.rb b/activesupport/test/gzip_test.rb index 75a0505899..0e3cf3b429 100644 --- a/activesupport/test/gzip_test.rb +++ b/activesupport/test/gzip_test.rb @@ -4,6 +4,12 @@ require 'active_support/core_ext/object/blank' class GzipTest < ActiveSupport::TestCase def test_compress_should_decompress_to_the_same_value assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World")) + assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", Zlib::NO_COMPRESSION)) + assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", Zlib::BEST_SPEED)) + assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", Zlib::BEST_COMPRESSION)) + assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", nil, Zlib::FILTERED)) + assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", nil, Zlib::HUFFMAN_ONLY)) + assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", nil, nil)) end def test_compress_should_return_a_binary_string @@ -12,4 +18,16 @@ class GzipTest < ActiveSupport::TestCase assert_equal Encoding.find('binary'), compressed.encoding assert !compressed.blank?, "a compressed blank string should not be blank" end + + def test_compress_should_return_gzipped_string_by_compression_level + source_string = "Hello World"*100 + + gzipped_by_speed = ActiveSupport::Gzip.compress(source_string, Zlib::BEST_SPEED) + assert_equal 1, Zlib::GzipReader.new(StringIO.new(gzipped_by_speed)).level + + gzipped_by_best_compression = ActiveSupport::Gzip.compress(source_string, Zlib::BEST_COMPRESSION) + assert_equal 9, Zlib::GzipReader.new(StringIO.new(gzipped_by_best_compression)).level + + assert_equal true, (gzipped_by_best_compression.bytesize < gzipped_by_speed.bytesize) + end end diff --git a/activesupport/test/hash_with_indifferent_access_test.rb b/activesupport/test/hash_with_indifferent_access_test.rb new file mode 100644 index 0000000000..843994147b --- /dev/null +++ b/activesupport/test/hash_with_indifferent_access_test.rb @@ -0,0 +1,10 @@ +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
\ No newline at end of file diff --git a/activesupport/test/i18n_test.rb b/activesupport/test/i18n_test.rb index 5ef59b6e6b..3faa15e7fd 100644 --- a/activesupport/test/i18n_test.rb +++ b/activesupport/test/i18n_test.rb @@ -99,7 +99,6 @@ class I18nTest < ActiveSupport::TestCase end def test_to_sentence_with_empty_i18n_store - I18n.backend.store_translations 'empty', {} assert_equal 'a, b, and c', %w[a b c].to_sentence(locale: 'empty') end end diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb index a1e5db6a2e..be68bb2e2e 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -61,9 +61,7 @@ class InflectorTest < ActiveSupport::TestCase assert_equal(plural, ActiveSupport::Inflector.pluralize(plural)) assert_equal(plural.capitalize, ActiveSupport::Inflector.pluralize(plural.capitalize)) end - end - SingularToPlural.each do |singular, plural| define_method "test_singularize_singular_#{singular}" do assert_equal(singular, ActiveSupport::Inflector.singularize(singular)) assert_equal(singular.capitalize, ActiveSupport::Inflector.singularize(singular.capitalize)) @@ -72,15 +70,17 @@ class InflectorTest < ActiveSupport::TestCase def test_overwrite_previous_inflectors - assert_equal("series", ActiveSupport::Inflector.singularize("series")) - ActiveSupport::Inflector.inflections.singular "series", "serie" - assert_equal("serie", ActiveSupport::Inflector.singularize("series")) - ActiveSupport::Inflector.inflections.uncountable "series" # Return to normal + with_dup do + assert_equal("series", ActiveSupport::Inflector.singularize("series")) + ActiveSupport::Inflector.inflections.singular "series", "serie" + assert_equal("serie", ActiveSupport::Inflector.singularize("series")) + end end - MixtureToTitleCase.each do |before, titleized| - define_method "test_titleize_#{before}" do - assert_equal(titleized, ActiveSupport::Inflector.titleize(before)) + MixtureToTitleCase.each_with_index do |(before, titleized), index| + define_method "test_titleize_mixture_to_title_case_#{index}" do + assert_equal(titleized, ActiveSupport::Inflector.titleize(before), "mixture \ + to TitleCase failed for #{before}") end end @@ -120,10 +120,14 @@ class InflectorTest < ActiveSupport::TestCase ["SSLError", "ssl_error", "SSL error", "SSL Error"], ["RESTful", "restful", "RESTful", "RESTful"], ["RESTfulController", "restful_controller", "RESTful controller", "RESTful Controller"], + ["Nested::RESTful", "nested/restful", "Nested/RESTful", "Nested/RESTful"], ["IHeartW3C", "i_heart_w3c", "I heart W3C", "I Heart W3C"], ["PhDRequired", "phd_required", "PhD required", "PhD Required"], ["IRoRU", "i_ror_u", "I RoR u", "I RoR U"], ["RESTfulHTTPAPI", "restful_http_api", "RESTful HTTP API", "RESTful HTTP API"], + ["HTTP::RESTful", "http/restful", "HTTP/RESTful", "HTTP/RESTful"], + ["HTTP::RESTfulAPI", "http/restful_api", "HTTP/RESTful API", "HTTP/RESTful API"], + ["APIRESTful", "api_restful", "API RESTful", "API RESTful"], # misdirection ["Capistrano", "capistrano", "Capistrano", "Capistrano"], @@ -200,6 +204,7 @@ class InflectorTest < ActiveSupport::TestCase def test_demodulize assert_equal "Account", ActiveSupport::Inflector.demodulize("MyApplication::Billing::Account") assert_equal "Account", ActiveSupport::Inflector.demodulize("Account") + assert_equal "Account", ActiveSupport::Inflector.demodulize("::Account") assert_equal "", ActiveSupport::Inflector.demodulize("") end @@ -231,25 +236,35 @@ class InflectorTest < ActiveSupport::TestCase end end +# FIXME: get following tests to pass on jruby, currently skipped +# +# Currently this fails because ActiveSupport::Multibyte::Unicode#tidy_bytes +# required a specific Encoding::Converter(UTF-8 to UTF8-MAC) which unavailable on JRuby +# causing our tests to error out. +# related bug http://jira.codehaus.org/browse/JRUBY-7194 def test_parameterize + jruby_skip "UTF-8 to UTF8-MAC Converter is unavailable" StringToParameterized.each do |some_string, parameterized_string| assert_equal(parameterized_string, ActiveSupport::Inflector.parameterize(some_string)) end end def test_parameterize_and_normalize + jruby_skip "UTF-8 to UTF8-MAC Converter is unavailable" StringToParameterizedAndNormalized.each do |some_string, parameterized_string| assert_equal(parameterized_string, ActiveSupport::Inflector.parameterize(some_string)) end end 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, '_')) 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__')) end @@ -278,6 +293,12 @@ class InflectorTest < ActiveSupport::TestCase end end + def test_humanize_without_capitalize + UnderscoreToHumanWithoutCapitalize.each do |underscore, human| + assert_equal(human, ActiveSupport::Inflector.humanize(underscore, capitalize: false)) + end + end + def test_humanize_by_rule ActiveSupport::Inflector.inflections do |inflect| inflect.human(/_cnt$/i, '\1_count') @@ -326,7 +347,7 @@ class InflectorTest < ActiveSupport::TestCase end def test_underscore_as_reverse_of_dasherize - UnderscoresToDashes.each do |underscored, dasherized| + UnderscoresToDashes.each_key do |underscored| assert_equal(underscored, ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.dasherize(underscored))) end end @@ -421,33 +442,36 @@ class InflectorTest < ActiveSupport::TestCase end end - Irregularities.each do |irregularity| - singular, plural = *irregularity - ActiveSupport::Inflector.inflections do |inflect| - define_method("test_irregularity_between_#{singular}_and_#{plural}") do - inflect.irregular(singular, plural) - assert_equal singular, ActiveSupport::Inflector.singularize(plural) - assert_equal plural, ActiveSupport::Inflector.pluralize(singular) + 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 end end end - Irregularities.each do |irregularity| - singular, plural = *irregularity - ActiveSupport::Inflector.inflections do |inflect| - define_method("test_pluralize_of_irregularity_#{plural}_should_be_the_same") do - inflect.irregular(singular, plural) - assert_equal plural, ActiveSupport::Inflector.pluralize(plural) + 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 end end end - Irregularities.each do |irregularity| - singular, plural = *irregularity - ActiveSupport::Inflector.inflections do |inflect| - define_method("test_singularize_of_irregularity_#{singular}_should_be_the_same") do - inflect.irregular(singular, plural) - assert_equal singular, ActiveSupport::Inflector.singularize(singular) + 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 end end end @@ -466,8 +490,8 @@ class InflectorTest < ActiveSupport::TestCase assert_equal [], inflect.uncountables # restore all the inflections - singulars.reverse.each { |singular| inflect.singular(*singular) } - plurals.reverse.each { |plural| inflect.plural(*plural) } + singulars.reverse_each { |singular| inflect.singular(*singular) } + plurals.reverse_each { |plural| inflect.plural(*plural) } inflect.uncountable(uncountables) assert_equal singulars, inflect.singulars @@ -478,10 +502,10 @@ class InflectorTest < ActiveSupport::TestCase end %w(plurals singulars uncountables humans acronyms).each do |scope| - ActiveSupport::Inflector.inflections do |inflect| - define_method("test_clear_inflections_with_#{scope}") do - with_dup do - # clear the inflections + 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 @@ -489,6 +513,14 @@ class InflectorTest < ActiveSupport::TestCase end end + def test_inflections_with_uncountable_words + ActiveSupport::Inflector.inflections do |inflect| + inflect.uncountable "HTTP" + end + + 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. # @@ -496,9 +528,10 @@ class InflectorTest < ActiveSupport::TestCase # 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__) - ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, original.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__, original) + 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 7704300938..3770f00fe3 100644 --- a/activesupport/test/inflector_test_cases.rb +++ b/activesupport/test/inflector_test_cases.rb @@ -63,6 +63,7 @@ module InflectorTestCases "news" => "news", "series" => "series", + "miniseries" => "miniseries", "species" => "species", "quiz" => "quizzes", @@ -105,7 +106,6 @@ module InflectorTestCases "prize" => "prizes", "edge" => "edges", - "cow" => "kine", "database" => "databases", # regression tests against improper inflection regexes @@ -141,6 +141,7 @@ module InflectorTestCases "HTMLTidyGenerator" => "html_tidy_generator", "FreeBSD" => "free_bsd", "HTML" => "html", + "ForceXMLController" => "force_xml_controller", } CamelWithModuleToUnderscoreWithSlash = { @@ -208,9 +209,17 @@ module InflectorTestCases } UnderscoreToHuman = { - "employee_salary" => "Employee salary", - "employee_id" => "Employee", - "underground" => "Underground" + 'employee_salary' => 'Employee salary', + 'employee_id' => 'Employee', + 'underground' => 'Underground', + '_id' => 'Id', + '_external_id' => 'External' + } + + UnderscoreToHumanWithoutCapitalize = { + "employee_salary" => "employee salary", + "employee_id" => "employee", + "underground" => "underground" } MixtureToTitleCase = { @@ -308,7 +317,7 @@ module InflectorTestCases 'child' => 'children', 'sex' => 'sexes', 'move' => 'moves', - 'cow' => 'kine', + 'cow' => 'kine', # Test inflections with different starting letters 'zombie' => 'zombies', 'genus' => 'genera' } diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb index d1454902e5..80bf255080 100644 --- a/activesupport/test/json/decoding_test.rb +++ b/activesupport/test/json/decoding_test.rb @@ -4,6 +4,12 @@ require 'active_support/json' require 'active_support/time' class TestJSONDecoding < ActiveSupport::TestCase + class Foo + def self.json_create(object) + "Foo" + end + end + TESTS = { %q({"returnTo":{"\/categories":"\/"}}) => {"returnTo" => {"/categories" => "/"}}, %q({"return\\"To\\":":{"\/categories":"\/"}}) => {"return\"To\":" => {"/categories" => "/"}}, @@ -52,36 +58,56 @@ class TestJSONDecoding < ActiveSupport::TestCase # tests escaping of "\n" char with Yaml backend %q({"a":"\n"}) => {"a"=>"\n"}, %q({"a":"\u000a"}) => {"a"=>"\n"}, - %q({"a":"Line1\u000aLine2"}) => {"a"=>"Line1\nLine2"} + %q({"a":"Line1\u000aLine2"}) => {"a"=>"Line1\nLine2"}, + # prevent json unmarshalling + %q({"json_class":"TestJSONDecoding::Foo"}) => {"json_class"=>"TestJSONDecoding::Foo"}, + # json "fragments" - these are invalid JSON, but ActionPack relies on this + %q("a string") => "a string", + %q(1.1) => 1.1, + %q(1) => 1, + %q(-1) => -1, + %q(true) => true, + %q(false) => false, + %q(null) => nil } - backends = [:ok_json] - backends << :json_gem if defined?(::JSON) - backends << :yajl if defined?(::Yajl) - - backends.each do |backend| - TESTS.each do |json, expected| - test "json decodes #{json} with the #{backend} backend" do - ActiveSupport.parse_json_times = true + TESTS.each_with_index do |(json, expected), index| + test "json decodes #{index}" do + with_parse_json_times(true) do silence_warnings do - ActiveSupport::JSON.with_backend backend do - assert_equal expected, ActiveSupport::JSON.decode(json) - end + assert_equal expected, ActiveSupport::JSON.decode(json), "JSON decoding \ + failed for #{json}" end end end + end - test "json decodes time json with time parsing disabled with the #{backend} backend" do - ActiveSupport.parse_json_times = false + test "json decodes time json with time parsing disabled" do + with_parse_json_times(false) do expected = {"a" => "2007-01-01 01:12:34 Z"} - ActiveSupport::JSON.with_backend backend do - assert_equal expected, ActiveSupport::JSON.decode(%({"a": "2007-01-01 01:12:34 Z"})) - end + assert_equal expected, ActiveSupport::JSON.decode(%({"a": "2007-01-01 01:12:34 Z"})) end end def test_failed_json_decoding + assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%(undefined)) } + assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%({a: 1})) } assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%({: 1})) } + assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%()) } + end + + def test_cannot_pass_unsupported_options + assert_raise(ArgumentError) { ActiveSupport::JSON.decode("", create_additions: true) } + end + + private + + def with_parse_json_times(value) + old_value = ActiveSupport.parse_json_times + ActiveSupport.parse_json_times = value + yield + ensure + ActiveSupport.parse_json_times = old_value end end diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index 12ce250eb3..7e976aa772 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -1,9 +1,14 @@ # encoding: utf-8 +require 'securerandom' require 'abstract_unit' require 'active_support/core_ext/string/inflections' require 'active_support/json' +require 'active_support/time' +require 'time_zone_test_helpers' class TestJSONEncoding < ActiveSupport::TestCase + include TimeZoneTestHelpers + class Foo def initialize(a, b) @a, @b = a, b @@ -12,13 +17,17 @@ class TestJSONEncoding < ActiveSupport::TestCase class Hashlike def to_hash - { :a => 1 } + { :foo => "hello", :bar => "world" } end end class Custom - def as_json(options) - 'custom' + def initialize(serialized) + @serialized = serialized + end + + def as_json(options = nil) + @serialized end end @@ -31,6 +40,25 @@ class TestJSONEncoding < ActiveSupport::TestCase 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) ]] @@ -40,13 +68,13 @@ class TestJSONEncoding < ActiveSupport::TestCase [ 1.0/0.0, %(null) ], [ -1.0/0.0, %(null) ], [ BigDecimal('0.0')/BigDecimal('0.0'), %(null) ], - [ BigDecimal('2.5'), %("#{BigDecimal('2.5').to_s}") ]] + [ BigDecimal('2.5'), %("#{BigDecimal('2.5')}") ]] - StringTests = [[ 'this is the <string>', %("this is the \\u003Cstring\\u003E")], + 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", - %("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") ]] + [ "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]) ]] @@ -60,8 +88,14 @@ class TestJSONEncoding < ActiveSupport::TestCase [ :"a b", %("a b") ]] ObjectTests = [[ Foo.new(1, 2), %({\"a\":1,\"b\":2}) ]] - HashlikeTests = [[ Hashlike.new, %({\"a\":1}) ]] - CustomTests = [[ Custom.new, '"custom"' ]] + 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]+)"']] @@ -70,8 +104,8 @@ class TestJSONEncoding < ActiveSupport::TestCase 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:10Z") ]] - StandardDateTimeTests = [[ DateTime.civil(2005,2,1,15,15,10), %("2005-02-01T15:15:10+00:00") ]] + 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) @@ -82,6 +116,8 @@ class TestJSONEncoding < ActiveSupport::TestCase constants.grep(/Tests$/).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| @@ -89,16 +125,16 @@ class TestJSONEncoding < ActiveSupport::TestCase end ensure ActiveSupport.escape_html_entities_in_json = false - ActiveSupport.use_standard_json_time_format = false + ActiveSupport.use_standard_json_time_format = prev end end end - def test_json_variable - assert_deprecated do - assert_equal ActiveSupport::JSON::Variable.new('foo'), 'foo' - assert_equal ActiveSupport::JSON::Variable.new('alert("foo")'), 'alert("foo")' - end + def test_process_status + # There doesn't seem to be a good way to get a handle on a Process::Status object without actually + # creating a child process, hence this to populate $? + system("not_a_real_program_#{SecureRandom.hex}") + assert_equal %({"exitstatus":#{$?.exitstatus},"pid":#{$?.pid}}), ActiveSupport::JSON.encode($?) end def test_hash_encoding @@ -140,22 +176,44 @@ class TestJSONEncoding < ActiveSupport::TestCase assert_equal "𐒑", decoded_hash['string'] end + def test_reading_encode_big_decimal_as_string_option + assert_deprecated do + assert ActiveSupport.encode_big_decimal_as_string + end + end + + def test_setting_deprecated_encode_big_decimal_as_string_option + assert_raise(NotImplementedError) do + ActiveSupport.encode_big_decimal_as_string = true + end + + assert_raise(NotImplementedError) do + ActiveSupport.encode_big_decimal_as_string = false + end + end + def test_exception_raised_when_encoding_circular_reference_in_array a = [1] a << a - assert_raise(ActiveSupport::JSON::Encoding::CircularReferenceError) { ActiveSupport::JSON.encode(a) } + assert_deprecated do + assert_raise(ActiveSupport::JSON::Encoding::CircularReferenceError) { ActiveSupport::JSON.encode(a) } + end end def test_exception_raised_when_encoding_circular_reference_in_hash a = { :name => 'foo' } a[:next] = a - assert_raise(ActiveSupport::JSON::Encoding::CircularReferenceError) { ActiveSupport::JSON.encode(a) } + assert_deprecated do + assert_raise(ActiveSupport::JSON::Encoding::CircularReferenceError) { ActiveSupport::JSON.encode(a) } + end end def test_exception_raised_when_encoding_circular_reference_in_hash_inside_array a = { :name => 'foo', :sub => [] } a[:sub] << a - assert_raise(ActiveSupport::JSON::Encoding::CircularReferenceError) { ActiveSupport::JSON.encode(a) } + assert_deprecated do + assert_raise(ActiveSupport::JSON::Encoding::CircularReferenceError) { ActiveSupport::JSON.encode(a) } + end end def test_hash_key_identifiers_are_always_quoted @@ -172,23 +230,24 @@ class TestJSONEncoding < ActiveSupport::TestCase end def test_time_to_json_includes_local_offset - ActiveSupport.use_standard_json_time_format = true - with_env_tz 'US/Eastern' do - assert_equal %("2005-02-01T15:15:10-05:00"), ActiveSupport::JSON.encode(Time.local(2005,2,1,15,15,10)) + with_standard_json_time_format(true) do + with_env_tz 'US/Eastern' do + assert_equal %("2005-02-01T15:15:10.000-05:00"), ActiveSupport::JSON.encode(Time.local(2005,2,1,15,15,10)) + end end - ensure - ActiveSupport.use_standard_json_time_format = false end def test_hash_with_time_to_json - assert_equal '{"time":"2009/01/01 00:00:00 +0000"}', { :time => Time.utc(2009) }.to_json + with_standard_json_time_format(false) do + assert_equal '{"time":"2009/01/01 00:00:00 +0000"}', { :time => Time.utc(2009) }.to_json + end end def test_nested_hash_with_float assert_nothing_raised do hash = { "CHI" => { - :dislay_name => "chicago", + :display_name => "chicago", :latitude => 123.234 } } @@ -196,6 +255,31 @@ class TestJSONEncoding < ActiveSupport::TestCase end end + def test_hash_like_with_options + h = Hashlike.new + json = h.to_json :only => [:foo] + + assert_equal({"foo"=>"hello"}, JSON.parse(json)) + end + + def test_object_to_json_with_options + obj = Object.new + obj.instance_variable_set :@foo, "hello" + obj.instance_variable_set :@bar, "world" + json = obj.to_json :only => ["foo"] + + assert_equal({"foo"=>"hello"}, JSON.parse(json)) + end + + def test_struct_to_json_with_options + struct = Struct.new(:foo, :bar).new + struct.foo = "hello" + struct.bar = "world" + json = struct.to_json :only => [:foo] + + assert_equal({"foo"=>"hello"}, JSON.parse(json)) + end + def test_hash_should_pass_encoding_options_to_children_in_as_json person = { :name => 'John', @@ -246,12 +330,39 @@ class TestJSONEncoding < ActiveSupport::TestCase assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json) end - def test_enumerable_should_pass_encoding_options_to_children_in_as_json - people = [ - { :name => 'John', :address => { :city => 'London', :country => 'UK' }}, - { :name => 'Jean', :address => { :city => 'Paris' , :country => 'France' }} + People = Class.new(BasicObject) do + include Enumerable + def initialize() + @people = [ + { :name => 'John', :address => { :city => 'London', :country => 'UK' }}, + { :name => 'Jean', :address => { :city => 'Paris' , :country => 'France' }} + ] + end + def each(*, &blk) + @people.each do |p| + yield p if blk + p + end.each + end + end + + def test_enumerable_should_generate_json_with_as_json + json = People.new.as_json :only => [:address, :city] + expected = [ + { 'address' => { 'city' => 'London' }}, + { 'address' => { 'city' => 'Paris' }} ] - json = people.each.as_json :only => [:address, :city] + + assert_equal(expected, json) + end + + def test_enumerable_should_generate_json_with_to_json + json = People.new.to_json :only => [:address, :city] + assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json) + end + + def test_enumerable_should_pass_encoding_options_to_children_in_as_json + json = People.new.each.as_json :only => [:address, :city] expected = [ { 'address' => { 'city' => 'London' }}, { 'address' => { 'city' => 'Paris' }} @@ -261,23 +372,39 @@ class TestJSONEncoding < ActiveSupport::TestCase end def test_enumerable_should_pass_encoding_options_to_children_in_to_json - people = [ - { :name => 'John', :address => { :city => 'London', :country => 'UK' }}, - { :name => 'Jean', :address => { :city => 'Paris' , :country => 'France' }} - ] - json = people.each.to_json :only => [:address, :city] + json = People.new.each.to_json :only => [:address, :city] assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json) end - def test_to_json_should_not_keep_options_around + def test_hash_to_json_should_not_keep_options_around f = CustomWithOptions.new f.foo = "hello" f.bar = "world" hash = {"foo" => f, "other_hash" => {"foo" => "other_foo", "test" => "other_test"}} assert_equal({"foo"=>{"foo"=>"hello","bar"=>"world"}, - "other_hash" => {"foo"=>"other_foo","test"=>"other_test"}}, JSON.parse(hash.to_json)) + "other_hash" => {"foo"=>"other_foo","test"=>"other_test"}}, ActiveSupport::JSON.decode(hash.to_json)) + end + + def test_array_to_json_should_not_keep_options_around + f = CustomWithOptions.new + f.foo = "hello" + f.bar = "world" + + array = [f, {"foo" => "other_foo", "test" => "other_test"}] + assert_equal([{"foo"=>"hello","bar"=>"world"}, + {"foo"=>"other_foo","test"=>"other_test"}], ActiveSupport::JSON.decode(array.to_json)) + end + + def test_hash_as_json_without_options + json = { foo: OptionsTest.new }.as_json + assert_equal({"foo" => :default}, json) + end + + def test_array_as_json_without_options + json = [ OptionsTest.new ].as_json + assert_equal([:default], json) end def test_struct_encoding @@ -302,24 +429,13 @@ class TestJSONEncoding < ActiveSupport::TestCase assert_equal({"name" => "David", "sub" => { "name" => "David", - "date" => "2010/01/01" }}, JSON.parse(json_custom)) + "date" => "2010-01-01" }}, ActiveSupport::JSON.decode(json_custom)) assert_equal({"name" => "David", "email" => "sample@example.com"}, - JSON.parse(json_strings)) - - assert_equal({"name" => "David", "date" => "2010/01/01"}, - JSON.parse(json_string_and_date)) - end - - def test_opt_out_big_decimal_string_serialization - big_decimal = BigDecimal('2.5') + ActiveSupport::JSON.decode(json_strings)) - begin - ActiveSupport.encode_big_decimal_as_string = false - assert_equal big_decimal.to_s, big_decimal.to_json - ensure - ActiveSupport.encode_big_decimal_as_string = true - end + assert_equal({"name" => "David", "date" => "2010-01-01"}, + ActiveSupport::JSON.decode(json_string_and_date)) end def test_nil_true_and_false_represented_as_themselves @@ -328,16 +444,104 @@ class TestJSONEncoding < ActiveSupport::TestCase assert_equal false, false.as_json end + def test_json_gem_dump_by_passing_active_support_encoder + h = HashWithAsJson.new + h[:foo] = "hello" + h[:bar] = "world" + + assert_equal %({"foo":"hello","bar":"world"}), JSON.dump(h) + assert_nil h.as_json_called + end + + def test_json_gem_generate_by_passing_active_support_encoder + h = HashWithAsJson.new + h[:foo] = "hello" + h[:bar] = "world" + + assert_equal %({"foo":"hello","bar":"world"}), JSON.generate(h) + assert_nil h.as_json_called + end + + def test_json_gem_pretty_generate_by_passing_active_support_encoder + h = HashWithAsJson.new + h[:foo] = "hello" + h[:bar] = "world" + + assert_equal <<EXPECTED.chomp, JSON.pretty_generate(h) +{ + "foo": "hello", + "bar": "world" +} +EXPECTED + assert_nil h.as_json_called + end + + def test_twz_to_json_with_use_standard_json_time_format_config_set_to_false + with_standard_json_time_format(false) do + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone) + assert_equal "\"1999/12/31 19:00:00 -0500\"", ActiveSupport::JSON.encode(time) + end + end + + def test_twz_to_json_with_use_standard_json_time_format_config_set_to_true + with_standard_json_time_format(true) do + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone) + assert_equal "\"1999-12-31T19:00:00.000-05:00\"", ActiveSupport::JSON.encode(time) + end + end + + def test_twz_to_json_with_custom_time_precision + with_standard_json_time_format(true) do + with_time_precision(0) do + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone) + assert_equal "\"1999-12-31T19:00:00-05:00\"", ActiveSupport::JSON.encode(time) + end + end + end + + def test_time_to_json_with_custom_time_precision + with_standard_json_time_format(true) do + with_time_precision(0) do + assert_equal "\"2000-01-01T00:00:00Z\"", ActiveSupport::JSON.encode(Time.utc(2000)) + end + end + end + + def test_datetime_to_json_with_custom_time_precision + with_standard_json_time_format(true) do + with_time_precision(0) do + assert_equal "\"2000-01-01T00:00:00+00:00\"", ActiveSupport::JSON.encode(DateTime.new(2000)) + end + end + end + + def test_twz_to_json_when_wrapping_a_date_time + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + time = ActiveSupport::TimeWithZone.new(DateTime.new(2000), zone) + assert_equal '"1999-12-31T19:00:00.000-05:00"', ActiveSupport::JSON.encode(time) + end + protected def object_keys(json_object) json_object[1..-2].scan(/([^{}:,\s]+):/).flatten.sort end - def with_env_tz(new_tz = 'US/Eastern') - old_tz, ENV['TZ'] = ENV['TZ'], new_tz + def with_standard_json_time_format(boolean = true) + old, ActiveSupport.use_standard_json_time_format = ActiveSupport.use_standard_json_time_format, boolean + yield + ensure + ActiveSupport.use_standard_json_time_format = old + end + + def with_time_precision(value) + old_value = ActiveSupport::JSON::Encoding.time_precision + ActiveSupport::JSON::Encoding.time_precision = value yield ensure - old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') + ActiveSupport::JSON::Encoding.time_precision = old_value end end diff --git a/activesupport/test/key_generator_test.rb b/activesupport/test/key_generator_test.rb index 525082d313..f7e8e9a795 100644 --- a/activesupport/test/key_generator_test.rb +++ b/activesupport/test/key_generator_test.rb @@ -29,4 +29,34 @@ class KeyGeneratorTest < ActiveSupport::TestCase end end +class CachingKeyGeneratorTest < ActiveSupport::TestCase + def setup + @secret = SecureRandom.hex(64) + @generator = ActiveSupport::KeyGenerator.new(@secret, :iterations=>2) + @caching_generator = ActiveSupport::CachingKeyGenerator.new(@generator) + end + + test "Generating a cached key for same salt and key size" do + derived_key = @caching_generator.generate_key("some_salt", 32) + cached_key = @caching_generator.generate_key("some_salt", 32) + + assert_equal derived_key, cached_key + assert_equal derived_key.object_id, cached_key.object_id + end + + test "Does not cache key for different salt" do + derived_key = @caching_generator.generate_key("some_salt", 32) + different_salt_key = @caching_generator.generate_key("other_salt", 32) + + assert_not_equal derived_key, different_salt_key + end + + test "Does not cache key for different length" do + derived_key = @caching_generator.generate_key("some_salt", 32) + different_length_key = @caching_generator.generate_key("some_salt", 64) + + assert_not_equal derived_key, different_length_key + end +end + end diff --git a/activesupport/test/load_paths_test.rb b/activesupport/test/load_paths_test.rb index 979e25bdf3..ac617a9fd8 100644 --- a/activesupport/test/load_paths_test.rb +++ b/activesupport/test/load_paths_test.rb @@ -10,7 +10,7 @@ class LoadPathsTest < ActiveSupport::TestCase } load_paths_count[File.expand_path('../../lib', __FILE__)] -= 1 - filtered = load_paths_count.select { |k, v| v > 1 } - assert filtered.empty?, filtered.inspect + load_paths_count.select! { |k, v| v > 1 } + assert load_paths_count.empty?, load_paths_count.inspect end end diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb index 06c7e8a1a7..eb71369397 100644 --- a/activesupport/test/message_encryptor_test.rb +++ b/activesupport/test/message_encryptor_test.rb @@ -1,12 +1,5 @@ require 'abstract_unit' - -begin - require 'openssl' - OpenSSL::Digest::SHA1 -rescue LoadError, NameError - $stderr.puts "Skipping MessageEncryptor test: broken OpenSSL install" -else - +require 'openssl' require 'active_support/time' require 'active_support/json' @@ -56,9 +49,25 @@ class MessageEncryptorTest < ActiveSupport::TestCase end def test_alternative_serialization_method + prev = ActiveSupport.use_standard_json_time_format + ActiveSupport.use_standard_json_time_format = true encryptor = ActiveSupport::MessageEncryptor.new(SecureRandom.hex(64), SecureRandom.hex(64), :serializer => JSONSerializer.new) message = encryptor.encrypt_and_sign({ :foo => 123, 'bar' => Time.utc(2010) }) - assert_equal encryptor.decrypt_and_verify(message), { "foo" => 123, "bar" => "2010-01-01T00:00:00Z" } + exp = { "foo" => 123, "bar" => "2010-01-01T00:00:00.000Z" } + assert_equal exp, encryptor.decrypt_and_verify(message) + ensure + ActiveSupport.use_standard_json_time_format = prev + end + + def test_message_obeys_strict_encoding + bad_encoding_characters = "\n!@#" + message, iv = @encryptor.encrypt_and_sign("This is a very \n\nhumble string"+bad_encoding_characters) + + assert_not_decrypted("#{::Base64.encode64 message.to_s}--#{::Base64.encode64 iv.to_s}") + assert_not_verified("#{::Base64.encode64 message.to_s}--#{::Base64.encode64 iv.to_s}") + + assert_not_decrypted([iv, message] * bad_encoding_characters) + assert_not_verified([iv, message] * bad_encoding_characters) end private @@ -76,10 +85,8 @@ class MessageEncryptorTest < ActiveSupport::TestCase end def munge(base64_string) - bits = ::Base64.decode64(base64_string) + bits = ::Base64.strict_decode64(base64_string) bits.reverse! ::Base64.strict_encode64(bits) end end - -end diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb index 5adff41653..6c3519df9a 100644 --- a/activesupport/test/message_verifier_test.rb +++ b/activesupport/test/message_verifier_test.rb @@ -1,17 +1,10 @@ require 'abstract_unit' - -begin - require 'openssl' - OpenSSL::Digest::SHA1 -rescue LoadError, NameError - $stderr.puts "Skipping MessageVerifier test: broken OpenSSL install" -else - +require 'openssl' require 'active_support/time' require 'active_support/json' class MessageVerifierTest < ActiveSupport::TestCase - + class JSONSerializer def dump(value) ActiveSupport::JSON.encode(value) @@ -21,40 +14,72 @@ class MessageVerifierTest < ActiveSupport::TestCase ActiveSupport::JSON.decode(value) end end - + def setup @verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!") @data = { :some => "data", :now => Time.local(2010) } end + def test_valid_message + data, hash = @verifier.generate(@data).split("--") + assert !@verifier.valid_message?(nil) + assert !@verifier.valid_message?("") + assert !@verifier.valid_message?("#{data.reverse}--#{hash}") + assert !@verifier.valid_message?("#{data}--#{hash.reverse}") + assert !@verifier.valid_message?("purejunk") + end + def test_simple_round_tripping message = @verifier.generate(@data) + assert_equal @data, @verifier.verified(message) assert_equal @data, @verifier.verify(message) end - def test_missing_signature_raises - assert_not_verified(nil) - assert_not_verified("") + def test_verified_returns_false_on_invalid_message + assert !@verifier.verified("purejunk") end - def test_tampered_data_raises - data, hash = @verifier.generate(@data).split("--") - assert_not_verified("#{data.reverse}--#{hash}") - assert_not_verified("#{data}--#{hash.reverse}") - assert_not_verified("purejunk") + def test_verify_exception_on_invalid_message + assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do + @verifier.verify("purejunk") + end end - + def test_alternative_serialization_method + prev = ActiveSupport.use_standard_json_time_format + ActiveSupport.use_standard_json_time_format = true verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!", :serializer => JSONSerializer.new) message = verifier.generate({ :foo => 123, 'bar' => Time.utc(2010) }) - assert_equal verifier.verify(message), { "foo" => 123, "bar" => "2010-01-01T00:00:00Z" } + exp = { "foo" => 123, "bar" => "2010-01-01T00:00:00.000Z" } + assert_equal exp, verifier.verified(message) + assert_equal exp, verifier.verify(message) + ensure + ActiveSupport.use_standard_json_time_format = prev end - - def assert_not_verified(message) - assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do - @verifier.verify(message) + + def test_raise_error_when_argument_class_is_not_loaded + # To generate the valid message below: + # + # AutoloadClass = Struct.new(:foo) + # valid_message = @verifier.generate(foo: AutoloadClass.new('foo')) + # + valid_message = "BAh7BjoIZm9vbzonTWVzc2FnZVZlcmlmaWVyVGVzdDo6QXV0b2xvYWRDbGFzcwY6CUBmb29JIghmb28GOgZFVA==--f3ef39a5241c365083770566dc7a9eb5d6ace914" + exception = assert_raise(ArgumentError, NameError) do + @verifier.verified(valid_message) end + assert_includes ["uninitialized constant MessageVerifierTest::AutoloadClass", + "undefined class/module MessageVerifierTest::AutoloadClass"], exception.message + exception = assert_raise(ArgumentError, NameError) do + @verifier.verify(valid_message) + end + assert_includes ["uninitialized constant MessageVerifierTest::AutoloadClass", + "undefined class/module MessageVerifierTest::AutoloadClass"], exception.message end -end + def test_raise_error_when_secret_is_nil + exception = assert_raise(ArgumentError) do + ActiveSupport::MessageVerifier.new(nil) + end + assert_equal exception.message, 'Secret should not be nil.' + end end diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb index 2bf73291a2..94748dd991 100644 --- a/activesupport/test/multibyte_chars_test.rb +++ b/activesupport/test/multibyte_chars_test.rb @@ -3,19 +3,12 @@ require 'abstract_unit' require 'multibyte_test_helpers' require 'active_support/core_ext/string/multibyte' -class String - def __method_for_multibyte_testing_with_integer_result; 1; end - def __method_for_multibyte_testing; 'result'; end - def __method_for_multibyte_testing!; 'result'; end - def __method_for_multibyte_testing_that_returns_nil!; end -end - class MultibyteCharsTest < ActiveSupport::TestCase include MultibyteTestHelpers def setup @proxy_class = ActiveSupport::Multibyte::Chars - @chars = @proxy_class.new UNICODE_STRING + @chars = @proxy_class.new UNICODE_STRING.dup end def test_wraps_the_original_string @@ -24,6 +17,8 @@ class MultibyteCharsTest < ActiveSupport::TestCase end def test_should_allow_method_calls_to_string + @chars.wrapped_string.singleton_class.class_eval { def __method_for_multibyte_testing; 'result'; end } + assert_nothing_raised do @chars.__method_for_multibyte_testing end @@ -33,28 +28,35 @@ class MultibyteCharsTest < ActiveSupport::TestCase end def test_forwarded_method_calls_should_return_new_chars_instance + @chars.wrapped_string.singleton_class.class_eval { def __method_for_multibyte_testing; 'result'; end } + assert_kind_of @proxy_class, @chars.__method_for_multibyte_testing assert_not_equal @chars.object_id, @chars.__method_for_multibyte_testing.object_id end def test_forwarded_bang_method_calls_should_return_the_original_chars_instance_when_result_is_not_nil + @chars.wrapped_string.singleton_class.class_eval { def __method_for_multibyte_testing!; 'result'; end } + assert_kind_of @proxy_class, @chars.__method_for_multibyte_testing! assert_equal @chars.object_id, @chars.__method_for_multibyte_testing!.object_id end def test_forwarded_bang_method_calls_should_return_nil_when_result_is_nil + @chars.wrapped_string.singleton_class.class_eval { def __method_for_multibyte_testing_that_returns_nil!; end } + assert_nil @chars.__method_for_multibyte_testing_that_returns_nil! end def test_methods_are_forwarded_to_wrapped_string_for_byte_strings - original_encoding = BYTE_STRING.encoding assert_equal BYTE_STRING.length, BYTE_STRING.mb_chars.length - ensure - BYTE_STRING.force_encoding(original_encoding) end def test_forwarded_method_with_non_string_result_should_be_returned_vertabim - assert_equal ''.__method_for_multibyte_testing_with_integer_result, @chars.__method_for_multibyte_testing_with_integer_result + str = '' + str.singleton_class.class_eval { def __method_for_multibyte_testing_with_integer_result; 1; end } + @chars.wrapped_string.singleton_class.class_eval { def __method_for_multibyte_testing_with_integer_result; 1; end } + + assert_equal str.__method_for_multibyte_testing_with_integer_result, @chars.__method_for_multibyte_testing_with_integer_result end def test_should_concatenate @@ -103,7 +105,6 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase @chars = UNICODE_STRING.dup.mb_chars # Ruby 1.9 only supports basic whitespace @whitespace = "\n\t " - @byte_order_mark = [65279].pack('U') end def test_split_should_return_an_array_of_chars_instances @@ -181,7 +182,7 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase end def test_sortability - words = %w(builder armor zebra).sort_by { |s| s.mb_chars } + words = %w(builder armor zebra).sort_by(&:mb_chars) assert_equal %w(armor builder zebra), words end @@ -221,9 +222,9 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase end def test_include_raises_when_nil_is_passed - @chars.include?(nil) - flunk "Expected chars.include?(nil) to raise TypeError or NoMethodError" - rescue Exception + assert_raises TypeError, NoMethodError, "Expected chars.include?(nil) to raise TypeError or NoMethodError" do + @chars.include?(nil) + end end def test_index_should_return_character_offset diff --git a/activesupport/test/multibyte_conformance.rb b/activesupport/test/multibyte_conformance_test.rb index 2baf724da4..f7bd21c8ab 100644 --- a/activesupport/test/multibyte_conformance.rb +++ b/activesupport/test/multibyte_conformance_test.rb @@ -10,7 +10,6 @@ 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 @@ -20,8 +19,9 @@ class Downloader target.write l end end - end - end + end + end + true end end @@ -31,11 +31,15 @@ class MultibyteConformanceTest < ActiveSupport::TestCase UNIDATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}/ucd" UNIDATA_FILE = '/NormalizationTest.txt' CACHE_DIR = File.join(Dir.tmpdir, 'cache') + FileUtils.mkdir_p(CACHE_DIR) + RUN_P = begin + Downloader.download(UNIDATA_URL + UNIDATA_FILE, CACHE_DIR + UNIDATA_FILE) + rescue + end def setup - FileUtils.mkdir_p(CACHE_DIR) - Downloader.download(UNIDATA_URL + UNIDATA_FILE, CACHE_DIR + UNIDATA_FILE) @proxy = ActiveSupport::Multibyte::Chars + skip "Unable to download test data" unless RUN_P end def test_normalizations_C @@ -111,7 +115,7 @@ class MultibyteConformanceTest < ActiveSupport::TestCase next if (line.empty? || line =~ /^\#/) cols, comment = line.split("#") - cols = cols.split(";").map{|e| e.strip}.reject{|e| e.empty? } + cols = cols.split(";").map(&:strip).reject(&:empty?) next unless cols.length == 5 # codepoints are in hex in the test suite, pack wants them as integers @@ -126,4 +130,4 @@ class MultibyteConformanceTest < ActiveSupport::TestCase def inspect_codepoints(str) str.to_s.unpack("U*").map{|cp| cp.to_s(16) }.join(' ') end -end
\ No newline at end of file +end diff --git a/activesupport/test/multibyte_proxy_test.rb b/activesupport/test/multibyte_proxy_test.rb new file mode 100644 index 0000000000..d8ffd7ca9c --- /dev/null +++ b/activesupport/test/multibyte_proxy_test.rb @@ -0,0 +1,34 @@ +# encoding: utf-8 + +require 'abstract_unit' + +class MultibyteProxyText < ActiveSupport::TestCase + class AsciiOnlyEncoder + attr_reader :wrapped_string + alias to_s wrapped_string + + def initialize(string) + @wrapped_string = string.gsub(/[^\u0000-\u007F]/, '?') + end + end + + def with_custom_encoder(encoder) + original_proxy_class = ActiveSupport::Multibyte.proxy_class + + begin + ActiveSupport::Multibyte.proxy_class = encoder + + yield + ensure + ActiveSupport::Multibyte.proxy_class = original_proxy_class + end + end + + test "custom multibyte encoder" do + with_custom_encoder(AsciiOnlyEncoder) do + assert_equal "s?me string 123", "søme string 123".mb_chars.to_s + end + + assert_equal "søme string 123", "søme string 123".mb_chars.to_s + end +end diff --git a/activesupport/test/multibyte_test_helpers.rb b/activesupport/test/multibyte_test_helpers.rb index fdbe2f4350..90af2dadd4 100644 --- a/activesupport/test/multibyte_test_helpers.rb +++ b/activesupport/test/multibyte_test_helpers.rb @@ -1,9 +1,9 @@ # encoding: utf-8 module MultibyteTestHelpers - UNICODE_STRING = 'こにちわ' - ASCII_STRING = 'ohayo' - BYTE_STRING = "\270\236\010\210\245".force_encoding("ASCII-8BIT") + UNICODE_STRING = 'こにちわ'.freeze + ASCII_STRING = 'ohayo'.freeze + BYTE_STRING = "\270\236\010\210\245".force_encoding("ASCII-8BIT").freeze def chars(str) ActiveSupport::Multibyte::Chars.new(str) diff --git a/activesupport/test/notifications/instrumenter_test.rb b/activesupport/test/notifications/instrumenter_test.rb index 62a9b61464..f46e96f636 100644 --- a/activesupport/test/notifications/instrumenter_test.rb +++ b/activesupport/test/notifications/instrumenter_test.rb @@ -34,6 +34,14 @@ module ActiveSupport assert called end + def test_instrument_yields_the_payload_for_further_modification + assert_equal 2, instrumenter.instrument("awesome") { |p| p[:result] = 1 + 1 } + assert_equal 1, notifier.finishes.size + name, _, payload = notifier.finishes.first + assert_equal "awesome", name + assert_equal Hash[:result => 2], payload + end + def test_start instrumenter.start("foo", payload) assert_equal [["foo", instrumenter.id, payload]], notifier.starts diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb index bcb393c7bc..f729f0a95b 100644 --- a/activesupport/test/notifications_test.rb +++ b/activesupport/test/notifications_test.rb @@ -81,6 +81,20 @@ module Notifications end end + class TestSubscriber + attr_reader :starts, :finishes, :publishes + + def initialize + @starts = [] + @finishes = [] + @publishes = [] + end + + def start(*args); @starts << args; end + def finish(*args); @finishes << args; end + def publish(*args); @publishes << args; end + end + class SyncPubSubTest < TestCase def test_events_are_published_to_a_listener @notifier.publish :foo @@ -99,7 +113,7 @@ module Notifications @notifier.publish :foo @notifier.publish :foo - @notifier.subscribe("not_existant") do |*args| + @notifier.subscribe("not_existent") do |*args| @events << ActiveSupport::Notifications::Event.new(*args) end @@ -144,6 +158,14 @@ module Notifications assert_equal [[:foo]], @another end + def test_publish_with_subscriber + subscriber = TestSubscriber.new + @notifier.subscribe nil, subscriber + @notifier.publish :foo + + assert_equal [[:foo]], subscriber.publishes + end + private def event(*args) args @@ -157,7 +179,7 @@ module Notifications assert_equal 2, instrument(:awesome) { 1 + 1 } end - def test_instrument_yields_the_paylod_for_further_modification + def test_instrument_yields_the_payload_for_further_modification assert_equal 2, instrument(:awesome) { |p| p[:result] = 1 + 1 } assert_equal 1, @events.size assert_equal :awesome, @events.first.name diff --git a/activesupport/test/number_helper_i18n_test.rb b/activesupport/test/number_helper_i18n_test.rb index 65aecece71..e6925e9083 100644 --- a/activesupport/test/number_helper_i18n_test.rb +++ b/activesupport/test/number_helper_i18n_test.rb @@ -43,6 +43,10 @@ module ActiveSupport :custom_units_for_number_to_human => {:mili => "mm", :centi => "cm", :deci => "dm", :unit => "m", :ten => "dam", :hundred => "hm", :thousand => "km"} end + def teardown + I18n.backend.reload! + end + def test_number_to_i18n_currency assert_equal("&$ - 10.00", number_to_currency(10, :locale => 'ts')) assert_equal("(&$ - 10.00)", number_to_currency(-10, :locale => 'ts')) @@ -50,8 +54,6 @@ module ActiveSupport end def test_number_to_currency_with_empty_i18n_store - I18n.backend.store_translations 'empty', {} - assert_equal("$10.00", number_to_currency(10, :locale => 'empty')) assert_equal("-$10.00", number_to_currency(-10, :locale => 'empty')) end @@ -80,8 +82,6 @@ module ActiveSupport end def test_number_with_i18n_precision_and_empty_i18n_store - I18n.backend.store_translations 'empty', {} - assert_equal("123456789.123", number_to_rounded(123456789.123456789, :locale => 'empty')) assert_equal("1.000", number_to_rounded(1.0000, :locale => 'empty')) end @@ -92,8 +92,6 @@ module ActiveSupport end def test_number_with_i18n_delimiter_and_empty_i18n_store - I18n.backend.store_translations 'empty', {} - assert_equal("1,000,000.234", number_to_delimited(1000000.234, :locale => 'empty')) end @@ -107,8 +105,6 @@ module ActiveSupport end def test_number_to_i18n_percentage_and_empty_i18n_store - I18n.backend.store_translations 'empty', {} - assert_equal("1.000%", number_to_percentage(1, :locale => 'empty')) assert_equal("1.243%", number_to_percentage(1.2434, :locale => 'empty')) assert_equal("12434.000%", number_to_percentage(12434, :locale => 'empty')) @@ -121,8 +117,6 @@ module ActiveSupport end def test_number_to_i18n_human_size_with_empty_i18n_store - I18n.backend.store_translations 'empty', {} - assert_equal("2 KB", number_to_human_size(2048, :locale => 'empty')) assert_equal("42 Bytes", number_to_human_size(42, :locale => 'empty')) end @@ -142,8 +136,6 @@ module ActiveSupport end def test_number_to_human_with_empty_i18n_store - I18n.backend.store_translations 'empty', {} - assert_equal "2 Thousand", number_to_human(2000, :locale => 'empty') assert_equal "1.23 Billion", number_to_human(1234567890, :locale => 'empty') end diff --git a/activesupport/test/number_helper_test.rb b/activesupport/test/number_helper_test.rb index 5f54587f93..50d84a9470 100644 --- a/activesupport/test/number_helper_test.rb +++ b/activesupport/test/number_helper_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'active_support/number_helper' +require 'active_support/core_ext/string/output_safety' module ActiveSupport module NumberHelper @@ -79,6 +80,9 @@ module ActiveSupport assert_equal("123.4%", number_helper.number_to_percentage(123.400, :precision => 3, :strip_insignificant_zeros => true)) assert_equal("1.000,000%", number_helper.number_to_percentage(1000, :delimiter => '.', :separator => ',')) assert_equal("1000.000 %", number_helper.number_to_percentage(1000, :format => "%n %")) + assert_equal("98a%", number_helper.number_to_percentage("98a")) + assert_equal("NaN%", number_helper.number_to_percentage(Float::NAN)) + assert_equal("Inf%", number_helper.number_to_percentage(Float::INFINITY)) end end @@ -94,6 +98,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("123,456.78", number_helper.number_to_delimited("123456.78".html_safe)) end end @@ -102,7 +107,6 @@ module ActiveSupport assert_equal '12 345 678', number_helper.number_to_delimited(12345678, :delimiter => ' ') assert_equal '12,345,678-05', number_helper.number_to_delimited(12345678.05, :separator => '-') assert_equal '12.345.678,05', number_helper.number_to_delimited(12345678.05, :separator => ',', :delimiter => '.') - assert_equal '12.345.678,05', number_helper.number_to_delimited(12345678.05, :delimiter => '.', :separator => ',') end end @@ -124,6 +128,14 @@ module ActiveSupport assert_equal("10.00", number_helper.number_to_rounded(9.995, :precision => 2)) assert_equal("11.00", number_helper.number_to_rounded(10.995, :precision => 2)) assert_equal("0.00", number_helper.number_to_rounded(-0.001, :precision => 2)) + + assert_equal("111.23460000000000000000", number_helper.number_to_rounded(111.2346, :precision => 20)) + assert_equal("111.23460000000000000000", number_helper.number_to_rounded(Rational(1112346, 10000), :precision => 20)) + assert_equal("111.23460000000000000000", number_helper.number_to_rounded('111.2346', :precision => 20)) + assert_equal("111.23460000000000000000", number_helper.number_to_rounded(BigDecimal(111.2346, Float::DIG), :precision => 20)) + assert_equal("111.2346" + "0"*96, number_helper.number_to_rounded('111.2346', :precision => 100)) + assert_equal("111.2346", number_helper.number_to_rounded(Rational(1112346, 10000), :precision => 4)) + assert_equal('0.00', number_helper.number_to_rounded(Rational(0, 1), :precision => 2)) end end @@ -156,6 +168,15 @@ module ActiveSupport assert_equal "10.0", number_helper.number_to_rounded(9.995, :precision => 3, :significant => true) assert_equal "9.99", number_helper.number_to_rounded(9.994, :precision => 3, :significant => true) assert_equal "11.0", number_helper.number_to_rounded(10.995, :precision => 3, :significant => true) + + assert_equal "9775.0000000000000000", number_helper.number_to_rounded(9775, :precision => 20, :significant => true ) + assert_equal "9775.0000000000000000", number_helper.number_to_rounded(9775.0, :precision => 20, :significant => true ) + assert_equal "9775.0000000000000000", number_helper.number_to_rounded(Rational(9775, 1), :precision => 20, :significant => true ) + assert_equal "97.750000000000000000", number_helper.number_to_rounded(Rational(9775, 100), :precision => 20, :significant => true ) + assert_equal "9775.0000000000000000", number_helper.number_to_rounded(BigDecimal(9775), :precision => 20, :significant => true ) + assert_equal "9775.0000000000000000", number_helper.number_to_rounded("9775", :precision => 20, :significant => true ) + assert_equal "9775." + "0"*96, number_helper.number_to_rounded("9775", :precision => 100, :significant => true ) + assert_equal("97.7", number_helper.number_to_rounded(Rational(9772, 100), :precision => 3, :significant => true)) end end @@ -301,6 +322,13 @@ module ActiveSupport end end + def test_number_to_human_with_custom_units_that_are_missing_the_needed_key + [@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper| + assert_equal '123', number_helper.number_to_human(123, units: { thousand: 'k'}) + assert_equal '123', number_helper.number_to_human(123, units: {}) + end + end + def test_number_to_human_with_custom_format [@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper| assert_equal '123 times Thousand', number_helper.number_to_human(123456, :format => "%n times %u") @@ -363,12 +391,6 @@ module ActiveSupport end end - def test_extending_or_including_number_helper_correctly_hides_private_methods - [@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper| - assert !number_helper.respond_to?(:valid_float?) - assert number_helper.respond_to?(:valid_float?, true) - end - end end end end diff --git a/activesupport/test/option_merger_test.rb b/activesupport/test/option_merger_test.rb index 9d139b61b8..4c0364e68b 100644 --- a/activesupport/test/option_merger_test.rb +++ b/activesupport/test/option_merger_test.rb @@ -79,6 +79,15 @@ class OptionMergerTest < ActiveSupport::TestCase assert_equal ActiveSupport::OptionMerger, ActiveSupport::OptionMerger.new('', '').class end + def test_option_merger_implicit_receiver + @options.with_options foo: "bar" do + merge! fizz: "buzz" + end + + expected = { hello: "world", foo: "bar", fizz: "buzz" } + assert_equal expected, @options + end + private def method_with_options(options = {}) options diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index 14ba4e0076..460a61613e 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -1,7 +1,8 @@ require 'abstract_unit' require 'active_support/json' -require 'active_support/core_ext/object/to_json' +require 'active_support/core_ext/object/json' require 'active_support/core_ext/hash/indifferent_access' +require 'active_support/core_ext/array/extract_options' class OrderedHashTest < ActiveSupport::TestCase def setup @@ -119,7 +120,9 @@ class OrderedHashTest < ActiveSupport::TestCase end def test_select - assert_equal @keys, @ordered_hash.select { true }.map(&:first) + new_ordered_hash = @ordered_hash.select { true } + assert_equal @keys, new_ordered_hash.map(&:first) + assert_instance_of ActiveSupport::OrderedHash, new_ordered_hash end def test_delete_if @@ -142,6 +145,7 @@ class OrderedHashTest < ActiveSupport::TestCase assert_equal copy, @ordered_hash assert !new_ordered_hash.keys.include?('pink') assert @ordered_hash.keys.include?('pink') + assert_instance_of ActiveSupport::OrderedHash, new_ordered_hash end def test_clear @@ -243,11 +247,7 @@ class OrderedHashTest < ActiveSupport::TestCase end def test_each_after_yaml_serialization - values = [] - @deserialized_ordered_hash = YAML.load(YAML.dump(@ordered_hash)) - - @deserialized_ordered_hash.each {|key, value| values << value} - assert_equal @values, values + assert_equal @values, YAML.load(YAML.dump(@ordered_hash)).values end def test_each_when_yielding_to_block_with_splat diff --git a/activesupport/test/ordered_options_test.rb b/activesupport/test/ordered_options_test.rb index f60f9a58e3..fdc745b23b 100644 --- a/activesupport/test/ordered_options_test.rb +++ b/activesupport/test/ordered_options_test.rb @@ -7,13 +7,13 @@ class OrderedOptionsTest < ActiveSupport::TestCase assert_nil a[:not_set] - a[:allow_concurreny] = true + a[:allow_concurrency] = true assert_equal 1, a.size - assert a[:allow_concurreny] + assert a[:allow_concurrency] - a[:allow_concurreny] = false + a[:allow_concurrency] = false assert_equal 1, a.size - assert !a[:allow_concurreny] + assert !a[:allow_concurrency] a["else_where"] = 56 assert_equal 2, a.size @@ -23,10 +23,10 @@ class OrderedOptionsTest < ActiveSupport::TestCase def test_looping a = ActiveSupport::OrderedOptions.new - a[:allow_concurreny] = true + a[:allow_concurrency] = true a["else_where"] = 56 - test = [[:allow_concurreny, true], [:else_where, 56]] + test = [[:allow_concurrency, true], [:else_where, 56]] a.each_with_index do |(key, value), index| assert_equal test[index].first, key @@ -39,13 +39,13 @@ class OrderedOptionsTest < ActiveSupport::TestCase assert_nil a.not_set - a.allow_concurreny = true + a.allow_concurrency = true assert_equal 1, a.size - assert a.allow_concurreny + assert a.allow_concurrency - a.allow_concurreny = false + a.allow_concurrency = false assert_equal 1, a.size - assert !a.allow_concurreny + assert !a.allow_concurrency a.else_where = 56 assert_equal 2, a.size diff --git a/activesupport/test/rescuable_test.rb b/activesupport/test/rescuable_test.rb index e099e47e0e..bd43ad0797 100644 --- a/activesupport/test/rescuable_test.rb +++ b/activesupport/test/rescuable_test.rb @@ -12,6 +12,12 @@ end class CoolError < StandardError end +module WeirdError + def self.===(other) + Exception === other && other.respond_to?(:weird?) + end +end + class Stargate attr_accessor :result @@ -29,6 +35,10 @@ class Stargate @result = e.message end + rescue_from WeirdError do + @result = 'weird' + end + def dispatch(method) send(method) rescue Exception => e @@ -47,6 +57,16 @@ class Stargate raise MadRonon.new("dex") end + def weird + StandardError.new.tap do |exc| + def exc.weird? + true + end + + raise exc + end + end + def sos @result = 'killed' end @@ -91,15 +111,20 @@ class RescuableTest < ActiveSupport::TestCase assert_equal 'dex', @stargate.result end + def test_rescue_from_error_dispatchers_with_case_operator + @stargate.dispatch :weird + assert_equal 'weird', @stargate.result + end + def test_rescues_defined_later_are_added_at_end_of_the_rescue_handlers_array - expected = ["WraithAttack", "WraithAttack", "NuclearExplosion", "MadRonon"] - result = @stargate.send(:rescue_handlers).collect {|e| e.first} + expected = ["WraithAttack", "WraithAttack", "NuclearExplosion", "MadRonon", "WeirdError"] + result = @stargate.send(:rescue_handlers).collect(&:first) assert_equal expected, result end - def test_children_should_inherit_rescue_defintions_from_parents_and_child_rescue_should_be_appended - expected = ["WraithAttack", "WraithAttack", "NuclearExplosion", "MadRonon", "CoolError"] - result = @cool_stargate.send(:rescue_handlers).collect {|e| e.first} + def test_children_should_inherit_rescue_definitions_from_parents_and_child_rescue_should_be_appended + expected = ["WraithAttack", "WraithAttack", "NuclearExplosion", "MadRonon", "WeirdError", "CoolError"] + result = @cool_stargate.send(:rescue_handlers).collect(&:first) assert_equal expected, result end end diff --git a/activesupport/test/safe_buffer_test.rb b/activesupport/test/safe_buffer_test.rb index 047b89be2a..4532152996 100644 --- a/activesupport/test/safe_buffer_test.rb +++ b/activesupport/test/safe_buffer_test.rb @@ -140,4 +140,34 @@ class SafeBufferTest < ActiveSupport::TestCase # should still be unsafe assert !y.html_safe?, "should not be safe" end + + test 'Should work with interpolation (array argument)' do + x = 'foo %s bar'.html_safe % ['qux'] + assert_equal 'foo qux bar', x + end + + test 'Should work with interpolation (hash argument)' do + x = 'foo %{x} bar'.html_safe % { x: 'qux' } + assert_equal 'foo qux bar', x + end + + test 'Should escape unsafe interpolated args' do + x = 'foo %{x} bar'.html_safe % { x: '<br/>' } + assert_equal 'foo <br/> bar', x + end + + test 'Should not escape safe interpolated args' do + x = 'foo %{x} bar'.html_safe % { x: '<br/>'.html_safe } + assert_equal 'foo <br/> bar', x + end + + test 'Should interpolate to a safe string' do + x = 'foo %{x} bar'.html_safe % { x: 'qux' } + assert x.html_safe?, 'should be safe' + end + + test 'Should not affect frozen objects when accessing characters' do + x = 'Hello'.html_safe + assert_equal x[/a/, 1], nil + end end diff --git a/activesupport/test/security_utils_test.rb b/activesupport/test/security_utils_test.rb new file mode 100644 index 0000000000..08d2e3baa6 --- /dev/null +++ b/activesupport/test/security_utils_test.rb @@ -0,0 +1,9 @@ +require 'abstract_unit' +require 'active_support/security_utils' + +class SecurityUtilsTest < ActiveSupport::TestCase + def test_secure_compare_should_perform_string_comparison + assert ActiveSupport::SecurityUtils.secure_compare('a', 'a') + assert !ActiveSupport::SecurityUtils.secure_compare('a', 'b') + end +end diff --git a/activesupport/test/subscriber_test.rb b/activesupport/test/subscriber_test.rb new file mode 100644 index 0000000000..a88d8d9eba --- /dev/null +++ b/activesupport/test/subscriber_test.rb @@ -0,0 +1,54 @@ +require 'abstract_unit' +require 'active_support/subscriber' + +class TestSubscriber < ActiveSupport::Subscriber + attach_to :doodle + + cattr_reader :events + + def self.clear + @@events = [] + end + + def open_party(event) + events << event + end + + private + + def private_party(event) + events << event + end +end + +# Monkey patch subscriber to test that only one subscriber per method is added. +class TestSubscriber + remove_method :open_party + def open_party(event) + events << event + end +end + +class SubscriberTest < ActiveSupport::TestCase + def setup + TestSubscriber.clear + end + + def test_attaches_subscribers + ActiveSupport::Notifications.instrument("open_party.doodle") + + assert_equal "open_party.doodle", TestSubscriber.events.first.name + end + + def test_attaches_only_one_subscriber + ActiveSupport::Notifications.instrument("open_party.doodle") + + assert_equal 1, TestSubscriber.events.size + end + + def test_does_not_attach_private_methods + ActiveSupport::Notifications.instrument("private_party.doodle") + + assert_equal [], TestSubscriber.events + end +end diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb index dfe9f3c11c..5e852c8050 100644 --- a/activesupport/test/test_case_test.rb +++ b/activesupport/test/test_case_test.rb @@ -1,118 +1,221 @@ require 'abstract_unit' -module ActiveSupport - class TestCaseTest < ActiveSupport::TestCase - class FakeRunner - attr_reader :puked - - def initialize - @puked = [] +class AssertDifferenceTest < ActiveSupport::TestCase + def setup + @object = Class.new do + attr_accessor :num + def increment + self.num += 1 end - def puke(klass, name, e) - @puked << [klass, name, e] + def decrement + self.num -= 1 end + end.new + @object.num = 0 + end + + def test_assert_not + assert_equal true, assert_not(nil) + assert_equal true, assert_not(false) + + e = assert_raises(Minitest::Assertion) { assert_not true } + assert_equal 'Expected true to be nil or false', e.message + + e = assert_raises(Minitest::Assertion) { assert_not true, 'custom' } + assert_equal 'custom', e.message + end + + def test_assert_no_difference_pass + assert_no_difference '@object.num' do + # ... + end + end - def options - nil + def test_assert_no_difference_fail + error = assert_raises(Minitest::Assertion) do + assert_no_difference '@object.num' do + @object.increment end + end + assert_equal "\"@object.num\" didn't change by 0.\nExpected: 0\n Actual: 1", error.message + end - def record(*args) + def test_assert_no_difference_with_message_fail + error = assert_raises(Minitest::Assertion) do + assert_no_difference '@object.num', 'Object Changed' do + @object.increment end end + assert_equal "Object Changed.\n\"@object.num\" didn't change by 0.\nExpected: 0\n Actual: 1", error.message + end - def test_standard_error_raised_within_setup_callback_is_puked - tc = Class.new(TestCase) do - def self.name; nil; end + def test_assert_difference + assert_difference '@object.num', +1 do + @object.increment + end + end - setup :bad_callback - def bad_callback; raise 'oh noes' end - def test_true; assert true end - end + def test_assert_difference_with_implicit_difference + assert_difference '@object.num' do + @object.increment + end + end - test_name = 'test_true' - fr = FakeRunner.new + def test_arbitrary_expression + assert_difference '@object.num + 1', +2 do + @object.increment + @object.increment + end + end - test = tc.new test_name - test.run fr - klass, name, exception = *fr.puked.first + def test_negative_differences + assert_difference '@object.num', -1 do + @object.decrement + end + end - assert_equal tc, klass - assert_equal test_name, name - assert_equal 'oh noes', exception.message + def test_expression_is_evaluated_in_the_appropriate_scope + silence_warnings do + local_scope = local_scope = 'foo' + assert_difference('local_scope; @object.num') { @object.increment } end + end - def test_standard_error_raised_within_teardown_callback_is_puked - tc = Class.new(TestCase) do - def self.name; nil; end + def test_array_of_expressions + assert_difference [ '@object.num', '@object.num + 1' ], +1 do + @object.increment + end + end - teardown :bad_callback - def bad_callback; raise 'oh noes' end - def test_true; assert true end + def test_array_of_expressions_identify_failure + assert_raises(Minitest::Assertion) do + assert_difference ['@object.num', '1 + 1'] do + @object.increment end + end + end - test_name = 'test_true' - fr = FakeRunner.new + def test_array_of_expressions_identify_failure_when_message_provided + assert_raises(Minitest::Assertion) do + assert_difference ['@object.num', '1 + 1'], 1, 'something went wrong' do + @object.increment + end + end + end +end - test = tc.new test_name - test.run fr - klass, name, exception = *fr.puked.first +class AlsoDoingNothingTest < ActiveSupport::TestCase +end - assert_equal tc, klass - assert_equal test_name, name - assert_equal 'oh noes', exception.message - end +# Setup and teardown callbacks. +class SetupAndTeardownTest < ActiveSupport::TestCase + setup :reset_callback_record, :foo + teardown :foo, :sentinel - def test_passthrough_exception_raised_within_test_method_is_not_rescued - tc = Class.new(TestCase) do - def self.name; nil; end + def test_inherited_setup_callbacks + assert_equal [:reset_callback_record, :foo], self.class._setup_callbacks.map(&:raw_filter) + assert_equal [:foo], @called_back + assert_equal [:foo, :sentinel], self.class._teardown_callbacks.map(&:raw_filter) + end - def test_which_raises_interrupt; raise Interrupt; end - end + def setup + end + + def teardown + end - test_name = 'test_which_raises_interrupt' - fr = FakeRunner.new + protected - test = tc.new test_name - assert_raises(Interrupt) { test.run fr } + def reset_callback_record + @called_back = [] end - def test_passthrough_exception_raised_within_setup_callback_is_not_rescued - tc = Class.new(TestCase) do - def self.name; nil; end + def foo + @called_back << :foo + end - setup :callback_which_raises_interrupt - def callback_which_raises_interrupt; raise Interrupt; end - def test_true; assert true end - end + def sentinel + assert_equal [:foo], @called_back + end +end - test_name = 'test_true' - fr = FakeRunner.new +class SubclassSetupAndTeardownTest < SetupAndTeardownTest + setup :bar + teardown :bar - test = tc.new test_name - assert_raises(Interrupt) { test.run fr } + def test_inherited_setup_callbacks + assert_equal [:reset_callback_record, :foo, :bar], self.class._setup_callbacks.map(&:raw_filter) + assert_equal [:foo, :bar], @called_back + assert_equal [:foo, :sentinel, :bar], self.class._teardown_callbacks.map(&:raw_filter) + end + + protected + def bar + @called_back << :bar + end + + def sentinel + assert_equal [:foo, :bar, :bar], @called_back end +end - def test_passthrough_exception_raised_within_teardown_callback_is_not_rescued - tc = Class.new(TestCase) do - def self.name; nil; end +class TestCaseTaggedLoggingTest < ActiveSupport::TestCase + def before_setup + require 'stringio' + @out = StringIO.new + self.tagged_logger = ActiveSupport::TaggedLogging.new(Logger.new(@out)) + super + end - teardown :callback_which_raises_interrupt - def callback_which_raises_interrupt; raise Interrupt; end - def test_true; assert true end - end + def test_logs_tagged_with_current_test_case + assert_match "#{self.class}: #{name}\n", @out.string + end +end - test_name = 'test_true' - fr = FakeRunner.new +class TestOrderTest < ActiveSupport::TestCase + def setup + @original_test_order = ActiveSupport::TestCase.test_order + end - test = tc.new test_name - assert_raises(Interrupt) { test.run fr } - end + def teardown + ActiveSupport::TestCase.test_order = @original_test_order + end - def test_pending_deprecation - assert_deprecated do - pending "should use #skip instead" - end + def test_defaults_to_sorted_with_warning + ActiveSupport::TestCase.test_order = nil + + assert_equal :sorted, assert_deprecated { ActiveSupport::TestCase.test_order } + + # It should only produce a deprecation warning the first time this is accessed + assert_equal :sorted, assert_not_deprecated { ActiveSupport::TestCase.test_order } + assert_equal :sorted, assert_not_deprecated { ActiveSupport.test_order } + end + + def test_test_order_is_global + ActiveSupport::TestCase.test_order = :random + + assert_equal :random, ActiveSupport.test_order + assert_equal :random, ActiveSupport::TestCase.test_order + assert_equal :random, self.class.test_order + assert_equal :random, Class.new(ActiveSupport::TestCase).test_order + + ActiveSupport.test_order = :sorted + + assert_equal :sorted, ActiveSupport.test_order + assert_equal :sorted, ActiveSupport::TestCase.test_order + assert_equal :sorted, self.class.test_order + assert_equal :sorted, Class.new(ActiveSupport::TestCase).test_order + end + + def test_i_suck_and_my_tests_are_order_dependent! + ActiveSupport::TestCase.test_order = :random + + klass = Class.new(ActiveSupport::TestCase) do + i_suck_and_my_tests_are_order_dependent! end + + assert_equal :alpha, klass.test_order + assert_equal :random, ActiveSupport::TestCase.test_order end end diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb deleted file mode 100644 index 3e6ac811a4..0000000000 --- a/activesupport/test/test_test.rb +++ /dev/null @@ -1,206 +0,0 @@ -require 'abstract_unit' - -class AssertDifferenceTest < ActiveSupport::TestCase - def setup - @object = Class.new do - attr_accessor :num - def increment - self.num += 1 - end - - def decrement - self.num -= 1 - end - end.new - @object.num = 0 - end - - def test_assert_not - assert_equal true, assert_not(nil) - assert_equal true, assert_not(false) - - e = assert_raises(MiniTest::Assertion) { assert_not true } - assert_equal 'Expected true to be nil or false', e.message - - e = assert_raises(MiniTest::Assertion) { assert_not true, 'custom' } - assert_equal 'custom', e.message - end - - def test_assert_no_difference - assert_no_difference '@object.num' do - # ... - end - end - - def test_assert_difference - assert_difference '@object.num', +1 do - @object.increment - end - end - - def test_assert_difference_with_implicit_difference - assert_difference '@object.num' do - @object.increment - end - end - - def test_arbitrary_expression - assert_difference '@object.num + 1', +2 do - @object.increment - @object.increment - end - end - - def test_negative_differences - assert_difference '@object.num', -1 do - @object.decrement - end - end - - def test_expression_is_evaluated_in_the_appropriate_scope - silence_warnings do - local_scope = local_scope = 'foo' - assert_difference('local_scope; @object.num') { @object.increment } - end - end - - def test_array_of_expressions - assert_difference [ '@object.num', '@object.num + 1' ], +1 do - @object.increment - end - end - - def test_array_of_expressions_identify_failure - assert_raises(MiniTest::Assertion) do - assert_difference ['@object.num', '1 + 1'] do - @object.increment - end - end - end - - def test_array_of_expressions_identify_failure_when_message_provided - assert_raises(MiniTest::Assertion) do - assert_difference ['@object.num', '1 + 1'], 1, 'something went wrong' do - @object.increment - end - end - end -end - -class AssertBlankTest < ActiveSupport::TestCase - BLANK = [ EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", [], {} ] - NOT_BLANK = [ EmptyFalse.new, Object.new, true, 0, 1, 'x', [nil], { nil => 0 } ] - - def test_assert_blank_true - BLANK.each { |value| - assert_deprecated { assert_blank value } - } - end - - def test_assert_blank_false - NOT_BLANK.each { |v| - assert_deprecated { - begin - assert_blank v - fail 'should not get to here' - rescue Exception => e - assert_match(/is not blank/, e.message) - end - } - } - end -end - -class AssertPresentTest < ActiveSupport::TestCase - BLANK = [ EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", [], {} ] - NOT_BLANK = [ EmptyFalse.new, Object.new, true, 0, 1, 'x', [nil], { nil => 0 } ] - - def test_assert_present_true - NOT_BLANK.each { |v| - assert_deprecated { assert_present v } - } - end - - def test_assert_present_false - BLANK.each { |v| - assert_deprecated { - begin - assert_present v - fail 'should not get to here' - rescue Exception => e - assert_match(/is blank/, e.message) - end - } - } - end -end - -class AlsoDoingNothingTest < ActiveSupport::TestCase -end - -# Setup and teardown callbacks. -class SetupAndTeardownTest < ActiveSupport::TestCase - setup :reset_callback_record, :foo - teardown :foo, :sentinel - - def test_inherited_setup_callbacks - assert_equal [:reset_callback_record, :foo], self.class._setup_callbacks.map(&:raw_filter) - assert_equal [:foo], @called_back - assert_equal [:foo, :sentinel], self.class._teardown_callbacks.map(&:raw_filter) - end - - def setup - end - - def teardown - end - - protected - - def reset_callback_record - @called_back = [] - end - - def foo - @called_back << :foo - end - - def sentinel - assert_equal [:foo], @called_back - end -end - - -class SubclassSetupAndTeardownTest < SetupAndTeardownTest - setup :bar - teardown :bar - - def test_inherited_setup_callbacks - assert_equal [:reset_callback_record, :foo, :bar], self.class._setup_callbacks.map(&:raw_filter) - assert_equal [:foo, :bar], @called_back - assert_equal [:foo, :sentinel, :bar], self.class._teardown_callbacks.map(&:raw_filter) - end - - protected - def bar - @called_back << :bar - end - - def sentinel - assert_equal [:foo, :bar, :bar], @called_back - end -end - - -class TestCaseTaggedLoggingTest < ActiveSupport::TestCase - def before_setup - require 'stringio' - @out = StringIO.new - self.tagged_logger = ActiveSupport::TaggedLogging.new(Logger.new(@out)) - super - end - - def test_logs_tagged_with_current_test_case - assert_match "#{self.class}: #{__name__}\n", @out.string - end -end diff --git a/activesupport/test/testing/constant_lookup_test.rb b/activesupport/test/testing/constant_lookup_test.rb index 19280ba74a..0f16419c8b 100644 --- a/activesupport/test/testing/constant_lookup_test.rb +++ b/activesupport/test/testing/constant_lookup_test.rb @@ -1,15 +1,16 @@ require 'abstract_unit' +require 'dependencies_test_helpers' class Foo; end class Bar < Foo def index; end def self.index; end end -class Baz < Bar; end module FooBar; end class ConstantLookupTest < ActiveSupport::TestCase include ActiveSupport::Testing::ConstantLookup + include DependenciesTestHelpers def find_foo(name) self.class.determine_constant_from_test_name(name) do |constant| @@ -56,4 +57,20 @@ class ConstantLookupTest < ActiveSupport::TestCase assert_nil find_module("DoesntExist::Nadda::Nope") assert_nil find_module("DoesntExist::Nadda::Nope::NotHere") end + + def test_does_not_swallow_exception_on_no_method_error + assert_raises(NoMethodError) { + with_autoloading_fixtures { + self.class.determine_constant_from_test_name("RaisesNoMethodError") + } + } + end + + def test_does_not_swallow_exception_on_no_name_error_within_constant + assert_raises(NameError) do + with_autoloading_fixtures do + self.class.determine_constant_from_test_name('RaisesNameError') + end + end + end end diff --git a/activesupport/test/time_travel_test.rb b/activesupport/test/time_travel_test.rb new file mode 100644 index 0000000000..065539671d --- /dev/null +++ b/activesupport/test/time_travel_test.rb @@ -0,0 +1,72 @@ +require 'abstract_unit' +require 'active_support/core_ext/date' +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 + end + + def test_time_helper_travel_with_block + 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 + end + + assert_not_equal expected_time.to_s(:db), Time.now.to_s(:db) + assert_not_equal expected_time.to_date, Date.today + 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 + end + + def test_time_helper_travel_to_with_block + 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 + end + + assert_not_equal expected_time, Time.now + assert_not_equal Date.new(2004, 11, 24), Date.today + end + + def test_time_helper_travel_back + 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 + travel_back + + assert_not_equal expected_time, Time.now + assert_not_equal Date.new(2004, 11, 24), Date.today + 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 + end + end +end diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index 9c3b5d0667..3e6d9652bb 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -1,7 +1,10 @@ require 'abstract_unit' require 'active_support/time' +require 'time_zone_test_helpers' class TimeZoneTest < ActiveSupport::TestCase + include TimeZoneTestHelpers + def test_utc_to_local zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] assert_equal Time.utc(1999, 12, 31, 19), zone.utc_to_local(Time.utc(2000, 1)) # standard offset -0500 @@ -19,7 +22,7 @@ class TimeZoneTest < ActiveSupport::TestCase assert_instance_of TZInfo::TimezonePeriod, zone.period_for_local(Time.utc(2000)) end - ActiveSupport::TimeZone::MAPPING.keys.each do |name| + ActiveSupport::TimeZone::MAPPING.each_key do |name| define_method("test_map_#{name.downcase.gsub(/[^a-z]/, '_')}_to_tzinfo") do zone = ActiveSupport::TimeZone[name] assert_respond_to zone.tzinfo, :period_for_local @@ -89,14 +92,65 @@ class TimeZoneTest < ActiveSupport::TestCase end def test_today - Time.stubs(:now).returns(Time.utc(2000, 1, 1, 4, 59, 59)) # 1 sec before midnight Jan 1 EST + travel_to(Time.utc(2000, 1, 1, 4, 59, 59)) # 1 sec before midnight Jan 1 EST assert_equal Date.new(1999, 12, 31), ActiveSupport::TimeZone['Eastern Time (US & Canada)'].today - Time.stubs(:now).returns(Time.utc(2000, 1, 1, 5)) # midnight Jan 1 EST + travel_to(Time.utc(2000, 1, 1, 5)) # midnight Jan 1 EST assert_equal Date.new(2000, 1, 1), ActiveSupport::TimeZone['Eastern Time (US & Canada)'].today - Time.stubs(:now).returns(Time.utc(2000, 1, 2, 4, 59, 59)) # 1 sec before midnight Jan 2 EST + travel_to(Time.utc(2000, 1, 2, 4, 59, 59)) # 1 sec before midnight Jan 2 EST assert_equal Date.new(2000, 1, 1), ActiveSupport::TimeZone['Eastern Time (US & Canada)'].today - Time.stubs(:now).returns(Time.utc(2000, 1, 2, 5)) # midnight Jan 2 EST + travel_to(Time.utc(2000, 1, 2, 5)) # midnight Jan 2 EST assert_equal Date.new(2000, 1, 2), ActiveSupport::TimeZone['Eastern Time (US & Canada)'].today + travel_back + end + + def test_tomorrow + travel_to(Time.utc(2000, 1, 1, 4, 59, 59)) # 1 sec before midnight Jan 1 EST + assert_equal Date.new(2000, 1, 1), ActiveSupport::TimeZone['Eastern Time (US & Canada)'].tomorrow + travel_to(Time.utc(2000, 1, 1, 5)) # midnight Jan 1 EST + assert_equal Date.new(2000, 1, 2), ActiveSupport::TimeZone['Eastern Time (US & Canada)'].tomorrow + travel_to(Time.utc(2000, 1, 2, 4, 59, 59)) # 1 sec before midnight Jan 2 EST + assert_equal Date.new(2000, 1, 2), ActiveSupport::TimeZone['Eastern Time (US & Canada)'].tomorrow + travel_to(Time.utc(2000, 1, 2, 5)) # midnight Jan 2 EST + assert_equal Date.new(2000, 1, 3), ActiveSupport::TimeZone['Eastern Time (US & Canada)'].tomorrow + travel_back + end + + def test_yesterday + travel_to(Time.utc(2000, 1, 1, 4, 59, 59)) # 1 sec before midnight Jan 1 EST + assert_equal Date.new(1999, 12, 30), ActiveSupport::TimeZone['Eastern Time (US & Canada)'].yesterday + travel_to(Time.utc(2000, 1, 1, 5)) # midnight Jan 1 EST + assert_equal Date.new(1999, 12, 31), ActiveSupport::TimeZone['Eastern Time (US & Canada)'].yesterday + travel_to(Time.utc(2000, 1, 2, 4, 59, 59)) # 1 sec before midnight Jan 2 EST + assert_equal Date.new(1999, 12, 31), ActiveSupport::TimeZone['Eastern Time (US & Canada)'].yesterday + travel_to(Time.utc(2000, 1, 2, 5)) # midnight Jan 2 EST + assert_equal Date.new(2000, 1, 1), ActiveSupport::TimeZone['Eastern Time (US & Canada)'].yesterday + travel_back + end + + def test_travel_to_a_date + with_env_tz do + Time.use_zone('Hawaii') do + date = Date.new(2014, 2, 18) + time = date.midnight + + travel_to date do + assert_equal date, Date.current + assert_equal time, Time.current + end + end + end + end + + def test_travel_to_travels_back_and_reraises_if_the_block_raises + ts = Time.current - 1.second + + travel_to ts do + raise + end + + flunk # ensure travel_to re-raises + rescue + assert_not_equal ts, Time.current end def test_local @@ -203,6 +257,15 @@ class TimeZoneTest < ActiveSupport::TestCase assert_equal Time.utc(1999,12,31,19), twz.time end + def test_parse_with_day_omitted + with_env_tz 'US/Eastern' do + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + assert_equal Time.local(2000, 2, 1), zone.parse('Feb', Time.local(2000, 1, 1)) + assert_equal Time.local(2005, 2, 1), zone.parse('Feb 2005', Time.local(2000, 1, 1)) + assert_equal Time.local(2005, 2, 2), zone.parse('2 Feb 2005', Time.local(2000, 1, 1)) + end + end + def test_parse_should_not_black_out_system_timezone_dst_jump with_env_tz('EET') do zone = ActiveSupport::TimeZone['Pacific Time (US & Canada)'] @@ -232,6 +295,22 @@ class TimeZoneTest < ActiveSupport::TestCase assert_equal Time.utc(2012, 5, 28, 7, 0, 0), twz.utc end + def test_parse_doesnt_use_local_dst + with_env_tz 'US/Eastern' do + zone = ActiveSupport::TimeZone['UTC'] + twz = zone.parse('2013-03-10 02:00:00') + assert_equal Time.utc(2013, 3, 10, 2, 0, 0), twz.time + end + end + + def test_parse_handles_dst_jump + with_env_tz 'US/Eastern' do + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + twz = zone.parse('2013-03-10 02:00:00') + assert_equal Time.utc(2013, 3, 10, 3, 0, 0), twz.time + end + end + def test_utc_offset_lazy_loaded_from_tzinfo_when_not_passed_in_to_initialize tzinfo = TZInfo::Timezone.get('America/New_York') zone = ActiveSupport::TimeZone.create(tzinfo.name, nil, tzinfo) @@ -340,12 +419,4 @@ class TimeZoneTest < ActiveSupport::TestCase assert ActiveSupport::TimeZone.us_zones.include?(ActiveSupport::TimeZone["Hawaii"]) assert !ActiveSupport::TimeZone.us_zones.include?(ActiveSupport::TimeZone["Kuala Lumpur"]) end - - protected - def with_env_tz(new_tz = 'US/Eastern') - old_tz, ENV['TZ'] = ENV['TZ'], new_tz - yield - ensure - old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') - end end diff --git a/activesupport/test/time_zone_test_helpers.rb b/activesupport/test/time_zone_test_helpers.rb new file mode 100644 index 0000000000..9632b89d09 --- /dev/null +++ b/activesupport/test/time_zone_test_helpers.rb @@ -0,0 +1,16 @@ +module TimeZoneTestHelpers + def with_tz_default(tz = nil) + old_tz = Time.zone + Time.zone = tz + yield + ensure + Time.zone = old_tz + end + + def with_env_tz(new_tz = 'US/Eastern') + old_tz, ENV['TZ'] = ENV['TZ'], new_tz + yield + ensure + old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') + end +end diff --git a/activesupport/test/transliterate_test.rb b/activesupport/test/transliterate_test.rb index b5d8142458..6833ae68a7 100644 --- a/activesupport/test/transliterate_test.rb +++ b/activesupport/test/transliterate_test.rb @@ -3,7 +3,6 @@ require 'abstract_unit' require 'active_support/inflector/transliterate' class TransliterateTest < ActiveSupport::TestCase - def test_transliterate_should_not_change_ascii_chars (0..127).each do |byte| char = [byte].pack("U") @@ -12,24 +11,25 @@ class TransliterateTest < ActiveSupport::TestCase end def test_transliterate_should_approximate_ascii - # create string with range of Unicode"s western characters with + # create string with range of Unicode's western characters with # diacritics, excluding the division and multiplication signs which for # some reason or other are floating in the middle of all the letters. string = (0xC0..0x17E).to_a.reject {|c| [0xD7, 0xF7].include?(c)}.pack("U*") string.each_char do |char| - assert_match %r{^[a-zA-Z']*$}, ActiveSupport::Inflector.transliterate(string) + assert_match %r{^[a-zA-Z']*$}, ActiveSupport::Inflector.transliterate(char) end end def test_transliterate_should_work_with_custom_i18n_rules_and_uncomposed_utf8 char = [117, 776].pack("U*") # "ü" as ASCII "u" plus COMBINING DIAERESIS I18n.backend.store_translations(:de, :i18n => {:transliterate => {:rule => {"ü" => "ue"}}}) - I18n.locale = :de + default_locale, I18n.locale = I18n.locale, :de assert_equal "ue", ActiveSupport::Inflector.transliterate(char) + ensure + I18n.locale = default_locale end def test_transliterate_should_allow_a_custom_replacement_char assert_equal "a*b", ActiveSupport::Inflector.transliterate("a索b", "*") end - end diff --git a/activesupport/test/ts_isolated.rb b/activesupport/test/ts_isolated.rb deleted file mode 100644 index 294d6595f7..0000000000 --- a/activesupport/test/ts_isolated.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'active_support/testing/autorun' -require 'active_support/test_case' -require 'rbconfig' -require 'active_support/core_ext/kernel/reporting' - -class TestIsolated < ActiveSupport::TestCase - ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) - - Dir["#{File.dirname(__FILE__)}/**/*_test.rb"].each do |file| - define_method("test #{file}") do - command = "#{ruby} -Ilib:test #{file}" - result = silence_stderr { `#{command}` } - assert $?.to_i.zero?, "#{command}\n#{result}" - end - end -end diff --git a/activesupport/test/xml_mini/jdom_engine_test.rb b/activesupport/test/xml_mini/jdom_engine_test.rb index f77d78d42c..ed4de8aba2 100644 --- a/activesupport/test/xml_mini/jdom_engine_test.rb +++ b/activesupport/test/xml_mini/jdom_engine_test.rb @@ -3,9 +3,12 @@ if RUBY_PLATFORM =~ /java/ require 'active_support/xml_mini' require 'active_support/core_ext/hash/conversions' + class JDOMEngineTest < ActiveSupport::TestCase include ActiveSupport + FILES_DIR = File.dirname(__FILE__) + '/../fixtures/xml' + def setup @default_backend = XmlMini.backend XmlMini.backend = 'JDOM' @@ -30,10 +33,41 @@ if RUBY_PLATFORM =~ /java/ assert_equal 'image/png', file.content_type end + def test_not_allowed_to_expand_entities_to_files + attack_xml = <<-EOT + <!DOCTYPE member [ + <!ENTITY a SYSTEM "file://#{FILES_DIR}/jdom_include.txt"> + ]> + <member>x&a;</member> + EOT + assert_equal 'x', Hash.from_xml(attack_xml)["member"] + end + + def test_not_allowed_to_expand_parameter_entities_to_files + attack_xml = <<-EOT + <!DOCTYPE member [ + <!ENTITY % b SYSTEM "file://#{FILES_DIR}/jdom_entities.txt"> + %b; + ]> + <member>x&a;</member> + EOT + assert_raise Java::OrgXmlSax::SAXParseException do + assert_equal 'x', Hash.from_xml(attack_xml)["member"] + end + end + + + def test_not_allowed_to_load_external_doctypes + attack_xml = <<-EOT + <!DOCTYPE member SYSTEM "file://#{FILES_DIR}/jdom_doctype.dtd"> + <member>x&a;</member> + EOT + assert_equal 'x', Hash.from_xml(attack_xml)["member"] + end + def test_exception_thrown_on_expansion_attack - assert_raise NativeException do + assert_raise Java::OrgXmlSax::SAXParseException do attack_xml = <<-EOT - <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE member [ <!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;"> <!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;"> @@ -142,10 +176,11 @@ if RUBY_PLATFORM =~ /java/ end private - def assert_equal_rexml(xml) - hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } - assert_equal(hash, XmlMini.parse(xml)) - end + def assert_equal_rexml(xml) + parsed_xml = XmlMini.parse(xml) + hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } + assert_equal(hash, parsed_xml) + end end else diff --git a/activesupport/test/xml_mini/libxml_engine_test.rb b/activesupport/test/xml_mini/libxml_engine_test.rb index 36ac4161ea..a8df2e1f7b 100644 --- a/activesupport/test/xml_mini/libxml_engine_test.rb +++ b/activesupport/test/xml_mini/libxml_engine_test.rb @@ -141,7 +141,7 @@ class LibxmlEngineTest < ActiveSupport::TestCase morning </root> eoxml - XmlMini.parse(io) + assert_equal_rexml(io) end def test_children_with_simple_cdata @@ -193,10 +193,12 @@ class LibxmlEngineTest < ActiveSupport::TestCase private - def assert_equal_rexml(xml) - hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } - assert_equal(hash, XmlMini.parse(xml)) - end + def assert_equal_rexml(xml) + parsed_xml = XmlMini.parse(xml) + xml.rewind if xml.respond_to?(:rewind) + hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } + assert_equal(hash, parsed_xml) + end end end diff --git a/activesupport/test/xml_mini/libxmlsax_engine_test.rb b/activesupport/test/xml_mini/libxmlsax_engine_test.rb index 82337961a1..d6d90639e2 100644 --- a/activesupport/test/xml_mini/libxmlsax_engine_test.rb +++ b/activesupport/test/xml_mini/libxmlsax_engine_test.rb @@ -141,7 +141,7 @@ class LibXMLSAXEngineTest < ActiveSupport::TestCase morning </root> eoxml - XmlMini.parse(io) + assert_equal_rexml(io) end def test_children_with_simple_cdata @@ -184,10 +184,12 @@ class LibXMLSAXEngineTest < ActiveSupport::TestCase end private - def assert_equal_rexml(xml) - hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } - assert_equal(hash, XmlMini.parse(xml)) - end + def assert_equal_rexml(xml) + parsed_xml = XmlMini.parse(xml) + xml.rewind if xml.respond_to?(:rewind) + hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } + assert_equal(hash, parsed_xml) + end end end diff --git a/activesupport/test/xml_mini/nokogiri_engine_test.rb b/activesupport/test/xml_mini/nokogiri_engine_test.rb index 71f57e43d2..2e962576b5 100644 --- a/activesupport/test/xml_mini/nokogiri_engine_test.rb +++ b/activesupport/test/xml_mini/nokogiri_engine_test.rb @@ -155,7 +155,7 @@ class NokogiriEngineTest < ActiveSupport::TestCase morning </root> eoxml - XmlMini.parse(io) + assert_equal_rexml(io) end def test_children_with_simple_cdata @@ -206,10 +206,12 @@ class NokogiriEngineTest < ActiveSupport::TestCase end private - def assert_equal_rexml(xml) - hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } - assert_equal(hash, XmlMini.parse(xml)) - end + def assert_equal_rexml(xml) + parsed_xml = XmlMini.parse(xml) + xml.rewind if xml.respond_to?(:rewind) + hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } + assert_equal(hash, parsed_xml) + end end end diff --git a/activesupport/test/xml_mini/nokogirisax_engine_test.rb b/activesupport/test/xml_mini/nokogirisax_engine_test.rb index 884494e95e..4f078f31e0 100644 --- a/activesupport/test/xml_mini/nokogirisax_engine_test.rb +++ b/activesupport/test/xml_mini/nokogirisax_engine_test.rb @@ -56,9 +56,9 @@ class NokogiriSAXEngineTest < ActiveSupport::TestCase end end - def test_setting_nokogiri_as_backend - XmlMini.backend = 'Nokogiri' - assert_equal XmlMini_Nokogiri, XmlMini.backend + def test_setting_nokogirisax_as_backend + XmlMini.backend = 'NokogiriSAX' + assert_equal XmlMini_NokogiriSAX, XmlMini.backend end def test_blank_returns_empty_hash @@ -156,7 +156,7 @@ class NokogiriSAXEngineTest < ActiveSupport::TestCase morning </root> eoxml - XmlMini.parse(io) + assert_equal_rexml(io) end def test_children_with_simple_cdata @@ -207,10 +207,12 @@ class NokogiriSAXEngineTest < ActiveSupport::TestCase end private - def assert_equal_rexml(xml) - hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } - assert_equal(hash, XmlMini.parse(xml)) - end + def assert_equal_rexml(xml) + parsed_xml = XmlMini.parse(xml) + xml.rewind if xml.respond_to?(:rewind) + hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } + assert_equal(hash, parsed_xml) + end end end diff --git a/activesupport/test/xml_mini/rexml_engine_test.rb b/activesupport/test/xml_mini/rexml_engine_test.rb index c4770405f2..0c1f11803c 100644 --- a/activesupport/test/xml_mini/rexml_engine_test.rb +++ b/activesupport/test/xml_mini/rexml_engine_test.rb @@ -24,6 +24,14 @@ class REXMLEngineTest < ActiveSupport::TestCase morning </root> eoxml - XmlMini.parse(io) + assert_equal_rexml(io) end + + private + def assert_equal_rexml(xml) + parsed_xml = XmlMini.parse(xml) + xml.rewind if xml.respond_to?(:rewind) + hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } + assert_equal(hash, parsed_xml) + end end diff --git a/activesupport/test/xml_mini_test.rb b/activesupport/test/xml_mini_test.rb index a025279e16..f49431cbbf 100644 --- a/activesupport/test/xml_mini_test.rb +++ b/activesupport/test/xml_mini_test.rb @@ -1,6 +1,9 @@ require 'abstract_unit' require 'active_support/xml_mini' require 'active_support/builder' +require 'active_support/core_ext/array' +require 'active_support/core_ext/hash' +require 'active_support/core_ext/big_decimal' module XmlMiniTest class RenameKeyTest < ActiveSupport::TestCase @@ -88,6 +91,61 @@ module XmlMiniTest assert_xml "<b>Howdy</b>" end + test "#to_tag should use the type value in the options hash" do + @xml.to_tag(:b, "blue", @options.merge(type: 'color')) + assert_xml( "<b type=\"color\">blue</b>" ) + end + + test "#to_tag accepts symbol types" do + @xml.to_tag(:b, :name, @options) + assert_xml( "<b type=\"symbol\">name</b>" ) + end + + test "#to_tag accepts boolean types" do + @xml.to_tag(:b, true, @options) + assert_xml( "<b type=\"boolean\">true</b>") + end + + test "#to_tag accepts float types" do + @xml.to_tag(:b, 3.14, @options) + assert_xml( "<b type=\"float\">3.14</b>") + end + + test "#to_tag accepts decimal types" do + @xml.to_tag(:b, ::BigDecimal.new("1.2"), @options) + assert_xml( "<b type=\"decimal\">1.2</b>") + end + + test "#to_tag accepts date types" do + @xml.to_tag(:b, Date.new(2001,2,3), @options) + assert_xml( "<b type=\"date\">2001-02-03</b>") + end + + test "#to_tag accepts datetime types" do + @xml.to_tag(:b, DateTime.new(2001,2,3,4,5,6,'+7'), @options) + assert_xml( "<b type=\"dateTime\">2001-02-03T04:05:06+07:00</b>") + end + + test "#to_tag accepts time types" do + @xml.to_tag(:b, Time.new(1993, 02, 24, 12, 0, 0, "+09:00"), @options) + assert_xml( "<b type=\"dateTime\">1993-02-24T12:00:00+09:00</b>") + end + + test "#to_tag accepts array types" do + @xml.to_tag(:b, ["first_name", "last_name"], @options) + assert_xml( "<b type=\"array\"><b>first_name</b><b>last_name</b></b>" ) + end + + test "#to_tag accepts hash types" do + @xml.to_tag(:b, { first_name: "Bob", last_name: "Marley" }, @options) + assert_xml( "<b><first-name>Bob</first-name><last-name>Marley</last-name></b>" ) + end + + test "#to_tag should not add type when skip types option is set" do + @xml.to_tag(:b, "Bob", @options.merge(skip_types: 1)) + assert_xml( "<b>Bob</b>" ) + end + test "#to_tag should dasherize the space when passed a string with spaces as a key" do @xml.to_tag("New York", 33, @options) assert_xml "<New---York type=\"integer\">33</New---York>" @@ -97,7 +155,6 @@ module XmlMiniTest @xml.to_tag(:"New York", 33, @options) assert_xml "<New---York type=\"integer\">33</New---York>" end - # TODO: test the remaining functions hidden in #to_tag. end class WithBackendTest < ActiveSupport::TestCase @@ -106,7 +163,11 @@ module XmlMiniTest module Nokogiri end setup do - @xml = ActiveSupport::XmlMini + @xml, @default_backend = ActiveSupport::XmlMini, ActiveSupport::XmlMini.backend + end + + teardown do + ActiveSupport::XmlMini.backend = @default_backend end test "#with_backend should switch backend and then switch back" do @@ -135,7 +196,11 @@ module XmlMiniTest module LibXML end setup do - @xml = ActiveSupport::XmlMini + @xml, @default_backend = ActiveSupport::XmlMini, ActiveSupport::XmlMini.backend + end + + teardown do + ActiveSupport::XmlMini.backend = @default_backend end test "#with_backend should be thread-safe" do @@ -161,4 +226,128 @@ module XmlMiniTest end end end + + class ParsingTest < ActiveSupport::TestCase + def setup + @parsing = ActiveSupport::XmlMini::PARSING + end + + def test_symbol + parser = @parsing['symbol'] + assert_equal :symbol, parser.call('symbol') + assert_equal :symbol, parser.call(:symbol) + assert_equal :'123', parser.call(123) + assert_raises(ArgumentError) { parser.call(Date.new(2013,11,12,02,11)) } + end + + def test_date + parser = @parsing['date'] + assert_equal Date.new(2013,11,12), parser.call("2013-11-12T0211Z") + assert_raises(TypeError) { parser.call(1384190018) } + assert_raises(ArgumentError) { parser.call("not really a date") } + end + + def test_datetime + parser = @parsing['datetime'] + assert_equal Time.new(2013,11,12,02,11,00,0), parser.call("2013-11-12T02:11:00Z") + assert_equal DateTime.new(2013,11,12), parser.call("2013-11-12T0211Z") + assert_equal DateTime.new(2013,11,12,02,11), parser.call("2013-11-12T02:11Z") + assert_equal DateTime.new(2013,11,12,02,11), parser.call("2013-11-12T11:11+9") + assert_raises(ArgumentError) { parser.call("1384190018") } + end + + def test_integer + parser = @parsing['integer'] + assert_equal 123, parser.call(123) + assert_equal 123, parser.call(123.003) + assert_equal 123, parser.call("123") + assert_equal 0, parser.call("") + assert_raises(ArgumentError) { parser.call(Date.new(2013,11,12,02,11)) } + end + + def test_float + parser = @parsing['float'] + assert_equal 123, parser.call("123") + assert_equal 123.003, parser.call("123.003") + assert_equal 123.0, parser.call("123,003") + assert_equal 0.0, parser.call("") + assert_equal 123, parser.call(123) + assert_equal 123.05, parser.call(123.05) + assert_raises(ArgumentError) { parser.call(Date.new(2013,11,12,02,11)) } + end + + def test_decimal + parser = @parsing['decimal'] + assert_equal 123, parser.call("123") + assert_equal 123.003, parser.call("123.003") + assert_equal 123.0, parser.call("123,003") + assert_equal 0.0, parser.call("") + assert_equal 123, parser.call(123) + assert_raises(ArgumentError) { parser.call(123.04) } + assert_raises(ArgumentError) { parser.call(Date.new(2013,11,12,02,11)) } + end + + def test_boolean + parser = @parsing['boolean'] + [1, true, "1"].each do |value| + assert parser.call(value) + end + + [0, false, "0"].each do |value| + assert_not parser.call(value) + end + end + + def test_string + parser = @parsing['string'] + assert_equal "123", parser.call(123) + assert_equal "123", parser.call("123") + assert_equal "[]", parser.call("[]") + assert_equal "[]", parser.call([]) + assert_equal "{}", parser.call({}) + assert_raises(ArgumentError) { parser.call(Date.new(2013,11,12,02,11)) } + end + + def test_yaml + yaml = <<YAML +product: + - sku : BL394D + quantity : 4 + description : Basketball +YAML + expected = { + "product"=> [ + {"sku"=>"BL394D", "quantity"=>4, "description"=>"Basketball"} + ] + } + parser = @parsing['yaml'] + assert_equal(expected, parser.call(yaml)) + assert_equal({1 => 'test'}, parser.call({1 => 'test'})) + assert_equal({"1 => 'test'"=>nil}, parser.call("{1 => 'test'}")) + end + + def test_base64Binary_and_binary + base64 = <<BASE64 +TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz +IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg +dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu +dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo +ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4= +BASE64 + expected_base64 = <<EXPECTED +Man is distinguished, not only by his reason, but by this singular passion from +other animals, which is a lust of the mind, that by a perseverance of delight +in the continued and indefatigable generation of knowledge, exceeds the short +vehemence of any carnal pleasure. +EXPECTED + + parser = @parsing['base64Binary'] + assert_equal expected_base64.gsub(/\n/," ").strip, parser.call(base64) + parser.call("NON BASE64 INPUT") + + parser = @parsing['binary'] + assert_equal expected_base64.gsub(/\n/," ").strip, parser.call(base64, 'encoding' => 'base64') + assert_equal "IGNORED INPUT", parser.call("IGNORED INPUT", {}) + end + end end |