Skip to content

Commit 8548b1f

Browse files
committed
test: support running specific tests/modules targets
Add support for specifying single tests or modules to run via the "--target" or "-t" command-line option. Multiple targets can be provided; only the specified tests or all tests in the specified module/s will run instead of the full suite. Examples: -t=<test name> runs an specific test. -t=<module name> runs all tests within the specified module. Both options can be provided multiple times.
1 parent 74ef7c6 commit 8548b1f

File tree

2 files changed

+76
-19
lines changed

2 files changed

+76
-19
lines changed

src/unit_test.c

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ int COUNT = 16;
2525
static int parse_jobs_count(const char* key, const char* value, struct tf_framework* tf);
2626
static int parse_iterations(const char* key, const char* value, struct tf_framework* tf);
2727
static int parse_seed(const char* key, const char* value, struct tf_framework* tf);
28+
static int parse_target(const char* key, const char* value, struct tf_framework* tf);
2829

2930
/* Mapping table: key -> handler */
3031
typedef int (*ArgHandler)(const char* key, const char* value, struct tf_framework* tf);
@@ -41,6 +42,7 @@ struct ArgMap {
4142
* converted to the appropriate type, and stored in 'tf->args' struct.
4243
*/
4344
static struct ArgMap arg_map[] = {
45+
{ "t", parse_target }, { "target", parse_target },
4446
{ "j", parse_jobs_count }, { "jobs", parse_jobs_count },
4547
{ "i", parse_iterations }, { "iterations", parse_iterations },
4648
{ "seed", parse_seed },
@@ -82,6 +84,8 @@ static void help(void) {
8284
printf(" --jobs=<num>, -j=<num> Number of parallel worker processes (default: 0 = sequential)\n");
8385
printf(" --iterations=<num>, -i=<num> Number of iterations for each test (default: 16)\n");
8486
printf(" --seed=<hex> Set a specific RNG seed (default: random)\n");
87+
printf(" --target=<test name>, -t=<name> Run a specific test (can be provided multiple times)\n");
88+
printf(" --target=<module name>, -t=<module> Run all tests within a specific module (can be provided multiple times)\n");
8589
printf("\n");
8690
printf("Notes:\n");
8791
printf(" - All arguments must be provided in the form '--key=value', '-key=value' or '-k=value'.\n");
@@ -152,6 +156,32 @@ static const char* normalize_key(const char* arg, const char** err_msg) {
152156
return key;
153157
}
154158

159+
static int parse_target(const char* key, const char* value, struct tf_framework* tf) {
160+
TestRef i = {/*group=*/0, /*idx=*/0};
161+
UNUSED(key);
162+
/* Find test index in the registry */
163+
for (i.group = 0; i.group < tf->num_modules; i.group++) {
164+
const struct tf_test_module* module = &tf->registry_modules[i.group];
165+
int add_all = strcmp(value, module->name) == 0; /* select all from module */
166+
for (i.idx = 0; i.idx < module->size; i.idx++) {
167+
if (add_all || strcmp(value, module->data[i.idx].name) == 0) {
168+
if (tf->args.targets.size >= MAX_ARGS) {
169+
fprintf(stderr, "Too many -target args (max: %d)\n", MAX_ARGS);
170+
return -1;
171+
}
172+
tf->args.targets.slots[tf->args.targets.size++] = i;
173+
/* Matched a single test, we're done */
174+
if (!add_all) return 0;
175+
}
176+
}
177+
/* If add_all was true, we added all tests in the module, so return */
178+
if (add_all) return 0;
179+
}
180+
fprintf(stderr, "Error: target '%s' not found (missing or module disabled).\n"
181+
"Run program with -print_tests option to display available tests and modules.\n", value);
182+
return -1;
183+
}
184+
155185
/* Read args: all must be in the form -key=value, --key=value or -key=value */
156186
static int read_args(int argc, char** argv, int start, struct tf_framework* tf) {
157187
int i;
@@ -203,13 +233,10 @@ static void run_test(const struct tf_test_entry* t) {
203233

204234
/* Process tests in sequential order */
205235
static int run_sequential(struct tf_framework* tf) {
206-
TestRef ref;
207-
const struct tf_test_module* mdl;
208-
for (ref.group = 0; ref.group < tf->num_modules; ref.group++) {
209-
mdl = &tf->registry_modules[ref.group];
210-
for (ref.idx = 0; ref.idx < mdl->size; ref.idx++) {
211-
run_test(&mdl->data[ref.idx]);
212-
}
236+
int it;
237+
for (it = 0; it < tf->args.targets.size; it++) {
238+
const TestRef* index = &tf->args.targets.slots[it];
239+
run_test(&tf->registry_modules[index->group].data[index->idx]);
213240
}
214241
return EXIT_SUCCESS;
215242
}
@@ -227,7 +254,7 @@ static int run_concurrent(struct tf_framework* tf) {
227254
/* Loop iterator */
228255
int it;
229256
/* Loop ref */
230-
TestRef ref;
257+
TestRef* ref;
231258
/* Launch worker processes */
232259
for (it = 0; it < tf->args.num_processes; it++) {
233260
pid_t pid;
@@ -244,9 +271,10 @@ static int run_concurrent(struct tf_framework* tf) {
244271

245272
if (pid == 0) {
246273
/* Child worker: run tests assigned via pipe */
274+
TestRef tref;
247275
close(pipes[it][1]); /* Close write end */
248-
while (read(pipes[it][0], &ref, sizeof(ref)) == sizeof(ref)) {
249-
run_test(&tf->registry_modules[ref.group].data[ref.idx]);
276+
while (read(pipes[it][0], &tref, sizeof(tref)) == sizeof(tref)) {
277+
run_test(&tf->registry_modules[tref.group].data[tref.idx]);
250278
}
251279
_exit(EXIT_SUCCESS); /* finish child process */
252280
} else {
@@ -258,15 +286,13 @@ static int run_concurrent(struct tf_framework* tf) {
258286

259287
/* Now that we have all sub-processes, distribute workload in round-robin */
260288
worker_idx = 0;
261-
for (ref.group = 0; ref.group < tf->num_modules; ref.group++) {
262-
const struct tf_test_module* mdl = &tf->registry_modules[ref.group];
263-
for (ref.idx = 0; ref.idx < mdl->size; ref.idx++) {
264-
if (write(pipes[worker_idx][1], &ref, sizeof(ref)) == -1) {
265-
perror("Error during workload distribution");
266-
return EXIT_FAILURE;
267-
}
268-
if (++worker_idx >= tf->args.num_processes) worker_idx = 0;
289+
for (it = 0; it < tf->args.targets.size; it++) {
290+
ref = &tf->args.targets.slots[it];
291+
if (write(pipes[worker_idx][1], ref, sizeof(*ref)) == -1) {
292+
perror("Error during workload distribution");
293+
return EXIT_FAILURE;
269294
}
295+
if (++worker_idx >= tf->args.num_processes) worker_idx = 0;
270296
}
271297

272298
/* Close all pipes to signal workers to exit */
@@ -295,6 +321,7 @@ static int tf_init(struct tf_framework* tf, int argc, char** argv)
295321
tf->args.num_processes = 0;
296322
tf->args.custom_seed = NULL;
297323
tf->args.help = 0;
324+
tf->args.targets.size = 0;
298325

299326
/* Disable buffering for stdout to improve reliability of getting
300327
* diagnostic information. Happens right at the start of main because
@@ -339,19 +366,40 @@ static int tf_init(struct tf_framework* tf, int argc, char** argv)
339366
static int tf_run(struct tf_framework* tf) {
340367
/* Process exit status */
341368
int status;
369+
/* Whether to run all tests */
370+
int run_all;
342371
/* Loop iterator */
343372
int it;
344373
/* Initial test time */
345374
int64_t start_time = gettime_i64();
346375

376+
/* Populate targets with all tests if none were explicitly specified */
377+
run_all = tf->args.targets.size == 0;
378+
if (run_all) {
379+
TestRef ref;
380+
for (ref.group = 0; ref.group < tf->num_modules; ref.group++) {
381+
const struct tf_test_module* group = &tf->registry_modules[ref.group];
382+
for (ref.idx = 0; ref.idx < group->size; ref.idx++) {
383+
if (tf->args.targets.size >= MAX_ARGS) {
384+
fprintf(stderr, "Internal Error: Number of tests (%d) exceeds MAX_ARGS (%d). "
385+
"Increase MAX_ARGS to accommodate all tests.\n", tf->args.targets.size, MAX_ARGS);
386+
return EXIT_FAILURE;
387+
}
388+
tf->args.targets.slots[tf->args.targets.size++] = ref;
389+
}
390+
}
391+
}
392+
347393
/* Log configuration */
348394
print_args(&tf->args);
349395

350396
/* Run test RNG tests (must run before we really initialize the test RNG) */
351397
/* Note: currently, these tests are executed sequentially because there */
352398
/* is really only one test. */
353399
for (it = 0; tf->registry_no_rng && it < tf->registry_no_rng->size; it++) {
354-
run_test(&tf->registry_no_rng->data[it]);
400+
if (run_all) { /* future: support filtering */
401+
run_test(&tf->registry_no_rng->data[it]);
402+
}
355403
}
356404

357405
/* Initialize test RNG and library contexts */

src/unit_test.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ typedef struct {
6868
int idx;
6969
} TestRef;
7070

71+
struct Targets {
72+
/* Target tests indexes */
73+
TestRef slots[MAX_ARGS];
74+
/* Next available slot */
75+
int size;
76+
};
77+
7178
/* --- Command-line args --- */
7279
struct tf_args {
7380
/* 0 => sequential; 1..MAX_SUBPROCESSES => parallel workers */
@@ -76,6 +83,8 @@ struct tf_args {
7683
const char* custom_seed;
7784
/* Whether to print the help msg */
7885
int help;
86+
/* Target tests indexes */
87+
struct Targets targets;
7988
};
8089

8190
/* --------------------------------------------------------- */

0 commit comments

Comments
 (0)