/src/libcups/cups/test-internal.h
Line | Count | Source (jump to first uncovered line) |
1 | | // |
2 | | // Unit test header for C/C++ programs. |
3 | | // |
4 | | // Copyright © 2021-2022 by Michael R Sweet. |
5 | | // |
6 | | // Redistribution and use in source and binary forms, with or without |
7 | | // modification, are permitted provided that the following conditions are met: |
8 | | // |
9 | | // 1. Redistributions of source code must retain the above copyright notice, |
10 | | // this list of conditions and the following disclaimer. |
11 | | // 2. Redistributions in binary form must reproduce the above copyright notice, |
12 | | // this list of conditions and the following disclaimer in the documentation |
13 | | // and/or other materials provided with the distribution. |
14 | | // |
15 | | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
16 | | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
19 | | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
25 | | // POSSIBILITY OF SUCH DAMAGE. |
26 | | // |
27 | | |
28 | | #ifndef TEST_H |
29 | | # define TEST_H |
30 | | # include <stdio.h> |
31 | | # include <stdlib.h> |
32 | | # include <stdarg.h> |
33 | | # include <stdbool.h> |
34 | | # include <string.h> |
35 | | # if _WIN32 |
36 | | # define isatty(f) _isatty(f) |
37 | | # else |
38 | | # include <unistd.h> |
39 | | # endif // !_WIN32 |
40 | | # ifdef __cplusplus |
41 | | extern "C" { |
42 | | # endif // __cplusplus |
43 | | |
44 | | |
45 | | // |
46 | | // This header implements a simple unit test framework for C/C++ programs. |
47 | | // Inline functions are provided to write a test summary to stdout and the |
48 | | // details to stderr. This allows unit test programs to output a summary to |
49 | | // stdout with details sent to stderr, e.g.: |
50 | | // |
51 | | // mytestprogram 2>test.log |
52 | | // |
53 | | // Documentation: |
54 | | // |
55 | | // void testBegin(const char *title, ...) |
56 | | // |
57 | | // Start a test with a printf-style title message. "Title:" (the formatted |
58 | | // title followed by a colon) is output. |
59 | | // |
60 | | // void testEnd(bool pass) |
61 | | // |
62 | | // End a test without an additional message. "pass" should be `true` if the |
63 | | // test passed and `false` otherwise. "PASS" or "FAIL" is output. |
64 | | // |
65 | | // void testEndMessage(bool pass, const char *message, ...) |
66 | | // |
67 | | // End a test with an additional printf-style message. "pass" should be |
68 | | // `true` if the test passed and `false` otherwise. "PASS (message)" or |
69 | | // "FAIL (message)" is output. |
70 | | // |
71 | | // testError(const char *error, ...) |
72 | | // |
73 | | // Sends a formatted error string to stderr. |
74 | | // |
75 | | // testHexDump(const unsigned char *buffer, size_t bytes) |
76 | | // |
77 | | // Sends a hex dump of the specified buffer to stderr. |
78 | | // |
79 | | // testMessage(const char *error, ...) |
80 | | // |
81 | | // Outputs a formatted message string. |
82 | | // |
83 | | // testProgress(void) |
84 | | // |
85 | | // Shows a progress spinner for long-running tests. |
86 | | // |
87 | | // bool testsPassed |
88 | | // |
89 | | // This global variable specifies whether all tests have passed (`true`) |
90 | | // or one or more have failed (`false`). |
91 | | // |
92 | | |
93 | | static bool testsPassed = true; // All tests passed? |
94 | | static int test_progress; // Current progress |
95 | | static char test_title[1024] = ""; // Current test title |
96 | | |
97 | | |
98 | | // Start a test |
99 | | static inline void |
100 | | testBegin(const char *title, ...) // I - printf-style title string |
101 | 0 | { |
102 | 0 | va_list ap; // Pointer to additional arguments |
103 | 0 |
|
104 | 0 |
|
105 | 0 | // Format the title string |
106 | 0 | va_start(ap, title); |
107 | 0 | vsnprintf(test_title, sizeof(test_title), title, ap); |
108 | 0 | va_end(ap); |
109 | 0 |
|
110 | 0 | // Send the title to stdout and stderr... |
111 | 0 | test_progress = 0; |
112 | 0 |
|
113 | 0 | printf("%s: ", test_title); |
114 | 0 | fflush(stdout); |
115 | 0 |
|
116 | 0 | if (!isatty(2)) |
117 | 0 | fprintf(stderr, "%s: ", test_title); |
118 | 0 | } |
119 | | |
120 | | |
121 | | // End a test with no additional information |
122 | | static inline void |
123 | | testEnd(bool pass) // I - `true` if the test passed, `false` otherwise |
124 | 0 | { |
125 | 0 | // Send the test result to stdout and stderr |
126 | 0 | if (test_progress) |
127 | 0 | putchar('\b'); |
128 | 0 |
|
129 | 0 | if (!pass) |
130 | 0 | testsPassed = false; |
131 | 0 |
|
132 | 0 | puts(pass ? "PASS" : "FAIL"); |
133 | 0 | if (!isatty(2)) |
134 | 0 | fputs(pass ? "PASS\n" : "FAIL\n", stderr); |
135 | 0 |
|
136 | 0 | test_title[0] = '\0'; |
137 | 0 | } |
138 | | |
139 | | |
140 | | // End a test with no additional information |
141 | | static inline void |
142 | | testEndMessage(bool pass, // I - `true` if the test passed, `false` otherwise |
143 | | const char *message, ...)// I - printf-style message |
144 | 0 | { |
145 | 0 | char buffer[1024]; // Formatted title string |
146 | 0 | va_list ap; // Pointer to additional arguments |
147 | 0 |
|
148 | 0 |
|
149 | 0 | // Format the title string |
150 | 0 | va_start(ap, message); |
151 | 0 | vsnprintf(buffer, sizeof(buffer), message, ap); |
152 | 0 | va_end(ap); |
153 | 0 |
|
154 | 0 | // Send the test result to stdout and stderr |
155 | 0 | if (test_progress) |
156 | 0 | putchar('\b'); |
157 | 0 |
|
158 | 0 | printf(pass ? "PASS (%s)\n" : "FAIL (%s)\n", buffer); |
159 | 0 | if (!isatty(2)) |
160 | 0 | fprintf(stderr, pass ? "PASS (%s)\n" : "FAIL (%s)\n", buffer); |
161 | 0 |
|
162 | 0 | test_title[0] = '\0'; |
163 | 0 | } |
164 | | |
165 | | |
166 | | // Show/update a progress spinner |
167 | | static inline void |
168 | | testProgress(void) |
169 | 0 | { |
170 | 0 | if (test_progress) |
171 | 0 | putchar('\b'); |
172 | 0 | putchar("-\\|/"[test_progress & 3]); |
173 | 0 | fflush(stdout); |
174 | 0 |
|
175 | 0 | test_progress ++; |
176 | 0 | } |
177 | | |
178 | | |
179 | | // Show an error to stderr... |
180 | | static inline void |
181 | | testError(const char *error, ...) // I - printf-style error string |
182 | 0 | { |
183 | 0 | char buffer[1024]; // Formatted title string |
184 | 0 | va_list ap; // Pointer to additional arguments |
185 | 0 |
|
186 | 0 |
|
187 | 0 | // Format the error string |
188 | 0 | va_start(ap, error); |
189 | 0 | vsnprintf(buffer, sizeof(buffer), error, ap); |
190 | 0 | va_end(ap); |
191 | 0 |
|
192 | 0 | // Send the error to stderr... |
193 | 0 | fprintf(stderr, "%s\n", buffer); |
194 | 0 |
|
195 | 0 | if (test_title[0]) |
196 | 0 | fprintf(stderr, "%s: ", test_title); |
197 | 0 | } |
198 | | |
199 | | |
200 | | // Show a message to stdout and stderr... |
201 | | static inline void |
202 | | testMessage(const char *error, ...) // I - printf-style error string |
203 | 0 | { |
204 | 0 | char buffer[1024]; // Formatted title string |
205 | 0 | va_list ap; // Pointer to additional arguments |
206 | 0 |
|
207 | 0 |
|
208 | 0 | // Format the error string |
209 | 0 | va_start(ap, error); |
210 | 0 | vsnprintf(buffer, sizeof(buffer), error, ap); |
211 | 0 | va_end(ap); |
212 | 0 |
|
213 | 0 | // Send the message to stdout and stderr too if needed... |
214 | 0 | printf("%s\n", buffer); |
215 | 0 | if (test_title[0]) |
216 | 0 | { |
217 | 0 | printf("%s: ", test_title); |
218 | 0 | fflush(stdout); |
219 | 0 | } |
220 | 0 |
|
221 | 0 | if (!isatty(2)) |
222 | 0 | { |
223 | 0 | fprintf(stderr, "%s\n", buffer); |
224 | 0 |
|
225 | 0 | if (test_title[0]) |
226 | 0 | fprintf(stderr, "%s: ", test_title); |
227 | 0 | } |
228 | 0 | } |
229 | | |
230 | | |
231 | | // Show a hex dump of a buffer to stderr... |
232 | | static inline void |
233 | | testHexDump(const unsigned char *buffer,// I - Buffer |
234 | | size_t bytes) // I - Number of bytes |
235 | 0 | { |
236 | 0 | size_t i, j; // Looping vars |
237 | 0 | int ch; // Current ASCII char |
238 | 0 |
|
239 | 0 |
|
240 | 0 | if (test_title[0]) |
241 | 0 | fputs("\n", stderr); |
242 | 0 |
|
243 | 0 | // Show lines of 16 bytes at a time... |
244 | 0 | for (i = 0; i < bytes; i += 16) |
245 | 0 | { |
246 | 0 | // Show the offset... |
247 | 0 | fprintf(stderr, "%04x ", (unsigned)i); |
248 | 0 |
|
249 | 0 | // Then up to 16 bytes in hex... |
250 | 0 | for (j = 0; j < 16; j ++) |
251 | 0 | { |
252 | 0 | if ((i + j) < bytes) |
253 | 0 | fprintf(stderr, " %02x", buffer[i + j]); |
254 | 0 | else |
255 | 0 | fputs(" ", stderr); |
256 | 0 | } |
257 | 0 |
|
258 | 0 | // Then the ASCII representation of the bytes... |
259 | 0 | fputs(" ", stderr); |
260 | 0 |
|
261 | 0 | for (j = 0; j < 16 && (i + j) < bytes; j ++) |
262 | 0 | { |
263 | 0 | ch = buffer[i + j] & 127; |
264 | 0 |
|
265 | 0 | if (ch < ' ' || ch == 127) |
266 | 0 | fputc('.', stderr); |
267 | 0 | else |
268 | 0 | fputc(ch, stderr); |
269 | 0 | } |
270 | 0 |
|
271 | 0 | fputc('\n', stderr); |
272 | 0 | } |
273 | 0 |
|
274 | 0 | if (test_title[0]) |
275 | 0 | fprintf(stderr, "%s: ", test_title); |
276 | 0 | } |
277 | | |
278 | | # ifdef __cplusplus |
279 | | } |
280 | | # endif // __cplusplus |
281 | | #endif // !TEST_H |