runners: test: Reworked -P/--powerloss to use another expr-like grammar

This reworks -P/--powerloss to be more consistent with other flexible
flags (-D/--define, -S/--probe, etc):

- Tweaks -P/--powerloss to accept multiple flags (-Pnone -Plinear)
  instead of a comma-separated list (-Pnone,linear)

- Adopts an expr-like grammar similar to -Dx='range(3)', -Sx=123shz, etc
  (see below)

- Generalizes run_powerloss_linear and run_powerloss_log to accept
  start/stop/step conditions, allowing for range and logrange exprs
  with minimal work

---

The new expr-like grammar follows what's worked well for -D/--define,
-S/--probe, etc, in which parens can be used to parameterize some of the
more complex scenarios. This makes the -P/--powerloss grammar more
consistent, less ad-hoc, easier to parse, while also providing
flexibility for future powerloss exprs.

As an example, bounded range/logrange variants of linear/log were easy
to add without each needing their own little syntax:

- none -> none              - Run with no powerlosses
- linear -> linear          - Run with linearly-decreasing powerlosses
- log -> log                - Run with exponentially-decreasing pls
- n -> permute(n)           - Run all permutations of n powerlosses
- exhaustive -> exhaustive  - Run all powerloss permutations
- {1,2,3} -> list(1,2,3)    - Run explicit list of powerlosses
- added range(a,b,s)        - Run explicit range of powerlosses
- added logrange(a,b,s)     - Run explicit range of 2^n powerlosses
- :1248g1 -> :1248g1        - Run custom leb128-encoded set of pls

Note we still keep :-prefixed leb128-encoded powerlosses as is. This is
enough of its own syntax that trying to map it to an expr doesn't really
make sense. And is humorously compatible with most future grammars.
This commit is contained in:
Christopher Haster
2026-02-09 18:30:09 -06:00
parent 4af4cf3212
commit 5a271da7eb
4 changed files with 292 additions and 298 deletions
+9 -9
View File
@@ -52,9 +52,9 @@ void *mappend(void **p,
} }
// a quick self-terminating text-safe varint scheme // a quick self-terminating text-safe varint scheme
static void leb16_print(uintmax_t x) { static void leb16_print(intmax_t x) {
// allow 'w' to indicate negative numbers // allow 'w' to indicate negative numbers
if ((intmax_t)x < 0) { if (x < 0) {
printf("w"); printf("w");
x = -x; x = -x;
} }
@@ -69,7 +69,7 @@ static void leb16_print(uintmax_t x) {
} }
} }
static uintmax_t leb16_parse(const char *s, char **tail) { static intmax_t leb16_parse(const char *s, char **tail) {
bool neg = false; bool neg = false;
uintmax_t x = 0; uintmax_t x = 0;
if (tail) { if (tail) {
@@ -392,13 +392,13 @@ intmax_t bench_override_cb(void *data, size_t i) {
if (v->step) { if (v->step) {
size_t range_count; size_t range_count;
if (v->step > 0) { if (v->step > 0) {
range_count = (v->stop-1 - v->start) / v->step + 1; range_count = (v->stop-1 - v->start) / +v->step + 1;
} else { } else {
range_count = (v->start-1 - v->stop) / -v->step + 1; range_count = (v->start-1 - v->stop) / -v->step + 1;
} }
if (i < range_count) { if (i < range_count) {
return i*v->step + v->start; return v->start + i*v->step;
} }
i -= range_count; i -= range_count;
// value? // value?
@@ -903,7 +903,7 @@ int __wrap_vprintf(const char *fmt, va_list args) {
// bench probe/recording state // bench probe/recording state
typedef struct bench_probe { typedef struct bench_probe {
const char *probe; const char *name;
size_t step; size_t step;
double runfreq; double runfreq;
double simfreq; double simfreq;
@@ -1017,7 +1017,7 @@ bench_record_t *bench_find(const char *probe) {
// find probe descriptor, if there is one // find probe descriptor, if there is one
bench_probe_t *probe_ = NULL; bench_probe_t *probe_ = NULL;
for (size_t i = 0; i < bench_probe_count; i++) { for (size_t i = 0; i < bench_probe_count; i++) {
if (strcmp(bench_probes[i].probe, probe) == 0) { if (strcmp(bench_probes[i].name, probe) == 0) {
probe_ = &bench_probes[i]; probe_ = &bench_probes[i];
break; break;
} }
@@ -2711,7 +2711,7 @@ int main(int argc, char **argv) {
if (*optarg == ',') { if (*optarg == ',') {
optarg += 1; optarg += 1;
step = strtoumax(optarg, &parsed, 0); step = strtoumax(optarg, &parsed, 0);
// allow empty string for stop=1 // allow empty string for step=1
if (parsed == optarg) { if (parsed == optarg) {
step = 1; step = 1;
} }
@@ -2814,7 +2814,7 @@ int main(int argc, char **argv) {
// parse into string key/intmax_t value, cannibalizing the // parse into string key/intmax_t value, cannibalizing the
// arg in the process // arg in the process
probe->probe = optarg; probe->name = optarg;
sep = strchr(optarg, '='); sep = strchr(optarg, '=');
if (sep) { if (sep) {
*sep = '\0'; *sep = '\0';
+278 -286
View File
@@ -52,9 +52,9 @@ void *mappend(void **p,
} }
// a quick self-terminating text-safe varint scheme // a quick self-terminating text-safe varint scheme
static void leb16_print(uintmax_t x) { static void leb16_print(intmax_t x) {
// allow 'w' to indicate negative numbers // allow 'w' to indicate negative numbers
if ((intmax_t)x < 0) { if (x < 0) {
printf("w"); printf("w");
x = -x; x = -x;
} }
@@ -69,7 +69,7 @@ static void leb16_print(uintmax_t x) {
} }
} }
static uintmax_t leb16_parse(const char *s, char **tail) { static intmax_t leb16_parse(const char *s, char **tail) {
bool neg = false; bool neg = false;
uintmax_t x = 0; uintmax_t x = 0;
if (tail) { if (tail) {
@@ -83,7 +83,7 @@ static uintmax_t leb16_parse(const char *s, char **tail) {
size_t i = 0; size_t i = 0;
while (true) { while (true) {
uintmax_t nibble = s[i]; intmax_t nibble = s[i];
if (nibble >= '0' && nibble <= '9') { if (nibble >= '0' && nibble <= '9') {
nibble = nibble - '0'; nibble = nibble - '0';
} else if (nibble >= 'a' && nibble <= 'v') { } else if (nibble >= 'a' && nibble <= 'v') {
@@ -117,7 +117,7 @@ typedef struct test_powerloss {
const struct test_powerloss *powerloss, const struct test_powerloss *powerloss,
const struct test_suite *suite, const struct test_suite *suite,
const struct test_case *case_); const struct test_case *case_);
const test_powercycles_t *cycles; const test_spowercycles_t *cycles;
size_t cycle_count; size_t cycle_count;
} test_powerloss_t; } test_powerloss_t;
@@ -403,13 +403,13 @@ intmax_t test_override_cb(void *data, size_t i) {
if (v->step) { if (v->step) {
size_t range_count; size_t range_count;
if (v->step > 0) { if (v->step > 0) {
range_count = (v->stop-1 - v->start) / v->step + 1; range_count = (v->stop-1 - v->start) / +v->step + 1;
} else { } else {
range_count = (v->start-1 - v->stop) / -v->step + 1; range_count = (v->start-1 - v->stop) / -v->step + 1;
} }
if (i < range_count) { if (i < range_count) {
return i*v->step + v->start; return v->start + i*v->step;
} }
i -= range_count; i -= range_count;
// value? // value?
@@ -456,10 +456,14 @@ test_ns_t test_read_sleep = 0.0;
test_ns_t test_prog_sleep = 0.0; test_ns_t test_prog_sleep = 0.0;
test_ns_t test_erase_sleep = 0.0; test_ns_t test_erase_sleep = 0.0;
volatile size_t TEST_PLS = 0; // incremented every powerloss const test_powerloss_t *test_powerlosses = NULL;
size_t test_powerloss_count = 0;
size_t test_powerloss_capacity = 0;
extern const test_powerloss_t test_default_powerlosses[];
extern const size_t test_default_powerloss_count;
volatile test_powercycles_t TEST_PLS = 0; // incremented every powerloss
extern const test_powerloss_t *test_powerlosses;
extern size_t test_powerloss_count;
// this determines both the backtrace buffer and the trace printf buffer, if // this determines both the backtrace buffer and the trace printf buffer, if
@@ -647,7 +651,7 @@ void test_permutation(size_t i, uint32_t *buffer, size_t size) {
static void perm_printid( static void perm_printid(
const struct test_suite *suite, const struct test_suite *suite,
const struct test_case *case_, const struct test_case *case_,
const test_powercycles_t *cycles, const test_spowercycles_t *cycles,
size_t cycle_count) { size_t cycle_count) {
(void)suite; (void)suite;
(void)cycles; (void)cycles;
@@ -733,7 +737,7 @@ static void run_powerloss_none(
const struct test_suite *suite, const struct test_suite *suite,
const struct test_case *case_); const struct test_case *case_);
#ifndef TEST_KIWIBD #ifndef TEST_KIWIBD
static void run_powerloss_cycles( static void run_powerloss_list(
const test_powerloss_t *powerloss, const test_powerloss_t *powerloss,
const struct test_suite *suite, const struct test_suite *suite,
const struct test_case *case_); const struct test_case *case_);
@@ -750,6 +754,14 @@ static void case_forperm(
const struct test_case *case_, const struct test_case *case_,
const test_powerloss_t *powerloss), const test_powerloss_t *powerloss),
void *data) { void *data) {
// default powerlosses?
const test_powerloss_t *powerlosses = test_powerlosses;
size_t powerloss_count = test_powerloss_count;
if (!powerlosses) {
powerlosses = test_default_powerlosses;
powerloss_count = test_default_powerloss_count;
}
// explicit permutation? // explicit permutation?
if (id && id->defines) { if (id && id->defines) {
// define case permutation, the exact case perm doesn't matter here // define case permutation, the exact case perm doesn't matter here
@@ -764,14 +776,14 @@ static void case_forperm(
if (id && id->powerloss.run) { if (id && id->powerloss.run) {
cb(data, suite, case_, &id->powerloss); cb(data, suite, case_, &id->powerloss);
} else { } else {
for (size_t p = 0; p < test_powerloss_count; p++) { for (size_t p = 0; p < powerloss_count; p++) {
// skip non-reentrant tests when powerloss testing // skip non-reentrant tests when powerloss testing
if (test_powerlosses[p].run != run_powerloss_none if (powerlosses[p].run != run_powerloss_none
&& !(case_->flags & TEST_REENTRANT)) { && !(case_->flags & TEST_REENTRANT)) {
continue; continue;
} }
cb(data, suite, case_, &test_powerlosses[p]); cb(data, suite, case_, &powerlosses[p]);
} }
} }
} }
@@ -807,14 +819,14 @@ static void case_forperm(
if (id && id->powerloss.run) { if (id && id->powerloss.run) {
cb(data, suite, case_, &id->powerloss); cb(data, suite, case_, &id->powerloss);
} else { } else {
for (size_t p = 0; p < test_powerloss_count; p++) { for (size_t p = 0; p < powerloss_count; p++) {
// skip non-reentrant tests when powerloss testing // skip non-reentrant tests when powerloss testing
if (test_powerlosses[p].run != run_powerloss_none if (powerlosses[p].run != run_powerloss_none
&& !(case_->flags & TEST_REENTRANT)) { && !(case_->flags & TEST_REENTRANT)) {
continue; continue;
} }
cb(data, suite, case_, &test_powerlosses[p]); cb(data, suite, case_, &powerlosses[p]);
} }
} }
} }
@@ -1444,6 +1456,21 @@ static void run_powerloss_linear(
const test_powerloss_t *powerloss, const test_powerloss_t *powerloss,
const struct test_suite *suite, const struct test_suite *suite,
const struct test_case *case_) { const struct test_case *case_) {
test_spowercycles_t start
= (powerloss->cycle_count >= 2)
? powerloss->cycles[0]
: 0;
test_spowercycles_t stop
= (powerloss->cycle_count >= 2)
? powerloss->cycles[1]
: (powerloss->cycle_count >= 1)
? powerloss->cycles[0]
: -1;
test_spowercycles_t step
= (powerloss->cycle_count >= 3)
? powerloss->cycles[2]
: 1;
// zero pls // zero pls
TEST_PLS = 0; TEST_PLS = 0;
@@ -1467,9 +1494,6 @@ static void run_powerloss_linear(
.read_sleep = test_read_sleep, \ .read_sleep = test_read_sleep, \
.prog_sleep = test_prog_sleep, \ .prog_sleep = test_prog_sleep, \
.erase_sleep = test_erase_sleep, \ .erase_sleep = test_erase_sleep, \
.power_cycles = (TEST_PLS < powerloss->cycle_count) \
? TEST_PLS+1 \
: 0, \
.powerloss_cb = powerloss_longjmp, \ .powerloss_cb = powerloss_longjmp, \
.powerloss_data = &powerloss_jmp, .powerloss_data = &powerloss_jmp,
#include TEST_STRINGIFY(TEST_DEFINES) #include TEST_STRINGIFY(TEST_DEFINES)
@@ -1488,6 +1512,15 @@ static void run_powerloss_linear(
printf("\n"); printf("\n");
while (true) { while (true) {
lfs3_emubd_setpowercycles(CFG,
(powerloss->cycle_count == 0
|| (test_spowercycles_t)TEST_PLS < (
(step > 0)
? (stop-1 - start) / +step + 1
: (start-1 - stop) / -step + 1))
? 1 + (start + TEST_PLS*step)
: 0);
if (!setjmp(powerloss_jmp)) { if (!setjmp(powerloss_jmp)) {
// run the test // run the test
@@ -1500,14 +1533,13 @@ static void run_powerloss_linear(
printf("powerloss "); printf("powerloss ");
perm_printid(suite, case_, NULL, 0); perm_printid(suite, case_, NULL, 0);
printf(":x"); printf(":x");
leb16_print(TEST_PLS+1); leb16_print(start);
leb16_print(start + (TEST_PLS+1)*step);
leb16_print(step);
printf("\n"); printf("\n");
// increment pls // increment pls
TEST_PLS += 1; TEST_PLS += 1;
lfs3_emubd_setpowercycles(CFG, (TEST_PLS < powerloss->cycle_count)
? TEST_PLS+1
: 0);
} }
printf("finished "); printf("finished ");
@@ -1528,6 +1560,21 @@ static void run_powerloss_log(
const test_powerloss_t *powerloss, const test_powerloss_t *powerloss,
const struct test_suite *suite, const struct test_suite *suite,
const struct test_case *case_) { const struct test_case *case_) {
test_spowercycles_t start
= (powerloss->cycle_count >= 2)
? powerloss->cycles[0]
: 0;
test_spowercycles_t stop
= (powerloss->cycle_count >= 2)
? powerloss->cycles[1]
: (powerloss->cycle_count >= 1)
? powerloss->cycles[0]
: -1;
test_spowercycles_t step
= (powerloss->cycle_count >= 3)
? powerloss->cycles[2]
: 1;
// zero pls // zero pls
TEST_PLS = 0; TEST_PLS = 0;
@@ -1551,9 +1598,6 @@ static void run_powerloss_log(
.read_sleep = test_read_sleep, \ .read_sleep = test_read_sleep, \
.prog_sleep = test_prog_sleep, \ .prog_sleep = test_prog_sleep, \
.erase_sleep = test_erase_sleep, \ .erase_sleep = test_erase_sleep, \
.power_cycles = (TEST_PLS < powerloss->cycle_count) \
? 1 << TEST_PLS \
: 0, \
.powerloss_cb = powerloss_longjmp, \ .powerloss_cb = powerloss_longjmp, \
.powerloss_data = &powerloss_jmp, .powerloss_data = &powerloss_jmp,
#include TEST_STRINGIFY(TEST_DEFINES) #include TEST_STRINGIFY(TEST_DEFINES)
@@ -1572,6 +1616,15 @@ static void run_powerloss_log(
printf("\n"); printf("\n");
while (true) { while (true) {
lfs3_emubd_setpowercycles(CFG,
(powerloss->cycle_count == 0
|| (test_spowercycles_t)TEST_PLS < (
(step > 0)
? (stop-1 - start) / +step + 1
: (start-1 - stop) / -step + 1))
? 1 << (start + (TEST_PLS+1)*step)
: 0);
if (!setjmp(powerloss_jmp)) { if (!setjmp(powerloss_jmp)) {
// run the test // run the test
@@ -1584,14 +1637,13 @@ static void run_powerloss_log(
printf("powerloss "); printf("powerloss ");
perm_printid(suite, case_, NULL, 0); perm_printid(suite, case_, NULL, 0);
printf(":y"); printf(":y");
leb16_print(TEST_PLS+1); leb16_print(start);
leb16_print(start + (TEST_PLS+1)*step);
leb16_print(step);
printf("\n"); printf("\n");
// increment pls // increment pls
TEST_PLS += 1; TEST_PLS += 1;
lfs3_emubd_setpowercycles(CFG, (TEST_PLS < powerloss->cycle_count)
? 1 << TEST_PLS
: 0);
} }
printf("finished "); printf("finished ");
@@ -1608,7 +1660,7 @@ static void run_powerloss_log(
#endif #endif
#ifndef TEST_KIWIBD #ifndef TEST_KIWIBD
static void run_powerloss_cycles( static void run_powerloss_list(
const test_powerloss_t *powerloss, const test_powerloss_t *powerloss,
const struct test_suite *suite, const struct test_suite *suite,
const struct test_case *case_) { const struct test_case *case_) {
@@ -1635,9 +1687,10 @@ static void run_powerloss_cycles(
.read_sleep = test_read_sleep, \ .read_sleep = test_read_sleep, \
.prog_sleep = test_prog_sleep, \ .prog_sleep = test_prog_sleep, \
.erase_sleep = test_erase_sleep, \ .erase_sleep = test_erase_sleep, \
.power_cycles = (TEST_PLS < powerloss->cycle_count) \ .power_cycles = \
? powerloss->cycles[TEST_PLS] \ (0 < powerloss->cycle_count) \
: 0, \ ? powerloss->cycles[0] \
: 0, \
.powerloss_cb = powerloss_longjmp, \ .powerloss_cb = powerloss_longjmp, \
.powerloss_data = &powerloss_jmp, .powerloss_data = &powerloss_jmp,
#include TEST_STRINGIFY(TEST_DEFINES) #include TEST_STRINGIFY(TEST_DEFINES)
@@ -1672,9 +1725,10 @@ static void run_powerloss_cycles(
// increment pls // increment pls
TEST_PLS += 1; TEST_PLS += 1;
lfs3_emubd_setpowercycles(CFG, (TEST_PLS < powerloss->cycle_count) lfs3_emubd_setpowercycles(CFG,
? powerloss->cycles[TEST_PLS] (TEST_PLS < powerloss->cycle_count)
: 0); ? powerloss->cycles[TEST_PLS]
: 0);
} }
printf("finished "); printf("finished ");
@@ -1783,7 +1837,9 @@ static void run_powerloss_exhaustive_layer(
*cycle = i+1; *cycle = i+1;
printf("powerloss "); printf("powerloss ");
perm_printid(suite, case_, cycles->cycles, cycles->cycle_count); perm_printid(suite, case_,
(test_spowercycles_t*)cycles->cycles,
cycles->cycle_count);
printf("\n"); printf("\n");
// now recurse // now recurse
@@ -1846,7 +1902,11 @@ static void run_powerloss_exhaustive(
run_powerloss_exhaustive_layer( run_powerloss_exhaustive_layer(
&(struct powerloss_exhaustive_cycles){NULL, 0, 0}, &(struct powerloss_exhaustive_cycles){NULL, 0, 0},
suite, case_, suite, case_,
CFG, BDCFG, powerloss->cycle_count, 0); CFG, BDCFG,
(powerloss->cycle_count == 0)
? SIZE_MAX
: (size_t)powerloss->cycles[0],
0);
printf("finished "); printf("finished ");
perm_printid(suite, case_, NULL, 0); perm_printid(suite, case_, NULL, 0);
@@ -1855,48 +1915,52 @@ static void run_powerloss_exhaustive(
#endif #endif
const test_powerloss_t builtin_powerlosses[] = { const test_powerloss_t test_builtin_powerlosses[] = {
{"none", run_powerloss_none, NULL, 0}, {"none", run_powerloss_none, NULL, 0},
#ifndef TEST_KIWIBD #ifndef TEST_KIWIBD
{"log", run_powerloss_log, NULL, SIZE_MAX}, {"linear", run_powerloss_linear, NULL, 0},
{"linear", run_powerloss_linear, NULL, SIZE_MAX}, {"log", run_powerloss_log, NULL, 0},
{"exhaustive", run_powerloss_exhaustive, NULL, SIZE_MAX}, {"permute(n)", run_powerloss_exhaustive, NULL, 1},
{"exhaustive", run_powerloss_exhaustive, NULL, 0},
{"list(1,2,3)", run_powerloss_list, NULL, SIZE_MAX},
{"range(a,b,s)", run_powerloss_linear, NULL, 3},
{"logrange(a,b,s)", run_powerloss_log, NULL, 3},
{":1248g1", NULL, NULL, SIZE_MAX},
#endif #endif
{NULL, NULL, NULL, 0}, {NULL, NULL, NULL, 0},
}; };
const char *const builtin_powerlosses_help[] = { const char *const test_builtin_powerlosses_help[] = {
"Run with no powerlosses.", "Run with no powerlosses.",
#ifndef TEST_KIWIBD #ifndef TEST_KIWIBD
"Run with exponentially-decreasing powerlosses.",
"Run with linearly-decreasing powerlosses.", "Run with linearly-decreasing powerlosses.",
"Run all powerloss permutations, this may take a while.", "Run with exponentially-decreasing powerlosses.",
"Run all permutations of n powerlosses.", "Run all permutations of n powerlosses.",
"Run custom comma-separated set of powerlosses.", "Run all powerloss permutations, this may take a while.",
"Run explicit list of powerlosses.",
"Run explicit range of powerlosses.",
"Run explicit range of 2^n powerlosses.",
"Run custom leb16-encoded set of powerlosses.", "Run custom leb16-encoded set of powerlosses.",
#endif #endif
}; };
// default to -Pnone,linear, which provides a good heuristic while still // default to -Pnone -Plinear, which provides a good heuristic while
// running quickly // still running quickly
const test_powerloss_t *test_powerlosses = (const test_powerloss_t[]){ const test_powerloss_t test_default_powerlosses[] = {
{"none", run_powerloss_none, NULL, 0}, {"none", run_powerloss_none, NULL, 0},
#ifndef TEST_KIWIBD #ifndef TEST_KIWIBD
{"linear", run_powerloss_linear, NULL, SIZE_MAX}, {"linear", run_powerloss_linear, NULL, 0},
#endif #endif
}; };
#ifndef TEST_KIWIBD const size_t test_default_powerloss_count
size_t test_powerloss_count = 2; = sizeof(test_default_powerlosses)
#else / sizeof(test_powerloss_t);
size_t test_powerloss_count = 1;
#endif
size_t test_powerloss_capacity = 0;
static void list_powerlosses(void) { static void list_powerlosses(void) {
// at least size so that names fit // at least size so that names fit
unsigned name_width = 23; unsigned name_width = 23;
for (size_t i = 0; builtin_powerlosses[i].name; i++) { for (size_t i = 0; test_builtin_powerlosses[i].name; i++) {
size_t len = strlen(builtin_powerlosses[i].name); size_t len = strlen(test_builtin_powerlosses[i].name);
if (len > name_width) { if (len > name_width) {
name_width = len; name_width = len;
} }
@@ -1905,19 +1969,12 @@ static void list_powerlosses(void) {
printf("%-*s %s\n", name_width, "scenario", "description"); printf("%-*s %s\n", name_width, "scenario", "description");
size_t i = 0; size_t i = 0;
for (; builtin_powerlosses[i].name; i++) { for (; test_builtin_powerlosses[i].name; i++) {
printf("%-*s %s\n", printf("%-*s %s\n",
name_width, name_width,
builtin_powerlosses[i].name, test_builtin_powerlosses[i].name,
builtin_powerlosses_help[i]); test_builtin_powerlosses_help[i]);
} }
// a couple more options with special parsing
#ifndef TEST_KIWIBD
printf("%-*s %s\n", name_width, "1,2,3", builtin_powerlosses_help[i++]);
printf("%-*s %s\n", name_width, "{1,2,3}", builtin_powerlosses_help[i++]);
printf("%-*s %s\n", name_width, ":1248g1", builtin_powerlosses_help[i++]);
#endif
} }
@@ -2066,7 +2123,7 @@ const char *const help_text[] = {
"List the available powerloss scenarios.", "List the available powerloss scenarios.",
"Override a test define.", "Override a test define.",
"How deep to evaluate recursive defines before erroring.", "How deep to evaluate recursive defines before erroring.",
"Comma-separated list of powerloss scenarios to test.", "Specify a powerloss scenario to test.",
"Comma-separated range of permutations to run.", "Comma-separated range of permutations to run.",
"Ignore test filters.", "Ignore test filters.",
"Don't run internal tests.", "Don't run internal tests.",
@@ -2249,7 +2306,7 @@ int main(int argc, char **argv) {
if (*optarg == ',') { if (*optarg == ',') {
optarg += 1; optarg += 1;
step = strtoumax(optarg, &parsed, 0); step = strtoumax(optarg, &parsed, 0);
// allow empty string for stop=1 // allow empty string for step=1
if (parsed == optarg) { if (parsed == optarg) {
step = 1; step = 1;
} }
@@ -2343,172 +2400,131 @@ int main(int argc, char **argv) {
break; break;
case OPT_POWERLOSS:; case OPT_POWERLOSS:;
// reset our powerloss scenarios // allocate space
if (test_powerloss_capacity > 0) { test_powerloss_t *powerloss = mappend(
free((test_powerloss_t*)test_powerlosses); (void**)&test_powerlosses,
} sizeof(test_powerloss_t),
test_powerlosses = NULL; &test_powerloss_count,
test_powerloss_count = 0; &test_powerloss_capacity);
test_powerloss_capacity = 0;
// parse the comma separated list of powerloss scenarios // leb16-encoded permutation?
while (*optarg) { #ifndef TEST_KIWIBD
// allocate space if (*optarg == ':') {
test_powerloss_t *powerloss = mappend( optarg += 1;
(void**)&test_powerlosses, powerloss->name = "leb16";
sizeof(test_powerloss_t), powerloss->run = run_powerloss_list;
&test_powerloss_count, powerloss->cycles = NULL;
&test_powerloss_capacity); powerloss->cycle_count = 0;
// parse the powerloss scenario // special case for linear power cycles
optarg += strspn(optarg, " "); if (*optarg == 'x') {
powerloss->run = run_powerloss_linear;
// named powerloss scenario optarg += 1;
size_t len = strcspn(optarg, " ,");
for (size_t i = 0; builtin_powerlosses[i].name; i++) { // special case for log power cycles
if (len == strlen(builtin_powerlosses[i].name) } else if (*optarg == 'y') {
&& memcmp(optarg, powerloss->run = run_powerloss_log;
builtin_powerlosses[i].name,
len) == 0) {
*powerloss = builtin_powerlosses[i];
optarg += len;
goto powerloss_next;
}
}
// comma-separated permutation
#ifndef TEST_KIWIBD
if (*optarg == '{') {
test_powercycles_t *cycles = NULL;
size_t cycle_count = 0;
size_t cycle_capacity = 0;
char *s = optarg + 1;
while (true) {
parsed = NULL;
*(test_powercycles_t*)mappend(
(void**)&cycles,
sizeof(test_powercycles_t),
&cycle_count,
&cycle_capacity)
= strtoumax(s, &parsed, 0);
s = parsed + strspn(parsed, " ");
if (*s == ',') {
s += 1;
continue;
} else if (*s == '}') {
s += 1;
break;
} else {
goto powerloss_unknown;
}
}
*powerloss = (test_powerloss_t){
"explicit",
run_powerloss_cycles,
cycles,
cycle_count};
optarg = s;
goto powerloss_next;
}
#endif
// leb16-encoded permutation
#ifndef TEST_KIWIBD
if (*optarg == ':') {
// special case for linear power cycles
if (optarg[1] == 'x') {
size_t cycle_count = leb16_parse(optarg+2, &optarg);
*powerloss = (test_powerloss_t){
"linear",
run_powerloss_linear,
NULL,
cycle_count};
goto powerloss_next;
// special case for log power cycles
} else if (optarg[1] == 'y') {
size_t cycle_count = leb16_parse(optarg+2, &optarg);
*powerloss = (test_powerloss_t){
"log",
run_powerloss_log,
NULL,
cycle_count};
goto powerloss_next;
// otherwise explicit power cycles
} else {
test_powercycles_t *cycles = NULL;
size_t cycle_count = 0;
size_t cycle_capacity = 0;
char *s = optarg + 1;
while (true) {
parsed = NULL;
uintmax_t x = leb16_parse(s, &parsed);
if (parsed == s) {
break;
}
*(test_powercycles_t*)mappend(
(void**)&cycles,
sizeof(test_powercycles_t),
&cycle_count,
&cycle_capacity) = x;
s = parsed;
}
*powerloss = (test_powerloss_t){
"explicit",
run_powerloss_cycles,
cycles,
cycle_count};
optarg = s;
goto powerloss_next;
}
}
#endif
// exhaustive permutations
#ifndef TEST_KIWIBD
{
parsed = NULL;
size_t count = strtoumax(optarg, &parsed, 0);
if (parsed == optarg) {
goto powerloss_unknown;
}
*powerloss = (test_powerloss_t){
"exhaustive",
run_powerloss_exhaustive,
NULL,
count};
optarg = (char*)parsed;
goto powerloss_next;
}
#endif
powerloss_unknown:;
// unknown scenario?
fprintf(stderr, "error: unknown powerloss scenario: %s\n",
optarg);
exit(-1);
powerloss_next:;
optarg += strspn(optarg, " ");
if (*optarg == ',') {
optarg += 1; optarg += 1;
} else if (*optarg == '\0') {
break;
} else {
goto powerloss_unknown;
} }
// parse power cycles
test_spowercycles_t *cycles = NULL;
size_t cycle_count = 0;
size_t cycle_capacity = 0;
while (true) {
parsed = NULL;
intmax_t x = leb16_parse(optarg, &parsed);
if (parsed == optarg) {
break;
}
*(test_spowercycles_t*)mappend(
(void**)&cycles,
sizeof(test_spowercycles_t),
&cycle_count,
&cycle_capacity) = x;
optarg = parsed;
}
powerloss->cycles = cycles;
powerloss->cycle_count = cycle_count;
break;
}
#endif
// parse powerloss scenario
size_t len = strcspn(optarg, " (");
const test_powerloss_t *scenario = NULL;
for (size_t i = 0; test_builtin_powerlosses[i].name; i++) {
if (len == strcspn(test_builtin_powerlosses[i].name, " (")
&& memcmp(
optarg,
test_builtin_powerlosses[i].name,
len) == 0) {
scenario = &test_builtin_powerlosses[i];
break;
}
}
if (!scenario) {
goto invalid_powerloss;
}
// parse into string name + args, cannibalizing the
// arg in the process
powerloss->name = optarg;
char *paren = strchr(optarg + strspn(optarg, " "), '(');
optarg[len] = '\0';
powerloss->run = scenario->run;
powerloss->cycles = NULL;
powerloss->cycle_count = 0;
if ((paren && scenario->cycle_count == 0)
|| (!paren && scenario->cycle_count > 0)) {
goto invalid_powerloss;
}
if (paren) {
optarg = paren+1;
// parse comma-separated powerloss args
test_spowercycles_t *cycles = NULL;
size_t cycle_count = 0;
size_t cycle_capacity = 0;
while (cycle_count < scenario->cycle_count) {
parsed = NULL;
*(test_spowercycles_t*)mappend(
(void**)&cycles,
sizeof(test_spowercycles_t),
&cycle_count,
&cycle_capacity)
= strtoumax(optarg, &parsed, 0);
if (parsed == optarg) {
goto invalid_powerloss;
}
optarg = parsed + strspn(parsed, " ");
if (*optarg != ',') {
break;
}
optarg += 1;
}
if (*optarg != ')') {
goto invalid_powerloss;
}
optarg += 1;
powerloss->cycles = cycles;
powerloss->cycle_count = cycle_count;
} }
break; break;
invalid_powerloss:;
fprintf(stderr, "error: invalid powerloss: %s\n", optarg);
exit(-1);
case OPT_STEP:; case OPT_STEP:;
parsed = NULL; parsed = NULL;
test_step_start = strtoumax(optarg, &parsed, 0); test_step_start = strtoumax(optarg, &parsed, 0);
@@ -2713,69 +2729,45 @@ getopt_done:;
// special case for linear power cycles // special case for linear power cycles
#ifndef TEST_KIWIBD #ifndef TEST_KIWIBD
if (cycles_ && *cycles_ == 'x') { if (cycles_) {
char *parsed = NULL; powerloss.name = "leb16";
size_t cycle_count = leb16_parse(cycles_+1, &parsed); powerloss.run = run_powerloss_list;
if (parsed == cycles_+1) { powerloss.cycles = NULL;
fprintf(stderr, "error: " powerloss.cycle_count = 0;
"could not parse test cycles: %s\n",
cycles_); // special case for linear power cycles
exit(-1); if (*cycles_ == 'x') {
powerloss.run = run_powerloss_linear;
cycles_ += 1;
// special case for log power cycles
} else if (*cycles_ == 'y') {
powerloss.run = run_powerloss_log;
cycles_ += 1;
} }
cycles_ = parsed;
powerloss = (test_powerloss_t){
"linear",
run_powerloss_linear,
NULL,
cycle_count};
// special case for log power cycles
} else if (cycles_ && *cycles_ == 'y') {
char *parsed = NULL;
size_t cycle_count = leb16_parse(cycles_+1, &parsed);
if (parsed == cycles_+1) {
fprintf(stderr, "error: "
"could not parse test cycles: %s\n",
cycles_);
exit(-1);
}
cycles_ = parsed;
powerloss = (test_powerloss_t){
"log",
run_powerloss_log,
NULL,
cycle_count};
// otherwise explicit power cycles
} else if (cycles_) {
// parse power cycles // parse power cycles
test_powercycles_t *cycles = NULL; test_spowercycles_t *cycles = NULL;
size_t cycle_count = 0; size_t cycle_count = 0;
size_t cycle_capacity = 0; size_t cycle_capacity = 0;
while (*cycles_ != '\0') {
while (true) {
char *parsed = NULL; char *parsed = NULL;
*(test_powercycles_t*)mappend( intmax_t x = leb16_parse(cycles_, &parsed);
(void**)&cycles,
sizeof(test_powercycles_t),
&cycle_count,
&cycle_capacity)
= leb16_parse(cycles_, &parsed);
if (parsed == cycles_) { if (parsed == cycles_) {
fprintf(stderr, "error: " break;
"could not parse test cycles: %s\n",
cycles_);
exit(-1);
} }
*(test_spowercycles_t*)mappend(
(void**)&cycles,
sizeof(test_spowercycles_t),
&cycle_count,
&cycle_capacity) = x;
cycles_ = parsed; cycles_ = parsed;
} }
powerloss = (test_powerloss_t){ powerloss.cycles = cycles;
"explicit", powerloss.cycle_count = cycle_count;
run_powerloss_cycles,
cycles,
cycle_count};
} }
#endif #endif
} }
+1 -1
View File
@@ -139,7 +139,7 @@ extern const size_t test_suite_count;
// this variable tracks the number of powerlosses triggered during the // this variable tracks the number of powerlosses triggered during the
// current test permutation, this is useful for both tests and debugging // current test permutation, this is useful for both tests and debugging
extern volatile size_t TEST_PLS; extern volatile test_powercycles_t TEST_PLS;
// deterministic prng for pseudo-randomness in tests // deterministic prng for pseudo-randomness in tests
uint32_t test_prng(uint32_t *state); uint32_t test_prng(uint32_t *state);
+4 -2
View File
@@ -820,7 +820,8 @@ def find_runner(runner, id=None, main=True, **args):
if args.get('define_depth'): if args.get('define_depth'):
cmd.append('--define-depth=%s' % args['define_depth']) cmd.append('--define-depth=%s' % args['define_depth'])
if args.get('powerloss'): if args.get('powerloss'):
cmd.append('-P%s' % args['powerloss']) for powerloss in args['powerloss']:
cmd.append('-P%s' % powerloss)
if args.get('force'): if args.get('force'):
cmd.append('--force') cmd.append('--force')
if args.get('no_internal'): if args.get('no_internal'):
@@ -1733,7 +1734,8 @@ if __name__ == "__main__":
help="How deep to evaluate recursive defines before erroring.") help="How deep to evaluate recursive defines before erroring.")
test_parser.add_argument( test_parser.add_argument(
'-P', '--powerloss', '-P', '--powerloss',
help="Comma-separated list of powerloss scenarios to test.") action='append',
help="Specify a powerloss scenario to test.")
test_parser.add_argument( test_parser.add_argument(
'--force', '--force',
action='store_true', action='store_true',