Home | History | Annotate | Download | only in cintltst
      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)); gMutexFailures++; }
     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 with 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 size) {
    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 put 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 successfully,
    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 again. */
    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 heap */
    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 test 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 created */
    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 successfully,
    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, myMutexUnlock, &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, myMutexUnlock, &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, myMutexUnlock, &status);
    297     TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
    298     status = U_ZERO_ERROR;
    299     u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, NULL, &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, myMutexUnlock, &status);
    305     TEST_STATUS(status, U_ZERO_ERROR);
    306     u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status);
    307     TEST_STATUS(status, U_ZERO_ERROR);
    308 
    309 
    310     /* After reinitializing ICU, we should not be able to set the mutex funcs again. */
    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, myMutexUnlock, &status);
    316     TEST_STATUS(status, U_INVALID_STATE_ERROR);
    317 
    318     /* Doing ICU operations should cause allocations to come through our test mutexes */
    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 resources. 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 successfully,
    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 
    476