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