1/* @title: Test Assertion Macros */
2#pragma once
3#include <asm.h>
4#include <compiler/core.h>
5#include <err.h>
6#include <math/bit.h>
7#include <stdbool.h>
8#include <stddef.h>
9#include <stdint.h>
10#include <string.h>
11#include <time/time.h>
12
13/* We expect
14 * - test_err(fmt, ...)
15 * - TEST_FAIL(msg)
16 * - test_global
17 *
18 * from test.h
19 */
20
21cc_analyzer_noreturn static inline struct test_verdict
22test_assert_failed(const char *msg) {
23 return TEST_FAIL(msg);
24}
25
26cc_analyzer_noreturn static inline void test_assert_void_failed(void) {}
27
28/* ==================== Boolean + Truthiness ==================== */
29
30#define TEST_ASSERT(cond) \
31 do { \
32 if (!(cond)) { \
33 test_err("assert \"%s\" failed", #cond); \
34 return test_assert_failed(#cond); \
35 } \
36 } while (0)
37
38#define TEST_ASSERT_TRUE(cond) TEST_ASSERT(cond)
39
40#define TEST_ASSERT_FALSE(cond) \
41 do { \
42 if (cond) { \
43 test_err("assert false \"%s\" failed (was true)", #cond); \
44 return test_assert_failed("!" #cond); \
45 } \
46 } while (0)
47
48#define TEST_ASSERT_MSG(cond, fmt, ...) \
49 do { \
50 if (!(cond)) { \
51 test_err("assert \"%s\" failed: " fmt, #cond, ##__VA_ARGS__); \
52 return test_assert_failed(#cond); \
53 } \
54 } while (0)
55
56/* ==================== Pointer ==================== */
57#define TEST_ASSERT_NULL(ptr) \
58 do { \
59 const void *_p = (const void *) (ptr); \
60 if (_p != NULL) { \
61 test_err("assert \"%s == NULL\" failed (got %p)", #ptr, _p); \
62 return test_assert_failed(#ptr " == NULL"); \
63 } \
64 } while (0)
65
66#define TEST_ASSERT_NONNULL(ptr) \
67 do { \
68 const void *_p = (const void *) (ptr); \
69 if (_p == NULL) { \
70 test_err("assert \"%s != NULL\" failed (got NULL)", #ptr); \
71 return test_assert_failed(#ptr " != NULL"); \
72 } \
73 } while (0)
74
75#define TEST_ASSERT_PTR_EQ(a, b) \
76 do { \
77 const void *_pa = (const void *) (a); \
78 const void *_pb = (const void *) (b); \
79 if (_pa != _pb) { \
80 test_err("assert ptr \"%s == %s\" failed (%p != %p)", #a, #b, _pa, \
81 _pb); \
82 return test_assert_failed(#a " == " #b); \
83 } \
84 } while (0)
85
86#define TEST_ASSERT_PTR_NE(a, b) \
87 do { \
88 const void *_pa = (const void *) (a); \
89 const void *_pb = (const void *) (b); \
90 if (_pa == _pb) { \
91 test_err("assert ptr \"%s != %s\" failed (both %p)", #a, #b, _pa); \
92 return test_assert_failed(#a " != " #b); \
93 } \
94 } while (0)
95
96/* ==================== Unsigned Equality & Comparison ==================== */
97#define TEST_ASSERT_EQ(a, b) \
98 do { \
99 typeof(a) _a = (a); \
100 typeof(b) _b = (b); \
101 if ((uint64_t) (_a) != (uint64_t) (_b)) { \
102 test_err("assert \"%s == %s\" failed (%llu != %llu / 0x%llx != " \
103 "0x%llx)", \
104 #a, #b, (unsigned long long) (uint64_t) (_a), \
105 (unsigned long long) (uint64_t) (_b), \
106 (unsigned long long) (uint64_t) (_a), \
107 (unsigned long long) (uint64_t) (_b)); \
108 return test_assert_failed(#a " == " #b); \
109 } \
110 } while (0)
111
112#define TEST_ASSERT_NE(a, b) \
113 do { \
114 typeof(a) _a = (a); \
115 typeof(b) _b = (b); \
116 if ((uint64_t) (_a) == (uint64_t) (_b)) { \
117 test_err("assert \"%s != %s\" failed (both equal %llu / 0x%llx)", \
118 #a, #b, (unsigned long long) (uint64_t) (_a), \
119 (unsigned long long) (uint64_t) (_a)); \
120 return test_assert_failed(#a " != " #b); \
121 } \
122 } while (0)
123
124#define TEST_ASSERT_LT(a, b) \
125 do { \
126 typeof(a) _a = (a); \
127 typeof(b) _b = (b); \
128 if (!((uint64_t) (_a) < (uint64_t) (_b))) { \
129 test_err("assert \"%s < %s\" failed (%llu >= %llu)", #a, #b, \
130 (unsigned long long) (uint64_t) (_a), \
131 (unsigned long long) (uint64_t) (_b)); \
132 return test_assert_failed(#a " < " #b); \
133 } \
134 } while (0)
135
136#define TEST_ASSERT_LE(a, b) \
137 do { \
138 typeof(a) _a = (a); \
139 typeof(b) _b = (b); \
140 if (!((uint64_t) (_a) <= (uint64_t) (_b))) { \
141 test_err("assert \"%s <= %s\" failed (%llu > %llu)", #a, #b, \
142 (unsigned long long) (uint64_t) (_a), \
143 (unsigned long long) (uint64_t) (_b)); \
144 return test_assert_failed(#a " <= " #b); \
145 } \
146 } while (0)
147
148#define TEST_ASSERT_GT(a, b) \
149 do { \
150 typeof(a) _a = (a); \
151 typeof(b) _b = (b); \
152 if (!((uint64_t) (_a) > (uint64_t) (_b))) { \
153 test_err("assert \"%s > %s\" failed (%llu <= %llu)", #a, #b, \
154 (unsigned long long) (uint64_t) (_a), \
155 (unsigned long long) (uint64_t) (_b)); \
156 return test_assert_failed(#a " > " #b); \
157 } \
158 } while (0)
159
160#define TEST_ASSERT_GE(a, b) \
161 do { \
162 typeof(a) _a = (a); \
163 typeof(b) _b = (b); \
164 if (!((uint64_t) (_a) >= (uint64_t) (_b))) { \
165 test_err("assert \"%s >= %s\" failed (%llu < %llu)", #a, #b, \
166 (unsigned long long) (uint64_t) (_a), \
167 (unsigned long long) (uint64_t) (_b)); \
168 return test_assert_failed(#a " >= " #b); \
169 } \
170 } while (0)
171
172/* ==================== Signed Integer Comparisons ==================== */
173#define TEST_ASSERT_EQ_S(a, b) \
174 do { \
175 typeof(a) _a = (a); \
176 typeof(b) _b = (b); \
177 if ((int64_t) (_a) != (int64_t) (_b)) { \
178 test_err("assert \"%s == %s\" failed (%lld != %lld)", #a, #b, \
179 (long long) (int64_t) (_a), (long long) (int64_t) (_b)); \
180 return test_assert_failed(#a " == " #b); \
181 } \
182 } while (0)
183
184#define TEST_ASSERT_LT_S(a, b) \
185 do { \
186 typeof(a) _a = (a); \
187 typeof(b) _b = (b); \
188 if (!((int64_t) (_a) < (int64_t) (_b))) { \
189 test_err("assert \"%s < %s\" failed (%lld >= %lld)", #a, #b, \
190 (long long) (int64_t) (_a), (long long) (int64_t) (_b)); \
191 return test_assert_failed(#a " < " #b); \
192 } \
193 } while (0)
194
195#define TEST_ASSERT_LE_S(a, b) \
196 do { \
197 typeof(a) _a = (a); \
198 typeof(b) _b = (b); \
199 if (!((int64_t) (_a) <= (int64_t) (_b))) { \
200 test_err("assert \"%s <= %s\" failed (%lld > %lld)", #a, #b, \
201 (long long) (int64_t) (_a), (long long) (int64_t) (_b)); \
202 return test_assert_failed(#a " <= " #b); \
203 } \
204 } while (0)
205
206#define TEST_ASSERT_GT_S(a, b) \
207 do { \
208 typeof(a) _a = (a); \
209 typeof(b) _b = (b); \
210 if (!((int64_t) (_a) > (int64_t) (_b))) { \
211 test_err("assert \"%s > %s\" failed (%lld <= %lld)", #a, #b, \
212 (long long) (int64_t) (_a), (long long) (int64_t) (_b)); \
213 return test_assert_failed(#a " > " #b); \
214 } \
215 } while (0)
216
217#define TEST_ASSERT_GE_S(a, b) \
218 do { \
219 typeof(a) _a = (a); \
220 typeof(b) _b = (b); \
221 if (!((int64_t) (_a) >= (int64_t) (_b))) { \
222 test_err("assert \"%s >= %s\" failed (%lld < %lld)", #a, #b, \
223 (long long) (int64_t) (_a), (long long) (int64_t) (_b)); \
224 return test_assert_failed(#a " >= " #b); \
225 } \
226 } while (0)
227
228/* ==================== Memory and String ==================== */
229#define TEST_ASSERT_STR_EQ(a, b) \
230 do { \
231 const char *_sa = (const char *) (a); \
232 const char *_sb = (const char *) (b); \
233 if (_sa == NULL || _sb == NULL || strcmp(_sa, _sb) != 0) { \
234 test_err("assert str \"%s == %s\" failed (\"%s\" != \"%s\")", #a, \
235 #b, _sa ? _sa : "<NULL>", _sb ? _sb : "<NULL>"); \
236 return test_assert_failed(#a " == " #b); \
237 } \
238 } while (0)
239
240#define TEST_ASSERT_STR_NE(a, b) \
241 do { \
242 const char *_sa = (const char *) (a); \
243 const char *_sb = (const char *) (b); \
244 if (_sa == _sb || \
245 (_sa != NULL && _sb != NULL && strcmp(_sa, _sb) == 0)) { \
246 test_err("assert str \"%s != %s\" failed (both \"%s\")", #a, #b, \
247 _sa ? _sa : "<NULL>"); \
248 return test_assert_failed(#a " != " #b); \
249 } \
250 } while (0)
251
252#define TEST_ASSERT_MEM_EQ(a, b, size) \
253 do { \
254 const void *_ma = (const void *) (a); \
255 const void *_mb = (const void *) (b); \
256 size_t _sz = (size_t) (size); \
257 if (memcmp(_ma, _mb, _sz) != 0) { \
258 test_err("assert mem \"%s == %s\" failed (size %zu)", #a, #b, \
259 _sz); \
260 return test_assert_failed(#a " == " #b); \
261 } \
262 } while (0)
263
264#define TEST_ASSERT_MEM_ZERO(ptr, size) \
265 do { \
266 const uint8_t *_pz = (const uint8_t *) (ptr); \
267 size_t _sz = (size_t) (size); \
268 bool _all_zero = true; \
269 size_t _first_bad = 0; \
270 for (size_t _i = 0; _i < _sz; _i++) { \
271 if (_pz[_i] != 0) { \
272 _all_zero = false; \
273 _first_bad = _i; \
274 break; \
275 } \
276 } \
277 if (!_all_zero) { \
278 test_err("assert mem_zero \"%s\" failed (nonzero byte 0x%02x at " \
279 "offset %zu of %zu)", \
280 #ptr, _pz[_first_bad], _first_bad, _sz); \
281 return test_assert_failed(#ptr " is zero"); \
282 } \
283 } while (0)
284
285/* ==================== Errors and statuses ==================== */
286#define TEST_ASSERT_OK(err) \
287 do { \
288 typeof(err) _err = (err); \
289 if (_err != 0) { \
290 test_err("assert ok \"%s == 0\" failed (error code %lld / " \
291 "0x%llx)", \
292 #err, (long long) (int64_t) (_err), \
293 (unsigned long long) (uint64_t) (_err)); \
294 return test_assert_failed(#err " == 0"); \
295 } \
296 } while (0)
297
298/* ==================== Range and Bit Manipulation ==================== */
299#define TEST_ASSERT_IN_RANGE(val, min, max) \
300 do { \
301 typeof(val) _v = (val); \
302 typeof(min) _min = (min); \
303 typeof(max) _max = (max); \
304 if (!((_v) >= (_min) && (_v) <= (_max))) { \
305 test_err("assert in range \"%s <= %s <= %s\" failed (val=%llu, " \
306 "range=[%llu, %llu])", \
307 #min, #val, #max, (unsigned long long) (uint64_t) (_v), \
308 (unsigned long long) (uint64_t) (_min), \
309 (unsigned long long) (uint64_t) (_max)); \
310 return test_assert_failed(#val " in range [" #min ", " #max "]"); \
311 } \
312 } while (0)
313
314#define TEST_ASSERT_BIT_SET(val, bit) \
315 do { \
316 uint64_t _v = (uint64_t) (val); \
317 uint32_t _b = (uint32_t) (bit); \
318 if (!BIT_TEST(_v, _b)) { \
319 test_err("assert bit set \"%s bit %u\" failed (val=0x%llx)", #val, \
320 _b, (unsigned long long) _v); \
321 return test_assert_failed(#val " bit " #bit " is set"); \
322 } \
323 } while (0)
324
325#define TEST_ASSERT_BIT_CLEAR(val, bit) \
326 do { \
327 uint64_t _v = (uint64_t) (val); \
328 uint32_t _b = (uint32_t) (bit); \
329 if (BIT_TEST(_v, _b)) { \
330 test_err("assert bit clear \"%s bit %u\" failed (val=0x%llx)", \
331 #val, _b, (unsigned long long) _v); \
332 return test_assert_failed(#val " bit " #bit " is clear"); \
333 } \
334 } while (0)
335
336#define TEST_ASSERT_MASK_SET(val, mask) \
337 do { \
338 uint64_t _v = (uint64_t) (val); \
339 uint64_t _m = (uint64_t) (mask); \
340 if ((_v & _m) != _m) { \
341 test_err("assert mask set \"%s & %s == %s\" failed (val=0x%llx, " \
342 "mask=0x%llx)", \
343 #val, #mask, #mask, (unsigned long long) _v, \
344 (unsigned long long) _m); \
345 return test_assert_failed(#val " has mask " #mask); \
346 } \
347 } while (0)
348
349/* ==================== For void helper functions ==================== */
350#define TEST_ASSERT_VOID(cond) \
351 do { \
352 if (!(cond)) { \
353 test_err("assert void \"%s\" failed", #cond); \
354 test_assert_void_failed(); \
355 return; \
356 } \
357 } while (0)
358
359#define TEST_ASSERT_VOID_MSG(cond, fmt, ...) \
360 do { \
361 if (!(cond)) { \
362 test_err("assert void \"%s\" failed: " fmt, #cond, ##__VA_ARGS__); \
363 test_assert_void_failed(); \
364 return; \
365 } \
366 } while (0)
367
368#define TEST_ASSERT_VOID_EQ(a, b) \
369 do { \
370 typeof(a) _a = (a); \
371 typeof(b) _b = (b); \
372 if ((uint64_t) (_a) != (uint64_t) (_b)) { \
373 test_err("assert void \"%s == %s\" failed (%llu != %llu / 0x%llx " \
374 "!= 0x%llx)", \
375 #a, #b, (unsigned long long) (uint64_t) (_a), \
376 (unsigned long long) (uint64_t) (_b), \
377 (unsigned long long) (uint64_t) (_a), \
378 (unsigned long long) (uint64_t) (_b)); \
379 test_assert_void_failed(); \
380 return; \
381 } \
382 } while (0)
383
384#define TEST_ASSERT_VOID_NONNULL(ptr) \
385 do { \
386 const void *_p = (const void *) (ptr); \
387 if (_p == NULL) { \
388 test_err("assert void \"%s != NULL\" failed", #ptr); \
389 test_assert_void_failed(); \
390 return; \
391 } \
392 } while (0)
393
394/* ==================== Soft assertions ==================== */
395#define TEST_EXPECT(cond) \
396 do { \
397 if (!(cond)) { \
398 test_err("expect \"%s\" failed", #cond); \
399 if (test_global.current_test) \
400 test_global.current_test->soft_fails++; \
401 } \
402 } while (0)
403
404#define TEST_EXPECT_EQ(a, b) \
405 do { \
406 typeof(a) _a = (a); \
407 typeof(b) _b = (b); \
408 if ((uint64_t) (_a) != (uint64_t) (_b)) { \
409 test_err("expect \"%s == %s\" failed (%llu != %llu / 0x%llx != " \
410 "0x%llx)", \
411 #a, #b, (unsigned long long) (uint64_t) (_a), \
412 (unsigned long long) (uint64_t) (_b), \
413 (unsigned long long) (uint64_t) (_a), \
414 (unsigned long long) (uint64_t) (_b)); \
415 if (test_global.current_test) \
416 test_global.current_test->soft_fails++; \
417 } \
418 } while (0)
419
420#define TEST_EXPECT_NONNULL(ptr) \
421 do { \
422 const void *_p = (const void *) (ptr); \
423 if (_p == NULL) { \
424 test_err("expect \"%s != NULL\" failed", #ptr); \
425 if (test_global.current_test) \
426 test_global.current_test->soft_fails++; \
427 } \
428 } while (0)
429
430#define TEST_EXPECT_MSG(cond, fmt, ...) \
431 do { \
432 if (!(cond)) { \
433 test_err("expect \"%s\" failed: " fmt, #cond, ##__VA_ARGS__); \
434 if (test_global.current_test) \
435 test_global.current_test->soft_fails++; \
436 } \
437 } while (0)
438
439/* ==================== Worker versions ==================== */
440
441/* These versions are for workers, since TEST_ASSERT can't be used
442 * there. However, they're cooperative, so they don't
443 * end right away, and we have a TEST_WORKER_CHECK_GOTO variant
444 * so unwinding can happen */
445
446#define TEST_WORKER_CHECK_MSG(fleet, cond, fmt, ...) \
447 do { \
448 if (cc_unlikely(!(cond))) { \
449 test_fleet_report((fleet), __RELFILE__, __LINE__, #cond ": " fmt, \
450 ##__VA_ARGS__); \
451 return false; \
452 } \
453 } while (0)
454
455#define TEST_WORKER_CHECK(fleet, cond) \
456 do { \
457 if (cc_unlikely(!(cond))) { \
458 test_fleet_report((fleet), __RELFILE__, __LINE__, "%s", #cond); \
459 return false; \
460 } \
461 } while (0)
462
463#define TEST_WORKER_CHECK_EQ(fleet, a, b) \
464 do { \
465 typeof(a) _wa = (a); \
466 typeof(b) _wb = (b); \
467 if (cc_unlikely((uint64_t) (_wa) != (uint64_t) (_wb))) { \
468 test_fleet_report((fleet), __RELFILE__, __LINE__, \
469 "%s == %s (%llu != %llu)", #a, #b, \
470 (unsigned long long) (uint64_t) (_wa), \
471 (unsigned long long) (uint64_t) (_wb)); \
472 return false; \
473 } \
474 } while (0)
475
476#define TEST_WORKER_CHECK_NONNULL(fleet, ptr) \
477 do { \
478 if (cc_unlikely((const void *) (ptr) == NULL)) { \
479 test_fleet_report((fleet), __RELFILE__, __LINE__, \
480 "%s != NULL (got NULL)", #ptr); \
481 return false; \
482 } \
483 } while (0)
484
485/* Does not end the test */
486#define TEST_WORKER_EXPECT(fleet, cond) \
487 do { \
488 if (cc_unlikely(!(cond))) { \
489 cc_ignore(fleet); \
490 test_err("worker expect \"%s\" failed", #cond); \
491 test_global.current_test->soft_fails++; \
492 } \
493 } while (0)
494
495#define TEST_WORKER_CHECK_GOTO(fleet, cond, label) \
496 do { \
497 if (cc_unlikely(!(cond))) { \
498 test_fleet_report((fleet), __RELFILE__, __LINE__, "%s", #cond); \
499 goto label; \
500 } \
501 } while (0)
502
503#define TEST_WORKER_CHECK_EQ_GOTO(fleet, a, b, label) \
504 do { \
505 typeof(a) _wa = (a); \
506 typeof(b) _wb = (b); \
507 if (cc_unlikely((uint64_t) (_wa) != (uint64_t) (_wb))) { \
508 test_fleet_report((fleet), __RELFILE__, __LINE__, \
509 "%s == %s (%llu != %llu)", #a, #b, \
510 (unsigned long long) (uint64_t) (_wa), \
511 (unsigned long long) (uint64_t) (_wb)); \
512 goto label; \
513 } \
514 } while (0)
515
516#define TEST_ASSERT_POLL(cond, timeout_ms) \
517 do { \
518 time_ms_t _poll_start = time_get_ms(); \
519 time_ms_t _poll_timeout = (timeout_ms); \
520 while (!(cond)) { \
521 if (time_get_ms() - _poll_start >= _poll_timeout) { \
522 test_err("assert poll \"%s\" timed out after %lu ms", #cond, \
523 (unsigned long) _poll_timeout); \
524 return test_assert_failed(#cond " timed out"); \
525 } \
526 cpu_pause(); \
527 } \
528 } while (0)
529
530#define TEST_WORKER_POLL(fleet, cond, timeout_ms) \
531 do { \
532 time_ms_t _poll_start = time_get_ms(); \
533 time_ms_t _poll_timeout = (timeout_ms); \
534 while (!(cond)) { \
535 if (test_fleet_must_stop(fleet) || \
536 time_get_ms() - _poll_start >= _poll_timeout) { \
537 test_fleet_report((fleet), __RELFILE__, __LINE__, \
538 "poll \"%s\" timed out after %lu ms", #cond, \
539 (unsigned long) _poll_timeout); \
540 return false; \
541 } \
542 cpu_pause(); \
543 } \
544 } while (0)
545
546#define TEST_WORKER_POLL_GOTO(fleet, cond, timeout_ms, label) \
547 do { \
548 time_ms_t _poll_start = time_get_ms(); \
549 time_ms_t _poll_timeout = (timeout_ms); \
550 while (!(cond)) { \
551 if (test_fleet_must_stop(fleet) || \
552 time_get_ms() - _poll_start >= _poll_timeout) { \
553 test_fleet_report((fleet), __RELFILE__, __LINE__, \
554 "poll \"%s\" timed out after %lu ms", #cond, \
555 (unsigned long) _poll_timeout); \
556 goto label; \
557 } \
558 cpu_pause(); \
559 } \
560 } while (0)
561