| /******************************************************************** |
| * COPYRIGHT: |
| * Copyright (c) 2003-2015, International Business Machines Corporation and |
| * others. All Rights Reserved. |
| ********************************************************************/ |
| /* |
| * File hpmufn.c |
| * |
| */ |
| |
| #include "unicode/utypes.h" |
| #include "unicode/putil.h" |
| #include "unicode/uclean.h" |
| #include "unicode/uchar.h" |
| #include "unicode/ures.h" |
| #include "cintltst.h" |
| #include "unicode/utrace.h" |
| #include <stdlib.h> |
| #include <string.h> |
| |
| /** |
| * This should align the memory properly on any machine. |
| */ |
| typedef union { |
| long t1; |
| double t2; |
| void *t3; |
| } ctest_AlignedMemory; |
| |
| static void TestHeapFunctions(void); |
| |
| void addHeapMutexTest(TestNode **root); |
| |
| |
| void |
| addHeapMutexTest(TestNode** root) |
| { |
| addTest(root, &TestHeapFunctions, "hpmufn/TestHeapFunctions" ); |
| } |
| |
| static int32_t gMutexFailures = 0; |
| |
| #define TEST_STATUS(status, expected) \ |
| if (status != expected) { \ |
| log_err_status(status, "FAIL at %s:%d. Actual status = \"%s\"; Expected status = \"%s\"\n", \ |
| __FILE__, __LINE__, u_errorName(status), u_errorName(expected)); gMutexFailures++; } |
| |
| |
| #define TEST_ASSERT(expr) \ |
| if (!(expr)) { \ |
| log_err("FAILED Assertion \"" #expr "\" at %s:%d.\n", __FILE__, __LINE__); \ |
| gMutexFailures++; \ |
| } |
| |
| |
| /* These tests do cleanup and reinitialize ICU in the course of their operation. |
| * The ICU data directory must be preserved across these operations. |
| * Here is a helper function to assist with that. |
| */ |
| static char *safeGetICUDataDirectory() { |
| const char *dataDir = u_getDataDirectory(); /* Returned string vanashes with u_cleanup */ |
| char *retStr = NULL; |
| if (dataDir != NULL) { |
| retStr = (char *)malloc(strlen(dataDir)+1); |
| strcpy(retStr, dataDir); |
| } |
| return retStr; |
| } |
| |
| |
| |
| /* |
| * Test Heap Functions. |
| * Implemented on top of the standard malloc heap. |
| * All blocks increased in size by 8 to 16 bytes, and the poiner returned to ICU is |
| * offset up by 8 to 16, which should cause a good heap corruption if one of our "blocks" |
| * ends up being freed directly, without coming through us. |
| * Allocations are counted, to check that ICU actually does call back to us. |
| */ |
| int gBlockCount = 0; |
| const void *gContext; |
| |
| static void * U_CALLCONV myMemAlloc(const void *context, size_t size) { |
| char *retPtr = (char *)malloc(size+sizeof(ctest_AlignedMemory)); |
| if (retPtr != NULL) { |
| retPtr += sizeof(ctest_AlignedMemory); |
| } |
| gBlockCount ++; |
| return retPtr; |
| } |
| |
| static void U_CALLCONV myMemFree(const void *context, void *mem) { |
| char *freePtr = (char *)mem; |
| if (freePtr != NULL) { |
| freePtr -= sizeof(ctest_AlignedMemory); |
| } |
| free(freePtr); |
| } |
| |
| |
| |
| static void * U_CALLCONV myMemRealloc(const void *context, void *mem, size_t size) { |
| char *p = (char *)mem; |
| char *retPtr; |
| |
| if (p!=NULL) { |
| p -= sizeof(ctest_AlignedMemory); |
| } |
| retPtr = realloc(p, size+sizeof(ctest_AlignedMemory)); |
| if (retPtr != NULL) { |
| p += sizeof(ctest_AlignedMemory); |
| } |
| return retPtr; |
| } |
| |
| |
| static void TestHeapFunctions() { |
| UErrorCode status = U_ZERO_ERROR; |
| UResourceBundle *rb = NULL; |
| char *icuDataDir; |
| UVersionInfo unicodeVersion = {0,0,0,0}; |
| |
| icuDataDir = safeGetICUDataDirectory(); /* save icu data dir, so we can put it back |
| * after doing u_cleanup(). */ |
| |
| |
| /* Verify that ICU can be cleaned up and reinitialized successfully. |
| * Failure here usually means that some ICU service didn't clean up successfully, |
| * probably because some earlier test accidently left something open. */ |
| ctest_resetICU(); |
| |
| /* Un-initialize ICU */ |
| u_cleanup(); |
| |
| /* Can not set memory functions with NULL values */ |
| status = U_ZERO_ERROR; |
| u_setMemoryFunctions(&gContext, NULL, myMemRealloc, myMemFree, &status); |
| TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
| status = U_ZERO_ERROR; |
| u_setMemoryFunctions(&gContext, myMemAlloc, NULL, myMemFree, &status); |
| TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
| status = U_ZERO_ERROR; |
| u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, NULL, &status); |
| TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
| |
| /* u_setMemoryFunctions() should work with null or non-null context pointer */ |
| status = U_ZERO_ERROR; |
| u_setMemoryFunctions(NULL, myMemAlloc, myMemRealloc, myMemFree, &status); |
| TEST_STATUS(status, U_ZERO_ERROR); |
| u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, myMemFree, &status); |
| TEST_STATUS(status, U_ZERO_ERROR); |
| |
| |
| /* After reinitializing ICU, we can not set the memory funcs again. */ |
| status = U_ZERO_ERROR; |
| u_setDataDirectory(icuDataDir); |
| u_init(&status); |
| TEST_STATUS(status, U_ZERO_ERROR); |
| |
| /* Doing ICU operations should cause allocations to come through our test heap */ |
| gBlockCount = 0; |
| status = U_ZERO_ERROR; |
| rb = ures_open(NULL, "es", &status); |
| TEST_STATUS(status, U_ZERO_ERROR); |
| if (gBlockCount == 0) { |
| log_err("Heap functions are not being called from ICU.\n"); |
| } |
| ures_close(rb); |
| |
| /* Cleanup should put the heap back to its default implementation. */ |
| ctest_resetICU(); |
| u_getUnicodeVersion(unicodeVersion); |
| if (unicodeVersion[0] <= 0) { |
| log_err("Properties doesn't reinitialize without u_init.\n"); |
| } |
| status = U_ZERO_ERROR; |
| u_init(&status); |
| TEST_STATUS(status, U_ZERO_ERROR); |
| |
| /* ICU operations should no longer cause allocations to come through our test heap */ |
| gBlockCount = 0; |
| status = U_ZERO_ERROR; |
| rb = ures_open(NULL, "fr", &status); |
| TEST_STATUS(status, U_ZERO_ERROR); |
| if (gBlockCount != 0) { |
| log_err("Heap functions did not reset after u_cleanup.\n"); |
| } |
| ures_close(rb); |
| free(icuDataDir); |
| |
| ctest_resetICU(); |
| } |
| |
| |