1 /***********************************************************************************************************************************
2 C Test Harness
3 ***********************************************************************************************************************************/
4 #ifndef TEST_COMMON_HARNESS_H
5 #define TEST_COMMON_HARNESS_H
6 
7 #include <inttypes.h>
8 
9 #include "common/debug.h"
10 #include "common/error.h"
11 
12 #include "common/harnessTest.intern.h"
13 
14 /***********************************************************************************************************************************
15 Constants
16 ***********************************************************************************************************************************/
17 #define BOGUS_STR                                                   "BOGUS"
18 
19 /***********************************************************************************************************************************
20 Functions
21 ***********************************************************************************************************************************/
22 // Begin a test if this function returns true, otherwise the user has skipped it
23 bool testBegin(const char *name);
24 
25 // Read a file (max 256k) into a buffer
26 void hrnFileRead(const char *fileName, unsigned char *buffer, size_t bufferSize);
27 
28 // Write a buffer to a file
29 void hrnFileWrite(const char *fileName, const unsigned char *buffer, size_t bufferSize);
30 
31 // Diff two strings using command-line diff tool
32 const char *hrnDiff(const char *actual, const char *expected);
33 
34 /***********************************************************************************************************************************
35 Getters/Setters
36 ***********************************************************************************************************************************/
37 // Time in MS
38 uint64_t testTimeMSec(void);
39 
40 // Time in MS at the beginning of the test run (since testBegin() was called)
41 uint64_t testTimeMSecBegin(void);
42 
43 // The path and name of the test executable
44 const char *testExe(void);
45 
46 // Is this test running in a container?
47 bool testContainer(void);
48 
49 // Get the 0-based index of the test.  Useful for modifying resources like port numbers to avoid conflicts when running tests in
50 // parallel.
51 unsigned int testIdx(void);
52 
53 /***********************************************************************************************************************************
54 Test that an expected error is actually thrown and error when it isn't
55 ***********************************************************************************************************************************/
56 #define TEST_ERROR(statement, errorTypeExpected, errorMessageExpected)                                                             \
57 {                                                                                                                                  \
58     bool TEST_ERROR_catch = false;                                                                                                 \
59                                                                                                                                    \
60     /* Set the line number for the current function in the stack trace */                                                          \
61     FUNCTION_HARNESS_STACK_TRACE_LINE_SET(__LINE__);                                                                               \
62                                                                                                                                    \
63     hrnTestLogPrefix(__LINE__);                                                                                                    \
64     printf("expect %s: %s\n", errorTypeName(&errorTypeExpected), errorMessageExpected);                                            \
65     fflush(stdout);                                                                                                                \
66                                                                                                                                    \
67     TRY_BEGIN()                                                                                                                    \
68     {                                                                                                                              \
69         statement;                                                                                                                 \
70     }                                                                                                                              \
71     CATCH_ANY()                                                                                                                    \
72     {                                                                                                                              \
73         TEST_ERROR_catch = true;                                                                                                   \
74                                                                                                                                    \
75         if (strcmp(errorMessage(), errorMessageExpected) != 0 || errorType() != &errorTypeExpected)                                \
76             THROW_FMT(                                                                                                             \
77                 TestError, "EXPECTED %s: %s\n\n BUT GOT %s: %s\n\nTHROWN AT:\n%s", errorTypeName(&errorTypeExpected),              \
78                 errorMessageExpected, errorName(), errorMessage(), errorStackTrace());                                             \
79     }                                                                                                                              \
80     TRY_END();                                                                                                                     \
81                                                                                                                                    \
82     if (!TEST_ERROR_catch)                                                                                                         \
83         THROW_FMT(                                                                                                                 \
84             TestError, "statement '%s' returned but error %s, '%s' was expected", #statement, errorTypeName(&errorTypeExpected),   \
85             errorMessageExpected);                                                                                                 \
86                                                                                                                                    \
87     FUNCTION_HARNESS_STACK_TRACE_LINE_SET(0);                                                                                      \
88 }
89 
90 /***********************************************************************************************************************************
91 Test error with a formatted expected message
92 ***********************************************************************************************************************************/
93 #define TEST_ERROR_FMT(statement, errorTypeExpected, ...)                                                                          \
94 {                                                                                                                                  \
95     char TEST_ERROR_FMT_buffer[8192];                                                                                              \
96                                                                                                                                    \
97     if (snprintf(TEST_ERROR_FMT_buffer, sizeof(TEST_ERROR_FMT_buffer), __VA_ARGS__) >= (int)sizeof(TEST_ERROR_FMT_buffer))         \
98         THROW_FMT(AssertError, "error message needs more than the %zu characters available", sizeof(TEST_ERROR_FMT_buffer));       \
99                                                                                                                                    \
100     TEST_ERROR(statement, errorTypeExpected, TEST_ERROR_FMT_buffer);                                                               \
101 }
102 
103 /***********************************************************************************************************************************
104 Output information about the test
105 ***********************************************************************************************************************************/
106 #define TEST_RESULT_INFO(comment)                                                                                                  \
107     hrnTestLogPrefix(__LINE__);                                                                                                    \
108     puts(comment);                                                                                                                 \
109     fflush(stdout);
110 
111 /***********************************************************************************************************************************
112 Test that a void statement returns and does not throw an error
113 ***********************************************************************************************************************************/
114 #define TEST_RESULT_VOID(statement, comment)                                                                                       \
115 {                                                                                                                                  \
116     TEST_RESULT_INFO(comment);                                                                                                     \
117     hrnTestResultBegin(#statement, false);                                                                                         \
118     statement;                                                                                                                     \
119     hrnTestResultEnd();                                                                                                            \
120 }
121 
122 /***********************************************************************************************************************************
123 Test that a statement does not error and assign it to the specified variable if not
124 ***********************************************************************************************************************************/
125 #define TEST_ASSIGN(lValue, statement, comment)                                                                                    \
126 {                                                                                                                                  \
127     TEST_RESULT_INFO(comment);                                                                                                     \
128     hrnTestResultBegin(#statement, true);                                                                                          \
129     lValue = statement;                                                                                                            \
130     hrnTestResultEnd();                                                                                                            \
131 }
132 
133 /***********************************************************************************************************************************
134 Macros to compare results of common data types
135 ***********************************************************************************************************************************/
136 #define TEST_RESULT_BOOL_PARAM(statement, expected, comment)                                                                       \
137     do                                                                                                                             \
138     {                                                                                                                              \
139         TEST_RESULT_INFO(comment);                                                                                                 \
140         hrnTestResultBegin(#statement, true);                                                                                      \
141         hrnTestResultBool(statement, expected);                                                                                    \
142     }                                                                                                                              \
143     while (0)
144 
145 #define TEST_RESULT_BOOL(statement, expected, comment)                                                                             \
146     TEST_RESULT_BOOL_PARAM(statement, expected, comment)
147 
148 #define TEST_RESULT_DOUBLE_PARAM(statement, expected, comment)                                                                     \
149     do                                                                                                                             \
150     {                                                                                                                              \
151         TEST_RESULT_INFO(comment);                                                                                                 \
152         hrnTestResultBegin(#statement, true);                                                                                      \
153         hrnTestResultDouble(statement, expected);                                                                                  \
154     }                                                                                                                              \
155     while (0)
156 
157 #define TEST_RESULT_DOUBLE(statement, expected, comment)                                                                           \
158     TEST_RESULT_DOUBLE_PARAM(statement, expected, comment)
159 
160 #define TEST_RESULT_INT_PARAM(statement, expected, operation, comment)                                                             \
161     do                                                                                                                             \
162     {                                                                                                                              \
163         TEST_RESULT_INFO(comment);                                                                                                 \
164         hrnTestResultBegin(#statement, true);                                                                                      \
165         hrnTestResultInt64(statement, expected, operation);                                                                        \
166     }                                                                                                                              \
167     while (0)
168 
169 #define TEST_RESULT_INT(statement, expected, comment)                                                                              \
170     TEST_RESULT_INT_PARAM(statement, expected, harnessTestResultOperationEq, comment)
171 #define TEST_RESULT_INT_NE(statement, expected, comment)                                                                           \
172     TEST_RESULT_INT_PARAM(statement, expected, harnessTestResultOperationNe, comment)
173 
174 #define TEST_RESULT_PTR_PARAM(statement, expected, operation, comment)                                                             \
175     do                                                                                                                             \
176     {                                                                                                                              \
177         TEST_RESULT_INFO(comment);                                                                                                 \
178         hrnTestResultBegin(#statement, true);                                                                                      \
179         hrnTestResultPtr(statement, expected, operation);                                                                          \
180     }                                                                                                                              \
181     while (0)
182 
183 // Compare raw pointers. When checking for NULL use the type-specific macro when available, e.g. TEST_RESULT_STR(). This is more
184 // type-safe and makes it clearer what is being tested.
185 #define TEST_RESULT_PTR(statement, expected, comment)                                                                              \
186     TEST_RESULT_PTR_PARAM(statement, expected, harnessTestResultOperationEq, comment)
187 #define TEST_RESULT_PTR_NE(statement, expected, comment)                                                                           \
188     TEST_RESULT_PTR_PARAM(statement, expected, harnessTestResultOperationNe, comment)
189 
190 #define TEST_RESULT_Z_PARAM(statement, expected, operation, comment)                                                               \
191     do                                                                                                                             \
192     {                                                                                                                              \
193         TEST_RESULT_INFO(comment);                                                                                                 \
194         hrnTestResultBegin(#statement, true);                                                                                      \
195         hrnTestResultZ(statement, expected, operation);                                                                            \
196     }                                                                                                                              \
197     while (0)
198 
199 #define TEST_RESULT_Z(statement, expected, comment)                                                                                \
200     TEST_RESULT_Z_PARAM(statement, expected, harnessTestResultOperationEq, comment)
201 #define TEST_RESULT_Z_NE(statement, expected, comment)                                                                             \
202     TEST_RESULT_Z_PARAM(statement, expected, harnessTestResultOperationNe, comment)
203 
204 #define TEST_RESULT_STR(statement, resultExpected, comment)                                                                        \
205     TEST_RESULT_Z(strZNull(statement), strZNull(resultExpected), comment)
206 #define TEST_RESULT_STR_Z(statement, resultExpected, comment)                                                                      \
207     TEST_RESULT_Z(strZNull(statement), resultExpected, comment)
208 #define TEST_RESULT_Z_STR(statement, resultExpected, comment)                                                                      \
209     TEST_RESULT_Z(statement, strZNull(resultExpected), comment)
210 
211 // Compare a string list to a \n delimited string
212 #define TEST_RESULT_STRLST_Z(statement, resultExpected, comment)                                                                   \
213     do                                                                                                                             \
214     {                                                                                                                              \
215         TEST_RESULT_INFO(comment);                                                                                                 \
216         hrnTestResultBegin(#statement, true);                                                                                      \
217         hrnTestResultStringList(statement, resultExpected, harnessTestResultOperationEq);                                          \
218     }                                                                                                                              \
219     while (0)
220 
221 #define TEST_RESULT_STRLST_STR(statement, resultExpected, comment)                                                                 \
222     TEST_RESULT_STRLST_Z(statement, strZNull(resultExpected), comment)
223 
224 #define TEST_RESULT_UINT_PARAM(statement, expected, operation, comment)                                                            \
225     do                                                                                                                             \
226     {                                                                                                                              \
227         TEST_RESULT_INFO(comment);                                                                                                 \
228         hrnTestResultBegin(#statement, true);                                                                                      \
229         hrnTestResultUInt64(statement, expected, operation);                                                                       \
230     }                                                                                                                              \
231     while (0)
232 
233 #define TEST_RESULT_UINT(statement, expected, comment)                                                                             \
234     TEST_RESULT_UINT_PARAM(statement, expected, harnessTestResultOperationEq, comment)
235 
236 #define TEST_RESULT_UINT_INT_PARAM(statement, expected, operation, comment)                                                        \
237     do                                                                                                                             \
238     {                                                                                                                              \
239         TEST_RESULT_INFO(comment);                                                                                                 \
240         hrnTestResultBegin(#statement, true);                                                                                      \
241         hrnTestResultUInt64Int64(statement, expected, operation);                                                                  \
242     }                                                                                                                              \
243     while (0)
244 
245 #define TEST_RESULT_UINT_INT(statement, expected, comment)                                                                         \
246     TEST_RESULT_UINT_INT_PARAM(statement, expected, harnessTestResultOperationEq, comment)
247 
248 /***********************************************************************************************************************************
249 System call harness
250 ***********************************************************************************************************************************/
251 #define HRN_SYSTEM(command)                                                                                                        \
252     do                                                                                                                             \
253     {                                                                                                                              \
254         int TEST_SYSTEM_FMT_result = system(command);                                                                              \
255                                                                                                                                    \
256         if (TEST_SYSTEM_FMT_result != 0)                                                                                           \
257         {                                                                                                                          \
258             THROW_FMT(                                                                                                             \
259                 AssertError, "SYSTEM COMMAND: %s\n\nFAILED WITH CODE %d\n\nTHROWN AT:\n%s", command, TEST_SYSTEM_FMT_result,       \
260                 errorStackTrace());                                                                                                \
261         }                                                                                                                          \
262     } while (0)
263 
264 #define HRN_SYSTEM_FMT(...)                                                                                                        \
265     do                                                                                                                             \
266     {                                                                                                                              \
267         char TEST_SYSTEM_FMT_buffer[8192];                                                                                         \
268                                                                                                                                    \
269         if (snprintf(TEST_SYSTEM_FMT_buffer, sizeof(TEST_SYSTEM_FMT_buffer), __VA_ARGS__) >= (int)sizeof(TEST_SYSTEM_FMT_buffer))  \
270             THROW_FMT(AssertError, "command needs more than the %zu characters available", sizeof(TEST_SYSTEM_FMT_buffer));        \
271                                                                                                                                    \
272         HRN_SYSTEM(TEST_SYSTEM_FMT_buffer);                                                                                        \
273     } while (0)
274 
275 /***********************************************************************************************************************************
276 Test log result
277 ***********************************************************************************************************************************/
278 #define TEST_RESULT_LOG(expected)                                                                                                  \
279     do                                                                                                                             \
280     {                                                                                                                              \
281         TRY_BEGIN()                                                                                                                \
282         {                                                                                                                          \
283             harnessLogResult(expected);                                                                                            \
284         }                                                                                                                          \
285         CATCH_ANY()                                                                                                                \
286         {                                                                                                                          \
287             THROW_FMT(AssertError, "LOG RESULT FAILED WITH:\n%s", errorMessage());                                                 \
288         }                                                                                                                          \
289         TRY_END();                                                                                                                 \
290     } while (0)
291 
292 #define TEST_RESULT_LOG_FMT(...)                                                                                                   \
293     do                                                                                                                             \
294     {                                                                                                                              \
295         char TEST_RESULT_LOG_FMT_buffer[65536];                                                                                    \
296                                                                                                                                    \
297         if (snprintf(TEST_RESULT_LOG_FMT_buffer, sizeof(TEST_RESULT_LOG_FMT_buffer), __VA_ARGS__) >=                               \
298             (int)sizeof(TEST_RESULT_LOG_FMT_buffer))                                                                               \
299         {                                                                                                                          \
300             THROW_FMT(                                                                                                             \
301                 AssertError, "expected result needs more than the %zu characters available", sizeof(TEST_RESULT_LOG_FMT_buffer));  \
302         }                                                                                                                          \
303                                                                                                                                    \
304         TEST_RESULT_LOG(TEST_RESULT_LOG_FMT_buffer);                                                                               \
305     } while (0)
306 
307 /***********************************************************************************************************************************
308 Logging macros
309 ***********************************************************************************************************************************/
310 #define TEST_LOG(message)                                                                                                          \
311     do                                                                                                                             \
312     {                                                                                                                              \
313         hrnTestLogPrefix(__LINE__);                                                                                                \
314         printf("%s\n", message);                                                                                                   \
315         fflush(stdout);                                                                                                            \
316     } while (0)
317 
318 #define TEST_LOG_FMT(format, ...)                                                                                                  \
319     do                                                                                                                             \
320     {                                                                                                                              \
321         hrnTestLogPrefix(__LINE__);                                                                                                \
322         printf(format "\n", __VA_ARGS__);                                                                                          \
323         fflush(stdout);                                                                                                            \
324     } while (0)
325 
326 /***********************************************************************************************************************************
327 Test title macro
328 ***********************************************************************************************************************************/
329 #define TEST_TITLE(message)                                                                                                        \
330     do                                                                                                                             \
331     {                                                                                                                              \
332         hrnTestLogTitle(__LINE__);                                                                                                 \
333         printf(" %s\n", message);                                                                                                  \
334         fflush(stdout);                                                                                                            \
335     } while (0)
336 
337 #define TEST_TITLE_FMT(format, ...)                                                                                                \
338     do                                                                                                                             \
339     {                                                                                                                              \
340         hrnTestLogTitle(__LINE__);                                                                                                 \
341         printf(" " format "\n", __VA_ARGS__);                                                                                      \
342         fflush(stdout);                                                                                                            \
343     } while (0)
344 
345 /***********************************************************************************************************************************
346 Is this a 64-bit system?  If not then it is 32-bit since 16-bit systems are not supported.
347 ***********************************************************************************************************************************/
348 #define TEST_64BIT()                                                                                                               \
349     (sizeof(size_t) == 8)
350 
351 /***********************************************************************************************************************************
352 Is this a big-endian system?
353 ***********************************************************************************************************************************/
354 #define TEST_BIG_ENDIAN() (!*(unsigned char *)&(uint16_t){1})
355 
356 #endif
357