1 /********************************************************************
3 * Copyright (c) 1997-2012, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 ********************************************************************/
6 /********************************************************************************
10 * Modification History:
12 * Madhu Katragadda Creation
13 *********************************************************************************
16 /*The main root for C API tests*/
21 #include "unicode/utypes.h"
22 #include "unicode/putil.h"
28 #include "unicode/uchar.h"
29 #include "unicode/ustring.h"
30 #include "unicode/ucnv.h"
31 #include "unicode/ures.h"
32 #include "unicode/uclean.h"
33 #include "unicode/ucal.h"
35 #include "putilimp.h" /* for uprv_getRawUTCtime() */
37 #include "uresimp.h" /* for ures_dumpCacheContents() */
44 #define CTST_MAX_ALLOC 8192
45 /* Array used as a queue */
46 static void * ctst_allocated_stuff
[CTST_MAX_ALLOC
] = {0};
47 static int ctst_allocated
= 0;
48 static UBool ctst_free
= FALSE
;
49 static int ctst_allocated_total
= 0;
51 #define CTST_LEAK_CHECK 1
53 #ifdef CTST_LEAK_CHECK
54 static void ctst_freeAll(void);
57 static char* _testDataPath
=NULL
;
60 * Forward Declarations
62 void ctest_setICU_DATA(void);
66 #if UCONFIG_NO_LEGACY_CONVERSION
67 # define TRY_CNV_1 "iso-8859-1"
68 # define TRY_CNV_2 "ibm-1208"
70 # define TRY_CNV_1 "iso-8859-7"
71 # define TRY_CNV_2 "sjis"
75 static const char* const * gOrigArgv
;
77 int main(int argc
, const char* const argv
[])
80 UBool defaultDataFound
;
82 const char *warnOrErr
= "Failure";
83 UDate startTime
, endTime
;
86 /* initial check for the default converter */
87 UErrorCode errorCode
= U_ZERO_ERROR
;
91 U_MAIN_INIT_ARGS(argc
, argv
);
93 startTime
= uprv_getRawUTCtime();
97 if (!initArgs(argc
, argv
, NULL
, NULL
)) {
98 /* Error already displayed. */
102 /* Check whether ICU will initialize without forcing the build data directory into
103 * the ICU_DATA path. Success here means either the data dll contains data, or that
104 * this test program was run with ICU_DATA set externally. Failure of this check
105 * is normal when ICU data is not packaged into a shared library.
107 * Whether or not this test succeeds, we want to cleanup and reinitialize
108 * with a data path so that data loading from individual files can be tested.
110 defaultDataFound
= TRUE
;
112 if (U_FAILURE(errorCode
)) {
114 "#### Note: ICU Init without build-specific setDataDirectory() failed. %s\n", u_errorName(errorCode
));
115 defaultDataFound
= FALSE
;
119 fprintf(stderr
, "After initial u_cleanup: RB cache %s empty.\n", ures_dumpCacheContents()?"WAS NOT":"was");
122 while (getTestOption(REPEAT_TESTS_OPTION
) > 0) { /* Loop runs once per complete execution of the tests
123 * used for -r (repeat) test option. */
124 if (!initArgs(argc
, argv
, NULL
, NULL
)) {
125 /* Error already displayed. */
128 errorCode
= U_ZERO_ERROR
;
131 if (!defaultDataFound
) {
132 ctest_setICU_DATA(); /* u_setDataDirectory() must happen Before u_init() */
135 if (U_FAILURE(errorCode
)) {
137 "#### ERROR! %s: u_init() failed with status = \"%s\".\n"
138 "*** Check the ICU_DATA environment variable and \n"
139 "*** check that the data files are present.\n", argv
[0], u_errorName(errorCode
));
140 if(!getTestOption(WARN_ON_MISSING_DATA_OPTION
)) {
141 fprintf(stderr
, "*** Exiting. Use the '-w' option if data files were\n*** purposely removed, to continue test anyway.\n");
150 cnv
= ucnv_open(TRY_CNV_2
, &errorCode
);
156 "*** %s! The converter for " TRY_CNV_2
" cannot be opened.\n"
157 "*** Check the ICU_DATA environment variable and \n"
158 "*** check that the data files are present.\n", warnOrErr
);
159 if(!getTestOption(WARN_ON_MISSING_DATA_OPTION
)) {
160 fprintf(stderr
, "*** Exitting. Use the '-w' option if data files were\n*** purposely removed, to continue test anyway.\n");
166 rb
= ures_open(NULL
, "en", &errorCode
);
167 if(U_SUCCESS(errorCode
)) {
172 "*** %s! The \"en\" locale resource bundle cannot be opened.\n"
173 "*** Check the ICU_DATA environment variable and \n"
174 "*** check that the data files are present.\n", warnOrErr
);
175 if(!getTestOption(WARN_ON_MISSING_DATA_OPTION
)) {
176 fprintf(stderr
, "*** Exitting. Use the '-w' option if data files were\n*** purposely removed, to continue test anyway.\n");
182 errorCode
= U_ZERO_ERROR
;
183 rb
= ures_open(NULL
, NULL
, &errorCode
);
184 if(U_SUCCESS(errorCode
)) {
186 if (errorCode
== U_USING_DEFAULT_WARNING
|| errorCode
== U_USING_FALLBACK_WARNING
) {
188 "#### Note: The default locale %s is not available\n", uloc_getDefault());
193 "*** %s! Can not open a resource bundle for the default locale %s\n", warnOrErr
, uloc_getDefault());
194 if(!getTestOption(WARN_ON_MISSING_DATA_OPTION
)) {
195 fprintf(stderr
, "*** Exitting. Use the '-w' option if data files were\n"
196 "*** purposely removed, to continue test anyway.\n");
201 fprintf(stdout
, "Default locale for this run is %s\n", uloc_getDefault());
203 /* Build a tree of all tests.
204 * Subsequently will be used to find / iterate the tests to run */
208 /* Tests acutally run HERE. TODO: separate command line option parsing & setting from test execution!! */
209 nerrors
= runTestRequest(root
, argc
, argv
);
211 setTestOption(REPEAT_TESTS_OPTION
, DECREMENT_OPTION_VALUE
);
212 if (getTestOption(REPEAT_TESTS_OPTION
) > 0) {
213 printf("Repeating tests %d more time(s)\n", getTestOption(REPEAT_TESTS_OPTION
));
215 cleanUpTestTree(root
);
217 #ifdef CTST_LEAK_CHECK
219 /* To check for leaks */
220 u_cleanup(); /* nuke the hashtable.. so that any still-open cnvs are leaked */
222 if(getTestOption(VERBOSITY_OPTION
) && ctst_allocated_total
>0) {
223 fprintf(stderr
,"ctst_freeAll(): cleaned up after %d allocations (queue of %d)\n", ctst_allocated_total
, CTST_MAX_ALLOC
);
226 if(ures_dumpCacheContents()) {
227 fprintf(stderr
, "Error: After final u_cleanup, RB cache was not empty.\n");
230 fprintf(stderr
,"OK: After final u_cleanup, RB cache was empty.\n");
235 } /* End of loop that repeats the entire test, if requested. (Normally doesn't loop) */
237 if (ALLOCATION_COUNT
> 0) {
238 fprintf(stderr
, "There were %d blocks leaked!\n", ALLOCATION_COUNT
);
241 endTime
= uprv_getRawUTCtime();
242 diffTime
= (int32_t)(endTime
- startTime
);
243 printf("Elapsed Time: %02d:%02d:%02d.%03d\n",
244 (int)((diffTime%U_MILLIS_PER_DAY
)/U_MILLIS_PER_HOUR
),
245 (int)((diffTime%U_MILLIS_PER_HOUR
)/U_MILLIS_PER_MINUTE
),
246 (int)((diffTime%U_MILLIS_PER_MINUTE
)/U_MILLIS_PER_SECOND
),
247 (int)(diffTime%U_MILLIS_PER_SECOND
));
249 return nerrors
? 1 : 0;
253 static void ctest_appendToDataDirectory(const char *toAppend)
255 const char *oldPath ="";
257 char *newPath = newBuf;
261 if((toAppend == NULL) || (*toAppend == 0)) {
265 oldPath = u_getDataDirectory();
266 if( (oldPath==NULL) || (*oldPath == 0)) {
267 u_setDataDirectory(toAppend);
269 oldLen = strlen(oldPath);
270 newLen = strlen(toAppend)+1+oldLen;
274 newPath = (char *)ctst_malloc(newLen);
277 strcpy(newPath, oldPath);
278 strcpy(newPath+oldLen, U_PATH_SEP_STRING);
279 strcpy(newPath+oldLen+1, toAppend);
281 u_setDataDirectory(newPath);
283 if(newPath != newBuf)
291 /* returns the path to icu/source/data */
292 const char * ctest_dataSrcDir()
294 static const char *dataSrcDir
= NULL
;
300 /* U_TOPSRCDIR is set by the makefiles on UNIXes when building cintltst and intltst
301 // to point to the top of the build hierarchy, which may or
302 // may not be the same as the source directory, depending on
303 // the configure options used. At any rate,
304 // set the data path to the built data from this directory.
305 // The value is complete with quotes, so it can be used
306 // as-is as a string constant.
308 #if defined (U_TOPSRCDIR)
310 dataSrcDir
= U_TOPSRCDIR U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
;
314 /* On Windows, the file name obtained from __FILE__ includes a full path.
315 * This file is "wherever\icu\source\test\cintltst\cintltst.c"
316 * Change to "wherever\icu\source\data"
319 static char p
[sizeof(__FILE__
) + 20];
324 /* We want to back over three '\' chars. */
325 /* Only Windows should end up here, so looking for '\' is safe. */
326 for (i
=1; i
<=3; i
++) {
327 pBackSlash
= strrchr(p
, U_FILE_SEP_CHAR
);
328 if (pBackSlash
!= NULL
) {
329 *pBackSlash
= 0; /* Truncate the string at the '\' */
333 if (pBackSlash
!= NULL
) {
334 /* We found and truncated three names from the path.
335 * Now append "source\data" and set the environment
337 strcpy(pBackSlash
, U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
);
341 /* __FILE__ on MSVC7 does not contain the directory */
342 FILE *file
= fopen(".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
"Makefile.in", "r");
345 dataSrcDir
= ".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
;
348 dataSrcDir
= ".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
;
358 /* returns the path to icu/source/data/out */
359 const char *ctest_dataOutDir()
361 static const char *dataOutDir
= NULL
;
367 /* U_TOPBUILDDIR is set by the makefiles on UNIXes when building cintltst and intltst
368 // to point to the top of the build hierarchy, which may or
369 // may not be the same as the source directory, depending on
370 // the configure options used. At any rate,
371 // set the data path to the built data from this directory.
372 // The value is complete with quotes, so it can be used
373 // as-is as a string constant.
375 #if defined (U_TOPBUILDDIR)
377 dataOutDir
= U_TOPBUILDDIR
"data"U_FILE_SEP_STRING
"out"U_FILE_SEP_STRING
;
381 /* On Windows, the file name obtained from __FILE__ includes a full path.
382 * This file is "wherever\icu\source\test\cintltst\cintltst.c"
383 * Change to "wherever\icu\source\data"
386 static char p
[sizeof(__FILE__
) + 20];
391 /* We want to back over three '\' chars. */
392 /* Only Windows should end up here, so looking for '\' is safe. */
393 for (i
=1; i
<=3; i
++) {
394 pBackSlash
= strrchr(p
, U_FILE_SEP_CHAR
);
395 if (pBackSlash
!= NULL
) {
396 *pBackSlash
= 0; /* Truncate the string at the '\' */
400 if (pBackSlash
!= NULL
) {
401 /* We found and truncated three names from the path.
402 * Now append "source\data" and set the environment
404 strcpy(pBackSlash
, U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
"out" U_FILE_SEP_STRING
);
408 /* __FILE__ on MSVC7 does not contain the directory */
409 FILE *file
= fopen(".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
"Makefile.in", "r");
412 dataOutDir
= ".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
"out" U_FILE_SEP_STRING
;
415 dataOutDir
= ".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
"out" U_FILE_SEP_STRING
;
424 /* ctest_setICU_DATA - if the ICU_DATA environment variable is not already
425 * set, try to deduce the directory in which ICU was built,
426 * and set ICU_DATA to "icu/source/data" in that location.
427 * The intent is to allow the tests to have a good chance
428 * of running without requiring that the user manually set
429 * ICU_DATA. Common data isn't a problem, since it is
430 * picked up via a static (build time) reference, but the
431 * tests dynamically load some data.
433 void ctest_setICU_DATA() {
435 /* No location for the data dir was identifiable.
436 * Add other fallbacks for the test data location here if the need arises
438 if (getenv("ICU_DATA") == NULL
) {
439 /* If ICU_DATA isn't set, set it to the usual location */
440 u_setDataDirectory(ctest_dataOutDir());
444 /* These tests do cleanup and reinitialize ICU in the course of their operation.
445 * The ICU data directory must be preserved across these operations.
446 * Here is a helper function to assist with that.
448 static char *safeGetICUDataDirectory() {
449 const char *dataDir
= u_getDataDirectory(); /* Returned string vanashes with u_cleanup */
451 if (dataDir
!= NULL
) {
452 retStr
= (char *)malloc(strlen(dataDir
)+1);
453 strcpy(retStr
, dataDir
);
458 UBool
ctest_resetICU() {
459 UErrorCode status
= U_ZERO_ERROR
;
460 char *dataDir
= safeGetICUDataDirectory();
463 if (!initArgs(gOrigArgc
, gOrigArgv
, NULL
, NULL
)) {
464 /* Error already displayed. */
467 u_setDataDirectory(dataDir
);
470 if (U_FAILURE(status
)) {
471 log_err_status(status
, "u_init failed with %s\n", u_errorName(status
));
477 UChar
* CharsToUChars(const char* str
) {
478 /* Might be faster to just use uprv_strlen() as the preflight len - liu */
479 int32_t len
= u_unescape(str
, 0, 0); /* preflight */
480 /* Do NOT use malloc() - we are supposed to be acting like user code! */
481 UChar
*buf
= (UChar
*) malloc(sizeof(UChar
) * (len
+ 1));
482 u_unescape(str
, buf
, len
+ 1);
486 char *austrdup(const UChar
* unichars
)
491 length
= u_strlen ( unichars
);
492 /*newString = (char*)malloc ( sizeof( char ) * 4 * ( length + 1 ) );*/ /* this leaks for now */
493 newString
= (char*)ctst_malloc ( sizeof( char ) * 4 * ( length
+ 1 ) ); /* this shouldn't */
495 if ( newString
== NULL
)
498 u_austrcpy ( newString
, unichars
);
503 char *aescstrdup(const UChar
* unichars
,int32_t length
){
504 char *newString
,*targetLimit
,*target
;
505 UConverterFromUCallback cb
;
507 UErrorCode errorCode
= U_ZERO_ERROR
;
508 #if U_CHARSET_FAMILY==U_EBCDIC_FAMILY
509 # if U_PLATFORM == U_PF_OS390
510 static const char convName
[] = "ibm-1047";
512 static const char convName
[] = "ibm-37";
515 static const char convName
[] = "US-ASCII";
517 UConverter
* conv
= ucnv_open(convName
, &errorCode
);
519 length
= u_strlen( unichars
);
521 newString
= (char*)ctst_malloc ( sizeof(char) * 8 * (length
+1));
523 targetLimit
= newString
+sizeof(char) * 8 * (length
+1);
524 ucnv_setFromUCallBack(conv
, UCNV_FROM_U_CALLBACK_ESCAPE
, UCNV_ESCAPE_C
, &cb
, &p
, &errorCode
);
525 ucnv_fromUnicode(conv
,&target
,targetLimit
, &unichars
, (UChar
*)(unichars
+length
),NULL
,TRUE
,&errorCode
);
531 const char* loadTestData(UErrorCode
* err
){
532 if( _testDataPath
== NULL
){
533 const char* directory
=NULL
;
534 UResourceBundle
* test
=NULL
;
536 const char* tdrelativepath
;
537 #if defined (U_TOPBUILDDIR)
538 tdrelativepath
= "test"U_FILE_SEP_STRING
"testdata"U_FILE_SEP_STRING
"out"U_FILE_SEP_STRING
;
539 directory
= U_TOPBUILDDIR
;
541 tdrelativepath
= ".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
"test"U_FILE_SEP_STRING
"testdata"U_FILE_SEP_STRING
"out"U_FILE_SEP_STRING
;
542 directory
= ctest_dataOutDir();
545 tdpath
= (char*) ctst_malloc(sizeof(char) *(( strlen(directory
) * strlen(tdrelativepath
)) + 10));
548 /* u_getDataDirectory shoul return \source\data ... set the
549 * directory to ..\source\data\..\test\testdata\out\testdata
551 * Fallback: When Memory mapped file is built
552 * ..\source\data\out\..\..\test\testdata\out\testdata
554 strcpy(tdpath
, directory
);
555 strcat(tdpath
, tdrelativepath
);
556 strcat(tdpath
,"testdata");
559 test
=ures_open(tdpath
, "testtypes", err
);
561 /* Fall back did not succeed either so return */
563 *err
= U_FILE_ACCESS_ERROR
;
564 log_data_err("Could not load testtypes.res in testdata bundle with path %s - %s\n", tdpath
, u_errorName(*err
));
568 _testDataPath
= tdpath
;
569 return _testDataPath
;
571 return _testDataPath
;
574 #define CTEST_MAX_TIMEZONE_SIZE 256
575 static UChar gOriginalTimeZone
[CTEST_MAX_TIMEZONE_SIZE
] = {0};
578 * Call this once to get a consistent timezone. Use ctest_resetTimeZone to set it back to the original value.
579 * @param optionalTimeZone Set this to a requested timezone.
580 * Set to NULL to use the standard test timezone (Pacific Time)
582 U_CFUNC
void ctest_setTimeZone(const char *optionalTimeZone
, UErrorCode
*status
) {
583 #if !UCONFIG_NO_FORMATTING
584 UChar zoneID
[CTEST_MAX_TIMEZONE_SIZE
];
586 if (optionalTimeZone
== NULL
) {
587 optionalTimeZone
= "America/Los_Angeles";
589 if (gOriginalTimeZone
[0]) {
590 log_data_err("*** Error: time zone saved twice. New value will be %s (Are you missing data?)\n",
593 ucal_getDefaultTimeZone(gOriginalTimeZone
, CTEST_MAX_TIMEZONE_SIZE
, status
);
594 if (U_FAILURE(*status
)) {
595 log_err("*** Error: Failed to save default time zone: %s\n",
596 u_errorName(*status
));
597 *status
= U_ZERO_ERROR
;
600 u_uastrncpy(zoneID
, optionalTimeZone
, CTEST_MAX_TIMEZONE_SIZE
-1);
601 zoneID
[CTEST_MAX_TIMEZONE_SIZE
-1] = 0;
602 ucal_setDefaultTimeZone(zoneID
, status
);
603 if (U_FAILURE(*status
)) {
604 log_err("*** Error: Failed to set default time zone to \"%s\": %s\n",
605 optionalTimeZone
, u_errorName(*status
));
611 * Call this once get back the original timezone
613 U_CFUNC
void ctest_resetTimeZone(void) {
614 #if !UCONFIG_NO_FORMATTING
615 UErrorCode status
= U_ZERO_ERROR
;
617 ucal_setDefaultTimeZone(gOriginalTimeZone
, &status
);
618 if (U_FAILURE(status
)) {
619 log_err("*** Error: Failed to reset default time zone: %s\n",
620 u_errorName(status
));
622 /* Set to an empty state */
623 gOriginalTimeZone
[0] = 0;
628 void *ctst_malloc(size_t size
) {
629 ctst_allocated_total
++;
630 if(ctst_allocated
>= CTST_MAX_ALLOC
- 1) {
634 if(ctst_allocated_stuff
[ctst_allocated
]) {
635 free(ctst_allocated_stuff
[ctst_allocated
]);
637 return ctst_allocated_stuff
[ctst_allocated
++] = malloc(size
);
640 #ifdef CTST_LEAK_CHECK
641 static void ctst_freeAll() {
643 if(ctst_free
== FALSE
) { /* only free up to the allocated mark */
644 for(i
=0; i
<ctst_allocated
; i
++) {
645 free(ctst_allocated_stuff
[i
]);
646 ctst_allocated_stuff
[i
] = NULL
;
648 } else { /* free all */
649 for(i
=0; i
<CTST_MAX_ALLOC
; i
++) {
650 free(ctst_allocated_stuff
[i
]);
651 ctst_allocated_stuff
[i
] = NULL
;
658 #define VERBOSE_ASSERTIONS
660 U_CFUNC UBool
assertSuccess(const char* msg
, UErrorCode
* ec
) {
662 if (U_FAILURE(*ec
)) {
663 log_err_status(*ec
, "FAIL: %s (%s)\n", msg
, u_errorName(*ec
));
669 /* if 'condition' is a UBool, the compiler complains bitterly about
670 expressions like 'a > 0' which it evaluates as int */
671 U_CFUNC UBool
assertTrue(const char* msg
, int /*not UBool*/ condition
) {
673 log_err("FAIL: assertTrue() failed: %s\n", msg
);
675 #ifdef VERBOSE_ASSERTIONS
677 log_verbose("Ok: %s\n", msg
);
680 return (UBool
)condition
;
683 U_CFUNC UBool
assertEquals(const char* message
, const char* expected
,
684 const char* actual
) {
685 if (uprv_strcmp(expected
, actual
) != 0) {
686 log_err("FAIL: %s; got \"%s\"; expected \"%s\"\n",
687 message
, actual
, expected
);
690 #ifdef VERBOSE_ASSERTIONS
692 log_verbose("Ok: %s; got \"%s\"\n", message
, actual
);
697 /*--------------------------------------------------------------------
698 * Time bomb - allows temporary behavior that expires at a given
700 *--------------------------------------------------------------------
703 U_CFUNC UBool
isICUVersionBefore(int major
, int minor
, int milli
) {
706 ov
[0] = (uint8_t)major
;
707 ov
[1] = (uint8_t)minor
;
708 ov
[2] = (uint8_t)milli
;
711 return uprv_memcmp(iv
, ov
, U_MAX_VERSION_LENGTH
) < 0;
714 U_CFUNC UBool
isICUVersionAtLeast(int major
, int minor
, int milli
) {
715 return !isICUVersionBefore(major
, minor
, milli
);