1/********************************************************************
2 * COPYRIGHT:
3 * Copyright (c) 2003-2013, 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 "unicode/utrace.h"
18#include <stdlib.h>
19#include <string.h>
20
21/**
22 * This should align the memory properly on any machine.
23 */
24typedef union {
25    long    t1;
26    double  t2;
27    void   *t3;
28} ctest_AlignedMemory;
29
30static void TestHeapFunctions(void);
31
32void addHeapMutexTest(TestNode **root);
33
34
35void
36addHeapMutexTest(TestNode** root)
37{
38    addTest(root, &TestHeapFunctions,       "hpmufn/TestHeapFunctions"  );
39}
40
41static int32_t gMutexFailures = 0;
42
43#define TEST_STATUS(status, expected) \
44if (status != expected) { \
45log_err_status(status, "FAIL at  %s:%d. Actual status = \"%s\";  Expected status = \"%s\"\n", \
46  __FILE__, __LINE__, u_errorName(status), u_errorName(expected)); gMutexFailures++; }
47
48
49#define TEST_ASSERT(expr) \
50if (!(expr)) { \
51    log_err("FAILED Assertion \"" #expr "\" at  %s:%d.\n", __FILE__, __LINE__); \
52    gMutexFailures++; \
53}
54
55
56/*  These tests do cleanup and reinitialize ICU in the course of their operation.
57 *    The ICU data directory must be preserved across these operations.
58 *    Here is a helper function to assist with that.
59 */
60static char *safeGetICUDataDirectory() {
61    const char *dataDir = u_getDataDirectory();  /* Returned string vanashes with u_cleanup */
62    char *retStr = NULL;
63    if (dataDir != NULL) {
64        retStr = (char *)malloc(strlen(dataDir)+1);
65        strcpy(retStr, dataDir);
66    }
67    return retStr;
68}
69
70
71
72/*
73 *  Test Heap Functions.
74 *    Implemented on top of the standard malloc heap.
75 *    All blocks increased in size by 8 to 16 bytes, and the poiner returned to ICU is
76 *       offset up by 8 to 16, which should cause a good heap corruption if one of our "blocks"
77 *       ends up being freed directly, without coming through us.
78 *    Allocations are counted, to check that ICU actually does call back to us.
79 */
80int    gBlockCount = 0;
81const void  *gContext;
82
83static void * U_CALLCONV myMemAlloc(const void *context, size_t size) {
84    char *retPtr = (char *)malloc(size+sizeof(ctest_AlignedMemory));
85    if (retPtr != NULL) {
86        retPtr += sizeof(ctest_AlignedMemory);
87    }
88    gBlockCount ++;
89    return retPtr;
90}
91
92static void U_CALLCONV myMemFree(const void *context, void *mem) {
93    char *freePtr = (char *)mem;
94    if (freePtr != NULL) {
95        freePtr -= sizeof(ctest_AlignedMemory);
96    }
97    free(freePtr);
98}
99
100
101
102static void * U_CALLCONV myMemRealloc(const void *context, void *mem, size_t size) {
103    char *p = (char *)mem;
104    char *retPtr;
105
106    if (p!=NULL) {
107        p -= sizeof(ctest_AlignedMemory);
108    }
109    retPtr = realloc(p, size+sizeof(ctest_AlignedMemory));
110    if (retPtr != NULL) {
111        p += sizeof(ctest_AlignedMemory);
112    }
113    return retPtr;
114}
115
116
117static void TestHeapFunctions() {
118    UErrorCode       status = U_ZERO_ERROR;
119    UResourceBundle *rb     = NULL;
120    char            *icuDataDir;
121    UVersionInfo unicodeVersion = {0,0,0,0};
122
123    icuDataDir = safeGetICUDataDirectory();   /* save icu data dir, so we can put it back
124                                               *  after doing u_cleanup().                */
125
126
127    /* Verify that ICU can be cleaned up and reinitialized successfully.
128     *  Failure here usually means that some ICU service didn't clean up successfully,
129     *  probably because some earlier test accidently left something open. */
130    ctest_resetICU();
131
132    /* Can not set memory functions if ICU is already initialized */
133    u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, myMemFree, &status);
134    TEST_STATUS(status, U_INVALID_STATE_ERROR);
135
136    /* Un-initialize ICU */
137    u_cleanup();
138
139    /* Can not set memory functions with NULL values */
140    status = U_ZERO_ERROR;
141    u_setMemoryFunctions(&gContext, NULL, myMemRealloc, myMemFree, &status);
142    TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
143    status = U_ZERO_ERROR;
144    u_setMemoryFunctions(&gContext, myMemAlloc, NULL, myMemFree, &status);
145    TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
146    status = U_ZERO_ERROR;
147    u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, NULL, &status);
148    TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
149
150    /* u_setMemoryFunctions() should work with null or non-null context pointer */
151    status = U_ZERO_ERROR;
152    u_setMemoryFunctions(NULL, myMemAlloc, myMemRealloc, myMemFree, &status);
153    TEST_STATUS(status, U_ZERO_ERROR);
154    u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, myMemFree, &status);
155    TEST_STATUS(status, U_ZERO_ERROR);
156
157
158    /* After reinitializing ICU, we should not be able to set the memory funcs again. */
159    status = U_ZERO_ERROR;
160    u_setDataDirectory(icuDataDir);
161    u_init(&status);
162    TEST_STATUS(status, U_ZERO_ERROR);
163    u_setMemoryFunctions(NULL, myMemAlloc, myMemRealloc, myMemFree, &status);
164    TEST_STATUS(status, U_INVALID_STATE_ERROR);
165
166    /* Doing ICU operations should cause allocations to come through our test heap */
167    gBlockCount = 0;
168    status = U_ZERO_ERROR;
169    rb = ures_open(NULL, "es", &status);
170    TEST_STATUS(status, U_ZERO_ERROR);
171    if (gBlockCount == 0) {
172        log_err("Heap functions are not being called from ICU.\n");
173    }
174    ures_close(rb);
175
176    /* Cleanup should put the heap back to its default implementation. */
177    ctest_resetICU();
178    u_getUnicodeVersion(unicodeVersion);
179    if (unicodeVersion[0] <= 0) {
180        log_err("Properties doesn't reinitialize without u_init.\n");
181    }
182    status = U_ZERO_ERROR;
183    u_init(&status);
184    TEST_STATUS(status, U_ZERO_ERROR);
185
186    /* ICU operations should no longer cause allocations to come through our test heap */
187    gBlockCount = 0;
188    status = U_ZERO_ERROR;
189    rb = ures_open(NULL, "fr", &status);
190    TEST_STATUS(status, U_ZERO_ERROR);
191    if (gBlockCount != 0) {
192        log_err("Heap functions did not reset after u_cleanup.\n");
193    }
194    ures_close(rb);
195    free(icuDataDir);
196
197    ctest_resetICU();
198}
199
200
201