PageRenderTime 26ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/Languages/Ruby/Tests/Libraries/Rails-3.0.0/activesupport/test/multibyte_chars_test.rb

http://github.com/IronLanguages/main
Ruby | 702 lines | 592 code | 98 blank | 12 comment | 3 complexity | 88d724b5414741723ebd0deb20cf727c MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. # encoding: utf-8
  2. require 'abstract_unit'
  3. require 'multibyte_test_helpers'
  4. class String
  5. def __method_for_multibyte_testing_with_integer_result; 1; end
  6. def __method_for_multibyte_testing; 'result'; end
  7. def __method_for_multibyte_testing!; 'result'; end
  8. end
  9. class MultibyteCharsTest < Test::Unit::TestCase
  10. include MultibyteTestHelpers
  11. def setup
  12. @proxy_class = ActiveSupport::Multibyte::Chars
  13. @chars = @proxy_class.new UNICODE_STRING
  14. end
  15. def test_wraps_the_original_string
  16. assert_equal UNICODE_STRING, @chars.to_s
  17. assert_equal UNICODE_STRING, @chars.wrapped_string
  18. end
  19. def test_should_allow_method_calls_to_string
  20. assert_nothing_raised do
  21. @chars.__method_for_multibyte_testing
  22. end
  23. assert_raise NoMethodError do
  24. @chars.__unknown_method
  25. end
  26. end
  27. def test_forwarded_method_calls_should_return_new_chars_instance
  28. assert_kind_of @proxy_class, @chars.__method_for_multibyte_testing
  29. assert_not_equal @chars.object_id, @chars.__method_for_multibyte_testing.object_id
  30. end
  31. def test_forwarded_bang_method_calls_should_return_the_original_chars_instance
  32. assert_kind_of @proxy_class, @chars.__method_for_multibyte_testing!
  33. assert_equal @chars.object_id, @chars.__method_for_multibyte_testing!.object_id
  34. end
  35. def test_methods_are_forwarded_to_wrapped_string_for_byte_strings
  36. assert_equal BYTE_STRING.class, BYTE_STRING.mb_chars.class
  37. end
  38. def test_forwarded_method_with_non_string_result_should_be_returned_vertabim
  39. assert_equal ''.__method_for_multibyte_testing_with_integer_result, @chars.__method_for_multibyte_testing_with_integer_result
  40. end
  41. def test_should_concatenate
  42. mb_a = 'a'.mb_chars
  43. mb_b = 'b'.mb_chars
  44. assert_equal 'ab', mb_a + 'b'
  45. assert_equal 'ab', 'a' + mb_b
  46. assert_equal 'ab', mb_a + mb_b
  47. assert_equal 'ab', mb_a << 'b'
  48. assert_equal 'ab', 'a' << mb_b
  49. assert_equal 'abb', mb_a << mb_b
  50. end
  51. def test_consumes_utf8_strings
  52. assert @proxy_class.consumes?(UNICODE_STRING)
  53. assert @proxy_class.consumes?(ASCII_STRING)
  54. assert !@proxy_class.consumes?(BYTE_STRING)
  55. end
  56. def test_unpack_utf8_strings
  57. assert_equal 4, ActiveSupport::Multibyte::Unicode.u_unpack(UNICODE_STRING).length
  58. assert_equal 5, ActiveSupport::Multibyte::Unicode.u_unpack(ASCII_STRING).length
  59. end
  60. def test_unpack_raises_encoding_error_on_broken_strings
  61. assert_raise(ActiveSupport::Multibyte::EncodingError) do
  62. ActiveSupport::Multibyte::Unicode.u_unpack(BYTE_STRING)
  63. end
  64. end
  65. def test_concatenation_should_return_a_proxy_class_instance
  66. assert_equal ActiveSupport::Multibyte.proxy_class, ('a'.mb_chars + 'b').class
  67. assert_equal ActiveSupport::Multibyte.proxy_class, ('a'.mb_chars << 'b').class
  68. end
  69. def test_ascii_strings_are_treated_at_utf8_strings
  70. assert_equal ActiveSupport::Multibyte.proxy_class, ASCII_STRING.mb_chars.class
  71. end
  72. def test_concatenate_should_return_proxy_instance
  73. assert(('a'.mb_chars + 'b').kind_of?(@proxy_class))
  74. assert(('a'.mb_chars + 'b'.mb_chars).kind_of?(@proxy_class))
  75. assert(('a'.mb_chars << 'b').kind_of?(@proxy_class))
  76. assert(('a'.mb_chars << 'b'.mb_chars).kind_of?(@proxy_class))
  77. end
  78. end
  79. class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase
  80. include MultibyteTestHelpers
  81. def setup
  82. @chars = UNICODE_STRING.dup.mb_chars
  83. if RUBY_VERSION < '1.9'
  84. # Multibyte support all kinds of whitespace (ie. NEWLINE, SPACE, EM SPACE)
  85. @whitespace = "\n\t#{[32, 8195].pack('U*')}"
  86. else
  87. # Ruby 1.9 only supports basic whitespace
  88. @whitespace = "\n\t "
  89. end
  90. @byte_order_mark = [65279].pack('U')
  91. end
  92. def test_split_should_return_an_array_of_chars_instances
  93. @chars.split(//).each do |character|
  94. assert_kind_of ActiveSupport::Multibyte.proxy_class, character
  95. end
  96. end
  97. def test_indexed_insert_accepts_fixnums
  98. @chars[2] = 32
  99. assert_equal 'こに わ', @chars
  100. end
  101. %w{capitalize downcase lstrip reverse rstrip strip upcase}.each do |method|
  102. class_eval(<<-EOTESTS)
  103. def test_#{method}_bang_should_return_self
  104. assert_equal @chars.object_id, @chars.send("#{method}!").object_id
  105. end
  106. def test_#{method}_bang_should_change_wrapped_string
  107. original = ' él piDió Un bUen café '
  108. proxy = chars(original.dup)
  109. proxy.send("#{method}!")
  110. assert_not_equal original, proxy.to_s
  111. end
  112. EOTESTS
  113. end
  114. def test_tidy_bytes_bang_should_return_self
  115. assert_equal @chars.object_id, @chars.tidy_bytes!.object_id
  116. end
  117. def test_tidy_bytes_bang_should_change_wrapped_string
  118. original = " Un bUen café \x92"
  119. proxy = chars(original.dup)
  120. proxy.tidy_bytes!
  121. assert_not_equal original, proxy.to_s
  122. end
  123. if RUBY_VERSION >= '1.9'
  124. def test_unicode_string_should_have_utf8_encoding
  125. assert_equal Encoding::UTF_8, UNICODE_STRING.encoding
  126. end
  127. end
  128. def test_identity
  129. assert_equal @chars, @chars
  130. assert @chars.eql?(@chars)
  131. assert !@chars.eql?(UNICODE_STRING)
  132. end
  133. def test_string_methods_are_chainable
  134. assert chars('').insert(0, '').kind_of?(ActiveSupport::Multibyte.proxy_class)
  135. assert chars('').rjust(1).kind_of?(ActiveSupport::Multibyte.proxy_class)
  136. assert chars('').ljust(1).kind_of?(ActiveSupport::Multibyte.proxy_class)
  137. assert chars('').center(1).kind_of?(ActiveSupport::Multibyte.proxy_class)
  138. assert chars('').rstrip.kind_of?(ActiveSupport::Multibyte.proxy_class)
  139. assert chars('').lstrip.kind_of?(ActiveSupport::Multibyte.proxy_class)
  140. assert chars('').strip.kind_of?(ActiveSupport::Multibyte.proxy_class)
  141. assert chars('').reverse.kind_of?(ActiveSupport::Multibyte.proxy_class)
  142. assert chars(' ').slice(0).kind_of?(ActiveSupport::Multibyte.proxy_class)
  143. assert chars('').limit(0).kind_of?(ActiveSupport::Multibyte.proxy_class)
  144. assert chars('').upcase.kind_of?(ActiveSupport::Multibyte.proxy_class)
  145. assert chars('').downcase.kind_of?(ActiveSupport::Multibyte.proxy_class)
  146. assert chars('').capitalize.kind_of?(ActiveSupport::Multibyte.proxy_class)
  147. assert chars('').normalize.kind_of?(ActiveSupport::Multibyte.proxy_class)
  148. assert chars('').decompose.kind_of?(ActiveSupport::Multibyte.proxy_class)
  149. assert chars('').compose.kind_of?(ActiveSupport::Multibyte.proxy_class)
  150. assert chars('').tidy_bytes.kind_of?(ActiveSupport::Multibyte.proxy_class)
  151. end
  152. def test_should_be_equal_to_the_wrapped_string
  153. assert_equal UNICODE_STRING, @chars
  154. assert_equal @chars, UNICODE_STRING
  155. end
  156. def test_should_not_be_equal_to_an_other_string
  157. assert_not_equal @chars, 'other'
  158. assert_not_equal 'other', @chars
  159. end
  160. def test_sortability
  161. words = %w(builder armor zebra).sort_by { |s| s.mb_chars }
  162. assert_equal %w(armor builder zebra), words
  163. end
  164. def test_should_return_character_offset_for_regexp_matches
  165. assert_nil(@chars =~ /wrong/u)
  166. assert_equal 0, (@chars =~ //u)
  167. assert_equal 0, (@chars =~ /こに/u)
  168. assert_equal 1, (@chars =~ //u)
  169. assert_equal 2, (@chars =~ //u)
  170. assert_equal 3, (@chars =~ //u)
  171. end
  172. def test_should_use_character_offsets_for_insert_offsets
  173. assert_equal '', ''.mb_chars.insert(0, '')
  174. assert_equal 'こわにちわ', @chars.insert(1, 'わ')
  175. assert_equal 'こわわわにちわ', @chars.insert(2, 'わわ')
  176. assert_equal 'わこわわわにちわ', @chars.insert(0, 'わ')
  177. assert_equal 'わこわわわにちわ', @chars.wrapped_string
  178. end
  179. def test_insert_should_be_destructive
  180. @chars.insert(1, 'わ')
  181. assert_equal 'こわにちわ', @chars
  182. end
  183. def test_insert_throws_index_error
  184. assert_raise(IndexError) { @chars.insert(-12, 'わ')}
  185. assert_raise(IndexError) { @chars.insert(12, 'わ') }
  186. end
  187. def test_should_know_if_one_includes_the_other
  188. assert @chars.include?('')
  189. assert @chars.include?('ち')
  190. assert @chars.include?('わ')
  191. assert !@chars.include?('こちわ')
  192. assert !@chars.include?('a')
  193. end
  194. def test_include_raises_when_nil_is_passed
  195. @chars.include?(nil)
  196. flunk "Expected chars.include?(nil) to raise TypeError or NoMethodError"
  197. rescue Exception
  198. end
  199. def test_index_should_return_character_offset
  200. assert_nil @chars.index('u')
  201. assert_equal 0, @chars.index('こに')
  202. assert_equal 2, @chars.index('ち')
  203. assert_equal 2, @chars.index('ち', -2)
  204. assert_equal nil, @chars.index('ち', -1)
  205. assert_equal 3, @chars.index('わ')
  206. assert_equal 5, 'ééxééx'.mb_chars.index('x', 4)
  207. end
  208. def test_rindex_should_return_character_offset
  209. assert_nil @chars.rindex('u')
  210. assert_equal 1, @chars.rindex('に')
  211. assert_equal 2, @chars.rindex('ち', -2)
  212. assert_nil @chars.rindex('ち', -3)
  213. assert_equal 6, 'Café périferôl'.mb_chars.rindex('é')
  214. assert_equal 13, 'Café périferôl'.mb_chars.rindex(/\w/u)
  215. end
  216. def test_indexed_insert_should_take_character_offsets
  217. @chars[2] = 'a'
  218. assert_equal 'こにaわ', @chars
  219. @chars[2] = 'ηη'
  220. assert_equal 'こにηηわ', @chars
  221. @chars[3, 2] = 'λλλ'
  222. assert_equal 'こにηλλλ', @chars
  223. @chars[1, 0] = "λ"
  224. assert_equal 'こλにηλλλ', @chars
  225. @chars[4..6] = "ηη"
  226. assert_equal 'こλにηηη', @chars
  227. @chars[/ηη/] = "λλλ"
  228. assert_equal 'こλにλλλη', @chars
  229. @chars[/(λλ)(.)/, 2] = "α"
  230. assert_equal 'こλにλλαη', @chars
  231. @chars["α"] = "¢"
  232. assert_equal 'こλにλλ¢η', @chars
  233. @chars["λλ"] = "ααα"
  234. assert_equal 'こλにααα¢η', @chars
  235. end
  236. def test_indexed_insert_should_raise_on_index_overflow
  237. before = @chars.to_s
  238. assert_raise(IndexError) { @chars[10] = 'a' }
  239. assert_raise(IndexError) { @chars[10, 4] = 'a' }
  240. assert_raise(IndexError) { @chars[/ii/] = 'a' }
  241. assert_raise(IndexError) { @chars[/()/, 10] = 'a' }
  242. assert_equal before, @chars
  243. end
  244. def test_indexed_insert_should_raise_on_range_overflow
  245. before = @chars.to_s
  246. assert_raise(RangeError) { @chars[10..12] = 'a' }
  247. assert_equal before, @chars
  248. end
  249. def test_rjust_should_raise_argument_errors_on_bad_arguments
  250. assert_raise(ArgumentError) { @chars.rjust(10, '') }
  251. assert_raise(ArgumentError) { @chars.rjust }
  252. end
  253. def test_rjust_should_count_characters_instead_of_bytes
  254. assert_equal UNICODE_STRING, @chars.rjust(-3)
  255. assert_equal UNICODE_STRING, @chars.rjust(0)
  256. assert_equal UNICODE_STRING, @chars.rjust(4)
  257. assert_equal " #{UNICODE_STRING}", @chars.rjust(5)
  258. assert_equal " #{UNICODE_STRING}", @chars.rjust(7)
  259. assert_equal "---#{UNICODE_STRING}", @chars.rjust(7, '-')
  260. assert_equal "ααα#{UNICODE_STRING}", @chars.rjust(7, 'α')
  261. assert_equal "aba#{UNICODE_STRING}", @chars.rjust(7, 'ab')
  262. assert_equal "αηα#{UNICODE_STRING}", @chars.rjust(7, 'αη')
  263. assert_equal "αηαη#{UNICODE_STRING}", @chars.rjust(8, 'αη')
  264. end
  265. def test_ljust_should_raise_argument_errors_on_bad_arguments
  266. assert_raise(ArgumentError) { @chars.ljust(10, '') }
  267. assert_raise(ArgumentError) { @chars.ljust }
  268. end
  269. def test_ljust_should_count_characters_instead_of_bytes
  270. assert_equal UNICODE_STRING, @chars.ljust(-3)
  271. assert_equal UNICODE_STRING, @chars.ljust(0)
  272. assert_equal UNICODE_STRING, @chars.ljust(4)
  273. assert_equal "#{UNICODE_STRING} ", @chars.ljust(5)
  274. assert_equal "#{UNICODE_STRING} ", @chars.ljust(7)
  275. assert_equal "#{UNICODE_STRING}---", @chars.ljust(7, '-')
  276. assert_equal "#{UNICODE_STRING}ααα", @chars.ljust(7, 'α')
  277. assert_equal "#{UNICODE_STRING}aba", @chars.ljust(7, 'ab')
  278. assert_equal "#{UNICODE_STRING}αηα", @chars.ljust(7, 'αη')
  279. assert_equal "#{UNICODE_STRING}αηαη", @chars.ljust(8, 'αη')
  280. end
  281. def test_center_should_raise_argument_errors_on_bad_arguments
  282. assert_raise(ArgumentError) { @chars.center(10, '') }
  283. assert_raise(ArgumentError) { @chars.center }
  284. end
  285. def test_center_should_count_characters_instead_of_bytes
  286. assert_equal UNICODE_STRING, @chars.center(-3)
  287. assert_equal UNICODE_STRING, @chars.center(0)
  288. assert_equal UNICODE_STRING, @chars.center(4)
  289. assert_equal "#{UNICODE_STRING} ", @chars.center(5)
  290. assert_equal " #{UNICODE_STRING} ", @chars.center(6)
  291. assert_equal " #{UNICODE_STRING} ", @chars.center(7)
  292. assert_equal "--#{UNICODE_STRING}--", @chars.center(8, '-')
  293. assert_equal "--#{UNICODE_STRING}---", @chars.center(9, '-')
  294. assert_equal "αα#{UNICODE_STRING}αα", @chars.center(8, 'α')
  295. assert_equal "αα#{UNICODE_STRING}ααα", @chars.center(9, 'α')
  296. assert_equal "a#{UNICODE_STRING}ab", @chars.center(7, 'ab')
  297. assert_equal "ab#{UNICODE_STRING}ab", @chars.center(8, 'ab')
  298. assert_equal "abab#{UNICODE_STRING}abab", @chars.center(12, 'ab')
  299. assert_equal "α#{UNICODE_STRING}αη", @chars.center(7, 'αη')
  300. assert_equal "αη#{UNICODE_STRING}αη", @chars.center(8, 'αη')
  301. end
  302. def test_lstrip_strips_whitespace_from_the_left_of_the_string
  303. assert_equal UNICODE_STRING, UNICODE_STRING.mb_chars.lstrip
  304. assert_equal UNICODE_STRING, (@whitespace + UNICODE_STRING).mb_chars.lstrip
  305. assert_equal UNICODE_STRING + @whitespace, (@whitespace + UNICODE_STRING + @whitespace).mb_chars.lstrip
  306. end
  307. def test_rstrip_strips_whitespace_from_the_right_of_the_string
  308. assert_equal UNICODE_STRING, UNICODE_STRING.mb_chars.rstrip
  309. assert_equal UNICODE_STRING, (UNICODE_STRING + @whitespace).mb_chars.rstrip
  310. assert_equal @whitespace + UNICODE_STRING, (@whitespace + UNICODE_STRING + @whitespace).mb_chars.rstrip
  311. end
  312. def test_strip_strips_whitespace
  313. assert_equal UNICODE_STRING, UNICODE_STRING.mb_chars.strip
  314. assert_equal UNICODE_STRING, (@whitespace + UNICODE_STRING).mb_chars.strip
  315. assert_equal UNICODE_STRING, (UNICODE_STRING + @whitespace).mb_chars.strip
  316. assert_equal UNICODE_STRING, (@whitespace + UNICODE_STRING + @whitespace).mb_chars.strip
  317. end
  318. def test_stripping_whitespace_leaves_whitespace_within_the_string_intact
  319. string_with_whitespace = UNICODE_STRING + @whitespace + UNICODE_STRING
  320. assert_equal string_with_whitespace, string_with_whitespace.mb_chars.strip
  321. assert_equal string_with_whitespace, string_with_whitespace.mb_chars.lstrip
  322. assert_equal string_with_whitespace, string_with_whitespace.mb_chars.rstrip
  323. end
  324. def test_size_returns_characters_instead_of_bytes
  325. assert_equal 0, ''.mb_chars.size
  326. assert_equal 4, @chars.size
  327. assert_equal 4, @chars.length
  328. assert_equal 5, ASCII_STRING.mb_chars.size
  329. end
  330. def test_reverse_reverses_characters
  331. assert_equal '', ''.mb_chars.reverse
  332. assert_equal 'わちにこ', @chars.reverse
  333. end
  334. def test_reverse_should_work_with_normalized_strings
  335. str = 'bös'
  336. reversed_str = 'söb'
  337. assert_equal chars(reversed_str).normalize(:kc), chars(str).normalize(:kc).reverse
  338. assert_equal chars(reversed_str).normalize(:c), chars(str).normalize(:c).reverse
  339. assert_equal chars(reversed_str).normalize(:d), chars(str).normalize(:d).reverse
  340. assert_equal chars(reversed_str).normalize(:kd), chars(str).normalize(:kd).reverse
  341. assert_equal chars(reversed_str).decompose, chars(str).decompose.reverse
  342. assert_equal chars(reversed_str).compose, chars(str).compose.reverse
  343. end
  344. def test_slice_should_take_character_offsets
  345. assert_equal nil, ''.mb_chars.slice(0)
  346. assert_equal 'こ', @chars.slice(0)
  347. assert_equal 'わ', @chars.slice(3)
  348. assert_equal nil, ''.mb_chars.slice(-1..1)
  349. assert_equal nil, ''.mb_chars.slice(-1, 1)
  350. assert_equal '', ''.mb_chars.slice(0..10)
  351. assert_equal 'にちわ', @chars.slice(1..3)
  352. assert_equal 'にちわ', @chars.slice(1, 3)
  353. assert_equal 'こ', @chars.slice(0, 1)
  354. assert_equal 'ちわ', @chars.slice(2..10)
  355. assert_equal '', @chars.slice(4..10)
  356. assert_equal 'に', @chars.slice(//u)
  357. assert_equal 'にち', @chars.slice(/./u)
  358. assert_equal nil, @chars.slice(/unknown/u)
  359. assert_equal 'にち', @chars.slice(/(にち)/u, 1)
  360. assert_equal nil, @chars.slice(/(にち)/u, 2)
  361. assert_equal nil, @chars.slice(7..6)
  362. end
  363. def test_slice_bang_returns_sliced_out_substring
  364. assert_equal 'にち', @chars.slice!(1..2)
  365. end
  366. def test_slice_bang_removes_the_slice_from_the_receiver
  367. chars = 'úüù'.mb_chars
  368. chars.slice!(0,2)
  369. assert_equal 'úü', chars
  370. end
  371. def test_slice_should_throw_exceptions_on_invalid_arguments
  372. assert_raise(TypeError) { @chars.slice(2..3, 1) }
  373. assert_raise(TypeError) { @chars.slice(1, 2..3) }
  374. assert_raise(ArgumentError) { @chars.slice(1, 1, 1) }
  375. end
  376. def test_ord_should_return_unicode_value_for_first_character
  377. assert_equal 12371, @chars.ord
  378. end
  379. def test_upcase_should_upcase_ascii_characters
  380. assert_equal '', ''.mb_chars.upcase
  381. assert_equal 'ABC', 'aBc'.mb_chars.upcase
  382. end
  383. def test_downcase_should_downcase_ascii_characters
  384. assert_equal '', ''.mb_chars.downcase
  385. assert_equal 'abc', 'aBc'.mb_chars.downcase
  386. end
  387. def test_capitalize_should_work_on_ascii_characters
  388. assert_equal '', ''.mb_chars.capitalize
  389. assert_equal 'Abc', 'abc'.mb_chars.capitalize
  390. end
  391. def test_titleize_should_work_on_ascii_characters
  392. assert_equal '', ''.mb_chars.titleize
  393. assert_equal 'Abc Abc', 'abc abc'.mb_chars.titleize
  394. end
  395. def test_respond_to_knows_which_methods_the_proxy_responds_to
  396. assert ''.mb_chars.respond_to?(:slice) # Defined on Chars
  397. assert ''.mb_chars.respond_to?(:capitalize!) # Defined on Chars
  398. assert ''.mb_chars.respond_to?(:gsub) # Defined on String
  399. assert !''.mb_chars.respond_to?(:undefined_method) # Not defined
  400. end
  401. def test_acts_like_string
  402. assert 'Bambi'.mb_chars.acts_like_string?
  403. end
  404. end
  405. # The default Multibyte Chars proxy has more features than the normal string implementation. Tests
  406. # for the implementation of these features should run on all Ruby versions and shouldn't be tested
  407. # through the proxy methods.
  408. class MultibyteCharsExtrasTest < Test::Unit::TestCase
  409. include MultibyteTestHelpers
  410. def test_upcase_should_be_unicode_aware
  411. assert_equal "АБВГД\0F", chars("аБвгд\0f").upcase
  412. assert_equal 'こにちわ', chars('こにちわ').upcase
  413. end
  414. def test_downcase_should_be_unicode_aware
  415. assert_equal "абвгд\0f", chars("аБвгд\0f").downcase
  416. assert_equal 'こにちわ', chars('こにちわ').downcase
  417. end
  418. def test_capitalize_should_be_unicode_aware
  419. { 'аБвг аБвг' => 'Абвг абвг',
  420. 'аБвг АБВГ' => 'Абвг абвг',
  421. 'АБВГ АБВГ' => 'Абвг абвг',
  422. '' => '' }.each do |f,t|
  423. assert_equal t, chars(f).capitalize
  424. end
  425. end
  426. def test_titleize_should_be_unicode_aware
  427. assert_equal "Él Que Se Enteró", chars("ÉL QUE SE ENTERÓ").titleize
  428. assert_equal "Абвг Абвг", chars("аБвг аБвг").titleize
  429. end
  430. def test_titleize_should_not_affect_characters_that_do_not_case_fold
  431. assert_equal "日本語", chars("日本語").titleize
  432. end
  433. def test_limit_should_not_break_on_blank_strings
  434. example = chars('')
  435. assert_equal example, example.limit(0)
  436. assert_equal example, example.limit(1)
  437. end
  438. def test_limit_should_work_on_a_multibyte_string
  439. example = chars(UNICODE_STRING)
  440. bytesize = UNICODE_STRING.respond_to?(:bytesize) ? UNICODE_STRING.bytesize : UNICODE_STRING.size
  441. assert_equal UNICODE_STRING, example.limit(bytesize)
  442. assert_equal '', example.limit(0)
  443. assert_equal '', example.limit(1)
  444. assert_equal 'こ', example.limit(3)
  445. assert_equal 'こに', example.limit(6)
  446. assert_equal 'こに', example.limit(8)
  447. assert_equal 'こにち', example.limit(9)
  448. assert_equal 'こにちわ', example.limit(50)
  449. end
  450. def test_limit_should_work_on_an_ascii_string
  451. ascii = chars(ASCII_STRING)
  452. assert_equal ASCII_STRING, ascii.limit(ASCII_STRING.length)
  453. assert_equal '', ascii.limit(0)
  454. assert_equal 'o', ascii.limit(1)
  455. assert_equal 'oh', ascii.limit(2)
  456. assert_equal 'ohay', ascii.limit(4)
  457. assert_equal 'ohayo', ascii.limit(50)
  458. end
  459. def test_limit_should_keep_under_the_specified_byte_limit
  460. example = chars(UNICODE_STRING)
  461. (1..UNICODE_STRING.length).each do |limit|
  462. assert example.limit(limit).to_s.length <= limit
  463. end
  464. end
  465. def test_composition_exclusion_is_set_up_properly
  466. # Normalization of DEVANAGARI LETTER QA breaks when composition exclusion isn't used correctly
  467. qa = [0x915, 0x93c].pack('U*')
  468. assert_equal qa, chars(qa).normalize(:c)
  469. end
  470. # Test for the Public Review Issue #29, bad explanation of composition might lead to a
  471. # bad implementation: http://www.unicode.org/review/pr-29.html
  472. def test_normalization_C_pri_29
  473. [
  474. [0x0B47, 0x0300, 0x0B3E],
  475. [0x1100, 0x0300, 0x1161]
  476. ].map { |c| c.pack('U*') }.each do |c|
  477. assert_equal_codepoints c, chars(c).normalize(:c)
  478. end
  479. end
  480. def test_normalization_shouldnt_strip_null_bytes
  481. null_byte_str = "Test\0test"
  482. assert_equal null_byte_str, chars(null_byte_str).normalize(:kc)
  483. assert_equal null_byte_str, chars(null_byte_str).normalize(:c)
  484. assert_equal null_byte_str, chars(null_byte_str).normalize(:d)
  485. assert_equal null_byte_str, chars(null_byte_str).normalize(:kd)
  486. assert_equal null_byte_str, chars(null_byte_str).decompose
  487. assert_equal null_byte_str, chars(null_byte_str).compose
  488. end
  489. def test_simple_normalization
  490. comp_str = [
  491. 44, # LATIN CAPITAL LETTER D
  492. 307, # COMBINING DOT ABOVE
  493. 328, # COMBINING OGONEK
  494. 323 # COMBINING DOT BELOW
  495. ].pack("U*")
  496. assert_equal_codepoints '', chars('').normalize
  497. assert_equal_codepoints [44,105,106,328,323].pack("U*"), chars(comp_str).normalize(:kc).to_s
  498. assert_equal_codepoints [44,307,328,323].pack("U*"), chars(comp_str).normalize(:c).to_s
  499. assert_equal_codepoints [44,307,110,780,78,769].pack("U*"), chars(comp_str).normalize(:d).to_s
  500. assert_equal_codepoints [44,105,106,110,780,78,769].pack("U*"), chars(comp_str).normalize(:kd).to_s
  501. end
  502. def test_should_compute_grapheme_length
  503. [
  504. ['', 0],
  505. ['abc', 3],
  506. ['こにちわ', 4],
  507. [[0x0924, 0x094D, 0x0930].pack('U*'), 2],
  508. [%w(cr lf), 1],
  509. [%w(l l), 1],
  510. [%w(l v), 1],
  511. [%w(l lv), 1],
  512. [%w(l lvt), 1],
  513. [%w(lv v), 1],
  514. [%w(lv t), 1],
  515. [%w(v v), 1],
  516. [%w(v t), 1],
  517. [%w(lvt t), 1],
  518. [%w(t t), 1],
  519. [%w(n extend), 1],
  520. [%w(n n), 2],
  521. [%w(n cr lf n), 3],
  522. [%w(n l v t), 2]
  523. ].each do |input, expected_length|
  524. if input.kind_of?(Array)
  525. str = string_from_classes(input)
  526. else
  527. str = input
  528. end
  529. assert_equal expected_length, chars(str).g_length
  530. end
  531. end
  532. def test_tidy_bytes_should_tidy_bytes
  533. single_byte_cases = {
  534. "\x21" => "!", # Valid ASCII byte, low
  535. "\x41" => "A", # Valid ASCII byte, mid
  536. "\x7E" => "~", # Valid ASCII byte, high
  537. "\x80" => "€", # Continuation byte, low (cp125)
  538. "\x94" => "”", # Continuation byte, mid (cp125)
  539. "\x9F" => "Ÿ", # Continuation byte, high (cp125)
  540. "\xC0" => "À", # Overlong encoding, start of 2-byte sequence, but codepoint < 128
  541. "\xC1" => "Á", # Overlong encoding, start of 2-byte sequence, but codepoint < 128
  542. "\xC2" => "Â", # Start of 2-byte sequence, low
  543. "\xC8" => "È", # Start of 2-byte sequence, mid
  544. "\xDF" => "ß", # Start of 2-byte sequence, high
  545. "\xE0" => "à", # Start of 3-byte sequence, low
  546. "\xE8" => "è", # Start of 3-byte sequence, mid
  547. "\xEF" => "ï", # Start of 3-byte sequence, high
  548. "\xF0" => "ð", # Start of 4-byte sequence
  549. "\xF1" => "ñ", # Unused byte
  550. "\xFF" => "ÿ", # Restricted byte
  551. "\x00" => "\x00" # null char
  552. }
  553. single_byte_cases.each do |bad, good|
  554. assert_equal good, chars(bad).tidy_bytes.to_s
  555. assert_equal "#{good}#{good}", chars("#{bad}#{bad}").tidy_bytes
  556. assert_equal "#{good}#{good}#{good}", chars("#{bad}#{bad}#{bad}").tidy_bytes
  557. assert_equal "#{good}a", chars("#{bad}a").tidy_bytes
  558. assert_equal "#{good}á", chars("#{bad}á").tidy_bytes
  559. assert_equal "a#{good}a", chars("a#{bad}a").tidy_bytes
  560. assert_equal "á#{good}á", chars("á#{bad}á").tidy_bytes
  561. assert_equal "a#{good}", chars("a#{bad}").tidy_bytes
  562. assert_equal "á#{good}", chars("á#{bad}").tidy_bytes
  563. end
  564. byte_string = "\270\236\010\210\245"
  565. tidy_string = [0xb8, 0x17e, 0x8, 0x2c6, 0xa5].pack('U*')
  566. assert_equal_codepoints tidy_string, chars(byte_string).tidy_bytes
  567. assert_nothing_raised { chars(byte_string).tidy_bytes.to_s.unpack('U*') }
  568. # UTF-8 leading byte followed by too few continuation bytes
  569. assert_equal_codepoints "\xc3\xb0\xc2\xa5\xc2\xa4\x21", chars("\xf0\xa5\xa4\x21").tidy_bytes
  570. end
  571. def test_tidy_bytes_should_forcibly_tidy_bytes_if_specified
  572. byte_string = "\xF0\xA5\xA4\xA4" # valid as both CP-1252 and UTF-8, but with different interpretations.
  573. assert_not_equal "𥤤", chars(byte_string).tidy_bytes
  574. # Forcible conversion to UTF-8
  575. assert_equal "𥤤", chars(byte_string).tidy_bytes(true)
  576. end
  577. private
  578. def string_from_classes(classes)
  579. # Characters from the character classes as described in UAX #29
  580. character_from_class = {
  581. :l => 0x1100, :v => 0x1160, :t => 0x11A8, :lv => 0xAC00, :lvt => 0xAC01, :cr => 0x000D, :lf => 0x000A,
  582. :extend => 0x094D, :n => 0x64
  583. }
  584. classes.collect do |k|
  585. character_from_class[k.intern]
  586. end.pack('U*')
  587. end
  588. end
  589. class MultibyteInternalsTest < ActiveSupport::TestCase
  590. include MultibyteTestHelpers
  591. test "Chars translates a character offset to a byte offset" do
  592. example = chars("Puisque c'était son erreur, il m'a aidé")
  593. [
  594. [0, 0],
  595. [3, 3],
  596. [12, 11],
  597. [14, 13],
  598. [41, 39]
  599. ].each do |byte_offset, character_offset|
  600. assert_equal character_offset, example.send(:translate_offset, byte_offset),
  601. "Expected byte offset #{byte_offset} to translate to #{character_offset}"
  602. end
  603. end
  604. end