diff options
author | Pratik Naik <pratiknaik@gmail.com> | 2008-09-27 14:04:46 +0100 |
---|---|---|
committer | Pratik Naik <pratiknaik@gmail.com> | 2008-09-27 14:04:46 +0100 |
commit | fda846cf5ddf523b00a39c26591489794b5de568 (patch) | |
tree | 00d4860d53e5c861fd9b3f483f04ff0d2db19307 /activesupport/test | |
parent | df046298715b1927a832973c4c29955696fee02c (diff) | |
parent | ea609b265ffc30cac00bf09a262027f96964ed6f (diff) | |
download | rails-fda846cf5ddf523b00a39c26591489794b5de568.tar.gz rails-fda846cf5ddf523b00a39c26591489794b5de568.tar.bz2 rails-fda846cf5ddf523b00a39c26591489794b5de568.zip |
Merge commit 'mainstream/master'
Conflicts:
activerecord/lib/active_record/base.rb
railties/Rakefile
railties/doc/guides/activerecord/association_basics.txt
railties/doc/guides/debugging/debugging_rails_applications.txt
railties/doc/guides/getting_started_with_rails/getting_started_with_rails.txt
railties/doc/guides/index.txt
railties/doc/guides/migrations/foreign_keys.txt
railties/doc/guides/migrations/migrations.txt
railties/doc/guides/migrations/writing_a_migration.txt
railties/doc/guides/routing/routing_outside_in.txt
Diffstat (limited to 'activesupport/test')
-rw-r--r-- | activesupport/test/abstract_unit.rb | 19 | ||||
-rw-r--r-- | activesupport/test/core_ext/date_ext_test.rb | 23 | ||||
-rw-r--r-- | activesupport/test/core_ext/date_time_ext_test.rb | 75 | ||||
-rw-r--r-- | activesupport/test/core_ext/hash_ext_test.rb | 10 | ||||
-rw-r--r-- | activesupport/test/core_ext/string_ext_test.rb | 59 | ||||
-rw-r--r-- | activesupport/test/core_ext/time_ext_test.rb | 81 | ||||
-rw-r--r-- | activesupport/test/core_ext/time_with_zone_test.rb | 74 | ||||
-rw-r--r-- | activesupport/test/inflector_test.rb | 6 | ||||
-rw-r--r-- | activesupport/test/inflector_test_cases.rb | 5 | ||||
-rw-r--r-- | activesupport/test/json/encoding_test.rb | 12 | ||||
-rw-r--r-- | activesupport/test/multibyte_chars_test.rb | 705 | ||||
-rw-r--r-- | activesupport/test/multibyte_conformance.rb | 105 | ||||
-rw-r--r-- | activesupport/test/multibyte_handler_test.rb | 372 | ||||
-rw-r--r-- | activesupport/test/multibyte_test_helpers.rb | 19 | ||||
-rw-r--r-- | activesupport/test/multibyte_unicode_database_test.rb | 28 |
15 files changed, 964 insertions, 629 deletions
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb index cce8d5d220..a698beaa0e 100644 --- a/activesupport/test/abstract_unit.rb +++ b/activesupport/test/abstract_unit.rb @@ -1,9 +1,15 @@ +# encoding: utf-8 + require 'test/unit' $:.unshift "#{File.dirname(__FILE__)}/../lib" $:.unshift File.dirname(__FILE__) require 'active_support' +if RUBY_VERSION < '1.9' + $KCODE = 'UTF8' +end + def uses_gem(gem_name, test_name, version = '> 0') require 'rubygems' gem gem_name.to_s, version @@ -22,3 +28,16 @@ end # Show backtraces for deprecated behavior for quicker cleanup. ActiveSupport::Deprecation.debug = true + +def with_kcode(code) + if RUBY_VERSION < '1.9' + begin + old_kcode, $KCODE = $KCODE, code + yield + ensure + $KCODE = old_kcode + end + else + yield + end +end
\ No newline at end of file diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index 0f3cf4c75c..b53c754780 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -210,6 +210,29 @@ class DateExtCalculationsTest < Test::Unit::TestCase end end + uses_mocha 'past?, today? and future?' do + def test_today + Date.stubs(:current).returns(Date.new(2000, 1, 1)) + assert_equal false, Date.new(1999, 12, 31).today? + assert_equal true, Date.new(2000,1,1).today? + assert_equal false, Date.new(2000,1,2).today? + end + + def test_past + Date.stubs(:current).returns(Date.new(2000, 1, 1)) + assert_equal true, Date.new(1999, 12, 31).past? + assert_equal false, Date.new(2000,1,1).past? + assert_equal false, Date.new(2000,1,2).past? + end + + def test_future + Date.stubs(:current).returns(Date.new(2000, 1, 1)) + assert_equal false, Date.new(1999, 12, 31).future? + assert_equal false, Date.new(2000,1,1).future? + assert_equal true, Date.new(2000,1,2).future? + end + end + uses_mocha 'TestDateCurrent' do def test_current_returns_date_today_when_zone_default_not_set with_env_tz 'US/Central' do diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index 854a3a05e1..be3cd8b5d6 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -207,6 +207,81 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase assert_match(/^2080-02-28T15:15:10-06:?00$/, DateTime.civil(2080, 2, 28, 15, 15, 10, -0.25).xmlschema) end + uses_mocha 'Test DateTime past?, today? and future?' do + def test_today_with_offset + Date.stubs(:current).returns(Date.new(2000, 1, 1)) + assert_equal false, DateTime.civil(1999,12,31,23,59,59, Rational(-18000, 86400)).today? + assert_equal true, DateTime.civil(2000,1,1,0,0,0, Rational(-18000, 86400)).today? + assert_equal true, DateTime.civil(2000,1,1,23,59,59, Rational(-18000, 86400)).today? + assert_equal false, DateTime.civil(2000,1,2,0,0,0, Rational(-18000, 86400)).today? + end + + def test_today_without_offset + Date.stubs(:current).returns(Date.new(2000, 1, 1)) + assert_equal false, DateTime.civil(1999,12,31,23,59,59).today? + assert_equal true, DateTime.civil(2000,1,1,0).today? + assert_equal true, DateTime.civil(2000,1,1,23,59,59).today? + assert_equal false, DateTime.civil(2000,1,2,0).today? + end + + def test_past_with_offset + DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) + assert_equal true, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).past? + assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).past? + assert_equal false, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).past? + end + + def test_past_without_offset + DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) + assert_equal true, DateTime.civil(2005,2,10,20,30,44).past? + assert_equal false, DateTime.civil(2005,2,10,20,30,45).past? + assert_equal false, DateTime.civil(2005,2,10,20,30,46).past? + end + + def test_future_with_offset + DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) + assert_equal false, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).future? + assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).future? + assert_equal true, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).future? + end + + def test_future_without_offset + DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) + assert_equal false, DateTime.civil(2005,2,10,20,30,44).future? + assert_equal false, DateTime.civil(2005,2,10,20,30,45).future? + assert_equal true, DateTime.civil(2005,2,10,20,30,46).future? + end + end + + uses_mocha 'TestDateTimeCurrent' do + def test_current_returns_date_today_when_zone_default_not_set + with_env_tz 'US/Eastern' do + Time.stubs(:now).returns Time.local(1999, 12, 31, 23, 59, 59) + assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current + end + end + + def test_current_returns_time_zone_today_when_zone_default_set + Time.zone_default = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + with_env_tz 'US/Eastern' do + Time.stubs(:now).returns Time.local(1999, 12, 31, 23, 59, 59) + assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current + end + ensure + Time.zone_default = nil + end + end + + def test_current_without_time_zone + assert DateTime.current.is_a?(DateTime) + end + + def test_current_with_time_zone + with_env_tz 'US/Eastern' do + assert DateTime.current.is_a?(DateTime) + end + end + def test_acts_like_time assert DateTime.new.acts_like_time? end diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 44d48e7577..9537f486cb 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -329,6 +329,16 @@ class HashExtTest < Test::Unit::TestCase end end + def test_indifferent_slice_access_with_symbols + original = {'login' => 'bender', 'password' => 'shiny', 'stuff' => 'foo'} + original = original.with_indifferent_access + + slice = original.slice(:login, :password) + + assert_equal 'bender', slice[:login] + assert_equal 'bender', slice['login'] + end + def test_except original = { :a => 'x', :b => 'y', :c => 10 } expected = { :a => 'x', :b => 'y' } diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index c0decf2c3f..b086c957fe 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -191,13 +191,12 @@ class StringInflectionsTest < Test::Unit::TestCase if RUBY_VERSION < '1.9' def test_each_char_with_utf8_string_when_kcode_is_utf8 - old_kcode, $KCODE = $KCODE, 'UTF8' - '€2.99'.each_char do |char| - assert_not_equal 1, char.length - break + with_kcode('UTF8') do + '€2.99'.each_char do |char| + assert_not_equal 1, char.length + break + end end - ensure - $KCODE = old_kcode end end end @@ -206,4 +205,52 @@ class StringBehaviourTest < Test::Unit::TestCase def test_acts_like_string assert 'Bambi'.acts_like_string? end +end + +class CoreExtStringMultibyteTest < Test::Unit::TestCase + UNICODE_STRING = 'こにちわ' + ASCII_STRING = 'ohayo' + BYTE_STRING = "\270\236\010\210\245" + + def test_core_ext_adds_mb_chars + assert UNICODE_STRING.respond_to?(:mb_chars) + end + + def test_string_should_recognize_utf8_strings + assert UNICODE_STRING.is_utf8? + assert ASCII_STRING.is_utf8? + assert !BYTE_STRING.is_utf8? + end + + if RUBY_VERSION < '1.8.7' + def test_core_ext_adds_chars + assert UNICODE_STRING.respond_to?(:chars) + end + + def test_chars_warns_about_deprecation + assert_deprecated("String#chars") do + ''.chars + end + end + end + + if RUBY_VERSION < '1.9' + def test_mb_chars_returns_self_when_kcode_not_set + with_kcode('none') do + assert UNICODE_STRING.mb_chars.kind_of?(String) + end + end + + def test_mb_chars_returns_an_instance_of_the_chars_proxy_when_kcode_utf8 + with_kcode('UTF8') do + assert UNICODE_STRING.mb_chars.kind_of?(ActiveSupport::Multibyte.proxy_class) + end + end + end + + if RUBY_VERSION >= '1.9' + def test_mb_chars_returns_string + assert UNICODE_STRING.mb_chars.kind_of?(String) + end + end end
\ No newline at end of file diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index c1bdee4ca9..8ceaedc7f4 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -117,6 +117,7 @@ class TimeExtCalculationsTest < Test::Unit::TestCase assert_equal Time.local(2007,3,31,23,59,59), Time.local(2007,3,31,0,0,0).end_of_quarter assert_equal Time.local(2007,12,31,23,59,59), Time.local(2007,12,21,10,10,10).end_of_quarter assert_equal Time.local(2007,6,30,23,59,59), Time.local(2007,4,1,0,0,0).end_of_quarter + assert_equal Time.local(2008,6,30,23,59,59), Time.local(2008,5,31,0,0,0).end_of_quarter end def test_end_of_year @@ -563,6 +564,74 @@ class TimeExtCalculationsTest < Test::Unit::TestCase assert_nothing_raised { Time.now.xmlschema } end + uses_mocha 'Test Time past?, today? and future?' do + def test_today_with_time_local + Date.stubs(:current).returns(Date.new(2000, 1, 1)) + assert_equal false, Time.local(1999,12,31,23,59,59).today? + assert_equal true, Time.local(2000,1,1,0).today? + assert_equal true, Time.local(2000,1,1,23,59,59).today? + assert_equal false, Time.local(2000,1,2,0).today? + end + + def test_today_with_time_utc + Date.stubs(:current).returns(Date.new(2000, 1, 1)) + assert_equal false, Time.utc(1999,12,31,23,59,59).today? + assert_equal true, Time.utc(2000,1,1,0).today? + assert_equal true, Time.utc(2000,1,1,23,59,59).today? + assert_equal false, Time.utc(2000,1,2,0).today? + end + + def test_past_with_time_current_as_time_local + with_env_tz 'US/Eastern' do + Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45)) + assert_equal true, Time.local(2005,2,10,15,30,44).past? + assert_equal false, Time.local(2005,2,10,15,30,45).past? + assert_equal false, Time.local(2005,2,10,15,30,46).past? + assert_equal true, Time.utc(2005,2,10,20,30,44).past? + assert_equal false, Time.utc(2005,2,10,20,30,45).past? + assert_equal false, Time.utc(2005,2,10,20,30,46).past? + end + end + + def test_past_with_time_current_as_time_with_zone + with_env_tz 'US/Eastern' do + twz = Time.utc(2005,2,10,15,30,45).in_time_zone('Central Time (US & Canada)') + Time.stubs(:current).returns(twz) + assert_equal true, Time.local(2005,2,10,10,30,44).past? + assert_equal false, Time.local(2005,2,10,10,30,45).past? + assert_equal false, Time.local(2005,2,10,10,30,46).past? + assert_equal true, Time.utc(2005,2,10,15,30,44).past? + assert_equal false, Time.utc(2005,2,10,15,30,45).past? + assert_equal false, Time.utc(2005,2,10,15,30,46).past? + end + end + + def test_future_with_time_current_as_time_local + with_env_tz 'US/Eastern' do + Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45)) + assert_equal false, Time.local(2005,2,10,15,30,44).future? + assert_equal false, Time.local(2005,2,10,15,30,45).future? + assert_equal true, Time.local(2005,2,10,15,30,46).future? + assert_equal false, Time.utc(2005,2,10,20,30,44).future? + assert_equal false, Time.utc(2005,2,10,20,30,45).future? + assert_equal true, Time.utc(2005,2,10,20,30,46).future? + end + end + + def test_future_with_time_current_as_time_with_zone + with_env_tz 'US/Eastern' do + twz = Time.utc(2005,2,10,15,30,45).in_time_zone('Central Time (US & Canada)') + Time.stubs(:current).returns(twz) + assert_equal false, Time.local(2005,2,10,10,30,44).future? + assert_equal false, Time.local(2005,2,10,10,30,45).future? + assert_equal true, Time.local(2005,2,10,10,30,46).future? + assert_equal false, Time.utc(2005,2,10,15,30,44).future? + assert_equal false, Time.utc(2005,2,10,15,30,45).future? + assert_equal true, Time.utc(2005,2,10,15,30,46).future? + end + end + end + def test_acts_like_time assert Time.new.acts_like_time? end @@ -640,24 +709,24 @@ class TimeExtMarshalingTest < Test::Unit::TestCase assert_equal t, unmarshaled assert_equal t.zone, unmarshaled.zone end - - def test_marshaling_with_local_instance + + def test_marshaling_with_local_instance t = Time.local(2000) marshaled = Marshal.dump t unmarshaled = Marshal.load marshaled assert_equal t, unmarshaled assert_equal t.zone, unmarshaled.zone end - - def test_marshaling_with_frozen_utc_instance + + def test_marshaling_with_frozen_utc_instance t = Time.utc(2000).freeze marshaled = Marshal.dump t unmarshaled = Marshal.load marshaled assert_equal t, unmarshaled assert_equal t.zone, unmarshaled.zone end - - def test_marshaling_with_frozen_local_instance + + def test_marshaling_with_frozen_local_instance t = Time.local(2000).freeze marshaled = Marshal.dump t unmarshaled = Marshal.load marshaled diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index dfe04485be..72b540efe0 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -152,6 +152,50 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal false, @twz.between?(Time.utc(2000,1,1,0,0,1), Time.utc(2000,1,1,0,0,2)) end + uses_mocha 'TimeWithZone past?, today? and future?' do + def test_today + Date.stubs(:current).returns(Date.new(2000, 1, 1)) + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(1999,12,31,23,59,59) ).today? + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,0) ).today? + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,23,59,59) ).today? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,2,0) ).today? + end + + def test_past_with_time_current_as_time_local + with_env_tz 'US/Eastern' do + Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45)) + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past? + end + end + + def test_past_with_time_current_as_time_with_zone + twz = ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45) ) + Time.stubs(:current).returns(twz) + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past? + end + + def test_future_with_time_current_as_time_local + with_env_tz 'US/Eastern' do + Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45)) + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future? + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future? + end + end + + def future_with_time_current_as_time_with_zone + twz = ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45) ) + Time.stubs(:current).returns(twz) + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future? + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future? + end + end + def test_eql? assert @twz.eql?(Time.utc(2000)) assert @twz.eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) ) @@ -353,7 +397,7 @@ class TimeWithZoneTest < Test::Unit::TestCase def test_date_part_value_methods silence_warnings do # silence warnings raised by tzinfo gem twz = ActiveSupport::TimeWithZone.new(Time.utc(1999,12,31,19,18,17,500), @time_zone) - twz.stubs(:method_missing).returns(nil) #ensure these methods are defined directly on class + twz.expects(:method_missing).never assert_equal 1999, twz.year assert_equal 12, twz.month assert_equal 31, twz.day @@ -361,6 +405,8 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal 18, twz.min assert_equal 17, twz.sec assert_equal 500, twz.usec + assert_equal 5, twz.wday + assert_equal 365, twz.yday end end end @@ -538,7 +584,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sun, 02 Apr 2006 10:30:01 EDT -04:00", twz.since(1.days + 1.second).inspect assert_equal "Sun, 02 Apr 2006 10:30:01 EDT -04:00", (twz + 1.days + 1.second).inspect end - + def test_advance_1_day_across_spring_dst_transition_backwards twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,2,10,30)) # In 2006, spring DST transition occurred Apr 2 at 2AM; this day was only 23 hours long @@ -548,7 +594,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", (twz - 1.days).inspect assert_equal "Sat, 01 Apr 2006 10:30:01 EST -05:00", twz.ago(1.days - 1.second).inspect end - + def test_advance_1_day_expressed_as_number_of_seconds_minutes_or_hours_across_spring_dst_transition twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,1,10,30)) # In 2006, spring DST transition occurred Apr 2 at 2AM; this day was only 23 hours long @@ -565,7 +611,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sun, 02 Apr 2006 11:30:00 EDT -04:00", twz.since(24.hours).inspect assert_equal "Sun, 02 Apr 2006 11:30:00 EDT -04:00", twz.advance(:hours => 24).inspect end - + def test_advance_1_day_expressed_as_number_of_seconds_minutes_or_hours_across_spring_dst_transition_backwards twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,2,11,30)) # In 2006, spring DST transition occurred Apr 2 at 2AM; this day was only 23 hours long @@ -582,7 +628,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.ago(24.hours).inspect assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.advance(:hours => -24).inspect end - + def test_advance_1_day_across_fall_dst_transition twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,28,10,30)) # In 2006, fall DST transition occurred Oct 29 at 2AM; this day was 25 hours long @@ -593,7 +639,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sun, 29 Oct 2006 10:30:01 EST -05:00", twz.since(1.days + 1.second).inspect assert_equal "Sun, 29 Oct 2006 10:30:01 EST -05:00", (twz + 1.days + 1.second).inspect end - + def test_advance_1_day_across_fall_dst_transition_backwards twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,29,10,30)) # In 2006, fall DST transition occurred Oct 29 at 2AM; this day was 25 hours long @@ -603,7 +649,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", (twz - 1.days).inspect assert_equal "Sat, 28 Oct 2006 10:30:01 EDT -04:00", twz.ago(1.days - 1.second).inspect end - + def test_advance_1_day_expressed_as_number_of_seconds_minutes_or_hours_across_fall_dst_transition twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,28,10,30)) # In 2006, fall DST transition occurred Oct 29 at 2AM; this day was 25 hours long @@ -620,7 +666,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sun, 29 Oct 2006 09:30:00 EST -05:00", twz.since(24.hours).inspect assert_equal "Sun, 29 Oct 2006 09:30:00 EST -05:00", twz.advance(:hours => 24).inspect end - + def test_advance_1_day_expressed_as_number_of_seconds_minutes_or_hours_across_fall_dst_transition_backwards twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,29,9,30)) # In 2006, fall DST transition occurred Oct 29 at 2AM; this day was 25 hours long @@ -669,7 +715,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.ago(1.month).inspect assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", (twz - 1.month).inspect end - + def test_advance_1_year twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2008,2,15,10,30)) assert_equal "Sun, 15 Feb 2009 10:30:00 EST -05:00", twz.advance(:years => 1).inspect @@ -679,7 +725,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Thu, 15 Feb 2007 10:30:00 EST -05:00", twz.years_ago(1).inspect assert_equal "Thu, 15 Feb 2007 10:30:00 EST -05:00", (twz - 1.year).inspect end - + def test_advance_1_year_during_dst twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2008,7,15,10,30)) assert_equal "Wed, 15 Jul 2009 10:30:00 EDT -04:00", twz.advance(:years => 1).inspect @@ -689,6 +735,14 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sun, 15 Jul 2007 10:30:00 EDT -04:00", twz.years_ago(1).inspect 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 end class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb index f304844e82..d30852c013 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -104,6 +104,12 @@ class InflectorTest < Test::Unit::TestCase end end + def test_parameterize_with_custom_separator + StringToParameterized.each do |some_string, parameterized_string| + assert_equal(parameterized_string.gsub('-', '_'), ActiveSupport::Inflector.parameterize(some_string, '_')) + end + end + def test_classify ClassNameToTableName.each do |class_name, table_name| assert_equal(class_name, ActiveSupport::Inflector.classify(table_name)) diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb index 8057809dbd..fc7a35f859 100644 --- a/activesupport/test/inflector_test_cases.rb +++ b/activesupport/test/inflector_test_cases.rb @@ -147,7 +147,10 @@ module InflectorTestCases "Random text with *(bad)* characters" => "random-text-with-bad-characters", "Malmö" => "malmo", "Garçons" => "garcons", - "Allow_Under_Scores" => "allow_under_scores" + "Allow_Under_Scores" => "allow_under_scores", + "Trailing bad characters!@#" => "trailing-bad-characters", + "!@#Leading bad characters" => "leading-bad-characters", + "Squeeze separators" => "squeeze-separators" } UnderscoreToHuman = { diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index 38bb8f3e79..497f028369 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -101,18 +101,6 @@ class TestJSONEncoding < Test::Unit::TestCase end protected - def with_kcode(code) - if RUBY_VERSION < '1.9' - begin - old_kcode, $KCODE = $KCODE, 'UTF8' - yield - ensure - $KCODE = old_kcode - end - else - yield - end - end def object_keys(json_object) json_object[1..-2].scan(/([^{}:,\s]+):/).flatten.sort diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb index a87309b038..ca2af9b986 100644 --- a/activesupport/test/multibyte_chars_test.rb +++ b/activesupport/test/multibyte_chars_test.rb @@ -1,186 +1,569 @@ # encoding: utf-8 + require 'abstract_unit' +require 'multibyte_test_helpers' -if RUBY_VERSION < '1.9' +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 +end -$KCODE = 'UTF8' +class MultibyteCharsTest < Test::Unit::TestCase + include MultibyteTestHelpers -class CharsTest < Test::Unit::TestCase - def setup - @s = { - :utf8 => "Abcd Блå ffi блa 埋", - :ascii => "asci ias c iia s", - :bytes => "\270\236\010\210\245" - } + @proxy_class = ActiveSupport::Multibyte::Chars + @chars = @proxy_class.new UNICODE_STRING + end + + def test_wraps_the_original_string + assert_equal UNICODE_STRING, @chars.to_s + assert_equal UNICODE_STRING, @chars.wrapped_string end - - def test_sanity - @s.each do |t, s| - assert s.respond_to?(:chars), "All string should have the chars method (#{t})" - assert s.respond_to?(:to_s), "All string should have the to_s method (#{t})" - assert_kind_of ActiveSupport::Multibyte::Chars, s.chars, "#chars should return an instance of Chars (#{t})" + + def test_should_allow_method_calls_to_string + assert_nothing_raised do + @chars.__method_for_multibyte_testing + end + assert_raises NoMethodError do + @chars.__unknown_method end end - - def test_comparability - @s.each do |t, s| - assert_equal s, s.chars.to_s, "Chars#to_s should return enclosed string unchanged" + + def test_forwarded_method_calls_should_return_new_chars_instance + assert @chars.__method_for_multibyte_testing.kind_of?(@proxy_class) + 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 + assert @chars.__method_for_multibyte_testing!.kind_of?(@proxy_class) + assert_equal @chars.object_id, @chars.__method_for_multibyte_testing!.object_id + end + + def test_methods_are_forwarded_to_wrapped_string_for_byte_strings + assert_equal BYTE_STRING.class, BYTE_STRING.mb_chars.class + 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 + end + + def test_should_concatenate + assert_equal 'ab', 'a'.mb_chars + 'b' + assert_equal 'ab', 'a' + 'b'.mb_chars + assert_equal 'ab', 'a'.mb_chars + 'b'.mb_chars + + assert_equal 'ab', 'a'.mb_chars << 'b' + assert_equal 'ab', 'a' << 'b'.mb_chars + assert_equal 'ab', 'a'.mb_chars << 'b'.mb_chars + end + + def test_consumes_utf8_strings + assert @proxy_class.consumes?(UNICODE_STRING) + assert @proxy_class.consumes?(ASCII_STRING) + assert !@proxy_class.consumes?(BYTE_STRING) + end + + def test_unpack_utf8_strings + assert_equal 4, @proxy_class.u_unpack(UNICODE_STRING).length + assert_equal 5, @proxy_class.u_unpack(ASCII_STRING).length + end + + def test_unpack_raises_encoding_error_on_broken_strings + assert_raises(ActiveSupport::Multibyte::EncodingError) do + @proxy_class.u_unpack(BYTE_STRING) end - assert_nothing_raised do - assert_equal "a", "a", "Normal string comparisons should be unaffected" - assert_not_equal "a", "b", "Normal string comparisons should be unaffected" - assert_not_equal "a".chars, "b".chars, "Chars objects should be comparable" - assert_equal "a".chars, "A".downcase.chars, "Chars objects should be comparable to each other" - assert_equal "a".chars, "A".downcase, "Chars objects should be comparable to strings coming from elsewhere" - end - - assert !@s[:utf8].eql?(@s[:utf8].chars), "Strict comparison is not supported" - assert_equal @s[:utf8], @s[:utf8].chars, "Chars should be compared by their enclosed string" - - other_string = @s[:utf8].dup - assert_equal other_string, @s[:utf8].chars, "Chars should be compared by their enclosed string" - assert_equal other_string.chars, @s[:utf8].chars, "Chars should be compared by their enclosed string" - - strings = ['builder'.chars, 'armor'.chars, 'zebra'.chars] - strings.sort! - assert_equal ['armor', 'builder', 'zebra'], strings, "Chars should be sortable based on their enclosed string" - - # This leads to a StackLevelTooDeep exception if the comparison is not wired properly - assert_raise(NameError) do - Chars - end - end - - def test_utf8? - assert @s[:utf8].is_utf8?, "UTF-8 strings are UTF-8" - assert @s[:ascii].is_utf8?, "All ASCII strings are also valid UTF-8" - assert !@s[:bytes].is_utf8?, "This bytestring isn't UTF-8" - end - - # The test for the following methods are defined here because they can only be defined on the Chars class for - # various reasons - - def test_gsub - assert_equal 'éxa', 'éda'.chars.gsub(/d/, 'x') - with_kcode('none') do - assert_equal 'éxa', 'éda'.chars.gsub(/d/, 'x') - end - end - - def test_split - word = "efficient" - chars = ["e", "ffi", "c", "i", "e", "n", "t"] - assert_equal chars, word.split(//) - assert_equal chars, word.chars.split(//) - assert_kind_of ActiveSupport::Multibyte::Chars, word.chars.split(//).first, "Split should return Chars instances" - end - - def test_regexp - with_kcode('none') do - assert_equal 12, (@s[:utf8].chars =~ /ffi/), - "Regex matching should be bypassed to String" - end - with_kcode('UTF8') do - assert_equal 9, (@s[:utf8].chars =~ /ffi/), - "Regex matching should be unicode aware" - assert_nil((''.chars =~ /\d+/), - "Non-matching regular expressions should return nil") - end - end - - def test_pragma - if RUBY_VERSION < '1.9' - with_kcode('UTF8') do - assert " ".chars.send(:utf8_pragma?), "UTF8 pragma should be on because KCODE is UTF8" + end + + if RUBY_VERSION < '1.9' + def test_concatenation_should_return_a_proxy_class_instance + assert_equal ActiveSupport::Multibyte.proxy_class, ('a'.mb_chars + 'b').class + assert_equal ActiveSupport::Multibyte.proxy_class, ('a'.mb_chars << 'b').class + end + + def test_ascii_strings_are_treated_at_utf8_strings + assert_equal ActiveSupport::Multibyte.proxy_class, ASCII_STRING.mb_chars.class + end + + def test_concatenate_should_return_proxy_instance + assert(('a'.mb_chars + 'b').kind_of?(@proxy_class)) + assert(('a'.mb_chars + 'b'.mb_chars).kind_of?(@proxy_class)) + assert(('a'.mb_chars << 'b').kind_of?(@proxy_class)) + assert(('a'.mb_chars << 'b'.mb_chars).kind_of?(@proxy_class)) + end + end +end + +class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase + include MultibyteTestHelpers + + def setup + @chars = UNICODE_STRING.dup.mb_chars + + # NEWLINE, SPACE, EM SPACE + @whitespace = "\n#{[32, 8195].pack('U*')}" + @whitespace.force_encoding(Encoding::UTF_8) if @whitespace.respond_to?(:force_encoding) + @byte_order_mark = [65279].pack('U') + end + + if RUBY_VERSION < '1.9' + def test_split_should_return_an_array_of_chars_instances + @chars.split(//).each do |character| + assert character.kind_of?(ActiveSupport::Multibyte.proxy_class) end - with_kcode('none') do - assert !" ".chars.send(:utf8_pragma?), "UTF8 pragma should be off because KCODE is not UTF8" + end + + def test_indexed_insert_accepts_fixnums + @chars[2] = 32 + assert_equal 'こに わ', @chars + end + + def test_overridden_bang_methods_return_self + [:rstrip!, :lstrip!, :strip!, :reverse!, :upcase!, :downcase!, :capitalize!].each do |method| + assert_equal @chars.object_id, @chars.send(method).object_id end - else - assert !" ".chars.send(:utf8_pragma?), "UTF8 pragma should be off in Ruby 1.9" - end - end - - def test_handler_setting - handler = ''.chars.handler - - ActiveSupport::Multibyte::Chars.handler = :first - assert_equal :first, ''.chars.handler - ActiveSupport::Multibyte::Chars.handler = :second - assert_equal :second, ''.chars.handler - assert_raise(NoMethodError) do - ''.chars.handler.split - end - - ActiveSupport::Multibyte::Chars.handler = handler - end - - def test_method_chaining - assert_kind_of ActiveSupport::Multibyte::Chars, ''.chars.downcase - assert_kind_of ActiveSupport::Multibyte::Chars, ''.chars.strip, "Strip should return a Chars object" - assert_kind_of ActiveSupport::Multibyte::Chars, ''.chars.downcase.strip, "The Chars object should be " + - "forwarded down the call path for chaining" - assert_equal 'foo', " FOO ".chars.normalize.downcase.strip, "The Chars that results from the " + - " operations should be comparable to the string value of the result" - end - - def test_passthrough_on_kcode - # The easiest way to check if the passthrough is in place is through #size - with_kcode('none') do - assert_equal 26, @s[:utf8].chars.size - end - with_kcode('UTF8') do - assert_equal 17, @s[:utf8].chars.size - end - end - - def test_destructiveness - # Note that we're testing the destructiveness here and not the correct behaviour of the methods - str = 'ac' - str.chars.insert(1, 'b') - assert_equal 'abc', str, 'Insert should be destructive for a string' - - str = 'ac' - str.chars.reverse! - assert_equal 'ca', str, 'reverse! should be destructive for a string' - end - - def test_resilience - assert_nothing_raised do - assert_equal 5, @s[:bytes].chars.size, "The sequence contains five interpretable bytes" + assert_equal @chars.object_id, @chars.slice!(1).object_id end - reversed = [0xb8, 0x17e, 0x8, 0x2c6, 0xa5].reverse.pack('U*') - assert_nothing_raised do - assert_equal reversed, @s[:bytes].chars.reverse.to_s, "Reversing the string should only yield interpretable bytes" + + def test_overridden_bang_methods_change_wrapped_string + [:rstrip!, :lstrip!, :strip!, :reverse!, :upcase!, :downcase!].each do |method| + original = ' Café ' + proxy = chars(original.dup) + proxy.send(method) + assert_not_equal original, proxy.to_s + end + proxy = chars('Café') + proxy.slice!(3) + assert_equal 'é', proxy.to_s + + proxy = chars('òu') + proxy.capitalize! + assert_equal 'Òu', proxy.to_s end - assert_nothing_raised do - @s[:bytes].chars.reverse! - assert_equal reversed, @s[:bytes].to_s, "Reversing the string should only yield interpretable bytes" + end + + if RUBY_VERSION >= '1.9' + def test_unicode_string_should_have_utf8_encoding + assert_equal Encoding::UTF_8, UNICODE_STRING.encoding end end - - def test_duck_typing - assert_equal true, 'test'.chars.respond_to?(:strip) - assert_equal true, 'test'.chars.respond_to?(:normalize) - assert_equal true, 'test'.chars.respond_to?(:normalize!) - assert_equal false, 'test'.chars.respond_to?(:a_method_that_doesnt_exist) + + def test_identity + assert_equal @chars, @chars + assert @chars.eql?(@chars) + if RUBY_VERSION <= '1.9' + assert !@chars.eql?(UNICODE_STRING) + else + assert @chars.eql?(UNICODE_STRING) + end end - - def test_acts_like_string - assert 'Bambi'.chars.acts_like_string? + + def test_string_methods_are_chainable + assert chars('').insert(0, '').kind_of?(ActiveSupport::Multibyte.proxy_class) + assert chars('').rjust(1).kind_of?(ActiveSupport::Multibyte.proxy_class) + assert chars('').ljust(1).kind_of?(ActiveSupport::Multibyte.proxy_class) + assert chars('').center(1).kind_of?(ActiveSupport::Multibyte.proxy_class) + assert chars('').rstrip.kind_of?(ActiveSupport::Multibyte.proxy_class) + assert chars('').lstrip.kind_of?(ActiveSupport::Multibyte.proxy_class) + assert chars('').strip.kind_of?(ActiveSupport::Multibyte.proxy_class) + assert chars('').reverse.kind_of?(ActiveSupport::Multibyte.proxy_class) + assert chars(' ').slice(0).kind_of?(ActiveSupport::Multibyte.proxy_class) + assert chars('').upcase.kind_of?(ActiveSupport::Multibyte.proxy_class) + assert chars('').downcase.kind_of?(ActiveSupport::Multibyte.proxy_class) + assert chars('').capitalize.kind_of?(ActiveSupport::Multibyte.proxy_class) + assert chars('').normalize.kind_of?(ActiveSupport::Multibyte.proxy_class) + assert chars('').decompose.kind_of?(ActiveSupport::Multibyte.proxy_class) + assert chars('').compose.kind_of?(ActiveSupport::Multibyte.proxy_class) + assert chars('').tidy_bytes.kind_of?(ActiveSupport::Multibyte.proxy_class) end - protected + def test_should_be_equal_to_the_wrapped_string + assert_equal UNICODE_STRING, @chars + assert_equal @chars, UNICODE_STRING + end - def with_kcode(kcode) - old_kcode, $KCODE = $KCODE, kcode - begin - yield - ensure - $KCODE = old_kcode + def test_should_not_be_equal_to_an_other_string + assert_not_equal @chars, 'other' + assert_not_equal 'other', @chars + end + + def test_sortability + words = %w(builder armor zebra).map(&:mb_chars).sort + assert_equal %w(armor builder zebra), words + end + + def test_should_return_character_offset_for_regexp_matches + assert_nil(@chars =~ /wrong/u) + assert_equal 0, (@chars =~ /こ/u) + assert_equal 1, (@chars =~ /に/u) + assert_equal 3, (@chars =~ /わ/u) + end + + def test_should_use_character_offsets_for_insert_offsets + assert_equal '', ''.mb_chars.insert(0, '') + assert_equal 'こわにちわ', @chars.insert(1, 'わ') + assert_equal 'こわわわにちわ', @chars.insert(2, 'わわ') + assert_equal 'わこわわわにちわ', @chars.insert(0, 'わ') + assert_equal 'わこわわわにちわ', @chars.wrapped_string if RUBY_VERSION < '1.9' + end + + def test_insert_should_be_destructive + @chars.insert(1, 'わ') + assert_equal 'こわにちわ', @chars + end + + def test_insert_throws_index_error + assert_raises(IndexError) { @chars.insert(-12, 'わ')} + assert_raises(IndexError) { @chars.insert(12, 'わ') } + end + + def test_should_know_if_one_includes_the_other + assert @chars.include?('') + assert @chars.include?('ち') + assert @chars.include?('わ') + assert !@chars.include?('こちわ') + assert !@chars.include?('a') + end + + def test_include_raises_type_error_when_nil_is_passed + assert_raises(TypeError) do + @chars.include?(nil) end end + + def test_index_should_return_character_offset + assert_nil @chars.index('u') + assert_equal 0, @chars.index('こに') + assert_equal 2, @chars.index('ち') + assert_equal 3, @chars.index('わ') + end + + def test_indexed_insert_should_take_character_offsets + @chars[2] = 'a' + assert_equal 'こにaわ', @chars + @chars[2] = 'ηη' + assert_equal 'こにηηわ', @chars + @chars[3, 2] = 'λλλ' + assert_equal 'こにηλλλ', @chars + @chars[1, 0] = "λ" + assert_equal 'こλにηλλλ', @chars + @chars[4..6] = "ηη" + assert_equal 'こλにηηη', @chars + @chars[/ηη/] = "λλλ" + assert_equal 'こλにλλλη', @chars + @chars[/(λλ)(.)/, 2] = "α" + assert_equal 'こλにλλαη', @chars + @chars["α"] = "¢" + assert_equal 'こλにλλ¢η', @chars + @chars["λλ"] = "ααα" + assert_equal 'こλにααα¢η', @chars + end + + def test_indexed_insert_should_raise_on_index_overflow + before = @chars.to_s + assert_raises(IndexError) { @chars[10] = 'a' } + assert_raises(IndexError) { @chars[10, 4] = 'a' } + assert_raises(IndexError) { @chars[/ii/] = 'a' } + assert_raises(IndexError) { @chars[/()/, 10] = 'a' } + assert_equal before, @chars + end + + def test_indexed_insert_should_raise_on_range_overflow + before = @chars.to_s + assert_raises(RangeError) { @chars[10..12] = 'a' } + assert_equal before, @chars + end + + def test_rjust_should_raise_argument_errors_on_bad_arguments + assert_raises(ArgumentError) { @chars.rjust(10, '') } + assert_raises(ArgumentError) { @chars.rjust } + end + + def test_rjust_should_count_characters_instead_of_bytes + assert_equal UNICODE_STRING, @chars.rjust(-3) + assert_equal UNICODE_STRING, @chars.rjust(0) + assert_equal UNICODE_STRING, @chars.rjust(4) + assert_equal " #{UNICODE_STRING}", @chars.rjust(5) + assert_equal " #{UNICODE_STRING}", @chars.rjust(7) + assert_equal "---#{UNICODE_STRING}", @chars.rjust(7, '-') + assert_equal "ααα#{UNICODE_STRING}", @chars.rjust(7, 'α') + assert_equal "aba#{UNICODE_STRING}", @chars.rjust(7, 'ab') + assert_equal "αηα#{UNICODE_STRING}", @chars.rjust(7, 'αη') + assert_equal "αηαη#{UNICODE_STRING}", @chars.rjust(8, 'αη') + end + + def test_ljust_should_raise_argument_errors_on_bad_arguments + assert_raises(ArgumentError) { @chars.ljust(10, '') } + assert_raises(ArgumentError) { @chars.ljust } + end + + def test_ljust_should_count_characters_instead_of_bytes + assert_equal UNICODE_STRING, @chars.ljust(-3) + assert_equal UNICODE_STRING, @chars.ljust(0) + assert_equal UNICODE_STRING, @chars.ljust(4) + assert_equal "#{UNICODE_STRING} ", @chars.ljust(5) + assert_equal "#{UNICODE_STRING} ", @chars.ljust(7) + assert_equal "#{UNICODE_STRING}---", @chars.ljust(7, '-') + assert_equal "#{UNICODE_STRING}ααα", @chars.ljust(7, 'α') + assert_equal "#{UNICODE_STRING}aba", @chars.ljust(7, 'ab') + assert_equal "#{UNICODE_STRING}αηα", @chars.ljust(7, 'αη') + assert_equal "#{UNICODE_STRING}αηαη", @chars.ljust(8, 'αη') + end + + def test_center_should_raise_argument_errors_on_bad_arguments + assert_raises(ArgumentError) { @chars.center(10, '') } + assert_raises(ArgumentError) { @chars.center } + end + + def test_center_should_count_charactes_instead_of_bytes + assert_equal UNICODE_STRING, @chars.center(-3) + assert_equal UNICODE_STRING, @chars.center(0) + assert_equal UNICODE_STRING, @chars.center(4) + assert_equal "#{UNICODE_STRING} ", @chars.center(5) + assert_equal " #{UNICODE_STRING} ", @chars.center(6) + assert_equal " #{UNICODE_STRING} ", @chars.center(7) + assert_equal "--#{UNICODE_STRING}--", @chars.center(8, '-') + assert_equal "--#{UNICODE_STRING}---", @chars.center(9, '-') + assert_equal "αα#{UNICODE_STRING}αα", @chars.center(8, 'α') + assert_equal "αα#{UNICODE_STRING}ααα", @chars.center(9, 'α') + assert_equal "a#{UNICODE_STRING}ab", @chars.center(7, 'ab') + assert_equal "ab#{UNICODE_STRING}ab", @chars.center(8, 'ab') + assert_equal "abab#{UNICODE_STRING}abab", @chars.center(12, 'ab') + assert_equal "α#{UNICODE_STRING}αη", @chars.center(7, 'αη') + assert_equal "αη#{UNICODE_STRING}αη", @chars.center(8, 'αη') + end + + def test_lstrip_strips_whitespace_from_the_left_of_the_string + assert_equal UNICODE_STRING, UNICODE_STRING.mb_chars.lstrip + assert_equal UNICODE_STRING, (@whitespace + UNICODE_STRING).mb_chars.lstrip + assert_equal UNICODE_STRING + @whitespace, (@whitespace + UNICODE_STRING + @whitespace).mb_chars.lstrip + end + + def test_rstrip_strips_whitespace_from_the_right_of_the_string + assert_equal UNICODE_STRING, UNICODE_STRING.mb_chars.rstrip + assert_equal UNICODE_STRING, (UNICODE_STRING + @whitespace).mb_chars.rstrip + assert_equal @whitespace + UNICODE_STRING, (@whitespace + UNICODE_STRING + @whitespace).mb_chars.rstrip + end + + def test_strip_strips_whitespace + assert_equal UNICODE_STRING, UNICODE_STRING.mb_chars.strip + assert_equal UNICODE_STRING, (@whitespace + UNICODE_STRING).mb_chars.strip + assert_equal UNICODE_STRING, (UNICODE_STRING + @whitespace).mb_chars.strip + assert_equal UNICODE_STRING, (@whitespace + UNICODE_STRING + @whitespace).mb_chars.strip + end + + def test_stripping_whitespace_leaves_whitespace_within_the_string_intact + string_with_whitespace = UNICODE_STRING + @whitespace + UNICODE_STRING + assert_equal string_with_whitespace, string_with_whitespace.mb_chars.strip + assert_equal string_with_whitespace, string_with_whitespace.mb_chars.lstrip + assert_equal string_with_whitespace, string_with_whitespace.mb_chars.rstrip + end + + def test_size_returns_characters_instead_of_bytes + assert_equal 0, ''.mb_chars.size + assert_equal 4, @chars.size + assert_equal 4, @chars.length + assert_equal 5, ASCII_STRING.mb_chars.size + end + + def test_reverse_reverses_characters + assert_equal '', ''.mb_chars.reverse + assert_equal 'わちにこ', @chars.reverse + end + + def test_slice_should_take_character_offsets + assert_equal nil, ''.mb_chars.slice(0) + assert_equal 'こ', @chars.slice(0) + assert_equal 'わ', @chars.slice(3) + assert_equal nil, ''.mb_chars.slice(-1..1) + assert_equal '', ''.mb_chars.slice(0..10) + assert_equal 'にちわ', @chars.slice(1..3) + assert_equal 'にちわ', @chars.slice(1, 3) + assert_equal 'こ', @chars.slice(0, 1) + assert_equal 'ちわ', @chars.slice(2..10) + assert_equal '', @chars.slice(4..10) + assert_equal 'に', @chars.slice(/に/u) + assert_equal 'にち', @chars.slice(/に\w/u) + assert_equal nil, @chars.slice(/unknown/u) + assert_equal 'にち', @chars.slice(/(にち)/u, 1) + assert_equal nil, @chars.slice(/(にち)/u, 2) + assert_equal nil, @chars.slice(7..6) + end + + def test_slice_should_throw_exceptions_on_invalid_arguments + assert_raise(TypeError) { @chars.slice(2..3, 1) } + assert_raise(TypeError) { @chars.slice(1, 2..3) } + assert_raise(ArgumentError) { @chars.slice(1, 1, 1) } + end + + def test_upcase_should_upcase_ascii_characters + assert_equal '', ''.mb_chars.upcase + assert_equal 'ABC', 'aBc'.mb_chars.upcase + end + + def test_downcase_should_downcase_ascii_characters + assert_equal '', ''.mb_chars.downcase + assert_equal 'abc', 'aBc'.mb_chars.downcase + end + + def test_capitalize_should_work_on_ascii_characters + assert_equal '', ''.mb_chars.capitalize + assert_equal 'Abc', 'abc'.mb_chars.capitalize + end + + def test_respond_to_knows_which_methods_the_proxy_responds_to + assert ''.mb_chars.respond_to?(:slice) # Defined on Chars + assert ''.mb_chars.respond_to?(:capitalize!) # Defined on Chars + assert ''.mb_chars.respond_to?(:gsub) # Defined on String + assert !''.mb_chars.respond_to?(:undefined_method) # Not defined + end + + def test_acts_like_string + assert 'Bambi'.mb_chars.acts_like_string? + end end +# The default Multibyte Chars proxy has more features than the normal string implementation. Tests +# for the implementation of these features should run on all Ruby versions and shouldn't be tested +# through the proxy methods. +class MultibyteCharsExtrasTest < Test::Unit::TestCase + include MultibyteTestHelpers + + if RUBY_VERSION >= '1.9' + def test_tidy_bytes_is_broken_on_1_9_0 + assert_raises(ArgumentError) do + assert_equal_codepoints [0xfffd].pack('U'), chars("\xef\xbf\xbd").tidy_bytes + end + end + end + + def test_upcase_should_be_unicode_aware + assert_equal "АБВГД\0F", chars("аБвгд\0f").upcase + assert_equal 'こにちわ', chars('こにちわ').upcase + end + + def test_downcase_should_be_unicode_aware + assert_equal "абвгд\0f", chars("аБвгд\0f").downcase + assert_equal 'こにちわ', chars('こにちわ').downcase + end + + def test_capitalize_should_be_unicode_aware + { 'аБвг аБвг' => 'Абвг абвг', + 'аБвг АБВГ' => 'Абвг абвг', + 'АБВГ АБВГ' => 'Абвг абвг', + '' => '' }.each do |f,t| + assert_equal t, chars(f).capitalize + end + end + + def test_composition_exclusion_is_set_up_properly + # Normalization of DEVANAGARI LETTER QA breaks when composition exclusion isn't used correctly + qa = [0x915, 0x93c].pack('U*') + assert_equal qa, chars(qa).normalize(:c) + end + + # Test for the Public Review Issue #29, bad explanation of composition might lead to a + # bad implementation: http://www.unicode.org/review/pr-29.html + def test_normalization_C_pri_29 + [ + [0x0B47, 0x0300, 0x0B3E], + [0x1100, 0x0300, 0x1161] + ].map { |c| c.pack('U*') }.each do |c| + assert_equal_codepoints c, chars(c).normalize(:c) + end + end + + def test_normalization_shouldnt_strip_null_bytes + null_byte_str = "Test\0test" + + assert_equal null_byte_str, chars(null_byte_str).normalize(:kc) + assert_equal null_byte_str, chars(null_byte_str).normalize(:c) + assert_equal null_byte_str, chars(null_byte_str).normalize(:d) + assert_equal null_byte_str, chars(null_byte_str).normalize(:kd) + assert_equal null_byte_str, chars(null_byte_str).decompose + assert_equal null_byte_str, chars(null_byte_str).compose + end + + def test_simple_normalization + comp_str = [ + 44, # LATIN CAPITAL LETTER D + 307, # COMBINING DOT ABOVE + 328, # COMBINING OGONEK + 323 # COMBINING DOT BELOW + ].pack("U*") + + assert_equal_codepoints '', chars('').normalize + assert_equal_codepoints [44,105,106,328,323].pack("U*"), chars(comp_str).normalize(:kc).to_s + assert_equal_codepoints [44,307,328,323].pack("U*"), chars(comp_str).normalize(:c).to_s + assert_equal_codepoints [44,307,110,780,78,769].pack("U*"), chars(comp_str).normalize(:d).to_s + assert_equal_codepoints [44,105,106,110,780,78,769].pack("U*"), chars(comp_str).normalize(:kd).to_s + end + + def test_should_compute_grapheme_length + [ + ['', 0], + ['abc', 3], + ['こにちわ', 4], + [[0x0924, 0x094D, 0x0930].pack('U*'), 2], + [%w(cr lf), 1], + [%w(l l), 1], + [%w(l v), 1], + [%w(l lv), 1], + [%w(l lvt), 1], + [%w(lv v), 1], + [%w(lv t), 1], + [%w(v v), 1], + [%w(v t), 1], + [%w(lvt t), 1], + [%w(t t), 1], + [%w(n extend), 1], + [%w(n n), 2], + [%w(n cr lf n), 3], + [%w(n l v t), 2] + ].each do |input, expected_length| + if input.kind_of?(Array) + str = string_from_classes(input) + else + str = input + end + assert_equal expected_length, chars(str).g_length + end + end + + def test_tidy_bytes_should_tidy_bytes + byte_string = "\270\236\010\210\245" + tidy_string = [0xb8, 0x17e, 0x8, 0x2c6, 0xa5].pack('U*') + ascii_padding = 'aa' + utf8_padding = 'éé' + + assert_equal_codepoints tidy_string, chars(byte_string).tidy_bytes + + assert_equal_codepoints ascii_padding.dup.insert(1, tidy_string), + chars(ascii_padding.dup.insert(1, byte_string)).tidy_bytes + assert_equal_codepoints utf8_padding.dup.insert(2, tidy_string), + chars(utf8_padding.dup.insert(2, byte_string)).tidy_bytes + assert_nothing_raised { chars(byte_string).tidy_bytes.to_s.unpack('U*') } + + assert_equal_codepoints "\xC3\xA7", chars("\xE7").tidy_bytes # iso_8859_1: small c cedilla + assert_equal_codepoints "\xE2\x80\x9C", chars("\x93").tidy_bytes # win_1252: left smart quote + assert_equal_codepoints "\xE2\x82\xAC", chars("\x80").tidy_bytes # win_1252: euro + assert_equal_codepoints "\x00", chars("\x00").tidy_bytes # null char + assert_equal_codepoints [0xfffd].pack('U'), chars("\xef\xbf\xbd").tidy_bytes # invalid char + rescue ArgumentError => e + raise e if RUBY_VERSION < '1.9' + end + + private + + def string_from_classes(classes) + # Characters from the character classes as described in UAX #29 + character_from_class = { + :l => 0x1100, :v => 0x1160, :t => 0x11A8, :lv => 0xAC00, :lvt => 0xAC01, :cr => 0x000D, :lf => 0x000A, + :extend => 0x094D, :n => 0x64 + } + classes.collect do |k| + character_from_class[k.intern] + end.pack('U*') + end end diff --git a/activesupport/test/multibyte_conformance.rb b/activesupport/test/multibyte_conformance.rb index 05fb9ef7a7..caae4791b4 100644 --- a/activesupport/test/multibyte_conformance.rb +++ b/activesupport/test/multibyte_conformance.rb @@ -1,13 +1,11 @@ -require 'abstract_unit' -require 'open-uri' - -if RUBY_VERSION < '1.9' +# encoding: utf-8 -$KCODE = 'UTF8' +require 'abstract_unit' +require 'multibyte_test_helpers' -UNIDATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::UNICODE_VERSION}/ucd" -UNIDATA_FILE = '/NormalizationTest.txt' -CACHE_DIR = File.dirname(__FILE__) + '/cache' +require 'fileutils' +require 'open-uri' +require 'tmpdir' class Downloader def self.download(from, to) @@ -27,17 +25,19 @@ class Downloader end end -class String - # Unicode Inspect returns the codepoints of the string in hex - def ui - "#{self} " + ("[%s]" % unpack("U*").map{|cp| cp.to_s(16) }.join(' ')) - end unless ''.respond_to?(:ui) -end - -Dir.mkdir(CACHE_DIR) unless File.exist?(CACHE_DIR) -Downloader.download(UNIDATA_URL + UNIDATA_FILE, CACHE_DIR + UNIDATA_FILE) - -module ConformanceTest +class MultibyteConformanceTest < Test::Unit::TestCase + include MultibyteTestHelpers + + UNIDATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::UNICODE_VERSION}/ucd" + UNIDATA_FILE = '/NormalizationTest.txt' + CACHE_DIR = File.join(Dir.tmpdir, 'cache') + + def setup + FileUtils.mkdir_p(CACHE_DIR) + Downloader.download(UNIDATA_URL + UNIDATA_FILE, CACHE_DIR + UNIDATA_FILE) + @proxy = ActiveSupport::Multibyte::Chars + end + def test_normalizations_C each_line_of_norm_tests do |*cols| col1, col2, col3, col4, col5, comment = *cols @@ -47,13 +47,13 @@ module ConformanceTest # # NFC # c2 == NFC(c1) == NFC(c2) == NFC(c3) - assert_equal col2.ui, @handler.normalize(col1, :c).ui, "Form C - Col 2 has to be NFC(1) - #{comment}" - assert_equal col2.ui, @handler.normalize(col2, :c).ui, "Form C - Col 2 has to be NFC(2) - #{comment}" - assert_equal col2.ui, @handler.normalize(col3, :c).ui, "Form C - Col 2 has to be NFC(3) - #{comment}" + assert_equal_codepoints col2, @proxy.new(col1).normalize(:c), "Form C - Col 2 has to be NFC(1) - #{comment}" + assert_equal_codepoints col2, @proxy.new(col2).normalize(:c), "Form C - Col 2 has to be NFC(2) - #{comment}" + assert_equal_codepoints col2, @proxy.new(col3).normalize(:c), "Form C - Col 2 has to be NFC(3) - #{comment}" # # c4 == NFC(c4) == NFC(c5) - assert_equal col4.ui, @handler.normalize(col4, :c).ui, "Form C - Col 4 has to be C(4) - #{comment}" - assert_equal col4.ui, @handler.normalize(col5, :c).ui, "Form C - Col 4 has to be C(5) - #{comment}" + assert_equal_codepoints col4, @proxy.new(col4).normalize(:c), "Form C - Col 4 has to be C(4) - #{comment}" + assert_equal_codepoints col4, @proxy.new(col5).normalize(:c), "Form C - Col 4 has to be C(5) - #{comment}" end end @@ -63,12 +63,12 @@ module ConformanceTest # # NFD # c3 == NFD(c1) == NFD(c2) == NFD(c3) - assert_equal col3.ui, @handler.normalize(col1, :d).ui, "Form D - Col 3 has to be NFD(1) - #{comment}" - assert_equal col3.ui, @handler.normalize(col2, :d).ui, "Form D - Col 3 has to be NFD(2) - #{comment}" - assert_equal col3.ui, @handler.normalize(col3, :d).ui, "Form D - Col 3 has to be NFD(3) - #{comment}" + assert_equal_codepoints col3, @proxy.new(col1).normalize(:d), "Form D - Col 3 has to be NFD(1) - #{comment}" + assert_equal_codepoints col3, @proxy.new(col2).normalize(:d), "Form D - Col 3 has to be NFD(2) - #{comment}" + assert_equal_codepoints col3, @proxy.new(col3).normalize(:d), "Form D - Col 3 has to be NFD(3) - #{comment}" # c5 == NFD(c4) == NFD(c5) - assert_equal col5.ui, @handler.normalize(col4, :d).ui, "Form D - Col 5 has to be NFD(4) - #{comment}" - assert_equal col5.ui, @handler.normalize(col5, :d).ui, "Form D - Col 5 has to be NFD(5) - #{comment}" + assert_equal_codepoints col5, @proxy.new(col4).normalize(:d), "Form D - Col 5 has to be NFD(4) - #{comment}" + assert_equal_codepoints col5, @proxy.new(col5).normalize(:d), "Form D - Col 5 has to be NFD(5) - #{comment}" end end @@ -78,11 +78,11 @@ module ConformanceTest # # NFKC # c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5) - assert_equal col4.ui, @handler.normalize(col1, :kc).ui, "Form D - Col 4 has to be NFKC(1) - #{comment}" - assert_equal col4.ui, @handler.normalize(col2, :kc).ui, "Form D - Col 4 has to be NFKC(2) - #{comment}" - assert_equal col4.ui, @handler.normalize(col3, :kc).ui, "Form D - Col 4 has to be NFKC(3) - #{comment}" - assert_equal col4.ui, @handler.normalize(col4, :kc).ui, "Form D - Col 4 has to be NFKC(4) - #{comment}" - assert_equal col4.ui, @handler.normalize(col5, :kc).ui, "Form D - Col 4 has to be NFKC(5) - #{comment}" + assert_equal_codepoints col4, @proxy.new(col1).normalize(:kc), "Form D - Col 4 has to be NFKC(1) - #{comment}" + assert_equal_codepoints col4, @proxy.new(col2).normalize(:kc), "Form D - Col 4 has to be NFKC(2) - #{comment}" + assert_equal_codepoints col4, @proxy.new(col3).normalize(:kc), "Form D - Col 4 has to be NFKC(3) - #{comment}" + assert_equal_codepoints col4, @proxy.new(col4).normalize(:kc), "Form D - Col 4 has to be NFKC(4) - #{comment}" + assert_equal_codepoints col4, @proxy.new(col5).normalize(:kc), "Form D - Col 4 has to be NFKC(5) - #{comment}" end end @@ -92,11 +92,11 @@ module ConformanceTest # # NFKD # c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5) - assert_equal col5.ui, @handler.normalize(col1, :kd).ui, "Form KD - Col 5 has to be NFKD(1) - #{comment}" - assert_equal col5.ui, @handler.normalize(col2, :kd).ui, "Form KD - Col 5 has to be NFKD(2) - #{comment}" - assert_equal col5.ui, @handler.normalize(col3, :kd).ui, "Form KD - Col 5 has to be NFKD(3) - #{comment}" - assert_equal col5.ui, @handler.normalize(col4, :kd).ui, "Form KD - Col 5 has to be NFKD(4) - #{comment}" - assert_equal col5.ui, @handler.normalize(col5, :kd).ui, "Form KD - Col 5 has to be NFKD(5) - #{comment}" + assert_equal_codepoints col5, @proxy.new(col1).normalize(:kd), "Form KD - Col 5 has to be NFKD(1) - #{comment}" + assert_equal_codepoints col5, @proxy.new(col2).normalize(:kd), "Form KD - Col 5 has to be NFKD(2) - #{comment}" + assert_equal_codepoints col5, @proxy.new(col3).normalize(:kd), "Form KD - Col 5 has to be NFKD(3) - #{comment}" + assert_equal_codepoints col5, @proxy.new(col4).normalize(:kd), "Form KD - Col 5 has to be NFKD(4) - #{comment}" + assert_equal_codepoints col5, @proxy.new(col5).normalize(:kd), "Form KD - Col 5 has to be NFKD(5) - #{comment}" end end @@ -104,7 +104,7 @@ module ConformanceTest def each_line_of_norm_tests(&block) lines = 0 max_test_lines = 0 # Don't limit below 38, because that's the header of the testfile - File.open(File.dirname(__FILE__) + '/cache' + UNIDATA_FILE, 'r') do | f | + File.open(File.join(CACHE_DIR, UNIDATA_FILE), 'r') do | f | until f.eof? || (max_test_lines > 38 and lines > max_test_lines) lines += 1 line = f.gets.chomp! @@ -122,25 +122,8 @@ module ConformanceTest end end end -end - -begin - require_library_or_gem('utf8proc_native') - require 'active_record/multibyte/handlers/utf8_handler_proc' - class ConformanceTestProc < Test::Unit::TestCase - include ConformanceTest - def setup - @handler = ::ActiveSupport::Multibyte::Handlers::UTF8HandlerProc + + def inspect_codepoints(str) + str.to_s.unpack("U*").map{|cp| cp.to_s(16) }.join(' ') end - end -rescue LoadError -end - -class ConformanceTestPure < Test::Unit::TestCase - include ConformanceTest - def setup - @handler = ::ActiveSupport::Multibyte::Handlers::UTF8Handler - end -end - -end +end
\ No newline at end of file diff --git a/activesupport/test/multibyte_handler_test.rb b/activesupport/test/multibyte_handler_test.rb deleted file mode 100644 index 5575ecc32d..0000000000 --- a/activesupport/test/multibyte_handler_test.rb +++ /dev/null @@ -1,372 +0,0 @@ -# encoding: utf-8 -require 'abstract_unit' - -if RUBY_VERSION < '1.9' - -$KCODE = 'UTF8' - -class String - # Unicode Inspect returns the codepoints of the string in hex - def ui - "#{self} " + ("[%s]" % unpack("U*").map{|cp| cp.to_s(16) }.join(' ')) - end unless ''.respond_to?(:ui) -end - -module UTF8HandlingTest - - def common_setup - # This is an ASCII string with some russian strings and a ligature. It's nicely calibrated, because - # slicing it at some specific bytes will kill your characters if you use standard Ruby routines. - # It has both capital and standard letters, so that we can test case conversions easily. - # It has 26 characters and 28 when the ligature gets split during normalization. - @string = "Abcd Блå ffi бла бла бла бла" - @string_kd = "Abcd Блå ffi бла бла бла бла" - @string_kc = "Abcd Блå ffi бла бла бла бла" - @string_c = "Abcd Блå ffi бла бла бла бла" - @string_d = "Abcd Блå ffi бла бла бла бла" - @bytestring = "\270\236\010\210\245" # Not UTF-8 - - # Characters from the character classes as described in UAX #29 - @character_from_class = { - :l => 0x1100, :v => 0x1160, :t => 0x11A8, :lv => 0xAC00, :lvt => 0xAC01, :cr => 0x000D, :lf => 0x000A, - :extend => 0x094D, :n => 0x64 - } - end - - def test_utf8_recognition - assert ActiveSupport::Multibyte::Handlers::UTF8Handler.consumes?(@string), - "Should recognize as a valid UTF-8 string" - assert !ActiveSupport::Multibyte::Handlers::UTF8Handler.consumes?(@bytestring), "This is bytestring, not UTF-8" - end - - def test_simple_normalization - # Normalization of DEVANAGARI LETTER QA breaks when composition exclusion isn't used correctly - assert_equal [0x915, 0x93c].pack('U*').ui, [0x915, 0x93c].pack('U*').chars.normalize(:c).to_s.ui - - null_byte_str = "Test\0test" - - assert_equal '', @handler.normalize(''), "Empty string should not break things" - assert_equal null_byte_str.ui, @handler.normalize(null_byte_str, :kc).ui, "Null byte should remain" - assert_equal null_byte_str.ui, @handler.normalize(null_byte_str, :c).ui, "Null byte should remain" - assert_equal null_byte_str.ui, @handler.normalize(null_byte_str, :d).ui, "Null byte should remain" - assert_equal null_byte_str.ui, @handler.normalize(null_byte_str, :kd).ui, "Null byte should remain" - assert_equal null_byte_str.ui, @handler.decompose(null_byte_str).ui, "Null byte should remain" - assert_equal null_byte_str.ui, @handler.compose(null_byte_str).ui, "Null byte should remain" - - comp_str = [ - 44, # LATIN CAPITAL LETTER D - 307, # COMBINING DOT ABOVE - 328, # COMBINING OGONEK - 323 # COMBINING DOT BELOW - ].pack("U*") - norm_str_KC = [44,105,106,328,323].pack("U*") - norm_str_C = [44,307,328,323].pack("U*") - norm_str_D = [44,307,110,780,78,769].pack("U*") - norm_str_KD = [44,105,106,110,780,78,769].pack("U*") - - assert_equal norm_str_KC.ui, @handler.normalize(comp_str, :kc).ui, "Should normalize KC" - assert_equal norm_str_C.ui, @handler.normalize(comp_str, :c).ui, "Should normalize C" - assert_equal norm_str_D.ui, @handler.normalize(comp_str, :d).ui, "Should normalize D" - assert_equal norm_str_KD.ui, @handler.normalize(comp_str, :kd).ui, "Should normalize KD" - - assert_raise(ActiveSupport::Multibyte::Handlers::EncodingError) { @handler.normalize(@bytestring) } - end - - # Test for the Public Review Issue #29, bad explanation of composition might lead to a - # bad implementation: http://www.unicode.org/review/pr-29.html - def test_normalization_C_pri_29 - [ - [0x0B47, 0x0300, 0x0B3E], - [0x1100, 0x0300, 0x1161] - ].map { |c| c.pack('U*') }.each do |c| - assert_equal c.ui, @handler.normalize(c, :c).ui, "Composition is implemented incorrectly" - end - end - - def test_casefolding - simple_str = "abCdef" - simple_str_upcase = "ABCDEF" - simple_str_downcase = "abcdef" - - assert_equal '', @handler.downcase(@handler.upcase('')), "Empty string should not break things" - assert_equal simple_str_upcase, @handler.upcase(simple_str), "should upcase properly" - assert_equal simple_str_downcase, @handler.downcase(simple_str), "should downcase properly" - assert_equal simple_str_downcase, @handler.downcase(@handler.upcase(simple_str_downcase)), "upcase and downcase should be mirrors" - - rus_str = "аБвгд\0f" - rus_str_upcase = "АБВГД\0F" - rus_str_downcase = "абвгд\0f" - assert_equal rus_str_upcase, @handler.upcase(rus_str), "should upcase properly honoring null-byte" - assert_equal rus_str_downcase, @handler.downcase(rus_str), "should downcase properly honoring null-byte" - - jap_str = "の埋め込み化対応はほぼ完成" - assert_equal jap_str, @handler.upcase(jap_str), "Japanse has no upcase, should remain unchanged" - assert_equal jap_str, @handler.downcase(jap_str), "Japanse has no downcase, should remain unchanged" - - assert_raise(ActiveSupport::Multibyte::Handlers::EncodingError) { @handler.upcase(@bytestring) } - end - - def test_capitalize - { 'аБвг аБвг' => 'Абвг абвг', - 'аБвг АБВГ' => 'Абвг абвг', - 'АБВГ АБВГ' => 'Абвг абвг', - '' => '' }.each do |f,t| - assert_equal t, @handler.capitalize(f), "Capitalize should work as expected" - end - assert_raise(ActiveSupport::Multibyte::Handlers::EncodingError) { @handler.capitalize(@bytestring) } - end - - def test_translate_offset - str = "Блaå" # [2, 2, 1, 2] bytes - assert_equal 0, @handler.translate_offset('', 0), "Offset for an empty string makes no sense, return 0" - assert_equal 0, @handler.translate_offset(str, 0), "First character, first byte" - assert_equal 0, @handler.translate_offset(str, 1), "First character, second byte" - assert_equal 1, @handler.translate_offset(str, 2), "Second character, third byte" - assert_equal 1, @handler.translate_offset(str, 3), "Second character, fourth byte" - assert_equal 2, @handler.translate_offset(str, 4), "Third character, fifth byte" - assert_equal 3, @handler.translate_offset(str, 5), "Fourth character, sixth byte" - assert_equal 3, @handler.translate_offset(str, 6), "Fourth character, seventh byte" - assert_raise(ActiveSupport::Multibyte::Handlers::EncodingError) { @handler.translate_offset(@bytestring, 3) } - end - - def test_insert - assert_equal '', @handler.insert('', 0, ''), "Empty string should not break things" - assert_equal "Abcd Блå ffiБУМ бла бла бла бла", @handler.insert(@string, 10, "БУМ"), - "Text should be inserted at right codepoints" - assert_equal "Abcd Блå ffiБУМ бла бла бла бла", @string, "Insert should be destructive" - assert_raise(ActiveSupport::Multibyte::Handlers::EncodingError) do - @handler.insert(@bytestring, 2, "\210") - end - end - - def test_reverse - str = "wБлåa \n" - rev = "\n aåлБw" - assert_equal '', @handler.reverse(''), "Empty string shouldn't change" - assert_equal rev.ui, @handler.reverse(str).ui, "Should reverse properly" - assert_raise(ActiveSupport::Multibyte::Handlers::EncodingError) { @handler.reverse(@bytestring) } - end - - def test_size - assert_equal 0, @handler.size(''), "Empty string has size 0" - assert_equal 26, @handler.size(@string), "String length should be 26" - assert_equal 26, @handler.length(@string), "String length method should be properly aliased" - assert_raise(ActiveSupport::Multibyte::Handlers::EncodingError) { @handler.size(@bytestring) } - end - - def test_slice - assert_equal 0x41, @handler.slice(@string, 0), "Singular characters should return codepoints" - assert_equal 0xE5, @handler.slice(@string, 7), "Singular characters should return codepoints" - assert_equal nil, @handler.slice('', -1..1), "Broken range should return nil" - assert_equal '', @handler.slice('', 0..10), "Empty string should not break things" - assert_equal "d Блå ffi", @handler.slice(@string, 3..9), "Unicode characters have to be returned" - assert_equal "d Блå ffi", @handler.slice(@string, 3, 7), "Unicode characters have to be returned" - assert_equal "A", @handler.slice(@string, 0, 1), "Slicing from an offset should return characters" - assert_equal " Блå ffi ", @handler.slice(@string, 4..10), "Unicode characters have to be returned" - assert_equal "ffi бла", @handler.slice(@string, /ffi бла/u), "Slicing on Regexps should be supported" - assert_equal "ffi бла", @handler.slice(@string, /ffi \w\wа/u), "Slicing on Regexps should be supported" - assert_equal nil, @handler.slice(@string, /unknown/u), "Slicing on Regexps with no match should return nil" - assert_equal "ffi бла", @handler.slice(@string, /(ffi бла)/u,1), "Slicing on Regexps with a match group should be supported" - assert_equal nil, @handler.slice(@string, /(ffi)/u,2), "Slicing with a Regexp and asking for an invalid match group should return nil" - assert_equal "", @handler.slice(@string, 7..6), "Range is empty, should return an empty string" - assert_raise(ActiveSupport::Multibyte::Handlers::EncodingError) { @handler.slice(@bytestring, 2..3) } - assert_raise(TypeError, "With 2 args, should raise TypeError for non-Numeric or Regexp first argument") { @handler.slice(@string, 2..3, 1) } - assert_raise(TypeError, "With 2 args, should raise TypeError for non-Numeric or Regexp second argument") { @handler.slice(@string, 1, 2..3) } - assert_raise(ArgumentError, "Should raise ArgumentError when there are more than 2 args") { @handler.slice(@string, 1, 1, 1) } - end - - def test_grapheme_cluster_length - assert_equal 0, @handler.g_length(''), "String should count 0 grapheme clusters" - assert_equal 2, @handler.g_length([0x0924, 0x094D, 0x0930].pack('U*')), "String should count 2 grapheme clusters" - assert_equal 1, @handler.g_length(string_from_classes(%w(cr lf))), "Don't cut between CR and LF" - assert_equal 1, @handler.g_length(string_from_classes(%w(l l))), "Don't cut between L" - assert_equal 1, @handler.g_length(string_from_classes(%w(l v))), "Don't cut between L and V" - assert_equal 1, @handler.g_length(string_from_classes(%w(l lv))), "Don't cut between L and LV" - assert_equal 1, @handler.g_length(string_from_classes(%w(l lvt))), "Don't cut between L and LVT" - assert_equal 1, @handler.g_length(string_from_classes(%w(lv v))), "Don't cut between LV and V" - assert_equal 1, @handler.g_length(string_from_classes(%w(lv t))), "Don't cut between LV and T" - assert_equal 1, @handler.g_length(string_from_classes(%w(v v))), "Don't cut between V and V" - assert_equal 1, @handler.g_length(string_from_classes(%w(v t))), "Don't cut between V and T" - assert_equal 1, @handler.g_length(string_from_classes(%w(lvt t))), "Don't cut between LVT and T" - assert_equal 1, @handler.g_length(string_from_classes(%w(t t))), "Don't cut between T and T" - assert_equal 1, @handler.g_length(string_from_classes(%w(n extend))), "Don't cut before Extend" - assert_equal 2, @handler.g_length(string_from_classes(%w(n n))), "Cut between normal characters" - assert_equal 3, @handler.g_length(string_from_classes(%w(n cr lf n))), "Don't cut between CR and LF" - assert_equal 2, @handler.g_length(string_from_classes(%w(n l v t))), "Don't cut between L, V and T" - assert_raise(ActiveSupport::Multibyte::Handlers::EncodingError) { @handler.g_length(@bytestring) } - end - - def test_index - s = "Καλημέρα κόσμε!" - assert_equal 0, @handler.index('', ''), "The empty string is always found at the beginning of the string" - assert_equal 0, @handler.index('haystack', ''), "The empty string is always found at the beginning of the string" - assert_equal 0, @handler.index(s, 'Κ'), "Greek K is at 0" - assert_equal 1, @handler.index(s, 'α'), "Greek Alpha is at 1" - - assert_equal nil, @handler.index(@bytestring, 'a') - assert_raise(ActiveSupport::Multibyte::Handlers::EncodingError) { @handler.index(@bytestring, "\010") } - end - - def test_indexed_insert - s = "Καλη!" - @handler[s, 2] = "a" - assert_equal "Καaη!", s - @handler[s, 2] = "ηη" - assert_equal "Καηηη!", s - assert_raises(IndexError) { @handler[s, 10] = 'a' } - assert_equal "Καηηη!", s - @handler[s, 2] = 32 - assert_equal "Κα ηη!", s - @handler[s, 3, 2] = "λλλ" - assert_equal "Κα λλλ!", s - @handler[s, 1, 0] = "λ" - assert_equal "Κλα λλλ!", s - assert_raises(IndexError) { @handler[s, 10, 4] = 'a' } - assert_equal "Κλα λλλ!", s - @handler[s, 4..6] = "ηη" - assert_equal "Κλα ηη!", s - assert_raises(RangeError) { @handler[s, 10..12] = 'a' } - assert_equal "Κλα ηη!", s - @handler[s, /ηη/] = "λλλ" - assert_equal "Κλα λλλ!", s - assert_raises(IndexError) { @handler[s, /ii/] = 'a' } - assert_equal "Κλα λλλ!", s - @handler[s, /(λλ)(.)/, 2] = "α" - assert_equal "Κλα λλα!", s - assert_raises(IndexError) { @handler[s, /()/, 10] = 'a' } - assert_equal "Κλα λλα!", s - @handler[s, "α"] = "η" - assert_equal "Κλη λλα!", s - @handler[s, "λλ"] = "ααα" - assert_equal "Κλη αααα!", s - end - - def test_rjust - s = "Καη" - assert_raises(ArgumentError) { @handler.rjust(s, 10, '') } - assert_raises(ArgumentError) { @handler.rjust(s) } - assert_equal "Καη", @handler.rjust(s, -3) - assert_equal "Καη", @handler.rjust(s, 0) - assert_equal "Καη", @handler.rjust(s, 3) - assert_equal " Καη", @handler.rjust(s, 5) - assert_equal " Καη", @handler.rjust(s, 7) - assert_equal "----Καη", @handler.rjust(s, 7, '-') - assert_equal "ααααΚαη", @handler.rjust(s, 7, 'α') - assert_equal "abaΚαη", @handler.rjust(s, 6, 'ab') - assert_equal "αηαΚαη", @handler.rjust(s, 6, 'αη') - end - - def test_ljust - s = "Καη" - assert_raises(ArgumentError) { @handler.ljust(s, 10, '') } - assert_raises(ArgumentError) { @handler.ljust(s) } - assert_equal "Καη", @handler.ljust(s, -3) - assert_equal "Καη", @handler.ljust(s, 0) - assert_equal "Καη", @handler.ljust(s, 3) - assert_equal "Καη ", @handler.ljust(s, 5) - assert_equal "Καη ", @handler.ljust(s, 7) - assert_equal "Καη----", @handler.ljust(s, 7, '-') - assert_equal "Καηαααα", @handler.ljust(s, 7, 'α') - assert_equal "Καηaba", @handler.ljust(s, 6, 'ab') - assert_equal "Καηαηα", @handler.ljust(s, 6, 'αη') - end - - def test_center - s = "Καη" - assert_raises(ArgumentError) { @handler.center(s, 10, '') } - assert_raises(ArgumentError) { @handler.center(s) } - assert_equal "Καη", @handler.center(s, -3) - assert_equal "Καη", @handler.center(s, 0) - assert_equal "Καη", @handler.center(s, 3) - assert_equal "Καη ", @handler.center(s, 4) - assert_equal " Καη ", @handler.center(s, 5) - assert_equal " Καη ", @handler.center(s, 6) - assert_equal "--Καη--", @handler.center(s, 7, '-') - assert_equal "--Καη---", @handler.center(s, 8, '-') - assert_equal "ααΚαηαα", @handler.center(s, 7, 'α') - assert_equal "ααΚαηααα", @handler.center(s, 8, 'α') - assert_equal "aΚαηab", @handler.center(s, 6, 'ab') - assert_equal "abΚαηab", @handler.center(s, 7, 'ab') - assert_equal "ababΚαηabab", @handler.center(s, 11, 'ab') - assert_equal "αΚαηαη", @handler.center(s, 6, 'αη') - assert_equal "αηΚαηαη", @handler.center(s, 7, 'αη') - end - - def test_strip - # A unicode aware version of strip should strip all 26 types of whitespace. This includes the NO BREAK SPACE - # aka BOM (byte order mark). The byte order mark has no place in UTF-8 because it's used to detect LE and BE. - b = "\n" + [ - 32, # SPACE - 8195, # EM SPACE - 8199, # FIGURE SPACE, - 8201, # THIN SPACE - 8202, # HAIR SPACE - 65279, # NO BREAK SPACE (ZW) - ].pack('U*') - m = "word блин\n\n\n word" - e = [ - 65279, # NO BREAK SPACE (ZW) - 8201, # THIN SPACE - 8199, # FIGURE SPACE, - 32, # SPACE - ].pack('U*') - string = b+m+e - - assert_equal '', @handler.strip(''), "Empty string should stay empty" - assert_equal m+e, @handler.lstrip(string), "Whitespace should be gone on the left" - assert_equal b+m, @handler.rstrip(string), "Whitespace should be gone on the right" - assert_equal m, @handler.strip(string), "Whitespace should be stripped on both sides" - - bs = "\n #{@bytestring} \n\n" - assert_equal @bytestring, @handler.strip(bs), "Invalid unicode strings should still strip" - end - - def test_tidy_bytes - result = [0xb8, 0x17e, 0x8, 0x2c6, 0xa5].pack('U*') - assert_equal result, @handler.tidy_bytes(@bytestring) - assert_equal "a#{result}a", @handler.tidy_bytes('a' + @bytestring + 'a'), - 'tidy_bytes should leave surrounding characters intact' - assert_equal "é#{result}é", @handler.tidy_bytes('é' + @bytestring + 'é'), - 'tidy_bytes should leave surrounding characters intact' - assert_nothing_raised { @handler.tidy_bytes(@bytestring).unpack('U*') } - - assert_equal "\xC3\xA7", @handler.tidy_bytes("\xE7") # iso_8859_1: small c cedilla - assert_equal "\xC2\xA9", @handler.tidy_bytes("\xA9") # iso_8859_1: copyright symbol - assert_equal "\xE2\x80\x9C", @handler.tidy_bytes("\x93") # win_1252: left smart quote - assert_equal "\xE2\x82\xAC", @handler.tidy_bytes("\x80") # win_1252: euro - assert_equal "\x00", @handler.tidy_bytes("\x00") # null char - assert_equal [0xfffd].pack('U'), @handler.tidy_bytes("\xef\xbf\xbd") # invalid char - end - - protected - - def string_from_classes(classes) - classes.collect do |k| - @character_from_class[k.intern] - end.pack('U*') - end -end - - -begin - require_library_or_gem('utf8proc_native') - require 'active_record/multibyte/handlers/utf8_handler_proc' - class UTF8HandlingTestProc < Test::Unit::TestCase - include UTF8HandlingTest - def setup - common_setup - @handler = ::ActiveSupport::Multibyte::Handlers::UTF8HandlerProc - end - end -rescue LoadError -end - -class UTF8HandlingTestPure < Test::Unit::TestCase - include UTF8HandlingTest - def setup - common_setup - @handler = ::ActiveSupport::Multibyte::Handlers::UTF8Handler - end -end - -end diff --git a/activesupport/test/multibyte_test_helpers.rb b/activesupport/test/multibyte_test_helpers.rb new file mode 100644 index 0000000000..597f949059 --- /dev/null +++ b/activesupport/test/multibyte_test_helpers.rb @@ -0,0 +1,19 @@ +# encoding: utf-8 + +module MultibyteTestHelpers + UNICODE_STRING = 'こにちわ' + ASCII_STRING = 'ohayo' + BYTE_STRING = "\270\236\010\210\245" + + def chars(str) + ActiveSupport::Multibyte::Chars.new(str) + end + + def inspect_codepoints(str) + str.to_s.unpack("U*").map{|cp| cp.to_s(16) }.join(' ') + end + + def assert_equal_codepoints(expected, actual, message=nil) + assert_equal(inspect_codepoints(expected), inspect_codepoints(actual), message) + end +end
\ No newline at end of file diff --git a/activesupport/test/multibyte_unicode_database_test.rb b/activesupport/test/multibyte_unicode_database_test.rb new file mode 100644 index 0000000000..fb415e08d3 --- /dev/null +++ b/activesupport/test/multibyte_unicode_database_test.rb @@ -0,0 +1,28 @@ +# encoding: utf-8 + +require 'abstract_unit' + +uses_mocha "MultibyteUnicodeDatabaseTest" do + +class MultibyteUnicodeDatabaseTest < Test::Unit::TestCase + + def setup + @ucd = ActiveSupport::Multibyte::UnicodeDatabase.new + end + + ActiveSupport::Multibyte::UnicodeDatabase::ATTRIBUTES.each do |attribute| + define_method "test_lazy_loading_on_attribute_access_of_#{attribute}" do + @ucd.expects(:load) + @ucd.send(attribute) + end + end + + def test_load + @ucd.load + ActiveSupport::Multibyte::UnicodeDatabase::ATTRIBUTES.each do |attribute| + assert @ucd.send(attribute).length > 1 + end + end +end + +end
\ No newline at end of file |