]>
Commit | Line | Data |
---|---|---|
1 | /******************************************************************** | |
2 | * COPYRIGHT: | |
3 | * Copyright (c) 1999-2003, International Business Machines Corporation and | |
4 | * others. All Rights Reserved. | |
5 | ********************************************************************/ | |
6 | ||
7 | #if defined(hpux) | |
8 | # ifndef _INCLUDE_POSIX_SOURCE | |
9 | # define _INCLUDE_POSIX_SOURCE | |
10 | # endif | |
11 | #endif | |
12 | ||
13 | #include <unicode/umachine.h> | |
14 | ||
15 | // Just turn off threads on cygwin, so that we can test | |
16 | // the other stuff. This needs to be investigated further. | |
17 | #if defined(U_CYGWIN) | |
18 | #define ICU_USE_THREADS 0 | |
19 | #endif | |
20 | ||
21 | #if !defined(WIN32) && !defined(XP_MAC) && !defined(U_RHAPSODY) | |
22 | #define POSIX 1 | |
23 | #endif | |
24 | ||
25 | #if defined(POSIX) || defined(U_SOLARIS) || defined(AIX) || defined(HPUX) | |
26 | ||
27 | #define HAVE_IMP | |
28 | ||
29 | #if (ICU_USE_THREADS == 1) | |
30 | #include <pthread.h> | |
31 | #endif | |
32 | ||
33 | #if defined(__hpux) && defined(HPUX_CMA) | |
34 | # if defined(read) // read being defined as cma_read causes trouble with iostream::read | |
35 | # undef read | |
36 | # endif | |
37 | #endif | |
38 | ||
39 | /* Define __EXTENSIONS__ for Solaris and old friends in strict mode. */ | |
40 | #ifndef __EXTENSIONS__ | |
41 | #define __EXTENSIONS__ | |
42 | #endif | |
43 | ||
44 | #include <signal.h> | |
45 | ||
46 | /* Define _XPG4_2 for Solaris and friends. */ | |
47 | #ifndef _XPG4_2 | |
48 | #define _XPG4_2 | |
49 | #endif | |
50 | ||
51 | /* Define __USE_XOPEN_EXTENDED for Linux and glibc. */ | |
52 | #ifndef __USE_XOPEN_EXTENDED | |
53 | #define __USE_XOPEN_EXTENDED | |
54 | #endif | |
55 | ||
56 | /* Define _INCLUDE_XOPEN_SOURCE_EXTENDED for HP/UX (11?). */ | |
57 | #ifndef _INCLUDE_XOPEN_SOURCE_EXTENDED | |
58 | #define _INCLUDE_XOPEN_SOURCE_EXTENDED | |
59 | #endif | |
60 | ||
61 | #include <unistd.h> | |
62 | ||
63 | #endif | |
64 | /* HPUX */ | |
65 | #ifdef sleep | |
66 | #undef sleep | |
67 | #endif | |
68 | ||
69 | #include "unicode/utypes.h" | |
70 | ||
71 | /* APP_NO_THREADS is an old symbol. We'll honour it if present. */ | |
72 | #ifdef APP_NO_THREADS | |
73 | # define ICU_USE_THREADS 0 | |
74 | #endif | |
75 | ||
76 | /* Default: use threads. */ | |
77 | #ifndef ICU_USE_THREADS | |
78 | # define ICU_USE_THREADS 1 | |
79 | #endif | |
80 | ||
81 | #include "tsmthred.h" | |
82 | ||
83 | ||
84 | MultithreadTest::MultithreadTest() | |
85 | { | |
86 | } | |
87 | ||
88 | MultithreadTest::~MultithreadTest() | |
89 | { | |
90 | } | |
91 | ||
92 | ||
93 | ||
94 | #if (ICU_USE_THREADS==0) | |
95 | void MultithreadTest::runIndexedTest( int32_t index, UBool exec, | |
96 | const char* &name, char* par ) { | |
97 | if (exec) logln("TestSuite MultithreadTest: "); | |
98 | ||
99 | if(index == 0) | |
100 | name = "NO_THREADED_TESTS"; | |
101 | else | |
102 | name = ""; | |
103 | ||
104 | if(exec) { logln("MultithreadTest - test DISABLED. ICU_USE_THREADS set to 0, check your configuration if this is a problem.."); | |
105 | } | |
106 | } | |
107 | #else | |
108 | ||
109 | ||
110 | ||
111 | // Note: A LOT OF THE FUNCTIONS IN THIS FILE SHOULD LIVE ELSEWHERE!!!!! | |
112 | // Note: A LOT OF THE FUNCTIONS IN THIS FILE SHOULD LIVE ELSEWHERE!!!!! | |
113 | // -srl | |
114 | ||
115 | #include <stdio.h> | |
116 | #include <string.h> | |
117 | #include <ctype.h> // tolower, toupper | |
118 | ||
119 | #include "unicode/putil.h" | |
120 | ||
121 | /* for mthreadtest*/ | |
122 | #include "unicode/numfmt.h" | |
123 | #include "unicode/choicfmt.h" | |
124 | #include "unicode/msgfmt.h" | |
125 | #include "unicode/locid.h" | |
126 | #include "unicode/ucol.h" | |
127 | #include "ucaconf.h" | |
128 | ||
129 | #ifdef WIN32 | |
130 | #define HAVE_IMP | |
131 | ||
132 | # define VC_EXTRALEAN | |
133 | # define WIN32_LEAN_AND_MEAN | |
134 | # define NOGDI | |
135 | # define NOUSER | |
136 | # define NOSERVICE | |
137 | # define NOIME | |
138 | # define NOMCX | |
139 | #include <windows.h> | |
140 | #include <process.h> | |
141 | ||
142 | struct Win32ThreadImplementation | |
143 | { | |
144 | unsigned long fHandle; | |
145 | }; | |
146 | ||
147 | extern "C" void __cdecl SimpleThreadProc(void *arg) | |
148 | { | |
149 | ((SimpleThread*)arg)->run(); | |
150 | } | |
151 | ||
152 | SimpleThread::SimpleThread() | |
153 | :fImplementation(0) | |
154 | { | |
155 | Win32ThreadImplementation *imp = new Win32ThreadImplementation; | |
156 | imp->fHandle = 0; | |
157 | ||
158 | fImplementation = imp; | |
159 | } | |
160 | ||
161 | SimpleThread::~SimpleThread() | |
162 | { | |
163 | delete (Win32ThreadImplementation*)fImplementation; | |
164 | } | |
165 | ||
166 | int32_t SimpleThread::start() | |
167 | { | |
168 | Win32ThreadImplementation *imp = (Win32ThreadImplementation*)fImplementation; | |
169 | if(imp->fHandle != NULL) { | |
170 | // The thread appears to have already been started. | |
171 | // This is probably an error on the part of our caller. | |
172 | return -1; | |
173 | } | |
174 | ||
175 | imp->fHandle = _beginthread( SimpleThreadProc, 0 /*stack size*/ , (void *)this ); | |
176 | if (imp->fHandle == -1) { | |
177 | // An error occured | |
178 | int err = errno; | |
179 | if (err == 0) { | |
180 | err = -1; | |
181 | } | |
182 | return err; | |
183 | } | |
184 | return 0; | |
185 | } | |
186 | ||
187 | void SimpleThread::sleep(int32_t millis) | |
188 | { | |
189 | ::Sleep(millis); | |
190 | } | |
191 | ||
192 | #elif defined XP_MAC | |
193 | ||
194 | // since the Mac has no preemptive threading (at least on MacOS 8), only | |
195 | // cooperative threading, threads are a no-op. We have no yield() calls | |
196 | // anywhere in the ICU, so we are guaranteed to be thread-safe. | |
197 | ||
198 | #define HAVE_IMP | |
199 | ||
200 | SimpleThread::SimpleThread() | |
201 | {} | |
202 | ||
203 | SimpleThread::~SimpleThread() | |
204 | {} | |
205 | ||
206 | int32_t | |
207 | SimpleThread::start() | |
208 | { return 0; } | |
209 | ||
210 | void | |
211 | SimpleThread::run() | |
212 | {} | |
213 | ||
214 | void | |
215 | SimpleThread::sleep(int32_t millis) | |
216 | {} | |
217 | #endif | |
218 | ||
219 | ||
220 | #if defined(POSIX)||defined(U_SOLARIS)||defined(AIX)||defined(HPUX) | |
221 | #define HAVE_IMP | |
222 | ||
223 | struct PosixThreadImplementation | |
224 | { | |
225 | pthread_t fThread; | |
226 | }; | |
227 | ||
228 | extern "C" void* SimpleThreadProc(void *arg) | |
229 | { | |
230 | ((SimpleThread*)arg)->run(); | |
231 | return 0; | |
232 | } | |
233 | ||
234 | SimpleThread::SimpleThread() :fImplementation(0) | |
235 | { | |
236 | PosixThreadImplementation *imp = new PosixThreadImplementation; | |
237 | fImplementation = imp; | |
238 | } | |
239 | ||
240 | SimpleThread::~SimpleThread() | |
241 | { | |
242 | delete (PosixThreadImplementation*)fImplementation; | |
243 | } | |
244 | ||
245 | int32_t SimpleThread::start() | |
246 | { | |
247 | PosixThreadImplementation *imp = (PosixThreadImplementation*)fImplementation; | |
248 | ||
249 | int32_t rc; | |
250 | ||
251 | pthread_attr_t attr; | |
252 | ||
253 | #ifdef HPUX_CMA | |
254 | rc = pthread_attr_create(&attr); | |
255 | rc = pthread_create(&(imp->fThread),attr,&SimpleThreadProc,(void*)this); | |
256 | pthread_attr_delete(&attr); | |
257 | #else | |
258 | rc = pthread_attr_init(&attr); | |
259 | rc = pthread_create(&(imp->fThread),&attr,&SimpleThreadProc,(void*)this); | |
260 | pthread_attr_destroy(&attr); | |
261 | #endif | |
262 | return rc; | |
263 | ||
264 | } | |
265 | ||
266 | void SimpleThread::sleep(int32_t millis) | |
267 | { | |
268 | #ifdef U_SOLARIS | |
269 | sigignore(SIGALRM); | |
270 | #endif | |
271 | ||
272 | #ifdef HPUX_CMA | |
273 | cma_sleep(millis/100); | |
274 | #elif defined(HPUX) || defined(OS390) | |
275 | millis *= 1000; | |
276 | while(millis >= 1000000) { | |
277 | usleep(999999); | |
278 | millis -= 1000000; | |
279 | } | |
280 | if(millis > 0) { | |
281 | usleep(millis); | |
282 | } | |
283 | #else | |
284 | usleep(millis * 1000); | |
285 | #endif | |
286 | } | |
287 | ||
288 | #endif | |
289 | // end POSIX | |
290 | ||
291 | ||
292 | #ifndef HAVE_IMP | |
293 | #error No implementation for threads! Cannot test. | |
294 | 0 = 216; //die | |
295 | #endif | |
296 | ||
297 | ||
298 | // *************** end fluff ****************** | |
299 | ||
300 | /* now begins the real test. */ | |
301 | void MultithreadTest::runIndexedTest( int32_t index, UBool exec, | |
302 | const char* &name, char* /*par*/ ) { | |
303 | if (exec) | |
304 | logln("TestSuite MultithreadTest: "); | |
305 | switch (index) { | |
306 | case 0: | |
307 | name = "TestThreads"; | |
308 | if (exec) | |
309 | TestThreads(); | |
310 | break; | |
311 | case 1: | |
312 | name = "TestMutex"; | |
313 | if (exec) | |
314 | TestMutex(); | |
315 | break; | |
316 | case 2: | |
317 | name = "TestThreadedIntl"; | |
318 | #if !UCONFIG_NO_FORMATTING | |
319 | if (exec) | |
320 | TestThreadedIntl(); | |
321 | #endif | |
322 | break; | |
323 | case 3: | |
324 | name = "TestCollators"; | |
325 | #if !UCONFIG_NO_COLLATION | |
326 | if (exec) | |
327 | TestCollators(); | |
328 | #endif /* #if !UCONFIG_NO_COLLATION */ | |
329 | break; | |
330 | default: | |
331 | name = ""; | |
332 | break; //needed to end loop | |
333 | } | |
334 | } | |
335 | ||
336 | ||
337 | /* | |
338 | TestThreads -- see if threads really work at all. | |
339 | ||
340 | Set up N threads pointing at N chars. When they are started, they will | |
341 | each sleep 1 second and then set their chars. At the end we make sure they | |
342 | are all set. | |
343 | */ | |
344 | ||
345 | #define THREADTEST_NRTHREADS 8 | |
346 | ||
347 | class TestThreadsThread : public SimpleThread | |
348 | { | |
349 | public: | |
350 | TestThreadsThread(char* whatToChange) { fWhatToChange = whatToChange; } | |
351 | virtual void run() { SimpleThread::sleep(1000); | |
352 | Mutex m; | |
353 | *fWhatToChange = '*'; | |
354 | } | |
355 | private: | |
356 | char *fWhatToChange; | |
357 | }; | |
358 | ||
359 | void MultithreadTest::TestThreads() | |
360 | { | |
361 | char threadTestChars[THREADTEST_NRTHREADS + 1]; | |
362 | SimpleThread *threads[THREADTEST_NRTHREADS]; | |
363 | ||
364 | int32_t i; | |
365 | for(i=0;i<THREADTEST_NRTHREADS;i++) | |
366 | { | |
367 | threadTestChars[i] = ' '; | |
368 | threads[i] = new TestThreadsThread(&threadTestChars[i]); | |
369 | } | |
370 | threadTestChars[THREADTEST_NRTHREADS] = '\0'; | |
371 | ||
372 | logln("->" + UnicodeString(threadTestChars) + "<- Firing off threads.. "); | |
373 | for(i=0;i<THREADTEST_NRTHREADS;i++) | |
374 | { | |
375 | if (threads[i]->start() != 0) { | |
376 | errln("Error starting thread %d", i); | |
377 | } | |
378 | SimpleThread::sleep(200); | |
379 | logln(" Subthread started."); | |
380 | } | |
381 | ||
382 | logln("Waiting for threads to be set.."); | |
383 | ||
384 | int32_t patience = 40; // seconds to wait | |
385 | ||
386 | while(patience--) | |
387 | { | |
388 | int32_t count = 0; | |
389 | umtx_lock(NULL); | |
390 | for(i=0;i<THREADTEST_NRTHREADS;i++) | |
391 | { | |
392 | if(threadTestChars[i] == '*') | |
393 | { | |
394 | count++; | |
395 | } | |
396 | } | |
397 | umtx_unlock(NULL); | |
398 | ||
399 | if(count == THREADTEST_NRTHREADS) | |
400 | { | |
401 | logln("->" + UnicodeString(threadTestChars) + "<- Got all threads! cya"); | |
402 | for(i=0;i<THREADTEST_NRTHREADS;i++) | |
403 | { | |
404 | delete threads[i]; | |
405 | } | |
406 | return; | |
407 | } | |
408 | ||
409 | logln("->" + UnicodeString(threadTestChars) + "<- Waiting.."); | |
410 | SimpleThread::sleep(500); | |
411 | } | |
412 | ||
413 | errln("->" + UnicodeString(threadTestChars) + "<- PATIENCE EXCEEDED!! Still missing some."); | |
414 | for(i=0;i<THREADTEST_NRTHREADS;i++) | |
415 | { | |
416 | delete threads[i]; | |
417 | } | |
418 | } | |
419 | ||
420 | ||
421 | class TestMutexThread1 : public SimpleThread | |
422 | { | |
423 | public: | |
424 | TestMutexThread1() : fDone(FALSE) {} | |
425 | virtual void run() | |
426 | { | |
427 | Mutex m; // grab the lock first thing | |
428 | SimpleThread::sleep(900); // then wait | |
429 | fDone = TRUE; // finally, set our flag | |
430 | } | |
431 | public: | |
432 | UBool fDone; | |
433 | }; | |
434 | ||
435 | class TestMutexThread2 : public SimpleThread | |
436 | { | |
437 | public: | |
438 | TestMutexThread2(TestMutexThread1& r) : fOtherThread(r), fDone(FALSE), fErr(FALSE) {} | |
439 | virtual void run() | |
440 | { | |
441 | SimpleThread::sleep(500); // wait, make sure they aquire the lock | |
442 | fElapsed = uprv_getUTCtime(); | |
443 | { | |
444 | Mutex m; // wait here | |
445 | ||
446 | fElapsed = uprv_getUTCtime() - fElapsed; | |
447 | ||
448 | if(fOtherThread.fDone == FALSE) | |
449 | fErr = TRUE; // they didnt get to it yet | |
450 | ||
451 | fDone = TRUE; // we're done. | |
452 | } | |
453 | } | |
454 | public: | |
455 | TestMutexThread1 & fOtherThread; | |
456 | UBool fDone, fErr; | |
457 | int32_t fElapsed; | |
458 | private: | |
459 | /** | |
460 | * The assignment operator has no real implementation. | |
461 | * It is provided to make the compiler happy. Do not call. | |
462 | */ | |
463 | TestMutexThread2& operator=(const TestMutexThread2&) { return *this; } | |
464 | }; | |
465 | ||
466 | void MultithreadTest::TestMutex() | |
467 | { | |
468 | /* this test uses printf so that we don't hang by calling UnicodeString inside of a mutex. */ | |
469 | //logln("Bye."); | |
470 | // printf("Warning: MultiThreadTest::Testmutex() disabled.\n"); | |
471 | // return; | |
472 | ||
473 | if(verbose) | |
474 | printf("Before mutex.\n"); | |
475 | { | |
476 | Mutex m; | |
477 | if(verbose) | |
478 | printf(" Exited 2nd mutex\n"); | |
479 | } | |
480 | if(verbose) | |
481 | printf("exited 1st mutex. Now testing with threads:"); | |
482 | ||
483 | TestMutexThread1 thread1; | |
484 | TestMutexThread2 thread2(thread1); | |
485 | if (thread2.start() != 0 || | |
486 | thread1.start() != 0 ) { | |
487 | errln("Error starting threads."); | |
488 | } | |
489 | ||
490 | for(int32_t patience = 12; patience > 0;patience--) | |
491 | { | |
492 | // TODO: Possible memory coherence issue in looking at fDone values | |
493 | // that are set in another thread without the mutex here. | |
494 | if(thread1.fDone && verbose) | |
495 | printf("Thread1 done\n"); | |
496 | ||
497 | if(thread1.fDone && thread2.fDone) | |
498 | { | |
499 | if(thread2.fErr) | |
500 | errln("Thread 2 says: thread1 didn't run before I aquired the mutex."); | |
501 | logln("took %lu seconds for thread2 to aquire the mutex.", thread2.fElapsed); | |
502 | return; | |
503 | } | |
504 | SimpleThread::sleep(1000); | |
505 | } | |
506 | if(verbose) | |
507 | printf("patience exceeded. [WARNING mutex may still be acquired.] "); | |
508 | } | |
509 | ||
510 | // *********** | |
511 | // *********** TestMultithreadedIntl. Test the ICU in a multithreaded way. | |
512 | ||
513 | ||
514 | ||
515 | ||
516 | // ** First, some utility classes. | |
517 | ||
518 | // | |
519 | ///* Here is an idea which needs more work | |
520 | // TestATest simply runs another Intltest subset against itself. | |
521 | // The correct subset of intltest that should be run in this way should be identified. | |
522 | // */ | |
523 | // | |
524 | //class TestATest : public SimpleThread | |
525 | //{ | |
526 | //public: | |
527 | // TestATest(IntlTest &t) : fTest(t), fDone(FALSE) {} | |
528 | // virtual void run() | |
529 | // { | |
530 | // fTest.runTest(NULL,"TestNumberSpelloutFormat"); | |
531 | // fErrs = fTest.getErrors(); | |
532 | // fDone = TRUE; | |
533 | // } | |
534 | //public: | |
535 | // IntlTest &fTest; | |
536 | // UBool fDone; | |
537 | // int32_t fErrs; | |
538 | //}; | |
539 | // | |
540 | // | |
541 | //#include "itutil.h" | |
542 | ////#include "tscoll.h" | |
543 | ////#include "ittxtbd.h" | |
544 | //#include "itformat.h" | |
545 | ////#include "itcwrap.h" | |
546 | // | |
547 | ///* main code was: | |
548 | // IntlTestFormat formatTest; | |
549 | //// IntlTestCollator collatorTest; | |
550 | // | |
551 | // #define NUMTESTS 2 | |
552 | // TestATest tests[NUMTESTS] = { TestATest(formatTest), TestATest(formatTest) }; | |
553 | // char testName[NUMTESTS][20] = { "formatTest", "formatTest2" }; | |
554 | //*/ | |
555 | ||
556 | ||
557 | #include <string.h> | |
558 | ||
559 | // * Show exactly where the string's differences lie. | |
560 | UnicodeString showDifference(const UnicodeString& expected, const UnicodeString& result) | |
561 | { | |
562 | UnicodeString res; | |
563 | res = expected + "<Expected\n"; | |
564 | if(expected.length() != result.length()) | |
565 | res += " [ Different lengths ] \n"; | |
566 | else | |
567 | { | |
568 | for(int32_t i=0;i<expected.length();i++) | |
569 | { | |
570 | if(expected[i] == result[i]) | |
571 | { | |
572 | res += " "; | |
573 | } | |
574 | else | |
575 | { | |
576 | res += "|"; | |
577 | } | |
578 | } | |
579 | res += "<Differences"; | |
580 | res += "\n"; | |
581 | } | |
582 | res += result + "<Result\n"; | |
583 | ||
584 | return res; | |
585 | } | |
586 | ||
587 | ||
588 | // ** ThreadWithStatus - a thread that we can check the status and error condition of | |
589 | ||
590 | ||
591 | class ThreadWithStatus : public SimpleThread | |
592 | { | |
593 | public: | |
594 | UBool getDone() { return fDone; } | |
595 | UBool getError() { return (fErrors > 0); } | |
596 | UBool getError(UnicodeString& fillinError) { fillinError = fErrorString; return (fErrors > 0); } | |
597 | virtual ~ThreadWithStatus(){} | |
598 | protected: | |
599 | ThreadWithStatus() : fDone(FALSE), fErrors(0) {} | |
600 | void done() { fDone = TRUE; } | |
601 | void error(const UnicodeString &error) { fErrors++; fErrorString = error; done(); } | |
602 | void error() { error("An error occured."); } | |
603 | private: | |
604 | UBool fDone; | |
605 | int32_t fErrors; | |
606 | UnicodeString fErrorString; | |
607 | }; | |
608 | ||
609 | #define kFormatThreadIterations 20 // # of iterations per thread | |
610 | #define kFormatThreadThreads 10 // # of threads to spawn | |
611 | #define kFormatThreadPatience 60 // time in seconds to wait for all threads | |
612 | ||
613 | #if !UCONFIG_NO_FORMATTING | |
614 | ||
615 | // ** FormatThreadTest - a thread that tests performing a number of numberformats. | |
616 | ||
617 | ||
618 | struct FormatThreadTestData | |
619 | { | |
620 | double number; | |
621 | UnicodeString string; | |
622 | FormatThreadTestData(double a, const UnicodeString& b) : number(a),string(b) {} | |
623 | } ; | |
624 | ||
625 | ||
626 | void errorToString(UErrorCode theStatus, UnicodeString &string) | |
627 | { | |
628 | string=u_errorName(theStatus); | |
629 | } | |
630 | ||
631 | // "Someone from {2} is receiving a #{0} error - {1}. Their telephone call is costing {3 number,currency}." | |
632 | ||
633 | void formatErrorMessage(UErrorCode &realStatus, const UnicodeString& pattern, const Locale& theLocale, | |
634 | UErrorCode inStatus0, /* statusString 1 */ const Locale &inCountry2, double currency3, // these numbers are the message arguments. | |
635 | UnicodeString &result) | |
636 | { | |
637 | if(U_FAILURE(realStatus)) | |
638 | return; // you messed up | |
639 | ||
640 | UnicodeString errString1; | |
641 | errorToString(inStatus0, errString1); | |
642 | ||
643 | UnicodeString countryName2; | |
644 | inCountry2.getDisplayCountry(theLocale,countryName2); | |
645 | ||
646 | Formattable myArgs[] = { | |
647 | Formattable((int32_t)inStatus0), // inStatus0 {0} | |
648 | Formattable(errString1), // statusString1 {1} | |
649 | Formattable(countryName2), // inCountry2 {2} | |
650 | Formattable(currency3)// currency3 {3,number,currency} | |
651 | }; | |
652 | ||
653 | MessageFormat *fmt = new MessageFormat("MessageFormat's API is broken!!!!!!!!!!!",realStatus); | |
654 | fmt->setLocale(theLocale); | |
655 | fmt->applyPattern(pattern, realStatus); | |
656 | ||
657 | if (U_FAILURE(realStatus)) { | |
658 | delete fmt; | |
659 | return; | |
660 | } | |
661 | ||
662 | FieldPosition ignore = 0; | |
663 | fmt->format(myArgs,4,result,ignore,realStatus); | |
664 | ||
665 | delete fmt; | |
666 | }; | |
667 | ||
668 | static UMTX ftMutex; | |
669 | ||
670 | class FormatThreadTest : public ThreadWithStatus | |
671 | { | |
672 | public: | |
673 | FormatThreadTest() // constructor is NOT multithread safe. | |
674 | : ThreadWithStatus(), | |
675 | fOffset(0) | |
676 | // the locale to use | |
677 | { | |
678 | static int32_t fgOffset = 0; | |
679 | fgOffset += 3; | |
680 | fOffset = fgOffset; | |
681 | } | |
682 | ||
683 | ||
684 | virtual void run() | |
685 | { | |
686 | // Keep this data here to avoid static initialization. | |
687 | FormatThreadTestData kNumberFormatTestData[] = | |
688 | { | |
689 | FormatThreadTestData((double)5.0, UnicodeString("5", "")), | |
690 | FormatThreadTestData( 6.0, UnicodeString("6", "")), | |
691 | FormatThreadTestData( 20.0, UnicodeString("20", "")), | |
692 | FormatThreadTestData( 8.0, UnicodeString("8", "")), | |
693 | FormatThreadTestData( 8.3, UnicodeString("8.3", "")), | |
694 | FormatThreadTestData( 12345, UnicodeString("12,345", "")), | |
695 | FormatThreadTestData( 81890.23, UnicodeString("81,890.23", "")), | |
696 | }; | |
697 | int32_t kNumberFormatTestDataLength = (int32_t)(sizeof(kNumberFormatTestData) / sizeof(kNumberFormatTestData[0])); | |
698 | ||
699 | // Keep this data here to avoid static initialization. | |
700 | FormatThreadTestData kPercentFormatTestData[] = | |
701 | { | |
702 | FormatThreadTestData((double)5.0, UnicodeString("500%", "")), | |
703 | FormatThreadTestData( 1.0, UnicodeString("100%", "")), | |
704 | FormatThreadTestData( 0.26, UnicodeString("26%", "")), | |
705 | FormatThreadTestData( 16384.99, CharsToUnicodeString("1\\u00a0638\\u00a0499%") ), // U+00a0 = NBSP | |
706 | FormatThreadTestData( 81890.23, CharsToUnicodeString("8\\u00a0189\\u00a0023%" )), | |
707 | }; | |
708 | int32_t kPercentFormatTestDataLength = (int32_t)(sizeof(kPercentFormatTestData) / sizeof(kPercentFormatTestData[0])); | |
709 | int32_t iteration; | |
710 | ||
711 | UErrorCode status = U_ZERO_ERROR; | |
712 | NumberFormat *formatter = NumberFormat::createInstance(Locale::getEnglish(),status); | |
713 | ||
714 | if(U_FAILURE(status)) | |
715 | { | |
716 | Mutex m(&ftMutex); | |
717 | error("Error on NumberFormat::createInstance()"); | |
718 | return; | |
719 | } | |
720 | ||
721 | NumberFormat *percentFormatter = NumberFormat::createPercentInstance(Locale::getFrench(),status); | |
722 | ||
723 | if(U_FAILURE(status)) | |
724 | { | |
725 | { | |
726 | Mutex m(&ftMutex); | |
727 | error("Error on NumberFormat::createPercentInstance()"); | |
728 | } | |
729 | delete formatter; | |
730 | return; | |
731 | } | |
732 | ||
733 | for(iteration = 0;!getError() && iteration<kFormatThreadIterations;iteration++) | |
734 | { | |
735 | ||
736 | int32_t whichLine = (iteration + fOffset)%kNumberFormatTestDataLength; | |
737 | ||
738 | UnicodeString output; | |
739 | ||
740 | formatter->format(kNumberFormatTestData[whichLine].number, output); | |
741 | ||
742 | if(0 != output.compare(kNumberFormatTestData[whichLine].string)) | |
743 | { | |
744 | Mutex m(&ftMutex); | |
745 | error("format().. expected " + kNumberFormatTestData[whichLine].string + " got " + output); | |
746 | continue; // will break | |
747 | } | |
748 | ||
749 | // Now check percent. | |
750 | output.remove(); | |
751 | whichLine = (iteration + fOffset)%kPercentFormatTestDataLength; | |
752 | ||
753 | percentFormatter->format(kPercentFormatTestData[whichLine].number, output); | |
754 | ||
755 | if(0 != output.compare(kPercentFormatTestData[whichLine].string)) | |
756 | { | |
757 | Mutex m(&ftMutex); | |
758 | error("percent format().. \n" + showDifference(kPercentFormatTestData[whichLine].string,output)); | |
759 | continue; | |
760 | } | |
761 | ||
762 | // Test message error | |
763 | #define kNumberOfMessageTests 3 | |
764 | UErrorCode statusToCheck; | |
765 | UnicodeString patternToCheck; | |
766 | Locale messageLocale; | |
767 | Locale countryToCheck; | |
768 | double currencyToCheck; | |
769 | ||
770 | UnicodeString expected; | |
771 | ||
772 | // load the cases. | |
773 | switch((iteration+fOffset) % kNumberOfMessageTests) | |
774 | { | |
775 | default: | |
776 | case 0: | |
777 | statusToCheck= U_FILE_ACCESS_ERROR; | |
778 | patternToCheck= "0:Someone from {2} is receiving a #{0} error - {1}. Their telephone call is costing {3,number,currency}."; // number,currency | |
779 | messageLocale= Locale("en","US"); | |
780 | countryToCheck= Locale("","HR"); | |
781 | currencyToCheck= 8192.77; | |
782 | expected= "0:Someone from Croatia is receiving a #4 error - U_FILE_ACCESS_ERROR. Their telephone call is costing $8,192.77."; | |
783 | break; | |
784 | case 1: | |
785 | statusToCheck= U_INDEX_OUTOFBOUNDS_ERROR; | |
786 | patternToCheck= "1:A customer in {2} is receiving a #{0} error - {1}. Their telephone call is costing {3,number,currency}."; // number,currency | |
787 | messageLocale= Locale("de","DE_PREEURO"); | |
788 | countryToCheck= Locale("","BF"); | |
789 | currencyToCheck= 2.32; | |
790 | expected= "1:A customer in Burkina Faso is receiving a #8 error - U_INDEX_OUTOFBOUNDS_ERROR. Their telephone call is costing $2.32."; | |
791 | case 2: | |
792 | statusToCheck= U_MEMORY_ALLOCATION_ERROR; | |
793 | patternToCheck= "2:user in {2} is receiving a #{0} error - {1}. They insist they just spent {3,number,currency} on memory."; // number,currency | |
794 | messageLocale= Locale("de","AT_PREEURO"); // Austrian German | |
795 | countryToCheck= Locale("","US"); // hmm | |
796 | currencyToCheck= 40193.12; | |
797 | expected= CharsToUnicodeString("2:user in Vereinigte Staaten is receiving a #7 error - U_MEMORY_ALLOCATION_ERROR. They insist they just spent \\u00f6S 40.193,12 on memory."); | |
798 | break; | |
799 | } | |
800 | ||
801 | UnicodeString result; | |
802 | UErrorCode status = U_ZERO_ERROR; | |
803 | formatErrorMessage(status,patternToCheck,messageLocale,statusToCheck,countryToCheck,currencyToCheck,result); | |
804 | if(U_FAILURE(status)) | |
805 | { | |
806 | UnicodeString tmp; | |
807 | errorToString(status,tmp); | |
808 | Mutex m(&ftMutex); | |
809 | error("Failure on message format, pattern=" + patternToCheck +", error = " + tmp); | |
810 | continue; | |
811 | } | |
812 | ||
813 | if(result != expected) | |
814 | { | |
815 | Mutex m(&ftMutex); | |
816 | error("PatternFormat: \n" + showDifference(expected,result)); | |
817 | continue; | |
818 | } | |
819 | } | |
820 | ||
821 | delete formatter; | |
822 | delete percentFormatter; | |
823 | Mutex m(&ftMutex); | |
824 | done(); | |
825 | } | |
826 | ||
827 | private: | |
828 | int32_t fOffset; // where we are testing from. | |
829 | }; | |
830 | ||
831 | // ** The actual test function. | |
832 | ||
833 | void MultithreadTest::TestThreadedIntl() | |
834 | { | |
835 | umtx_init(&ftMutex); | |
836 | ||
837 | FormatThreadTest tests[kFormatThreadThreads]; | |
838 | ||
839 | logln(UnicodeString("Spawning: ") + kFormatThreadThreads + " threads * " + kFormatThreadIterations + " iterations each."); | |
840 | for(int32_t j = 0; j < kFormatThreadThreads; j++) { | |
841 | int32_t threadStatus = tests[j].start(); | |
842 | if (threadStatus != 0) { | |
843 | errln("System Error %d starting thread number %d.", threadStatus, j); | |
844 | return; | |
845 | } | |
846 | } | |
847 | ||
848 | int32_t patience; | |
849 | for(patience = kFormatThreadPatience;patience > 0; patience --) | |
850 | { | |
851 | logln("Waiting..."); | |
852 | ||
853 | int32_t i; | |
854 | int32_t terrs = 0; | |
855 | int32_t completed =0; | |
856 | ||
857 | for(i=0;i<kFormatThreadThreads;i++) { | |
858 | umtx_lock(&ftMutex); | |
859 | UBool threadIsDone = tests[i].getDone(); | |
860 | umtx_unlock(&ftMutex); | |
861 | if(threadIsDone) | |
862 | { | |
863 | completed++; | |
864 | ||
865 | logln(UnicodeString("Test #") + i + " is complete.. "); | |
866 | ||
867 | UnicodeString theErr; | |
868 | if(tests[i].getError(theErr)) | |
869 | { | |
870 | terrs++; | |
871 | errln(UnicodeString("#") + i + ": " + theErr); | |
872 | } | |
873 | // print out the error, too, if any. | |
874 | } | |
875 | } | |
876 | ||
877 | if(completed == kFormatThreadThreads) | |
878 | { | |
879 | logln("Done!"); | |
880 | ||
881 | if(terrs) | |
882 | { | |
883 | errln("There were errors."); | |
884 | } | |
885 | ||
886 | break; | |
887 | } | |
888 | ||
889 | SimpleThread::sleep(900); | |
890 | } | |
891 | ||
892 | if (patience <= 0) { | |
893 | errln("patience exceeded. "); | |
894 | } | |
895 | umtx_destroy(&ftMutex); | |
896 | return; | |
897 | } | |
898 | ||
899 | #endif /* #if !UCONFIG_NO_FORMATTING */ | |
900 | ||
901 | #if !UCONFIG_NO_COLLATION | |
902 | ||
903 | #define kCollatorThreadThreads 10 // # of threads to spawn | |
904 | #define kCollatorThreadPatience kCollatorThreadThreads*100 | |
905 | ||
906 | struct Line { | |
907 | UChar buff[25]; | |
908 | int32_t buflen; | |
909 | } ; | |
910 | ||
911 | class CollatorThreadTest : public ThreadWithStatus | |
912 | { | |
913 | private: | |
914 | const UCollator *coll; | |
915 | const Line *lines; | |
916 | int32_t noLines; | |
917 | public: | |
918 | CollatorThreadTest() : ThreadWithStatus(), | |
919 | coll(NULL), | |
920 | lines(NULL), | |
921 | noLines(0) | |
922 | { | |
923 | }; | |
924 | void setCollator(UCollator *c, Line *l, int32_t nl) | |
925 | { | |
926 | coll = c; | |
927 | lines = l; | |
928 | noLines = nl; | |
929 | } | |
930 | virtual void run() { | |
931 | //sleep(10000); | |
932 | int32_t line = 0; | |
933 | ||
934 | uint8_t sk1[1024], sk2[1024]; | |
935 | uint8_t *oldSk = NULL, *newSk = sk1; | |
936 | int32_t resLen = 0, oldLen = 0; | |
937 | int32_t i = 0; | |
938 | ||
939 | for(i = 0; i < noLines; i++) { | |
940 | resLen = ucol_getSortKey(coll, lines[i].buff, lines[i].buflen, newSk, 1024); | |
941 | ||
942 | int32_t res = 0, cmpres = 0, cmpres2 = 0; | |
943 | ||
944 | if(oldSk != NULL) { | |
945 | res = strcmp((char *)oldSk, (char *)newSk); | |
946 | cmpres = ucol_strcoll(coll, lines[i-1].buff, lines[i-1].buflen, lines[i].buff, lines[i].buflen); | |
947 | cmpres2 = ucol_strcoll(coll, lines[i].buff, lines[i].buflen, lines[i-1].buff, lines[i-1].buflen); | |
948 | //cmpres = res; | |
949 | //cmpres2 = -cmpres; | |
950 | ||
951 | if(cmpres != -cmpres2) { | |
952 | error("Compare result not symmetrical on line "+ line); | |
953 | } | |
954 | ||
955 | if(((res&0x80000000) != (cmpres&0x80000000)) || (res == 0 && cmpres != 0) || (res != 0 && cmpres == 0)) { | |
956 | error(UnicodeString("Difference between ucol_strcoll and sortkey compare on line ")+ UnicodeString(line)); | |
957 | } | |
958 | ||
959 | if(res > 0) { | |
960 | error(UnicodeString("Line %i is not greater or equal than previous line ")+ UnicodeString(i)); | |
961 | break; | |
962 | } else if(res == 0) { /* equal */ | |
963 | res = u_strcmpCodePointOrder(lines[i-1].buff, lines[i].buff); | |
964 | if (res == 0) { | |
965 | error(UnicodeString("Probable error in test file on line %i (comparing identical strings)")+ UnicodeString(i)); | |
966 | break; | |
967 | } else if (res > 0) { | |
968 | error(UnicodeString("Sortkeys are identical, but code point comapare gives >0 on line ")+ UnicodeString(i)); | |
969 | } | |
970 | } | |
971 | } | |
972 | ||
973 | oldSk = newSk; | |
974 | oldLen = resLen; | |
975 | ||
976 | newSk = (newSk == sk1)?sk2:sk1; | |
977 | } | |
978 | ||
979 | Mutex m; | |
980 | done(); | |
981 | } | |
982 | }; | |
983 | ||
984 | void MultithreadTest::TestCollators() | |
985 | { | |
986 | ||
987 | UErrorCode status = U_ZERO_ERROR; | |
988 | FILE *testFile = NULL; | |
989 | char testDataPath[1024]; | |
990 | uprv_strcpy(testDataPath, IntlTest::loadTestData(status)); | |
991 | char* index = 0; | |
992 | if (U_FAILURE(status)) { | |
993 | errln("ERROR: could not open test data %s", u_errorName(status)); | |
994 | return; | |
995 | } | |
996 | index=strrchr(testDataPath,(char)U_FILE_SEP_CHAR); | |
997 | ||
998 | if((unsigned int)(index-testDataPath) != (strlen(testDataPath)-1)){ | |
999 | *(index+1)=0; | |
1000 | } | |
1001 | uprv_strcat(testDataPath,".."U_FILE_SEP_STRING); | |
1002 | uprv_strcat(testDataPath, "CollationTest_"); | |
1003 | ||
1004 | const char* type = "NON_IGNORABLE"; | |
1005 | ||
1006 | const char *ext = ".txt"; | |
1007 | if(testFile) { | |
1008 | fclose(testFile); | |
1009 | } | |
1010 | char buffer[1024]; | |
1011 | uprv_strcpy(buffer, testDataPath); | |
1012 | uprv_strcat(buffer, type); | |
1013 | int32_t bufLen = uprv_strlen(buffer); | |
1014 | ||
1015 | // we try to open 3 files: | |
1016 | // path/CollationTest_type.txt | |
1017 | // path/CollationTest_type_SHORT.txt | |
1018 | // path/CollationTest_type_STUB.txt | |
1019 | // we are going to test with the first one that we manage to open. | |
1020 | ||
1021 | uprv_strcpy(buffer+bufLen, ext); | |
1022 | ||
1023 | testFile = fopen(buffer, "rb"); | |
1024 | ||
1025 | if(testFile == 0) { | |
1026 | uprv_strcpy(buffer+bufLen, "_SHORT"); | |
1027 | uprv_strcat(buffer, ext); | |
1028 | testFile = fopen(buffer, "rb"); | |
1029 | ||
1030 | if(testFile == 0) { | |
1031 | uprv_strcpy(buffer+bufLen, "_STUB"); | |
1032 | uprv_strcat(buffer, ext); | |
1033 | testFile = fopen(buffer, "rb"); | |
1034 | ||
1035 | if (testFile == 0) { | |
1036 | *(buffer+bufLen) = 0; | |
1037 | errln("ERROR: could not open any of the conformance test files, tried opening base %s", buffer); | |
1038 | return; | |
1039 | } else { | |
1040 | infoln( | |
1041 | "INFO: Working with the stub file.\n" | |
1042 | "If you need the full conformance test, please\n" | |
1043 | "download the appropriate data files from:\n" | |
1044 | "http://oss.software.ibm.com/cvs/icu4j/unicodetools/com/ibm/text/data/"); | |
1045 | } | |
1046 | } | |
1047 | } | |
1048 | ||
1049 | Line *lines = new Line[65000]; | |
1050 | uprv_memset(lines, 0, sizeof(Line)*65000); | |
1051 | int32_t lineNum = 0; | |
1052 | ||
1053 | UChar bufferU[1024]; | |
1054 | int32_t buflen = 0; | |
1055 | uint32_t first = 0; | |
1056 | uint32_t offset = 0; | |
1057 | ||
1058 | while (fgets(buffer, 1024, testFile) != NULL) { | |
1059 | offset = 0; | |
1060 | if(*buffer == 0 || buffer[0] == '#') { | |
1061 | continue; | |
1062 | } | |
1063 | offset = u_parseString(buffer, bufferU, 1024, &first, &status); | |
1064 | buflen = offset; | |
1065 | bufferU[offset++] = 0; | |
1066 | lines[lineNum].buflen = buflen; | |
1067 | //lines[lineNum].buff = new UChar[buflen+1]; | |
1068 | u_memcpy(lines[lineNum].buff, bufferU, buflen); | |
1069 | lineNum++; | |
1070 | } | |
1071 | fclose(testFile); | |
1072 | ||
1073 | ||
1074 | ||
1075 | UCollator *coll = ucol_open("root", &status); | |
1076 | if(U_FAILURE(status)) { | |
1077 | errln("Couldn't open UCA collator"); | |
1078 | return; | |
1079 | } | |
1080 | ucol_setAttribute(coll, UCOL_NORMALIZATION_MODE, UCOL_ON, &status); | |
1081 | ucol_setAttribute(coll, UCOL_CASE_FIRST, UCOL_OFF, &status); | |
1082 | ucol_setAttribute(coll, UCOL_CASE_LEVEL, UCOL_OFF, &status); | |
1083 | ucol_setAttribute(coll, UCOL_STRENGTH, UCOL_TERTIARY, &status); | |
1084 | ucol_setAttribute(coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE, &status); | |
1085 | ||
1086 | int32_t noSpawned = 0; | |
1087 | int32_t spawnResult = 0; | |
1088 | CollatorThreadTest *tests; | |
1089 | tests = new CollatorThreadTest[kCollatorThreadThreads]; | |
1090 | ||
1091 | logln(UnicodeString("Spawning: ") + kCollatorThreadThreads + " threads * " + kFormatThreadIterations + " iterations each."); | |
1092 | int32_t j = 0; | |
1093 | for(j = 0; j < kCollatorThreadThreads; j++) { | |
1094 | //logln("Setting collator %i", j); | |
1095 | tests[j].setCollator(coll, lines, lineNum); | |
1096 | } | |
1097 | for(j = 0; j < kCollatorThreadThreads; j++) { | |
1098 | log("%i ", j); | |
1099 | spawnResult = tests[j].start(); | |
1100 | if(spawnResult != 0) { | |
1101 | infoln("THREAD INFO: Couldn't spawn more than %i threads", noSpawned); | |
1102 | break; | |
1103 | } | |
1104 | noSpawned++; | |
1105 | } | |
1106 | logln("Spawned all"); | |
1107 | ||
1108 | //for(int32_t patience = kCollatorThreadPatience;patience > 0; patience --) | |
1109 | for(;;) | |
1110 | { | |
1111 | logln("Waiting..."); | |
1112 | ||
1113 | int32_t i; | |
1114 | int32_t terrs = 0; | |
1115 | int32_t completed =0; | |
1116 | ||
1117 | for(i=0;i<kCollatorThreadThreads;i++) | |
1118 | { | |
1119 | umtx_lock(NULL); | |
1120 | UBool threadIsDone = tests[i].getDone(); | |
1121 | umtx_unlock(NULL); | |
1122 | if(threadIsDone) | |
1123 | { | |
1124 | completed++; | |
1125 | ||
1126 | //logln(UnicodeString("Test #") + i + " is complete.. "); | |
1127 | ||
1128 | UnicodeString theErr; | |
1129 | if(tests[i].getError(theErr)) | |
1130 | { | |
1131 | terrs++; | |
1132 | errln(UnicodeString("#") + i + ": " + theErr); | |
1133 | } | |
1134 | // print out the error, too, if any. | |
1135 | } | |
1136 | } | |
1137 | logln("Completed %i tests", completed); | |
1138 | ||
1139 | if(completed == noSpawned) | |
1140 | { | |
1141 | logln("Done! All %i tests are finished", noSpawned); | |
1142 | ||
1143 | if(terrs) | |
1144 | { | |
1145 | errln("There were errors."); | |
1146 | } | |
1147 | ucol_close(coll); | |
1148 | delete[] tests; | |
1149 | //for(i = 0; i < lineNum; i++) { | |
1150 | //delete[] lines[i].buff; | |
1151 | //} | |
1152 | delete[] lines; | |
1153 | ||
1154 | return; | |
1155 | } | |
1156 | ||
1157 | SimpleThread::sleep(900); | |
1158 | } | |
1159 | errln("patience exceeded. "); | |
1160 | ucol_close(coll); | |
1161 | } | |
1162 | ||
1163 | #endif /* #if !UCONFIG_NO_COLLATION */ | |
1164 | ||
1165 | #endif // ICU_USE_THREADS | |
1166 |