OLD | NEW |
(Empty) | |
| 1 /******************************************************************** |
| 2 * COPYRIGHT: |
| 3 * Copyright (c) 2003-2009, International Business Machines Corporation and |
| 4 * others. All Rights Reserved. |
| 5 ********************************************************************/ |
| 6 /* |
| 7 * File hpmufn.c |
| 8 * |
| 9 */ |
| 10 |
| 11 #include "unicode/utypes.h" |
| 12 #include "unicode/putil.h" |
| 13 #include "unicode/uclean.h" |
| 14 #include "unicode/uchar.h" |
| 15 #include "unicode/ures.h" |
| 16 #include "cintltst.h" |
| 17 #include "umutex.h" |
| 18 #include "unicode/utrace.h" |
| 19 #include <stdlib.h> |
| 20 #include <string.h> |
| 21 |
| 22 /** |
| 23 * This should align the memory properly on any machine. |
| 24 */ |
| 25 typedef union { |
| 26 long t1; |
| 27 double t2; |
| 28 void *t3; |
| 29 } ctest_AlignedMemory; |
| 30 |
| 31 static void TestHeapFunctions(void); |
| 32 static void TestMutexFunctions(void); |
| 33 static void TestIncDecFunctions(void); |
| 34 |
| 35 void addHeapMutexTest(TestNode **root); |
| 36 |
| 37 |
| 38 void |
| 39 addHeapMutexTest(TestNode** root) |
| 40 { |
| 41 addTest(root, &TestHeapFunctions, "hpmufn/TestHeapFunctions" ); |
| 42 addTest(root, &TestMutexFunctions, "hpmufn/TestMutexFunctions" ); |
| 43 addTest(root, &TestIncDecFunctions, "hpmufn/TestIncDecFunctions"); |
| 44 } |
| 45 |
| 46 static int32_t gMutexFailures = 0; |
| 47 |
| 48 #define TEST_STATUS(status, expected) \ |
| 49 if (status != expected) { \ |
| 50 log_err_status(status, "FAIL at %s:%d. Actual status = \"%s\"; Expected status
= \"%s\"\n", \ |
| 51 __FILE__, __LINE__, u_errorName(status), u_errorName(expected)); gMutexFailure
s++; } |
| 52 |
| 53 |
| 54 #define TEST_ASSERT(expr) \ |
| 55 if (!(expr)) { \ |
| 56 log_err("FAILED Assertion \"" #expr "\" at %s:%d.\n", __FILE__, __LINE__);
\ |
| 57 gMutexFailures++; \ |
| 58 } |
| 59 |
| 60 |
| 61 /* These tests do cleanup and reinitialize ICU in the course of their operation
. |
| 62 * The ICU data directory must be preserved across these operations. |
| 63 * Here is a helper function to assist with that. |
| 64 */ |
| 65 static char *safeGetICUDataDirectory() { |
| 66 const char *dataDir = u_getDataDirectory(); /* Returned string vanashes wit
h u_cleanup */ |
| 67 char *retStr = NULL; |
| 68 if (dataDir != NULL) { |
| 69 retStr = (char *)malloc(strlen(dataDir)+1); |
| 70 strcpy(retStr, dataDir); |
| 71 } |
| 72 return retStr; |
| 73 } |
| 74 |
| 75 |
| 76 |
| 77 /* |
| 78 * Test Heap Functions. |
| 79 * Implemented on top of the standard malloc heap. |
| 80 * All blocks increased in size by 8 to 16 bytes, and the poiner returned to
ICU is |
| 81 * offset up by 8 to 16, which should cause a good heap corruption if one
of our "blocks" |
| 82 * ends up being freed directly, without coming through us. |
| 83 * Allocations are counted, to check that ICU actually does call back to us. |
| 84 */ |
| 85 int gBlockCount = 0; |
| 86 const void *gContext; |
| 87 |
| 88 static void * U_CALLCONV myMemAlloc(const void *context, size_t size) { |
| 89 char *retPtr = (char *)malloc(size+sizeof(ctest_AlignedMemory)); |
| 90 if (retPtr != NULL) { |
| 91 retPtr += sizeof(ctest_AlignedMemory); |
| 92 } |
| 93 gBlockCount ++; |
| 94 return retPtr; |
| 95 } |
| 96 |
| 97 static void U_CALLCONV myMemFree(const void *context, void *mem) { |
| 98 char *freePtr = (char *)mem; |
| 99 if (freePtr != NULL) { |
| 100 freePtr -= sizeof(ctest_AlignedMemory); |
| 101 } |
| 102 free(freePtr); |
| 103 } |
| 104 |
| 105 |
| 106 |
| 107 static void * U_CALLCONV myMemRealloc(const void *context, void *mem, size_t siz
e) { |
| 108 char *p = (char *)mem; |
| 109 char *retPtr; |
| 110 |
| 111 if (p!=NULL) { |
| 112 p -= sizeof(ctest_AlignedMemory); |
| 113 } |
| 114 retPtr = realloc(p, size+sizeof(ctest_AlignedMemory)); |
| 115 if (retPtr != NULL) { |
| 116 p += sizeof(ctest_AlignedMemory); |
| 117 } |
| 118 return retPtr; |
| 119 } |
| 120 |
| 121 |
| 122 static void TestHeapFunctions() { |
| 123 UErrorCode status = U_ZERO_ERROR; |
| 124 UResourceBundle *rb = NULL; |
| 125 char *icuDataDir; |
| 126 UVersionInfo unicodeVersion = {0,0,0,0}; |
| 127 |
| 128 icuDataDir = safeGetICUDataDirectory(); /* save icu data dir, so we can pu
t it back |
| 129 * after doing u_cleanup().
*/ |
| 130 |
| 131 |
| 132 /* Verify that ICU can be cleaned up and reinitialized successfully. |
| 133 * Failure here usually means that some ICU service didn't clean up success
fully, |
| 134 * probably because some earlier test accidently left something open. */ |
| 135 ctest_resetICU(); |
| 136 |
| 137 /* Can not set memory functions if ICU is already initialized */ |
| 138 u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, myMemFree, &status
); |
| 139 TEST_STATUS(status, U_INVALID_STATE_ERROR); |
| 140 |
| 141 /* Un-initialize ICU */ |
| 142 u_cleanup(); |
| 143 |
| 144 /* Can not set memory functions with NULL values */ |
| 145 status = U_ZERO_ERROR; |
| 146 u_setMemoryFunctions(&gContext, NULL, myMemRealloc, myMemFree, &status); |
| 147 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
| 148 status = U_ZERO_ERROR; |
| 149 u_setMemoryFunctions(&gContext, myMemAlloc, NULL, myMemFree, &status); |
| 150 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
| 151 status = U_ZERO_ERROR; |
| 152 u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, NULL, &status); |
| 153 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
| 154 |
| 155 /* u_setMemoryFunctions() should work with null or non-null context pointer
*/ |
| 156 status = U_ZERO_ERROR; |
| 157 u_setMemoryFunctions(NULL, myMemAlloc, myMemRealloc, myMemFree, &status); |
| 158 TEST_STATUS(status, U_ZERO_ERROR); |
| 159 u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, myMemFree, &status
); |
| 160 TEST_STATUS(status, U_ZERO_ERROR); |
| 161 |
| 162 |
| 163 /* After reinitializing ICU, we should not be able to set the memory funcs a
gain. */ |
| 164 status = U_ZERO_ERROR; |
| 165 u_setDataDirectory(icuDataDir); |
| 166 u_init(&status); |
| 167 TEST_STATUS(status, U_ZERO_ERROR); |
| 168 u_setMemoryFunctions(NULL, myMemAlloc, myMemRealloc, myMemFree, &status); |
| 169 TEST_STATUS(status, U_INVALID_STATE_ERROR); |
| 170 |
| 171 /* Doing ICU operations should cause allocations to come through our test he
ap */ |
| 172 gBlockCount = 0; |
| 173 status = U_ZERO_ERROR; |
| 174 rb = ures_open(NULL, "es", &status); |
| 175 TEST_STATUS(status, U_ZERO_ERROR); |
| 176 if (gBlockCount == 0) { |
| 177 log_err("Heap functions are not being called from ICU.\n"); |
| 178 } |
| 179 ures_close(rb); |
| 180 |
| 181 /* Cleanup should put the heap back to its default implementation. */ |
| 182 ctest_resetICU(); |
| 183 u_getUnicodeVersion(unicodeVersion); |
| 184 if (unicodeVersion[0] <= 0) { |
| 185 log_err("Properties doesn't reinitialize without u_init.\n"); |
| 186 } |
| 187 status = U_ZERO_ERROR; |
| 188 u_init(&status); |
| 189 TEST_STATUS(status, U_ZERO_ERROR); |
| 190 |
| 191 /* ICU operations should no longer cause allocations to come through our tes
t heap */ |
| 192 gBlockCount = 0; |
| 193 status = U_ZERO_ERROR; |
| 194 rb = ures_open(NULL, "fr", &status); |
| 195 TEST_STATUS(status, U_ZERO_ERROR); |
| 196 if (gBlockCount != 0) { |
| 197 log_err("Heap functions did not reset after u_cleanup.\n"); |
| 198 } |
| 199 ures_close(rb); |
| 200 free(icuDataDir); |
| 201 |
| 202 ctest_resetICU(); |
| 203 } |
| 204 |
| 205 |
| 206 /* |
| 207 * Test u_setMutexFunctions() |
| 208 */ |
| 209 |
| 210 int gTotalMutexesInitialized = 0; /* Total number of mutexes cre
ated */ |
| 211 int gTotalMutexesActive = 0; /* Total mutexes created, but
not destroyed */ |
| 212 int gAccumulatedLocks = 0; |
| 213 const void *gMutexContext; |
| 214 |
| 215 typedef struct DummyMutex { |
| 216 int fLockCount; |
| 217 int fMagic; |
| 218 } DummyMutex; |
| 219 |
| 220 |
| 221 static void U_CALLCONV myMutexInit(const void *context, UMTX *mutex, UErrorCode
*status) { |
| 222 DummyMutex *theMutex; |
| 223 |
| 224 TEST_STATUS(*status, U_ZERO_ERROR); |
| 225 theMutex = (DummyMutex *)malloc(sizeof(DummyMutex)); |
| 226 theMutex->fLockCount = 0; |
| 227 theMutex->fMagic = 123456; |
| 228 gTotalMutexesInitialized++; |
| 229 gTotalMutexesActive++; |
| 230 gMutexContext = context; |
| 231 *mutex = theMutex; |
| 232 } |
| 233 |
| 234 |
| 235 static void U_CALLCONV myMutexDestroy(const void *context, UMTX *mutex) { |
| 236 DummyMutex *This = *(DummyMutex **)mutex; |
| 237 |
| 238 gTotalMutexesActive--; |
| 239 TEST_ASSERT(This->fLockCount == 0); |
| 240 TEST_ASSERT(This->fMagic == 123456); |
| 241 This->fMagic = 0; |
| 242 This->fLockCount = 0; |
| 243 free(This); |
| 244 } |
| 245 |
| 246 static void U_CALLCONV myMutexLock(const void *context, UMTX *mutex) { |
| 247 DummyMutex *This = *(DummyMutex **)mutex; |
| 248 |
| 249 TEST_ASSERT(This->fMagic == 123456); |
| 250 This->fLockCount++; |
| 251 gAccumulatedLocks++; |
| 252 } |
| 253 |
| 254 static void U_CALLCONV myMutexUnlock(const void *context, UMTX *mutex) { |
| 255 DummyMutex *This = *(DummyMutex **)mutex; |
| 256 |
| 257 TEST_ASSERT(This->fMagic == 123456); |
| 258 This->fLockCount--; |
| 259 TEST_ASSERT(This->fLockCount >= 0); |
| 260 } |
| 261 |
| 262 |
| 263 |
| 264 static void TestMutexFunctions() { |
| 265 UErrorCode status = U_ZERO_ERROR; |
| 266 UResourceBundle *rb = NULL; |
| 267 char *icuDataDir; |
| 268 |
| 269 gMutexFailures = 0; |
| 270 |
| 271 /* Save initial ICU state so that it can be restored later. |
| 272 * u_cleanup(), which is called in this test, resets ICU's state. |
| 273 */ |
| 274 icuDataDir = safeGetICUDataDirectory(); |
| 275 |
| 276 /* Verify that ICU can be cleaned up and reinitialized successfully. |
| 277 * Failure here usually means that some ICU service didn't clean up success
fully, |
| 278 * probably because some earlier test accidently left something open. */ |
| 279 ctest_resetICU(); |
| 280 |
| 281 /* Can not set mutex functions if ICU is already initialized */ |
| 282 u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myM
utexUnlock, &status); |
| 283 TEST_STATUS(status, U_INVALID_STATE_ERROR); |
| 284 |
| 285 /* Un-initialize ICU */ |
| 286 u_cleanup(); |
| 287 |
| 288 /* Can not set Mutex functions with NULL values */ |
| 289 status = U_ZERO_ERROR; |
| 290 u_setMutexFunctions(&gContext, NULL, myMutexDestroy, myMutexLock, myMutexUnl
ock, &status); |
| 291 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
| 292 status = U_ZERO_ERROR; |
| 293 u_setMutexFunctions(&gContext, myMutexInit, NULL, myMutexLock, myMutexUnlock
, &status); |
| 294 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
| 295 status = U_ZERO_ERROR; |
| 296 u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, NULL, myMutexUnl
ock, &status); |
| 297 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
| 298 status = U_ZERO_ERROR; |
| 299 u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, NUL
L, &status); |
| 300 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
| 301 |
| 302 /* u_setMutexFunctions() should work with null or non-null context pointer *
/ |
| 303 status = U_ZERO_ERROR; |
| 304 u_setMutexFunctions(NULL, myMutexInit, myMutexDestroy, myMutexLock, myMutexU
nlock, &status); |
| 305 TEST_STATUS(status, U_ZERO_ERROR); |
| 306 u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myM
utexUnlock, &status); |
| 307 TEST_STATUS(status, U_ZERO_ERROR); |
| 308 |
| 309 |
| 310 /* After reinitializing ICU, we should not be able to set the mutex funcs ag
ain. */ |
| 311 status = U_ZERO_ERROR; |
| 312 u_setDataDirectory(icuDataDir); |
| 313 u_init(&status); |
| 314 TEST_STATUS(status, U_ZERO_ERROR); |
| 315 u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myM
utexUnlock, &status); |
| 316 TEST_STATUS(status, U_INVALID_STATE_ERROR); |
| 317 |
| 318 /* Doing ICU operations should cause allocations to come through our test mu
texes */ |
| 319 gBlockCount = 0; |
| 320 status = U_ZERO_ERROR; |
| 321 /* |
| 322 * Note: If we get assertion failures here because |
| 323 * uresbund.c:resbMutex's fMagic is wrong, check if ures_flushCache() did |
| 324 * flush and delete the cache. If it fails to empty the cache, it will not |
| 325 * delete it and ures_cleanup() will not destroy resbMutex. |
| 326 * That would leave a mutex from the default implementation which does not |
| 327 * pass this test implementation's assertions. |
| 328 */ |
| 329 rb = ures_open(NULL, "es", &status); |
| 330 TEST_STATUS(status, U_ZERO_ERROR); |
| 331 TEST_ASSERT(gTotalMutexesInitialized > 0); |
| 332 TEST_ASSERT(gTotalMutexesActive > 0); |
| 333 |
| 334 ures_close(rb); |
| 335 |
| 336 /* Cleanup should destroy all of the mutexes. */ |
| 337 ctest_resetICU(); |
| 338 status = U_ZERO_ERROR; |
| 339 TEST_ASSERT(gTotalMutexesInitialized > 0); |
| 340 TEST_ASSERT(gTotalMutexesActive == 0); |
| 341 |
| 342 |
| 343 /* Additional ICU operations should no longer use our dummy test mutexes */ |
| 344 gTotalMutexesInitialized = 0; |
| 345 gTotalMutexesActive = 0; |
| 346 u_init(&status); |
| 347 TEST_STATUS(status, U_ZERO_ERROR); |
| 348 |
| 349 status = U_ZERO_ERROR; |
| 350 rb = ures_open(NULL, "fr", &status); |
| 351 TEST_STATUS(status, U_ZERO_ERROR); |
| 352 TEST_ASSERT(gTotalMutexesInitialized == 0); |
| 353 TEST_ASSERT(gTotalMutexesActive == 0); |
| 354 |
| 355 ures_close(rb); |
| 356 free(icuDataDir); |
| 357 |
| 358 if(gMutexFailures) { |
| 359 log_info("Note: these failures may be caused by ICU failing to initialize/
uninitialize properly.\n"); |
| 360 log_verbose("Check for prior tests which may not have closed all open reso
urces. See the internal function ures_flushCache()\n"); |
| 361 } |
| 362 } |
| 363 |
| 364 |
| 365 |
| 366 |
| 367 /* |
| 368 * Test Atomic Increment & Decrement Functions |
| 369 */ |
| 370 |
| 371 int gIncCount = 0; |
| 372 int gDecCount = 0; |
| 373 const void *gIncDecContext; |
| 374 const void *gExpectedContext = &gIncDecContext; |
| 375 |
| 376 |
| 377 static int32_t U_CALLCONV myIncFunc(const void *context, int32_t *p) { |
| 378 int32_t retVal; |
| 379 TEST_ASSERT(context == gExpectedContext); |
| 380 gIncCount++; |
| 381 retVal = ++(*p); |
| 382 return retVal; |
| 383 } |
| 384 |
| 385 static int32_t U_CALLCONV myDecFunc(const void *context, int32_t *p) { |
| 386 int32_t retVal; |
| 387 TEST_ASSERT(context == gExpectedContext); |
| 388 gDecCount++; |
| 389 retVal = --(*p); |
| 390 return retVal; |
| 391 } |
| 392 |
| 393 |
| 394 |
| 395 |
| 396 static void TestIncDecFunctions() { |
| 397 UErrorCode status = U_ZERO_ERROR; |
| 398 int32_t t = 1; /* random value to make sure that Inc/dec works */ |
| 399 char *dataDir; |
| 400 |
| 401 /* Save ICU's data dir and tracing functions so that they can be resored |
| 402 after cleanup and reinit. */ |
| 403 dataDir = safeGetICUDataDirectory(); |
| 404 |
| 405 /* Verify that ICU can be cleaned up and reinitialized successfully. |
| 406 * Failure here usually means that some ICU service didn't clean up success
fully, |
| 407 * probably because some earlier test accidently left something open. */ |
| 408 ctest_resetICU(); |
| 409 |
| 410 /* Can not set mutex functions if ICU is already initialized */ |
| 411 u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc, &status); |
| 412 TEST_STATUS(status, U_INVALID_STATE_ERROR); |
| 413 |
| 414 /* Clean up ICU */ |
| 415 u_cleanup(); |
| 416 |
| 417 /* Can not set functions with NULL values */ |
| 418 status = U_ZERO_ERROR; |
| 419 u_setAtomicIncDecFunctions(&gIncDecContext, NULL, myDecFunc, &status); |
| 420 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
| 421 status = U_ZERO_ERROR; |
| 422 u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, NULL, &status); |
| 423 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
| 424 |
| 425 /* u_setIncDecFunctions() should work with null or non-null context pointer
*/ |
| 426 status = U_ZERO_ERROR; |
| 427 gExpectedContext = NULL; |
| 428 u_setAtomicIncDecFunctions(NULL, myIncFunc, myDecFunc, &status); |
| 429 TEST_STATUS(status, U_ZERO_ERROR); |
| 430 gExpectedContext = &gIncDecContext; |
| 431 u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc, &status); |
| 432 TEST_STATUS(status, U_ZERO_ERROR); |
| 433 |
| 434 |
| 435 /* After reinitializing ICU, we should not be able to set the inc/dec funcs
again. */ |
| 436 status = U_ZERO_ERROR; |
| 437 u_setDataDirectory(dataDir); |
| 438 u_init(&status); |
| 439 TEST_STATUS(status, U_ZERO_ERROR); |
| 440 gExpectedContext = &gIncDecContext; |
| 441 u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc, &status); |
| 442 TEST_STATUS(status, U_INVALID_STATE_ERROR); |
| 443 |
| 444 /* Doing ICU operations should cause our functions to be called */ |
| 445 gIncCount = 0; |
| 446 gDecCount = 0; |
| 447 umtx_atomic_inc(&t); |
| 448 TEST_ASSERT(t == 2); |
| 449 umtx_atomic_dec(&t); |
| 450 TEST_ASSERT(t == 1); |
| 451 TEST_ASSERT(gIncCount > 0); |
| 452 TEST_ASSERT(gDecCount > 0); |
| 453 |
| 454 |
| 455 /* Cleanup should cancel use of our inc/dec functions. */ |
| 456 /* Additional ICU operations should not use them */ |
| 457 ctest_resetICU(); |
| 458 gIncCount = 0; |
| 459 gDecCount = 0; |
| 460 status = U_ZERO_ERROR; |
| 461 u_setDataDirectory(dataDir); |
| 462 u_init(&status); |
| 463 TEST_ASSERT(gIncCount == 0); |
| 464 TEST_ASSERT(gDecCount == 0); |
| 465 |
| 466 status = U_ZERO_ERROR; |
| 467 umtx_atomic_inc(&t); |
| 468 umtx_atomic_dec(&t); |
| 469 TEST_STATUS(status, U_ZERO_ERROR); |
| 470 TEST_ASSERT(gIncCount == 0); |
| 471 TEST_ASSERT(gDecCount == 0); |
| 472 |
| 473 free(dataDir); |
| 474 } |
| 475 |
OLD | NEW |