]> git.draconx.ca Git - cdecl99.git/blob - t/cdeclerr.c
libcdecl: Remove error code argument from cdecl__err.
[cdecl99.git] / t / cdeclerr.c
1 /*
2  * Helper application to test internal library error reporting.
3  * Copyright © 2023-2024 Nick Bowler
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
18
19 #include <config.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <assert.h>
23 #include <ctype.h>
24
25 #include "cdecl-internal.h"
26 #include "cdecl.h"
27 #include "errmsg.h"
28 #include "tap.h"
29
30 const char *cdecl__token_name(unsigned token)
31 {
32         assert(0);
33 }
34
35 static char *fmt_char(int c, char *buf)
36 {
37         int escape = 0;
38
39         switch (c) {
40         case '\a': escape = 'a';  break;
41         case '\b': escape = 'b';  break;
42         case '\t': escape = 't';  break;
43         case '\n': escape = 'n';  break;
44         case '\v': escape = 'v';  break;
45         case '\f': escape = 'f';  break;
46         case '\r': escape = 'r';  break;
47         case '\\': escape = '\\'; break;
48         case '\'': escape = '\''; break;
49         }
50
51         if (escape)
52                 sprintf(buf, "'\\%c'", escape);
53         else if (isprint((unsigned char)c))
54                 sprintf(buf, "'%c'", c);
55         else
56                 sprintf(buf, "'\\%.3o'", c & 0777u);
57
58         return buf;
59 }
60
61 static void check_code(const struct cdecl_error *err, unsigned expect)
62 {
63         if (!tap_result(err->code == expect, "returned error code")) {
64                 tap_diag("Failed, unexpected result");
65                 tap_diag("   Received: %u", err->code);
66                 tap_diag("   Expected: %u", expect);
67         }
68 }
69
70 static void check_fixed_string(size_t len)
71 {
72         const struct cdecl_error *err;
73         char *work1, *work2;
74         size_t errlen;
75
76         assert(len > 0);
77         work1 = calloc(2, len);
78         if (!work1)
79                 abort();
80
81         work2 = work1 + len;
82         memset(work1, 'X', len - 1);
83         memset(work2, 'X', len - 1);
84
85         tap_diag("cdecl__err w/ %lu-byte string", (unsigned long)len);
86         cdecl__err(work1, "XX");
87         memset(work1, 0, len);
88         err = cdecl_get_error();
89
90         check_code(err, CDECL_ENOPARSE);
91
92         errlen = strlen(err->str);
93         if (!tap_result(errlen == len-1, "returned string length")) {
94                 tap_diag("Failed, unexpected result");
95                 tap_diag("   Received: %lu", (unsigned long)errlen);
96                 tap_diag("   Expected: %lu", (unsigned long)len);
97         }
98
99         if (!tap_result(!memcmp(err->str, work2, len), "returned string")) {
100                 char buf[8];
101                 size_t n;
102
103                 n = strspn(err->str, "X");
104                 if (n >= len)
105                         n = len;
106
107                 tap_diag("Failed, first incorrect character at offset %lu",
108                          (unsigned long)n);
109                 tap_diag("   Received: %s", fmt_char(err->str[n], buf));
110                 tap_diag("   Expected: %s", fmt_char(work2[n], buf));
111         }
112
113         free(work1);
114 }
115
116 static void check_format_string(const char *fmt, const char *arg)
117 {
118         size_t sz = strlen(fmt) + strlen(arg);
119         const struct cdecl_error *err;
120         char *work;
121
122         work = malloc(sz + 1);
123         if (!work)
124                 abort();
125         sprintf(work, fmt, arg);
126
127         cdecl__err(fmt, arg);
128         err = cdecl_get_error();
129
130         tap_diag("cdecl__err(\"%s\", \"%s\")", fmt, arg);
131         check_code(err, CDECL_ENOPARSE);
132
133         if (!tap_result(!strcmp(err->str, work), "returned string")) {
134                 tap_diag("Failed, unexpected result");
135                 tap_diag("   Received: %.*s", (int)sz, err->str);
136                 tap_diag("   Expected: %s", work);
137         }
138
139         free(work);
140 }
141
142 static void check_enomem()
143 {
144         const char expmsg[] = "failed to allocate memory";
145         const struct cdecl_error *err;
146
147         tap_diag("cdecl__errmsg(CDECL__ENOMEM)");
148         cdecl__errmsg(CDECL__ENOMEM);
149         err = cdecl_get_error();
150
151         check_code(err, CDECL_ENOMEM);
152         if (!tap_result(!strcmp(err->str, "failed to allocate memory"),
153                         "returned string"))
154         {
155                 unsigned i;
156
157                 for (i = 0; i < sizeof expmsg; i++) {
158                         if (err->str[i] != expmsg[i])
159                                 break;
160                 }
161
162                 tap_diag("Failed, first incorrect character at offset %u", i);
163                 tap_diag("   Received: %.*s", (int)sizeof expmsg, err->str);
164                 tap_diag("   Expected: %s", expmsg);
165         }
166 }
167
168 #define IFNLS(a, b) (ENABLE_NLS ? (a) : (b))
169
170 int main(void)
171 {
172         tap_plan(3*3 + 2 + 2);
173
174         check_fixed_string(50);
175         check_fixed_string(500);
176         check_fixed_string(5000);
177
178         /*
179          * When NLS is disabled, for implementation simplicity only format
180          * strings ending with %s are supported as this is sufficient.
181          * Otherwise, %s may appear anywhere.
182          */
183         check_format_string(IFNLS("hello %s world", "hello world %s"), "za");
184
185         check_enomem();
186
187         tap_done();
188 }