1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /********************************************************************
5 * Copyright (c) 1999-2015, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 ********************************************************************/
9 #include "simplethread.h"
11 #include "unicode/utypes.h"
12 #include "unicode/ustring.h"
16 #include "indiancal.h"
18 #include "unicode/localpointer.h"
19 #include "unicode/resbund.h"
20 #include "unicode/udata.h"
21 #include "unicode/uloc.h"
22 #include "unicode/locid.h"
26 #include "unicode/ushape.h"
27 #include "unicode/translit.h"
28 #include "sharedobject.h"
29 #include "unifiedcache.h"
33 MultithreadTest::MultithreadTest()
37 MultithreadTest::~MultithreadTest()
43 #include <ctype.h> // tolower, toupper
46 #include "unicode/putil.h"
49 #include "unicode/numfmt.h"
50 #include "unicode/choicfmt.h"
51 #include "unicode/msgfmt.h"
52 #include "unicode/locid.h"
53 #include "unicode/coll.h"
54 #include "unicode/calendar.h"
58 void MultithreadTest::runIndexedTest( int32_t index
, UBool exec
,
59 const char* &name
, char* /*par*/ ) {
61 logln("TestSuite MultithreadTest: ");
64 TESTCASE_AUTO(TestThreads
);
65 #if !UCONFIG_NO_FORMATTING
66 TESTCASE_AUTO(TestThreadedIntl
);
68 #if !UCONFIG_NO_COLLATION
69 TESTCASE_AUTO(TestCollators
);
70 #endif /* #if !UCONFIG_NO_COLLATION */
71 TESTCASE_AUTO(TestString
);
72 TESTCASE_AUTO(TestArabicShapingThreads
);
73 TESTCASE_AUTO(TestAnyTranslit
);
74 TESTCASE_AUTO(TestUnifiedCache
);
75 #if !UCONFIG_NO_TRANSLITERATION
76 TESTCASE_AUTO(TestBreakTranslit
);
77 TESTCASE_AUTO(TestIncDec
);
78 #if !UCONFIG_NO_FORMATTING
79 TESTCASE_AUTO(Test20104
);
80 #endif /* #if !UCONFIG_NO_FORMATTING */
81 #endif /* #if !UCONFIG_NO_TRANSLITERATION */
86 //-----------------------------------------------------------------------------------
88 // TestThreads -- see if threads really work at all.
90 // Set up N threads pointing at N chars. When they are started, they will
91 // set their chars. At the end we make sure they are all set.
93 //-----------------------------------------------------------------------------------
95 class TestThreadsThread
: public SimpleThread
98 TestThreadsThread(char* whatToChange
) { fWhatToChange
= whatToChange
; }
99 virtual void run() { Mutex m
;
100 *fWhatToChange
= '*';
107 void MultithreadTest::TestThreads()
109 static const int32_t THREADTEST_NRTHREADS
= 8;
110 char threadTestChars
[THREADTEST_NRTHREADS
+ 1];
111 SimpleThread
*threads
[THREADTEST_NRTHREADS
];
112 int32_t numThreadsStarted
= 0;
115 for(i
=0;i
<THREADTEST_NRTHREADS
;i
++)
117 threadTestChars
[i
] = ' ';
118 threads
[i
] = new TestThreadsThread(&threadTestChars
[i
]);
120 threadTestChars
[THREADTEST_NRTHREADS
] = '\0';
122 logln("->" + UnicodeString(threadTestChars
) + "<- Firing off threads.. ");
123 for(i
=0;i
<THREADTEST_NRTHREADS
;i
++)
125 if (threads
[i
]->start() != 0) {
126 errln("Error starting thread %d", i
);
131 logln(" Subthread started.");
134 assertTrue(WHERE
, THREADTEST_NRTHREADS
== numThreadsStarted
);
136 logln("Waiting for threads to be set..");
137 for(i
=0; i
<THREADTEST_NRTHREADS
; i
++) {
139 if (threadTestChars
[i
] != '*') {
140 errln("%s:%d Thread %d failed.", __FILE__
, __LINE__
, i
);
147 //-----------------------------------------------------------------------------------
149 // TestArabicShapeThreads -- see if calls to u_shapeArabic in many threads works successfully
151 // Set up N threads pointing at N chars. When they are started, they will make calls to doTailTest which tests
152 // u_shapeArabic, if the calls are successful it will the set * chars.
153 // At the end we make sure all threads managed to run u_shapeArabic successfully.
154 // This is a unit test for ticket 9473
156 //-----------------------------------------------------------------------------------
158 class TestArabicShapeThreads
: public SimpleThread
161 TestArabicShapeThreads() {}
162 virtual void run() { doTailTest(); }
168 void TestArabicShapeThreads::doTailTest(void) {
169 static const UChar src
[] = { 0x0020, 0x0633, 0 };
170 static const UChar dst_old
[] = { 0xFEB1, 0x200B,0 };
171 static const UChar dst_new
[] = { 0xFEB1, 0xFE73,0 };
172 UChar dst
[3] = { 0x0000, 0x0000,0 };
176 for (int32_t loopCount
= 0; loopCount
< 100; loopCount
++) {
177 status
= U_ZERO_ERROR
;
178 length
= u_shapeArabic(src
, -1, dst
, UPRV_LENGTHOF(dst
),
179 U_SHAPE_LETTERS_SHAPE
|U_SHAPE_SEEN_TWOCELL_NEAR
, &status
);
180 if(U_FAILURE(status
)) {
181 IntlTest::gTest
->errln("Fail: status %s\n", u_errorName(status
));
183 } else if(length
!=2) {
184 IntlTest::gTest
->errln("Fail: len %d expected 3\n", length
);
186 } else if(u_strncmp(dst
,dst_old
,UPRV_LENGTHOF(dst
))) {
187 IntlTest::gTest
->errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n",
188 dst
[0],dst
[1],dst_old
[0],dst_old
[1]);
194 status
= U_ZERO_ERROR
;
195 length
= u_shapeArabic(src
, -1, dst
, UPRV_LENGTHOF(dst
),
196 U_SHAPE_LETTERS_SHAPE
|U_SHAPE_SEEN_TWOCELL_NEAR
|U_SHAPE_TAIL_NEW_UNICODE
, &status
);
197 if(U_FAILURE(status
)) {
198 IntlTest::gTest
->errln("Fail: status %s\n", u_errorName(status
));
200 } else if(length
!=2) {
201 IntlTest::gTest
->errln("Fail: len %d expected 3\n", length
);
203 } else if(u_strncmp(dst
,dst_new
,UPRV_LENGTHOF(dst
))) {
204 IntlTest::gTest
->errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n",
205 dst
[0],dst
[1],dst_new
[0],dst_new
[1]);
213 void MultithreadTest::TestArabicShapingThreads()
215 TestArabicShapeThreads threads
[30];
219 logln("-> do TestArabicShapingThreads <- Firing off threads.. ");
220 for(i
=0; i
< UPRV_LENGTHOF(threads
); i
++) {
221 if (threads
[i
].start() != 0) {
222 errln("Error starting thread %d", i
);
226 for(i
=0; i
< UPRV_LENGTHOF(threads
); i
++) {
229 logln("->TestArabicShapingThreads <- Got all threads! cya");
233 //-------------------------------------------------------------------------------------------
235 // TestMultithreadedIntl. Test ICU Formatting in a multi-threaded environment
237 //-------------------------------------------------------------------------------------------
240 // * Show exactly where the string's differences lie.
241 UnicodeString
showDifference(const UnicodeString
& expected
, const UnicodeString
& result
)
244 res
= expected
+ u
"<Expected\n";
245 if(expected
.length() != result
.length())
246 res
+= u
" [ Different lengths ] \n";
249 for(int32_t i
=0;i
<expected
.length();i
++)
251 if(expected
[i
] == result
[i
])
260 res
+= u
"<Differences";
263 res
+= result
+ u
"<Result\n";
269 //-------------------------------------------------------------------------------------------
271 // FormatThreadTest - a thread that tests performing a number of numberformats.
273 //-------------------------------------------------------------------------------------------
275 const int kFormatThreadIterations
= 100; // # of iterations per thread
276 const int kFormatThreadThreads
= 10; // # of threads to spawn
278 #if !UCONFIG_NO_FORMATTING
282 struct FormatThreadTestData
285 UnicodeString string
;
286 FormatThreadTestData(double a
, const UnicodeString
& b
) : number(a
),string(b
) {}
290 // "Someone from {2} is receiving a #{0} error - {1}. Their telephone call is costing {3 number,currency}."
292 static void formatErrorMessage(UErrorCode
&realStatus
, const UnicodeString
& pattern
, const Locale
& theLocale
,
293 UErrorCode inStatus0
, // statusString 1
294 const Locale
&inCountry2
, double currency3
, // these numbers are the message arguments.
295 UnicodeString
&result
)
297 if(U_FAILURE(realStatus
))
298 return; // you messed up
300 UnicodeString
errString1(u_errorName(inStatus0
));
302 UnicodeString countryName2
;
303 inCountry2
.getDisplayCountry(theLocale
,countryName2
);
305 Formattable myArgs
[] = {
306 Formattable((int32_t)inStatus0
), // inStatus0 {0}
307 Formattable(errString1
), // statusString1 {1}
308 Formattable(countryName2
), // inCountry2 {2}
309 Formattable(currency3
)// currency3 {3,number,currency}
312 LocalPointer
<MessageFormat
> fmt(new MessageFormat(u
"MessageFormat's API is broken!!!!!!!!!!!",realStatus
), realStatus
);
313 if (U_FAILURE(realStatus
)) {
316 fmt
->setLocale(theLocale
);
317 fmt
->applyPattern(pattern
, realStatus
);
319 FieldPosition ignore
= 0;
320 fmt
->format(myArgs
,4,result
,ignore
,realStatus
);
324 * Shared formatters & data used by instances of ThreadSafeFormat.
325 * Exactly one instance of this class is created, and it is then shared concurrently
326 * by the multiple instances of ThreadSafeFormat.
328 class ThreadSafeFormatSharedData
{
330 ThreadSafeFormatSharedData(UErrorCode
&status
);
331 ~ThreadSafeFormatSharedData();
332 LocalPointer
<NumberFormat
> fFormat
;
333 Formattable fYDDThing
;
334 Formattable fBBDThing
;
335 UnicodeString fYDDStr
;
336 UnicodeString fBBDStr
;
339 const ThreadSafeFormatSharedData
*gSharedData
= NULL
;
341 ThreadSafeFormatSharedData::ThreadSafeFormatSharedData(UErrorCode
&status
) {
342 fFormat
.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status
));
343 static const UChar
*kYDD
= u
"YDD";
344 static const UChar
*kBBD
= u
"BBD";
345 fYDDThing
.adoptObject(new CurrencyAmount(123.456, kYDD
, status
));
346 fBBDThing
.adoptObject(new CurrencyAmount(987.654, kBBD
, status
));
347 if (U_FAILURE(status
)) {
350 fFormat
->format(fYDDThing
, fYDDStr
, NULL
, status
);
351 fFormat
->format(fBBDThing
, fBBDStr
, NULL
, status
);
355 ThreadSafeFormatSharedData::~ThreadSafeFormatSharedData() {
360 * Class for thread-safe testing of format.
361 * Instances of this class appear as members of class FormatThreadTest.
362 * Multiple instances of FormatThreadTest coexist.
363 * ThreadSafeFormat::doStuff() is called concurrently to test the thread safety of
364 * various shared format operations.
366 class ThreadSafeFormat
{
368 /* give a unique offset to each thread */
369 ThreadSafeFormat(UErrorCode
&status
);
370 UBool
doStuff(int32_t offset
, UnicodeString
&appendErr
, UErrorCode
&status
) const;
372 LocalPointer
<NumberFormat
> fFormat
; // formatter - en_US constructed currency
376 ThreadSafeFormat::ThreadSafeFormat(UErrorCode
&status
) {
377 fFormat
.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status
));
380 static const UChar
*kUSD
= u
"USD";
382 UBool
ThreadSafeFormat::doStuff(int32_t offset
, UnicodeString
&appendErr
, UErrorCode
&status
) const {
385 if(u_strcmp(fFormat
->getCurrency(), kUSD
)) {
386 appendErr
.append(u
"fFormat currency != ")
389 .append(fFormat
->getCurrency())
394 if(u_strcmp(gSharedData
->fFormat
->getCurrency(), kUSD
)) {
395 appendErr
.append(u
"gFormat currency != ")
398 .append(gSharedData
->fFormat
->getCurrency())
403 const UnicodeString
*o
=NULL
;
405 const NumberFormat
*nf
= NULL
; // only operate on it as const.
407 case 0: f
= gSharedData
->fYDDThing
; o
= &gSharedData
->fYDDStr
; nf
= gSharedData
->fFormat
.getAlias(); break;
408 case 1: f
= gSharedData
->fBBDThing
; o
= &gSharedData
->fBBDStr
; nf
= gSharedData
->fFormat
.getAlias(); break;
409 case 2: f
= gSharedData
->fYDDThing
; o
= &gSharedData
->fYDDStr
; nf
= fFormat
.getAlias(); break;
410 case 3: f
= gSharedData
->fBBDThing
; o
= &gSharedData
->fBBDStr
; nf
= fFormat
.getAlias(); break;
412 nf
->format(f
, str
, NULL
, status
);
415 appendErr
.append(showDifference(*o
, str
));
421 UBool U_CALLCONV
isAcceptable(void *, const char *, const char *, const UDataInfo
*) {
425 //static UMTX debugMutex = NULL;
426 //static UMTX gDebugMutex;
429 class FormatThreadTest
: public SimpleThread
435 LocalPointer
<ThreadSafeFormat
> fTSF
;
437 FormatThreadTest() // constructor is NOT multithread safe.
445 UErrorCode status
= U_ZERO_ERROR
; // TODO: rearrange code to allow checking of status.
446 fTSF
.adoptInstead(new ThreadSafeFormat(status
));
447 static int32_t fgOffset
= 0;
456 LocalPointer
<NumberFormat
> percentFormatter
;
457 UErrorCode status
= U_ZERO_ERROR
;
461 for (int i
=0; i
<4000; i
++) {
462 status
= U_ZERO_ERROR
;
463 UDataMemory
*data1
= udata_openChoice(0, "res", "en_US", isAcceptable
, 0, &status
);
464 UDataMemory
*data2
= udata_openChoice(0, "res", "fr", isAcceptable
, 0, &status
);
467 if (U_FAILURE(status
)) {
468 error("udata_openChoice failed.\n");
478 for (m
=0; m
<4000; m
++) {
479 status
= U_ZERO_ERROR
;
480 UResourceBundle
*res
= NULL
;
481 const char *localeName
= NULL
;
483 Locale loc
= Locale::getEnglish();
485 localeName
= loc
.getName();
486 // localeName = "en";
488 // ResourceBundle bund = ResourceBundle(0, loc, status);
489 //umtx_lock(&gDebugMutex);
490 res
= ures_open(NULL
, localeName
, &status
);
491 //umtx_unlock(&gDebugMutex);
493 //umtx_lock(&gDebugMutex);
495 //umtx_unlock(&gDebugMutex);
497 if (U_FAILURE(status
)) {
498 error("Resource bundle construction failed.\n");
505 // Keep this data here to avoid static initialization.
506 FormatThreadTestData kNumberFormatTestData
[] =
508 FormatThreadTestData((double)5.0, UnicodeString(u
"5")),
509 FormatThreadTestData( 6.0, UnicodeString(u
"6")),
510 FormatThreadTestData( 20.0, UnicodeString(u
"20")),
511 FormatThreadTestData( 8.0, UnicodeString(u
"8")),
512 FormatThreadTestData( 8.3, UnicodeString(u
"8.3")),
513 FormatThreadTestData( 12345, UnicodeString(u
"12,345")),
514 FormatThreadTestData( 81890.23, UnicodeString(u
"81,890.23")),
516 int32_t kNumberFormatTestDataLength
= UPRV_LENGTHOF(kNumberFormatTestData
);
518 // Keep this data here to avoid static initialization.
519 FormatThreadTestData kPercentFormatTestData
[] =
521 FormatThreadTestData((double)5.0, CharsToUnicodeString("500\\u00a0%")),
522 FormatThreadTestData( 1.0, CharsToUnicodeString("100\\u00a0%")),
523 FormatThreadTestData( 0.26, CharsToUnicodeString("26\\u00a0%")),
524 FormatThreadTestData(
525 16384.99, CharsToUnicodeString("1\\u202F638\\u202F499\\u00a0%")), // U+202F = NNBSP
526 FormatThreadTestData(
527 81890.23, CharsToUnicodeString("8\\u202F189\\u202F023\\u00a0%")),
529 int32_t kPercentFormatTestDataLength
= UPRV_LENGTHOF(kPercentFormatTestData
);
532 status
= U_ZERO_ERROR
;
533 LocalPointer
<NumberFormat
> formatter(NumberFormat::createInstance(Locale::getEnglish(),status
));
534 if(U_FAILURE(status
)) {
535 IntlTest::gTest
->dataerrln("%s:%d Error %s on NumberFormat::createInstance().",
536 __FILE__
, __LINE__
, u_errorName(status
));
537 goto cleanupAndReturn
;
540 percentFormatter
.adoptInstead(NumberFormat::createPercentInstance(Locale::getFrench(),status
));
541 if(U_FAILURE(status
)) {
542 IntlTest::gTest
->errln("%s:%d Error %s on NumberFormat::createPercentInstance().",
543 __FILE__
, __LINE__
, u_errorName(status
));
544 goto cleanupAndReturn
;
547 for(iteration
= 0;!IntlTest::gTest
->getErrors() && iteration
<kFormatThreadIterations
;iteration
++)
550 int32_t whichLine
= (iteration
+ fOffset
)%kNumberFormatTestDataLength
;
552 UnicodeString output
;
554 formatter
->format(kNumberFormatTestData
[whichLine
].number
, output
);
556 if(0 != output
.compare(kNumberFormatTestData
[whichLine
].string
)) {
557 IntlTest::gTest
->errln("format().. expected " + kNumberFormatTestData
[whichLine
].string
559 goto cleanupAndReturn
;
562 // Now check percent.
564 whichLine
= (iteration
+ fOffset
)%kPercentFormatTestDataLength
;
566 percentFormatter
->format(kPercentFormatTestData
[whichLine
].number
, output
);
567 if(0 != output
.compare(kPercentFormatTestData
[whichLine
].string
))
569 IntlTest::gTest
->errln("percent format().. \n" +
570 showDifference(kPercentFormatTestData
[whichLine
].string
,output
));
571 goto cleanupAndReturn
;
574 // Test message error
575 const int kNumberOfMessageTests
= 3;
576 UErrorCode statusToCheck
;
577 UnicodeString patternToCheck
;
578 Locale messageLocale
;
579 Locale countryToCheck
;
580 double currencyToCheck
;
582 UnicodeString expected
;
585 switch((iteration
+fOffset
) % kNumberOfMessageTests
)
589 statusToCheck
= U_FILE_ACCESS_ERROR
;
590 patternToCheck
= u
"0:Someone from {2} is receiving a #{0}"
591 " error - {1}. Their telephone call is costing "
592 "{3,number,currency}."; // number,currency
593 messageLocale
= Locale("en","US");
594 countryToCheck
= Locale("","HR");
595 currencyToCheck
= 8192.77;
596 expected
= u
"0:Someone from Croatia is receiving a #4 error - "
597 "U_FILE_ACCESS_ERROR. Their telephone call is costing $8,192.77.";
600 statusToCheck
= U_INDEX_OUTOFBOUNDS_ERROR
;
601 patternToCheck
= u
"1:A customer in {2} is receiving a #{0} error - {1}. "
602 "Their telephone call is costing {3,number,currency}."; // number,currency
603 messageLocale
= Locale("de","DE@currency=DEM");
604 countryToCheck
= Locale("","BF");
605 currencyToCheck
= 2.32;
606 expected
= u
"1:A customer in Burkina Faso is receiving a #8 error - U_INDEX_OUTOFBOUNDS_ERROR. "
607 u
"Their telephone call is costing 2,32\u00A0DM.";
610 statusToCheck
= U_MEMORY_ALLOCATION_ERROR
;
611 patternToCheck
= u
"2:user in {2} is receiving a #{0} error - {1}. "
612 "They insist they just spent {3,number,currency} "
613 "on memory."; // number,currency
614 messageLocale
= Locale("de","AT@currency=ATS"); // Austrian German
615 countryToCheck
= Locale("","US"); // hmm
616 currencyToCheck
= 40193.12;
617 expected
= u
"2:user in Vereinigte Staaten is receiving a #7 error"
618 u
" - U_MEMORY_ALLOCATION_ERROR. They insist they just spent"
619 u
" \u00f6S\u00A040.193,12 on memory.";
623 UnicodeString result
;
624 UErrorCode status
= U_ZERO_ERROR
;
625 formatErrorMessage(status
,patternToCheck
,messageLocale
,statusToCheck
,
626 countryToCheck
,currencyToCheck
,result
);
627 if(U_FAILURE(status
))
629 UnicodeString
tmp(u_errorName(status
));
630 IntlTest::gTest
->errln(u
"Failure on message format, pattern=" + patternToCheck
+
632 goto cleanupAndReturn
;
635 if(result
!= expected
)
637 IntlTest::gTest
->errln(u
"PatternFormat: \n" + showDifference(expected
,result
));
638 goto cleanupAndReturn
;
640 // test the Thread Safe Format
641 UnicodeString appendErr
;
642 if(!fTSF
->doStuff(fNum
, appendErr
, status
)) {
643 IntlTest::gTest
->errln(appendErr
);
644 goto cleanupAndReturn
;
646 } /* end of for loop */
655 int32_t fOffset
; // where we are testing from.
658 // ** The actual test function.
660 void MultithreadTest::TestThreadedIntl()
662 UnicodeString theErr
;
664 UErrorCode threadSafeErr
= U_ZERO_ERROR
;
666 ThreadSafeFormatSharedData
sharedData(threadSafeErr
);
667 assertSuccess(WHERE
, threadSafeErr
, TRUE
);
670 // Create and start the test threads
672 logln("Spawning: %d threads * %d iterations each.",
673 kFormatThreadThreads
, kFormatThreadIterations
);
674 FormatThreadTest tests
[kFormatThreadThreads
];
676 for(j
= 0; j
< UPRV_LENGTHOF(tests
); j
++) {
678 int32_t threadStatus
= tests
[j
].start();
679 if (threadStatus
!= 0) {
680 errln("%s:%d System Error %d starting thread number %d.",
681 __FILE__
, __LINE__
, threadStatus
, j
);
687 for (j
=0; j
<UPRV_LENGTHOF(tests
); j
++) {
689 logln("Thread # %d is complete..", j
);
692 #endif /* #if !UCONFIG_NO_FORMATTING */
698 //-------------------------------------------------------------------------------------------
700 // Collation threading test
702 //-------------------------------------------------------------------------------------------
703 #if !UCONFIG_NO_COLLATION
705 #define kCollatorThreadThreads 10 // # of threads to spawn
706 #define kCollatorThreadPatience kCollatorThreadThreads*30
714 static UCollationResult
715 normalizeResult(int32_t result
) {
716 return result
<0 ? UCOL_LESS
: result
==0 ? UCOL_EQUAL
: UCOL_GREATER
;
719 class CollatorThreadTest
: public SimpleThread
722 const Collator
*coll
;
725 UBool isAtLeastUCA62
;
727 CollatorThreadTest() : SimpleThread(),
734 void setCollator(Collator
*c
, Line
*l
, int32_t nl
, UBool atLeastUCA62
)
739 isAtLeastUCA62
= atLeastUCA62
;
742 uint8_t sk1
[1024], sk2
[1024];
743 uint8_t *oldSk
= NULL
, *newSk
= sk1
;
748 for(i
= 0; i
< noLines
; i
++) {
749 if(lines
[i
].buflen
== 0) { continue; }
751 int32_t resLen
= coll
->getSortKey(lines
[i
].buff
, lines
[i
].buflen
, newSk
, 1024);
754 int32_t skres
= strcmp((char *)oldSk
, (char *)newSk
);
755 int32_t cmpres
= coll
->compare(lines
[prev
].buff
, lines
[prev
].buflen
, lines
[i
].buff
, lines
[i
].buflen
);
756 int32_t cmpres2
= coll
->compare(lines
[i
].buff
, lines
[i
].buflen
, lines
[prev
].buff
, lines
[prev
].buflen
);
758 if(cmpres
!= -cmpres2
) {
759 IntlTest::gTest
->errln(UnicodeString(u
"Compare result not symmetrical on line ") + (i
+ 1));
763 if(cmpres
!= normalizeResult(skres
)) {
764 IntlTest::gTest
->errln(UnicodeString(u
"Difference between coll->compare and sortkey compare on line ") + (i
+ 1));
768 int32_t res
= cmpres
;
769 if(res
== 0 && !isAtLeastUCA62
) {
770 // Up to UCA 6.1, the collation test files use a custom tie-breaker,
771 // comparing the raw input strings.
772 res
= u_strcmpCodePointOrder(lines
[prev
].buff
, lines
[i
].buff
);
773 // Starting with UCA 6.2, the collation test files use the standard UCA tie-breaker,
774 // comparing the NFD versions of the input strings,
775 // which we do via setting strength=identical.
778 IntlTest::gTest
->errln(UnicodeString(u
"Line is not greater or equal than previous line, for line ") + (i
+ 1));
785 (void)oldLen
; // Suppress set but not used warning.
788 newSk
= (newSk
== sk1
)?sk2
:sk1
;
793 void MultithreadTest::TestCollators()
796 UErrorCode status
= U_ZERO_ERROR
;
797 FILE *testFile
= NULL
;
798 char testDataPath
[1024];
799 strcpy(testDataPath
, IntlTest::getSourceTestData(status
));
800 if (U_FAILURE(status
)) {
801 errln("ERROR: could not open test data %s", u_errorName(status
));
804 strcat(testDataPath
, "CollationTest_");
806 const char* type
= "NON_IGNORABLE";
808 const char *ext
= ".txt";
813 strcpy(buffer
, testDataPath
);
814 strcat(buffer
, type
);
815 size_t bufLen
= strlen(buffer
);
817 // we try to open 3 files:
818 // path/CollationTest_type.txt
819 // path/CollationTest_type_SHORT.txt
820 // path/CollationTest_type_STUB.txt
821 // we are going to test with the first one that we manage to open.
823 strcpy(buffer
+bufLen
, ext
);
825 testFile
= fopen(buffer
, "rb");
828 strcpy(buffer
+bufLen
, "_SHORT");
830 testFile
= fopen(buffer
, "rb");
833 strcpy(buffer
+bufLen
, "_STUB");
835 testFile
= fopen(buffer
, "rb");
838 *(buffer
+bufLen
) = 0;
839 dataerrln("could not open any of the conformance test files, tried opening base %s", buffer
);
843 "INFO: Working with the stub file.\n"
844 "If you need the full conformance test, please\n"
845 "download the appropriate data files from:\n"
846 "http://source.icu-project.org/repos/icu/tools/trunk/unicodetools/com/ibm/text/data/");
851 LocalArray
<Line
> lines(new Line
[200000]);
852 memset(lines
.getAlias(), 0, sizeof(Line
)*200000);
858 while (fgets(buffer
, 1024, testFile
) != NULL
) {
859 if(*buffer
== 0 || buffer
[0] == '#') {
860 // Store empty and comment lines so that errors are reported
861 // for the real test file lines.
862 lines
[lineNum
].buflen
= 0;
863 lines
[lineNum
].buff
[0] = 0;
865 int32_t buflen
= u_parseString(buffer
, bufferU
, 1024, &first
, &status
);
866 lines
[lineNum
].buflen
= buflen
;
867 u_memcpy(lines
[lineNum
].buff
, bufferU
, buflen
);
868 lines
[lineNum
].buff
[buflen
] = 0;
873 if(U_FAILURE(status
)) {
874 dataerrln("Couldn't read the test file!");
878 UVersionInfo uniVersion
;
879 static const UVersionInfo v62
= { 6, 2, 0, 0 };
880 u_getUnicodeVersion(uniVersion
);
881 UBool isAtLeastUCA62
= uprv_memcmp(uniVersion
, v62
, 4) >= 0;
883 LocalPointer
<Collator
> coll(Collator::createInstance(Locale::getRoot(), status
));
884 if(U_FAILURE(status
)) {
885 errcheckln(status
, "Couldn't open UCA collator");
888 coll
->setAttribute(UCOL_NORMALIZATION_MODE
, UCOL_ON
, status
);
889 coll
->setAttribute(UCOL_CASE_FIRST
, UCOL_OFF
, status
);
890 coll
->setAttribute(UCOL_CASE_LEVEL
, UCOL_OFF
, status
);
891 coll
->setAttribute(UCOL_STRENGTH
, isAtLeastUCA62
? UCOL_IDENTICAL
: UCOL_TERTIARY
, status
);
892 coll
->setAttribute(UCOL_ALTERNATE_HANDLING
, UCOL_NON_IGNORABLE
, status
);
894 int32_t spawnResult
= 0;
895 LocalArray
<CollatorThreadTest
> tests(new CollatorThreadTest
[kCollatorThreadThreads
]);
897 logln(UnicodeString(u
"Spawning: ") + kCollatorThreadThreads
+ u
" threads * " + kFormatThreadIterations
+ u
" iterations each.");
899 for(j
= 0; j
< kCollatorThreadThreads
; j
++) {
900 //logln("Setting collator %i", j);
901 tests
[j
].setCollator(coll
.getAlias(), lines
.getAlias(), lineNum
, isAtLeastUCA62
);
903 for(j
= 0; j
< kCollatorThreadThreads
; j
++) {
905 spawnResult
= tests
[j
].start();
906 if(spawnResult
!= 0) {
907 errln("%s:%d THREAD INFO: thread %d failed to start with status %d", __FILE__
, __LINE__
, j
, spawnResult
);
911 logln("Spawned all");
913 for(int32_t i
=0;i
<kCollatorThreadThreads
;i
++) {
915 //logln(UnicodeString("Test #") + i + " is complete.. ");
919 #endif /* #if !UCONFIG_NO_COLLATION */
924 //-------------------------------------------------------------------------------------------
928 //-------------------------------------------------------------------------------------------
930 const int kStringThreadIterations
= 2500;// # of iterations per thread
931 const int kStringThreadThreads
= 10; // # of threads to spawn
934 class StringThreadTest2
: public SimpleThread
939 static const UnicodeString
*gSharedString
;
941 StringThreadTest2() // constructor is NOT multithread safe.
953 for (loopCount
= 0; loopCount
< kStringThreadIterations
; loopCount
++) {
954 if (*gSharedString
!= u
"This is the original test string.") {
955 IntlTest::gTest
->errln("%s:%d Original string is corrupt.", __FILE__
, __LINE__
);
958 UnicodeString s1
= *gSharedString
;
960 UnicodeString
s2(s1
);
961 UnicodeString s3
= *gSharedString
;
972 const UnicodeString
*StringThreadTest2::gSharedString
= NULL
;
974 // ** The actual test function.
977 void MultithreadTest::TestString()
980 StringThreadTest2::gSharedString
= new UnicodeString(u
"This is the original test string.");
981 StringThreadTest2 tests
[kStringThreadThreads
];
983 logln(UnicodeString(u
"Spawning: ") + kStringThreadThreads
+ u
" threads * " + kStringThreadIterations
+ u
" iterations each.");
984 for(j
= 0; j
< kStringThreadThreads
; j
++) {
985 int32_t threadStatus
= tests
[j
].start();
986 if (threadStatus
!= 0) {
987 errln("%s:%d System Error %d starting thread number %d.", __FILE__
, __LINE__
, threadStatus
, j
);
991 // Force a failure, to verify test is functioning and can report errors.
992 // const_cast<UnicodeString *>(StringThreadTest2::gSharedString)->setCharAt(5, 'x');
994 for(j
=0; j
<kStringThreadThreads
; j
++) {
996 logln(UnicodeString(u
"Test #") + j
+ u
" is complete.. ");
999 delete StringThreadTest2::gSharedString
;
1000 StringThreadTest2::gSharedString
= NULL
;
1005 // Test for ticket #10673, race in cache code in AnyTransliterator.
1006 // It's difficult to make the original unsafe code actually fail, but
1007 // this test will fairly reliably take the code path for races in
1008 // populating the cache.
1011 #if !UCONFIG_NO_TRANSLITERATION
1012 Transliterator
*gSharedTranslit
= NULL
;
1013 class TxThread
: public SimpleThread
{
1020 TxThread::~TxThread() {}
1021 void TxThread::run() {
1022 UnicodeString
greekString(u
"διαφορετικούς");
1023 gSharedTranslit
->transliterate(greekString
);
1024 IntlTest::gTest
->assertEquals(WHERE
, UnicodeString(u
"diaphoretikoús"), greekString
);
1029 void MultithreadTest::TestAnyTranslit() {
1030 #if !UCONFIG_NO_TRANSLITERATION
1031 UErrorCode status
= U_ZERO_ERROR
;
1032 LocalPointer
<Transliterator
> tx(Transliterator::createInstance("Any-Latin", UTRANS_FORWARD
, status
));
1033 if (!assertSuccess(WHERE
, status
, true)) { return; }
1035 gSharedTranslit
= tx
.getAlias();
1036 TxThread threads
[4];
1038 for (i
=0; i
<UPRV_LENGTHOF(threads
); i
++) {
1042 for (i
=0; i
<UPRV_LENGTHOF(threads
); i
++) {
1045 gSharedTranslit
= NULL
;
1046 #endif // !UCONFIG_NO_TRANSLITERATION
1052 // Unified Cache Test
1055 // Each thread fetches a pair of objects. There are 8 distinct pairs:
1056 // ("en_US", "bs"), ("en_GB", "ca"), ("fr_FR", "ca_AD") etc.
1057 // These pairs represent 8 distinct languages
1059 // Note that only one value per language gets created in the cache.
1060 // In particular each cached value can have multiple keys.
1061 static const char *gCacheLocales
[] = {
1062 "en_US", "en_GB", "fr_FR", "fr",
1063 "de", "sr_ME", "sr_BA", "sr_CS"};
1064 static const char *gCacheLocales2
[] = {
1065 "bs", "ca", "ca_AD", "ca_ES",
1066 "en_US", "fi", "ff_CM", "ff_GN"};
1068 static int32_t gObjectsCreated
= 0; // protected by gCTMutex
1069 static const int32_t CACHE_LOAD
= 3;
1071 class UCTMultiThreadItem
: public SharedObject
{
1074 UCTMultiThreadItem(const char *x
) : value(NULL
) {
1075 value
= uprv_strdup(x
);
1077 virtual ~UCTMultiThreadItem() {
1084 static std::mutex
*gCTMutex
= nullptr;
1085 static std::condition_variable
*gCTConditionVar
= nullptr;
1088 const UCTMultiThreadItem
*LocaleCacheKey
<UCTMultiThreadItem
>::createObject(
1089 const void *context
, UErrorCode
&status
) const {
1090 const UnifiedCache
*cacheContext
= (const UnifiedCache
*) context
;
1092 if (uprv_strcmp(fLoc
.getLanguage(), fLoc
.getName()) != 0) {
1093 const UCTMultiThreadItem
*result
= NULL
;
1094 if (cacheContext
== NULL
) {
1095 UnifiedCache::getByLocale(fLoc
.getLanguage(), result
, status
);
1098 cacheContext
->get(LocaleCacheKey
<UCTMultiThreadItem
>(fLoc
.getLanguage()), result
, status
);
1102 bool firstObject
= false;
1104 std::unique_lock
<std::mutex
> lock(*gCTMutex
);
1105 firstObject
= (gObjectsCreated
== 0);
1107 // Force the first object creation that comes through to wait
1108 // until other have completed. Verifies that cache doesn't
1109 // deadlock when a creation is slow.
1111 // Note that gObjectsCreated needs to be incremeneted from 0 to 1
1112 // early, to keep subsequent threads from entering this path.
1113 gObjectsCreated
= 1;
1114 while (gObjectsCreated
< 3) {
1115 gCTConditionVar
->wait(lock
);
1120 const UCTMultiThreadItem
*result
=
1121 new UCTMultiThreadItem(fLoc
.getLanguage());
1122 if (result
== NULL
) {
1123 status
= U_MEMORY_ALLOCATION_ERROR
;
1128 // Log that we created an object. The first object was already counted,
1129 // don't do it again.
1131 std::unique_lock
<std::mutex
> lock(*gCTMutex
);
1133 gObjectsCreated
+= 1;
1135 gCTConditionVar
->notify_all();
1143 class UnifiedCacheThread
: public SimpleThread
{
1146 const UnifiedCache
*cache
,
1148 const char *loc2
) : fCache(cache
), fLoc(loc
), fLoc2(loc2
) {}
1149 ~UnifiedCacheThread() {}
1151 void exerciseByLocale(const Locale
&);
1152 const UnifiedCache
*fCache
;
1157 void UnifiedCacheThread::exerciseByLocale(const Locale
&locale
) {
1158 UErrorCode status
= U_ZERO_ERROR
;
1159 const UCTMultiThreadItem
*origItem
= NULL
;
1161 LocaleCacheKey
<UCTMultiThreadItem
>(locale
), fCache
, origItem
, status
);
1162 U_ASSERT(U_SUCCESS(status
));
1163 IntlTest::gTest
->assertEquals(WHERE
, locale
.getLanguage(), origItem
->value
);
1165 // Fetch the same item again many times. We should always get the same
1166 // pointer since this client is already holding onto it
1167 for (int32_t i
= 0; i
< 1000; ++i
) {
1168 const UCTMultiThreadItem
*item
= NULL
;
1170 LocaleCacheKey
<UCTMultiThreadItem
>(locale
), fCache
, item
, status
);
1171 IntlTest::gTest
->assertTrue(WHERE
, item
== origItem
);
1176 origItem
->removeRef();
1179 void UnifiedCacheThread::run() {
1180 // Run the exercise with 2 different locales so that we can exercise
1181 // eviction more. If each thread exercises just one locale, then
1182 // eviction can't start until the threads end.
1183 exerciseByLocale(fLoc
);
1184 exerciseByLocale(fLoc2
);
1187 void MultithreadTest::TestUnifiedCache() {
1189 // Start with our own local cache so that we have complete control
1190 // and set the eviction policy to evict starting with 2 unused
1192 UErrorCode status
= U_ZERO_ERROR
;
1193 UnifiedCache::getInstance(status
);
1194 UnifiedCache
cache(status
);
1195 cache
.setEvictionPolicy(2, 0, status
);
1196 U_ASSERT(U_SUCCESS(status
));
1198 gCTMutex
= new std::mutex();
1199 gCTConditionVar
= new std::condition_variable();
1201 gObjectsCreated
= 0;
1203 UnifiedCacheThread
*threads
[CACHE_LOAD
][UPRV_LENGTHOF(gCacheLocales
)];
1204 for (int32_t i
=0; i
<CACHE_LOAD
; ++i
) {
1205 for (int32_t j
=0; j
<UPRV_LENGTHOF(gCacheLocales
); ++j
) {
1206 // Each thread works with a pair of locales.
1207 threads
[i
][j
] = new UnifiedCacheThread(
1208 &cache
, gCacheLocales
[j
], gCacheLocales2
[j
]);
1209 threads
[i
][j
]->start();
1213 for (int32_t i
=0; i
<CACHE_LOAD
; ++i
) {
1214 for (int32_t j
=0; j
<UPRV_LENGTHOF(gCacheLocales
); ++j
) {
1215 threads
[i
][j
]->join();
1218 // Because of cache eviction, we can't assert exactly how many
1219 // distinct objects get created over the course of this run.
1220 // However we know that at least 8 objects get created because that
1221 // is how many distinct languages we have in our test.
1222 if (gObjectsCreated
< 8) {
1223 errln("%s:%d Too few objects created.", __FILE__
, __LINE__
);
1225 // We know that each thread cannot create more than 2 objects in
1226 // the cache, and there are UPRV_LENGTHOF(gCacheLocales) pairs of
1227 // objects fetched from the cache. If the threads run in series because
1228 // of eviction, at worst case each thread creates two objects.
1229 if (gObjectsCreated
> 2 * CACHE_LOAD
* UPRV_LENGTHOF(gCacheLocales
)) {
1230 errln("%s:%d Too many objects created, got %d, expected %d", __FILE__
, __LINE__
, gObjectsCreated
, 2 * CACHE_LOAD
* UPRV_LENGTHOF(gCacheLocales
));
1234 assertEquals(WHERE
, 2, cache
.unusedCount());
1237 for (int32_t i
=0; i
<CACHE_LOAD
; ++i
) {
1238 for (int32_t j
=0; j
<UPRV_LENGTHOF(gCacheLocales
); ++j
) {
1239 delete threads
[i
][j
];
1243 delete gCTConditionVar
;
1246 #if !UCONFIG_NO_TRANSLITERATION
1248 // BreakTransliterator Threading Test
1249 // This is a test for bug #11603. Test verified to fail prior to fix.
1252 static const Transliterator
*gSharedTransliterator
;
1253 static const UnicodeString
*gTranslitInput
;
1254 static const UnicodeString
*gTranslitExpected
;
1256 class BreakTranslitThread
: public SimpleThread
{
1258 BreakTranslitThread() {}
1259 ~BreakTranslitThread() {}
1263 void BreakTranslitThread::run() {
1264 for (int i
=0; i
<10; i
++) {
1265 icu::UnicodeString
s(*gTranslitInput
);
1266 gSharedTransliterator
->transliterate(s
);
1267 if (*gTranslitExpected
!= s
) {
1268 IntlTest::gTest
->errln("%s:%d Transliteration threading failure.", __FILE__
, __LINE__
);
1274 void MultithreadTest::TestBreakTranslit() {
1275 UErrorCode status
= U_ZERO_ERROR
;
1276 UnicodeString
input(
1277 u
"\u0E42\u0E14\u0E22\u0E1E\u0E37\u0E49\u0E19\u0E10\u0E32\u0E19\u0E41\u0E25\u0E49\u0E27,");
1278 // Thai script, โดยพื้นฐานแล้ว
1279 gTranslitInput
= &input
;
1281 gSharedTransliterator
= Transliterator::createInstance(
1282 UnicodeString(u
"Any-Latin; Lower; NFD; [:Diacritic:]Remove; NFC; Latin-ASCII;"), UTRANS_FORWARD
, status
);
1283 assertSuccess(WHERE
, status
);
1284 if (!assertTrue(WHERE
, gSharedTransliterator
!= nullptr)) {
1288 UnicodeString
expected(*gTranslitInput
);
1289 gSharedTransliterator
->transliterate(expected
);
1290 gTranslitExpected
= &expected
;
1292 BreakTranslitThread threads
[4];
1293 for (int i
=0; i
<UPRV_LENGTHOF(threads
); ++i
) {
1296 for (int i
=0; i
<UPRV_LENGTHOF(threads
); ++i
) {
1300 delete gSharedTransliterator
;
1301 gTranslitInput
= NULL
;
1302 gTranslitExpected
= NULL
;
1306 class TestIncDecThread
: public SimpleThread
{
1308 TestIncDecThread() {}
1312 static u_atomic_int32_t gIncDecCounter
;
1314 void TestIncDecThread::run() {
1315 umtx_atomic_inc(&gIncDecCounter
);
1316 for (int32_t i
=0; i
<5000000; ++i
) {
1317 umtx_atomic_inc(&gIncDecCounter
);
1318 umtx_atomic_dec(&gIncDecCounter
);
1322 void MultithreadTest::TestIncDec()
1324 static constexpr int NUM_THREADS
= 4;
1326 TestIncDecThread threads
[NUM_THREADS
];
1327 for (auto &thread
:threads
) {
1330 for (auto &thread
:threads
) {
1333 assertEquals(WHERE
, NUM_THREADS
, gIncDecCounter
);
1336 #if !UCONFIG_NO_FORMATTING
1337 static Calendar
*gSharedCalendar
= {};
1339 class Test20104Thread
: public SimpleThread
{
1341 Test20104Thread() {}
1345 void Test20104Thread::run() {
1346 gSharedCalendar
->defaultCenturyStartYear();
1349 void MultithreadTest::Test20104() {
1350 UErrorCode status
= U_ZERO_ERROR
;
1351 Locale
loc("hi_IN");
1352 gSharedCalendar
= new IndianCalendar(loc
, status
);
1353 assertSuccess(WHERE
, status
);
1355 static constexpr int NUM_THREADS
= 4;
1356 Test20104Thread threads
[NUM_THREADS
];
1357 for (auto &thread
:threads
) {
1360 for (auto &thread
:threads
) {
1363 delete gSharedCalendar
;
1364 // Note: failure is reported by Thread Sanitizer. Test itself succeeds.
1366 #endif /* !UCONFIG_NO_FORMATTING */
1368 #endif /* !UCONFIG_NO_TRANSLITERATION */