Index: icu46/source/test/cintltst/hpmufn.c |
=================================================================== |
--- icu46/source/test/cintltst/hpmufn.c (revision 0) |
+++ icu46/source/test/cintltst/hpmufn.c (revision 0) |
@@ -0,0 +1,475 @@ |
+/******************************************************************** |
+ * COPYRIGHT: |
+ * Copyright (c) 2003-2009, 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 "umutex.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); |
+static void TestMutexFunctions(void); |
+static void TestIncDecFunctions(void); |
+ |
+void addHeapMutexTest(TestNode **root); |
+ |
+ |
+void |
+addHeapMutexTest(TestNode** root) |
+{ |
+ addTest(root, &TestHeapFunctions, "hpmufn/TestHeapFunctions" ); |
+ addTest(root, &TestMutexFunctions, "hpmufn/TestMutexFunctions" ); |
+ addTest(root, &TestIncDecFunctions, "hpmufn/TestIncDecFunctions"); |
+} |
+ |
+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(); |
+ |
+ /* Can not set memory functions if ICU is already initialized */ |
+ u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, myMemFree, &status); |
+ TEST_STATUS(status, U_INVALID_STATE_ERROR); |
+ |
+ /* 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 should not be able to set the memory funcs again. */ |
+ status = U_ZERO_ERROR; |
+ u_setDataDirectory(icuDataDir); |
+ u_init(&status); |
+ TEST_STATUS(status, U_ZERO_ERROR); |
+ u_setMemoryFunctions(NULL, myMemAlloc, myMemRealloc, myMemFree, &status); |
+ TEST_STATUS(status, U_INVALID_STATE_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(); |
+} |
+ |
+ |
+/* |
+ * Test u_setMutexFunctions() |
+ */ |
+ |
+int gTotalMutexesInitialized = 0; /* Total number of mutexes created */ |
+int gTotalMutexesActive = 0; /* Total mutexes created, but not destroyed */ |
+int gAccumulatedLocks = 0; |
+const void *gMutexContext; |
+ |
+typedef struct DummyMutex { |
+ int fLockCount; |
+ int fMagic; |
+} DummyMutex; |
+ |
+ |
+static void U_CALLCONV myMutexInit(const void *context, UMTX *mutex, UErrorCode *status) { |
+ DummyMutex *theMutex; |
+ |
+ TEST_STATUS(*status, U_ZERO_ERROR); |
+ theMutex = (DummyMutex *)malloc(sizeof(DummyMutex)); |
+ theMutex->fLockCount = 0; |
+ theMutex->fMagic = 123456; |
+ gTotalMutexesInitialized++; |
+ gTotalMutexesActive++; |
+ gMutexContext = context; |
+ *mutex = theMutex; |
+} |
+ |
+ |
+static void U_CALLCONV myMutexDestroy(const void *context, UMTX *mutex) { |
+ DummyMutex *This = *(DummyMutex **)mutex; |
+ |
+ gTotalMutexesActive--; |
+ TEST_ASSERT(This->fLockCount == 0); |
+ TEST_ASSERT(This->fMagic == 123456); |
+ This->fMagic = 0; |
+ This->fLockCount = 0; |
+ free(This); |
+} |
+ |
+static void U_CALLCONV myMutexLock(const void *context, UMTX *mutex) { |
+ DummyMutex *This = *(DummyMutex **)mutex; |
+ |
+ TEST_ASSERT(This->fMagic == 123456); |
+ This->fLockCount++; |
+ gAccumulatedLocks++; |
+} |
+ |
+static void U_CALLCONV myMutexUnlock(const void *context, UMTX *mutex) { |
+ DummyMutex *This = *(DummyMutex **)mutex; |
+ |
+ TEST_ASSERT(This->fMagic == 123456); |
+ This->fLockCount--; |
+ TEST_ASSERT(This->fLockCount >= 0); |
+} |
+ |
+ |
+ |
+static void TestMutexFunctions() { |
+ UErrorCode status = U_ZERO_ERROR; |
+ UResourceBundle *rb = NULL; |
+ char *icuDataDir; |
+ |
+ gMutexFailures = 0; |
+ |
+ /* Save initial ICU state so that it can be restored later. |
+ * u_cleanup(), which is called in this test, resets ICU's state. |
+ */ |
+ icuDataDir = safeGetICUDataDirectory(); |
+ |
+ /* 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(); |
+ |
+ /* Can not set mutex functions if ICU is already initialized */ |
+ u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status); |
+ TEST_STATUS(status, U_INVALID_STATE_ERROR); |
+ |
+ /* Un-initialize ICU */ |
+ u_cleanup(); |
+ |
+ /* Can not set Mutex functions with NULL values */ |
+ status = U_ZERO_ERROR; |
+ u_setMutexFunctions(&gContext, NULL, myMutexDestroy, myMutexLock, myMutexUnlock, &status); |
+ TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
+ status = U_ZERO_ERROR; |
+ u_setMutexFunctions(&gContext, myMutexInit, NULL, myMutexLock, myMutexUnlock, &status); |
+ TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
+ status = U_ZERO_ERROR; |
+ u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, NULL, myMutexUnlock, &status); |
+ TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
+ status = U_ZERO_ERROR; |
+ u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, NULL, &status); |
+ TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
+ |
+ /* u_setMutexFunctions() should work with null or non-null context pointer */ |
+ status = U_ZERO_ERROR; |
+ u_setMutexFunctions(NULL, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status); |
+ TEST_STATUS(status, U_ZERO_ERROR); |
+ u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status); |
+ TEST_STATUS(status, U_ZERO_ERROR); |
+ |
+ |
+ /* After reinitializing ICU, we should not be able to set the mutex funcs again. */ |
+ status = U_ZERO_ERROR; |
+ u_setDataDirectory(icuDataDir); |
+ u_init(&status); |
+ TEST_STATUS(status, U_ZERO_ERROR); |
+ u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status); |
+ TEST_STATUS(status, U_INVALID_STATE_ERROR); |
+ |
+ /* Doing ICU operations should cause allocations to come through our test mutexes */ |
+ gBlockCount = 0; |
+ status = U_ZERO_ERROR; |
+ /* |
+ * Note: If we get assertion failures here because |
+ * uresbund.c:resbMutex's fMagic is wrong, check if ures_flushCache() did |
+ * flush and delete the cache. If it fails to empty the cache, it will not |
+ * delete it and ures_cleanup() will not destroy resbMutex. |
+ * That would leave a mutex from the default implementation which does not |
+ * pass this test implementation's assertions. |
+ */ |
+ rb = ures_open(NULL, "es", &status); |
+ TEST_STATUS(status, U_ZERO_ERROR); |
+ TEST_ASSERT(gTotalMutexesInitialized > 0); |
+ TEST_ASSERT(gTotalMutexesActive > 0); |
+ |
+ ures_close(rb); |
+ |
+ /* Cleanup should destroy all of the mutexes. */ |
+ ctest_resetICU(); |
+ status = U_ZERO_ERROR; |
+ TEST_ASSERT(gTotalMutexesInitialized > 0); |
+ TEST_ASSERT(gTotalMutexesActive == 0); |
+ |
+ |
+ /* Additional ICU operations should no longer use our dummy test mutexes */ |
+ gTotalMutexesInitialized = 0; |
+ gTotalMutexesActive = 0; |
+ u_init(&status); |
+ TEST_STATUS(status, U_ZERO_ERROR); |
+ |
+ status = U_ZERO_ERROR; |
+ rb = ures_open(NULL, "fr", &status); |
+ TEST_STATUS(status, U_ZERO_ERROR); |
+ TEST_ASSERT(gTotalMutexesInitialized == 0); |
+ TEST_ASSERT(gTotalMutexesActive == 0); |
+ |
+ ures_close(rb); |
+ free(icuDataDir); |
+ |
+ if(gMutexFailures) { |
+ log_info("Note: these failures may be caused by ICU failing to initialize/uninitialize properly.\n"); |
+ log_verbose("Check for prior tests which may not have closed all open resources. See the internal function ures_flushCache()\n"); |
+ } |
+} |
+ |
+ |
+ |
+ |
+/* |
+ * Test Atomic Increment & Decrement Functions |
+ */ |
+ |
+int gIncCount = 0; |
+int gDecCount = 0; |
+const void *gIncDecContext; |
+const void *gExpectedContext = &gIncDecContext; |
+ |
+ |
+static int32_t U_CALLCONV myIncFunc(const void *context, int32_t *p) { |
+ int32_t retVal; |
+ TEST_ASSERT(context == gExpectedContext); |
+ gIncCount++; |
+ retVal = ++(*p); |
+ return retVal; |
+} |
+ |
+static int32_t U_CALLCONV myDecFunc(const void *context, int32_t *p) { |
+ int32_t retVal; |
+ TEST_ASSERT(context == gExpectedContext); |
+ gDecCount++; |
+ retVal = --(*p); |
+ return retVal; |
+} |
+ |
+ |
+ |
+ |
+static void TestIncDecFunctions() { |
+ UErrorCode status = U_ZERO_ERROR; |
+ int32_t t = 1; /* random value to make sure that Inc/dec works */ |
+ char *dataDir; |
+ |
+ /* Save ICU's data dir and tracing functions so that they can be resored |
+ after cleanup and reinit. */ |
+ dataDir = safeGetICUDataDirectory(); |
+ |
+ /* 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(); |
+ |
+ /* Can not set mutex functions if ICU is already initialized */ |
+ u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc, &status); |
+ TEST_STATUS(status, U_INVALID_STATE_ERROR); |
+ |
+ /* Clean up ICU */ |
+ u_cleanup(); |
+ |
+ /* Can not set functions with NULL values */ |
+ status = U_ZERO_ERROR; |
+ u_setAtomicIncDecFunctions(&gIncDecContext, NULL, myDecFunc, &status); |
+ TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
+ status = U_ZERO_ERROR; |
+ u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, NULL, &status); |
+ TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); |
+ |
+ /* u_setIncDecFunctions() should work with null or non-null context pointer */ |
+ status = U_ZERO_ERROR; |
+ gExpectedContext = NULL; |
+ u_setAtomicIncDecFunctions(NULL, myIncFunc, myDecFunc, &status); |
+ TEST_STATUS(status, U_ZERO_ERROR); |
+ gExpectedContext = &gIncDecContext; |
+ u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc, &status); |
+ TEST_STATUS(status, U_ZERO_ERROR); |
+ |
+ |
+ /* After reinitializing ICU, we should not be able to set the inc/dec funcs again. */ |
+ status = U_ZERO_ERROR; |
+ u_setDataDirectory(dataDir); |
+ u_init(&status); |
+ TEST_STATUS(status, U_ZERO_ERROR); |
+ gExpectedContext = &gIncDecContext; |
+ u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc, &status); |
+ TEST_STATUS(status, U_INVALID_STATE_ERROR); |
+ |
+ /* Doing ICU operations should cause our functions to be called */ |
+ gIncCount = 0; |
+ gDecCount = 0; |
+ umtx_atomic_inc(&t); |
+ TEST_ASSERT(t == 2); |
+ umtx_atomic_dec(&t); |
+ TEST_ASSERT(t == 1); |
+ TEST_ASSERT(gIncCount > 0); |
+ TEST_ASSERT(gDecCount > 0); |
+ |
+ |
+ /* Cleanup should cancel use of our inc/dec functions. */ |
+ /* Additional ICU operations should not use them */ |
+ ctest_resetICU(); |
+ gIncCount = 0; |
+ gDecCount = 0; |
+ status = U_ZERO_ERROR; |
+ u_setDataDirectory(dataDir); |
+ u_init(&status); |
+ TEST_ASSERT(gIncCount == 0); |
+ TEST_ASSERT(gDecCount == 0); |
+ |
+ status = U_ZERO_ERROR; |
+ umtx_atomic_inc(&t); |
+ umtx_atomic_dec(&t); |
+ TEST_STATUS(status, U_ZERO_ERROR); |
+ TEST_ASSERT(gIncCount == 0); |
+ TEST_ASSERT(gDecCount == 0); |
+ |
+ free(dataDir); |
+} |
+ |
Property changes on: icu46/source/test/cintltst/hpmufn.c |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |