#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>
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, "tsutil/hpmufn/TestHeapFunctions" );
addTest(root, &TestMutexFunctions, "tsutil/hpmufn/TestMutexFunctions" );
addTest(root, &TestIncDecFunctions, "tsutil/hpmufn/TestIncDecFunctions");
}
static int32_t gMutexFailures = 0;
#define TEST_STATUS(status, expected) \
if (status != expected) { \
log_err("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++; \
}
static char *safeGetICUDataDirectory() {
const char *dataDir = u_getDataDirectory();
char *retStr = NULL;
if (dataDir != NULL) {
retStr = (char *)malloc(strlen(dataDir)+1);
strcpy(retStr, dataDir);
}
return retStr;
}
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;
UTraceEntry *traceEntryFunc;
UTraceExit *traceExitFunc;
UTraceData *traceDataFunc;
const void *traceContext;
int32_t traceLevel;
icuDataDir = safeGetICUDataDirectory();
utrace_getFunctions(&traceContext, &traceEntryFunc, &traceExitFunc, &traceDataFunc);
traceLevel = utrace_getLevel();
u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, myMemFree, &status);
TEST_STATUS(status, U_INVALID_STATE_ERROR);
u_cleanup();
utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
utrace_setLevel(traceLevel);
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);
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);
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);
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);
u_cleanup();
utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
utrace_setLevel(traceLevel);
u_setDataDirectory(icuDataDir);
status = U_ZERO_ERROR;
u_init(&status);
TEST_STATUS(status, U_ZERO_ERROR);
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);
}
int gTotalMutexesInitialized = 0;
int gTotalMutexesActive = 0;
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;
UTraceEntry *traceEntryFunc;
UTraceExit *traceExitFunc;
UTraceData *traceDataFunc;
const void *traceContext;
int32_t traceLevel;
gMutexFailures = 0;
icuDataDir = safeGetICUDataDirectory();
utrace_getFunctions(&traceContext, &traceEntryFunc, &traceExitFunc, &traceDataFunc);
traceLevel = utrace_getLevel();
u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status);
TEST_STATUS(status, U_INVALID_STATE_ERROR);
u_cleanup();
utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
utrace_setLevel(traceLevel);
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);
status = U_ZERO_ERROR;
u_setMutexFunctions(&gContext, 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);
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);
gBlockCount = 0;
status = U_ZERO_ERROR;
rb = ures_open(NULL, "es", &status);
TEST_STATUS(status, U_ZERO_ERROR);
TEST_ASSERT(gTotalMutexesInitialized > 0);
TEST_ASSERT(gTotalMutexesActive > 0);
ures_close(rb);
u_cleanup();
u_setDataDirectory(icuDataDir);
utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
utrace_setLevel(traceLevel);
status = U_ZERO_ERROR;
TEST_ASSERT(gTotalMutexesInitialized > 0);
TEST_ASSERT(gTotalMutexesActive == 0);
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");
}
}
int gIncCount = 0;
int gDecCount = 0;
const void *gIncDecContext;
static int32_t U_CALLCONV myIncFunc(const void *context, int32_t *p) {
int32_t retVal;
TEST_ASSERT(context == gIncDecContext);
gIncCount++;
retVal = ++(*p);
return retVal;
}
static int32_t U_CALLCONV myDecFunc(const void *context, int32_t *p) {
int32_t retVal;
TEST_ASSERT(context == gIncDecContext);
gDecCount++;
retVal = --(*p);
return retVal;
}
static void TestIncDecFunctions() {
UErrorCode status = U_ZERO_ERROR;
int32_t t = 1;
char *dataDir;
UTraceEntry *traceEntryFunc;
UTraceExit *traceExitFunc;
UTraceData *traceDataFunc;
const void *traceContext;
int32_t traceLevel;
u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc, &status);
TEST_STATUS(status, U_INVALID_STATE_ERROR);
dataDir = safeGetICUDataDirectory();
utrace_getFunctions(&traceContext, &traceEntryFunc, &traceExitFunc, &traceDataFunc);
traceLevel = utrace_getLevel();
u_cleanup();
utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
utrace_setLevel(traceLevel);
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);
status = U_ZERO_ERROR;
u_setAtomicIncDecFunctions(NULL, myIncFunc, myDecFunc, &status);
TEST_STATUS(status, U_ZERO_ERROR);
u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc, &status);
TEST_STATUS(status, U_ZERO_ERROR);
status = U_ZERO_ERROR;
u_setDataDirectory(dataDir);
u_init(&status);
TEST_STATUS(status, U_ZERO_ERROR);
u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc, &status);
TEST_STATUS(status, U_INVALID_STATE_ERROR);
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);
u_cleanup();
utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
utrace_setLevel(traceLevel);
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);
}