• 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) 2003-2014, International Business Machines
7 *   Corporation and others.  All Rights Reserved.
8 *
9 *******************************************************************************
10 *   file name:  convtest.cpp
11 *   encoding:   UTF-8
12 *   tab size:   8 (not used)
13 *   indentation:4
14 *
15 *   created on: 2003jul15
16 *   created by: Markus W. Scherer
17 *
18 *   Test file for data-driven conversion tests.
19 */
20 
21 #include "unicode/utypes.h"
22 
23 #if !UCONFIG_NO_LEGACY_CONVERSION
24 /*
25  * Note: Turning off all of convtest.cpp if !UCONFIG_NO_LEGACY_CONVERSION
26  * is slightly unnecessary - it removes tests for Unicode charsets
27  * like UTF-8 that should work.
28  * However, there is no easy way for the test to detect whether a test case
29  * is for a Unicode charset, so it would be difficult to only exclude those.
30  * Also, regular testing of ICU is done with all modules on, therefore
31  * not testing conversion for a custom configuration like this should be ok.
32  */
33 
34 #include "unicode/ucnv.h"
35 #include "unicode/unistr.h"
36 #include "unicode/parsepos.h"
37 #include "unicode/uniset.h"
38 #include "unicode/ustring.h"
39 #include "unicode/ures.h"
40 #include "unicode/utf16.h"
41 #include "convtest.h"
42 #include "cmemory.h"
43 #include "unicode/tstdtmod.h"
44 #include <string.h>
45 #include <stdlib.h>
46 
47 enum {
48     // characters used in test data for callbacks
49     SUB_CB='?',
50     SKIP_CB='0',
51     STOP_CB='.',
52     ESC_CB='&'
53 };
54 
ConversionTest()55 ConversionTest::ConversionTest() {
56     UErrorCode errorCode=U_ZERO_ERROR;
57     utf8Cnv=ucnv_open("UTF-8", &errorCode);
58     ucnv_setToUCallBack(utf8Cnv, UCNV_TO_U_CALLBACK_STOP, NULL, NULL, NULL, &errorCode);
59     if(U_FAILURE(errorCode)) {
60         errln("unable to open UTF-8 converter");
61     }
62 }
63 
~ConversionTest()64 ConversionTest::~ConversionTest() {
65     ucnv_close(utf8Cnv);
66 }
67 
68 void
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)69 ConversionTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char * /*par*/) {
70     if (exec) logln("TestSuite ConversionTest: ");
71     TESTCASE_AUTO_BEGIN;
72 #if !UCONFIG_NO_FILE_IO
73     TESTCASE_AUTO(TestToUnicode);
74     TESTCASE_AUTO(TestFromUnicode);
75     TESTCASE_AUTO(TestGetUnicodeSet);
76 #endif
77     TESTCASE_AUTO(TestGetUnicodeSet2);
78     TESTCASE_AUTO(TestDefaultIgnorableCallback);
79     TESTCASE_AUTO(TestUTF8ToUTF8Overflow);
80     TESTCASE_AUTO_END;
81 }
82 
83 // test data interface ----------------------------------------------------- ***
84 
85 void
TestToUnicode()86 ConversionTest::TestToUnicode() {
87     ConversionCase cc;
88     char charset[100], cbopt[4];
89     const char *option;
90     UnicodeString s, unicode;
91     int32_t offsetsLength;
92     UConverterToUCallback callback;
93 
94     TestDataModule *dataModule;
95     TestData *testData;
96     const DataMap *testCase;
97     UErrorCode errorCode;
98     int32_t i;
99 
100     errorCode=U_ZERO_ERROR;
101     dataModule=TestDataModule::getTestDataModule("conversion", *this, errorCode);
102     if(U_SUCCESS(errorCode)) {
103         testData=dataModule->createTestData("toUnicode", errorCode);
104         if(U_SUCCESS(errorCode)) {
105             for(i=0; testData->nextCase(testCase, errorCode); ++i) {
106                 if(U_FAILURE(errorCode)) {
107                     errln("error retrieving conversion/toUnicode test case %d - %s",
108                             i, u_errorName(errorCode));
109                     errorCode=U_ZERO_ERROR;
110                     continue;
111                 }
112 
113                 cc.caseNr=i;
114 
115                 s=testCase->getString("charset", errorCode);
116                 s.extract(0, 0x7fffffff, charset, sizeof(charset), "");
117                 cc.charset=charset;
118 
119                 cc.bytes=testCase->getBinary(cc.bytesLength, "bytes", errorCode);
120                 unicode=testCase->getString("unicode", errorCode);
121                 cc.unicode=unicode.getBuffer();
122                 cc.unicodeLength=unicode.length();
123 
124                 offsetsLength=0;
125                 cc.offsets=testCase->getIntVector(offsetsLength, "offsets", errorCode);
126                 if(offsetsLength==0) {
127                     cc.offsets=NULL;
128                 } else if(offsetsLength!=unicode.length()) {
129                     errln("toUnicode[%d] unicode[%d] and offsets[%d] must have the same length",
130                             i, unicode.length(), offsetsLength);
131                     errorCode=U_ILLEGAL_ARGUMENT_ERROR;
132                 }
133 
134                 cc.finalFlush= 0!=testCase->getInt28("flush", errorCode);
135                 cc.fallbacks= 0!=testCase->getInt28("fallbacks", errorCode);
136 
137                 s=testCase->getString("errorCode", errorCode);
138                 if(s==UNICODE_STRING("invalid", 7)) {
139                     cc.outErrorCode=U_INVALID_CHAR_FOUND;
140                 } else if(s==UNICODE_STRING("illegal", 7)) {
141                     cc.outErrorCode=U_ILLEGAL_CHAR_FOUND;
142                 } else if(s==UNICODE_STRING("truncated", 9)) {
143                     cc.outErrorCode=U_TRUNCATED_CHAR_FOUND;
144                 } else if(s==UNICODE_STRING("illesc", 6)) {
145                     cc.outErrorCode=U_ILLEGAL_ESCAPE_SEQUENCE;
146                 } else if(s==UNICODE_STRING("unsuppesc", 9)) {
147                     cc.outErrorCode=U_UNSUPPORTED_ESCAPE_SEQUENCE;
148                 } else {
149                     cc.outErrorCode=U_ZERO_ERROR;
150                 }
151 
152                 s=testCase->getString("callback", errorCode);
153                 s.extract(0, 0x7fffffff, cbopt, sizeof(cbopt), "");
154                 cc.cbopt=cbopt;
155                 switch(cbopt[0]) {
156                 case SUB_CB:
157                     callback=UCNV_TO_U_CALLBACK_SUBSTITUTE;
158                     break;
159                 case SKIP_CB:
160                     callback=UCNV_TO_U_CALLBACK_SKIP;
161                     break;
162                 case STOP_CB:
163                     callback=UCNV_TO_U_CALLBACK_STOP;
164                     break;
165                 case ESC_CB:
166                     callback=UCNV_TO_U_CALLBACK_ESCAPE;
167                     break;
168                 default:
169                     callback=NULL;
170                     break;
171                 }
172                 option=callback==NULL ? cbopt : cbopt+1;
173                 if(*option==0) {
174                     option=NULL;
175                 }
176 
177                 cc.invalidChars=testCase->getBinary(cc.invalidLength, "invalidChars", errorCode);
178 
179                 if(U_FAILURE(errorCode)) {
180                     errln("error parsing conversion/toUnicode test case %d - %s",
181                             i, u_errorName(errorCode));
182                     errorCode=U_ZERO_ERROR;
183                 } else {
184                     logln("TestToUnicode[%d] %s", i, charset);
185                     ToUnicodeCase(cc, callback, option);
186                 }
187             }
188             delete testData;
189         }
190         delete dataModule;
191     }
192     else {
193         dataerrln("Could not load test conversion data");
194     }
195 }
196 
197 void
TestFromUnicode()198 ConversionTest::TestFromUnicode() {
199     ConversionCase cc;
200     char charset[100], cbopt[4];
201     const char *option;
202     UnicodeString s, unicode, invalidUChars;
203     int32_t offsetsLength, index;
204     UConverterFromUCallback callback;
205 
206     TestDataModule *dataModule;
207     TestData *testData;
208     const DataMap *testCase;
209     const UChar *p;
210     UErrorCode errorCode;
211     int32_t i, length;
212 
213     errorCode=U_ZERO_ERROR;
214     dataModule=TestDataModule::getTestDataModule("conversion", *this, errorCode);
215     if(U_SUCCESS(errorCode)) {
216         testData=dataModule->createTestData("fromUnicode", errorCode);
217         if(U_SUCCESS(errorCode)) {
218             for(i=0; testData->nextCase(testCase, errorCode); ++i) {
219                 if(U_FAILURE(errorCode)) {
220                     errln("error retrieving conversion/fromUnicode test case %d - %s",
221                             i, u_errorName(errorCode));
222                     errorCode=U_ZERO_ERROR;
223                     continue;
224                 }
225 
226                 cc.caseNr=i;
227 
228                 s=testCase->getString("charset", errorCode);
229                 s.extract(0, 0x7fffffff, charset, sizeof(charset), "");
230                 cc.charset=charset;
231 
232                 unicode=testCase->getString("unicode", errorCode);
233                 cc.unicode=unicode.getBuffer();
234                 cc.unicodeLength=unicode.length();
235                 cc.bytes=testCase->getBinary(cc.bytesLength, "bytes", errorCode);
236 
237                 offsetsLength=0;
238                 cc.offsets=testCase->getIntVector(offsetsLength, "offsets", errorCode);
239                 if(offsetsLength==0) {
240                     cc.offsets=NULL;
241                 } else if(offsetsLength!=cc.bytesLength) {
242                     errln("fromUnicode[%d] bytes[%d] and offsets[%d] must have the same length",
243                             i, cc.bytesLength, offsetsLength);
244                     errorCode=U_ILLEGAL_ARGUMENT_ERROR;
245                 }
246 
247                 cc.finalFlush= 0!=testCase->getInt28("flush", errorCode);
248                 cc.fallbacks= 0!=testCase->getInt28("fallbacks", errorCode);
249 
250                 s=testCase->getString("errorCode", errorCode);
251                 if(s==UNICODE_STRING("invalid", 7)) {
252                     cc.outErrorCode=U_INVALID_CHAR_FOUND;
253                 } else if(s==UNICODE_STRING("illegal", 7)) {
254                     cc.outErrorCode=U_ILLEGAL_CHAR_FOUND;
255                 } else if(s==UNICODE_STRING("truncated", 9)) {
256                     cc.outErrorCode=U_TRUNCATED_CHAR_FOUND;
257                 } else {
258                     cc.outErrorCode=U_ZERO_ERROR;
259                 }
260 
261                 s=testCase->getString("callback", errorCode);
262                 cc.setSub=0; // default: no subchar
263 
264                 if((index=s.indexOf((UChar)0))>0) {
265                     // read NUL-separated subchar first, if any
266                     // copy the subchar from Latin-1 characters
267                     // start after the NUL
268                     p=s.getTerminatedBuffer();
269                     length=index+1;
270                     p+=length;
271                     length=s.length()-length;
272                     if(length<=0 || length>=(int32_t)sizeof(cc.subchar)) {
273                         errorCode=U_ILLEGAL_ARGUMENT_ERROR;
274                     } else {
275                         int32_t j;
276 
277                         for(j=0; j<length; ++j) {
278                             cc.subchar[j]=(char)p[j];
279                         }
280                         // NUL-terminate the subchar
281                         cc.subchar[j]=0;
282                         cc.setSub=1;
283                     }
284 
285                     // remove the NUL and subchar from s
286                     s.truncate(index);
287                 } else if((index=s.indexOf((UChar)0x3d))>0) /* '=' */ {
288                     // read a substitution string, separated by an equal sign
289                     p=s.getBuffer()+index+1;
290                     length=s.length()-(index+1);
291                     if(length<0 || length>=UPRV_LENGTHOF(cc.subString)) {
292                         errorCode=U_ILLEGAL_ARGUMENT_ERROR;
293                     } else {
294                         u_memcpy(cc.subString, p, length);
295                         // NUL-terminate the subString
296                         cc.subString[length]=0;
297                         cc.setSub=-1;
298                     }
299 
300                     // remove the equal sign and subString from s
301                     s.truncate(index);
302                 }
303 
304                 s.extract(0, 0x7fffffff, cbopt, sizeof(cbopt), "");
305                 cc.cbopt=cbopt;
306                 switch(cbopt[0]) {
307                 case SUB_CB:
308                     callback=UCNV_FROM_U_CALLBACK_SUBSTITUTE;
309                     break;
310                 case SKIP_CB:
311                     callback=UCNV_FROM_U_CALLBACK_SKIP;
312                     break;
313                 case STOP_CB:
314                     callback=UCNV_FROM_U_CALLBACK_STOP;
315                     break;
316                 case ESC_CB:
317                     callback=UCNV_FROM_U_CALLBACK_ESCAPE;
318                     break;
319                 default:
320                     callback=NULL;
321                     break;
322                 }
323                 option=callback==NULL ? cbopt : cbopt+1;
324                 if(*option==0) {
325                     option=NULL;
326                 }
327 
328                 invalidUChars=testCase->getString("invalidUChars", errorCode);
329                 cc.invalidUChars=invalidUChars.getBuffer();
330                 cc.invalidLength=invalidUChars.length();
331 
332                 if(U_FAILURE(errorCode)) {
333                     errln("error parsing conversion/fromUnicode test case %d - %s",
334                             i, u_errorName(errorCode));
335                     errorCode=U_ZERO_ERROR;
336                 } else {
337                     logln("TestFromUnicode[%d] %s", i, charset);
338                     FromUnicodeCase(cc, callback, option);
339                 }
340             }
341             delete testData;
342         }
343         delete dataModule;
344     }
345     else {
346         dataerrln("Could not load test conversion data");
347     }
348 }
349 
350 static const UChar ellipsis[]={ 0x2e, 0x2e, 0x2e };
351 
352 void
TestGetUnicodeSet()353 ConversionTest::TestGetUnicodeSet() {
354     char charset[100];
355     UnicodeString s, map, mapnot;
356     int32_t which;
357 
358     ParsePosition pos;
359     UnicodeSet cnvSet, mapSet, mapnotSet, diffSet;
360     UnicodeSet *cnvSetPtr = &cnvSet;
361     LocalUConverterPointer cnv;
362 
363     TestDataModule *dataModule;
364     TestData *testData;
365     const DataMap *testCase;
366     UErrorCode errorCode;
367     int32_t i;
368 
369     errorCode=U_ZERO_ERROR;
370     dataModule=TestDataModule::getTestDataModule("conversion", *this, errorCode);
371     if(U_SUCCESS(errorCode)) {
372         testData=dataModule->createTestData("getUnicodeSet", errorCode);
373         if(U_SUCCESS(errorCode)) {
374             for(i=0; testData->nextCase(testCase, errorCode); ++i) {
375                 if(U_FAILURE(errorCode)) {
376                     errln("error retrieving conversion/getUnicodeSet test case %d - %s",
377                             i, u_errorName(errorCode));
378                     errorCode=U_ZERO_ERROR;
379                     continue;
380                 }
381 
382                 s=testCase->getString("charset", errorCode);
383                 s.extract(0, 0x7fffffff, charset, sizeof(charset), "");
384 
385                 map=testCase->getString("map", errorCode);
386                 mapnot=testCase->getString("mapnot", errorCode);
387 
388                 which=testCase->getInt28("which", errorCode);
389 
390                 if(U_FAILURE(errorCode)) {
391                     errln("error parsing conversion/getUnicodeSet test case %d - %s",
392                             i, u_errorName(errorCode));
393                     errorCode=U_ZERO_ERROR;
394                     continue;
395                 }
396 
397                 // test this test case
398                 mapSet.clear();
399                 mapnotSet.clear();
400 
401                 pos.setIndex(0);
402                 mapSet.applyPattern(map, pos, 0, NULL, errorCode);
403                 if(U_FAILURE(errorCode) || pos.getIndex()!=map.length()) {
404                     errln("error creating the map set for conversion/getUnicodeSet test case %d - %s\n"
405                           "    error index %d  index %d  U+%04x",
406                             i, u_errorName(errorCode), pos.getErrorIndex(), pos.getIndex(), map.char32At(pos.getIndex()));
407                     errorCode=U_ZERO_ERROR;
408                     continue;
409                 }
410 
411                 pos.setIndex(0);
412                 mapnotSet.applyPattern(mapnot, pos, 0, NULL, errorCode);
413                 if(U_FAILURE(errorCode) || pos.getIndex()!=mapnot.length()) {
414                     errln("error creating the mapnot set for conversion/getUnicodeSet test case %d - %s\n"
415                           "    error index %d  index %d  U+%04x",
416                             i, u_errorName(errorCode), pos.getErrorIndex(), pos.getIndex(), mapnot.char32At(pos.getIndex()));
417                     errorCode=U_ZERO_ERROR;
418                     continue;
419                 }
420 
421                 logln("TestGetUnicodeSet[%d] %s", i, charset);
422 
423                 cnv.adoptInstead(cnv_open(charset, errorCode));
424                 if(U_FAILURE(errorCode)) {
425                     errcheckln(errorCode, "error opening \"%s\" for conversion/getUnicodeSet test case %d - %s",
426                             charset, i, u_errorName(errorCode));
427                     errorCode=U_ZERO_ERROR;
428                     continue;
429                 }
430 
431                 ucnv_getUnicodeSet(cnv.getAlias(), cnvSetPtr->toUSet(), (UConverterUnicodeSet)which, &errorCode);
432 
433                 if(U_FAILURE(errorCode)) {
434                     errln("error in ucnv_getUnicodeSet(\"%s\") for conversion/getUnicodeSet test case %d - %s",
435                             charset, i, u_errorName(errorCode));
436                     errorCode=U_ZERO_ERROR;
437                     continue;
438                 }
439 
440                 // are there items that must be in cnvSet but are not?
441                 (diffSet=mapSet).removeAll(cnvSet);
442                 if(!diffSet.isEmpty()) {
443                     diffSet.toPattern(s, TRUE);
444                     if(s.length()>100) {
445                         s.replace(100, 0x7fffffff, ellipsis, UPRV_LENGTHOF(ellipsis));
446                     }
447                     errln("error: ucnv_getUnicodeSet(\"%s\") is missing items - conversion/getUnicodeSet test case %d",
448                             charset, i);
449                     errln(s);
450                 }
451 
452                 // are there items that must not be in cnvSet but are?
453                 (diffSet=mapnotSet).retainAll(cnvSet);
454                 if(!diffSet.isEmpty()) {
455                     diffSet.toPattern(s, TRUE);
456                     if(s.length()>100) {
457                         s.replace(100, 0x7fffffff, ellipsis, UPRV_LENGTHOF(ellipsis));
458                     }
459                     errln("error: ucnv_getUnicodeSet(\"%s\") contains unexpected items - conversion/getUnicodeSet test case %d",
460                             charset, i);
461                     errln(s);
462                 }
463             }
464             delete testData;
465         }
466         delete dataModule;
467     }
468     else {
469         dataerrln("Could not load test conversion data");
470     }
471 }
472 
473 U_CDECL_BEGIN
474 static void U_CALLCONV
getUnicodeSetCallback(const void * context,UConverterFromUnicodeArgs *,const UChar *,int32_t,UChar32 codePoint,UConverterCallbackReason reason,UErrorCode * pErrorCode)475 getUnicodeSetCallback(const void *context,
476                       UConverterFromUnicodeArgs * /*fromUArgs*/,
477                       const UChar* /*codeUnits*/,
478                       int32_t /*length*/,
479                       UChar32 codePoint,
480                       UConverterCallbackReason reason,
481                       UErrorCode *pErrorCode) {
482     if(reason<=UCNV_IRREGULAR) {
483         ((UnicodeSet *)context)->remove(codePoint);  // the converter cannot convert this code point
484         *pErrorCode=U_ZERO_ERROR;                    // skip
485     }  // else ignore the reset, close and clone calls.
486 }
487 U_CDECL_END
488 
489 // Compare ucnv_getUnicodeSet() with the set of characters that can be converted.
490 void
TestGetUnicodeSet2()491 ConversionTest::TestGetUnicodeSet2() {
492     // Build a string with all code points.
493     UChar32 cpLimit;
494     int32_t s0Length;
495     if(quick) {
496         cpLimit=s0Length=0x10000;  // BMP only
497     } else {
498         cpLimit=0x110000;
499         s0Length=0x10000+0x200000;  // BMP + surrogate pairs
500     }
501     UChar *s0=new UChar[s0Length];
502     if(s0==NULL) {
503         return;
504     }
505     UChar *s=s0;
506     UChar32 c;
507     UChar c2;
508     // low BMP
509     for(c=0; c<=0xd7ff; ++c) {
510         *s++=(UChar)c;
511     }
512     // trail surrogates
513     for(c=0xdc00; c<=0xdfff; ++c) {
514         *s++=(UChar)c;
515     }
516     // lead surrogates
517     // (after trails so that there is not even one surrogate pair in between)
518     for(c=0xd800; c<=0xdbff; ++c) {
519         *s++=(UChar)c;
520     }
521     // high BMP
522     for(c=0xe000; c<=0xffff; ++c) {
523         *s++=(UChar)c;
524     }
525     // supplementary code points = surrogate pairs
526     if(cpLimit==0x110000) {
527         for(c=0xd800; c<=0xdbff; ++c) {
528             for(c2=0xdc00; c2<=0xdfff; ++c2) {
529                 *s++=(UChar)c;
530                 *s++=c2;
531             }
532         }
533     }
534 
535     static const char *const cnvNames[]={
536         "UTF-8",
537         "UTF-7",
538         "UTF-16",
539         "US-ASCII",
540         "ISO-8859-1",
541         "windows-1252",
542         "Shift-JIS",
543         "ibm-1390",  // EBCDIC_STATEFUL table
544         "ibm-16684",  // DBCS-only extension table based on EBCDIC_STATEFUL table
545         "HZ",
546         "ISO-2022-JP",
547         "JIS7",
548         "ISO-2022-CN",
549         "ISO-2022-CN-EXT",
550         "LMBCS"
551     };
552     LocalUConverterPointer cnv;
553     char buffer[1024];
554     int32_t i;
555     for(i=0; i<UPRV_LENGTHOF(cnvNames); ++i) {
556         UErrorCode errorCode=U_ZERO_ERROR;
557         cnv.adoptInstead(cnv_open(cnvNames[i], errorCode));
558         if(U_FAILURE(errorCode)) {
559             errcheckln(errorCode, "failed to open converter %s - %s", cnvNames[i], u_errorName(errorCode));
560             continue;
561         }
562         UnicodeSet expected;
563         ucnv_setFromUCallBack(cnv.getAlias(), getUnicodeSetCallback, &expected, NULL, NULL, &errorCode);
564         if(U_FAILURE(errorCode)) {
565             errln("failed to set the callback on converter %s - %s", cnvNames[i], u_errorName(errorCode));
566             continue;
567         }
568         UConverterUnicodeSet which;
569         for(which=UCNV_ROUNDTRIP_SET; which<UCNV_SET_COUNT; which=(UConverterUnicodeSet)((int)which+1)) {
570             if(which==UCNV_ROUNDTRIP_AND_FALLBACK_SET) {
571                 ucnv_setFallback(cnv.getAlias(), TRUE);
572             }
573             expected.add(0, cpLimit-1);
574             s=s0;
575             UBool flush;
576             do {
577                 char *t=buffer;
578                 flush=(UBool)(s==s0+s0Length);
579                 ucnv_fromUnicode(cnv.getAlias(), &t, buffer+sizeof(buffer), (const UChar **)&s, s0+s0Length, NULL, flush, &errorCode);
580                 if(U_FAILURE(errorCode)) {
581                     if(errorCode==U_BUFFER_OVERFLOW_ERROR) {
582                         errorCode=U_ZERO_ERROR;
583                         continue;
584                     } else {
585                         break;  // unexpected error, should not occur
586                     }
587                 }
588             } while(!flush);
589             UnicodeSet set;
590             ucnv_getUnicodeSet(cnv.getAlias(), set.toUSet(), which, &errorCode);
591             if(cpLimit<0x110000) {
592                 set.remove(cpLimit, 0x10ffff);
593             }
594             if(which==UCNV_ROUNDTRIP_SET) {
595                 // ignore PUA code points because they will be converted even if they
596                 // are fallbacks and when other fallbacks are turned off,
597                 // but ucnv_getUnicodeSet(UCNV_ROUNDTRIP_SET) delivers true roundtrips
598                 expected.remove(0xe000, 0xf8ff);
599                 expected.remove(0xf0000, 0xffffd);
600                 expected.remove(0x100000, 0x10fffd);
601                 set.remove(0xe000, 0xf8ff);
602                 set.remove(0xf0000, 0xffffd);
603                 set.remove(0x100000, 0x10fffd);
604             }
605             if(set!=expected) {
606                 // First try to see if we have different sets because ucnv_getUnicodeSet()
607                 // added strings: The above conversion method does not tell us what strings might be convertible.
608                 // Remove strings from the set and compare again.
609                 set.removeAllStrings();
610             }
611             if(set!=expected) {
612                 UnicodeSet diffSet;
613                 UnicodeString out;
614 
615                 // are there items that must be in the set but are not?
616                 (diffSet=expected).removeAll(set);
617                 if(!diffSet.isEmpty()) {
618                     diffSet.toPattern(out, TRUE);
619                     if(out.length()>100) {
620                         out.replace(100, 0x7fffffff, ellipsis, UPRV_LENGTHOF(ellipsis));
621                     }
622                     errln("error: ucnv_getUnicodeSet(\"%s\") is missing items - which set: %d",
623                             cnvNames[i], which);
624                     errln(out);
625                 }
626 
627                 // are there items that must not be in the set but are?
628                 (diffSet=set).removeAll(expected);
629                 if(!diffSet.isEmpty()) {
630                     diffSet.toPattern(out, TRUE);
631                     if(out.length()>100) {
632                         out.replace(100, 0x7fffffff, ellipsis, UPRV_LENGTHOF(ellipsis));
633                     }
634                     errln("error: ucnv_getUnicodeSet(\"%s\") contains unexpected items - which set: %d",
635                             cnvNames[i], which);
636                     errln(out);
637                 }
638             }
639         }
640     }
641 
642     delete [] s0;
643 }
644 
645 // Test all codepoints which has the default ignorable Unicode property are ignored if they have no mapping
646 // If there are any failures, the hard coded list (IS_DEFAULT_IGNORABLE_CODE_POINT) in ucnv_err.c should be updated
647 void
TestDefaultIgnorableCallback()648 ConversionTest::TestDefaultIgnorableCallback() {
649     UErrorCode status = U_ZERO_ERROR;
650     const char *cnv_name = "euc-jp-2007";
651     const char *pattern_ignorable = "[:Default_Ignorable_Code_Point:]";
652     const char *pattern_not_ignorable = "[:^Default_Ignorable_Code_Point:]";
653 
654     LocalPointer<UnicodeSet> set_ignorable(new UnicodeSet(pattern_ignorable, status));
655     if (U_FAILURE(status)) {
656         dataerrln("Unable to create Unicodeset: %s - %s\n", pattern_ignorable, u_errorName(status));
657         return;
658     }
659 
660     LocalPointer<UnicodeSet> set_not_ignorable(new UnicodeSet(pattern_not_ignorable, status));
661     if (U_FAILURE(status)) {
662         dataerrln("Unable to create Unicodeset: %s - %s\n", pattern_not_ignorable, u_errorName(status));
663         return;
664     }
665 
666     LocalUConverterPointer cnv(cnv_open(cnv_name, status));
667     if (U_FAILURE(status)) {
668         dataerrln("Unable to open converter: %s - %s\n", cnv_name, u_errorName(status));
669         return;
670     }
671 
672     // set callback for the converter
673     ucnv_setFromUCallBack(cnv.getAlias(), UCNV_FROM_U_CALLBACK_SUBSTITUTE, NULL, NULL, NULL, &status);
674 
675     UChar32 input[1];
676     char output[10];
677     int32_t outputLength;
678 
679     // test default ignorables are ignored
680     int size = set_ignorable->size();
681     for (int i = 0; i < size; i++) {
682         status = U_ZERO_ERROR;
683         outputLength= 0;
684 
685         input[0] = set_ignorable->charAt(i);
686 
687         outputLength = ucnv_fromUChars(cnv.getAlias(), output, 10, UnicodeString::fromUTF32(input, 1).getTerminatedBuffer(), -1, &status);
688         if (U_FAILURE(status) || outputLength != 0) {
689             errln("Ignorable code point: U+%04X not skipped as expected - %s", input[0], u_errorName(status));
690         }
691     }
692 
693     // test non-ignorables are not ignored
694     size = set_not_ignorable->size();
695     for (int i = 0; i < size; i++) {
696         status = U_ZERO_ERROR;
697         outputLength= 0;
698 
699         input[0] = set_not_ignorable->charAt(i);
700 
701         if (input[0] == 0) {
702             continue;
703         }
704 
705         outputLength = ucnv_fromUChars(cnv.getAlias(), output, 10, UnicodeString::fromUTF32(input, 1).getTerminatedBuffer(), -1, &status);
706         if (U_FAILURE(status) || outputLength <= 0) {
707             errln("Non-ignorable code point: U+%04X skipped unexpectedly - %s", input[0], u_errorName(status));
708         }
709     }
710 }
711 
712 void
TestUTF8ToUTF8Overflow()713 ConversionTest::TestUTF8ToUTF8Overflow() {
714     IcuTestErrorCode errorCode(*this, "TestUTF8ToUTF8Overflow");
715     LocalUConverterPointer cnv1(ucnv_open("UTF-8", errorCode));
716     LocalUConverterPointer cnv2(ucnv_open("UTF-8", errorCode));
717     static const char *text = "aä";  // ä: 2 bytes
718     const char *source = text;
719     const char *sourceLimit = text + strlen(text);
720     char result[20];
721     char *target = result;
722     const char *targetLimit = result + sizeof(result);
723     UChar buffer16[20];
724     UChar *pivotSource = buffer16;
725     UChar *pivotTarget = buffer16;
726     const UChar *pivotLimit = buffer16 + UPRV_LENGTHOF(buffer16);
727     int32_t length;
728 
729     // Convert with insufficient target capacity.
730     result[2] = 5;
731     ucnv_convertEx(cnv2.getAlias(), cnv1.getAlias(),
732                    &target, result + 2, &source, sourceLimit,
733                    buffer16, &pivotSource, &pivotTarget, pivotLimit,
734                    FALSE, FALSE, errorCode);
735     assertEquals("overflow", U_BUFFER_OVERFLOW_ERROR, errorCode.reset());
736     length = (int32_t)(target - result);
737     assertEquals("number of bytes written", 2, length);
738     assertEquals("next byte not clobbered", 5, result[2]);
739 
740     // Convert the rest and flush.
741     ucnv_convertEx(cnv2.getAlias(), cnv1.getAlias(),
742                    &target, targetLimit, &source, sourceLimit,
743                    buffer16, &pivotSource, &pivotTarget, pivotLimit,
744                    FALSE, TRUE, errorCode);
745 
746     assertSuccess("UTF-8->UTF-8", errorCode);
747     length = (int32_t)(target - result);
748     assertEquals("3 bytes", 3, length);
749     if (length == 3) {
750         assertTrue("result same as input", memcmp(text, result, length) == 0);
751     }
752 
753     ucnv_reset(cnv1.getAlias());
754     ucnv_reset(cnv2.getAlias());
755     memset(result, 0, sizeof(result));
756     static const char *text2 = "a��";  // U+1F6B2 bicycle: 4 bytes
757     source = text2;
758     sourceLimit = text2 + strlen(text2);
759     target = result;
760     pivotSource = pivotTarget = buffer16;
761 
762     // Convert with insufficient target capacity.
763     result[3] = 5;
764     ucnv_convertEx(cnv2.getAlias(), cnv1.getAlias(),
765                    &target, result + 3, &source, sourceLimit,
766                    buffer16, &pivotSource, &pivotTarget, pivotLimit,
767                    FALSE, FALSE, errorCode);
768     assertEquals("text2 overflow", U_BUFFER_OVERFLOW_ERROR, errorCode.reset());
769     length = (int32_t)(target - result);
770     assertEquals("text2 number of bytes written", 3, length);
771     assertEquals("text2 next byte not clobbered", 5, result[3]);
772 
773     // Convert the rest and flush.
774     ucnv_convertEx(cnv2.getAlias(), cnv1.getAlias(),
775                    &target, targetLimit, &source, sourceLimit,
776                    buffer16, &pivotSource, &pivotTarget, pivotLimit,
777                    FALSE, TRUE, errorCode);
778 
779     assertSuccess("text2 UTF-8->UTF-8", errorCode);
780     length = (int32_t)(target - result);
781     assertEquals("text2 5 bytes", 5, length);
782     if (length == 5) {
783         assertTrue("text2 result same as input", memcmp(text2, result, length) == 0);
784     }
785 
786     ucnv_reset(cnv1.getAlias());
787     ucnv_reset(cnv2.getAlias());
788     memset(result, 0, sizeof(result));
789     static const char *illFormed = "\xf1\x91\x93\x96\x91\x94";  // U+514D6 + two more trail bytes
790     source = illFormed;
791     sourceLimit = illFormed + strlen(illFormed);
792     target = result;
793     pivotSource = pivotTarget = buffer16;
794 
795     ucnv_setToUCallBack(cnv1.getAlias(), UCNV_TO_U_CALLBACK_STOP, nullptr, nullptr, nullptr, errorCode);
796 
797     // Convert only two bytes and flush (but expect failure).
798     char errorBytes[10];
799     int8_t errorLength;
800     result[0] = 5;
801     ucnv_convertEx(cnv2.getAlias(), cnv1.getAlias(),
802                    &target, targetLimit, &source, source + 2,
803                    buffer16, &pivotSource, &pivotTarget, pivotLimit,
804                    FALSE, TRUE, errorCode);
805     assertEquals("illFormed truncated", U_TRUNCATED_CHAR_FOUND, errorCode.reset());
806     length = (int32_t)(target - result);
807     assertEquals("illFormed number of bytes written", 0, length);
808     errorLength = UPRV_LENGTHOF(errorBytes);
809     ucnv_getInvalidChars(cnv1.getAlias(), errorBytes, &errorLength, errorCode);
810     assertEquals("illFormed truncated errorLength", 2, (int32_t)errorLength);
811     if (errorLength == 2) {
812         assertEquals("illFormed truncated errorBytes", 0xf191,
813                      ((int32_t)(uint8_t)errorBytes[0] << 8) | (uint8_t)errorBytes[1]);
814     }
815 
816     // Continue conversion starting with a trail byte.
817     ucnv_convertEx(cnv2.getAlias(), cnv1.getAlias(),
818                    &target, targetLimit, &source, sourceLimit,
819                    buffer16, &pivotSource, &pivotTarget, pivotLimit,
820                    FALSE, TRUE, errorCode);
821 
822     assertEquals("illFormed trail byte", U_ILLEGAL_CHAR_FOUND, errorCode.reset());
823     length = (int32_t)(target - result);
824     assertEquals("illFormed trail byte number of bytes written", 0, length);
825     errorLength = UPRV_LENGTHOF(errorBytes);
826     ucnv_getInvalidChars(cnv1.getAlias(), errorBytes, &errorLength, errorCode);
827     assertEquals("illFormed trail byte errorLength", 1, (int32_t)errorLength);
828     if (errorLength == 1) {
829         assertEquals("illFormed trail byte errorBytes", 0x93, (int32_t)(uint8_t)errorBytes[0]);
830     }
831 }
832 
833 // open testdata or ICU data converter ------------------------------------- ***
834 
835 UConverter *
cnv_open(const char * name,UErrorCode & errorCode)836 ConversionTest::cnv_open(const char *name, UErrorCode &errorCode) {
837     if(name!=NULL && *name=='+') {
838         // Converter names that start with '+' are ignored in ICU4J tests.
839         ++name;
840     }
841     if(name!=NULL && *name=='*') {
842         /* loadTestData(): set the data directory */
843         return ucnv_openPackage(loadTestData(errorCode), name+1, &errorCode);
844     } else {
845         return ucnv_open(name, &errorCode);
846     }
847 }
848 
849 // output helpers ---------------------------------------------------------- ***
850 
851 static inline char
hexDigit(uint8_t digit)852 hexDigit(uint8_t digit) {
853     return digit<=9 ? (char)('0'+digit) : (char)('a'-10+digit);
854 }
855 
856 static char *
printBytes(const uint8_t * bytes,int32_t length,char * out)857 printBytes(const uint8_t *bytes, int32_t length, char *out) {
858     uint8_t b;
859 
860     if(length>0) {
861         b=*bytes++;
862         --length;
863         *out++=hexDigit((uint8_t)(b>>4));
864         *out++=hexDigit((uint8_t)(b&0xf));
865     }
866 
867     while(length>0) {
868         b=*bytes++;
869         --length;
870         *out++=' ';
871         *out++=hexDigit((uint8_t)(b>>4));
872         *out++=hexDigit((uint8_t)(b&0xf));
873     }
874     *out++=0;
875     return out;
876 }
877 
878 static char *
printUnicode(const UChar * unicode,int32_t length,char * out)879 printUnicode(const UChar *unicode, int32_t length, char *out) {
880     UChar32 c;
881     int32_t i;
882 
883     for(i=0; i<length;) {
884         if(i>0) {
885             *out++=' ';
886         }
887         U16_NEXT(unicode, i, length, c);
888         // write 4..6 digits
889         if(c>=0x100000) {
890             *out++='1';
891         }
892         if(c>=0x10000) {
893             *out++=hexDigit((uint8_t)((c>>16)&0xf));
894         }
895         *out++=hexDigit((uint8_t)((c>>12)&0xf));
896         *out++=hexDigit((uint8_t)((c>>8)&0xf));
897         *out++=hexDigit((uint8_t)((c>>4)&0xf));
898         *out++=hexDigit((uint8_t)(c&0xf));
899     }
900     *out++=0;
901     return out;
902 }
903 
904 static char *
printOffsets(const int32_t * offsets,int32_t length,char * out)905 printOffsets(const int32_t *offsets, int32_t length, char *out) {
906     int32_t i, o, d;
907 
908     if(offsets==NULL) {
909         length=0;
910     }
911 
912     for(i=0; i<length; ++i) {
913         if(i>0) {
914             *out++=' ';
915         }
916         o=offsets[i];
917 
918         // print all offsets with 2 characters each (-x, -9..99, xx)
919         if(o<-9) {
920             *out++='-';
921             *out++='x';
922         } else if(o<0) {
923             *out++='-';
924             *out++=(char)('0'-o);
925         } else if(o<=99) {
926             *out++=(d=o/10)==0 ? ' ' : (char)('0'+d);
927             *out++=(char)('0'+o%10);
928         } else /* o>99 */ {
929             *out++='x';
930             *out++='x';
931         }
932     }
933     *out++=0;
934     return out;
935 }
936 
937 // toUnicode test worker functions ----------------------------------------- ***
938 
939 static int32_t
stepToUnicode(ConversionCase & cc,UConverter * cnv,UChar * result,int32_t resultCapacity,int32_t * resultOffsets,int32_t step,UErrorCode * pErrorCode)940 stepToUnicode(ConversionCase &cc, UConverter *cnv,
941               UChar *result, int32_t resultCapacity,
942               int32_t *resultOffsets, /* also resultCapacity */
943               int32_t step,
944               UErrorCode *pErrorCode) {
945     const char *source, *sourceLimit, *bytesLimit;
946     UChar *target, *targetLimit, *resultLimit;
947     UBool flush;
948 
949     source=(const char *)cc.bytes;
950     target=result;
951     bytesLimit=source+cc.bytesLength;
952     resultLimit=result+resultCapacity;
953 
954     if(step>=0) {
955         // call ucnv_toUnicode() with in/out buffers no larger than (step) at a time
956         // move only one buffer (in vs. out) at a time to be extra mean
957         // step==0 performs bulk conversion and generates offsets
958 
959         // initialize the partial limits for the loop
960         if(step==0) {
961             // use the entire buffers
962             sourceLimit=bytesLimit;
963             targetLimit=resultLimit;
964             flush=cc.finalFlush;
965         } else {
966             // start with empty partial buffers
967             sourceLimit=source;
968             targetLimit=target;
969             flush=FALSE;
970 
971             // output offsets only for bulk conversion
972             resultOffsets=NULL;
973         }
974 
975         for(;;) {
976             // resetting the opposite conversion direction must not affect this one
977             ucnv_resetFromUnicode(cnv);
978 
979             // convert
980             ucnv_toUnicode(cnv,
981                 &target, targetLimit,
982                 &source, sourceLimit,
983                 resultOffsets,
984                 flush, pErrorCode);
985 
986             // check pointers and errors
987             if(source>sourceLimit || target>targetLimit) {
988                 *pErrorCode=U_INTERNAL_PROGRAM_ERROR;
989                 break;
990             } else if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) {
991                 if(target!=targetLimit) {
992                     // buffer overflow must only be set when the target is filled
993                     *pErrorCode=U_INTERNAL_PROGRAM_ERROR;
994                     break;
995                 } else if(targetLimit==resultLimit) {
996                     // not just a partial overflow
997                     break;
998                 }
999 
1000                 // the partial target is filled, set a new limit, reset the error and continue
1001                 targetLimit=(resultLimit-target)>=step ? target+step : resultLimit;
1002                 *pErrorCode=U_ZERO_ERROR;
1003             } else if(U_FAILURE(*pErrorCode)) {
1004                 // some other error occurred, done
1005                 break;
1006             } else {
1007                 if(source!=sourceLimit) {
1008                     // when no error occurs, then the input must be consumed
1009                     *pErrorCode=U_INTERNAL_PROGRAM_ERROR;
1010                     break;
1011                 }
1012 
1013                 if(sourceLimit==bytesLimit) {
1014                     // we are done
1015                     break;
1016                 }
1017 
1018                 // the partial conversion succeeded, set a new limit and continue
1019                 sourceLimit=(bytesLimit-source)>=step ? source+step : bytesLimit;
1020                 flush=(UBool)(cc.finalFlush && sourceLimit==bytesLimit);
1021             }
1022         }
1023     } else /* step<0 */ {
1024         /*
1025          * step==-1: call only ucnv_getNextUChar()
1026          * otherwise alternate between ucnv_toUnicode() and ucnv_getNextUChar()
1027          *   if step==-2 or -3, then give ucnv_toUnicode() the whole remaining input,
1028          *   else give it at most (-step-2)/2 bytes
1029          */
1030         UChar32 c;
1031 
1032         // end the loop by getting an index out of bounds error
1033         for(;;) {
1034             // resetting the opposite conversion direction must not affect this one
1035             ucnv_resetFromUnicode(cnv);
1036 
1037             // convert
1038             if((step&1)!=0 /* odd: -1, -3, -5, ... */) {
1039                 sourceLimit=source; // use sourceLimit not as a real limit
1040                                     // but to remember the pre-getNextUChar source pointer
1041                 c=ucnv_getNextUChar(cnv, &source, bytesLimit, pErrorCode);
1042 
1043                 // check pointers and errors
1044                 if(*pErrorCode==U_INDEX_OUTOFBOUNDS_ERROR) {
1045                     if(source!=bytesLimit) {
1046                         *pErrorCode=U_INTERNAL_PROGRAM_ERROR;
1047                     } else {
1048                         *pErrorCode=U_ZERO_ERROR;
1049                     }
1050                     break;
1051                 } else if(U_FAILURE(*pErrorCode)) {
1052                     break;
1053                 }
1054                 // source may not move if c is from previous overflow
1055 
1056                 if(target==resultLimit) {
1057                     *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1058                     break;
1059                 }
1060                 if(c<=0xffff) {
1061                     *target++=(UChar)c;
1062                 } else {
1063                     *target++=U16_LEAD(c);
1064                     if(target==resultLimit) {
1065                         *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1066                         break;
1067                     }
1068                     *target++=U16_TRAIL(c);
1069                 }
1070 
1071                 // alternate between -n-1 and -n but leave -1 alone
1072                 if(step<-1) {
1073                     ++step;
1074                 }
1075             } else /* step is even */ {
1076                 // allow only one UChar output
1077                 targetLimit=target<resultLimit ? target+1 : resultLimit;
1078 
1079                 // as with ucnv_getNextUChar(), we always flush (if we go to bytesLimit)
1080                 // and never output offsets
1081                 if(step==-2) {
1082                     sourceLimit=bytesLimit;
1083                 } else {
1084                     sourceLimit=source+(-step-2)/2;
1085                     if(sourceLimit>bytesLimit) {
1086                         sourceLimit=bytesLimit;
1087                     }
1088                 }
1089 
1090                 ucnv_toUnicode(cnv,
1091                     &target, targetLimit,
1092                     &source, sourceLimit,
1093                     NULL, (UBool)(sourceLimit==bytesLimit), pErrorCode);
1094 
1095                 // check pointers and errors
1096                 if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) {
1097                     if(target!=targetLimit) {
1098                         // buffer overflow must only be set when the target is filled
1099                         *pErrorCode=U_INTERNAL_PROGRAM_ERROR;
1100                         break;
1101                     } else if(targetLimit==resultLimit) {
1102                         // not just a partial overflow
1103                         break;
1104                     }
1105 
1106                     // the partial target is filled, set a new limit and continue
1107                     *pErrorCode=U_ZERO_ERROR;
1108                 } else if(U_FAILURE(*pErrorCode)) {
1109                     // some other error occurred, done
1110                     break;
1111                 } else {
1112                     if(source!=sourceLimit) {
1113                         // when no error occurs, then the input must be consumed
1114                         *pErrorCode=U_INTERNAL_PROGRAM_ERROR;
1115                         break;
1116                     }
1117 
1118                     // we are done (flush==TRUE) but we continue, to get the index out of bounds error above
1119                 }
1120 
1121                 --step;
1122             }
1123         }
1124     }
1125 
1126     return (int32_t)(target-result);
1127 }
1128 
1129 UBool
ToUnicodeCase(ConversionCase & cc,UConverterToUCallback callback,const char * option)1130 ConversionTest::ToUnicodeCase(ConversionCase &cc, UConverterToUCallback callback, const char *option) {
1131     // open the converter
1132     IcuTestErrorCode errorCode(*this, "ToUnicodeCase");
1133     LocalUConverterPointer cnv(cnv_open(cc.charset, errorCode));
1134     // with no data, the above crashes with "pointer being freed was not allocated" for charset "x11-compound-text", see #13078
1135     if(errorCode.isFailure()) {
1136         errcheckln(errorCode, "toUnicode[%d](%s cb=\"%s\" fb=%d flush=%d) ucnv_open() failed - %s",
1137                 cc.caseNr, cc.charset, cc.cbopt, cc.fallbacks, cc.finalFlush, errorCode.errorName());
1138         errorCode.reset();
1139         return FALSE;
1140     }
1141 
1142     // set the callback
1143     if(callback!=NULL) {
1144         ucnv_setToUCallBack(cnv.getAlias(), callback, option, NULL, NULL, errorCode);
1145         if(U_FAILURE(errorCode)) {
1146             errln("toUnicode[%d](%s cb=\"%s\" fb=%d flush=%d) ucnv_setToUCallBack() failed - %s",
1147                     cc.caseNr, cc.charset, cc.cbopt, cc.fallbacks, cc.finalFlush, u_errorName(errorCode));
1148             return FALSE;
1149         }
1150     }
1151 
1152     int32_t resultOffsets[256];
1153     UChar result[256];
1154     int32_t resultLength;
1155     UBool ok;
1156 
1157     static const struct {
1158         int32_t step;
1159         const char *name;
1160     } steps[]={
1161         { 0, "bulk" }, // must be first for offsets to be checked
1162         { 1, "step=1" },
1163         { 3, "step=3" },
1164         { 7, "step=7" },
1165         { -1, "getNext" },
1166         { -2, "toU(bulk)+getNext" },
1167         { -3, "getNext+toU(bulk)" },
1168         { -4, "toU(1)+getNext" },
1169         { -5, "getNext+toU(1)" },
1170         { -12, "toU(5)+getNext" },
1171         { -13, "getNext+toU(5)" },
1172     };
1173     int32_t i, step;
1174 
1175     ok=TRUE;
1176     for(i=0; i<UPRV_LENGTHOF(steps) && ok; ++i) {
1177         step=steps[i].step;
1178         if(step<0 && !cc.finalFlush) {
1179             // skip ucnv_getNextUChar() if !finalFlush because
1180             // ucnv_getNextUChar() always implies flush
1181             continue;
1182         }
1183         if(step!=0) {
1184             // bulk test is first, then offsets are not checked any more
1185             cc.offsets=NULL;
1186         }
1187         else {
1188             for (int32_t i = 0; i < UPRV_LENGTHOF(resultOffsets); i++) {
1189                 resultOffsets[i] = -1;
1190             }
1191         }
1192         for (int32_t i = 0; i < UPRV_LENGTHOF(result); i++) {
1193             result[i] = -1;
1194         }
1195         errorCode.reset();
1196         resultLength=stepToUnicode(cc, cnv.getAlias(),
1197                                 result, UPRV_LENGTHOF(result),
1198                                 step==0 ? resultOffsets : NULL,
1199                                 step, errorCode);
1200         ok=checkToUnicode(
1201                 cc, cnv.getAlias(), steps[i].name,
1202                 result, resultLength,
1203                 cc.offsets!=NULL ? resultOffsets : NULL,
1204                 errorCode);
1205         if(errorCode.isFailure() || !cc.finalFlush) {
1206             // reset if an error occurred or we did not flush
1207             // otherwise do nothing to make sure that flushing resets
1208             ucnv_resetToUnicode(cnv.getAlias());
1209         }
1210         if (cc.offsets != NULL && resultOffsets[resultLength] != -1) {
1211             errln("toUnicode[%d](%s) Conversion wrote too much to offsets at index %d",
1212                 cc.caseNr, cc.charset, resultLength);
1213         }
1214         if (result[resultLength] != (UChar)-1) {
1215             errln("toUnicode[%d](%s) Conversion wrote too much to result at index %d",
1216                 cc.caseNr, cc.charset, resultLength);
1217         }
1218     }
1219 
1220     // not a real loop, just a convenience for breaking out of the block
1221     while(ok && cc.finalFlush) {
1222         // test ucnv_toUChars()
1223         memset(result, 0, sizeof(result));
1224 
1225         errorCode.reset();
1226         resultLength=ucnv_toUChars(cnv.getAlias(),
1227                         result, UPRV_LENGTHOF(result),
1228                         (const char *)cc.bytes, cc.bytesLength,
1229                         errorCode);
1230         ok=checkToUnicode(
1231                 cc, cnv.getAlias(), "toUChars",
1232                 result, resultLength,
1233                 NULL,
1234                 errorCode);
1235         if(!ok) {
1236             break;
1237         }
1238 
1239         // test preflighting
1240         // keep the correct result for simple checking
1241         errorCode.reset();
1242         resultLength=ucnv_toUChars(cnv.getAlias(),
1243                         NULL, 0,
1244                         (const char *)cc.bytes, cc.bytesLength,
1245                         errorCode);
1246         if(errorCode.get()==U_STRING_NOT_TERMINATED_WARNING || errorCode.get()==U_BUFFER_OVERFLOW_ERROR) {
1247             errorCode.reset();
1248         }
1249         ok=checkToUnicode(
1250                 cc, cnv.getAlias(), "preflight toUChars",
1251                 result, resultLength,
1252                 NULL,
1253                 errorCode);
1254         break;
1255     }
1256 
1257     errorCode.reset();  // all errors have already been reported
1258     return ok;
1259 }
1260 
1261 UBool
checkToUnicode(ConversionCase & cc,UConverter * cnv,const char * name,const UChar * result,int32_t resultLength,const int32_t * resultOffsets,UErrorCode resultErrorCode)1262 ConversionTest::checkToUnicode(ConversionCase &cc, UConverter *cnv, const char *name,
1263                                const UChar *result, int32_t resultLength,
1264                                const int32_t *resultOffsets,
1265                                UErrorCode resultErrorCode) {
1266     char resultInvalidChars[8];
1267     int8_t resultInvalidLength;
1268     UErrorCode errorCode;
1269 
1270     const char *msg;
1271 
1272     // reset the message; NULL will mean "ok"
1273     msg=NULL;
1274 
1275     errorCode=U_ZERO_ERROR;
1276     resultInvalidLength=sizeof(resultInvalidChars);
1277     ucnv_getInvalidChars(cnv, resultInvalidChars, &resultInvalidLength, &errorCode);
1278     if(U_FAILURE(errorCode)) {
1279         errln("toUnicode[%d](%s cb=\"%s\" fb=%d flush=%d %s) ucnv_getInvalidChars() failed - %s",
1280                 cc.caseNr, cc.charset, cc.cbopt, cc.fallbacks, cc.finalFlush, name, u_errorName(errorCode));
1281         return FALSE;
1282     }
1283 
1284     // check everything that might have gone wrong
1285     if(cc.unicodeLength!=resultLength) {
1286         msg="wrong result length";
1287     } else if(0!=u_memcmp(cc.unicode, result, cc.unicodeLength)) {
1288         msg="wrong result string";
1289     } else if(cc.offsets!=NULL && 0!=memcmp(cc.offsets, resultOffsets, cc.unicodeLength*sizeof(*cc.offsets))) {
1290         msg="wrong offsets";
1291     } else if(cc.outErrorCode!=resultErrorCode) {
1292         msg="wrong error code";
1293     } else if(cc.invalidLength!=resultInvalidLength) {
1294         msg="wrong length of last invalid input";
1295     } else if(0!=memcmp(cc.invalidChars, resultInvalidChars, cc.invalidLength)) {
1296         msg="wrong last invalid input";
1297     }
1298 
1299     if(msg==NULL) {
1300         return TRUE;
1301     } else {
1302         char buffer[2000]; // one buffer for all strings
1303         char *s, *bytesString, *unicodeString, *resultString,
1304             *offsetsString, *resultOffsetsString,
1305             *invalidCharsString, *resultInvalidCharsString;
1306 
1307         bytesString=s=buffer;
1308         s=printBytes(cc.bytes, cc.bytesLength, bytesString);
1309         s=printUnicode(cc.unicode, cc.unicodeLength, unicodeString=s);
1310         s=printUnicode(result, resultLength, resultString=s);
1311         s=printOffsets(cc.offsets, cc.unicodeLength, offsetsString=s);
1312         s=printOffsets(resultOffsets, resultLength, resultOffsetsString=s);
1313         s=printBytes(cc.invalidChars, cc.invalidLength, invalidCharsString=s);
1314         s=printBytes((uint8_t *)resultInvalidChars, resultInvalidLength, resultInvalidCharsString=s);
1315 
1316         if((s-buffer)>(int32_t)sizeof(buffer)) {
1317             errln("toUnicode[%d](%s cb=\"%s\" fb=%d flush=%d %s) fatal error: checkToUnicode() test output buffer overflow writing %d chars\n",
1318                     cc.caseNr, cc.charset, cc.cbopt, cc.fallbacks, cc.finalFlush, name, (int)(s-buffer));
1319             exit(1);
1320         }
1321 
1322         errln("toUnicode[%d](%s cb=\"%s\" fb=%d flush=%d %s) failed: %s\n"
1323               "  bytes <%s>[%d]\n"
1324               " expected <%s>[%d]\n"
1325               "  result  <%s>[%d]\n"
1326               " offsets         <%s>\n"
1327               "  result offsets <%s>\n"
1328               " error code expected %s got %s\n"
1329               "  invalidChars expected <%s> got <%s>\n",
1330               cc.caseNr, cc.charset, cc.cbopt, cc.fallbacks, cc.finalFlush, name, msg,
1331               bytesString, cc.bytesLength,
1332               unicodeString, cc.unicodeLength,
1333               resultString, resultLength,
1334               offsetsString,
1335               resultOffsetsString,
1336               u_errorName(cc.outErrorCode), u_errorName(resultErrorCode),
1337               invalidCharsString, resultInvalidCharsString);
1338 
1339         return FALSE;
1340     }
1341 }
1342 
1343 // fromUnicode test worker functions --------------------------------------- ***
1344 
1345 static int32_t
stepFromUTF8(ConversionCase & cc,UConverter * utf8Cnv,UConverter * cnv,char * result,int32_t resultCapacity,int32_t step,UErrorCode * pErrorCode)1346 stepFromUTF8(ConversionCase &cc,
1347              UConverter *utf8Cnv, UConverter *cnv,
1348              char *result, int32_t resultCapacity,
1349              int32_t step,
1350              UErrorCode *pErrorCode) {
1351     const char *source, *sourceLimit, *utf8Limit;
1352     UChar pivotBuffer[32];
1353     UChar *pivotSource, *pivotTarget, *pivotLimit;
1354     char *target, *targetLimit, *resultLimit;
1355     UBool flush;
1356 
1357     source=cc.utf8;
1358     pivotSource=pivotTarget=pivotBuffer;
1359     target=result;
1360     utf8Limit=source+cc.utf8Length;
1361     resultLimit=result+resultCapacity;
1362 
1363     // call ucnv_convertEx() with in/out buffers no larger than (step) at a time
1364     // move only one buffer (in vs. out) at a time to be extra mean
1365     // step==0 performs bulk conversion
1366 
1367     // initialize the partial limits for the loop
1368     if(step==0) {
1369         // use the entire buffers
1370         sourceLimit=utf8Limit;
1371         targetLimit=resultLimit;
1372         flush=cc.finalFlush;
1373 
1374         pivotLimit=pivotBuffer+UPRV_LENGTHOF(pivotBuffer);
1375     } else {
1376         // start with empty partial buffers
1377         sourceLimit=source;
1378         targetLimit=target;
1379         flush=FALSE;
1380 
1381         // empty pivot is not allowed, make it of length step
1382         pivotLimit=pivotBuffer+step;
1383     }
1384 
1385     for(;;) {
1386         // resetting the opposite conversion direction must not affect this one
1387         ucnv_resetFromUnicode(utf8Cnv);
1388         ucnv_resetToUnicode(cnv);
1389 
1390         // convert
1391         ucnv_convertEx(cnv, utf8Cnv,
1392             &target, targetLimit,
1393             &source, sourceLimit,
1394             pivotBuffer, &pivotSource, &pivotTarget, pivotLimit,
1395             FALSE, flush, pErrorCode);
1396 
1397         // check pointers and errors
1398         if(source>sourceLimit || target>targetLimit) {
1399             *pErrorCode=U_INTERNAL_PROGRAM_ERROR;
1400             break;
1401         } else if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) {
1402             if(target!=targetLimit) {
1403                 // buffer overflow must only be set when the target is filled
1404                 *pErrorCode=U_INTERNAL_PROGRAM_ERROR;
1405                 break;
1406             } else if(targetLimit==resultLimit) {
1407                 // not just a partial overflow
1408                 break;
1409             }
1410 
1411             // the partial target is filled, set a new limit, reset the error and continue
1412             targetLimit=(resultLimit-target)>=step ? target+step : resultLimit;
1413             *pErrorCode=U_ZERO_ERROR;
1414         } else if(U_FAILURE(*pErrorCode)) {
1415             if(pivotSource==pivotBuffer) {
1416                 // toUnicode error, should not occur
1417                 // toUnicode errors are tested in cintltst TestConvertExFromUTF8()
1418                 break;
1419             } else {
1420                 // fromUnicode error
1421                 // some other error occurred, done
1422                 break;
1423             }
1424         } else {
1425             if(source!=sourceLimit) {
1426                 // when no error occurs, then the input must be consumed
1427                 *pErrorCode=U_INTERNAL_PROGRAM_ERROR;
1428                 break;
1429             }
1430 
1431             if(sourceLimit==utf8Limit) {
1432                 // we are done
1433                 if(*pErrorCode==U_STRING_NOT_TERMINATED_WARNING) {
1434                     // ucnv_convertEx() warns about not terminating the output
1435                     // but ucnv_fromUnicode() does not and so
1436                     // checkFromUnicode() does not expect it
1437                     *pErrorCode=U_ZERO_ERROR;
1438                 }
1439                 break;
1440             }
1441 
1442             // the partial conversion succeeded, set a new limit and continue
1443             sourceLimit=(utf8Limit-source)>=step ? source+step : utf8Limit;
1444             flush=(UBool)(cc.finalFlush && sourceLimit==utf8Limit);
1445         }
1446     }
1447 
1448     return (int32_t)(target-result);
1449 }
1450 
1451 static int32_t
stepFromUnicode(ConversionCase & cc,UConverter * cnv,char * result,int32_t resultCapacity,int32_t * resultOffsets,int32_t step,UErrorCode * pErrorCode)1452 stepFromUnicode(ConversionCase &cc, UConverter *cnv,
1453                 char *result, int32_t resultCapacity,
1454                 int32_t *resultOffsets, /* also resultCapacity */
1455                 int32_t step,
1456                 UErrorCode *pErrorCode) {
1457     const UChar *source, *sourceLimit, *unicodeLimit;
1458     char *target, *targetLimit, *resultLimit;
1459     UBool flush;
1460 
1461     source=cc.unicode;
1462     target=result;
1463     unicodeLimit=source+cc.unicodeLength;
1464     resultLimit=result+resultCapacity;
1465 
1466     // call ucnv_fromUnicode() with in/out buffers no larger than (step) at a time
1467     // move only one buffer (in vs. out) at a time to be extra mean
1468     // step==0 performs bulk conversion and generates offsets
1469 
1470     // initialize the partial limits for the loop
1471     if(step==0) {
1472         // use the entire buffers
1473         sourceLimit=unicodeLimit;
1474         targetLimit=resultLimit;
1475         flush=cc.finalFlush;
1476     } else {
1477         // start with empty partial buffers
1478         sourceLimit=source;
1479         targetLimit=target;
1480         flush=FALSE;
1481 
1482         // output offsets only for bulk conversion
1483         resultOffsets=NULL;
1484     }
1485 
1486     for(;;) {
1487         // resetting the opposite conversion direction must not affect this one
1488         ucnv_resetToUnicode(cnv);
1489 
1490         // convert
1491         ucnv_fromUnicode(cnv,
1492             &target, targetLimit,
1493             &source, sourceLimit,
1494             resultOffsets,
1495             flush, pErrorCode);
1496 
1497         // check pointers and errors
1498         if(source>sourceLimit || target>targetLimit) {
1499             *pErrorCode=U_INTERNAL_PROGRAM_ERROR;
1500             break;
1501         } else if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) {
1502             if(target!=targetLimit) {
1503                 // buffer overflow must only be set when the target is filled
1504                 *pErrorCode=U_INTERNAL_PROGRAM_ERROR;
1505                 break;
1506             } else if(targetLimit==resultLimit) {
1507                 // not just a partial overflow
1508                 break;
1509             }
1510 
1511             // the partial target is filled, set a new limit, reset the error and continue
1512             targetLimit=(resultLimit-target)>=step ? target+step : resultLimit;
1513             *pErrorCode=U_ZERO_ERROR;
1514         } else if(U_FAILURE(*pErrorCode)) {
1515             // some other error occurred, done
1516             break;
1517         } else {
1518             if(source!=sourceLimit) {
1519                 // when no error occurs, then the input must be consumed
1520                 *pErrorCode=U_INTERNAL_PROGRAM_ERROR;
1521                 break;
1522             }
1523 
1524             if(sourceLimit==unicodeLimit) {
1525                 // we are done
1526                 break;
1527             }
1528 
1529             // the partial conversion succeeded, set a new limit and continue
1530             sourceLimit=(unicodeLimit-source)>=step ? source+step : unicodeLimit;
1531             flush=(UBool)(cc.finalFlush && sourceLimit==unicodeLimit);
1532         }
1533     }
1534 
1535     return (int32_t)(target-result);
1536 }
1537 
1538 UBool
FromUnicodeCase(ConversionCase & cc,UConverterFromUCallback callback,const char * option)1539 ConversionTest::FromUnicodeCase(ConversionCase &cc, UConverterFromUCallback callback, const char *option) {
1540     UConverter *cnv;
1541     UErrorCode errorCode;
1542 
1543     // open the converter
1544     errorCode=U_ZERO_ERROR;
1545     cnv=cnv_open(cc.charset, errorCode);
1546     if(U_FAILURE(errorCode)) {
1547         errcheckln(errorCode, "fromUnicode[%d](%s cb=\"%s\" fb=%d flush=%d) ucnv_open() failed - %s",
1548                 cc.caseNr, cc.charset, cc.cbopt, cc.fallbacks, cc.finalFlush, u_errorName(errorCode));
1549         return FALSE;
1550     }
1551     ucnv_resetToUnicode(utf8Cnv);
1552 
1553     // set the callback
1554     if(callback!=NULL) {
1555         ucnv_setFromUCallBack(cnv, callback, option, NULL, NULL, &errorCode);
1556         if(U_FAILURE(errorCode)) {
1557             errln("fromUnicode[%d](%s cb=\"%s\" fb=%d flush=%d) ucnv_setFromUCallBack() failed - %s",
1558                     cc.caseNr, cc.charset, cc.cbopt, cc.fallbacks, cc.finalFlush, u_errorName(errorCode));
1559             ucnv_close(cnv);
1560             return FALSE;
1561         }
1562     }
1563 
1564     // set the fallbacks flag
1565     // TODO change with Jitterbug 2401, then add a similar call for toUnicode too
1566     ucnv_setFallback(cnv, cc.fallbacks);
1567 
1568     // set the subchar
1569     int32_t length;
1570 
1571     if(cc.setSub>0) {
1572         length=(int32_t)strlen(cc.subchar);
1573         ucnv_setSubstChars(cnv, cc.subchar, (int8_t)length, &errorCode);
1574         if(U_FAILURE(errorCode)) {
1575             errln("fromUnicode[%d](%s cb=\"%s\" fb=%d flush=%d) ucnv_setSubstChars() failed - %s",
1576                     cc.caseNr, cc.charset, cc.cbopt, cc.fallbacks, cc.finalFlush, u_errorName(errorCode));
1577             ucnv_close(cnv);
1578             return FALSE;
1579         }
1580     } else if(cc.setSub<0) {
1581         ucnv_setSubstString(cnv, cc.subString, -1, &errorCode);
1582         if(U_FAILURE(errorCode)) {
1583             errln("fromUnicode[%d](%s cb=\"%s\" fb=%d flush=%d) ucnv_setSubstString() failed - %s",
1584                     cc.caseNr, cc.charset, cc.cbopt, cc.fallbacks, cc.finalFlush, u_errorName(errorCode));
1585             ucnv_close(cnv);
1586             return FALSE;
1587         }
1588     }
1589 
1590     // convert unicode to utf8
1591     char utf8[256];
1592     cc.utf8=utf8;
1593     u_strToUTF8(utf8, UPRV_LENGTHOF(utf8), &cc.utf8Length,
1594                 cc.unicode, cc.unicodeLength,
1595                 &errorCode);
1596     if(U_FAILURE(errorCode)) {
1597         // skip UTF-8 testing of a string with an unpaired surrogate,
1598         // or of one that's too long
1599         // toUnicode errors are tested in cintltst TestConvertExFromUTF8()
1600         cc.utf8Length=-1;
1601     }
1602 
1603     int32_t resultOffsets[256];
1604     char result[256];
1605     int32_t resultLength;
1606     UBool ok;
1607 
1608     static const struct {
1609         int32_t step;
1610         const char *name, *utf8Name;
1611     } steps[]={
1612         { 0, "bulk",   "utf8" }, // must be first for offsets to be checked
1613         { 1, "step=1", "utf8 step=1" },
1614         { 3, "step=3", "utf8 step=3" },
1615         { 7, "step=7", "utf8 step=7" }
1616     };
1617     int32_t i, step;
1618 
1619     ok=TRUE;
1620     for(i=0; i<UPRV_LENGTHOF(steps) && ok; ++i) {
1621         step=steps[i].step;
1622         for (int32_t i = 0; i < UPRV_LENGTHOF(resultOffsets); i++) {
1623             resultOffsets[i] = -1;
1624         }
1625         for (int32_t i = 0; i < UPRV_LENGTHOF(result); i++) {
1626             result[i] = -1;
1627         }
1628         errorCode=U_ZERO_ERROR;
1629         resultLength=stepFromUnicode(cc, cnv,
1630                                 result, UPRV_LENGTHOF(result),
1631                                 step==0 ? resultOffsets : NULL,
1632                                 step, &errorCode);
1633         ok=checkFromUnicode(
1634                 cc, cnv, steps[i].name,
1635                 (uint8_t *)result, resultLength,
1636                 cc.offsets!=NULL ? resultOffsets : NULL,
1637                 errorCode);
1638         if(U_FAILURE(errorCode) || !cc.finalFlush) {
1639             // reset if an error occurred or we did not flush
1640             // otherwise do nothing to make sure that flushing resets
1641             ucnv_resetFromUnicode(cnv);
1642         }
1643         if (resultOffsets[resultLength] != -1) {
1644             errln("fromUnicode[%d](%s) Conversion wrote too much to offsets at index %d",
1645                 cc.caseNr, cc.charset, resultLength);
1646         }
1647         if (result[resultLength] != (char)-1) {
1648             errln("fromUnicode[%d](%s) Conversion wrote too much to result at index %d",
1649                 cc.caseNr, cc.charset, resultLength);
1650         }
1651 
1652         // bulk test is first, then offsets are not checked any more
1653         cc.offsets=NULL;
1654 
1655         // test direct conversion from UTF-8
1656         if(cc.utf8Length>=0) {
1657             errorCode=U_ZERO_ERROR;
1658             resultLength=stepFromUTF8(cc, utf8Cnv, cnv,
1659                                     result, UPRV_LENGTHOF(result),
1660                                     step, &errorCode);
1661             ok=checkFromUnicode(
1662                     cc, cnv, steps[i].utf8Name,
1663                     (uint8_t *)result, resultLength,
1664                     NULL,
1665                     errorCode);
1666             if(U_FAILURE(errorCode) || !cc.finalFlush) {
1667                 // reset if an error occurred or we did not flush
1668                 // otherwise do nothing to make sure that flushing resets
1669                 ucnv_resetToUnicode(utf8Cnv);
1670                 ucnv_resetFromUnicode(cnv);
1671             }
1672         }
1673     }
1674 
1675     // not a real loop, just a convenience for breaking out of the block
1676     while(ok && cc.finalFlush) {
1677         // test ucnv_fromUChars()
1678         memset(result, 0, sizeof(result));
1679 
1680         errorCode=U_ZERO_ERROR;
1681         resultLength=ucnv_fromUChars(cnv,
1682                         result, UPRV_LENGTHOF(result),
1683                         cc.unicode, cc.unicodeLength,
1684                         &errorCode);
1685         ok=checkFromUnicode(
1686                 cc, cnv, "fromUChars",
1687                 (uint8_t *)result, resultLength,
1688                 NULL,
1689                 errorCode);
1690         if(!ok) {
1691             break;
1692         }
1693 
1694         // test preflighting
1695         // keep the correct result for simple checking
1696         errorCode=U_ZERO_ERROR;
1697         resultLength=ucnv_fromUChars(cnv,
1698                         NULL, 0,
1699                         cc.unicode, cc.unicodeLength,
1700                         &errorCode);
1701         if(errorCode==U_STRING_NOT_TERMINATED_WARNING || errorCode==U_BUFFER_OVERFLOW_ERROR) {
1702             errorCode=U_ZERO_ERROR;
1703         }
1704         ok=checkFromUnicode(
1705                 cc, cnv, "preflight fromUChars",
1706                 (uint8_t *)result, resultLength,
1707                 NULL,
1708                 errorCode);
1709         break;
1710     }
1711 
1712     ucnv_close(cnv);
1713     return ok;
1714 }
1715 
1716 UBool
checkFromUnicode(ConversionCase & cc,UConverter * cnv,const char * name,const uint8_t * result,int32_t resultLength,const int32_t * resultOffsets,UErrorCode resultErrorCode)1717 ConversionTest::checkFromUnicode(ConversionCase &cc, UConverter *cnv, const char *name,
1718                                  const uint8_t *result, int32_t resultLength,
1719                                  const int32_t *resultOffsets,
1720                                  UErrorCode resultErrorCode) {
1721     UChar resultInvalidUChars[8];
1722     int8_t resultInvalidLength;
1723     UErrorCode errorCode;
1724 
1725     const char *msg;
1726 
1727     // reset the message; NULL will mean "ok"
1728     msg=NULL;
1729 
1730     errorCode=U_ZERO_ERROR;
1731     resultInvalidLength=UPRV_LENGTHOF(resultInvalidUChars);
1732     ucnv_getInvalidUChars(cnv, resultInvalidUChars, &resultInvalidLength, &errorCode);
1733     if(U_FAILURE(errorCode)) {
1734         errln("fromUnicode[%d](%s cb=\"%s\" fb=%d flush=%d %s) ucnv_getInvalidUChars() failed - %s",
1735                 cc.caseNr, cc.charset, cc.cbopt, cc.fallbacks, cc.finalFlush, name, u_errorName(errorCode));
1736         return FALSE;
1737     }
1738 
1739     // check everything that might have gone wrong
1740     if(cc.bytesLength!=resultLength) {
1741         msg="wrong result length";
1742     } else if(0!=memcmp(cc.bytes, result, cc.bytesLength)) {
1743         msg="wrong result string";
1744     } else if(cc.offsets!=NULL && 0!=memcmp(cc.offsets, resultOffsets, cc.bytesLength*sizeof(*cc.offsets))) {
1745         msg="wrong offsets";
1746     } else if(cc.outErrorCode!=resultErrorCode) {
1747         msg="wrong error code";
1748     } else if(cc.invalidLength!=resultInvalidLength) {
1749         msg="wrong length of last invalid input";
1750     } else if(0!=u_memcmp(cc.invalidUChars, resultInvalidUChars, cc.invalidLength)) {
1751         msg="wrong last invalid input";
1752     }
1753 
1754     if(msg==NULL) {
1755         return TRUE;
1756     } else {
1757         char buffer[2000]; // one buffer for all strings
1758         char *s, *unicodeString, *bytesString, *resultString,
1759             *offsetsString, *resultOffsetsString,
1760             *invalidCharsString, *resultInvalidUCharsString;
1761 
1762         unicodeString=s=buffer;
1763         s=printUnicode(cc.unicode, cc.unicodeLength, unicodeString);
1764         s=printBytes(cc.bytes, cc.bytesLength, bytesString=s);
1765         s=printBytes(result, resultLength, resultString=s);
1766         s=printOffsets(cc.offsets, cc.bytesLength, offsetsString=s);
1767         s=printOffsets(resultOffsets, resultLength, resultOffsetsString=s);
1768         s=printUnicode(cc.invalidUChars, cc.invalidLength, invalidCharsString=s);
1769         s=printUnicode(resultInvalidUChars, resultInvalidLength, resultInvalidUCharsString=s);
1770 
1771         if((s-buffer)>(int32_t)sizeof(buffer)) {
1772             errln("fromUnicode[%d](%s cb=\"%s\" fb=%d flush=%d %s) fatal error: checkFromUnicode() test output buffer overflow writing %d chars\n",
1773                     cc.caseNr, cc.charset, cc.cbopt, cc.fallbacks, cc.finalFlush, name, (int)(s-buffer));
1774             exit(1);
1775         }
1776 
1777         errln("fromUnicode[%d](%s cb=\"%s\" fb=%d flush=%d %s) failed: %s\n"
1778               "  unicode <%s>[%d]\n"
1779               " expected <%s>[%d]\n"
1780               "  result  <%s>[%d]\n"
1781               " offsets         <%s>\n"
1782               "  result offsets <%s>\n"
1783               " error code expected %s got %s\n"
1784               "  invalidChars expected <%s> got <%s>\n",
1785               cc.caseNr, cc.charset, cc.cbopt, cc.fallbacks, cc.finalFlush, name, msg,
1786               unicodeString, cc.unicodeLength,
1787               bytesString, cc.bytesLength,
1788               resultString, resultLength,
1789               offsetsString,
1790               resultOffsetsString,
1791               u_errorName(cc.outErrorCode), u_errorName(resultErrorCode),
1792               invalidCharsString, resultInvalidUCharsString);
1793 
1794         return FALSE;
1795     }
1796 }
1797 
1798 #endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */
1799