1 /*
2  * Runner for littlefs tests
3  *
4  * Copyright (c) 2022, The littlefs authors.
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 #ifndef _POSIX_C_SOURCE
8 #define _POSIX_C_SOURCE 199309L
9 #endif
10 
11 #include "runners/test_runner.h"
12 #include "bd/lfs_emubd.h"
13 
14 #include <getopt.h>
15 #include <sys/types.h>
16 #include <errno.h>
17 #include <setjmp.h>
18 #include <fcntl.h>
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <time.h>
23 #include <execinfo.h>
24 
25 
26 // some helpers
27 
28 // append to an array with amortized doubling
mappend(void ** p,size_t size,size_t * count,size_t * capacity)29 void *mappend(void **p,
30         size_t size,
31         size_t *count,
32         size_t *capacity) {
33     uint8_t *p_ = *p;
34     size_t count_ = *count;
35     size_t capacity_ = *capacity;
36 
37     count_ += 1;
38     if (count_ > capacity_) {
39         capacity_ = (2*capacity_ < 4) ? 4 : 2*capacity_;
40 
41         p_ = realloc(p_, capacity_*size);
42         if (!p_) {
43             return NULL;
44         }
45     }
46 
47     *p = p_;
48     *count = count_;
49     *capacity = capacity_;
50     return &p_[(count_-1)*size];
51 }
52 
53 // a quick self-terminating text-safe varint scheme
leb16_print(uintmax_t x)54 static void leb16_print(uintmax_t x) {
55     // allow 'w' to indicate negative numbers
56     if ((intmax_t)x < 0) {
57         printf("w");
58         x = -x;
59     }
60 
61     while (true) {
62         char nibble = (x & 0xf) | (x > 0xf ? 0x10 : 0);
63         printf("%c", (nibble < 10) ? '0'+nibble : 'a'+nibble-10);
64         if (x <= 0xf) {
65             break;
66         }
67         x >>= 4;
68     }
69 }
70 
leb16_parse(const char * s,char ** tail)71 static uintmax_t leb16_parse(const char *s, char **tail) {
72     bool neg = false;
73     uintmax_t x = 0;
74     if (tail) {
75         *tail = (char*)s;
76     }
77 
78     if (s[0] == 'w') {
79         neg = true;
80         s = s+1;
81     }
82 
83     size_t i = 0;
84     while (true) {
85         uintmax_t nibble = s[i];
86         if (nibble >= '0' && nibble <= '9') {
87             nibble = nibble - '0';
88         } else if (nibble >= 'a' && nibble <= 'v') {
89             nibble = nibble - 'a' + 10;
90         } else {
91             // invalid?
92             return 0;
93         }
94 
95         x |= (nibble & 0xf) << (4*i);
96         i += 1;
97         if (!(nibble & 0x10)) {
98             s = s + i;
99             break;
100         }
101     }
102 
103     if (tail) {
104         *tail = (char*)s;
105     }
106     return neg ? -x : x;
107 }
108 
109 
110 
111 // test_runner types
112 
113 typedef struct test_geometry {
114     const char *name;
115     test_define_t defines[TEST_GEOMETRY_DEFINE_COUNT];
116 } test_geometry_t;
117 
118 typedef struct test_powerloss {
119     const char *name;
120     void (*run)(
121             const lfs_emubd_powercycles_t *cycles,
122             size_t cycle_count,
123             const struct test_suite *suite,
124             const struct test_case *case_);
125     const lfs_emubd_powercycles_t *cycles;
126     size_t cycle_count;
127 } test_powerloss_t;
128 
129 typedef struct test_id {
130     const char *name;
131     const test_define_t *defines;
132     size_t define_count;
133     const lfs_emubd_powercycles_t *cycles;
134     size_t cycle_count;
135 } test_id_t;
136 
137 
138 // test suites are linked into a custom ld section
139 extern struct test_suite __start__test_suites;
140 extern struct test_suite __stop__test_suites;
141 
142 const struct test_suite *test_suites = &__start__test_suites;
143 #define TEST_SUITE_COUNT \
144     ((size_t)(&__stop__test_suites - &__start__test_suites))
145 
146 
147 // test define management
148 typedef struct test_define_map {
149     const test_define_t *defines;
150     size_t count;
151 } test_define_map_t;
152 
153 typedef struct test_define_names {
154     const char *const *names;
155     size_t count;
156 } test_define_names_t;
157 
test_define_lit(void * data)158 intmax_t test_define_lit(void *data) {
159     return (intptr_t)data;
160 }
161 
162 #define TEST_CONST(x) {test_define_lit, (void*)(uintptr_t)(x)}
163 #define TEST_LIT(x) ((test_define_t)TEST_CONST(x))
164 
165 
166 #define TEST_DEF(k, v) \
167     intmax_t test_define_##k(void *data) { \
168         (void)data; \
169         return v; \
170     }
171 
172     TEST_IMPLICIT_DEFINES
173 #undef TEST_DEF
174 
175 #define TEST_DEFINE_MAP_OVERRIDE    0
176 #define TEST_DEFINE_MAP_EXPLICIT    1
177 #define TEST_DEFINE_MAP_PERMUTATION 2
178 #define TEST_DEFINE_MAP_GEOMETRY    3
179 #define TEST_DEFINE_MAP_IMPLICIT    4
180 #define TEST_DEFINE_MAP_COUNT       5
181 
182 test_define_map_t test_define_maps[TEST_DEFINE_MAP_COUNT] = {
183     [TEST_DEFINE_MAP_IMPLICIT] = {
184         (const test_define_t[TEST_IMPLICIT_DEFINE_COUNT]) {
185             #define TEST_DEF(k, v) \
186                 [k##_i] = {test_define_##k, NULL},
187 
188                 TEST_IMPLICIT_DEFINES
189             #undef TEST_DEF
190         },
191         TEST_IMPLICIT_DEFINE_COUNT,
192     },
193 };
194 
195 #define TEST_DEFINE_NAMES_SUITE    0
196 #define TEST_DEFINE_NAMES_IMPLICIT 1
197 #define TEST_DEFINE_NAMES_COUNT    2
198 
199 test_define_names_t test_define_names[TEST_DEFINE_NAMES_COUNT] = {
200     [TEST_DEFINE_NAMES_IMPLICIT] = {
201         (const char *const[TEST_IMPLICIT_DEFINE_COUNT]){
202             #define TEST_DEF(k, v) \
203                 [k##_i] = #k,
204 
205                 TEST_IMPLICIT_DEFINES
206             #undef TEST_DEF
207         },
208         TEST_IMPLICIT_DEFINE_COUNT,
209     },
210 };
211 
212 intmax_t *test_define_cache;
213 size_t test_define_cache_count;
214 unsigned *test_define_cache_mask;
215 
test_define_name(size_t define)216 const char *test_define_name(size_t define) {
217     // lookup in our test names
218     for (size_t i = 0; i < TEST_DEFINE_NAMES_COUNT; i++) {
219         if (define < test_define_names[i].count
220                 && test_define_names[i].names
221                 && test_define_names[i].names[define]) {
222             return test_define_names[i].names[define];
223         }
224     }
225 
226     return NULL;
227 }
228 
test_define_ispermutation(size_t define)229 bool test_define_ispermutation(size_t define) {
230     // is this define specific to the permutation?
231     for (size_t i = 0; i < TEST_DEFINE_MAP_IMPLICIT; i++) {
232         if (define < test_define_maps[i].count
233                 && test_define_maps[i].defines[define].cb) {
234             return true;
235         }
236     }
237 
238     return false;
239 }
240 
test_define(size_t define)241 intmax_t test_define(size_t define) {
242     // is the define in our cache?
243     if (define < test_define_cache_count
244             && (test_define_cache_mask[define/(8*sizeof(unsigned))]
245                 & (1 << (define%(8*sizeof(unsigned)))))) {
246         return test_define_cache[define];
247     }
248 
249     // lookup in our test defines
250     for (size_t i = 0; i < TEST_DEFINE_MAP_COUNT; i++) {
251         if (define < test_define_maps[i].count
252                 && test_define_maps[i].defines[define].cb) {
253             intmax_t v = test_define_maps[i].defines[define].cb(
254                     test_define_maps[i].defines[define].data);
255 
256             // insert into cache!
257             test_define_cache[define] = v;
258             test_define_cache_mask[define / (8*sizeof(unsigned))]
259                     |= 1 << (define%(8*sizeof(unsigned)));
260 
261             return v;
262         }
263     }
264 
265     return 0;
266 
267     // not found?
268     const char *name = test_define_name(define);
269     fprintf(stderr, "error: undefined define %s (%zd)\n",
270             name ? name : "(unknown)",
271             define);
272     assert(false);
273     exit(-1);
274 }
275 
test_define_flush(void)276 void test_define_flush(void) {
277     // clear cache between permutations
278     memset(test_define_cache_mask, 0,
279             sizeof(unsigned)*(
280                 (test_define_cache_count+(8*sizeof(unsigned))-1)
281                 / (8*sizeof(unsigned))));
282 }
283 
284 // geometry updates
285 const test_geometry_t *test_geometry = NULL;
286 
test_define_geometry(const test_geometry_t * geometry)287 void test_define_geometry(const test_geometry_t *geometry) {
288     test_define_maps[TEST_DEFINE_MAP_GEOMETRY] = (test_define_map_t){
289             geometry->defines, TEST_GEOMETRY_DEFINE_COUNT};
290 }
291 
292 // override updates
293 typedef struct test_override {
294     const char *name;
295     const intmax_t *defines;
296     size_t permutations;
297 } test_override_t;
298 
299 const test_override_t *test_overrides = NULL;
300 size_t test_override_count = 0;
301 
302 test_define_t *test_override_defines = NULL;
303 size_t test_override_define_count = 0;
304 size_t test_override_define_permutations = 1;
305 size_t test_override_define_capacity = 0;
306 
307 // suite/perm updates
test_define_suite(const struct test_suite * suite)308 void test_define_suite(const struct test_suite *suite) {
309     test_define_names[TEST_DEFINE_NAMES_SUITE] = (test_define_names_t){
310             suite->define_names, suite->define_count};
311 
312     // make sure our cache is large enough
313     if (lfs_max(suite->define_count, TEST_IMPLICIT_DEFINE_COUNT)
314             > test_define_cache_count) {
315         // align to power of two to avoid any superlinear growth
316         size_t ncount = 1 << lfs_npw2(
317                 lfs_max(suite->define_count, TEST_IMPLICIT_DEFINE_COUNT));
318         test_define_cache = realloc(test_define_cache, ncount*sizeof(intmax_t));
319         test_define_cache_mask = realloc(test_define_cache_mask,
320                 sizeof(unsigned)*(
321                     (ncount+(8*sizeof(unsigned))-1)
322                     / (8*sizeof(unsigned))));
323         test_define_cache_count = ncount;
324     }
325 
326     // map any overrides
327     if (test_override_count > 0) {
328         // first figure out the total size of override permutations
329         size_t count = 0;
330         size_t permutations = 1;
331         for (size_t i = 0; i < test_override_count; i++) {
332             for (size_t d = 0;
333                     d < lfs_max(
334                         suite->define_count,
335                         TEST_IMPLICIT_DEFINE_COUNT);
336                     d++) {
337                 // define name match?
338                 const char *name = test_define_name(d);
339                 if (name && strcmp(name, test_overrides[i].name) == 0) {
340                     count = lfs_max(count, d+1);
341                     permutations *= test_overrides[i].permutations;
342                     break;
343                 }
344             }
345         }
346         test_override_define_count = count;
347         test_override_define_permutations = permutations;
348 
349         // make sure our override arrays are big enough
350         if (count * permutations > test_override_define_capacity) {
351             // align to power of two to avoid any superlinear growth
352             size_t ncapacity = 1 << lfs_npw2(count * permutations);
353             test_override_defines = realloc(
354                     test_override_defines,
355                     sizeof(test_define_t)*ncapacity);
356             test_override_define_capacity = ncapacity;
357         }
358 
359         // zero unoverridden defines
360         memset(test_override_defines, 0,
361                 sizeof(test_define_t) * count * permutations);
362 
363         // compute permutations
364         size_t p = 1;
365         for (size_t i = 0; i < test_override_count; i++) {
366             for (size_t d = 0;
367                     d < lfs_max(
368                         suite->define_count,
369                         TEST_IMPLICIT_DEFINE_COUNT);
370                     d++) {
371                 // define name match?
372                 const char *name = test_define_name(d);
373                 if (name && strcmp(name, test_overrides[i].name) == 0) {
374                     // scatter the define permutations based on already
375                     // seen permutations
376                     for (size_t j = 0; j < permutations; j++) {
377                         test_override_defines[j*count + d] = TEST_LIT(
378                                 test_overrides[i].defines[(j/p)
379                                     % test_overrides[i].permutations]);
380                     }
381 
382                     // keep track of how many permutations we've seen so far
383                     p *= test_overrides[i].permutations;
384                     break;
385                 }
386             }
387         }
388     }
389 }
390 
test_define_perm(const struct test_suite * suite,const struct test_case * case_,size_t perm)391 void test_define_perm(
392         const struct test_suite *suite,
393         const struct test_case *case_,
394         size_t perm) {
395     if (case_->defines) {
396         test_define_maps[TEST_DEFINE_MAP_PERMUTATION] = (test_define_map_t){
397                 case_->defines + perm*suite->define_count,
398                 suite->define_count};
399     } else {
400         test_define_maps[TEST_DEFINE_MAP_PERMUTATION] = (test_define_map_t){
401                 NULL, 0};
402     }
403 }
404 
test_define_override(size_t perm)405 void test_define_override(size_t perm) {
406     test_define_maps[TEST_DEFINE_MAP_OVERRIDE] = (test_define_map_t){
407             test_override_defines + perm*test_override_define_count,
408             test_override_define_count};
409 }
410 
test_define_explicit(const test_define_t * defines,size_t define_count)411 void test_define_explicit(
412         const test_define_t *defines,
413         size_t define_count) {
414     test_define_maps[TEST_DEFINE_MAP_EXPLICIT] = (test_define_map_t){
415             defines, define_count};
416 }
417 
test_define_cleanup(void)418 void test_define_cleanup(void) {
419     // test define management can allocate a few things
420     free(test_define_cache);
421     free(test_define_cache_mask);
422     free(test_override_defines);
423 }
424 
425 
426 
427 // test state
428 extern const test_geometry_t *test_geometries;
429 extern size_t test_geometry_count;
430 
431 extern const test_powerloss_t *test_powerlosses;
432 extern size_t test_powerloss_count;
433 
434 const test_id_t *test_ids = (const test_id_t[]) {
435     {NULL, NULL, 0, NULL, 0},
436 };
437 size_t test_id_count = 1;
438 
439 size_t test_step_start = 0;
440 size_t test_step_stop = -1;
441 size_t test_step_step = 1;
442 
443 const char *test_disk_path = NULL;
444 const char *test_trace_path = NULL;
445 bool test_trace_backtrace = false;
446 uint32_t test_trace_period = 0;
447 uint32_t test_trace_freq = 0;
448 FILE *test_trace_file = NULL;
449 uint32_t test_trace_cycles = 0;
450 uint64_t test_trace_time = 0;
451 uint64_t test_trace_open_time = 0;
452 lfs_emubd_sleep_t test_read_sleep = 0.0;
453 lfs_emubd_sleep_t test_prog_sleep = 0.0;
454 lfs_emubd_sleep_t test_erase_sleep = 0.0;
455 
456 // this determines both the backtrace buffer and the trace printf buffer, if
457 // trace ends up interleaved or truncated this may need to be increased
458 #ifndef TEST_TRACE_BACKTRACE_BUFFER_SIZE
459 #define TEST_TRACE_BACKTRACE_BUFFER_SIZE 8192
460 #endif
461 void *test_trace_backtrace_buffer[
462     TEST_TRACE_BACKTRACE_BUFFER_SIZE / sizeof(void*)];
463 
464 // trace printing
test_trace(const char * fmt,...)465 void test_trace(const char *fmt, ...) {
466     if (test_trace_path) {
467         // sample at a specific period?
468         if (test_trace_period) {
469             if (test_trace_cycles % test_trace_period != 0) {
470                 test_trace_cycles += 1;
471                 return;
472             }
473             test_trace_cycles += 1;
474         }
475 
476         // sample at a specific frequency?
477         if (test_trace_freq) {
478             struct timespec t;
479             clock_gettime(CLOCK_MONOTONIC, &t);
480             uint64_t now = (uint64_t)t.tv_sec*1000*1000*1000
481                     + (uint64_t)t.tv_nsec;
482             if (now - test_trace_time < (1000*1000*1000) / test_trace_freq) {
483                 return;
484             }
485             test_trace_time = now;
486         }
487 
488         if (!test_trace_file) {
489             // Tracing output is heavy and trying to open every trace
490             // call is slow, so we only try to open the trace file every
491             // so often. Note this doesn't affect successfully opened files
492             struct timespec t;
493             clock_gettime(CLOCK_MONOTONIC, &t);
494             uint64_t now = (uint64_t)t.tv_sec*1000*1000*1000
495                     + (uint64_t)t.tv_nsec;
496             if (now - test_trace_open_time < 100*1000*1000) {
497                 return;
498             }
499             test_trace_open_time = now;
500 
501             // try to open the trace file
502             int fd;
503             if (strcmp(test_trace_path, "-") == 0) {
504                 fd = dup(1);
505                 if (fd < 0) {
506                     return;
507                 }
508             } else {
509                 fd = open(
510                         test_trace_path,
511                         O_WRONLY | O_CREAT | O_APPEND | O_NONBLOCK,
512                         0666);
513                 if (fd < 0) {
514                     return;
515                 }
516                 int err = fcntl(fd, F_SETFL, O_WRONLY | O_CREAT | O_APPEND);
517                 assert(!err);
518             }
519 
520             FILE *f = fdopen(fd, "a");
521             assert(f);
522             int err = setvbuf(f, NULL, _IOFBF,
523                     TEST_TRACE_BACKTRACE_BUFFER_SIZE);
524             assert(!err);
525             test_trace_file = f;
526         }
527 
528         // print trace
529         va_list va;
530         va_start(va, fmt);
531         int res = vfprintf(test_trace_file, fmt, va);
532         va_end(va);
533         if (res < 0) {
534             fclose(test_trace_file);
535             test_trace_file = NULL;
536             return;
537         }
538 
539         if (test_trace_backtrace) {
540             // print backtrace
541             size_t count = backtrace(
542                     test_trace_backtrace_buffer,
543                     TEST_TRACE_BACKTRACE_BUFFER_SIZE);
544             // note we skip our own stack frame
545             for (size_t i = 1; i < count; i++) {
546                 res = fprintf(test_trace_file, "\tat %p\n",
547                         test_trace_backtrace_buffer[i]);
548                 if (res < 0) {
549                     fclose(test_trace_file);
550                     test_trace_file = NULL;
551                     return;
552                 }
553             }
554         }
555 
556         // flush immediately
557         fflush(test_trace_file);
558     }
559 }
560 
561 
562 // test prng
test_prng(uint32_t * state)563 uint32_t test_prng(uint32_t *state) {
564     // A simple xorshift32 generator, easily reproducible. Keep in mind
565     // determinism is much more important than actual randomness here.
566     uint32_t x = *state;
567     x ^= x << 13;
568     x ^= x >> 17;
569     x ^= x << 5;
570     *state = x;
571     return x;
572 }
573 
574 
575 // encode our permutation into a reusable id
perm_printid(const struct test_suite * suite,const struct test_case * case_,const lfs_emubd_powercycles_t * cycles,size_t cycle_count)576 static void perm_printid(
577         const struct test_suite *suite,
578         const struct test_case *case_,
579         const lfs_emubd_powercycles_t *cycles,
580         size_t cycle_count) {
581     (void)suite;
582     // case[:permutation[:powercycles]]
583     printf("%s:", case_->name);
584     for (size_t d = 0;
585             d < lfs_max(
586                 suite->define_count,
587                 TEST_IMPLICIT_DEFINE_COUNT);
588             d++) {
589         if (test_define_ispermutation(d)) {
590             leb16_print(d);
591             leb16_print(TEST_DEFINE(d));
592         }
593     }
594 
595     // only print power-cycles if any occured
596     if (cycles) {
597         printf(":");
598         for (size_t i = 0; i < cycle_count; i++) {
599             leb16_print(cycles[i]);
600         }
601     }
602 }
603 
604 
605 // a quick trie for keeping track of permutations we've seen
606 typedef struct test_seen {
607     struct test_seen_branch *branches;
608     size_t branch_count;
609     size_t branch_capacity;
610 } test_seen_t;
611 
612 struct test_seen_branch {
613     intmax_t define;
614     struct test_seen branch;
615 };
616 
test_seen_insert(test_seen_t * seen,const struct test_suite * suite,const struct test_case * case_)617 bool test_seen_insert(
618         test_seen_t *seen,
619         const struct test_suite *suite,
620         const struct test_case *case_) {
621     (void)case_;
622     bool was_seen = true;
623 
624     // use the currently set defines
625     for (size_t d = 0;
626             d < lfs_max(
627                 suite->define_count,
628                 TEST_IMPLICIT_DEFINE_COUNT);
629             d++) {
630         // treat unpermuted defines the same as 0
631         intmax_t define = test_define_ispermutation(d) ? TEST_DEFINE(d) : 0;
632 
633         // already seen?
634         struct test_seen_branch *branch = NULL;
635         for (size_t i = 0; i < seen->branch_count; i++) {
636             if (seen->branches[i].define == define) {
637                 branch = &seen->branches[i];
638                 break;
639             }
640         }
641 
642         // need to create a new node
643         if (!branch) {
644             was_seen = false;
645             branch = mappend(
646                     (void**)&seen->branches,
647                     sizeof(struct test_seen_branch),
648                     &seen->branch_count,
649                     &seen->branch_capacity);
650             branch->define = define;
651             branch->branch = (test_seen_t){NULL, 0, 0};
652         }
653 
654         seen = &branch->branch;
655     }
656 
657     return was_seen;
658 }
659 
test_seen_cleanup(test_seen_t * seen)660 void test_seen_cleanup(test_seen_t *seen) {
661     for (size_t i = 0; i < seen->branch_count; i++) {
662         test_seen_cleanup(&seen->branches[i].branch);
663     }
664     free(seen->branches);
665 }
666 
667 static void run_powerloss_none(
668         const lfs_emubd_powercycles_t *cycles,
669         size_t cycle_count,
670         const struct test_suite *suite,
671         const struct test_case *case_);
672 static void run_powerloss_cycles(
673         const lfs_emubd_powercycles_t *cycles,
674         size_t cycle_count,
675         const struct test_suite *suite,
676         const struct test_case *case_);
677 
678 // iterate through permutations in a test case
case_forperm(const struct test_suite * suite,const struct test_case * case_,const test_define_t * defines,size_t define_count,const lfs_emubd_powercycles_t * cycles,size_t cycle_count,void (* cb)(void * data,const struct test_suite * suite,const struct test_case * case_,const test_powerloss_t * powerloss),void * data)679 static void case_forperm(
680         const struct test_suite *suite,
681         const struct test_case *case_,
682         const test_define_t *defines,
683         size_t define_count,
684         const lfs_emubd_powercycles_t *cycles,
685         size_t cycle_count,
686         void (*cb)(
687             void *data,
688             const struct test_suite *suite,
689             const struct test_case *case_,
690             const test_powerloss_t *powerloss),
691         void *data) {
692     // explicit permutation?
693     if (defines) {
694         test_define_explicit(defines, define_count);
695 
696         for (size_t v = 0; v < test_override_define_permutations; v++) {
697             // define override permutation
698             test_define_override(v);
699             test_define_flush();
700 
701             // explicit powerloss cycles?
702             if (cycles) {
703                 cb(data, suite, case_, &(test_powerloss_t){
704                         .run=run_powerloss_cycles,
705                         .cycles=cycles,
706                         .cycle_count=cycle_count});
707             } else {
708                 for (size_t p = 0; p < test_powerloss_count; p++) {
709                     // skip non-reentrant tests when powerloss testing
710                     if (test_powerlosses[p].run != run_powerloss_none
711                             && !(case_->flags & TEST_REENTRANT)) {
712                         continue;
713                     }
714 
715                     cb(data, suite, case_, &test_powerlosses[p]);
716                 }
717             }
718         }
719 
720         return;
721     }
722 
723     test_seen_t seen = {NULL, 0, 0};
724 
725     for (size_t k = 0; k < case_->permutations; k++) {
726         // define permutation
727         test_define_perm(suite, case_, k);
728 
729         for (size_t v = 0; v < test_override_define_permutations; v++) {
730             // define override permutation
731             test_define_override(v);
732 
733             for (size_t g = 0; g < test_geometry_count; g++) {
734                 // define geometry
735                 test_define_geometry(&test_geometries[g]);
736                 test_define_flush();
737 
738                 // have we seen this permutation before?
739                 bool was_seen = test_seen_insert(&seen, suite, case_);
740                 if (!(k == 0 && v == 0 && g == 0) && was_seen) {
741                     continue;
742                 }
743 
744                 if (cycles) {
745                     cb(data, suite, case_, &(test_powerloss_t){
746                             .run=run_powerloss_cycles,
747                             .cycles=cycles,
748                             .cycle_count=cycle_count});
749                 } else {
750                     for (size_t p = 0; p < test_powerloss_count; p++) {
751                         // skip non-reentrant tests when powerloss testing
752                         if (test_powerlosses[p].run != run_powerloss_none
753                                 && !(case_->flags & TEST_REENTRANT)) {
754                             continue;
755                         }
756 
757                         cb(data, suite, case_, &test_powerlosses[p]);
758                     }
759                 }
760             }
761         }
762     }
763 
764     test_seen_cleanup(&seen);
765 }
766 
767 
768 // how many permutations are there actually in a test case
769 struct perm_count_state {
770     size_t total;
771     size_t filtered;
772 };
773 
perm_count(void * data,const struct test_suite * suite,const struct test_case * case_,const test_powerloss_t * powerloss)774 void perm_count(
775         void *data,
776         const struct test_suite *suite,
777         const struct test_case *case_,
778         const test_powerloss_t *powerloss) {
779     struct perm_count_state *state = data;
780     (void)suite;
781     (void)case_;
782     (void)powerloss;
783 
784     state->total += 1;
785 
786     if (case_->filter && !case_->filter()) {
787         return;
788     }
789 
790     state->filtered += 1;
791 }
792 
793 
794 // operations we can do
summary(void)795 static void summary(void) {
796     printf("%-23s  %7s %7s %7s %11s\n",
797             "", "flags", "suites", "cases", "perms");
798     size_t suites = 0;
799     size_t cases = 0;
800     test_flags_t flags = 0;
801     struct perm_count_state perms = {0, 0};
802 
803     for (size_t t = 0; t < test_id_count; t++) {
804         for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
805             test_define_suite(&test_suites[i]);
806 
807             for (size_t j = 0; j < test_suites[i].case_count; j++) {
808                 // does neither suite nor case name match?
809                 if (test_ids[t].name && !(
810                         strcmp(test_ids[t].name,
811                             test_suites[i].name) == 0
812                         || strcmp(test_ids[t].name,
813                             test_suites[i].cases[j].name) == 0)) {
814                     continue;
815                 }
816 
817                 cases += 1;
818                 case_forperm(
819                         &test_suites[i],
820                         &test_suites[i].cases[j],
821                         test_ids[t].defines,
822                         test_ids[t].define_count,
823                         test_ids[t].cycles,
824                         test_ids[t].cycle_count,
825                         perm_count,
826                         &perms);
827             }
828 
829             suites += 1;
830             flags |= test_suites[i].flags;
831         }
832     }
833 
834     char perm_buf[64];
835     sprintf(perm_buf, "%zu/%zu", perms.filtered, perms.total);
836     char flag_buf[64];
837     sprintf(flag_buf, "%s%s",
838             (flags & TEST_REENTRANT) ? "r" : "",
839             (!flags) ? "-" : "");
840     printf("%-23s  %7s %7zu %7zu %11s\n",
841             "TOTAL",
842             flag_buf,
843             suites,
844             cases,
845             perm_buf);
846 }
847 
list_suites(void)848 static void list_suites(void) {
849     // at least size so that names fit
850     unsigned name_width = 23;
851     for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
852         size_t len = strlen(test_suites[i].name);
853         if (len > name_width) {
854             name_width = len;
855         }
856     }
857     name_width = 4*((name_width+1+4-1)/4)-1;
858 
859     printf("%-*s  %7s %7s %11s\n",
860             name_width, "suite", "flags", "cases", "perms");
861     for (size_t t = 0; t < test_id_count; t++) {
862         for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
863             test_define_suite(&test_suites[i]);
864 
865             size_t cases = 0;
866             struct perm_count_state perms = {0, 0};
867 
868             for (size_t j = 0; j < test_suites[i].case_count; j++) {
869                 // does neither suite nor case name match?
870                 if (test_ids[t].name && !(
871                         strcmp(test_ids[t].name,
872                             test_suites[i].name) == 0
873                         || strcmp(test_ids[t].name,
874                             test_suites[i].cases[j].name) == 0)) {
875                     continue;
876                 }
877 
878                 cases += 1;
879                 case_forperm(
880                         &test_suites[i],
881                         &test_suites[i].cases[j],
882                         test_ids[t].defines,
883                         test_ids[t].define_count,
884                         test_ids[t].cycles,
885                         test_ids[t].cycle_count,
886                         perm_count,
887                         &perms);
888             }
889 
890             // no tests found?
891             if (!cases) {
892                 continue;
893             }
894 
895             char perm_buf[64];
896             sprintf(perm_buf, "%zu/%zu", perms.filtered, perms.total);
897             char flag_buf[64];
898             sprintf(flag_buf, "%s%s",
899                     (test_suites[i].flags & TEST_REENTRANT) ? "r" : "",
900                     (!test_suites[i].flags) ? "-" : "");
901             printf("%-*s  %7s %7zu %11s\n",
902                     name_width,
903                     test_suites[i].name,
904                     flag_buf,
905                     cases,
906                     perm_buf);
907         }
908     }
909 }
910 
list_cases(void)911 static void list_cases(void) {
912     // at least size so that names fit
913     unsigned name_width = 23;
914     for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
915         for (size_t j = 0; j < test_suites[i].case_count; j++) {
916             size_t len = strlen(test_suites[i].cases[j].name);
917             if (len > name_width) {
918                 name_width = len;
919             }
920         }
921     }
922     name_width = 4*((name_width+1+4-1)/4)-1;
923 
924     printf("%-*s  %7s %11s\n", name_width, "case", "flags", "perms");
925     for (size_t t = 0; t < test_id_count; t++) {
926         for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
927             test_define_suite(&test_suites[i]);
928 
929             for (size_t j = 0; j < test_suites[i].case_count; j++) {
930                 // does neither suite nor case name match?
931                 if (test_ids[t].name && !(
932                         strcmp(test_ids[t].name,
933                             test_suites[i].name) == 0
934                         || strcmp(test_ids[t].name,
935                             test_suites[i].cases[j].name) == 0)) {
936                     continue;
937                 }
938 
939                 struct perm_count_state perms = {0, 0};
940                 case_forperm(
941                         &test_suites[i],
942                         &test_suites[i].cases[j],
943                         test_ids[t].defines,
944                         test_ids[t].define_count,
945                         test_ids[t].cycles,
946                         test_ids[t].cycle_count,
947                         perm_count,
948                         &perms);
949 
950                 char perm_buf[64];
951                 sprintf(perm_buf, "%zu/%zu", perms.filtered, perms.total);
952                 char flag_buf[64];
953                 sprintf(flag_buf, "%s%s",
954                         (test_suites[i].cases[j].flags & TEST_REENTRANT)
955                             ? "r" : "",
956                         (!test_suites[i].cases[j].flags)
957                             ? "-" : "");
958                 printf("%-*s  %7s %11s\n",
959                         name_width,
960                         test_suites[i].cases[j].name,
961                         flag_buf,
962                         perm_buf);
963             }
964         }
965     }
966 }
967 
list_suite_paths(void)968 static void list_suite_paths(void) {
969     // at least size so that names fit
970     unsigned name_width = 23;
971     for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
972         size_t len = strlen(test_suites[i].name);
973         if (len > name_width) {
974             name_width = len;
975         }
976     }
977     name_width = 4*((name_width+1+4-1)/4)-1;
978 
979     printf("%-*s  %s\n", name_width, "suite", "path");
980     for (size_t t = 0; t < test_id_count; t++) {
981         for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
982             size_t cases = 0;
983 
984             for (size_t j = 0; j < test_suites[i].case_count; j++) {
985                 // does neither suite nor case name match?
986                 if (test_ids[t].name && !(
987                         strcmp(test_ids[t].name,
988                             test_suites[i].name) == 0
989                         || strcmp(test_ids[t].name,
990                             test_suites[i].cases[j].name) == 0)) {
991                     continue;
992                 }
993 
994                 cases += 1;
995             }
996 
997             // no tests found?
998             if (!cases) {
999                 continue;
1000             }
1001 
1002             printf("%-*s  %s\n",
1003                     name_width,
1004                     test_suites[i].name,
1005                     test_suites[i].path);
1006         }
1007     }
1008 }
1009 
list_case_paths(void)1010 static void list_case_paths(void) {
1011     // at least size so that names fit
1012     unsigned name_width = 23;
1013     for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
1014         for (size_t j = 0; j < test_suites[i].case_count; j++) {
1015             size_t len = strlen(test_suites[i].cases[j].name);
1016             if (len > name_width) {
1017                 name_width = len;
1018             }
1019         }
1020     }
1021     name_width = 4*((name_width+1+4-1)/4)-1;
1022 
1023     printf("%-*s  %s\n", name_width, "case", "path");
1024     for (size_t t = 0; t < test_id_count; t++) {
1025         for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
1026             for (size_t j = 0; j < test_suites[i].case_count; j++) {
1027                 // does neither suite nor case name match?
1028                 if (test_ids[t].name && !(
1029                         strcmp(test_ids[t].name,
1030                             test_suites[i].name) == 0
1031                         || strcmp(test_ids[t].name,
1032                             test_suites[i].cases[j].name) == 0)) {
1033                     continue;
1034                 }
1035 
1036                 printf("%-*s  %s\n",
1037                         name_width,
1038                         test_suites[i].cases[j].name,
1039                         test_suites[i].cases[j].path);
1040             }
1041         }
1042     }
1043 }
1044 
1045 struct list_defines_define {
1046     const char *name;
1047     intmax_t *values;
1048     size_t value_count;
1049     size_t value_capacity;
1050 };
1051 
1052 struct list_defines_defines {
1053     struct list_defines_define *defines;
1054     size_t define_count;
1055     size_t define_capacity;
1056 };
1057 
list_defines_add(struct list_defines_defines * defines,size_t d)1058 static void list_defines_add(
1059         struct list_defines_defines *defines,
1060         size_t d) {
1061     const char *name = test_define_name(d);
1062     intmax_t value = TEST_DEFINE(d);
1063 
1064     // define already in defines?
1065     for (size_t i = 0; i < defines->define_count; i++) {
1066         if (strcmp(defines->defines[i].name, name) == 0) {
1067             // value already in values?
1068             for (size_t j = 0; j < defines->defines[i].value_count; j++) {
1069                 if (defines->defines[i].values[j] == value) {
1070                     return;
1071                 }
1072             }
1073 
1074             *(intmax_t*)mappend(
1075                 (void**)&defines->defines[i].values,
1076                 sizeof(intmax_t),
1077                 &defines->defines[i].value_count,
1078                 &defines->defines[i].value_capacity) = value;
1079 
1080             return;
1081         }
1082     }
1083 
1084     // new define?
1085     struct list_defines_define *define = mappend(
1086             (void**)&defines->defines,
1087             sizeof(struct list_defines_define),
1088             &defines->define_count,
1089             &defines->define_capacity);
1090     define->name = name;
1091     define->values = malloc(sizeof(intmax_t));
1092     define->values[0] = value;
1093     define->value_count = 1;
1094     define->value_capacity = 1;
1095 }
1096 
perm_list_defines(void * data,const struct test_suite * suite,const struct test_case * case_,const test_powerloss_t * powerloss)1097 void perm_list_defines(
1098         void *data,
1099         const struct test_suite *suite,
1100         const struct test_case *case_,
1101         const test_powerloss_t *powerloss) {
1102     struct list_defines_defines *defines = data;
1103     (void)suite;
1104     (void)case_;
1105     (void)powerloss;
1106 
1107     // collect defines
1108     for (size_t d = 0;
1109             d < lfs_max(suite->define_count,
1110                 TEST_IMPLICIT_DEFINE_COUNT);
1111             d++) {
1112         if (d < TEST_IMPLICIT_DEFINE_COUNT
1113                 || test_define_ispermutation(d)) {
1114             list_defines_add(defines, d);
1115         }
1116     }
1117 }
1118 
perm_list_permutation_defines(void * data,const struct test_suite * suite,const struct test_case * case_,const test_powerloss_t * powerloss)1119 void perm_list_permutation_defines(
1120         void *data,
1121         const struct test_suite *suite,
1122         const struct test_case *case_,
1123         const test_powerloss_t *powerloss) {
1124     struct list_defines_defines *defines = data;
1125     (void)suite;
1126     (void)case_;
1127     (void)powerloss;
1128 
1129     // collect permutation_defines
1130     for (size_t d = 0;
1131             d < lfs_max(suite->define_count,
1132                 TEST_IMPLICIT_DEFINE_COUNT);
1133             d++) {
1134         if (test_define_ispermutation(d)) {
1135             list_defines_add(defines, d);
1136         }
1137     }
1138 }
1139 
1140 extern const test_geometry_t builtin_geometries[];
1141 
list_defines(void)1142 static void list_defines(void) {
1143     struct list_defines_defines defines = {NULL, 0, 0};
1144 
1145     // add defines
1146     for (size_t t = 0; t < test_id_count; t++) {
1147         for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
1148             test_define_suite(&test_suites[i]);
1149 
1150             for (size_t j = 0; j < test_suites[i].case_count; j++) {
1151                 // does neither suite nor case name match?
1152                 if (test_ids[t].name && !(
1153                         strcmp(test_ids[t].name,
1154                             test_suites[i].name) == 0
1155                         || strcmp(test_ids[t].name,
1156                             test_suites[i].cases[j].name) == 0)) {
1157                     continue;
1158                 }
1159 
1160                 case_forperm(
1161                         &test_suites[i],
1162                         &test_suites[i].cases[j],
1163                         test_ids[t].defines,
1164                         test_ids[t].define_count,
1165                         test_ids[t].cycles,
1166                         test_ids[t].cycle_count,
1167                         perm_list_defines,
1168                         &defines);
1169             }
1170         }
1171     }
1172 
1173     for (size_t i = 0; i < defines.define_count; i++) {
1174         printf("%s=", defines.defines[i].name);
1175         for (size_t j = 0; j < defines.defines[i].value_count; j++) {
1176             printf("%jd", defines.defines[i].values[j]);
1177             if (j != defines.defines[i].value_count-1) {
1178                 printf(",");
1179             }
1180         }
1181         printf("\n");
1182     }
1183 
1184     for (size_t i = 0; i < defines.define_count; i++) {
1185         free(defines.defines[i].values);
1186     }
1187     free(defines.defines);
1188 }
1189 
list_permutation_defines(void)1190 static void list_permutation_defines(void) {
1191     struct list_defines_defines defines = {NULL, 0, 0};
1192 
1193     // add permutation defines
1194     for (size_t t = 0; t < test_id_count; t++) {
1195         for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
1196             test_define_suite(&test_suites[i]);
1197 
1198             for (size_t j = 0; j < test_suites[i].case_count; j++) {
1199                 // does neither suite nor case name match?
1200                 if (test_ids[t].name && !(
1201                         strcmp(test_ids[t].name,
1202                             test_suites[i].name) == 0
1203                         || strcmp(test_ids[t].name,
1204                             test_suites[i].cases[j].name) == 0)) {
1205                     continue;
1206                 }
1207 
1208                 case_forperm(
1209                         &test_suites[i],
1210                         &test_suites[i].cases[j],
1211                         test_ids[t].defines,
1212                         test_ids[t].define_count,
1213                         test_ids[t].cycles,
1214                         test_ids[t].cycle_count,
1215                         perm_list_permutation_defines,
1216                         &defines);
1217             }
1218         }
1219     }
1220 
1221     for (size_t i = 0; i < defines.define_count; i++) {
1222         printf("%s=", defines.defines[i].name);
1223         for (size_t j = 0; j < defines.defines[i].value_count; j++) {
1224             printf("%jd", defines.defines[i].values[j]);
1225             if (j != defines.defines[i].value_count-1) {
1226                 printf(",");
1227             }
1228         }
1229         printf("\n");
1230     }
1231 
1232     for (size_t i = 0; i < defines.define_count; i++) {
1233         free(defines.defines[i].values);
1234     }
1235     free(defines.defines);
1236 }
1237 
list_implicit_defines(void)1238 static void list_implicit_defines(void) {
1239     struct list_defines_defines defines = {NULL, 0, 0};
1240 
1241     // yes we do need to define a suite, this does a bit of bookeeping
1242     // such as setting up the define cache
1243     test_define_suite(&(const struct test_suite){0});
1244 
1245     // make sure to include builtin geometries here
1246     extern const test_geometry_t builtin_geometries[];
1247     for (size_t g = 0; builtin_geometries[g].name; g++) {
1248         test_define_geometry(&builtin_geometries[g]);
1249         test_define_flush();
1250 
1251         // add implicit defines
1252         for (size_t d = 0; d < TEST_IMPLICIT_DEFINE_COUNT; d++) {
1253             list_defines_add(&defines, d);
1254         }
1255     }
1256 
1257     for (size_t i = 0; i < defines.define_count; i++) {
1258         printf("%s=", defines.defines[i].name);
1259         for (size_t j = 0; j < defines.defines[i].value_count; j++) {
1260             printf("%jd", defines.defines[i].values[j]);
1261             if (j != defines.defines[i].value_count-1) {
1262                 printf(",");
1263             }
1264         }
1265         printf("\n");
1266     }
1267 
1268     for (size_t i = 0; i < defines.define_count; i++) {
1269         free(defines.defines[i].values);
1270     }
1271     free(defines.defines);
1272 }
1273 
1274 
1275 
1276 // geometries to test
1277 
1278 const test_geometry_t builtin_geometries[] = {
1279     {"default", {{0}, TEST_CONST(16),   TEST_CONST(512),   {0}}},
1280     {"eeprom",  {{0}, TEST_CONST(1),    TEST_CONST(512),   {0}}},
1281     {"emmc",    {{0}, {0},              TEST_CONST(512),   {0}}},
1282     {"nor",     {{0}, TEST_CONST(1),    TEST_CONST(4096),  {0}}},
1283     {"nand",    {{0}, TEST_CONST(4096), TEST_CONST(32768), {0}}},
1284     {NULL, {{0}, {0}, {0}, {0}}},
1285 };
1286 
1287 const test_geometry_t *test_geometries = builtin_geometries;
1288 size_t test_geometry_count = 5;
1289 
list_geometries(void)1290 static void list_geometries(void) {
1291     // at least size so that names fit
1292     unsigned name_width = 23;
1293     for (size_t g = 0; builtin_geometries[g].name; g++) {
1294         size_t len = strlen(builtin_geometries[g].name);
1295         if (len > name_width) {
1296             name_width = len;
1297         }
1298     }
1299     name_width = 4*((name_width+1+4-1)/4)-1;
1300 
1301     // yes we do need to define a suite, this does a bit of bookeeping
1302     // such as setting up the define cache
1303     test_define_suite(&(const struct test_suite){0});
1304 
1305     printf("%-*s  %7s %7s %7s %7s %11s\n",
1306             name_width, "geometry", "read", "prog", "erase", "count", "size");
1307     for (size_t g = 0; builtin_geometries[g].name; g++) {
1308         test_define_geometry(&builtin_geometries[g]);
1309         test_define_flush();
1310         printf("%-*s  %7ju %7ju %7ju %7ju %11ju\n",
1311                 name_width,
1312                 builtin_geometries[g].name,
1313                 READ_SIZE,
1314                 PROG_SIZE,
1315                 ERASE_SIZE,
1316                 ERASE_COUNT,
1317                 ERASE_SIZE*ERASE_COUNT);
1318     }
1319 }
1320 
1321 
1322 // scenarios to run tests under power-loss
1323 
run_powerloss_none(const lfs_emubd_powercycles_t * cycles,size_t cycle_count,const struct test_suite * suite,const struct test_case * case_)1324 static void run_powerloss_none(
1325         const lfs_emubd_powercycles_t *cycles,
1326         size_t cycle_count,
1327         const struct test_suite *suite,
1328         const struct test_case *case_) {
1329     (void)cycles;
1330     (void)cycle_count;
1331     (void)suite;
1332 
1333     // create block device and configuration
1334     lfs_emubd_t bd;
1335 
1336     struct lfs_config cfg = {
1337         .context            = &bd,
1338         .read               = lfs_emubd_read,
1339         .prog               = lfs_emubd_prog,
1340         .erase              = lfs_emubd_erase,
1341         .sync               = lfs_emubd_sync,
1342         .read_size          = READ_SIZE,
1343         .prog_size          = PROG_SIZE,
1344         .block_size         = BLOCK_SIZE,
1345         .block_count        = BLOCK_COUNT,
1346         .block_cycles       = BLOCK_CYCLES,
1347         .cache_size         = CACHE_SIZE,
1348         .lookahead_size     = LOOKAHEAD_SIZE,
1349     #ifdef LFS_MULTIVERSION
1350         .disk_version       = DISK_VERSION,
1351     #endif
1352     };
1353 
1354     struct lfs_emubd_config bdcfg = {
1355         .read_size          = READ_SIZE,
1356         .prog_size          = PROG_SIZE,
1357         .erase_size         = ERASE_SIZE,
1358         .erase_count        = ERASE_COUNT,
1359         .erase_value        = ERASE_VALUE,
1360         .erase_cycles       = ERASE_CYCLES,
1361         .badblock_behavior  = BADBLOCK_BEHAVIOR,
1362         .disk_path          = test_disk_path,
1363         .read_sleep         = test_read_sleep,
1364         .prog_sleep         = test_prog_sleep,
1365         .erase_sleep        = test_erase_sleep,
1366     };
1367 
1368     int err = lfs_emubd_create(&cfg, &bdcfg);
1369     if (err) {
1370         fprintf(stderr, "error: could not create block device: %d\n", err);
1371         exit(-1);
1372     }
1373 
1374     // run the test
1375     printf("running ");
1376     perm_printid(suite, case_, NULL, 0);
1377     printf("\n");
1378 
1379     case_->run(&cfg);
1380 
1381     printf("finished ");
1382     perm_printid(suite, case_, NULL, 0);
1383     printf("\n");
1384 
1385     // cleanup
1386     err = lfs_emubd_destroy(&cfg);
1387     if (err) {
1388         fprintf(stderr, "error: could not destroy block device: %d\n", err);
1389         exit(-1);
1390     }
1391 }
1392 
powerloss_longjmp(void * c)1393 static void powerloss_longjmp(void *c) {
1394     jmp_buf *powerloss_jmp = c;
1395     longjmp(*powerloss_jmp, 1);
1396 }
1397 
run_powerloss_linear(const lfs_emubd_powercycles_t * cycles,size_t cycle_count,const struct test_suite * suite,const struct test_case * case_)1398 static void run_powerloss_linear(
1399         const lfs_emubd_powercycles_t *cycles,
1400         size_t cycle_count,
1401         const struct test_suite *suite,
1402         const struct test_case *case_) {
1403     (void)cycles;
1404     (void)cycle_count;
1405     (void)suite;
1406 
1407     // create block device and configuration
1408     lfs_emubd_t bd;
1409     jmp_buf powerloss_jmp;
1410     volatile lfs_emubd_powercycles_t i = 1;
1411 
1412     struct lfs_config cfg = {
1413         .context            = &bd,
1414         .read               = lfs_emubd_read,
1415         .prog               = lfs_emubd_prog,
1416         .erase              = lfs_emubd_erase,
1417         .sync               = lfs_emubd_sync,
1418         .read_size          = READ_SIZE,
1419         .prog_size          = PROG_SIZE,
1420         .block_size         = BLOCK_SIZE,
1421         .block_count        = BLOCK_COUNT,
1422         .block_cycles       = BLOCK_CYCLES,
1423         .cache_size         = CACHE_SIZE,
1424         .lookahead_size     = LOOKAHEAD_SIZE,
1425     #ifdef LFS_MULTIVERSION
1426         .disk_version       = DISK_VERSION,
1427     #endif
1428     };
1429 
1430     struct lfs_emubd_config bdcfg = {
1431         .read_size          = READ_SIZE,
1432         .prog_size          = PROG_SIZE,
1433         .erase_size         = ERASE_SIZE,
1434         .erase_count        = ERASE_COUNT,
1435         .erase_value        = ERASE_VALUE,
1436         .erase_cycles       = ERASE_CYCLES,
1437         .badblock_behavior  = BADBLOCK_BEHAVIOR,
1438         .disk_path          = test_disk_path,
1439         .read_sleep         = test_read_sleep,
1440         .prog_sleep         = test_prog_sleep,
1441         .erase_sleep        = test_erase_sleep,
1442         .power_cycles       = i,
1443         .powerloss_behavior = POWERLOSS_BEHAVIOR,
1444         .powerloss_cb       = powerloss_longjmp,
1445         .powerloss_data     = &powerloss_jmp,
1446     };
1447 
1448     int err = lfs_emubd_create(&cfg, &bdcfg);
1449     if (err) {
1450         fprintf(stderr, "error: could not create block device: %d\n", err);
1451         exit(-1);
1452     }
1453 
1454     // run the test, increasing power-cycles as power-loss events occur
1455     printf("running ");
1456     perm_printid(suite, case_, NULL, 0);
1457     printf("\n");
1458 
1459     while (true) {
1460         if (!setjmp(powerloss_jmp)) {
1461             // run the test
1462             case_->run(&cfg);
1463             break;
1464         }
1465 
1466         // power-loss!
1467         printf("powerloss ");
1468         perm_printid(suite, case_, NULL, 0);
1469         printf(":");
1470         for (lfs_emubd_powercycles_t j = 1; j <= i; j++) {
1471             leb16_print(j);
1472         }
1473         printf("\n");
1474 
1475         i += 1;
1476         lfs_emubd_setpowercycles(&cfg, i);
1477     }
1478 
1479     printf("finished ");
1480     perm_printid(suite, case_, NULL, 0);
1481     printf("\n");
1482 
1483     // cleanup
1484     err = lfs_emubd_destroy(&cfg);
1485     if (err) {
1486         fprintf(stderr, "error: could not destroy block device: %d\n", err);
1487         exit(-1);
1488     }
1489 }
1490 
run_powerloss_log(const lfs_emubd_powercycles_t * cycles,size_t cycle_count,const struct test_suite * suite,const struct test_case * case_)1491 static void run_powerloss_log(
1492         const lfs_emubd_powercycles_t *cycles,
1493         size_t cycle_count,
1494         const struct test_suite *suite,
1495         const struct test_case *case_) {
1496     (void)cycles;
1497     (void)cycle_count;
1498     (void)suite;
1499 
1500     // create block device and configuration
1501     lfs_emubd_t bd;
1502     jmp_buf powerloss_jmp;
1503     volatile lfs_emubd_powercycles_t i = 1;
1504 
1505     struct lfs_config cfg = {
1506         .context            = &bd,
1507         .read               = lfs_emubd_read,
1508         .prog               = lfs_emubd_prog,
1509         .erase              = lfs_emubd_erase,
1510         .sync               = lfs_emubd_sync,
1511         .read_size          = READ_SIZE,
1512         .prog_size          = PROG_SIZE,
1513         .block_size         = BLOCK_SIZE,
1514         .block_count        = BLOCK_COUNT,
1515         .block_cycles       = BLOCK_CYCLES,
1516         .cache_size         = CACHE_SIZE,
1517         .lookahead_size     = LOOKAHEAD_SIZE,
1518     #ifdef LFS_MULTIVERSION
1519         .disk_version       = DISK_VERSION,
1520     #endif
1521     };
1522 
1523     struct lfs_emubd_config bdcfg = {
1524         .read_size          = READ_SIZE,
1525         .prog_size          = PROG_SIZE,
1526         .erase_size         = ERASE_SIZE,
1527         .erase_count        = ERASE_COUNT,
1528         .erase_value        = ERASE_VALUE,
1529         .erase_cycles       = ERASE_CYCLES,
1530         .badblock_behavior  = BADBLOCK_BEHAVIOR,
1531         .disk_path          = test_disk_path,
1532         .read_sleep         = test_read_sleep,
1533         .prog_sleep         = test_prog_sleep,
1534         .erase_sleep        = test_erase_sleep,
1535         .power_cycles       = i,
1536         .powerloss_behavior = POWERLOSS_BEHAVIOR,
1537         .powerloss_cb       = powerloss_longjmp,
1538         .powerloss_data     = &powerloss_jmp,
1539     };
1540 
1541     int err = lfs_emubd_create(&cfg, &bdcfg);
1542     if (err) {
1543         fprintf(stderr, "error: could not create block device: %d\n", err);
1544         exit(-1);
1545     }
1546 
1547     // run the test, increasing power-cycles as power-loss events occur
1548     printf("running ");
1549     perm_printid(suite, case_, NULL, 0);
1550     printf("\n");
1551 
1552     while (true) {
1553         if (!setjmp(powerloss_jmp)) {
1554             // run the test
1555             case_->run(&cfg);
1556             break;
1557         }
1558 
1559         // power-loss!
1560         printf("powerloss ");
1561         perm_printid(suite, case_, NULL, 0);
1562         printf(":");
1563         for (lfs_emubd_powercycles_t j = 1; j <= i; j *= 2) {
1564             leb16_print(j);
1565         }
1566         printf("\n");
1567 
1568         i *= 2;
1569         lfs_emubd_setpowercycles(&cfg, i);
1570     }
1571 
1572     printf("finished ");
1573     perm_printid(suite, case_, NULL, 0);
1574     printf("\n");
1575 
1576     // cleanup
1577     err = lfs_emubd_destroy(&cfg);
1578     if (err) {
1579         fprintf(stderr, "error: could not destroy block device: %d\n", err);
1580         exit(-1);
1581     }
1582 }
1583 
run_powerloss_cycles(const lfs_emubd_powercycles_t * cycles,size_t cycle_count,const struct test_suite * suite,const struct test_case * case_)1584 static void run_powerloss_cycles(
1585         const lfs_emubd_powercycles_t *cycles,
1586         size_t cycle_count,
1587         const struct test_suite *suite,
1588         const struct test_case *case_) {
1589     (void)suite;
1590 
1591     // create block device and configuration
1592     lfs_emubd_t bd;
1593     jmp_buf powerloss_jmp;
1594     volatile size_t i = 0;
1595 
1596     struct lfs_config cfg = {
1597         .context            = &bd,
1598         .read               = lfs_emubd_read,
1599         .prog               = lfs_emubd_prog,
1600         .erase              = lfs_emubd_erase,
1601         .sync               = lfs_emubd_sync,
1602         .read_size          = READ_SIZE,
1603         .prog_size          = PROG_SIZE,
1604         .block_size         = BLOCK_SIZE,
1605         .block_count        = BLOCK_COUNT,
1606         .block_cycles       = BLOCK_CYCLES,
1607         .cache_size         = CACHE_SIZE,
1608         .lookahead_size     = LOOKAHEAD_SIZE,
1609     #ifdef LFS_MULTIVERSION
1610         .disk_version       = DISK_VERSION,
1611     #endif
1612     };
1613 
1614     struct lfs_emubd_config bdcfg = {
1615         .read_size          = READ_SIZE,
1616         .prog_size          = PROG_SIZE,
1617         .erase_size         = ERASE_SIZE,
1618         .erase_count        = ERASE_COUNT,
1619         .erase_value        = ERASE_VALUE,
1620         .erase_cycles       = ERASE_CYCLES,
1621         .badblock_behavior  = BADBLOCK_BEHAVIOR,
1622         .disk_path          = test_disk_path,
1623         .read_sleep         = test_read_sleep,
1624         .prog_sleep         = test_prog_sleep,
1625         .erase_sleep        = test_erase_sleep,
1626         .power_cycles       = (i < cycle_count) ? cycles[i] : 0,
1627         .powerloss_behavior = POWERLOSS_BEHAVIOR,
1628         .powerloss_cb       = powerloss_longjmp,
1629         .powerloss_data     = &powerloss_jmp,
1630     };
1631 
1632     int err = lfs_emubd_create(&cfg, &bdcfg);
1633     if (err) {
1634         fprintf(stderr, "error: could not create block device: %d\n", err);
1635         exit(-1);
1636     }
1637 
1638     // run the test, increasing power-cycles as power-loss events occur
1639     printf("running ");
1640     perm_printid(suite, case_, NULL, 0);
1641     printf("\n");
1642 
1643     while (true) {
1644         if (!setjmp(powerloss_jmp)) {
1645             // run the test
1646             case_->run(&cfg);
1647             break;
1648         }
1649 
1650         // power-loss!
1651         assert(i <= cycle_count);
1652         printf("powerloss ");
1653         perm_printid(suite, case_, cycles, i+1);
1654         printf("\n");
1655 
1656         i += 1;
1657         lfs_emubd_setpowercycles(&cfg,
1658                 (i < cycle_count) ? cycles[i] : 0);
1659     }
1660 
1661     printf("finished ");
1662     perm_printid(suite, case_, NULL, 0);
1663     printf("\n");
1664 
1665     // cleanup
1666     err = lfs_emubd_destroy(&cfg);
1667     if (err) {
1668         fprintf(stderr, "error: could not destroy block device: %d\n", err);
1669         exit(-1);
1670     }
1671 }
1672 
1673 struct powerloss_exhaustive_state {
1674     struct lfs_config *cfg;
1675 
1676     lfs_emubd_t *branches;
1677     size_t branch_count;
1678     size_t branch_capacity;
1679 };
1680 
1681 struct powerloss_exhaustive_cycles {
1682     lfs_emubd_powercycles_t *cycles;
1683     size_t cycle_count;
1684     size_t cycle_capacity;
1685 };
1686 
powerloss_exhaustive_branch(void * c)1687 static void powerloss_exhaustive_branch(void *c) {
1688     struct powerloss_exhaustive_state *state = c;
1689     // append to branches
1690     lfs_emubd_t *branch = mappend(
1691             (void**)&state->branches,
1692             sizeof(lfs_emubd_t),
1693             &state->branch_count,
1694             &state->branch_capacity);
1695     if (!branch) {
1696         fprintf(stderr, "error: exhaustive: out of memory\n");
1697         exit(-1);
1698     }
1699 
1700     // create copy-on-write copy
1701     int err = lfs_emubd_copy(state->cfg, branch);
1702     if (err) {
1703         fprintf(stderr, "error: exhaustive: could not create bd copy\n");
1704         exit(-1);
1705     }
1706 
1707     // also trigger on next power cycle
1708     lfs_emubd_setpowercycles(state->cfg, 1);
1709 }
1710 
run_powerloss_exhaustive_layer(struct powerloss_exhaustive_cycles * cycles,const struct test_suite * suite,const struct test_case * case_,struct lfs_config * cfg,struct lfs_emubd_config * bdcfg,size_t depth)1711 static void run_powerloss_exhaustive_layer(
1712         struct powerloss_exhaustive_cycles *cycles,
1713         const struct test_suite *suite,
1714         const struct test_case *case_,
1715         struct lfs_config *cfg,
1716         struct lfs_emubd_config *bdcfg,
1717         size_t depth) {
1718     (void)suite;
1719 
1720     struct powerloss_exhaustive_state state = {
1721         .cfg = cfg,
1722         .branches = NULL,
1723         .branch_count = 0,
1724         .branch_capacity = 0,
1725     };
1726 
1727     // run through the test without additional powerlosses, collecting possible
1728     // branches as we do so
1729     lfs_emubd_setpowercycles(state.cfg, depth > 0 ? 1 : 0);
1730     bdcfg->powerloss_data = &state;
1731 
1732     // run the tests
1733     case_->run(cfg);
1734 
1735     // aggressively clean up memory here to try to keep our memory usage low
1736     int err = lfs_emubd_destroy(cfg);
1737     if (err) {
1738         fprintf(stderr, "error: could not destroy block device: %d\n", err);
1739         exit(-1);
1740     }
1741 
1742     // recurse into each branch
1743     for (size_t i = 0; i < state.branch_count; i++) {
1744         // first push and print the branch
1745         lfs_emubd_powercycles_t *cycle = mappend(
1746                 (void**)&cycles->cycles,
1747                 sizeof(lfs_emubd_powercycles_t),
1748                 &cycles->cycle_count,
1749                 &cycles->cycle_capacity);
1750         if (!cycle) {
1751             fprintf(stderr, "error: exhaustive: out of memory\n");
1752             exit(-1);
1753         }
1754         *cycle = i+1;
1755 
1756         printf("powerloss ");
1757         perm_printid(suite, case_, cycles->cycles, cycles->cycle_count);
1758         printf("\n");
1759 
1760         // now recurse
1761         cfg->context = &state.branches[i];
1762         run_powerloss_exhaustive_layer(cycles,
1763                 suite, case_,
1764                 cfg, bdcfg, depth-1);
1765 
1766         // pop the cycle
1767         cycles->cycle_count -= 1;
1768     }
1769 
1770     // clean up memory
1771     free(state.branches);
1772 }
1773 
run_powerloss_exhaustive(const lfs_emubd_powercycles_t * cycles,size_t cycle_count,const struct test_suite * suite,const struct test_case * case_)1774 static void run_powerloss_exhaustive(
1775         const lfs_emubd_powercycles_t *cycles,
1776         size_t cycle_count,
1777         const struct test_suite *suite,
1778         const struct test_case *case_) {
1779     (void)cycles;
1780     (void)suite;
1781 
1782     // create block device and configuration
1783     lfs_emubd_t bd;
1784 
1785     struct lfs_config cfg = {
1786         .context            = &bd,
1787         .read               = lfs_emubd_read,
1788         .prog               = lfs_emubd_prog,
1789         .erase              = lfs_emubd_erase,
1790         .sync               = lfs_emubd_sync,
1791         .read_size          = READ_SIZE,
1792         .prog_size          = PROG_SIZE,
1793         .block_size         = BLOCK_SIZE,
1794         .block_count        = BLOCK_COUNT,
1795         .block_cycles       = BLOCK_CYCLES,
1796         .cache_size         = CACHE_SIZE,
1797         .lookahead_size     = LOOKAHEAD_SIZE,
1798     #ifdef LFS_MULTIVERSION
1799         .disk_version       = DISK_VERSION,
1800     #endif
1801     };
1802 
1803     struct lfs_emubd_config bdcfg = {
1804         .read_size          = READ_SIZE,
1805         .prog_size          = PROG_SIZE,
1806         .erase_size         = ERASE_SIZE,
1807         .erase_count        = ERASE_COUNT,
1808         .erase_value        = ERASE_VALUE,
1809         .erase_cycles       = ERASE_CYCLES,
1810         .badblock_behavior  = BADBLOCK_BEHAVIOR,
1811         .disk_path          = test_disk_path,
1812         .read_sleep         = test_read_sleep,
1813         .prog_sleep         = test_prog_sleep,
1814         .erase_sleep        = test_erase_sleep,
1815         .powerloss_behavior = POWERLOSS_BEHAVIOR,
1816         .powerloss_cb       = powerloss_exhaustive_branch,
1817         .powerloss_data     = NULL,
1818     };
1819 
1820     int err = lfs_emubd_create(&cfg, &bdcfg);
1821     if (err) {
1822         fprintf(stderr, "error: could not create block device: %d\n", err);
1823         exit(-1);
1824     }
1825 
1826     // run the test, increasing power-cycles as power-loss events occur
1827     printf("running ");
1828     perm_printid(suite, case_, NULL, 0);
1829     printf("\n");
1830 
1831     // recursively exhaust each layer of powerlosses
1832     run_powerloss_exhaustive_layer(
1833             &(struct powerloss_exhaustive_cycles){NULL, 0, 0},
1834             suite, case_,
1835             &cfg, &bdcfg, cycle_count);
1836 
1837     printf("finished ");
1838     perm_printid(suite, case_, NULL, 0);
1839     printf("\n");
1840 }
1841 
1842 
1843 const test_powerloss_t builtin_powerlosses[] = {
1844     {"none",       run_powerloss_none,       NULL, 0},
1845     {"log",        run_powerloss_log,        NULL, 0},
1846     {"linear",     run_powerloss_linear,     NULL, 0},
1847     {"exhaustive", run_powerloss_exhaustive, NULL, SIZE_MAX},
1848     {NULL, NULL, NULL, 0},
1849 };
1850 
1851 const char *const builtin_powerlosses_help[] = {
1852     "Run with no power-losses.",
1853     "Run with exponentially-decreasing power-losses.",
1854     "Run with linearly-decreasing power-losses.",
1855     "Run a all permutations of power-losses, this may take a while.",
1856     "Run a all permutations of n power-losses.",
1857     "Run a custom comma-separated set of power-losses.",
1858     "Run a custom leb16-encoded set of power-losses.",
1859 };
1860 
1861 // default to -Pnone,linear, which provides a good heuristic while still
1862 // running quickly
1863 const test_powerloss_t *test_powerlosses = (const test_powerloss_t[]){
1864     {"none",   run_powerloss_none,   NULL, 0},
1865     {"linear", run_powerloss_linear, NULL, 0},
1866 };
1867 size_t test_powerloss_count = 2;
1868 
list_powerlosses(void)1869 static void list_powerlosses(void) {
1870     // at least size so that names fit
1871     unsigned name_width = 23;
1872     for (size_t i = 0; builtin_powerlosses[i].name; i++) {
1873         size_t len = strlen(builtin_powerlosses[i].name);
1874         if (len > name_width) {
1875             name_width = len;
1876         }
1877     }
1878     name_width = 4*((name_width+1+4-1)/4)-1;
1879 
1880     printf("%-*s %s\n", name_width, "scenario", "description");
1881     size_t i = 0;
1882     for (; builtin_powerlosses[i].name; i++) {
1883         printf("%-*s %s\n",
1884                 name_width,
1885                 builtin_powerlosses[i].name,
1886                 builtin_powerlosses_help[i]);
1887     }
1888 
1889     // a couple more options with special parsing
1890     printf("%-*s %s\n", name_width, "1,2,3",   builtin_powerlosses_help[i+0]);
1891     printf("%-*s %s\n", name_width, "{1,2,3}", builtin_powerlosses_help[i+1]);
1892     printf("%-*s %s\n", name_width, ":1248g1", builtin_powerlosses_help[i+2]);
1893 }
1894 
1895 
1896 // global test step count
1897 size_t test_step = 0;
1898 
perm_run(void * data,const struct test_suite * suite,const struct test_case * case_,const test_powerloss_t * powerloss)1899 void perm_run(
1900         void *data,
1901         const struct test_suite *suite,
1902         const struct test_case *case_,
1903         const test_powerloss_t *powerloss) {
1904     (void)data;
1905 
1906     // skip this step?
1907     if (!(test_step >= test_step_start
1908             && test_step < test_step_stop
1909             && (test_step-test_step_start) % test_step_step == 0)) {
1910         test_step += 1;
1911         return;
1912     }
1913     test_step += 1;
1914 
1915     // filter?
1916     if (case_->filter && !case_->filter()) {
1917         printf("skipped ");
1918         perm_printid(suite, case_, NULL, 0);
1919         printf("\n");
1920         return;
1921     }
1922 
1923     powerloss->run(
1924             powerloss->cycles, powerloss->cycle_count,
1925             suite, case_);
1926 }
1927 
run(void)1928 static void run(void) {
1929     // ignore disconnected pipes
1930     signal(SIGPIPE, SIG_IGN);
1931 
1932     for (size_t t = 0; t < test_id_count; t++) {
1933         for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
1934             test_define_suite(&test_suites[i]);
1935 
1936             for (size_t j = 0; j < test_suites[i].case_count; j++) {
1937                 // does neither suite nor case name match?
1938                 if (test_ids[t].name && !(
1939                         strcmp(test_ids[t].name,
1940                             test_suites[i].name) == 0
1941                         || strcmp(test_ids[t].name,
1942                             test_suites[i].cases[j].name) == 0)) {
1943                     continue;
1944                 }
1945 
1946                 case_forperm(
1947                         &test_suites[i],
1948                         &test_suites[i].cases[j],
1949                         test_ids[t].defines,
1950                         test_ids[t].define_count,
1951                         test_ids[t].cycles,
1952                         test_ids[t].cycle_count,
1953                         perm_run,
1954                         NULL);
1955             }
1956         }
1957     }
1958 }
1959 
1960 
1961 
1962 // option handling
1963 enum opt_flags {
1964     OPT_HELP                     = 'h',
1965     OPT_SUMMARY                  = 'Y',
1966     OPT_LIST_SUITES              = 'l',
1967     OPT_LIST_CASES               = 'L',
1968     OPT_LIST_SUITE_PATHS         = 1,
1969     OPT_LIST_CASE_PATHS          = 2,
1970     OPT_LIST_DEFINES             = 3,
1971     OPT_LIST_PERMUTATION_DEFINES = 4,
1972     OPT_LIST_IMPLICIT_DEFINES    = 5,
1973     OPT_LIST_GEOMETRIES          = 6,
1974     OPT_LIST_POWERLOSSES         = 7,
1975     OPT_DEFINE                   = 'D',
1976     OPT_GEOMETRY                 = 'G',
1977     OPT_POWERLOSS                = 'P',
1978     OPT_STEP                     = 's',
1979     OPT_DISK                     = 'd',
1980     OPT_TRACE                    = 't',
1981     OPT_TRACE_BACKTRACE          = 8,
1982     OPT_TRACE_PERIOD             = 9,
1983     OPT_TRACE_FREQ               = 10,
1984     OPT_READ_SLEEP               = 11,
1985     OPT_PROG_SLEEP               = 12,
1986     OPT_ERASE_SLEEP              = 13,
1987 };
1988 
1989 const char *short_opts = "hYlLD:G:P:s:d:t:";
1990 
1991 const struct option long_opts[] = {
1992     {"help",             no_argument,       NULL, OPT_HELP},
1993     {"summary",          no_argument,       NULL, OPT_SUMMARY},
1994     {"list-suites",      no_argument,       NULL, OPT_LIST_SUITES},
1995     {"list-cases",       no_argument,       NULL, OPT_LIST_CASES},
1996     {"list-suite-paths", no_argument,       NULL, OPT_LIST_SUITE_PATHS},
1997     {"list-case-paths",  no_argument,       NULL, OPT_LIST_CASE_PATHS},
1998     {"list-defines",     no_argument,       NULL, OPT_LIST_DEFINES},
1999     {"list-permutation-defines",
2000                          no_argument,       NULL, OPT_LIST_PERMUTATION_DEFINES},
2001     {"list-implicit-defines",
2002                          no_argument,       NULL, OPT_LIST_IMPLICIT_DEFINES},
2003     {"list-geometries",  no_argument,       NULL, OPT_LIST_GEOMETRIES},
2004     {"list-powerlosses", no_argument,       NULL, OPT_LIST_POWERLOSSES},
2005     {"define",           required_argument, NULL, OPT_DEFINE},
2006     {"geometry",         required_argument, NULL, OPT_GEOMETRY},
2007     {"powerloss",        required_argument, NULL, OPT_POWERLOSS},
2008     {"step",             required_argument, NULL, OPT_STEP},
2009     {"disk",             required_argument, NULL, OPT_DISK},
2010     {"trace",            required_argument, NULL, OPT_TRACE},
2011     {"trace-backtrace",  no_argument,       NULL, OPT_TRACE_BACKTRACE},
2012     {"trace-period",     required_argument, NULL, OPT_TRACE_PERIOD},
2013     {"trace-freq",       required_argument, NULL, OPT_TRACE_FREQ},
2014     {"read-sleep",       required_argument, NULL, OPT_READ_SLEEP},
2015     {"prog-sleep",       required_argument, NULL, OPT_PROG_SLEEP},
2016     {"erase-sleep",      required_argument, NULL, OPT_ERASE_SLEEP},
2017     {NULL, 0, NULL, 0},
2018 };
2019 
2020 const char *const help_text[] = {
2021     "Show this help message.",
2022     "Show quick summary.",
2023     "List test suites.",
2024     "List test cases.",
2025     "List the path for each test suite.",
2026     "List the path and line number for each test case.",
2027     "List all defines in this test-runner.",
2028     "List explicit defines in this test-runner.",
2029     "List implicit defines in this test-runner.",
2030     "List the available disk geometries.",
2031     "List the available power-loss scenarios.",
2032     "Override a test define.",
2033     "Comma-separated list of disk geometries to test.",
2034     "Comma-separated list of power-loss scenarios to test.",
2035     "Comma-separated range of test permutations to run (start,stop,step).",
2036     "Direct block device operations to this file.",
2037     "Direct trace output to this file.",
2038     "Include a backtrace with every trace statement.",
2039     "Sample trace output at this period in cycles.",
2040     "Sample trace output at this frequency in hz.",
2041     "Artificial read delay in seconds.",
2042     "Artificial prog delay in seconds.",
2043     "Artificial erase delay in seconds.",
2044 };
2045 
main(int argc,char ** argv)2046 int main(int argc, char **argv) {
2047     void (*op)(void) = run;
2048 
2049     size_t test_override_capacity = 0;
2050     size_t test_geometry_capacity = 0;
2051     size_t test_powerloss_capacity = 0;
2052     size_t test_id_capacity = 0;
2053 
2054     // parse options
2055     while (true) {
2056         int c = getopt_long(argc, argv, short_opts, long_opts, NULL);
2057         switch (c) {
2058             // generate help message
2059             case OPT_HELP: {
2060                 printf("usage: %s [options] [test_id]\n", argv[0]);
2061                 printf("\n");
2062 
2063                 printf("options:\n");
2064                 size_t i = 0;
2065                 while (long_opts[i].name) {
2066                     size_t indent;
2067                     if (long_opts[i].has_arg == no_argument) {
2068                         if (long_opts[i].val >= '0' && long_opts[i].val < 'z') {
2069                             indent = printf("  -%c, --%s ",
2070                                     long_opts[i].val,
2071                                     long_opts[i].name);
2072                         } else {
2073                             indent = printf("  --%s ",
2074                                     long_opts[i].name);
2075                         }
2076                     } else {
2077                         if (long_opts[i].val >= '0' && long_opts[i].val < 'z') {
2078                             indent = printf("  -%c %s, --%s %s ",
2079                                     long_opts[i].val,
2080                                     long_opts[i].name,
2081                                     long_opts[i].name,
2082                                     long_opts[i].name);
2083                         } else {
2084                             indent = printf("  --%s %s ",
2085                                     long_opts[i].name,
2086                                     long_opts[i].name);
2087                         }
2088                     }
2089 
2090                     // a quick, hacky, byte-level method for text wrapping
2091                     size_t len = strlen(help_text[i]);
2092                     size_t j = 0;
2093                     if (indent < 24) {
2094                         printf("%*s %.80s\n",
2095                                 (int)(24-1-indent),
2096                                 "",
2097                                 &help_text[i][j]);
2098                         j += 80;
2099                     } else {
2100                         printf("\n");
2101                     }
2102 
2103                     while (j < len) {
2104                         printf("%24s%.80s\n", "", &help_text[i][j]);
2105                         j += 80;
2106                     }
2107 
2108                     i += 1;
2109                 }
2110 
2111                 printf("\n");
2112                 exit(0);
2113             }
2114             // summary/list flags
2115             case OPT_SUMMARY:
2116                 op = summary;
2117                 break;
2118             case OPT_LIST_SUITES:
2119                 op = list_suites;
2120                 break;
2121             case OPT_LIST_CASES:
2122                 op = list_cases;
2123                 break;
2124             case OPT_LIST_SUITE_PATHS:
2125                 op = list_suite_paths;
2126                 break;
2127             case OPT_LIST_CASE_PATHS:
2128                 op = list_case_paths;
2129                 break;
2130             case OPT_LIST_DEFINES:
2131                 op = list_defines;
2132                 break;
2133             case OPT_LIST_PERMUTATION_DEFINES:
2134                 op = list_permutation_defines;
2135                 break;
2136             case OPT_LIST_IMPLICIT_DEFINES:
2137                 op = list_implicit_defines;
2138                 break;
2139             case OPT_LIST_GEOMETRIES:
2140                 op = list_geometries;
2141                 break;
2142             case OPT_LIST_POWERLOSSES:
2143                 op = list_powerlosses;
2144                 break;
2145             // configuration
2146             case OPT_DEFINE: {
2147                 // allocate space
2148                 test_override_t *override = mappend(
2149                         (void**)&test_overrides,
2150                         sizeof(test_override_t),
2151                         &test_override_count,
2152                         &test_override_capacity);
2153 
2154                 // parse into string key/intmax_t value, cannibalizing the
2155                 // arg in the process
2156                 char *sep = strchr(optarg, '=');
2157                 char *parsed = NULL;
2158                 if (!sep) {
2159                     goto invalid_define;
2160                 }
2161                 *sep = '\0';
2162                 override->name = optarg;
2163                 optarg = sep+1;
2164 
2165                 // parse comma-separated permutations
2166                 {
2167                     override->defines = NULL;
2168                     override->permutations = 0;
2169                     size_t override_capacity = 0;
2170                     while (true) {
2171                         optarg += strspn(optarg, " ");
2172 
2173                         if (strncmp(optarg, "range", strlen("range")) == 0) {
2174                             // range of values
2175                             optarg += strlen("range");
2176                             optarg += strspn(optarg, " ");
2177                             if (*optarg != '(') {
2178                                 goto invalid_define;
2179                             }
2180                             optarg += 1;
2181 
2182                             intmax_t start = strtoumax(optarg, &parsed, 0);
2183                             intmax_t stop = -1;
2184                             intmax_t step = 1;
2185                             // allow empty string for start=0
2186                             if (parsed == optarg) {
2187                                 start = 0;
2188                             }
2189                             optarg = parsed + strspn(parsed, " ");
2190 
2191                             if (*optarg != ',' && *optarg != ')') {
2192                                 goto invalid_define;
2193                             }
2194 
2195                             if (*optarg == ',') {
2196                                 optarg += 1;
2197                                 stop = strtoumax(optarg, &parsed, 0);
2198                                 // allow empty string for stop=end
2199                                 if (parsed == optarg) {
2200                                     stop = -1;
2201                                 }
2202                                 optarg = parsed + strspn(parsed, " ");
2203 
2204                                 if (*optarg != ',' && *optarg != ')') {
2205                                     goto invalid_define;
2206                                 }
2207 
2208                                 if (*optarg == ',') {
2209                                     optarg += 1;
2210                                     step = strtoumax(optarg, &parsed, 0);
2211                                     // allow empty string for stop=1
2212                                     if (parsed == optarg) {
2213                                         step = 1;
2214                                     }
2215                                     optarg = parsed + strspn(parsed, " ");
2216 
2217                                     if (*optarg != ')') {
2218                                         goto invalid_define;
2219                                     }
2220                                 }
2221                             } else {
2222                                 // single value = stop only
2223                                 stop = start;
2224                                 start = 0;
2225                             }
2226 
2227                             if (*optarg != ')') {
2228                                 goto invalid_define;
2229                             }
2230                             optarg += 1;
2231 
2232                             // calculate the range of values
2233                             assert(step != 0);
2234                             for (intmax_t i = start;
2235                                     (step < 0)
2236                                         ? i > stop
2237                                         : (uintmax_t)i < (uintmax_t)stop;
2238                                     i += step) {
2239                                 *(intmax_t*)mappend(
2240                                         (void**)&override->defines,
2241                                         sizeof(intmax_t),
2242                                         &override->permutations,
2243                                         &override_capacity) = i;
2244                             }
2245                         } else if (*optarg != '\0') {
2246                             // single value
2247                             intmax_t define = strtoimax(optarg, &parsed, 0);
2248                             if (parsed == optarg) {
2249                                 goto invalid_define;
2250                             }
2251                             optarg = parsed + strspn(parsed, " ");
2252                             *(intmax_t*)mappend(
2253                                     (void**)&override->defines,
2254                                     sizeof(intmax_t),
2255                                     &override->permutations,
2256                                     &override_capacity) = define;
2257                         } else {
2258                             break;
2259                         }
2260 
2261                         if (*optarg == ',') {
2262                             optarg += 1;
2263                         }
2264                     }
2265                 }
2266                 assert(override->permutations > 0);
2267                 break;
2268 
2269 invalid_define:
2270                 fprintf(stderr, "error: invalid define: %s\n", optarg);
2271                 exit(-1);
2272             }
2273             case OPT_GEOMETRY: {
2274                 // reset our geometry scenarios
2275                 if (test_geometry_capacity > 0) {
2276                     free((test_geometry_t*)test_geometries);
2277                 }
2278                 test_geometries = NULL;
2279                 test_geometry_count = 0;
2280                 test_geometry_capacity = 0;
2281 
2282                 // parse the comma separated list of disk geometries
2283                 while (*optarg) {
2284                     // allocate space
2285                     test_geometry_t *geometry = mappend(
2286                             (void**)&test_geometries,
2287                             sizeof(test_geometry_t),
2288                             &test_geometry_count,
2289                             &test_geometry_capacity);
2290 
2291                     // parse the disk geometry
2292                     optarg += strspn(optarg, " ");
2293 
2294                     // named disk geometry
2295                     size_t len = strcspn(optarg, " ,");
2296                     for (size_t i = 0; builtin_geometries[i].name; i++) {
2297                         if (len == strlen(builtin_geometries[i].name)
2298                                 && memcmp(optarg,
2299                                     builtin_geometries[i].name,
2300                                     len) == 0)  {
2301                             *geometry = builtin_geometries[i];
2302                             optarg += len;
2303                             goto geometry_next;
2304                         }
2305                     }
2306 
2307                     // comma-separated read/prog/erase/count
2308                     if (*optarg == '{') {
2309                         lfs_size_t sizes[4];
2310                         size_t count = 0;
2311 
2312                         char *s = optarg + 1;
2313                         while (count < 4) {
2314                             char *parsed = NULL;
2315                             sizes[count] = strtoumax(s, &parsed, 0);
2316                             count += 1;
2317 
2318                             s = parsed + strspn(parsed, " ");
2319                             if (*s == ',') {
2320                                 s += 1;
2321                                 continue;
2322                             } else if (*s == '}') {
2323                                 s += 1;
2324                                 break;
2325                             } else {
2326                                 goto geometry_unknown;
2327                             }
2328                         }
2329 
2330                         // allow implicit r=p and p=e for common geometries
2331                         memset(geometry, 0, sizeof(test_geometry_t));
2332                         if (count >= 3) {
2333                             geometry->defines[READ_SIZE_i]
2334                                     = TEST_LIT(sizes[0]);
2335                             geometry->defines[PROG_SIZE_i]
2336                                     = TEST_LIT(sizes[1]);
2337                             geometry->defines[ERASE_SIZE_i]
2338                                     = TEST_LIT(sizes[2]);
2339                         } else if (count >= 2) {
2340                             geometry->defines[PROG_SIZE_i]
2341                                     = TEST_LIT(sizes[0]);
2342                             geometry->defines[ERASE_SIZE_i]
2343                                     = TEST_LIT(sizes[1]);
2344                         } else {
2345                             geometry->defines[ERASE_SIZE_i]
2346                                     = TEST_LIT(sizes[0]);
2347                         }
2348                         if (count >= 4) {
2349                             geometry->defines[ERASE_COUNT_i]
2350                                     = TEST_LIT(sizes[3]);
2351                         }
2352                         optarg = s;
2353                         goto geometry_next;
2354                     }
2355 
2356                     // leb16-encoded read/prog/erase/count
2357                     if (*optarg == ':') {
2358                         lfs_size_t sizes[4];
2359                         size_t count = 0;
2360 
2361                         char *s = optarg + 1;
2362                         while (true) {
2363                             char *parsed = NULL;
2364                             uintmax_t x = leb16_parse(s, &parsed);
2365                             if (parsed == s || count >= 4) {
2366                                 break;
2367                             }
2368 
2369                             sizes[count] = x;
2370                             count += 1;
2371                             s = parsed;
2372                         }
2373 
2374                         // allow implicit r=p and p=e for common geometries
2375                         memset(geometry, 0, sizeof(test_geometry_t));
2376                         if (count >= 3) {
2377                             geometry->defines[READ_SIZE_i]
2378                                     = TEST_LIT(sizes[0]);
2379                             geometry->defines[PROG_SIZE_i]
2380                                     = TEST_LIT(sizes[1]);
2381                             geometry->defines[ERASE_SIZE_i]
2382                                     = TEST_LIT(sizes[2]);
2383                         } else if (count >= 2) {
2384                             geometry->defines[PROG_SIZE_i]
2385                                     = TEST_LIT(sizes[0]);
2386                             geometry->defines[ERASE_SIZE_i]
2387                                     = TEST_LIT(sizes[1]);
2388                         } else {
2389                             geometry->defines[ERASE_SIZE_i]
2390                                     = TEST_LIT(sizes[0]);
2391                         }
2392                         if (count >= 4) {
2393                             geometry->defines[ERASE_COUNT_i]
2394                                     = TEST_LIT(sizes[3]);
2395                         }
2396                         optarg = s;
2397                         goto geometry_next;
2398                     }
2399 
2400 geometry_unknown:
2401                     // unknown scenario?
2402                     fprintf(stderr, "error: unknown disk geometry: %s\n",
2403                             optarg);
2404                     exit(-1);
2405 
2406 geometry_next:
2407                     optarg += strspn(optarg, " ");
2408                     if (*optarg == ',') {
2409                         optarg += 1;
2410                     } else if (*optarg == '\0') {
2411                         break;
2412                     } else {
2413                         goto geometry_unknown;
2414                     }
2415                 }
2416                 break;
2417             }
2418             case OPT_POWERLOSS: {
2419                 // reset our powerloss scenarios
2420                 if (test_powerloss_capacity > 0) {
2421                     free((test_powerloss_t*)test_powerlosses);
2422                 }
2423                 test_powerlosses = NULL;
2424                 test_powerloss_count = 0;
2425                 test_powerloss_capacity = 0;
2426 
2427                 // parse the comma separated list of power-loss scenarios
2428                 while (*optarg) {
2429                     // allocate space
2430                     test_powerloss_t *powerloss = mappend(
2431                             (void**)&test_powerlosses,
2432                             sizeof(test_powerloss_t),
2433                             &test_powerloss_count,
2434                             &test_powerloss_capacity);
2435 
2436                     // parse the power-loss scenario
2437                     optarg += strspn(optarg, " ");
2438 
2439                     // named power-loss scenario
2440                     size_t len = strcspn(optarg, " ,");
2441                     for (size_t i = 0; builtin_powerlosses[i].name; i++) {
2442                         if (len == strlen(builtin_powerlosses[i].name)
2443                                 && memcmp(optarg,
2444                                     builtin_powerlosses[i].name,
2445                                     len) == 0) {
2446                             *powerloss = builtin_powerlosses[i];
2447                             optarg += len;
2448                             goto powerloss_next;
2449                         }
2450                     }
2451 
2452                     // comma-separated permutation
2453                     if (*optarg == '{') {
2454                         lfs_emubd_powercycles_t *cycles = NULL;
2455                         size_t cycle_count = 0;
2456                         size_t cycle_capacity = 0;
2457 
2458                         char *s = optarg + 1;
2459                         while (true) {
2460                             char *parsed = NULL;
2461                             *(lfs_emubd_powercycles_t*)mappend(
2462                                     (void**)&cycles,
2463                                     sizeof(lfs_emubd_powercycles_t),
2464                                     &cycle_count,
2465                                     &cycle_capacity)
2466                                     = strtoumax(s, &parsed, 0);
2467 
2468                             s = parsed + strspn(parsed, " ");
2469                             if (*s == ',') {
2470                                 s += 1;
2471                                 continue;
2472                             } else if (*s == '}') {
2473                                 s += 1;
2474                                 break;
2475                             } else {
2476                                 goto powerloss_unknown;
2477                             }
2478                         }
2479 
2480                         *powerloss = (test_powerloss_t){
2481                             .run = run_powerloss_cycles,
2482                             .cycles = cycles,
2483                             .cycle_count = cycle_count,
2484                         };
2485                         optarg = s;
2486                         goto powerloss_next;
2487                     }
2488 
2489                     // leb16-encoded permutation
2490                     if (*optarg == ':') {
2491                         lfs_emubd_powercycles_t *cycles = NULL;
2492                         size_t cycle_count = 0;
2493                         size_t cycle_capacity = 0;
2494 
2495                         char *s = optarg + 1;
2496                         while (true) {
2497                             char *parsed = NULL;
2498                             uintmax_t x = leb16_parse(s, &parsed);
2499                             if (parsed == s) {
2500                                 break;
2501                             }
2502 
2503                             *(lfs_emubd_powercycles_t*)mappend(
2504                                     (void**)&cycles,
2505                                     sizeof(lfs_emubd_powercycles_t),
2506                                     &cycle_count,
2507                                     &cycle_capacity) = x;
2508                             s = parsed;
2509                         }
2510 
2511                         *powerloss = (test_powerloss_t){
2512                             .run = run_powerloss_cycles,
2513                             .cycles = cycles,
2514                             .cycle_count = cycle_count,
2515                         };
2516                         optarg = s;
2517                         goto powerloss_next;
2518                     }
2519 
2520                     // exhaustive permutations
2521                     {
2522                         char *parsed = NULL;
2523                         size_t count = strtoumax(optarg, &parsed, 0);
2524                         if (parsed == optarg) {
2525                             goto powerloss_unknown;
2526                         }
2527                         *powerloss = (test_powerloss_t){
2528                             .run = run_powerloss_exhaustive,
2529                             .cycles = NULL,
2530                             .cycle_count = count,
2531                         };
2532                         optarg = (char*)parsed;
2533                         goto powerloss_next;
2534                     }
2535 
2536 powerloss_unknown:
2537                     // unknown scenario?
2538                     fprintf(stderr, "error: unknown power-loss scenario: %s\n",
2539                             optarg);
2540                     exit(-1);
2541 
2542 powerloss_next:
2543                     optarg += strspn(optarg, " ");
2544                     if (*optarg == ',') {
2545                         optarg += 1;
2546                     } else if (*optarg == '\0') {
2547                         break;
2548                     } else {
2549                         goto powerloss_unknown;
2550                     }
2551                 }
2552                 break;
2553             }
2554             case OPT_STEP: {
2555                 char *parsed = NULL;
2556                 test_step_start = strtoumax(optarg, &parsed, 0);
2557                 test_step_stop = -1;
2558                 test_step_step = 1;
2559                 // allow empty string for start=0
2560                 if (parsed == optarg) {
2561                     test_step_start = 0;
2562                 }
2563                 optarg = parsed + strspn(parsed, " ");
2564 
2565                 if (*optarg != ',' && *optarg != '\0') {
2566                     goto step_unknown;
2567                 }
2568 
2569                 if (*optarg == ',') {
2570                     optarg += 1;
2571                     test_step_stop = strtoumax(optarg, &parsed, 0);
2572                     // allow empty string for stop=end
2573                     if (parsed == optarg) {
2574                         test_step_stop = -1;
2575                     }
2576                     optarg = parsed + strspn(parsed, " ");
2577 
2578                     if (*optarg != ',' && *optarg != '\0') {
2579                         goto step_unknown;
2580                     }
2581 
2582                     if (*optarg == ',') {
2583                         optarg += 1;
2584                         test_step_step = strtoumax(optarg, &parsed, 0);
2585                         // allow empty string for stop=1
2586                         if (parsed == optarg) {
2587                             test_step_step = 1;
2588                         }
2589                         optarg = parsed + strspn(parsed, " ");
2590 
2591                         if (*optarg != '\0') {
2592                             goto step_unknown;
2593                         }
2594                     }
2595                 } else {
2596                     // single value = stop only
2597                     test_step_stop = test_step_start;
2598                     test_step_start = 0;
2599                 }
2600 
2601                 break;
2602 step_unknown:
2603                 fprintf(stderr, "error: invalid step: %s\n", optarg);
2604                 exit(-1);
2605             }
2606             case OPT_DISK:
2607                 test_disk_path = optarg;
2608                 break;
2609             case OPT_TRACE:
2610                 test_trace_path = optarg;
2611                 break;
2612             case OPT_TRACE_BACKTRACE:
2613                 test_trace_backtrace = true;
2614                 break;
2615             case OPT_TRACE_PERIOD: {
2616                 char *parsed = NULL;
2617                 test_trace_period = strtoumax(optarg, &parsed, 0);
2618                 if (parsed == optarg) {
2619                     fprintf(stderr, "error: invalid trace-period: %s\n", optarg);
2620                     exit(-1);
2621                 }
2622                 break;
2623             }
2624             case OPT_TRACE_FREQ: {
2625                 char *parsed = NULL;
2626                 test_trace_freq = strtoumax(optarg, &parsed, 0);
2627                 if (parsed == optarg) {
2628                     fprintf(stderr, "error: invalid trace-freq: %s\n", optarg);
2629                     exit(-1);
2630                 }
2631                 break;
2632             }
2633             case OPT_READ_SLEEP: {
2634                 char *parsed = NULL;
2635                 double read_sleep = strtod(optarg, &parsed);
2636                 if (parsed == optarg) {
2637                     fprintf(stderr, "error: invalid read-sleep: %s\n", optarg);
2638                     exit(-1);
2639                 }
2640                 test_read_sleep = read_sleep*1.0e9;
2641                 break;
2642             }
2643             case OPT_PROG_SLEEP: {
2644                 char *parsed = NULL;
2645                 double prog_sleep = strtod(optarg, &parsed);
2646                 if (parsed == optarg) {
2647                     fprintf(stderr, "error: invalid prog-sleep: %s\n", optarg);
2648                     exit(-1);
2649                 }
2650                 test_prog_sleep = prog_sleep*1.0e9;
2651                 break;
2652             }
2653             case OPT_ERASE_SLEEP: {
2654                 char *parsed = NULL;
2655                 double erase_sleep = strtod(optarg, &parsed);
2656                 if (parsed == optarg) {
2657                     fprintf(stderr, "error: invalid erase-sleep: %s\n", optarg);
2658                     exit(-1);
2659                 }
2660                 test_erase_sleep = erase_sleep*1.0e9;
2661                 break;
2662             }
2663             // done parsing
2664             case -1:
2665                 goto getopt_done;
2666             // unknown arg, getopt prints a message for us
2667             default:
2668                 exit(-1);
2669         }
2670     }
2671 getopt_done: ;
2672 
2673     if (argc > optind) {
2674         // reset our test identifier list
2675         test_ids = NULL;
2676         test_id_count = 0;
2677         test_id_capacity = 0;
2678     }
2679 
2680     // parse test identifier, if any, cannibalizing the arg in the process
2681     for (; argc > optind; optind++) {
2682         test_define_t *defines = NULL;
2683         size_t define_count = 0;
2684         lfs_emubd_powercycles_t *cycles = NULL;
2685         size_t cycle_count = 0;
2686 
2687         // parse name, can be suite or case
2688         char *name = argv[optind];
2689         char *defines_ = strchr(name, ':');
2690         if (defines_) {
2691             *defines_ = '\0';
2692             defines_ += 1;
2693         }
2694 
2695         // remove optional path and .toml suffix
2696         char *slash = strrchr(name, '/');
2697         if (slash) {
2698             name = slash+1;
2699         }
2700 
2701         size_t name_len = strlen(name);
2702         if (name_len > 5 && strcmp(&name[name_len-5], ".toml") == 0) {
2703             name[name_len-5] = '\0';
2704         }
2705 
2706         if (defines_) {
2707             // parse defines
2708             char *cycles_ = strchr(defines_, ':');
2709             if (cycles_) {
2710                 *cycles_ = '\0';
2711                 cycles_ += 1;
2712             }
2713 
2714             while (true) {
2715                 char *parsed;
2716                 size_t d = leb16_parse(defines_, &parsed);
2717                 intmax_t v = leb16_parse(parsed, &parsed);
2718                 if (parsed == defines_) {
2719                     break;
2720                 }
2721                 defines_ = parsed;
2722 
2723                 if (d >= define_count) {
2724                     // align to power of two to avoid any superlinear growth
2725                     size_t ncount = 1 << lfs_npw2(d+1);
2726                     defines = realloc(defines,
2727                             ncount*sizeof(test_define_t));
2728                     memset(defines+define_count, 0,
2729                             (ncount-define_count)*sizeof(test_define_t));
2730                     define_count = ncount;
2731                 }
2732                 defines[d] = TEST_LIT(v);
2733             }
2734 
2735             if (cycles_) {
2736                 // parse power cycles
2737                 size_t cycle_capacity = 0;
2738                 while (*cycles_ != '\0') {
2739                     char *parsed = NULL;
2740                     *(lfs_emubd_powercycles_t*)mappend(
2741                             (void**)&cycles,
2742                             sizeof(lfs_emubd_powercycles_t),
2743                             &cycle_count,
2744                             &cycle_capacity)
2745                             = leb16_parse(cycles_, &parsed);
2746                     if (parsed == cycles_) {
2747                         fprintf(stderr, "error: "
2748                                 "could not parse test cycles: %s\n",
2749                                 cycles_);
2750                         exit(-1);
2751                     }
2752                     cycles_ = parsed;
2753                 }
2754             }
2755         }
2756 
2757         // append to identifier list
2758         *(test_id_t*)mappend(
2759                 (void**)&test_ids,
2760                 sizeof(test_id_t),
2761                 &test_id_count,
2762                 &test_id_capacity) = (test_id_t){
2763             .name = name,
2764             .defines = defines,
2765             .define_count = define_count,
2766             .cycles = cycles,
2767             .cycle_count = cycle_count,
2768         };
2769     }
2770 
2771     // do the thing
2772     op();
2773 
2774     // cleanup (need to be done for valgrind testing)
2775     test_define_cleanup();
2776     if (test_overrides) {
2777         for (size_t i = 0; i < test_override_count; i++) {
2778             free((void*)test_overrides[i].defines);
2779         }
2780         free((void*)test_overrides);
2781     }
2782     if (test_geometry_capacity) {
2783         free((void*)test_geometries);
2784     }
2785     if (test_powerloss_capacity) {
2786         for (size_t i = 0; i < test_powerloss_count; i++) {
2787             free((void*)test_powerlosses[i].cycles);
2788         }
2789         free((void*)test_powerlosses);
2790     }
2791     if (test_id_capacity) {
2792         for (size_t i = 0; i < test_id_count; i++) {
2793             free((void*)test_ids[i].defines);
2794             free((void*)test_ids[i].cycles);
2795         }
2796         free((void*)test_ids);
2797     }
2798 }
2799