1 /********************************************************************
3 * Copyright (c) 1997-2011, 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_LEAK_CHECK 1
45 #ifdef CTST_LEAK_CHECK
46 U_CFUNC
void ctst_freeAll(void);
49 static char* _testDataPath
=NULL
;
52 * Forward Declarations
54 void ctest_setICU_DATA(void);
58 #if UCONFIG_NO_LEGACY_CONVERSION
59 # define TRY_CNV_1 "iso-8859-1"
60 # define TRY_CNV_2 "ibm-1208"
62 # define TRY_CNV_1 "iso-8859-7"
63 # define TRY_CNV_2 "sjis"
67 static const char* const * gOrigArgv
;
69 int main(int argc
, const char* const argv
[])
72 UBool defaultDataFound
;
74 const char *warnOrErr
= "Failure";
75 UDate startTime
, endTime
;
78 /* initial check for the default converter */
79 UErrorCode errorCode
= U_ZERO_ERROR
;
83 U_MAIN_INIT_ARGS(argc
, argv
);
85 startTime
= uprv_getRawUTCtime();
89 if (!initArgs(argc
, argv
, NULL
, NULL
)) {
90 /* Error already displayed. */
94 /* Check whether ICU will initialize without forcing the build data directory into
95 * the ICU_DATA path. Success here means either the data dll contains data, or that
96 * this test program was run with ICU_DATA set externally. Failure of this check
97 * is normal when ICU data is not packaged into a shared library.
99 * Whether or not this test succeeds, we want to cleanup and reinitialize
100 * with a data path so that data loading from individual files can be tested.
102 defaultDataFound
= TRUE
;
104 if (U_FAILURE(errorCode
)) {
106 "#### Note: ICU Init without build-specific setDataDirectory() failed. %s\n", u_errorName(errorCode
));
107 defaultDataFound
= FALSE
;
111 fprintf(stderr
, "After initial u_cleanup: RB cache %s empty.\n", ures_dumpCacheContents()?"WAS NOT":"was");
114 while (getTestOption(REPEAT_TESTS_OPTION
) > 0) { /* Loop runs once per complete execution of the tests
115 * used for -r (repeat) test option. */
116 if (!initArgs(argc
, argv
, NULL
, NULL
)) {
117 /* Error already displayed. */
120 errorCode
= U_ZERO_ERROR
;
123 if (!defaultDataFound
) {
124 ctest_setICU_DATA(); /* u_setDataDirectory() must happen Before u_init() */
127 if (U_FAILURE(errorCode
)) {
129 "#### ERROR! %s: u_init() failed with status = \"%s\".\n"
130 "*** Check the ICU_DATA environment variable and \n"
131 "*** check that the data files are present.\n", argv
[0], u_errorName(errorCode
));
132 if(!getTestOption(WARN_ON_MISSING_DATA_OPTION
)) {
133 fprintf(stderr
, "*** Exiting. Use the '-w' option if data files were\n*** purposely removed, to continue test anyway.\n");
142 cnv
= ucnv_open(TRY_CNV_2
, &errorCode
);
148 "*** %s! The converter for " TRY_CNV_2
" cannot be opened.\n"
149 "*** Check the ICU_DATA environment variable and \n"
150 "*** check that the data files are present.\n", warnOrErr
);
151 if(!getTestOption(WARN_ON_MISSING_DATA_OPTION
)) {
152 fprintf(stderr
, "*** Exitting. Use the '-w' option if data files were\n*** purposely removed, to continue test anyway.\n");
158 rb
= ures_open(NULL
, "en", &errorCode
);
159 if(U_SUCCESS(errorCode
)) {
164 "*** %s! The \"en\" locale resource bundle cannot be opened.\n"
165 "*** Check the ICU_DATA environment variable and \n"
166 "*** check that the data files are present.\n", warnOrErr
);
167 if(!getTestOption(WARN_ON_MISSING_DATA_OPTION
)) {
168 fprintf(stderr
, "*** Exitting. Use the '-w' option if data files were\n*** purposely removed, to continue test anyway.\n");
174 errorCode
= U_ZERO_ERROR
;
175 rb
= ures_open(NULL
, NULL
, &errorCode
);
176 if(U_SUCCESS(errorCode
)) {
178 if (errorCode
== U_USING_DEFAULT_WARNING
|| errorCode
== U_USING_FALLBACK_WARNING
) {
180 "#### Note: The default locale %s is not available\n", uloc_getDefault());
185 "*** %s! Can not open a resource bundle for the default locale %s\n", warnOrErr
, uloc_getDefault());
186 if(!getTestOption(WARN_ON_MISSING_DATA_OPTION
)) {
187 fprintf(stderr
, "*** Exitting. Use the '-w' option if data files were\n"
188 "*** purposely removed, to continue test anyway.\n");
193 fprintf(stdout
, "Default locale for this run is %s\n", uloc_getDefault());
195 /* Build a tree of all tests.
196 * Subsequently will be used to find / iterate the tests to run */
200 /* Tests acutally run HERE. TODO: separate command line option parsing & setting from test execution!! */
201 nerrors
= runTestRequest(root
, argc
, argv
);
203 setTestOption(REPEAT_TESTS_OPTION
, DECREMENT_OPTION_VALUE
);
204 if (getTestOption(REPEAT_TESTS_OPTION
) > 0) {
205 printf("Repeating tests %d more time(s)\n", getTestOption(REPEAT_TESTS_OPTION
));
207 cleanUpTestTree(root
);
209 #ifdef CTST_LEAK_CHECK
211 /* To check for leaks */
212 u_cleanup(); /* nuke the hashtable.. so that any still-open cnvs are leaked */
214 if(ures_dumpCacheContents()) {
215 fprintf(stderr
, "Error: After final u_cleanup, RB cache was not empty.\n");
218 fprintf(stderr
,"OK: After final u_cleanup, RB cache was empty.\n");
223 } /* End of loop that repeats the entire test, if requested. (Normally doesn't loop) */
225 if (ALLOCATION_COUNT
> 0) {
226 fprintf(stderr
, "There were %d blocks leaked!\n", ALLOCATION_COUNT
);
229 endTime
= uprv_getRawUTCtime();
230 diffTime
= (int32_t)(endTime
- startTime
);
231 printf("Elapsed Time: %02d:%02d:%02d.%03d\n",
232 (int)((diffTime%U_MILLIS_PER_DAY
)/U_MILLIS_PER_HOUR
),
233 (int)((diffTime%U_MILLIS_PER_HOUR
)/U_MILLIS_PER_MINUTE
),
234 (int)((diffTime%U_MILLIS_PER_MINUTE
)/U_MILLIS_PER_SECOND
),
235 (int)(diffTime%U_MILLIS_PER_SECOND
));
237 return nerrors
? 1 : 0;
241 static void ctest_appendToDataDirectory(const char *toAppend)
243 const char *oldPath ="";
245 char *newPath = newBuf;
249 if((toAppend == NULL) || (*toAppend == 0)) {
253 oldPath = u_getDataDirectory();
254 if( (oldPath==NULL) || (*oldPath == 0)) {
255 u_setDataDirectory(toAppend);
257 oldLen = strlen(oldPath);
258 newLen = strlen(toAppend)+1+oldLen;
262 newPath = (char *)ctst_malloc(newLen);
265 strcpy(newPath, oldPath);
266 strcpy(newPath+oldLen, U_PATH_SEP_STRING);
267 strcpy(newPath+oldLen+1, toAppend);
269 u_setDataDirectory(newPath);
271 if(newPath != newBuf)
279 /* returns the path to icu/source/data */
280 const char * ctest_dataSrcDir()
282 static const char *dataSrcDir
= NULL
;
288 /* U_TOPSRCDIR is set by the makefiles on UNIXes when building cintltst and intltst
289 // to point to the top of the build hierarchy, which may or
290 // may not be the same as the source directory, depending on
291 // the configure options used. At any rate,
292 // set the data path to the built data from this directory.
293 // The value is complete with quotes, so it can be used
294 // as-is as a string constant.
296 #if defined (U_TOPSRCDIR)
298 dataSrcDir
= U_TOPSRCDIR U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
;
302 /* On Windows, the file name obtained from __FILE__ includes a full path.
303 * This file is "wherever\icu\source\test\cintltst\cintltst.c"
304 * Change to "wherever\icu\source\data"
307 static char p
[sizeof(__FILE__
) + 20];
312 /* We want to back over three '\' chars. */
313 /* Only Windows should end up here, so looking for '\' is safe. */
314 for (i
=1; i
<=3; i
++) {
315 pBackSlash
= strrchr(p
, U_FILE_SEP_CHAR
);
316 if (pBackSlash
!= NULL
) {
317 *pBackSlash
= 0; /* Truncate the string at the '\' */
321 if (pBackSlash
!= NULL
) {
322 /* We found and truncated three names from the path.
323 * Now append "source\data" and set the environment
325 strcpy(pBackSlash
, U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
);
329 /* __FILE__ on MSVC7 does not contain the directory */
330 FILE *file
= fopen(".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
"Makefile.in", "r");
333 dataSrcDir
= ".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
;
336 dataSrcDir
= ".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
;
346 /* returns the path to icu/source/data/out */
347 const char *ctest_dataOutDir()
349 static const char *dataOutDir
= NULL
;
355 /* U_TOPBUILDDIR is set by the makefiles on UNIXes when building cintltst and intltst
356 // to point to the top of the build hierarchy, which may or
357 // may not be the same as the source directory, depending on
358 // the configure options used. At any rate,
359 // set the data path to the built data from this directory.
360 // The value is complete with quotes, so it can be used
361 // as-is as a string constant.
363 #if defined (U_TOPBUILDDIR)
365 dataOutDir
= U_TOPBUILDDIR
"data"U_FILE_SEP_STRING
"out"U_FILE_SEP_STRING
;
369 /* On Windows, the file name obtained from __FILE__ includes a full path.
370 * This file is "wherever\icu\source\test\cintltst\cintltst.c"
371 * Change to "wherever\icu\source\data"
374 static char p
[sizeof(__FILE__
) + 20];
379 /* We want to back over three '\' chars. */
380 /* Only Windows should end up here, so looking for '\' is safe. */
381 for (i
=1; i
<=3; i
++) {
382 pBackSlash
= strrchr(p
, U_FILE_SEP_CHAR
);
383 if (pBackSlash
!= NULL
) {
384 *pBackSlash
= 0; /* Truncate the string at the '\' */
388 if (pBackSlash
!= NULL
) {
389 /* We found and truncated three names from the path.
390 * Now append "source\data" and set the environment
392 strcpy(pBackSlash
, U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
"out" U_FILE_SEP_STRING
);
396 /* __FILE__ on MSVC7 does not contain the directory */
397 FILE *file
= fopen(".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
"Makefile.in", "r");
400 dataOutDir
= ".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
"data" U_FILE_SEP_STRING
"out" U_FILE_SEP_STRING
;
403 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
;
412 /* ctest_setICU_DATA - if the ICU_DATA environment variable is not already
413 * set, try to deduce the directory in which ICU was built,
414 * and set ICU_DATA to "icu/source/data" in that location.
415 * The intent is to allow the tests to have a good chance
416 * of running without requiring that the user manually set
417 * ICU_DATA. Common data isn't a problem, since it is
418 * picked up via a static (build time) reference, but the
419 * tests dynamically load some data.
421 void ctest_setICU_DATA() {
423 /* No location for the data dir was identifiable.
424 * Add other fallbacks for the test data location here if the need arises
426 if (getenv("ICU_DATA") == NULL
) {
427 /* If ICU_DATA isn't set, set it to the usual location */
428 u_setDataDirectory(ctest_dataOutDir());
432 /* These tests do cleanup and reinitialize ICU in the course of their operation.
433 * The ICU data directory must be preserved across these operations.
434 * Here is a helper function to assist with that.
436 static char *safeGetICUDataDirectory() {
437 const char *dataDir
= u_getDataDirectory(); /* Returned string vanashes with u_cleanup */
439 if (dataDir
!= NULL
) {
440 retStr
= (char *)malloc(strlen(dataDir
)+1);
441 strcpy(retStr
, dataDir
);
446 UBool
ctest_resetICU() {
447 UErrorCode status
= U_ZERO_ERROR
;
448 char *dataDir
= safeGetICUDataDirectory();
451 if (!initArgs(gOrigArgc
, gOrigArgv
, NULL
, NULL
)) {
452 /* Error already displayed. */
455 u_setDataDirectory(dataDir
);
458 if (U_FAILURE(status
)) {
459 log_err_status(status
, "u_init failed with %s\n", u_errorName(status
));
465 UChar
* CharsToUChars(const char* str
) {
466 /* Might be faster to just use uprv_strlen() as the preflight len - liu */
467 int32_t len
= u_unescape(str
, 0, 0); /* preflight */
468 /* Do NOT use malloc() - we are supposed to be acting like user code! */
469 UChar
*buf
= (UChar
*) malloc(sizeof(UChar
) * (len
+ 1));
470 u_unescape(str
, buf
, len
+ 1);
474 char *austrdup(const UChar
* unichars
)
479 length
= u_strlen ( unichars
);
480 /*newString = (char*)malloc ( sizeof( char ) * 4 * ( length + 1 ) );*/ /* this leaks for now */
481 newString
= (char*)ctst_malloc ( sizeof( char ) * 4 * ( length
+ 1 ) ); /* this shouldn't */
483 if ( newString
== NULL
)
486 u_austrcpy ( newString
, unichars
);
491 char *aescstrdup(const UChar
* unichars
,int32_t length
){
492 char *newString
,*targetLimit
,*target
;
493 UConverterFromUCallback cb
;
495 UErrorCode errorCode
= U_ZERO_ERROR
;
496 #if U_CHARSET_FAMILY==U_EBCDIC_FAMILY
497 # if U_PLATFORM == U_PF_OS390
498 static const char convName
[] = "ibm-1047";
500 static const char convName
[] = "ibm-37";
503 static const char convName
[] = "US-ASCII";
505 UConverter
* conv
= ucnv_open(convName
, &errorCode
);
507 length
= u_strlen( unichars
);
509 newString
= (char*)ctst_malloc ( sizeof(char) * 8 * (length
+1));
511 targetLimit
= newString
+sizeof(char) * 8 * (length
+1);
512 ucnv_setFromUCallBack(conv
, UCNV_FROM_U_CALLBACK_ESCAPE
, UCNV_ESCAPE_C
, &cb
, &p
, &errorCode
);
513 ucnv_fromUnicode(conv
,&target
,targetLimit
, &unichars
, (UChar
*)(unichars
+length
),NULL
,TRUE
,&errorCode
);
519 const char* loadTestData(UErrorCode
* err
){
520 if( _testDataPath
== NULL
){
521 const char* directory
=NULL
;
522 UResourceBundle
* test
=NULL
;
524 const char* tdrelativepath
;
525 #if defined (U_TOPBUILDDIR)
526 tdrelativepath
= "test"U_FILE_SEP_STRING
"testdata"U_FILE_SEP_STRING
"out"U_FILE_SEP_STRING
;
527 directory
= U_TOPBUILDDIR
;
529 tdrelativepath
= ".."U_FILE_SEP_STRING
".."U_FILE_SEP_STRING
"test"U_FILE_SEP_STRING
"testdata"U_FILE_SEP_STRING
"out"U_FILE_SEP_STRING
;
530 directory
= ctest_dataOutDir();
533 tdpath
= (char*) ctst_malloc(sizeof(char) *(( strlen(directory
) * strlen(tdrelativepath
)) + 10));
536 /* u_getDataDirectory shoul return \source\data ... set the
537 * directory to ..\source\data\..\test\testdata\out\testdata
539 * Fallback: When Memory mapped file is built
540 * ..\source\data\out\..\..\test\testdata\out\testdata
542 strcpy(tdpath
, directory
);
543 strcat(tdpath
, tdrelativepath
);
544 strcat(tdpath
,"testdata");
547 test
=ures_open(tdpath
, "testtypes", err
);
549 /* Fall back did not succeed either so return */
551 *err
= U_FILE_ACCESS_ERROR
;
552 log_data_err("Could not load testtypes.res in testdata bundle with path %s - %s\n", tdpath
, u_errorName(*err
));
556 _testDataPath
= tdpath
;
557 return _testDataPath
;
559 return _testDataPath
;
562 #define CTEST_MAX_TIMEZONE_SIZE 256
563 static UChar gOriginalTimeZone
[CTEST_MAX_TIMEZONE_SIZE
] = {0};
566 * Call this once to get a consistent timezone. Use ctest_resetTimeZone to set it back to the original value.
567 * @param optionalTimeZone Set this to a requested timezone.
568 * Set to NULL to use the standard test timezone (Pacific Time)
570 U_CFUNC
void ctest_setTimeZone(const char *optionalTimeZone
, UErrorCode
*status
) {
571 #if !UCONFIG_NO_FORMATTING
572 UChar zoneID
[CTEST_MAX_TIMEZONE_SIZE
];
574 if (optionalTimeZone
== NULL
) {
575 optionalTimeZone
= "America/Los_Angeles";
577 if (gOriginalTimeZone
[0]) {
578 log_data_err("*** Error: time zone saved twice. New value will be %s (Are you missing data?)\n",
581 ucal_getDefaultTimeZone(gOriginalTimeZone
, CTEST_MAX_TIMEZONE_SIZE
, status
);
582 if (U_FAILURE(*status
)) {
583 log_err("*** Error: Failed to save default time zone: %s\n",
584 u_errorName(*status
));
585 *status
= U_ZERO_ERROR
;
588 u_uastrncpy(zoneID
, optionalTimeZone
, CTEST_MAX_TIMEZONE_SIZE
-1);
589 zoneID
[CTEST_MAX_TIMEZONE_SIZE
-1] = 0;
590 ucal_setDefaultTimeZone(zoneID
, status
);
591 if (U_FAILURE(*status
)) {
592 log_err("*** Error: Failed to set default time zone to \"%s\": %s\n",
593 optionalTimeZone
, u_errorName(*status
));
599 * Call this once get back the original timezone
601 U_CFUNC
void ctest_resetTimeZone(void) {
602 #if !UCONFIG_NO_FORMATTING
603 UErrorCode status
= U_ZERO_ERROR
;
605 ucal_setDefaultTimeZone(gOriginalTimeZone
, &status
);
606 if (U_FAILURE(status
)) {
607 log_err("*** Error: Failed to reset default time zone: %s\n",
608 u_errorName(status
));
610 /* Set to an empty state */
611 gOriginalTimeZone
[0] = 0;
615 #define CTST_MAX_ALLOC 8192
616 /* Array used as a queue */
617 static void * ctst_allocated_stuff
[CTST_MAX_ALLOC
] = {0};
618 static int ctst_allocated
= 0;
619 static UBool ctst_free
= FALSE
;
621 void *ctst_malloc(size_t size
) {
622 if(ctst_allocated
>= CTST_MAX_ALLOC
- 1) {
626 if(ctst_allocated_stuff
[ctst_allocated
]) {
627 free(ctst_allocated_stuff
[ctst_allocated
]);
629 return ctst_allocated_stuff
[ctst_allocated
++] = malloc(size
);
632 #ifdef CTST_LEAK_CHECK
633 void ctst_freeAll() {
636 for(i
=0; i
<ctst_allocated
; i
++) {
637 free(ctst_allocated_stuff
[i
]);
638 ctst_allocated_stuff
[i
] = NULL
;
641 for(i
=0; i
<CTST_MAX_ALLOC
; i
++) {
642 free(ctst_allocated_stuff
[i
]);
643 ctst_allocated_stuff
[i
] = NULL
;
650 #define VERBOSE_ASSERTIONS
652 U_CFUNC UBool
assertSuccess(const char* msg
, UErrorCode
* ec
) {
654 if (U_FAILURE(*ec
)) {
655 log_err_status(*ec
, "FAIL: %s (%s)\n", msg
, u_errorName(*ec
));
661 /* if 'condition' is a UBool, the compiler complains bitterly about
662 expressions like 'a > 0' which it evaluates as int */
663 U_CFUNC UBool
assertTrue(const char* msg
, int /*not UBool*/ condition
) {
665 log_err("FAIL: assertTrue() failed: %s\n", msg
);
667 #ifdef VERBOSE_ASSERTIONS
669 log_verbose("Ok: %s\n", msg
);
672 return (UBool
)condition
;
675 U_CFUNC UBool
assertEquals(const char* message
, const char* expected
,
676 const char* actual
) {
677 if (uprv_strcmp(expected
, actual
) != 0) {
678 log_err("FAIL: %s; got \"%s\"; expected \"%s\"\n",
679 message
, actual
, expected
);
682 #ifdef VERBOSE_ASSERTIONS
684 log_verbose("Ok: %s; got \"%s\"\n", message
, actual
);
689 /*--------------------------------------------------------------------
690 * Time bomb - allows temporary behavior that expires at a given
692 *--------------------------------------------------------------------
695 U_CFUNC UBool
isICUVersionBefore(int major
, int minor
, int milli
) {
698 ov
[0] = (uint8_t)major
;
699 ov
[1] = (uint8_t)minor
;
700 ov
[2] = (uint8_t)milli
;
703 return uprv_memcmp(iv
, ov
, U_MAX_VERSION_LENGTH
) < 0;
706 U_CFUNC UBool
isICUVersionAtLeast(int major
, int minor
, int milli
) {
707 return !isICUVersionBefore(major
, minor
, milli
);