asoma.dev · the C reference for libose
libose
the C library every o.se host is built on
Loading...
Searching...
No Matches
ose_test_unit.h
Go to the documentation of this file.
1/*
2 Copyright (c) 2020-23 John MacCallum Permission is hereby granted,
3 free of charge, to any person obtaining a copy of this software
4 and associated documentation files (the "Software"), to deal in
5 the Software without restriction, including without limitation the
6 rights to use, copy, modify, merge, publish, distribute,
7 sublicense, and/or sell copies of the Software, and to permit
8 persons to whom the Software is furnished to do so, subject to the
9 following conditions:
10
11 The above copyright notice and this permission notice shall be
12 included in all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
18 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 DEALINGS IN THE SOFTWARE.
22*/
23
29
59
60#include "ose_test_bundle_literal.h"
61#include "ose_test_vm_literal.h"
62
73#define UNIT_TEST_FUNCTION(fn) TEST_FUNCTION(ut_, fn)
74
85#define SKIP_UNIT_TEST_FUNCTION(fn) SKIP_TEST_FUNCTION(fn)
86
95void printb(const char * const b)
96{
97 if(b){
98 // prevent load of misaligned address runtime error
99 char buf[8];
100 char *p = align(buf);
101 for(int i = 0; i < 4; i++){
102 p[i] = b[i];
103 }
104 int32_t s = htonl(*((int32_t *)p));
105 for(int32_t i = 0; i < s + 4; i++){
106 if(b[i] <= 32 || b[i] > 127){
107 printf("\\x%02x", b[i]);
108 }else{
109 printf("%c", b[i]);
110 }
111 }
112 }else{
113 printf("NULL");
114 }
115}
116
135/* A FAILING SUITE HAS TO STOP.
136 *
137 * On 2026-09-09 ut_ose_context printed one failing assertion -- the same
138 * line, over and over, at full speed -- into a redirected log for about two
139 * hours and filled a 1TB disk. The macro below prints ONCE per failure, so
140 * that cannot happen unless control returns to a UNIT_TEST that has already
141 * run: a longjmp into a frame that has gone. Whatever the route, the harness
142 * had no bound at all, and a test suite that can consume a disk is a worse
143 * failure than whatever it was trying to report.
144 *
145 * This is the bound, not the diagnosis. It costs nothing on a passing run
146 * and turns an unbounded spin into an exit with a message.
147 */
148#ifndef OSE_TEST_MAX_FAILURES
149#define OSE_TEST_MAX_FAILURES 200
150#endif
151
152#define UNIT_TEST_IMPL(test, \
153 expected_result, \
154 desc, \
155 file, \
156 func, \
157 line) \
158 { \
159 testctr++; \
160 int32_t r = ASSERTION_FAILED; \
161 if(!setjmp(env)){ \
162 r = test; \
163 } \
164 if(r != expected_result){ \
165 printf(ANSI_COLOR_RED \
166 "TEST %d FAILED: " \
167 ANSI_COLOR_RESET \
168 "%s:%s:%d:\n" \
169 "expected %d, but got %d\n", \
170 testctr, \
171 file, func, line, \
172 expected_result, r); \
173 failctr++; \
174 if(failctr > OSE_TEST_MAX_FAILURES){ \
175 printf("ABANDONED: %d failures in one test " \
176 "function -- see OSE_TEST_MAX_FAILURES\n", \
177 failctr); \
178 fflush(stdout); \
179 exit(1); \
180 } \
181 }else if(verbose){ \
182 printf(ANSI_COLOR_GREEN \
183 "TEST %d PASSED:\n" \
184 ANSI_COLOR_RESET \
185 "%s = %d\n", \
186 testctr, #test, expected_result); \
187 } \
188 } \
189
201#define UNIT_TEST(test, expected_result, desc) \
202 UNIT_TEST_IMPL(test, expected_result, desc, \
203 __FILE__, __func__, __LINE__);
204
217#define UNIT_TEST_WITH_SIMPLE_BUNDLE(b, \
218 test, \
219 expected_result, \
220 desc) \
221 { \
222 char buf[MAX_BNDLSIZE] __attribute__ ((aligned (16))); \
223 char *p = NULL; \
224 ose_bundle bundle; \
225 /* NO ROOM TO GROW, ON EVERY PLATFORM. A bundle's \
226 total size is the word 8 bytes before it, and this \
227 bundle used to start 4 bytes into buf -- so that \
228 word was whatever the stack held below buf. On a \
229 Mac it happened to allow a resize and on Linux it \
230 did not, and eight int64 cases failed only in CI. \
231 It is zero now: a test that resizes needs \
232 UNIT_TEST_WITH_CONTEXT_BUNDLE, and fails anywhere \
233 without it. */ \
234 if(b){ \
235 p = buf; \
236 memset(buf, 0, sizeof(buf)); \
237 memcpy(p + 4, b, sizeof(b)); \
238 bundle = ose_makeBundle(p + 8); \
239 }else{ \
240 /* for testing NULL pointer input */ \
241 bundle = ose_makeBundle(p); \
242 } \
243 if(verbose){ \
244 printf("bundle = "); \
245 printb(b); \
246 printf("\n"); \
247 } \
248 UNIT_TEST(test, expected_result, desc); \
249 }
250
251#define UNIT_TEST_WITH_CONTEXT_BUNDLE_IMPL(testbndl, \
252 bundle_literal, \
253 bundle_literal_size, \
254 copy_offset, \
255 call_offset, \
256 test, \
257 expected_result, \
258 desc) \
259 { \
260 ose_bundle bundle = (ose_bundle){NULL}; \
261 char buf[bundle_literal_size + 4] \
262 __attribute__ ((aligned (16))); \
263 /* Start from a known state rather than whatever the stack holds. \
264 \
265 Note this is hygiene, not a fix for the cache: this fixture cannot \
266 be used with anything that touches the context cache at all. The \
267 cache slots are addressed 560 bytes below the working bundle \
268 pointer, and this literal places that pointer only 128 bytes into \
269 buf -- so cache reads and writes land 432 bytes *before* the \
270 array, outside it entirely, where zeroing cannot reach them. Since \
271 ose_context_getFirstOffsetForMatch calls whatever non-zero value \
272 it finds in that slot, the result is a segfault wherever that \
273 stack memory is not already zero. Measured, not inferred. */ \
274 memset(buf, 0, sizeof(buf)); \
275 if(testbndl){ \
276 memcpy(buf, bundle_literal, bundle_literal_size + 4); \
277 char *p = buf + copy_offset; \
278 bundle.b = buf + call_offset; \
279 int32_t sizeofb = sizeof(testbndl); \
280 int32_t size1 = ose_ntohl(*((int32_t *)(p - 4))); \
281 int32_t size2 = ose_ntohl(*((int32_t *)(p + size1))); \
282 *((int32_t *)(p + size1)) = 0; \
283 memcpy(p - 4, testbndl, sizeofb); \
284 *((int32_t *)(p + (sizeofb - 4))) \
285 = ose_htonl(size2 - (sizeofb - 20)); \
286 }else{ \
287 } \
288 UNIT_TEST(test, expected_result, desc); \
289 if(verbose){ \
290 printf("result bundle = "); \
291 printb(bundle.b); \
292 printf("\n"); \
293 } \
294 }
295
308#define UNIT_TEST_WITH_CONTEXT_BUNDLE(testbndl, \
309 test, \
310 expected_result, \
311 desc) \
312 UNIT_TEST_WITH_CONTEXT_BUNDLE_IMPL(testbndl, \
313 ose_test_bundle_bytes, \
314 OSE_TEST_BUNDLE_TOTAL_SIZE - 4, \
315 OSE_TEST_BUNDLE_WORKING_OFFSET, \
316 OSE_TEST_BUNDLE_WORKING_OFFSET, \
317 test, \
318 expected_result, \
319 desc)
320
353#define UNIT_TEST_WITH_VM(testbndl, \
354 test, \
355 expected_result, \
356 desc) \
357 UNIT_TEST_WITH_CONTEXT_BUNDLE_IMPL(testbndl, \
358 ose_test_vm_bytes, \
359 OSE_TEST_VM_TOTAL_SIZE - 4, \
360 OSE_TEST_VM_STACK_BUNDLE_OFFSET, \
361 OSE_TEST_VM_WORKING_BUNDLE_OFFSET, \
362 test, \
363 expected_result, \
364 desc)
365
379#define UNIT_TEST_BUNDLE_TRANSFORM(testfn, \
380 testbndl, \
381 resultbndl, \
382 desc) \
383 if(verbose){ \
384 printf("test bundle = "); \
385 printb(testbndl); \
386 printf("\n"); \
387 printf("expected result = "); \
388 printb(resultbndl); \
389 printf("\n"); \
390 } \
391 UNIT_TEST_WITH_CONTEXT_BUNDLE( \
392 testbndl, \
393 (testfn(bundle), \
394 COMPARE_BUNDLES(resultbndl, \
395 ose_getBundlePtr(bundle) - 4)), \
396 0, \
397 desc);
398
431#define UNIT_TEST_BUNDLE_ERRNO(testfn, \
432 testbndl, \
433 errno, \
434 desc) \
435 UNIT_TEST_WITH_CONTEXT_BUNDLE( \
436 testbndl, \
437 (testfn(bundle), \
438 ose_errno_get(bundle)), \
439 errno, \
440 desc);
459#define UNIT_TEST_BUNDLE_TRANSFORM_ARGS(testfn, \
460 testbndl, \
461 resultbndl, \
462 desc, \
463 ...) \
464 UNIT_TEST_WITH_CONTEXT_BUNDLE( \
465 testbndl, \
466 (testfn(bundle, __VA_ARGS__), \
467 COMPARE_BUNDLES(resultbndl, \
468 ose_getBundlePtr(bundle) - 4)), \
469 0, \
470 desc);
471
486#define UNIT_TEST_BUNDLE_TRANSFORM_TRIGGER_ASSERTION(testfn, \
487 testbndl, \
488 desc) \
489 UNIT_TEST_WITH_CONTEXT_BUNDLE(testbndl, \
490 (testfn(bundle), 0), \
491 ASSERTION_FAILED, \
492 desc);
493
509#define UNIT_TEST_BUNDLE_TRANSFORM_TRIGGER_ASSERTION_ARGS(testfn, \
510 testbndl, \
511 desc, \
512 ...) \
513 UNIT_TEST_WITH_CONTEXT_BUNDLE(testbndl, \
514 (testfn(bundle, __VA_ARGS__), 0), \
515 ASSERTION_FAILED, \
516 desc);
517
532#define UNIT_TEST_VM_ERRNO(testfn, \
533 testbndl, \
534 errno, \
535 desc) \
536 UNIT_TEST_WITH_VM(testbndl, \
537 (testfn(bundle), \
538 ose_errno_get(bundle)), \
539 errno, \
540 desc)
541
556#define UNIT_TEST_VM_STACK_COMPARE(testfn, \
557 testbndl, \
558 resultbndl, \
559 desc) \
560 UNIT_TEST_WITH_VM(testbndl, \
561 (testfn(bundle), \
562 ose_errno_get(bundle) == OSE_ERR_NONE \
563 && !COMPARE_BUNDLES(resultbndl, \
564 buf + OSE_TEST_VM_STACK_BUNDLE_OFFSET - 4)), \
565 1, \
566 desc)
567
568
575#define SKIP_UNIT_TEST_WITH_BUNDLE(b, test, expected_result) \
576 SKIP_TEST(test, expected_result)
577
584#define SKIP_UNIT_TEST(test, expected_result) \
585 SKIP_TEST(test, expected_result)
void printb(const char *const b)
Print each byte of a bundle.
Definition ose_test_unit.h:95