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