1// © 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-2013, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 ********************************************************************/
8/*
9 * File tracetst.c
10 *
11 */
12
13
14#include "unicode/utypes.h"
15#include "unicode/utrace.h"
16#include "unicode/uclean.h"
17#include "unicode/uchar.h"
18#include "unicode/ures.h"
19#include "unicode/ucnv.h"
20#include "cintltst.h"
21#include <stdlib.h>
22#include <stdio.h>
23#include <string.h>
24
25/* We define the following to always test tracing, even when it's off in the library. */
26#if U_ENABLE_TRACING
27#define ENABLE_TRACING_ORIG_VAL 1
28#else
29#define ENABLE_TRACING_ORIG_VAL 0
30#endif
31#undef U_ENABLE_TRACING
32#define U_ENABLE_TRACING 1
33#include "utracimp.h"
34
35
36static void TestTraceAPI(void);
37
38
39void
40addUTraceTest(TestNode** root);
41
42void
43addUTraceTest(TestNode** root)
44{
45    addTest(root, &TestTraceAPI,            "tsutil/TraceTest/TestTraceAPI"  );
46}
47
48
49/*
50 * Macro for assert style tests.
51 */
52#define TEST_ASSERT(expr) \
53if (!(expr)) { \
54    log_err("FAILED Assertion \"" #expr "\" at  %s:%d.\n", __FILE__, __LINE__); \
55}
56
57
58/*
59 *  test_format.   Helper function for checking the results of a formatting
60 *                 operation.  Executes the format op and compares actual
61 *                 results with the expected results.
62 *
63 *       params:   format:  the format to be applied.
64 *                 bufCap   buffer size to pass to formatter.
65 *                 indent:  indent value to give to formatter
66 *                 result   expected result.  Do not truncate for short bufCap -
67 *                          this function will do it.
68 *                 line     __LINE__, so we can report where failure happened.
69 *                 ...      variable args to pass to formatter
70 *
71 */
72static void test_format(const char *format, int32_t bufCap, int32_t indent,
73                        const char *result, int32_t line, ...) {
74    int32_t  len;
75    va_list  args;
76    char  buf[300];
77    char  expectedResult[300];
78
79    /* check that local buffers are big enough for the test case */
80    if (sizeof(buf) <= bufCap) {
81        log_err("At file:line %s:%d, requested bufCap too large.\n");
82        return;
83    }
84    if (strlen(result) >= sizeof(expectedResult)) {
85        log_err("At file:line %s:%d, expected result too large.\n");
86        return;
87    }
88
89   /* Guarantee a nul term if buffer is smaller than output */
90    strcpy(expectedResult, result);
91    expectedResult[bufCap] = 0;
92
93    /* run the formatter */
94    va_start(args, line);
95    memset(buf, 0, sizeof(buf));
96    len = utrace_vformat(buf, bufCap, indent, format, args);
97    (void)len;    /* Suppress set but not used warning. */
98
99    /* Check results.   */
100    if (strcmp(expectedResult, buf) != 0) {
101        log_err("At file:line %s:%d  Expected \"%s\", got \"%s\"  \n",
102             __FILE__, line, expectedResult, buf);
103    }
104    va_end(args);
105}
106
107
108/*
109 *  define trace functions for use in this test.
110 */
111static int    gTraceEntryCount;
112static int    gTraceExitCount;
113static int    gTraceDataCount;
114static UBool  gFnNameError   = FALSE;
115static UBool  gFnFormatError = FALSE;
116
117static void U_CALLCONV testTraceEntry(const void *context, int32_t fnNumber) {
118    const char *fnName;
119    const char *bogusFnName;
120
121    gTraceEntryCount++;
122
123    /* Verify that a name is available for the fnNumber passed to us */
124    bogusFnName = utrace_functionName(-1);
125    fnName = utrace_functionName(fnNumber);
126    if (strcmp(fnName, bogusFnName) == 0) {
127        gFnNameError = TRUE;
128    }
129    /* printf("%s() Enter\n", fnName); */
130
131}
132
133static void U_CALLCONV testTraceExit(const void *context, int32_t fnNumber,
134                   const char *fmt, va_list args) {
135    char        buf[1000];
136    const char *fnName;
137    const char *bogusFnName;
138
139    gTraceExitCount++;
140
141    /* Verify that a name is available for the fnNumber passed to us */
142    bogusFnName = utrace_functionName(-1);
143    fnName = utrace_functionName(fnNumber);
144    if (strcmp(fnName, bogusFnName) == 0) {
145        gFnNameError = TRUE;
146    }
147
148    /* Verify that the format can be used.  */
149    buf[0] = 0;
150    utrace_vformat(buf, sizeof(buf), 0, fmt, args);
151    if (strlen(buf) == 0) {
152        gFnFormatError = TRUE;
153    }
154
155    /* printf("%s() %s\n", fnName, buf); */
156
157}
158
159static void U_CALLCONV testTraceData(const void *context, int32_t fnNumber, int32_t level,
160                   const char *fmt, va_list args) {
161    char        buf[1000];
162    const char *fnName;
163    const char *bogusFnName;
164
165    gTraceDataCount++;
166
167    /* Verify that a name is available for the fnNumber passed to us */
168    bogusFnName = utrace_functionName(-1);
169    fnName = utrace_functionName(fnNumber);
170    if (strcmp(fnName, bogusFnName) == 0) {
171        gFnNameError = TRUE;
172    }
173
174    /* Verify that the format can be used.  */
175    buf[0] = 0;
176    utrace_vformat(buf, sizeof(buf), 0, fmt, args);
177    if (strlen(buf) == 0) {
178        gFnFormatError = TRUE;
179    }
180
181    /* printf("  %s()   %s\n", fnName, buf); */
182}
183
184static UConverter * psuedo_ucnv_open(const char *name, UErrorCode * err)
185{
186    UTRACE_ENTRY_OC(UTRACE_UCNV_LOAD);
187
188    UTRACE_DATA2(UTRACE_OPEN_CLOSE, "error code is %s for %s", u_errorName(*err), name);
189
190    UTRACE_EXIT_PTR_STATUS(NULL, *err);
191    return NULL;
192}
193static void psuedo_ucnv_close(UConverter * cnv)
194{
195    UTRACE_ENTRY_OC(UTRACE_UCNV_UNLOAD);
196    UTRACE_DATA1(UTRACE_OPEN_CLOSE, "unload converter %p", cnv);
197    UTRACE_EXIT_VALUE((int32_t)TRUE);
198}
199
200
201/*
202 *   TestTraceAPI
203 */
204static void TestTraceAPI() {
205
206
207    UTraceEntry   *originalTEntryFunc;
208    UTraceExit    *originalTExitFunc;
209    UTraceData    *originalTDataFunc;
210    const void    *originalTContext;
211    int32_t        originalLevel;
212
213    /*
214     * Save the original tracing state so that we can restore it after the test.
215     */
216    utrace_getFunctions(&originalTContext, &originalTEntryFunc, &originalTExitFunc,
217                        &originalTDataFunc);
218    originalLevel = utrace_getLevel();
219
220
221    /* verify that set/get of tracing functions returns what was set.  */
222    {
223        UTraceEntry *e;
224        UTraceExit  *x;
225        UTraceData  *d;
226        const void  *context;
227        const void  *newContext = (const char *)originalTContext + 1;
228
229        TEST_ASSERT(originalTEntryFunc != testTraceEntry);
230        TEST_ASSERT(originalTExitFunc != testTraceExit);
231        TEST_ASSERT(originalTDataFunc != testTraceData);
232
233        utrace_setFunctions(newContext, testTraceEntry, testTraceExit, testTraceData);
234        utrace_getFunctions(&context, &e, &x, &d);
235        TEST_ASSERT(e == testTraceEntry);
236        TEST_ASSERT(x == testTraceExit);
237        TEST_ASSERT(d == testTraceData);
238        TEST_ASSERT(context == newContext);
239    }
240
241    /* verify that set/get level work as a pair, and that the level
242     * identifiers all exist.
243     */
244
245    {
246        int32_t  level;
247
248        utrace_setLevel(UTRACE_OFF);
249        level = utrace_getLevel();
250        TEST_ASSERT(level==UTRACE_OFF);
251        utrace_setLevel(UTRACE_VERBOSE);
252        level = utrace_getLevel();
253        TEST_ASSERT(level==UTRACE_VERBOSE);
254        utrace_setLevel(UTRACE_ERROR);
255        utrace_setLevel(UTRACE_WARNING);
256        utrace_setLevel(UTRACE_OPEN_CLOSE);
257        utrace_setLevel(UTRACE_INFO);
258    }
259
260    /*
261     * Open and close a converter with tracing enabled.
262     *   Verify that our tracing callback functions get called.
263     */
264    {
265        UErrorCode  status = U_ZERO_ERROR;
266        UConverter *cnv;
267
268        gTraceEntryCount = 0;
269        gTraceExitCount  = 0;
270        gTraceDataCount  = 0;
271        gFnNameError     = FALSE;
272        gFnFormatError   = FALSE;
273        utrace_setLevel(UTRACE_OPEN_CLOSE);
274#if ENABLE_TRACING_ORIG_VAL
275        cnv = ucnv_open(NULL, &status);
276        TEST_ASSERT(U_SUCCESS(status));
277        ucnv_close(cnv);
278#else
279        cnv = psuedo_ucnv_open(NULL, &status);
280        TEST_ASSERT(U_SUCCESS(status));
281        psuedo_ucnv_close(cnv);
282#endif
283        TEST_ASSERT(gTraceEntryCount > 0);
284        TEST_ASSERT(gTraceExitCount  > 0);
285        TEST_ASSERT(gTraceDataCount  > 0);
286        TEST_ASSERT(gFnNameError   == FALSE);
287        TEST_ASSERT(gFnFormatError == FALSE);
288    }
289
290
291
292    /*
293     * trace data formatter operation.
294     */
295    {
296        UChar s1[] = {0x41fe, 0x42, 0x43, 00};
297        const char  *a1[] = {"s1", "s2", "s3"};
298        void  *ptr;
299
300        test_format("hello, world", 50, 0, "hello, world", __LINE__);
301        test_format("hello, world", 50, 4, "    hello, world", __LINE__);
302        test_format("hello, world", 3, 0,  "hello, world", __LINE__);
303
304        test_format("a character %c", 50, 0, "a character x", __LINE__, 'x');
305        test_format("a string %s ", 50, 0, "a string hello ", __LINE__, "hello");
306        test_format("uchars %S ", 50, 0, "uchars 41fe 0042 0043 0000  ", __LINE__, s1, -1);
307        test_format("uchars %S ", 50, 0, "uchars 41fe 0042  ", __LINE__, s1, 2);
308
309        test_format("a byte %b--", 50, 0, "a byte dd--", __LINE__, 0xdd);
310        test_format("a 16 bit val %h", 50, 0, "a 16 bit val 1234", __LINE__, 0x1234);
311        test_format("a 32 bit val %d...", 50, 0, "a 32 bit val 6789abcd...", __LINE__, 0x6789abcd);
312        test_format("a 64 bit val %l", 50, 0, "a 64 bit val 123456780abcdef0"
313            , __LINE__, INT64_C(0x123456780abcdef0));
314
315        if (sizeof(void *) == 4) {
316            ptr = (void *)0xdeadbeef;
317            test_format("a 32 bit ptr %p", 50, 0, "a 32 bit ptr deadbeef", __LINE__, ptr);
318        } else if (sizeof(void *) == 8) {
319            ptr = (void *) INT64_C(0x1000200030004000);
320            test_format("a 64 bit ptr %p", 50, 0, "a 64 bit ptr 1000200030004000", __LINE__, ptr);
321        } else if (sizeof(void *) == 16) {
322            /* iSeries */
323            union {
324                int32_t arr[4];
325                void *ptr;
326            } massiveBigEndianPtr = {{ 0x10002000, 0x30004000, 0x50006000, 0x70008000 }};
327            ptr = massiveBigEndianPtr.ptr;
328            test_format("a 128 bit ptr %p", 50, 0, "a 128 bit ptr 10002000300040005000600070008000", __LINE__, ptr);
329        } else {
330            TEST_ASSERT(FALSE);
331            /*  TODO:  others? */
332        }
333
334        test_format("%vc", 100, 0, "abc[ffffffff]", __LINE__, "abc", -1);
335        test_format("%vs", 100, 0, "s1\ns2\n[00000002]", __LINE__, a1, 2);
336        test_format("%vs", 100, 4, "    s1\n    s2\n    [00000002]", __LINE__, a1, 2);
337
338        test_format("%vb", 100, 0, "41 42 43 [00000003]", __LINE__, "\x41\x42\x43", 3);
339
340        /* Null ptrs for strings, vectors  */
341        test_format("Null string - %s", 50, 0, "Null string - *NULL*", __LINE__, NULL);
342        test_format("Null string - %S", 50, 0, "Null string - *NULL*", __LINE__, NULL, -1);
343        test_format("Null vector - %vc", 50, 0, "Null vector - *NULL* [00000002]", __LINE__, NULL, 2);
344        test_format("Null vector - %vC", 50, 0, "Null vector - *NULL* [00000002]", __LINE__, NULL, 2);
345        test_format("Null vector - %vd", 50, 0, "Null vector - *NULL* [00000002]", __LINE__, NULL, 2);
346
347    }
348
349    /*
350     * utrace_format.  Only need a minimal test to see that the function works at all.
351     *                 Full functionality is tested via utrace_vformat.
352     */
353    {
354        char      buf[100];
355        int32_t   x;
356        x = utrace_format(buf, 100, 0, "%s", "Hello, World.");
357        TEST_ASSERT(strcmp(buf, "Hello, World.") == 0);
358        TEST_ASSERT(x == 14);
359    }
360
361    /*
362     * utrace_functionName.  Just spot-check a couple of them.
363     */
364    {
365        const char   *name;
366        name = utrace_functionName(UTRACE_U_INIT);
367        TEST_ASSERT(strcmp(name, "u_init") == 0);
368        name = utrace_functionName(UTRACE_UCNV_OPEN);
369        TEST_ASSERT(strcmp(name, "ucnv_open") == 0);
370        name = utrace_functionName(UTRACE_UCOL_GET_SORTKEY);
371        TEST_ASSERT(strcmp(name, "ucol_getSortKey") == 0);
372    }
373
374
375
376    /*  Restore the trace function settings to their original values. */
377    utrace_setFunctions(originalTContext, originalTEntryFunc, originalTExitFunc, originalTDataFunc);
378    utrace_setLevel(originalLevel);
379}
380
381
382
383