Skip to content

Commit a5f816e

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 fa8a57c commit a5f816e

File tree

2 files changed

+75
-24
lines changed

2 files changed

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

204236
/* Process tests in sequential order */
205237
static int run_sequential(struct tf_framework* tf) {
206-
tf_test_ref 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-
}
238+
int it;
239+
for (it = 0; it < tf->args.targets.size; it++) {
240+
run_test(tf->args.targets.slots[it]);
213241
}
214242
return EXIT_SUCCESS;
215243
}
@@ -222,7 +250,7 @@ static int run_concurrent(struct tf_framework* tf) {
222250
int pipefd[2];
223251
int status = EXIT_SUCCESS;
224252
int it; /* loop iterator */
225-
tf_test_ref ref; /* test index */
253+
unsigned char idx; /* test index */
226254

227255
if (pipe(pipefd) != 0) {
228256
perror("Error during pipe setup");
@@ -239,8 +267,8 @@ static int run_concurrent(struct tf_framework* tf) {
239267
if (pid == 0) {
240268
/* Child worker: read jobs from the shared pipe */
241269
close(pipefd[1]); /* children never write */
242-
while (read(pipefd[0], &ref, sizeof(ref)) == sizeof(ref)) {
243-
run_test(&tf->registry_modules[ref.group].data[ref.idx]);
270+
while (read(pipefd[0], &idx, sizeof(idx)) == sizeof(idx)) {
271+
run_test(tf->args.targets.slots[(int)idx]);
244272
}
245273
_exit(EXIT_SUCCESS); /* finish child process */
246274
} else {
@@ -251,14 +279,12 @@ static int run_concurrent(struct tf_framework* tf) {
251279

252280
/* Parent: write all tasks into the pipe */
253281
close(pipefd[0]); /* close read end */
254-
for (ref.group = 0; ref.group < tf->num_modules; ref.group++) {
255-
const struct tf_test_module* mdl = &tf->registry_modules[ref.group];
256-
for (ref.idx = 0; ref.idx < mdl->size; ref.idx++) {
257-
if (write(pipefd[1], &ref, sizeof(ref)) == -1) {
258-
perror("Error during workload distribution");
259-
close(pipefd[1]);
260-
return EXIT_FAILURE;
261-
}
282+
for (it = 0; it < tf->args.targets.size; it++) {
283+
idx = (unsigned char)it;
284+
if (write(pipefd[1], &idx, sizeof(idx)) == -1) {
285+
perror("Error during workload distribution");
286+
close(pipefd[1]);
287+
return EXIT_FAILURE;
262288
}
263289
}
264290
/* Close write end to signal EOF */
@@ -287,6 +313,7 @@ static int tf_init(struct tf_framework* tf, int argc, char** argv)
287313
tf->args.num_processes = 0;
288314
tf->args.custom_seed = NULL;
289315
tf->args.help = 0;
316+
tf->args.targets.size = 0;
290317

291318
/* Disable buffering for stdout to improve reliability of getting
292319
* diagnostic information. Happens right at the start of main because
@@ -331,19 +358,40 @@ static int tf_init(struct tf_framework* tf, int argc, char** argv)
331358
static int tf_run(struct tf_framework* tf) {
332359
/* Process exit status */
333360
int status;
361+
/* Whether to run all tests */
362+
int run_all;
334363
/* Loop iterator */
335364
int it;
336365
/* Initial test time */
337366
int64_t start_time = gettime_i64();
338367

368+
/* Populate targets with all tests if none were explicitly specified */
369+
run_all = tf->args.targets.size == 0;
370+
if (run_all) {
371+
int group, idx;
372+
for (group = 0; group < tf->num_modules; group++) {
373+
const struct tf_test_module* module = &tf->registry_modules[group];
374+
for (idx = 0; idx < module->size; idx++) {
375+
if (tf->args.targets.size >= MAX_ARGS) {
376+
fprintf(stderr, "Internal Error: Number of tests (%d) exceeds MAX_ARGS (%d). "
377+
"Increase MAX_ARGS to accommodate all tests.\n", tf->args.targets.size, MAX_ARGS);
378+
return EXIT_FAILURE;
379+
}
380+
tf->args.targets.slots[tf->args.targets.size++] = &module->data[idx];
381+
}
382+
}
383+
}
384+
339385
/* Log configuration */
340386
print_args(&tf->args);
341387

342388
/* Run test RNG tests (must run before we really initialize the test RNG) */
343389
/* Note: currently, these tests are executed sequentially because there */
344390
/* is really only one test. */
345391
for (it = 0; tf->registry_no_rng && it < tf->registry_no_rng->size; it++) {
346-
run_test(&tf->registry_no_rng->data[it]);
392+
if (run_all) { /* future: support filtering */
393+
run_test(&tf->registry_no_rng->data[it]);
394+
}
347395
}
348396

349397
/* Initialize test RNG and library contexts */

src/unit_test.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,12 @@ struct tf_test_module {
6262
typedef int (*setup_ctx_fn)(void);
6363
typedef int (*teardown_fn)(void);
6464

65-
/* Reference to a test in the registry. Group index and test index */
66-
typedef struct {
67-
int group;
68-
int idx;
69-
} tf_test_ref;
65+
struct tf_targets {
66+
/* Target tests indexes */
67+
const struct tf_test_entry* slots[MAX_ARGS];
68+
/* Next available slot */
69+
int size;
70+
};
7071

7172
/* --- Command-line args --- */
7273
struct tf_args {
@@ -76,6 +77,8 @@ struct tf_args {
7677
const char* custom_seed;
7778
/* Whether to print the help msg */
7879
int help;
80+
/* Target tests indexes */
81+
struct tf_targets targets;
7982
};
8083

8184
/* --------------------------------------------------------- */

0 commit comments

Comments
 (0)