1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 *
6 * Copyright (C) 2002-2016, International Business Machines
7 * Corporation and others. All Rights Reserved.
8 *
9 *******************************************************************************
10 * file name: strcase.cpp
11 * encoding: UTF-8
12 * tab size: 8 (not used)
13 * indentation:4
14 *
15 * created on: 2002mar12
16 * created by: Markus W. Scherer
17 *
18 * Test file for string casing C++ API functions.
19 */
20
21 #include "unicode/std_string.h"
22 #include "unicode/brkiter.h"
23 #include "unicode/casemap.h"
24 #include "unicode/edits.h"
25 #include "unicode/uchar.h"
26 #include "unicode/ures.h"
27 #include "unicode/uloc.h"
28 #include "unicode/locid.h"
29 #include "unicode/ubrk.h"
30 #include "unicode/unistr.h"
31 #include "unicode/ucasemap.h"
32 #include "unicode/ustring.h"
33 #include "ucase.h"
34 #include "ustrtest.h"
35 #include "unicode/tstdtmod.h"
36 #include "cmemory.h"
37 #include "testutil.h"
38
39 class StringCaseTest: public IntlTest {
40 public:
41 StringCaseTest();
42 virtual ~StringCaseTest();
43
44 void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=0);
45
46 void TestCaseConversion();
47
48 void TestCasingImpl(const UnicodeString &input,
49 const UnicodeString &output,
50 int32_t whichCase,
51 void *iter, const char *localeID, uint32_t options);
52 void TestCasing();
53 void TestTitleOptions();
54 void TestFullCaseFoldingIterator();
55 void TestGreekUpper();
56 void TestLongUpper();
57 void TestMalformedUTF8();
58 void TestBufferOverflow();
59 void TestEdits();
60 void TestCopyMoveEdits();
61 void TestEditsFindFwdBwd();
62 void TestMergeEdits();
63 void TestCaseMapWithEdits();
64 void TestCaseMapUTF8WithEdits();
65 void TestCaseMapToString();
66 void TestCaseMapUTF8ToString();
67 void TestLongUnicodeString();
68 void TestBug13127();
69 void TestInPlaceTitle();
70 void TestCaseMapEditsIteratorDocs();
71 void TestCaseMapGreekExtended();
72
73 private:
74 void assertGreekUpper(const char16_t *s, const char16_t *expected);
75
76 Locale GREEK_LOCALE_;
77 };
78
StringCaseTest()79 StringCaseTest::StringCaseTest() : GREEK_LOCALE_("el") {}
80
~StringCaseTest()81 StringCaseTest::~StringCaseTest() {}
82
createStringCaseTest()83 extern IntlTest *createStringCaseTest() {
84 return new StringCaseTest();
85 }
86
87 void
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)88 StringCaseTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char * /*par*/) {
89 if(exec) {
90 logln("TestSuite StringCaseTest: ");
91 }
92 TESTCASE_AUTO_BEGIN;
93 TESTCASE_AUTO(TestCaseConversion);
94 #if !UCONFIG_NO_BREAK_ITERATION && !UCONFIG_NO_FILE_IO && !UCONFIG_NO_LEGACY_CONVERSION
95 TESTCASE_AUTO(TestCasing);
96 TESTCASE_AUTO(TestTitleOptions);
97 #endif
98 TESTCASE_AUTO(TestFullCaseFoldingIterator);
99 TESTCASE_AUTO(TestGreekUpper);
100 TESTCASE_AUTO(TestLongUpper);
101 TESTCASE_AUTO(TestMalformedUTF8);
102 TESTCASE_AUTO(TestBufferOverflow);
103 TESTCASE_AUTO(TestEdits);
104 TESTCASE_AUTO(TestCopyMoveEdits);
105 TESTCASE_AUTO(TestEditsFindFwdBwd);
106 TESTCASE_AUTO(TestMergeEdits);
107 TESTCASE_AUTO(TestCaseMapWithEdits);
108 TESTCASE_AUTO(TestCaseMapUTF8WithEdits);
109 TESTCASE_AUTO(TestCaseMapToString);
110 TESTCASE_AUTO(TestCaseMapUTF8ToString);
111 TESTCASE_AUTO(TestLongUnicodeString);
112 #if !UCONFIG_NO_BREAK_ITERATION
113 TESTCASE_AUTO(TestBug13127);
114 TESTCASE_AUTO(TestInPlaceTitle);
115 #endif
116 TESTCASE_AUTO(TestCaseMapEditsIteratorDocs);
117 TESTCASE_AUTO(TestCaseMapGreekExtended);
118 TESTCASE_AUTO_END;
119 }
120
121 void
TestCaseConversion()122 StringCaseTest::TestCaseConversion()
123 {
124 static const UChar uppercaseGreek[] =
125 { 0x399, 0x395, 0x3a3, 0x3a5, 0x3a3, 0x20, 0x03a7, 0x3a1, 0x399, 0x3a3, 0x3a4,
126 0x39f, 0x3a3, 0 };
127 // "IESUS CHRISTOS"
128
129 static const UChar lowercaseGreek[] =
130 { 0x3b9, 0x3b5, 0x3c3, 0x3c5, 0x3c2, 0x20, 0x03c7, 0x3c1, 0x3b9, 0x3c3, 0x3c4,
131 0x3bf, 0x3c2, 0 };
132 // "iesus christos"
133
134 static const UChar lowercaseTurkish[] =
135 { 0x69, 0x73, 0x74, 0x61, 0x6e, 0x62, 0x75, 0x6c, 0x2c, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x63, 0x6f,
136 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x0131, 0x6e, 0x6f, 0x70, 0x6c, 0x65, 0x21, 0 };
137
138 static const UChar uppercaseTurkish[] =
139 { 0x54, 0x4f, 0x50, 0x4b, 0x41, 0x50, 0x49, 0x20, 0x50, 0x41, 0x4c, 0x41, 0x43, 0x45, 0x2c, 0x20,
140 0x0130, 0x53, 0x54, 0x41, 0x4e, 0x42, 0x55, 0x4c, 0 };
141
142 UnicodeString expectedResult;
143 UnicodeString test3;
144
145 test3 += (UChar32)0x0130;
146 test3 += "STANBUL, NOT CONSTANTINOPLE!";
147
148 UnicodeString test4(test3);
149 test4.toLower(Locale(""));
150 expectedResult = UnicodeString("i\\u0307stanbul, not constantinople!", "").unescape();
151 if (test4 != expectedResult)
152 errln("1. toLower failed: expected \"" + expectedResult + "\", got \"" + test4 + "\".");
153
154 test4 = test3;
155 test4.toLower(Locale("tr", "TR"));
156 expectedResult = lowercaseTurkish;
157 if (test4 != expectedResult)
158 errln("2. toLower failed: expected \"" + expectedResult + "\", got \"" + test4 + "\".");
159
160 test3 = "topkap";
161 test3 += (UChar32)0x0131;
162 test3 += " palace, istanbul";
163 test4 = test3;
164
165 test4.toUpper(Locale(""));
166 expectedResult = "TOPKAPI PALACE, ISTANBUL";
167 if (test4 != expectedResult)
168 errln("toUpper failed: expected \"" + expectedResult + "\", got \"" + test4 + "\".");
169
170 test4 = test3;
171 test4.toUpper(Locale("tr", "TR"));
172 expectedResult = uppercaseTurkish;
173 if (test4 != expectedResult)
174 errln("toUpper failed: expected \"" + expectedResult + "\", got \"" + test4 + "\".");
175
176 test3 = CharsToUnicodeString("S\\u00FC\\u00DFmayrstra\\u00DFe");
177
178 test3.toUpper(Locale("de", "DE"));
179 expectedResult = CharsToUnicodeString("S\\u00DCSSMAYRSTRASSE");
180 if (test3 != expectedResult)
181 errln("toUpper failed: expected \"" + expectedResult + "\", got \"" + test3 + "\".");
182
183 test4.replace(0, test4.length(), uppercaseGreek);
184
185 test4.toLower(Locale("el", "GR"));
186 expectedResult = lowercaseGreek;
187 if (test4 != expectedResult)
188 errln("toLower failed: expected \"" + expectedResult + "\", got \"" + test4 + "\".");
189
190 test4.replace(0, test4.length(), lowercaseGreek);
191
192 test4.toUpper();
193 expectedResult = uppercaseGreek;
194 if (test4 != expectedResult)
195 errln("toUpper failed: expected \"" + expectedResult + "\", got \"" + test4 + "\".");
196
197 // more string case mapping tests with the new implementation
198 {
199 static const UChar
200
201 beforeLower[]= { 0x61, 0x42, 0x49, 0x3a3, 0xdf, 0x3a3, 0x2f, 0xd93f, 0xdfff },
202 lowerRoot[]= { 0x61, 0x62, 0x69, 0x3c3, 0xdf, 0x3c2, 0x2f, 0xd93f, 0xdfff },
203 lowerTurkish[]={ 0x61, 0x62, 0x131, 0x3c3, 0xdf, 0x3c2, 0x2f, 0xd93f, 0xdfff },
204
205 beforeUpper[]= { 0x61, 0x42, 0x69, 0x3c2, 0xdf, 0x3c3, 0x2f, 0xfb03, 0xfb03, 0xfb03, 0xd93f, 0xdfff },
206 upperRoot[]= { 0x41, 0x42, 0x49, 0x3a3, 0x53, 0x53, 0x3a3, 0x2f, 0x46, 0x46, 0x49, 0x46, 0x46, 0x49, 0x46, 0x46, 0x49, 0xd93f, 0xdfff },
207 upperTurkish[]={ 0x41, 0x42, 0x130, 0x3a3, 0x53, 0x53, 0x3a3, 0x2f, 0x46, 0x46, 0x49, 0x46, 0x46, 0x49, 0x46, 0x46, 0x49, 0xd93f, 0xdfff },
208
209 beforeMiniUpper[]= { 0xdf, 0x61 },
210 miniUpper[]= { 0x53, 0x53, 0x41 };
211
212 UnicodeString s;
213
214 /* lowercase with root locale */
215 s=UnicodeString(FALSE, beforeLower, UPRV_LENGTHOF(beforeLower));
216 s.toLower("");
217 if( s.length()!=UPRV_LENGTHOF(lowerRoot) ||
218 s!=UnicodeString(FALSE, lowerRoot, s.length())
219 ) {
220 errln("error in toLower(root locale)=\"" + s + "\" expected \"" + UnicodeString(FALSE, lowerRoot, UPRV_LENGTHOF(lowerRoot)) + "\"");
221 }
222
223 /* lowercase with turkish locale */
224 s=UnicodeString(FALSE, beforeLower, UPRV_LENGTHOF(beforeLower));
225 s.setCharAt(0, beforeLower[0]).toLower(Locale("tr"));
226 if( s.length()!=UPRV_LENGTHOF(lowerTurkish) ||
227 s!=UnicodeString(FALSE, lowerTurkish, s.length())
228 ) {
229 errln("error in toLower(turkish locale)=\"" + s + "\" expected \"" + UnicodeString(FALSE, lowerTurkish, UPRV_LENGTHOF(lowerTurkish)) + "\"");
230 }
231
232 /* uppercase with root locale */
233 s=UnicodeString(FALSE, beforeUpper, UPRV_LENGTHOF(beforeUpper));
234 s.setCharAt(0, beforeUpper[0]).toUpper(Locale(""));
235 if( s.length()!=UPRV_LENGTHOF(upperRoot) ||
236 s!=UnicodeString(FALSE, upperRoot, s.length())
237 ) {
238 errln("error in toUpper(root locale)=\"" + s + "\" expected \"" + UnicodeString(FALSE, upperRoot, UPRV_LENGTHOF(upperRoot)) + "\"");
239 }
240
241 /* uppercase with turkish locale */
242 s=UnicodeString(FALSE, beforeUpper, UPRV_LENGTHOF(beforeUpper));
243 s.toUpper(Locale("tr"));
244 if( s.length()!=UPRV_LENGTHOF(upperTurkish) ||
245 s!=UnicodeString(FALSE, upperTurkish, s.length())
246 ) {
247 errln("error in toUpper(turkish locale)=\"" + s + "\" expected \"" + UnicodeString(FALSE, upperTurkish, UPRV_LENGTHOF(upperTurkish)) + "\"");
248 }
249
250 /* uppercase a short string with root locale */
251 s=UnicodeString(FALSE, beforeMiniUpper, UPRV_LENGTHOF(beforeMiniUpper));
252 s.setCharAt(0, beforeMiniUpper[0]).toUpper("");
253 if( s.length()!=UPRV_LENGTHOF(miniUpper) ||
254 s!=UnicodeString(FALSE, miniUpper, s.length())
255 ) {
256 errln("error in toUpper(root locale)=\"" + s + "\" expected \"" + UnicodeString(FALSE, miniUpper, UPRV_LENGTHOF(miniUpper)) + "\"");
257 }
258 }
259
260 // test some supplementary characters (>= Unicode 3.1)
261 {
262 UnicodeString t;
263
264 UnicodeString
265 deseretInput=UnicodeString("\\U0001043C\\U00010414", "").unescape(),
266 deseretLower=UnicodeString("\\U0001043C\\U0001043C", "").unescape(),
267 deseretUpper=UnicodeString("\\U00010414\\U00010414", "").unescape();
268 (t=deseretInput).toLower();
269 if(t!=deseretLower) {
270 errln("error lowercasing Deseret (plane 1) characters");
271 }
272 (t=deseretInput).toUpper();
273 if(t!=deseretUpper) {
274 errln("error uppercasing Deseret (plane 1) characters");
275 }
276 }
277
278 // test some more cases that looked like problems
279 {
280 UnicodeString t;
281
282 UnicodeString
283 ljInput=UnicodeString("ab'cD \\uFB00i\\u0131I\\u0130 \\u01C7\\u01C8\\u01C9 \\U0001043C\\U00010414", "").unescape(),
284 ljLower=UnicodeString("ab'cd \\uFB00i\\u0131ii\\u0307 \\u01C9\\u01C9\\u01C9 \\U0001043C\\U0001043C", "").unescape(),
285 ljUpper=UnicodeString("AB'CD FFIII\\u0130 \\u01C7\\u01C7\\u01C7 \\U00010414\\U00010414", "").unescape();
286 (t=ljInput).toLower("en");
287 if(t!=ljLower) {
288 errln("error lowercasing LJ characters");
289 }
290 (t=ljInput).toUpper("en");
291 if(t!=ljUpper) {
292 errln("error uppercasing LJ characters");
293 }
294 }
295
296 #if !UCONFIG_NO_NORMALIZATION
297 // some context-sensitive casing depends on normalization data being present
298
299 // Unicode 3.1.1 SpecialCasing tests
300 {
301 UnicodeString t;
302
303 // sigmas preceded and/or followed by cased letters
304 UnicodeString
305 sigmas=UnicodeString("i\\u0307\\u03a3\\u0308j \\u0307\\u03a3\\u0308j i\\u00ad\\u03a3\\u0308 \\u0307\\u03a3\\u0308 ", "").unescape(),
306 sigmasLower=UnicodeString("i\\u0307\\u03c3\\u0308j \\u0307\\u03c3\\u0308j i\\u00ad\\u03c2\\u0308 \\u0307\\u03c3\\u0308 ", "").unescape(),
307 sigmasUpper=UnicodeString("I\\u0307\\u03a3\\u0308J \\u0307\\u03a3\\u0308J I\\u00ad\\u03a3\\u0308 \\u0307\\u03a3\\u0308 ", "").unescape();
308
309 (t=sigmas).toLower();
310 if(t!=sigmasLower) {
311 errln("error in sigmas.toLower()=\"" + t + "\" expected \"" + sigmasLower + "\"");
312 }
313
314 (t=sigmas).toUpper(Locale(""));
315 if(t!=sigmasUpper) {
316 errln("error in sigmas.toUpper()=\"" + t + "\" expected \"" + sigmasUpper + "\"");
317 }
318
319 // turkish & azerbaijani dotless i & dotted I
320 // remove dot above if there was a capital I before and there are no more accents above
321 UnicodeString
322 dots=UnicodeString("I \\u0130 I\\u0307 I\\u0327\\u0307 I\\u0301\\u0307 I\\u0327\\u0307\\u0301", "").unescape(),
323 dotsTurkish=UnicodeString("\\u0131 i i i\\u0327 \\u0131\\u0301\\u0307 i\\u0327\\u0301", "").unescape(),
324 dotsDefault=UnicodeString("i i\\u0307 i\\u0307 i\\u0327\\u0307 i\\u0301\\u0307 i\\u0327\\u0307\\u0301", "").unescape();
325
326 (t=dots).toLower("tr");
327 if(t!=dotsTurkish) {
328 errln("error in dots.toLower(tr)=\"" + t + "\" expected \"" + dotsTurkish + "\"");
329 }
330
331 (t=dots).toLower("de");
332 if(t!=dotsDefault) {
333 errln("error in dots.toLower(de)=\"" + t + "\" expected \"" + dotsDefault + "\"");
334 }
335 }
336
337 // more Unicode 3.1.1 tests
338 {
339 UnicodeString t;
340
341 // lithuanian dot above in uppercasing
342 UnicodeString
343 dots=UnicodeString("a\\u0307 \\u0307 i\\u0307 j\\u0327\\u0307 j\\u0301\\u0307", "").unescape(),
344 dotsLithuanian=UnicodeString("A\\u0307 \\u0307 I J\\u0327 J\\u0301\\u0307", "").unescape(),
345 dotsDefault=UnicodeString("A\\u0307 \\u0307 I\\u0307 J\\u0327\\u0307 J\\u0301\\u0307", "").unescape();
346
347 (t=dots).toUpper("lt");
348 if(t!=dotsLithuanian) {
349 errln("error in dots.toUpper(lt)=\"" + t + "\" expected \"" + dotsLithuanian + "\"");
350 }
351
352 (t=dots).toUpper("de");
353 if(t!=dotsDefault) {
354 errln("error in dots.toUpper(de)=\"" + t + "\" expected \"" + dotsDefault + "\"");
355 }
356
357 // lithuanian adds dot above to i in lowercasing if there are more above accents
358 UnicodeString
359 i=UnicodeString("I I\\u0301 J J\\u0301 \\u012e \\u012e\\u0301 \\u00cc\\u00cd\\u0128", "").unescape(),
360 iLithuanian=UnicodeString("i i\\u0307\\u0301 j j\\u0307\\u0301 \\u012f \\u012f\\u0307\\u0301 i\\u0307\\u0300i\\u0307\\u0301i\\u0307\\u0303", "").unescape(),
361 iDefault=UnicodeString("i i\\u0301 j j\\u0301 \\u012f \\u012f\\u0301 \\u00ec\\u00ed\\u0129", "").unescape();
362
363 (t=i).toLower("lt");
364 if(t!=iLithuanian) {
365 errln("error in i.toLower(lt)=\"" + t + "\" expected \"" + iLithuanian + "\"");
366 }
367
368 (t=i).toLower("de");
369 if(t!=iDefault) {
370 errln("error in i.toLower(de)=\"" + t + "\" expected \"" + iDefault + "\"");
371 }
372 }
373
374 #endif
375
376 // test case folding
377 {
378 UnicodeString
379 s=UnicodeString("A\\u00df\\u00b5\\ufb03\\U0001040c\\u0130\\u0131", "").unescape(),
380 f=UnicodeString("ass\\u03bcffi\\U00010434i\\u0307\\u0131", "").unescape(),
381 g=UnicodeString("ass\\u03bcffi\\U00010434i\\u0131", "").unescape(),
382 t;
383
384 (t=s).foldCase();
385 if(f!=t) {
386 errln("error in foldCase(\"" + s + "\", default)=\"" + t + "\" but expected \"" + f + "\"");
387 }
388
389 // alternate handling for dotted I/dotless i (U+0130, U+0131)
390 (t=s).foldCase(U_FOLD_CASE_EXCLUDE_SPECIAL_I);
391 if(g!=t) {
392 errln("error in foldCase(\"" + s + "\", U_FOLD_CASE_EXCLUDE_SPECIAL_I)=\"" + t + "\" but expected \"" + g + "\"");
393 }
394 }
395 }
396
397 // data-driven case mapping tests ------------------------------------------ ***
398
399 enum {
400 TEST_LOWER,
401 TEST_UPPER,
402 TEST_TITLE,
403 TEST_FOLD,
404 TEST_COUNT
405 };
406
407 // names of TestData children in casing.txt
408 static const char *const dataNames[TEST_COUNT+1]={
409 "lowercasing",
410 "uppercasing",
411 "titlecasing",
412 "casefolding",
413 ""
414 };
415
416 void
TestCasingImpl(const UnicodeString & input,const UnicodeString & output,int32_t whichCase,void * iter,const char * localeID,uint32_t options)417 StringCaseTest::TestCasingImpl(const UnicodeString &input,
418 const UnicodeString &output,
419 int32_t whichCase,
420 void *iter, const char *localeID, uint32_t options) {
421 // UnicodeString
422 UnicodeString result;
423 const char *name;
424 Locale locale(localeID);
425
426 result=input;
427 switch(whichCase) {
428 case TEST_LOWER:
429 name="toLower";
430 result.toLower(locale);
431 break;
432 case TEST_UPPER:
433 name="toUpper";
434 result.toUpper(locale);
435 break;
436 #if !UCONFIG_NO_BREAK_ITERATION
437 case TEST_TITLE:
438 name="toTitle";
439 result.toTitle((BreakIterator *)iter, locale, options);
440 break;
441 #endif
442 case TEST_FOLD:
443 name="foldCase";
444 result.foldCase(options);
445 break;
446 default:
447 name="";
448 break; // won't happen
449 }
450 if(result!=output) {
451 dataerrln("error: UnicodeString.%s() got a wrong result for a test case from casing.res", name);
452 }
453 #if !UCONFIG_NO_BREAK_ITERATION
454 if(whichCase==TEST_TITLE && options==0) {
455 result=input;
456 result.toTitle((BreakIterator *)iter, locale);
457 if(result!=output) {
458 dataerrln("error: UnicodeString.toTitle(options=0) got a wrong result for a test case from casing.res");
459 }
460 }
461 #endif
462
463 // UTF-8
464 char utf8In[100], utf8Out[100];
465 int32_t utf8InLength, utf8OutLength, resultLength;
466 UChar *buffer;
467
468 IcuTestErrorCode errorCode(*this, "TestCasingImpl");
469 LocalUCaseMapPointer csm(ucasemap_open(localeID, options, errorCode));
470 #if !UCONFIG_NO_BREAK_ITERATION
471 if(iter!=NULL) {
472 // Clone the break iterator so that the UCaseMap can safely adopt it.
473 UBreakIterator *clone=ubrk_safeClone((UBreakIterator *)iter, NULL, NULL, errorCode);
474 ucasemap_setBreakIterator(csm.getAlias(), clone, errorCode);
475 }
476 #endif
477
478 u_strToUTF8(utf8In, (int32_t)sizeof(utf8In), &utf8InLength, input.getBuffer(), input.length(), errorCode);
479 switch(whichCase) {
480 case TEST_LOWER:
481 name="ucasemap_utf8ToLower";
482 utf8OutLength=ucasemap_utf8ToLower(csm.getAlias(),
483 utf8Out, (int32_t)sizeof(utf8Out),
484 utf8In, utf8InLength, errorCode);
485 break;
486 case TEST_UPPER:
487 name="ucasemap_utf8ToUpper";
488 utf8OutLength=ucasemap_utf8ToUpper(csm.getAlias(),
489 utf8Out, (int32_t)sizeof(utf8Out),
490 utf8In, utf8InLength, errorCode);
491 break;
492 #if !UCONFIG_NO_BREAK_ITERATION
493 case TEST_TITLE:
494 name="ucasemap_utf8ToTitle";
495 utf8OutLength=ucasemap_utf8ToTitle(csm.getAlias(),
496 utf8Out, (int32_t)sizeof(utf8Out),
497 utf8In, utf8InLength, errorCode);
498 break;
499 #endif
500 case TEST_FOLD:
501 name="ucasemap_utf8FoldCase";
502 utf8OutLength=ucasemap_utf8FoldCase(csm.getAlias(),
503 utf8Out, (int32_t)sizeof(utf8Out),
504 utf8In, utf8InLength, errorCode);
505 break;
506 default:
507 name="";
508 utf8OutLength=0;
509 break; // won't happen
510 }
511 buffer=result.getBuffer(utf8OutLength);
512 u_strFromUTF8(buffer, result.getCapacity(), &resultLength, utf8Out, utf8OutLength, errorCode);
513 result.releaseBuffer(errorCode.isSuccess() ? resultLength : 0);
514
515 if(errorCode.isFailure()) {
516 errcheckln(errorCode, "error: %s() got an error for a test case from casing.res - %s", name, u_errorName(errorCode));
517 errorCode.reset();
518 } else if(result!=output) {
519 errln("error: %s() got a wrong result for a test case from casing.res", name);
520 errln("expected \"" + output + "\" got \"" + result + "\"" );
521 }
522 }
523
524 void
TestCasing()525 StringCaseTest::TestCasing() {
526 UErrorCode status = U_ZERO_ERROR;
527 #if !UCONFIG_NO_BREAK_ITERATION
528 LocalUBreakIteratorPointer iter;
529 #endif
530 char cLocaleID[100];
531 UnicodeString locale, input, output, optionsString, result;
532 uint32_t options;
533 int32_t whichCase, type;
534 LocalPointer<TestDataModule> driver(TestDataModule::getTestDataModule("casing", *this, status));
535 if(U_SUCCESS(status)) {
536 for(whichCase=0; whichCase<TEST_COUNT; ++whichCase) {
537 #if UCONFIG_NO_BREAK_ITERATION
538 if(whichCase==TEST_TITLE) {
539 continue;
540 }
541 #endif
542 LocalPointer<TestData> casingTest(driver->createTestData(dataNames[whichCase], status));
543 if(U_FAILURE(status)) {
544 errln("TestCasing failed to createTestData(%s) - %s", dataNames[whichCase], u_errorName(status));
545 break;
546 }
547 const DataMap *myCase = NULL;
548 while(casingTest->nextCase(myCase, status)) {
549 input = myCase->getString("Input", status);
550 output = myCase->getString("Output", status);
551
552 if(whichCase!=TEST_FOLD) {
553 locale = myCase->getString("Locale", status);
554 }
555 locale.extract(0, 0x7fffffff, cLocaleID, sizeof(cLocaleID), "");
556
557 #if !UCONFIG_NO_BREAK_ITERATION
558 if(whichCase==TEST_TITLE) {
559 type = myCase->getInt("Type", status);
560 if(type>=0) {
561 iter.adoptInstead(ubrk_open((UBreakIteratorType)type, cLocaleID, NULL, 0, &status));
562 } else if(type==-2) {
563 // Open a trivial break iterator that only delivers { 0, length }
564 // or even just { 0 } as boundaries.
565 static const UChar rules[] = { 0x2e, 0x2a, 0x3b }; // ".*;"
566 UParseError parseError;
567 iter.adoptInstead(ubrk_openRules(rules, UPRV_LENGTHOF(rules), NULL, 0, &parseError, &status));
568 }
569 }
570 #endif
571 options = 0;
572 if(whichCase==TEST_TITLE || whichCase==TEST_FOLD) {
573 optionsString = myCase->getString("Options", status);
574 if(optionsString.indexOf((UChar)0x54)>=0) { // T
575 options|=U_FOLD_CASE_EXCLUDE_SPECIAL_I;
576 }
577 if(optionsString.indexOf((UChar)0x4c)>=0) { // L
578 options|=U_TITLECASE_NO_LOWERCASE;
579 }
580 if(optionsString.indexOf((UChar)0x41)>=0) { // A
581 options|=U_TITLECASE_NO_BREAK_ADJUSTMENT;
582 }
583 }
584
585 if(U_FAILURE(status)) {
586 dataerrln("error: TestCasing() setup failed for %s test case from casing.res: %s", dataNames[whichCase], u_errorName(status));
587 status = U_ZERO_ERROR;
588 } else {
589 #if UCONFIG_NO_BREAK_ITERATION
590 LocalPointer<UMemory> iter;
591 #endif
592 TestCasingImpl(input, output, whichCase, iter.getAlias(), cLocaleID, options);
593 }
594
595 #if !UCONFIG_NO_BREAK_ITERATION
596 iter.adoptInstead(NULL);
597 #endif
598 }
599 }
600 }
601
602 #if !UCONFIG_NO_BREAK_ITERATION
603 // more tests for API coverage
604 status=U_ZERO_ERROR;
605 input=UNICODE_STRING_SIMPLE("sTrA\\u00dfE").unescape();
606 (result=input).toTitle(NULL);
607 if(result!=UNICODE_STRING_SIMPLE("Stra\\u00dfe").unescape()) {
608 dataerrln("UnicodeString::toTitle(NULL) failed.");
609 }
610 #endif
611 }
612
613 void
TestTitleOptions()614 StringCaseTest::TestTitleOptions() {
615 // New options in ICU 60.
616 TestCasingImpl(u"ʻcAt! ʻeTc.", u"ʻCat! ʻetc.", TEST_TITLE,
617 nullptr, "", U_TITLECASE_WHOLE_STRING);
618 TestCasingImpl(u"a ʻCaT. A ʻdOg! ʻeTc.", u"A ʻCaT. A ʻdOg! ʻETc.", TEST_TITLE,
619 nullptr, "", U_TITLECASE_SENTENCES|U_TITLECASE_NO_LOWERCASE);
620 TestCasingImpl(u"49eRs", u"49ers", TEST_TITLE,
621 nullptr, "", U_TITLECASE_WHOLE_STRING);
622 TestCasingImpl(u"«丰(aBc)»", u"«丰(abc)»", TEST_TITLE,
623 nullptr, "", U_TITLECASE_WHOLE_STRING);
624 TestCasingImpl(u"49eRs", u"49Ers", TEST_TITLE,
625 nullptr, "", U_TITLECASE_WHOLE_STRING|U_TITLECASE_ADJUST_TO_CASED);
626 TestCasingImpl(u"«丰(aBc)»", u"«丰(Abc)»", TEST_TITLE,
627 nullptr, "", U_TITLECASE_WHOLE_STRING|U_TITLECASE_ADJUST_TO_CASED);
628 TestCasingImpl(u" john. Smith", u" John. Smith", TEST_TITLE,
629 nullptr, "", U_TITLECASE_WHOLE_STRING|U_TITLECASE_NO_LOWERCASE);
630 TestCasingImpl(u" john. Smith", u" john. smith", TEST_TITLE,
631 nullptr, "", U_TITLECASE_WHOLE_STRING|U_TITLECASE_NO_BREAK_ADJUSTMENT);
632 TestCasingImpl(u"«ijs»", u"«IJs»", TEST_TITLE,
633 nullptr, "nl-BE", U_TITLECASE_WHOLE_STRING);
634 TestCasingImpl(u"«ijs»", u"«İjs»", TEST_TITLE,
635 nullptr, "tr-DE", U_TITLECASE_WHOLE_STRING);
636
637 #if !UCONFIG_NO_BREAK_ITERATION
638 // Test conflicting settings.
639 // If & when we add more options, then the ORed combinations may become
640 // indistinguishable from valid values.
641 IcuTestErrorCode errorCode(*this, "TestTitleOptions");
642 CaseMap::toTitle("", U_TITLECASE_NO_BREAK_ADJUSTMENT|U_TITLECASE_ADJUST_TO_CASED, nullptr,
643 u"", 0, nullptr, 0, nullptr, errorCode);
644 if (errorCode.get() != U_ILLEGAL_ARGUMENT_ERROR) {
645 errln("CaseMap::toTitle(multiple adjustment options) -> %s not illegal argument",
646 errorCode.errorName());
647 }
648 errorCode.reset();
649 CaseMap::toTitle("", U_TITLECASE_WHOLE_STRING|U_TITLECASE_SENTENCES, nullptr,
650 u"", 0, nullptr, 0, nullptr, errorCode);
651 if (errorCode.get() != U_ILLEGAL_ARGUMENT_ERROR) {
652 errln("CaseMap::toTitle(multiple iterator options) -> %s not illegal argument",
653 errorCode.errorName());
654 }
655 errorCode.reset();
656 LocalPointer<BreakIterator> iter(
657 BreakIterator::createCharacterInstance(Locale::getRoot(), errorCode));
658 CaseMap::toTitle("", U_TITLECASE_WHOLE_STRING, iter.getAlias(),
659 u"", 0, nullptr, 0, nullptr, errorCode);
660 if (errorCode.get() != U_ILLEGAL_ARGUMENT_ERROR) {
661 errln("CaseMap::toTitle(iterator option + iterator) -> %s not illegal argument",
662 errorCode.errorName());
663 }
664 errorCode.reset();
665 #endif
666 }
667
668 void
TestFullCaseFoldingIterator()669 StringCaseTest::TestFullCaseFoldingIterator() {
670 UnicodeString ffi=UNICODE_STRING_SIMPLE("ffi");
671 UnicodeString ss=UNICODE_STRING_SIMPLE("ss");
672 FullCaseFoldingIterator iter;
673 int32_t count=0;
674 int32_t countSpecific=0;
675 UChar32 c;
676 UnicodeString full;
677 while((c=iter.next(full))>=0) {
678 ++count;
679 // Check that the full Case_Folding has more than 1 code point.
680 if(!full.hasMoreChar32Than(0, 0x7fffffff, 1)) {
681 errln("error: FullCaseFoldingIterator.next()=U+%04lX full Case_Folding has at most 1 code point", (long)c);
682 continue;
683 }
684 // Check that full == Case_Folding(c).
685 UnicodeString cf(c);
686 cf.foldCase();
687 if(full!=cf) {
688 errln("error: FullCaseFoldingIterator.next()=U+%04lX full Case_Folding != cf(c)", (long)c);
689 continue;
690 }
691 // Spot-check a couple of specific cases.
692 if((full==ffi && c==0xfb03) || (full==ss && (c==0xdf || c==0x1e9e))) {
693 ++countSpecific;
694 }
695 }
696 if(countSpecific!=3) {
697 errln("error: FullCaseFoldingIterator did not yield exactly the expected specific cases");
698 }
699 if(count<70) {
700 errln("error: FullCaseFoldingIterator yielded only %d (cp, full) pairs", (int)count);
701 }
702 }
703
704 void
assertGreekUpper(const char16_t * s,const char16_t * expected)705 StringCaseTest::assertGreekUpper(const char16_t *s, const char16_t *expected) {
706 UnicodeString s16(s);
707 UnicodeString expected16(expected);
708 UnicodeString msg = UnicodeString("UnicodeString::toUpper/Greek(\"") + s16 + "\")";
709 UnicodeString result16(s16);
710 result16.toUpper(GREEK_LOCALE_);
711 assertEquals(msg, expected16, result16);
712
713 msg = UnicodeString("u_strToUpper/Greek(\"") + s16 + "\") cap=";
714 int32_t length = expected16.length();
715 int32_t capacities[] = {
716 // Keep in sync with the UTF-8 capacities near the bottom of this function.
717 0, length / 2, length - 1, length, length + 1
718 };
719 for (int32_t i = 0; i < UPRV_LENGTHOF(capacities); ++i) {
720 int32_t cap = capacities[i];
721 UChar *dest16 = result16.getBuffer(expected16.length() + 1);
722 u_memset(dest16, 0x55AA, result16.getCapacity());
723 UErrorCode errorCode = U_ZERO_ERROR;
724 length = u_strToUpper(dest16, cap, s16.getBuffer(), s16.length(), "el", &errorCode);
725 assertEquals(msg + cap, expected16.length(), length);
726 UErrorCode expectedErrorCode;
727 if (cap < expected16.length()) {
728 expectedErrorCode = U_BUFFER_OVERFLOW_ERROR;
729 } else if (cap == expected16.length()) {
730 expectedErrorCode = U_STRING_NOT_TERMINATED_WARNING;
731 } else {
732 expectedErrorCode = U_ZERO_ERROR;
733 assertEquals(msg + cap + " NUL", 0, dest16[length]);
734 }
735 assertEquals(msg + cap + " errorCode", expectedErrorCode, errorCode);
736 result16.releaseBuffer(length);
737 if (cap >= expected16.length()) {
738 assertEquals(msg + cap, expected16, result16);
739 }
740 }
741
742 UErrorCode errorCode = U_ZERO_ERROR;
743 LocalUCaseMapPointer csm(ucasemap_open("el", 0, &errorCode));
744 assertSuccess("ucasemap_open", errorCode);
745 std::string s8;
746 s16.toUTF8String(s8);
747 msg = UnicodeString("ucasemap_utf8ToUpper/Greek(\"") + s16 + "\")";
748 char dest8[1000];
749 length = ucasemap_utf8ToUpper(csm.getAlias(), dest8, UPRV_LENGTHOF(dest8),
750 s8.data(), s8.length(), &errorCode);
751 assertSuccess("ucasemap_utf8ToUpper", errorCode);
752 StringPiece result8(dest8, length);
753 UnicodeString result16From8 = UnicodeString::fromUTF8(result8);
754 assertEquals(msg, expected16, result16From8);
755
756 msg += " cap=";
757 capacities[1] = length / 2;
758 capacities[2] = length - 1;
759 capacities[3] = length;
760 capacities[4] = length + 1;
761 char dest8b[1000];
762 int32_t expected8Length = length; // Assuming the previous call worked.
763 for (int32_t i = 0; i < UPRV_LENGTHOF(capacities); ++i) {
764 int32_t cap = capacities[i];
765 memset(dest8b, 0x5A, UPRV_LENGTHOF(dest8b));
766 UErrorCode errorCode = U_ZERO_ERROR;
767 length = ucasemap_utf8ToUpper(csm.getAlias(), dest8b, cap,
768 s8.data(), s8.length(), &errorCode);
769 assertEquals(msg + cap, expected8Length, length);
770 UErrorCode expectedErrorCode;
771 if (cap < expected8Length) {
772 expectedErrorCode = U_BUFFER_OVERFLOW_ERROR;
773 } else if (cap == expected8Length) {
774 expectedErrorCode = U_STRING_NOT_TERMINATED_WARNING;
775 } else {
776 expectedErrorCode = U_ZERO_ERROR;
777 // Casts to int32_t to avoid matching UBool.
778 assertEquals(msg + cap + " NUL", (int32_t)0, (int32_t)dest8b[length]);
779 }
780 assertEquals(msg + cap + " errorCode", expectedErrorCode, errorCode);
781 if (cap >= expected8Length) {
782 assertEquals(msg + cap + " (memcmp)", 0, memcmp(dest8, dest8b, expected8Length));
783 }
784 }
785 }
786
787 void
TestGreekUpper()788 StringCaseTest::TestGreekUpper() {
789 // http://bugs.icu-project.org/trac/ticket/5456
790 assertGreekUpper(u"άδικος, κείμενο, ίριδα", u"ΑΔΙΚΟΣ, ΚΕΙΜΕΝΟ, ΙΡΙΔΑ");
791 // https://bugzilla.mozilla.org/show_bug.cgi?id=307039
792 // https://bug307039.bmoattachments.org/attachment.cgi?id=194893
793 assertGreekUpper(u"Πατάτα", u"ΠΑΤΑΤΑ");
794 assertGreekUpper(u"Αέρας, Μυστήριο, Ωραίο", u"ΑΕΡΑΣ, ΜΥΣΤΗΡΙΟ, ΩΡΑΙΟ");
795 assertGreekUpper(u"Μαΐου, Πόρος, Ρύθμιση", u"ΜΑΪΟΥ, ΠΟΡΟΣ, ΡΥΘΜΙΣΗ");
796 assertGreekUpper(u"ΰ, Τηρώ, Μάιος", u"Ϋ, ΤΗΡΩ, ΜΑΪΟΣ");
797 assertGreekUpper(u"άυλος", u"ΑΫΛΟΣ");
798 assertGreekUpper(u"ΑΫΛΟΣ", u"ΑΫΛΟΣ");
799 assertGreekUpper(u"Άκλιτα ρήματα ή άκλιτες μετοχές", u"ΑΚΛΙΤΑ ΡΗΜΑΤΑ Ή ΑΚΛΙΤΕΣ ΜΕΤΟΧΕΣ");
800 // http://www.unicode.org/udhr/d/udhr_ell_monotonic.html
801 assertGreekUpper(u"Επειδή η αναγνώριση της αξιοπρέπειας", u"ΕΠΕΙΔΗ Η ΑΝΑΓΝΩΡΙΣΗ ΤΗΣ ΑΞΙΟΠΡΕΠΕΙΑΣ");
802 assertGreekUpper(u"νομικού ή διεθνούς", u"ΝΟΜΙΚΟΥ Ή ΔΙΕΘΝΟΥΣ");
803 // http://unicode.org/udhr/d/udhr_ell_polytonic.html
804 assertGreekUpper(u"Ἐπειδὴ ἡ ἀναγνώριση", u"ΕΠΕΙΔΗ Η ΑΝΑΓΝΩΡΙΣΗ");
805 assertGreekUpper(u"νομικοῦ ἢ διεθνοῦς", u"ΝΟΜΙΚΟΥ Ή ΔΙΕΘΝΟΥΣ");
806 // From Google bug report
807 assertGreekUpper(u"Νέο, Δημιουργία", u"ΝΕΟ, ΔΗΜΙΟΥΡΓΙΑ");
808 // http://crbug.com/234797
809 assertGreekUpper(u"Ελάτε να φάτε τα καλύτερα παϊδάκια!", u"ΕΛΑΤΕ ΝΑ ΦΑΤΕ ΤΑ ΚΑΛΥΤΕΡΑ ΠΑΪΔΑΚΙΑ!");
810 assertGreekUpper(u"Μαΐου, τρόλεϊ", u"ΜΑΪΟΥ, ΤΡΟΛΕΪ");
811 assertGreekUpper(u"Το ένα ή το άλλο.", u"ΤΟ ΕΝΑ Ή ΤΟ ΑΛΛΟ.");
812 // http://multilingualtypesetting.co.uk/blog/greek-typesetting-tips/
813 assertGreekUpper(u"ρωμέικα", u"ΡΩΜΕΪΚΑ");
814 assertGreekUpper(u"ή.", u"Ή.");
815 }
816
817 void
TestLongUpper()818 StringCaseTest::TestLongUpper() {
819 if (quick) {
820 logln("not exhaustive mode: skipping this test");
821 return;
822 }
823 // Ticket #12663, crash with an extremely long string where
824 // U+0390 maps to 0399 0308 0301 so that the result is three times as long
825 // and overflows an int32_t.
826 int32_t length = 0x40000004; // more than 1G UChars
827 UnicodeString s(length, (UChar32)0x390, length);
828 UnicodeString result;
829 UChar *dest = result.getBuffer(length + 1);
830 if (s.isBogus() || dest == NULL) {
831 logln("Out of memory, unable to run this test on this machine.");
832 return;
833 }
834 IcuTestErrorCode errorCode(*this, "TestLongUpper");
835 int32_t destLength = u_strToUpper(dest, result.getCapacity(),
836 s.getBuffer(), s.length(), "", errorCode);
837 result.releaseBuffer(destLength);
838 if (errorCode.reset() != U_INDEX_OUTOFBOUNDS_ERROR) {
839 errln("expected U_INDEX_OUTOFBOUNDS_ERROR, got %s (destLength is undefined, got %ld)",
840 errorCode.errorName(), (long)destLength);
841 }
842 }
843
TestMalformedUTF8()844 void StringCaseTest::TestMalformedUTF8() {
845 // ticket #12639
846 IcuTestErrorCode errorCode(*this, "TestMalformedUTF8");
847 LocalUCaseMapPointer csm(ucasemap_open("en", U_TITLECASE_NO_BREAK_ADJUSTMENT, errorCode));
848 if (errorCode.isFailure()) {
849 errln("ucasemap_open(English) failed - %s", errorCode.errorName());
850 return;
851 }
852 char src[1] = { (char)0x85 }; // malformed UTF-8
853 char dest[3] = { 0, 0, 0 };
854 int32_t destLength;
855 #if !UCONFIG_NO_BREAK_ITERATION
856 destLength = ucasemap_utf8ToTitle(csm.getAlias(), dest, 3, src, 1, errorCode);
857 if (errorCode.isFailure() || destLength != 1 || dest[0] != src[0]) {
858 errln("ucasemap_utf8ToTitle(\\x85) failed: %s destLength=%d dest[0]=0x%02x",
859 errorCode.errorName(), (int)destLength, dest[0]);
860 }
861 #endif
862
863 errorCode.reset();
864 dest[0] = 0;
865 destLength = ucasemap_utf8ToLower(csm.getAlias(), dest, 3, src, 1, errorCode);
866 if (errorCode.isFailure() || destLength != 1 || dest[0] != src[0]) {
867 errln("ucasemap_utf8ToLower(\\x85) failed: %s destLength=%d dest[0]=0x%02x",
868 errorCode.errorName(), (int)destLength, dest[0]);
869 }
870
871 errorCode.reset();
872 dest[0] = 0;
873 destLength = ucasemap_utf8ToUpper(csm.getAlias(), dest, 3, src, 1, errorCode);
874 if (errorCode.isFailure() || destLength != 1 || dest[0] != src[0]) {
875 errln("ucasemap_utf8ToUpper(\\x85) failed: %s destLength=%d dest[0]=0x%02x",
876 errorCode.errorName(), (int)destLength, dest[0]);
877 }
878
879 errorCode.reset();
880 dest[0] = 0;
881 destLength = ucasemap_utf8FoldCase(csm.getAlias(), dest, 3, src, 1, errorCode);
882 if (errorCode.isFailure() || destLength != 1 || dest[0] != src[0]) {
883 errln("ucasemap_utf8FoldCase(\\x85) failed: %s destLength=%d dest[0]=0x%02x",
884 errorCode.errorName(), (int)destLength, dest[0]);
885 }
886 }
887
TestBufferOverflow()888 void StringCaseTest::TestBufferOverflow() {
889 // Ticket #12849, incorrect result from Title Case preflight operation,
890 // when buffer overflow error is expected.
891 IcuTestErrorCode errorCode(*this, "TestBufferOverflow");
892 LocalUCaseMapPointer csm(ucasemap_open("en", 0, errorCode));
893 if (errorCode.isFailure()) {
894 errln("ucasemap_open(English) failed - %s", errorCode.errorName());
895 return;
896 }
897
898 UnicodeString data("hello world");
899 int32_t result;
900 #if !UCONFIG_NO_BREAK_ITERATION
901 result = ucasemap_toTitle(csm.getAlias(), NULL, 0, data.getBuffer(), data.length(), errorCode);
902 if (errorCode.get() != U_BUFFER_OVERFLOW_ERROR || result != data.length()) {
903 errln("%s:%d ucasemap_toTitle(\"hello world\") failed: "
904 "expected (U_BUFFER_OVERFLOW_ERROR, %d), got (%s, %d)",
905 __FILE__, __LINE__, data.length(), errorCode.errorName(), result);
906 }
907 #endif
908 errorCode.reset();
909
910 std::string data_utf8;
911 data.toUTF8String(data_utf8);
912 #if !UCONFIG_NO_BREAK_ITERATION
913 result = ucasemap_utf8ToTitle(csm.getAlias(), NULL, 0, data_utf8.c_str(), data_utf8.length(), errorCode);
914 if (errorCode.get() != U_BUFFER_OVERFLOW_ERROR || result != (int32_t)data_utf8.length()) {
915 errln("%s:%d ucasemap_toTitle(\"hello world\") failed: "
916 "expected (U_BUFFER_OVERFLOW_ERROR, %d), got (%s, %d)",
917 __FILE__, __LINE__, data_utf8.length(), errorCode.errorName(), result);
918 }
919 #endif
920 errorCode.reset();
921 }
922
TestEdits()923 void StringCaseTest::TestEdits() {
924 IcuTestErrorCode errorCode(*this, "TestEdits");
925 Edits edits;
926 assertFalse("new Edits hasChanges", edits.hasChanges());
927 assertEquals("new Edits numberOfChanges", 0, edits.numberOfChanges());
928 assertEquals("new Edits", 0, edits.lengthDelta());
929 edits.addUnchanged(1); // multiple unchanged ranges are combined
930 edits.addUnchanged(10000); // too long, and they are split
931 edits.addReplace(0, 0);
932 edits.addUnchanged(2);
933 assertFalse("unchanged 10003 hasChanges", edits.hasChanges());
934 assertEquals("unchanged 10003 numberOfChanges", 0, edits.numberOfChanges());
935 assertEquals("unchanged 10003", 0, edits.lengthDelta());
936 edits.addReplace(2, 1); // multiple short equal-lengths edits are compressed
937 edits.addUnchanged(0);
938 edits.addReplace(2, 1);
939 edits.addReplace(2, 1);
940 edits.addReplace(0, 10);
941 edits.addReplace(100, 0);
942 edits.addReplace(3000, 4000); // variable-length encoding
943 edits.addReplace(100000, 100000);
944 assertTrue("some edits hasChanges", edits.hasChanges());
945 assertEquals("some edits numberOfChanges", 7, edits.numberOfChanges());
946 assertEquals("some edits", -3 + 10 - 100 + 1000, edits.lengthDelta());
947 UErrorCode outErrorCode = U_ZERO_ERROR;
948 assertFalse("edits done: copyErrorTo", edits.copyErrorTo(outErrorCode));
949
950 static const EditChange coarseExpectedChanges[] = {
951 { FALSE, 10003, 10003 },
952 { TRUE, 103106, 104013 }
953 };
954 TestUtility::checkEditsIter(*this, u"coarse",
955 edits.getCoarseIterator(), edits.getCoarseIterator(),
956 coarseExpectedChanges, UPRV_LENGTHOF(coarseExpectedChanges), TRUE, errorCode);
957 TestUtility::checkEditsIter(*this, u"coarse changes",
958 edits.getCoarseChangesIterator(), edits.getCoarseChangesIterator(),
959 coarseExpectedChanges, UPRV_LENGTHOF(coarseExpectedChanges), FALSE, errorCode);
960
961 static const EditChange fineExpectedChanges[] = {
962 { FALSE, 10003, 10003 },
963 { TRUE, 2, 1 },
964 { TRUE, 2, 1 },
965 { TRUE, 2, 1 },
966 { TRUE, 0, 10 },
967 { TRUE, 100, 0 },
968 { TRUE, 3000, 4000 },
969 { TRUE, 100000, 100000 }
970 };
971 TestUtility::checkEditsIter(*this, u"fine",
972 edits.getFineIterator(), edits.getFineIterator(),
973 fineExpectedChanges, UPRV_LENGTHOF(fineExpectedChanges), TRUE, errorCode);
974 TestUtility::checkEditsIter(*this, u"fine changes",
975 edits.getFineChangesIterator(), edits.getFineChangesIterator(),
976 fineExpectedChanges, UPRV_LENGTHOF(fineExpectedChanges), FALSE, errorCode);
977
978 edits.reset();
979 assertFalse("reset hasChanges", edits.hasChanges());
980 assertEquals("reset numberOfChanges", 0, edits.numberOfChanges());
981 assertEquals("reset", 0, edits.lengthDelta());
982 Edits::Iterator ei = edits.getCoarseChangesIterator();
983 assertFalse("reset then iterator", ei.next(errorCode));
984 }
985
TestCopyMoveEdits()986 void StringCaseTest::TestCopyMoveEdits() {
987 IcuTestErrorCode errorCode(*this, "TestCopyMoveEdits");
988 // Exceed the stack array capacity.
989 Edits a;
990 for (int32_t i = 0; i < 250; ++i) {
991 a.addReplace(i % 10, (i % 10) + 1);
992 }
993 assertEquals("a: many edits, length delta", 250, a.lengthDelta());
994
995 // copy
996 Edits b(a);
997 assertEquals("b: copy of many edits, length delta", 250, b.lengthDelta());
998 assertEquals("a remains: many edits, length delta", 250, a.lengthDelta());
999 TestUtility::checkEqualEdits(*this, u"b copy of a", a, b, errorCode);
1000
1001 // assign
1002 Edits c;
1003 c.addUnchanged(99);
1004 c.addReplace(88, 77);
1005 c = b;
1006 assertEquals("c: assigned many edits, length delta", 250, c.lengthDelta());
1007 assertEquals("b remains: many edits, length delta", 250, b.lengthDelta());
1008 TestUtility::checkEqualEdits(*this, u"c = b", b, c, errorCode);
1009
1010 // std::move trouble on these platforms.
1011 // See https://ssl.icu-project.org/trac/ticket/13393
1012 #if !(U_PLATFORM == U_PF_AIX || U_PLATFORM == U_PF_OS390)
1013 // move constructor empties object with heap array
1014 Edits d(std::move(a));
1015 assertEquals("d: move-constructed many edits, length delta", 250, d.lengthDelta());
1016 assertFalse("a moved away: no more hasChanges", a.hasChanges());
1017 TestUtility::checkEqualEdits(*this, u"d() <- a", d, b, errorCode);
1018 Edits empty;
1019 TestUtility::checkEqualEdits(*this, u"a moved away", empty, a, errorCode);
1020
1021 // move assignment empties object with heap array
1022 Edits e;
1023 e.addReplace(0, 1000);
1024 e = std::move(b);
1025 assertEquals("e: move-assigned many edits, length delta", 250, e.lengthDelta());
1026 assertFalse("b moved away: no more hasChanges", b.hasChanges());
1027 TestUtility::checkEqualEdits(*this, u"e <- b", e, c, errorCode);
1028 TestUtility::checkEqualEdits(*this, u"b moved away", empty, b, errorCode);
1029
1030 // Edits::Iterator default constructor.
1031 Edits::Iterator iter;
1032 assertFalse("Edits::Iterator().next()", iter.next(errorCode));
1033 assertSuccess("Edits::Iterator().next()", errorCode);
1034 iter = e.getFineChangesIterator();
1035 assertTrue("iter.next()", iter.next(errorCode));
1036 assertSuccess("iter.next()", errorCode);
1037 assertTrue("iter.hasChange()", iter.hasChange());
1038 assertEquals("iter.newLength()", 1, iter.newLength());
1039 #endif
1040 }
1041
TestEditsFindFwdBwd()1042 void StringCaseTest::TestEditsFindFwdBwd() {
1043 IcuTestErrorCode errorCode(*this, "TestEditsFindFwdBwd");
1044 // Some users need index mappings to be efficient when they are out of order.
1045 // The most interesting failure case for this test is it taking a very long time.
1046 Edits e;
1047 constexpr int32_t N = 200000;
1048 for (int32_t i = 0; i < N; ++i) {
1049 e.addUnchanged(1);
1050 e.addReplace(3, 1);
1051 }
1052 Edits::Iterator iter = e.getFineIterator();
1053 for (int32_t i = 0; i <= N; i += 2) {
1054 assertEquals("ascending", i * 2, iter.sourceIndexFromDestinationIndex(i, errorCode));
1055 assertEquals("ascending", i * 2 + 1, iter.sourceIndexFromDestinationIndex(i + 1, errorCode));
1056 }
1057 for (int32_t i = N; i >= 0; i -= 2) {
1058 assertEquals("descending", i * 2 + 1, iter.sourceIndexFromDestinationIndex(i + 1, errorCode));
1059 assertEquals("descending", i * 2, iter.sourceIndexFromDestinationIndex(i, errorCode));
1060 }
1061 }
1062
TestMergeEdits()1063 void StringCaseTest::TestMergeEdits() {
1064 // For debugging, set -v to see matching edits up to a failure.
1065 IcuTestErrorCode errorCode(*this, "TestMergeEdits");
1066 Edits ab, bc, ac, expected_ac;
1067
1068 // Simple: Two parallel non-changes.
1069 ab.addUnchanged(2);
1070 bc.addUnchanged(2);
1071 expected_ac.addUnchanged(2);
1072
1073 // Simple: Two aligned changes.
1074 ab.addReplace(3, 2);
1075 bc.addReplace(2, 1);
1076 expected_ac.addReplace(3, 1);
1077
1078 // Unequal non-changes.
1079 ab.addUnchanged(5);
1080 bc.addUnchanged(3);
1081 expected_ac.addUnchanged(3);
1082 // ab ahead by 2
1083
1084 // Overlapping changes accumulate until they share a boundary.
1085 ab.addReplace(4, 3);
1086 bc.addReplace(3, 2);
1087 ab.addReplace(4, 3);
1088 bc.addReplace(3, 2);
1089 ab.addReplace(4, 3);
1090 bc.addReplace(3, 2);
1091 bc.addUnchanged(4);
1092 expected_ac.addReplace(14, 8);
1093 // bc ahead by 2
1094
1095 // Balance out intermediate-string lengths.
1096 ab.addUnchanged(2);
1097 expected_ac.addUnchanged(2);
1098
1099 // Insert something and delete it: Should disappear.
1100 ab.addReplace(0, 5);
1101 ab.addReplace(0, 2);
1102 bc.addReplace(7, 0);
1103
1104 // Parallel change to make a new boundary.
1105 ab.addReplace(1, 2);
1106 bc.addReplace(2, 3);
1107 expected_ac.addReplace(1, 3);
1108
1109 // Multiple ab deletions should remain separate at the boundary.
1110 ab.addReplace(1, 0);
1111 ab.addReplace(2, 0);
1112 ab.addReplace(3, 0);
1113 expected_ac.addReplace(1, 0);
1114 expected_ac.addReplace(2, 0);
1115 expected_ac.addReplace(3, 0);
1116
1117 // Unequal non-changes can be split for another boundary.
1118 ab.addUnchanged(2);
1119 bc.addUnchanged(1);
1120 expected_ac.addUnchanged(1);
1121 // ab ahead by 1
1122
1123 // Multiple bc insertions should create a boundary and remain separate.
1124 bc.addReplace(0, 4);
1125 bc.addReplace(0, 5);
1126 bc.addReplace(0, 6);
1127 expected_ac.addReplace(0, 4);
1128 expected_ac.addReplace(0, 5);
1129 expected_ac.addReplace(0, 6);
1130 // ab ahead by 1
1131
1132 // Multiple ab deletions in the middle of a bc change are merged.
1133 bc.addReplace(2, 2);
1134 // bc ahead by 1
1135 ab.addReplace(1, 0);
1136 ab.addReplace(2, 0);
1137 ab.addReplace(3, 0);
1138 ab.addReplace(4, 1);
1139 expected_ac.addReplace(11, 2);
1140
1141 // Multiple bc insertions in the middle of an ab change are merged.
1142 ab.addReplace(5, 6);
1143 bc.addReplace(3, 3);
1144 // ab ahead by 3
1145 bc.addReplace(0, 4);
1146 bc.addReplace(0, 5);
1147 bc.addReplace(0, 6);
1148 bc.addReplace(3, 7);
1149 expected_ac.addReplace(5, 25);
1150
1151 // Delete around a deletion.
1152 ab.addReplace(4, 4);
1153 ab.addReplace(3, 0);
1154 ab.addUnchanged(2);
1155 bc.addReplace(2, 2);
1156 bc.addReplace(4, 0);
1157 expected_ac.addReplace(9, 2);
1158
1159 // Insert into an insertion.
1160 ab.addReplace(0, 2);
1161 bc.addReplace(1, 1);
1162 bc.addReplace(0, 8);
1163 bc.addUnchanged(4);
1164 expected_ac.addReplace(0, 10);
1165 // bc ahead by 3
1166
1167 // Balance out intermediate-string lengths.
1168 ab.addUnchanged(3);
1169 expected_ac.addUnchanged(3);
1170
1171 // Deletions meet insertions.
1172 // Output order is arbitrary in principle, but we expect insertions first
1173 // and want to keep it that way.
1174 ab.addReplace(2, 0);
1175 ab.addReplace(4, 0);
1176 ab.addReplace(6, 0);
1177 bc.addReplace(0, 1);
1178 bc.addReplace(0, 3);
1179 bc.addReplace(0, 5);
1180 expected_ac.addReplace(0, 1);
1181 expected_ac.addReplace(0, 3);
1182 expected_ac.addReplace(0, 5);
1183 expected_ac.addReplace(2, 0);
1184 expected_ac.addReplace(4, 0);
1185 expected_ac.addReplace(6, 0);
1186
1187 // End with a non-change, so that further edits are never reordered.
1188 ab.addUnchanged(1);
1189 bc.addUnchanged(1);
1190 expected_ac.addUnchanged(1);
1191
1192 ac.mergeAndAppend(ab, bc, errorCode);
1193 assertSuccess("ab+bc", errorCode);
1194 if (!TestUtility::checkEqualEdits(*this, u"ab+bc", expected_ac, ac, errorCode)) {
1195 return;
1196 }
1197
1198 // Append more Edits.
1199 Edits ab2, bc2;
1200 ab2.addUnchanged(5);
1201 bc2.addReplace(1, 2);
1202 bc2.addUnchanged(4);
1203 expected_ac.addReplace(1, 2);
1204 expected_ac.addUnchanged(4);
1205 ac.mergeAndAppend(ab2, bc2, errorCode);
1206 assertSuccess("ab2+bc2", errorCode);
1207 if (!TestUtility::checkEqualEdits(*this, u"ab2+bc2", expected_ac, ac, errorCode)) {
1208 return;
1209 }
1210
1211 // Append empty edits.
1212 Edits empty;
1213 ac.mergeAndAppend(empty, empty, errorCode);
1214 assertSuccess("empty+empty", errorCode);
1215 if (!TestUtility::checkEqualEdits(*this, u"empty+empty", expected_ac, ac, errorCode)) {
1216 return;
1217 }
1218
1219 // Error: Append more edits with mismatched intermediate-string lengths.
1220 Edits mismatch;
1221 mismatch.addReplace(1, 1);
1222 ac.mergeAndAppend(ab2, mismatch, errorCode);
1223 assertEquals("ab2+mismatch", U_ILLEGAL_ARGUMENT_ERROR, errorCode.get());
1224 errorCode.reset();
1225 ac.mergeAndAppend(mismatch, bc2, errorCode);
1226 assertEquals("mismatch+bc2", U_ILLEGAL_ARGUMENT_ERROR, errorCode.get());
1227 errorCode.reset();
1228 }
1229
TestCaseMapWithEdits()1230 void StringCaseTest::TestCaseMapWithEdits() {
1231 IcuTestErrorCode errorCode(*this, "TestCaseMapWithEdits");
1232 UChar dest[20];
1233 Edits edits;
1234
1235 int32_t length = CaseMap::toLower("tr", U_OMIT_UNCHANGED_TEXT,
1236 u"IstanBul", 8, dest, UPRV_LENGTHOF(dest), &edits, errorCode);
1237 assertEquals(u"toLower(IstanBul)", UnicodeString(u"ıb"), UnicodeString(TRUE, dest, length));
1238 static const EditChange lowerExpectedChanges[] = {
1239 { TRUE, 1, 1 },
1240 { FALSE, 4, 4 },
1241 { TRUE, 1, 1 },
1242 { FALSE, 2, 2 }
1243 };
1244 TestUtility::checkEditsIter(*this, u"toLower(IstanBul)",
1245 edits.getFineIterator(), edits.getFineIterator(),
1246 lowerExpectedChanges, UPRV_LENGTHOF(lowerExpectedChanges),
1247 TRUE, errorCode);
1248
1249 edits.reset();
1250 length = CaseMap::toUpper("el", U_OMIT_UNCHANGED_TEXT,
1251 u"Πατάτα", 6, dest, UPRV_LENGTHOF(dest), &edits, errorCode);
1252 assertEquals(u"toUpper(Πατάτα)", UnicodeString(u"ΑΤΑΤΑ"), UnicodeString(TRUE, dest, length));
1253 static const EditChange upperExpectedChanges[] = {
1254 { FALSE, 1, 1 },
1255 { TRUE, 1, 1 },
1256 { TRUE, 1, 1 },
1257 { TRUE, 1, 1 },
1258 { TRUE, 1, 1 },
1259 { TRUE, 1, 1 }
1260 };
1261 TestUtility::checkEditsIter(*this, u"toUpper(Πατάτα)",
1262 edits.getFineIterator(), edits.getFineIterator(),
1263 upperExpectedChanges, UPRV_LENGTHOF(upperExpectedChanges),
1264 TRUE, errorCode);
1265
1266 edits.reset();
1267
1268 #if !UCONFIG_NO_BREAK_ITERATION
1269 length = CaseMap::toTitle("nl",
1270 U_OMIT_UNCHANGED_TEXT |
1271 U_TITLECASE_NO_BREAK_ADJUSTMENT |
1272 U_TITLECASE_NO_LOWERCASE,
1273 nullptr, u"IjssEL IglOo", 12,
1274 dest, UPRV_LENGTHOF(dest), &edits, errorCode);
1275 assertEquals(u"toTitle(IjssEL IglOo)", UnicodeString(u"J"), UnicodeString(TRUE, dest, length));
1276 static const EditChange titleExpectedChanges[] = {
1277 { FALSE, 1, 1 },
1278 { TRUE, 1, 1 },
1279 { FALSE, 10, 10 }
1280 };
1281 TestUtility::checkEditsIter(*this, u"toTitle(IjssEL IglOo)",
1282 edits.getFineIterator(), edits.getFineIterator(),
1283 titleExpectedChanges, UPRV_LENGTHOF(titleExpectedChanges),
1284 TRUE, errorCode);
1285 #endif
1286
1287 // No explicit nor automatic edits.reset(). Edits should be appended.
1288 length = CaseMap::fold(U_OMIT_UNCHANGED_TEXT | U_EDITS_NO_RESET | U_FOLD_CASE_EXCLUDE_SPECIAL_I,
1289 u"IßtanBul", 8, dest, UPRV_LENGTHOF(dest), &edits, errorCode);
1290 assertEquals(u"foldCase(IßtanBul)", UnicodeString(u"ıssb"), UnicodeString(TRUE, dest, length));
1291 static const EditChange foldExpectedChanges[] = {
1292 #if !UCONFIG_NO_BREAK_ITERATION
1293 // From titlecasing.
1294 { FALSE, 1, 1 },
1295 { TRUE, 1, 1 },
1296 { FALSE, 10, 10 },
1297 #endif
1298 // From case folding.
1299 { TRUE, 1, 1 },
1300 { TRUE, 1, 2 },
1301 { FALSE, 3, 3 },
1302 { TRUE, 1, 1 },
1303 { FALSE, 2, 2 }
1304 };
1305 TestUtility::checkEditsIter(*this, u"foldCase(no Edits reset, IßtanBul)",
1306 edits.getFineIterator(), edits.getFineIterator(),
1307 foldExpectedChanges, UPRV_LENGTHOF(foldExpectedChanges),
1308 TRUE, errorCode);
1309 }
1310
TestCaseMapUTF8WithEdits()1311 void StringCaseTest::TestCaseMapUTF8WithEdits() {
1312 IcuTestErrorCode errorCode(*this, "TestCaseMapUTF8WithEdits");
1313 char dest[50];
1314 Edits edits;
1315
1316 int32_t length = CaseMap::utf8ToLower("tr", U_OMIT_UNCHANGED_TEXT,
1317 u8"IstanBul", 8, dest, UPRV_LENGTHOF(dest), &edits, errorCode);
1318 assertEquals(u"toLower(IstanBul)", UnicodeString(u"ıb"),
1319 UnicodeString::fromUTF8(StringPiece(dest, length)));
1320 static const EditChange lowerExpectedChanges[] = {
1321 { TRUE, 1, 2 },
1322 { FALSE, 4, 4 },
1323 { TRUE, 1, 1 },
1324 { FALSE, 2, 2 }
1325 };
1326 TestUtility::checkEditsIter(*this, u"toLower(IstanBul)",
1327 edits.getFineIterator(), edits.getFineIterator(),
1328 lowerExpectedChanges, UPRV_LENGTHOF(lowerExpectedChanges),
1329 TRUE, errorCode);
1330
1331 edits.reset();
1332 length = CaseMap::utf8ToUpper("el", U_OMIT_UNCHANGED_TEXT,
1333 u8"Πατάτα", 6 * 2, dest, UPRV_LENGTHOF(dest), &edits, errorCode);
1334 assertEquals(u"toUpper(Πατάτα)", UnicodeString(u"ΑΤΑΤΑ"),
1335 UnicodeString::fromUTF8(StringPiece(dest, length)));
1336 static const EditChange upperExpectedChanges[] = {
1337 { FALSE, 2, 2 },
1338 { TRUE, 2, 2 },
1339 { TRUE, 2, 2 },
1340 { TRUE, 2, 2 },
1341 { TRUE, 2, 2 },
1342 { TRUE, 2, 2 }
1343 };
1344 TestUtility::checkEditsIter(*this, u"toUpper(Πατάτα)",
1345 edits.getFineIterator(), edits.getFineIterator(),
1346 upperExpectedChanges, UPRV_LENGTHOF(upperExpectedChanges),
1347 TRUE, errorCode);
1348
1349 edits.reset();
1350 #if !UCONFIG_NO_BREAK_ITERATION
1351 length = CaseMap::utf8ToTitle("nl",
1352 U_OMIT_UNCHANGED_TEXT |
1353 U_TITLECASE_NO_BREAK_ADJUSTMENT |
1354 U_TITLECASE_NO_LOWERCASE,
1355 nullptr, u8"IjssEL IglOo", 12,
1356 dest, UPRV_LENGTHOF(dest), &edits, errorCode);
1357 assertEquals(u"toTitle(IjssEL IglOo)", UnicodeString(u"J"),
1358 UnicodeString::fromUTF8(StringPiece(dest, length)));
1359 static const EditChange titleExpectedChanges[] = {
1360 { FALSE, 1, 1 },
1361 { TRUE, 1, 1 },
1362 { FALSE, 10, 10 }
1363 };
1364 TestUtility::checkEditsIter(*this, u"toTitle(IjssEL IglOo)",
1365 edits.getFineIterator(), edits.getFineIterator(),
1366 titleExpectedChanges, UPRV_LENGTHOF(titleExpectedChanges),
1367 TRUE, errorCode);
1368 #endif
1369
1370 // No explicit nor automatic edits.reset(). Edits should be appended.
1371 length = CaseMap::utf8Fold(U_OMIT_UNCHANGED_TEXT | U_EDITS_NO_RESET |
1372 U_FOLD_CASE_EXCLUDE_SPECIAL_I,
1373 u8"IßtanBul", 1 + 2 + 6, dest, UPRV_LENGTHOF(dest), &edits, errorCode);
1374 assertEquals(u"foldCase(IßtanBul)", UnicodeString(u"ıssb"),
1375 UnicodeString::fromUTF8(StringPiece(dest, length)));
1376 static const EditChange foldExpectedChanges[] = {
1377 #if !UCONFIG_NO_BREAK_ITERATION
1378 // From titlecasing.
1379 { FALSE, 1, 1 },
1380 { TRUE, 1, 1 },
1381 { FALSE, 10, 10 },
1382 #endif
1383 // From case folding.
1384 { TRUE, 1, 2 },
1385 { TRUE, 2, 2 },
1386 { FALSE, 3, 3 },
1387 { TRUE, 1, 1 },
1388 { FALSE, 2, 2 }
1389 };
1390 TestUtility::checkEditsIter(*this, u"foldCase(IßtanBul)",
1391 edits.getFineIterator(), edits.getFineIterator(),
1392 foldExpectedChanges, UPRV_LENGTHOF(foldExpectedChanges),
1393 TRUE, errorCode);
1394 }
1395
TestCaseMapToString()1396 void StringCaseTest::TestCaseMapToString() {
1397 // This test function name is parallel with one in UCharacterCaseTest.java.
1398 // It is a bit of a misnomer until we have CaseMap API that writes to
1399 // a UnicodeString, at which point we should change this code here.
1400 IcuTestErrorCode errorCode(*this, "TestCaseMapToString");
1401 UChar dest[20];
1402
1403 // Omit unchanged text.
1404 int32_t length = CaseMap::toLower("tr", U_OMIT_UNCHANGED_TEXT,
1405 u"IstanBul", 8, dest, UPRV_LENGTHOF(dest), nullptr, errorCode);
1406 assertEquals(u"toLower(IstanBul)",
1407 UnicodeString(u"ıb"), UnicodeString(TRUE, dest, length));
1408 length = CaseMap::toUpper("el", U_OMIT_UNCHANGED_TEXT,
1409 u"Πατάτα", 6, dest, UPRV_LENGTHOF(dest), nullptr, errorCode);
1410 assertEquals(u"toUpper(Πατάτα)",
1411 UnicodeString(u"ΑΤΑΤΑ"), UnicodeString(TRUE, dest, length));
1412 #if !UCONFIG_NO_BREAK_ITERATION
1413 length = CaseMap::toTitle("nl",
1414 U_OMIT_UNCHANGED_TEXT |
1415 U_TITLECASE_NO_BREAK_ADJUSTMENT |
1416 U_TITLECASE_NO_LOWERCASE,
1417 nullptr, u"IjssEL IglOo", 12,
1418 dest, UPRV_LENGTHOF(dest), nullptr, errorCode);
1419 assertEquals(u"toTitle(IjssEL IglOo)",
1420 UnicodeString(u"J"), UnicodeString(TRUE, dest, length));
1421 #endif
1422 length = CaseMap::fold(U_OMIT_UNCHANGED_TEXT | U_FOLD_CASE_EXCLUDE_SPECIAL_I,
1423 u"IßtanBul", 8, dest, UPRV_LENGTHOF(dest), nullptr, errorCode);
1424 assertEquals(u"foldCase(IßtanBul)",
1425 UnicodeString(u"ıssb"), UnicodeString(TRUE, dest, length));
1426
1427 // Return the whole result string.
1428 length = CaseMap::toLower("tr", 0,
1429 u"IstanBul", 8, dest, UPRV_LENGTHOF(dest), nullptr, errorCode);
1430 assertEquals(u"toLower(IstanBul)",
1431 UnicodeString(u"ıstanbul"), UnicodeString(TRUE, dest, length));
1432 length = CaseMap::toUpper("el", 0,
1433 u"Πατάτα", 6, dest, UPRV_LENGTHOF(dest), nullptr, errorCode);
1434 assertEquals(u"toUpper(Πατάτα)",
1435 UnicodeString(u"ΠΑΤΑΤΑ"), UnicodeString(TRUE, dest, length));
1436 #if !UCONFIG_NO_BREAK_ITERATION
1437 length = CaseMap::toTitle("nl",
1438 U_TITLECASE_NO_BREAK_ADJUSTMENT |
1439 U_TITLECASE_NO_LOWERCASE,
1440 nullptr, u"IjssEL IglOo", 12,
1441 dest, UPRV_LENGTHOF(dest), nullptr, errorCode);
1442 assertEquals(u"toTitle(IjssEL IglOo)",
1443 UnicodeString(u"IJssEL IglOo"), UnicodeString(TRUE, dest, length));
1444 #endif
1445 length = CaseMap::fold(U_FOLD_CASE_EXCLUDE_SPECIAL_I,
1446 u"IßtanBul", 8, dest, UPRV_LENGTHOF(dest), nullptr, errorCode);
1447 assertEquals(u"foldCase(IßtanBul)",
1448 UnicodeString(u"ısstanbul"), UnicodeString(TRUE, dest, length));
1449 }
1450
TestCaseMapUTF8ToString()1451 void StringCaseTest::TestCaseMapUTF8ToString() {
1452 IcuTestErrorCode errorCode(*this, "TestCaseMapUTF8ToString");
1453 std::string dest;
1454 StringByteSink<std::string> sink(&dest);
1455
1456 // Omit unchanged text.
1457 CaseMap::utf8ToLower("tr", U_OMIT_UNCHANGED_TEXT, u8"IstanBul", sink, nullptr, errorCode);
1458 assertEquals(u"toLower(IstanBul)", UnicodeString(u"ıb"), UnicodeString::fromUTF8(dest));
1459 dest.clear();
1460 CaseMap::utf8ToUpper("el", U_OMIT_UNCHANGED_TEXT, u8"Πατάτα", sink, nullptr, errorCode);
1461 assertEquals(u"toUpper(Πατάτα)", UnicodeString(u"ΑΤΑΤΑ"),
1462 UnicodeString::fromUTF8(dest));
1463 #if !UCONFIG_NO_BREAK_ITERATION
1464 dest.clear();
1465 CaseMap::utf8ToTitle(
1466 "nl", U_OMIT_UNCHANGED_TEXT | U_TITLECASE_NO_BREAK_ADJUSTMENT | U_TITLECASE_NO_LOWERCASE,
1467 nullptr, u8"IjssEL IglOo", sink, nullptr, errorCode);
1468 assertEquals(u"toTitle(IjssEL IglOo)", UnicodeString(u"J"),
1469 UnicodeString::fromUTF8(dest));
1470 #endif
1471 dest.clear();
1472 CaseMap::utf8Fold(U_OMIT_UNCHANGED_TEXT | U_FOLD_CASE_EXCLUDE_SPECIAL_I,
1473 u8"IßtanBul", sink, nullptr, errorCode);
1474 assertEquals(u"foldCase(IßtanBul)", UnicodeString(u"ıssb"),
1475 UnicodeString::fromUTF8(dest));
1476
1477 // Return the whole result string.
1478 dest.clear();
1479 CaseMap::utf8ToLower("tr", 0, u8"IstanBul", sink, nullptr, errorCode);
1480 assertEquals(u"toLower(IstanBul)", UnicodeString(u"ıstanbul"),
1481 UnicodeString::fromUTF8(dest));
1482 dest.clear();
1483 CaseMap::utf8ToUpper("el", 0, u8"Πατάτα", sink, nullptr, errorCode);
1484 assertEquals(u"toUpper(Πατάτα)", UnicodeString(u"ΠΑΤΑΤΑ"),
1485 UnicodeString::fromUTF8(dest));
1486 #if !UCONFIG_NO_BREAK_ITERATION
1487 dest.clear();
1488 CaseMap::utf8ToTitle("nl", U_TITLECASE_NO_BREAK_ADJUSTMENT | U_TITLECASE_NO_LOWERCASE,
1489 nullptr, u8"IjssEL IglOo", sink, nullptr, errorCode);
1490 assertEquals(u"toTitle(IjssEL IglOo)", UnicodeString(u"IJssEL IglOo"),
1491 UnicodeString::fromUTF8(dest));
1492 #endif
1493 dest.clear();
1494 CaseMap::utf8Fold(U_FOLD_CASE_EXCLUDE_SPECIAL_I, u8"IßtanBul", sink, nullptr, errorCode);
1495 assertEquals(u"foldCase(IßtanBul)", UnicodeString(u"ısstanbul"),
1496 UnicodeString::fromUTF8(dest));
1497 }
1498
TestLongUnicodeString()1499 void StringCaseTest::TestLongUnicodeString() {
1500 // Code coverage for UnicodeString case mapping code handling
1501 // long strings or many changes in a string.
1502 UnicodeString s(TRUE,
1503 (const UChar *)
1504 u"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeF"
1505 u"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeF"
1506 u"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeF"
1507 u"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeF"
1508 u"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeF"
1509 u"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeF", 6 * 51);
1510 UnicodeString expected(TRUE,
1511 (const UChar *)
1512 u"AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEEF"
1513 u"AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEEF"
1514 u"AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEEF"
1515 u"AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEEF"
1516 u"AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEEF"
1517 u"AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEEF", 6 * 51);
1518 s.toUpper(Locale::getRoot());
1519 assertEquals("string length 306", expected, s);
1520 }
1521
1522 #if !UCONFIG_NO_BREAK_ITERATION
TestBug13127()1523 void StringCaseTest::TestBug13127() {
1524 // Test case crashed when the bug was present.
1525 const char16_t *s16 = u"日本語";
1526 UnicodeString s(TRUE, s16, -1);
1527 s.toTitle(0, Locale::getEnglish());
1528 }
1529
TestInPlaceTitle()1530 void StringCaseTest::TestInPlaceTitle() {
1531 // Similar to TestBug13127. u_strToTitle() can modify the buffer in-place.
1532 IcuTestErrorCode errorCode(*this, "TestInPlaceTitle");
1533 char16_t s[32] = u"ß ß ß日本語 abcdef";
1534 const char16_t *expected = u"Ss Ss Ss日本語 Abcdef";
1535 int32_t length = u_strToTitle(s, UPRV_LENGTHOF(s), s, -1, nullptr, "", errorCode);
1536 assertEquals("u_strToTitle(in-place) length", u_strlen(expected), length);
1537 assertEquals("u_strToTitle(in-place)", expected, s);
1538 }
1539 #endif
1540
TestCaseMapEditsIteratorDocs()1541 void StringCaseTest::TestCaseMapEditsIteratorDocs() {
1542 IcuTestErrorCode status(*this, "TestCaseMapEditsIteratorDocs");
1543 const char16_t* input = u"abcßDeF";
1544 int32_t inputLength = u_strlen(input);
1545 // output: "abcssdef"
1546
1547 char16_t output[10];
1548 Edits edits;
1549 CaseMap::fold(0, input, -1, output, 10, &edits, status);
1550
1551 static const char16_t* fineIteratorExpected[] = {
1552 u"{ src[0..3] ≡ dest[0..3] (no-change) }",
1553 u"{ src[3..4] ⇝ dest[3..5], repl[0..2] }",
1554 u"{ src[4..5] ⇝ dest[5..6], repl[2..3] }",
1555 u"{ src[5..6] ≡ dest[6..7] (no-change) }",
1556 u"{ src[6..7] ⇝ dest[7..8], repl[3..4] }",
1557 };
1558 static const char16_t* fineChangesIteratorExpected[] = {
1559 u"{ src[3..4] ⇝ dest[3..5], repl[0..2] }",
1560 u"{ src[4..5] ⇝ dest[5..6], repl[2..3] }",
1561 u"{ src[6..7] ⇝ dest[7..8], repl[3..4] }",
1562 };
1563 static const char16_t* coarseIteratorExpected[] = {
1564 u"{ src[0..3] ≡ dest[0..3] (no-change) }",
1565 u"{ src[3..5] ⇝ dest[3..6], repl[0..3] }",
1566 u"{ src[5..6] ≡ dest[6..7] (no-change) }",
1567 u"{ src[6..7] ⇝ dest[7..8], repl[3..4] }",
1568 };
1569 static const char16_t* coarseChangesIteratorExpected[] = {
1570 u"{ src[3..5] ⇝ dest[3..6], repl[0..3] }",
1571 u"{ src[6..7] ⇝ dest[7..8], repl[3..4] }",
1572 };
1573
1574 // Expected destination indices when source index is queried
1575 static int32_t expectedDestFineEditIndices[] = {0, 0, 0, 3, 5, 6, 7};
1576 static int32_t expectedDestCoarseEditIndices[] = {0, 0, 0, 3, 3, 6, 7};
1577 static int32_t expectedDestFineStringIndices[] = {0, 1, 2, 3, 5, 6, 7};
1578 static int32_t expectedDestCoarseStringIndices[] = {0, 1, 2, 3, 6, 6, 7};
1579
1580 // Expected source indices when destination index is queried
1581 static int32_t expectedSrcFineEditIndices[] = { 0, 0, 0, 3, 3, 4, 5, 6 };
1582 static int32_t expectedSrcCoarseEditIndices[] = { 0, 0, 0, 3, 3, 3, 5, 6 };
1583 static int32_t expectedSrcFineStringIndices[] = { 0, 1, 2, 3, 4, 4, 5, 6 };
1584 static int32_t expectedSrcCoarseStringIndices[] = { 0, 1, 2, 3, 5, 5, 5, 6 };
1585
1586 // Demonstrate the iterator next() method:
1587 Edits::Iterator fineIterator = edits.getFineIterator();
1588 int i = 0;
1589 UnicodeString toString;
1590 while (fineIterator.next(status)) {
1591 UnicodeString expected = fineIteratorExpected[i++];
1592 assertEquals(UnicodeString(u"Iteration #") + i,
1593 expected,
1594 fineIterator.toString(toString.remove()));
1595 }
1596 Edits::Iterator fineChangesIterator = edits.getFineChangesIterator();
1597 i = 0;
1598 while (fineChangesIterator.next(status)) {
1599 UnicodeString expected = fineChangesIteratorExpected[i++];
1600 assertEquals(UnicodeString(u"Iteration #") + i,
1601 expected,
1602 fineChangesIterator.toString(toString.remove()));
1603 }
1604 Edits::Iterator coarseIterator = edits.getCoarseIterator();
1605 i = 0;
1606 while (coarseIterator.next(status)) {
1607 UnicodeString expected = coarseIteratorExpected[i++];
1608 assertEquals(UnicodeString(u"Iteration #") + i,
1609 expected,
1610 coarseIterator.toString(toString.remove()));
1611 }
1612 Edits::Iterator coarseChangesIterator = edits.getCoarseChangesIterator();
1613 i = 0;
1614 while (coarseChangesIterator.next(status)) {
1615 UnicodeString expected = coarseChangesIteratorExpected[i++];
1616 assertEquals(UnicodeString(u"Iteration #") + i,
1617 expected,
1618 coarseChangesIterator.toString(toString.remove()));
1619 }
1620
1621 // Demonstrate the iterator indexing methods:
1622 // fineIterator should have the same behavior as fineChangesIterator, and
1623 // coarseIterator should have the same behavior as coarseChangesIterator.
1624 for (int32_t srcIndex=0; srcIndex<inputLength; srcIndex++) {
1625 fineIterator.findSourceIndex(srcIndex, status);
1626 fineChangesIterator.findSourceIndex(srcIndex, status);
1627 coarseIterator.findSourceIndex(srcIndex, status);
1628 coarseChangesIterator.findSourceIndex(srcIndex, status);
1629
1630 assertEquals(UnicodeString("Source index: ") + srcIndex,
1631 expectedDestFineEditIndices[srcIndex],
1632 fineIterator.destinationIndex());
1633 assertEquals(UnicodeString("Source index: ") + srcIndex,
1634 expectedDestFineEditIndices[srcIndex],
1635 fineChangesIterator.destinationIndex());
1636 assertEquals(UnicodeString("Source index: ") + srcIndex,
1637 expectedDestCoarseEditIndices[srcIndex],
1638 coarseIterator.destinationIndex());
1639 assertEquals(UnicodeString("Source index: ") + srcIndex,
1640 expectedDestCoarseEditIndices[srcIndex],
1641 coarseChangesIterator.destinationIndex());
1642
1643 assertEquals(UnicodeString("Source index: ") + srcIndex,
1644 expectedDestFineStringIndices[srcIndex],
1645 fineIterator.destinationIndexFromSourceIndex(srcIndex, status));
1646 assertEquals(UnicodeString("Source index: ") + srcIndex,
1647 expectedDestFineStringIndices[srcIndex],
1648 fineChangesIterator.destinationIndexFromSourceIndex(srcIndex, status));
1649 assertEquals(UnicodeString("Source index: ") + srcIndex,
1650 expectedDestCoarseStringIndices[srcIndex],
1651 coarseIterator.destinationIndexFromSourceIndex(srcIndex, status));
1652 assertEquals(UnicodeString("Source index: ") + srcIndex,
1653 expectedDestCoarseStringIndices[srcIndex],
1654 coarseChangesIterator.destinationIndexFromSourceIndex(srcIndex, status));
1655 }
1656 for (int32_t destIndex=0; destIndex<inputLength; destIndex++) {
1657 fineIterator.findDestinationIndex(destIndex, status);
1658 fineChangesIterator.findDestinationIndex(destIndex, status);
1659 coarseIterator.findDestinationIndex(destIndex, status);
1660 coarseChangesIterator.findDestinationIndex(destIndex, status);
1661
1662 assertEquals(UnicodeString("Destination index: ") + destIndex,
1663 expectedSrcFineEditIndices[destIndex],
1664 fineIterator.sourceIndex());
1665 assertEquals(UnicodeString("Destination index: ") + destIndex,
1666 expectedSrcFineEditIndices[destIndex],
1667 fineChangesIterator.sourceIndex());
1668 assertEquals(UnicodeString("Destination index: ") + destIndex,
1669 expectedSrcCoarseEditIndices[destIndex],
1670 coarseIterator.sourceIndex());
1671 assertEquals(UnicodeString("Destination index: ") + destIndex,
1672 expectedSrcCoarseEditIndices[destIndex],
1673 coarseChangesIterator.sourceIndex());
1674
1675 assertEquals(UnicodeString("Destination index: ") + destIndex,
1676 expectedSrcFineStringIndices[destIndex],
1677 fineIterator.sourceIndexFromDestinationIndex(destIndex, status));
1678 assertEquals(UnicodeString("Destination index: ") + destIndex,
1679 expectedSrcFineStringIndices[destIndex],
1680 fineChangesIterator.sourceIndexFromDestinationIndex(destIndex, status));
1681 assertEquals(UnicodeString("Destination index: ") + destIndex,
1682 expectedSrcCoarseStringIndices[destIndex],
1683 coarseIterator.sourceIndexFromDestinationIndex(destIndex, status));
1684 assertEquals(UnicodeString("Destination index: ") + destIndex,
1685 expectedSrcCoarseStringIndices[destIndex],
1686 coarseChangesIterator.sourceIndexFromDestinationIndex(destIndex, status));
1687 }
1688 }
1689
TestCaseMapGreekExtended()1690 void StringCaseTest::TestCaseMapGreekExtended() {
1691 // Ticket 13851
1692 UnicodeString s(u"\u1F80\u1F88\u1FFC");
1693 UnicodeString result(s);
1694 result.toLower(Locale::getRoot());
1695 assertEquals(u"lower", u"\u1F80\u1F80\u1FF3", result);
1696 #if !UCONFIG_NO_BREAK_ITERATION
1697 result = s;
1698 result.toTitle(nullptr, Locale::getRoot());
1699 assertEquals(u"title", u"\u1F88\u1F80\u1FF3", result);
1700 #endif
1701 }
1702
1703 //#endif
1704