1/**
2 * Copyright © 2012 Canonical, Ltd.
3 *
4 *  Permission is hereby granted, free of charge, to any person obtaining a
5 *  copy of this software and associated documentation files (the "Software"),
6 *  to deal in the Software without restriction, including without limitation
7 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 *  and/or sell copies of the Software, and to permit persons to whom the
9 *  Software is furnished to do so, subject to the following conditions:
10 *
11 *  The above copyright notice and this permission notice (including the next
12 *  paragraph) shall be included in all copies or substantial portions of the
13 *  Software.
14 *
15 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 *  DEALINGS IN THE SOFTWARE.
22 */
23
24/* Test relies on assert() */
25#undef NDEBUG
26
27#ifdef HAVE_DIX_CONFIG_H
28#include <dix-config.h>
29#endif
30
31#include <stdint.h>
32#include <unistd.h>
33#include "assert.h"
34#include "misc.h"
35
36#include "tests-common.h"
37
38struct number_format_test {
39    uint64_t number;
40    char string[21];
41    char hex_string[17];
42};
43
44struct signed_number_format_test {
45    int64_t number;
46    char string[21];
47};
48
49struct float_number_format_test {
50    double number;
51    char string[21];
52};
53
54static Bool
55check_signed_number_format_test(long int number)
56{
57    char string[21];
58    char expected[21];
59
60    sprintf(expected, "%ld", number);
61    FormatInt64(number, string);
62    if(strncmp(string, expected, 21) != 0) {
63        fprintf(stderr, "Failed to convert %jd to decimal string (expected %s but got %s)\n",
64                (intmax_t) number, expected, string);
65        return FALSE;
66    }
67
68    return TRUE;
69}
70
71static Bool
72check_float_format_test(double number)
73{
74    char string[21];
75    char expected[21];
76
77    /* we currently always print float as .2f */
78    sprintf(expected, "%.2f", number);
79
80    FormatDouble(number, string);
81    if(strncmp(string, expected, 21) != 0) {
82        fprintf(stderr, "Failed to convert %f to string (%s vs %s)\n",
83                number, expected, string);
84        return FALSE;
85    }
86
87    return TRUE;
88}
89
90static Bool
91check_number_format_test(long unsigned int number)
92{
93    char string[21];
94    char expected[21];
95
96    sprintf(expected, "%lu", number);
97
98    FormatUInt64(number, string);
99    if(strncmp(string, expected, 21) != 0) {
100        fprintf(stderr, "Failed to convert %ju to decimal string (%s vs %s)\n",
101                (intmax_t) number, expected, string);
102        return FALSE;
103    }
104
105    sprintf(expected, "%lx", number);
106    FormatUInt64Hex(number, string);
107    if(strncmp(string, expected, 17) != 0) {
108        fprintf(stderr, "Failed to convert %ju to hexadecimal string (%s vs %s)\n",
109                (intmax_t) number, expected, string);
110        return FALSE;
111    }
112
113    return TRUE;
114}
115
116/* FIXME: max range stuff */
117double float_tests[] = { 0, 5, 0.1, 0.01, 5.2342, 10.2301,
118                         -1, -2.00, -0.6023, -1203.30
119                        };
120
121#pragma GCC diagnostic push
122#pragma GCC diagnostic ignored "-Woverflow"
123
124static void
125number_formatting(void)
126{
127    int i;
128    long unsigned int unsigned_tests[] = { 0,/* Zero */
129                                           5, /* Single digit number */
130                                           12, /* Two digit decimal number */
131                                           37, /* Two digit hex number */
132                                           0xC90B2, /* Large < 32 bit number */
133                                           0x15D027BF211B37A, /* Large > 32 bit number */
134                                           0xFFFFFFFFFFFFFFFF, /* Maximum 64-bit number */
135    };
136
137    long int signed_tests[] = { 0,/* Zero */
138                                5, /* Single digit number */
139                                12, /* Two digit decimal number */
140                                37, /* Two digit hex number */
141                                0xC90B2, /* Large < 32 bit number */
142                                0x15D027BF211B37A, /* Large > 32 bit number */
143                                0x7FFFFFFFFFFFFFFF, /* Maximum 64-bit signed number */
144                                -1, /* Single digit number */
145                                -12, /* Two digit decimal number */
146                                -0xC90B2, /* Large < 32 bit number */
147                                -0x15D027BF211B37A, /* Large > 32 bit number */
148                                -0x7FFFFFFFFFFFFFFF, /* Maximum 64-bit signed number */
149    } ;
150
151    for (i = 0; i < ARRAY_SIZE(unsigned_tests); i++)
152        assert(check_number_format_test(unsigned_tests[i]));
153
154    for (i = 0; i < ARRAY_SIZE(signed_tests); i++)
155        assert(check_signed_number_format_test(signed_tests[i]));
156
157    for (i = 0; i < ARRAY_SIZE(float_tests); i++)
158        assert(check_float_format_test(float_tests[i]));
159}
160#pragma GCC diagnostic pop
161
162#pragma GCC diagnostic push
163#pragma GCC diagnostic ignored "-Wformat-security"
164#pragma GCC diagnostic ignored "-Wformat"
165#pragma GCC diagnostic ignored "-Wformat-extra-args"
166static void logging_format(void)
167{
168    const char *log_file_path = "/tmp/Xorg-logging-test.log";
169    const char *str = "%s %d %u %% %p %i";
170    char buf[1024];
171    int i;
172    unsigned int ui;
173    long li;
174    unsigned long lui;
175    FILE *f;
176    char read_buf[2048];
177    char *logmsg;
178    uintptr_t ptr;
179
180    /* set up buf to contain ".....end" */
181    memset(buf, '.', sizeof(buf));
182    strcpy(&buf[sizeof(buf) - 4], "end");
183
184    LogInit(log_file_path, NULL);
185    assert((f = fopen(log_file_path, "r")));
186
187#define read_log_msg(msg) do {                                  \
188        msg = fgets(read_buf, sizeof(read_buf), f);             \
189        assert(msg != NULL);                                   \
190        msg = strchr(read_buf, ']');                            \
191        assert(msg != NULL);                                    \
192        assert(strlen(msg) > 2);                                \
193        msg = msg + 2; /* advance past [time.stamp] */          \
194    } while (0)
195
196    /* boring test message */
197    LogMessageVerbSigSafe(X_ERROR, -1, "test message\n");
198    read_log_msg(logmsg);
199    assert(strcmp(logmsg, "(EE) test message\n") == 0);
200
201    /* long buf is truncated to "....en\n" */
202    LogMessageVerbSigSafe(X_ERROR, -1, buf);
203    read_log_msg(logmsg);
204    assert(strcmp(&logmsg[strlen(logmsg) - 3], "en\n") == 0);
205
206    /* same thing, this time as string substitution */
207    LogMessageVerbSigSafe(X_ERROR, -1, "%s", buf);
208    read_log_msg(logmsg);
209    assert(strcmp(&logmsg[strlen(logmsg) - 3], "en\n") == 0);
210
211    /* strings containing placeholders should just work */
212    LogMessageVerbSigSafe(X_ERROR, -1, "%s\n", str);
213    read_log_msg(logmsg);
214    assert(strcmp(logmsg, "(EE) %s %d %u %% %p %i\n") == 0);
215
216    /* literal % */
217    LogMessageVerbSigSafe(X_ERROR, -1, "test %%\n");
218    read_log_msg(logmsg);
219    assert(strcmp(logmsg, "(EE) test %\n") == 0);
220
221    /* character */
222    LogMessageVerbSigSafe(X_ERROR, -1, "test %c\n", 'a');
223    read_log_msg(logmsg);
224    assert(strcmp(logmsg, "(EE) test a\n") == 0);
225
226    /* something unsupported % */
227    LogMessageVerbSigSafe(X_ERROR, -1, "test %Q\n");
228    read_log_msg(logmsg);
229    assert(strstr(logmsg, "BUG") != NULL);
230    LogMessageVerbSigSafe(X_ERROR, -1, "\n");
231    fseek(f, 0, SEEK_END);
232
233    /* string substitution */
234    LogMessageVerbSigSafe(X_ERROR, -1, "%s\n", "substituted string");
235    read_log_msg(logmsg);
236    assert(strcmp(logmsg, "(EE) substituted string\n") == 0);
237
238    /* Invalid format */
239    LogMessageVerbSigSafe(X_ERROR, -1, "%4", 4);
240    read_log_msg(logmsg);
241    assert(strcmp(logmsg, "(EE) ") == 0);
242    LogMessageVerbSigSafe(X_ERROR, -1, "\n");
243    fseek(f, 0, SEEK_END);
244
245    /* %hld is bogus */
246    LogMessageVerbSigSafe(X_ERROR, -1, "%hld\n", 4);
247    read_log_msg(logmsg);
248    assert(strstr(logmsg, "BUG") != NULL);
249    LogMessageVerbSigSafe(X_ERROR, -1, "\n");
250    fseek(f, 0, SEEK_END);
251
252    /* number substitution */
253    ui = 0;
254    do {
255        char expected[30];
256        sprintf(expected, "(EE) %u\n", ui);
257        LogMessageVerbSigSafe(X_ERROR, -1, "%u\n", ui);
258        read_log_msg(logmsg);
259        assert(strcmp(logmsg, expected) == 0);
260
261        sprintf(expected, "(EE) %x\n", ui);
262        LogMessageVerbSigSafe(X_ERROR, -1, "%x\n", ui);
263        read_log_msg(logmsg);
264        assert(strcmp(logmsg, expected) == 0);
265
266        if (ui == 0)
267            ui = 1;
268        else
269            ui <<= 1;
270    } while(ui);
271
272    lui = 0;
273    do {
274        char expected[30];
275        sprintf(expected, "(EE) %lu\n", lui);
276        LogMessageVerbSigSafe(X_ERROR, -1, "%lu\n", lui);
277        read_log_msg(logmsg);
278
279        sprintf(expected, "(EE) %lld\n", (unsigned long long)ui);
280        LogMessageVerbSigSafe(X_ERROR, -1, "%lld\n", (unsigned long long)ui);
281        read_log_msg(logmsg);
282        assert(strcmp(logmsg, expected) == 0);
283
284        sprintf(expected, "(EE) %lx\n", lui);
285        printf("%s\n", expected);
286        LogMessageVerbSigSafe(X_ERROR, -1, "%lx\n", lui);
287        read_log_msg(logmsg);
288        assert(strcmp(logmsg, expected) == 0);
289
290        sprintf(expected, "(EE) %llx\n", (unsigned long long)ui);
291        LogMessageVerbSigSafe(X_ERROR, -1, "%llx\n", (unsigned long long)ui);
292        read_log_msg(logmsg);
293        assert(strcmp(logmsg, expected) == 0);
294
295        if (lui == 0)
296            lui = 1;
297        else
298            lui <<= 1;
299    } while(lui);
300
301    /* signed number substitution */
302    i = 0;
303    do {
304        char expected[30];
305        sprintf(expected, "(EE) %d\n", i);
306        LogMessageVerbSigSafe(X_ERROR, -1, "%d\n", i);
307        read_log_msg(logmsg);
308        assert(strcmp(logmsg, expected) == 0);
309
310        sprintf(expected, "(EE) %d\n", i | INT_MIN);
311        LogMessageVerbSigSafe(X_ERROR, -1, "%d\n", i | INT_MIN);
312        read_log_msg(logmsg);
313        assert(strcmp(logmsg, expected) == 0);
314
315        if (i == 0)
316            i = 1;
317        else
318            i <<= 1;
319    } while(i > INT_MIN);
320
321    li = 0;
322    do {
323        char expected[30];
324        sprintf(expected, "(EE) %ld\n", li);
325        LogMessageVerbSigSafe(X_ERROR, -1, "%ld\n", li);
326        read_log_msg(logmsg);
327        assert(strcmp(logmsg, expected) == 0);
328
329        sprintf(expected, "(EE) %ld\n", li | LONG_MIN);
330        LogMessageVerbSigSafe(X_ERROR, -1, "%ld\n", li | LONG_MIN);
331        read_log_msg(logmsg);
332        assert(strcmp(logmsg, expected) == 0);
333
334        sprintf(expected, "(EE) %lld\n", (long long)li);
335        LogMessageVerbSigSafe(X_ERROR, -1, "%lld\n", (long long)li);
336        read_log_msg(logmsg);
337        assert(strcmp(logmsg, expected) == 0);
338
339        sprintf(expected, "(EE) %lld\n", (long long)(li | LONG_MIN));
340        LogMessageVerbSigSafe(X_ERROR, -1, "%lld\n", (long long)(li | LONG_MIN));
341        read_log_msg(logmsg);
342        assert(strcmp(logmsg, expected) == 0);
343
344        if (li == 0)
345            li = 1;
346        else
347            li <<= 1;
348    } while(li > LONG_MIN);
349
350
351    /* pointer substitution */
352    /* we print a null-pointer differently to printf */
353    LogMessageVerbSigSafe(X_ERROR, -1, "%p\n", NULL);
354    read_log_msg(logmsg);
355    assert(strcmp(logmsg, "(EE) 0x0\n") == 0);
356
357    ptr = 1;
358    do {
359        char expected[30];
360#ifdef __sun /* Solaris doesn't autoadd "0x" to %p format */
361        sprintf(expected, "(EE) 0x%p\n", (void*)ptr);
362#else
363        sprintf(expected, "(EE) %p\n", (void*)ptr);
364#endif
365        LogMessageVerbSigSafe(X_ERROR, -1, "%p\n", (void*)ptr);
366        read_log_msg(logmsg);
367        assert(strcmp(logmsg, expected) == 0);
368        ptr <<= 1;
369    } while(ptr);
370
371
372    for (i = 0; i < ARRAY_SIZE(float_tests); i++) {
373        double d = float_tests[i];
374        char expected[30];
375        sprintf(expected, "(EE) %.2f\n", d);
376        LogMessageVerbSigSafe(X_ERROR, -1, "%f\n", d);
377        read_log_msg(logmsg);
378        assert(strcmp(logmsg, expected) == 0);
379
380        /* test for length modifiers, we just ignore them atm */
381        LogMessageVerbSigSafe(X_ERROR, -1, "%.3f\n", d);
382        read_log_msg(logmsg);
383        assert(strcmp(logmsg, expected) == 0);
384
385        LogMessageVerbSigSafe(X_ERROR, -1, "%3f\n", d);
386        read_log_msg(logmsg);
387        assert(strcmp(logmsg, expected) == 0);
388
389        LogMessageVerbSigSafe(X_ERROR, -1, "%.0f\n", d);
390        read_log_msg(logmsg);
391        assert(strcmp(logmsg, expected) == 0);
392    }
393
394
395    LogClose(EXIT_NO_ERROR);
396    unlink(log_file_path);
397
398#undef read_log_msg
399}
400#pragma GCC diagnostic pop /* "-Wformat-security" */
401
402int
403signal_logging_test(void)
404{
405    number_formatting();
406    logging_format();
407
408    return 0;
409}
410