Skip to content

Commit 878e897

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 b0a4cd0 commit 878e897

File tree

2 files changed

+77
-19
lines changed

2 files changed

+77
-19
lines changed

src/unit_test.c

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ int COUNT = 16;
3636

3737
static int parse_jobs_count(const char* key, const char* value, struct Args* out);
3838
static int parse_iterations(const char* arg);
39+
static int parse_target(const char* value, struct TestFramework* tf);
3940

4041
/*
4142
* Main entry point for handling command-line arguments.
@@ -61,6 +62,10 @@ static int parse_arg(const char* key, const char* value, struct TestFramework* t
6162
tf->args.custom_seed = (!value || strcmp(value, "NULL") == 0) ? NULL : value;
6263
return 0;
6364
}
65+
/* Test target */
66+
if (strcmp(key, "t") == 0 || strcmp(key, "target") == 0) {
67+
return parse_target(value, tf);
68+
}
6469

6570
/* Unknown key: report just so typos don’t silently pass. */
6671
printf("Unknown argument '-%s=%s'\n", key, value);
@@ -75,6 +80,8 @@ static void help(void) {
7580
printf(" -j=<num>, -jobs=<num> Number of parallel worker processes (default: 0 = sequential)\n");
7681
printf(" -iter=<num>, -iterations=<num> Number of iterations for each test (default: 16)\n");
7782
printf(" -seed=<hex> Set a specific RNG seed (default: random)\n");
83+
printf(" -target=<test name>, -t=<name> Run a specific test (can be provided multiple times)\n");
84+
printf(" -target=<module name>, -t=<module> Run all tests within a specific module (can be provided multiple times)\n");
7885
printf("\n");
7986
printf("Notes:\n");
8087
printf(" - All arguments must be provided in the form '-key=value'.\n");
@@ -116,6 +123,31 @@ static int parse_iterations(const char* arg) {
116123
return 0;
117124
}
118125

126+
static int parse_target(const char* value, struct TestFramework* tf) {
127+
TestRef i = {/*idx_module=*/0, /*idx_test=*/0};
128+
if (tf->args.targets.size >= MAX_ARGS) {
129+
fprintf(stderr, "Too many -target args (max: %d)\n", MAX_ARGS);
130+
return -1;
131+
}
132+
/* Find test index in the registry */
133+
for (i.group = 0; i.group < tf->num_modules; i.group++) {
134+
struct TestModule* module = &tf->registry_modules[i.group];
135+
int add_all = strcmp(value, module->name) == 0; /* select all from module */
136+
for (i.idx = 0; i.idx < module->size; i.idx++) {
137+
if (add_all || strcmp(value, module->data[i.idx].name) == 0) {
138+
tf->args.targets.slots[tf->args.targets.size] = i;
139+
tf->args.targets.size++;
140+
/* Matched a single test, we're done */
141+
if (!add_all) return 0;
142+
}
143+
}
144+
/* If add_all was true, we added all tests in the module, so return */
145+
if (add_all) return 0;
146+
}
147+
fprintf(stderr, "Error: target not found: '%s'\n", value);
148+
return -1;
149+
}
150+
119151
/* Read args; all must be "-key=value" */
120152
static int read_args(int argc, char** argv, int start, struct TestFramework* tf) {
121153
int i;
@@ -149,13 +181,10 @@ static void run_test(const struct TestEntry* t) {
149181

150182
/* Process tests in sequential order */
151183
static int run_sequential(struct TestFramework* tf) {
152-
TestRef ref;
153-
struct TestModule* mdl;
154-
for (ref.group = 0; ref.group < tf->num_modules; ref.group++) {
155-
mdl = &tf->registry_modules[ref.group];
156-
for (ref.idx = 0; ref.idx < mdl->size; ref.idx++) {
157-
run_test(&mdl->data[ref.idx]);
158-
}
184+
int it;
185+
for (it = 0; it < tf->args.targets.size; it++) {
186+
TestRef* index = &tf->args.targets.slots[it];
187+
run_test(&tf->registry_modules[index->group].data[index->idx]);
159188
}
160189
return EXIT_SUCCESS;
161190
}
@@ -173,7 +202,7 @@ static int run_concurrent(struct TestFramework* tf) {
173202
/* Loop iterator */
174203
int it;
175204
/* Loop ref */
176-
TestRef ref;
205+
TestRef* ref;
177206
/* Launch worker processes */
178207
for (it = 0; it < tf->args.num_processes; it++) {
179208
pid_t pid;
@@ -190,9 +219,10 @@ static int run_concurrent(struct TestFramework* tf) {
190219

191220
if (pid == 0) {
192221
/* Child worker: run tests assigned via pipe */
222+
TestRef tref;
193223
close(pipes[it][1]); /* Close write end */
194-
while (read(pipes[it][0], &ref, sizeof(ref)) == sizeof(ref)) {
195-
run_test(&tf->registry_modules[ref.group].data[ref.idx]);
224+
while (read(pipes[it][0], &tref, sizeof(tref)) == sizeof(tref)) {
225+
run_test(&tf->registry_modules[tref.group].data[tref.idx]);
196226
}
197227
_exit(EXIT_SUCCESS); /* finish child process */
198228
} else {
@@ -204,15 +234,13 @@ static int run_concurrent(struct TestFramework* tf) {
204234

205235
/* Now that we have all sub-processes, distribute workload in round-robin */
206236
worker_idx = 0;
207-
for (ref.group = 0; ref.group < tf->num_modules; ref.group++) {
208-
struct TestModule* mdl = &tf->registry_modules[ref.group];
209-
for (ref.idx = 0; ref.idx < mdl->size; ref.idx++) {
210-
if (write(pipes[worker_idx][1], &ref, sizeof(ref)) == -1) {
211-
perror("Error during workload distribution");
212-
return EXIT_FAILURE;
213-
}
214-
if (++worker_idx >= tf->args.num_processes) worker_idx = 0;
237+
for (it = 0; it < tf->args.targets.size; it++) {
238+
ref = &tf->args.targets.slots[it];
239+
if (write(pipes[worker_idx][1], ref, sizeof(*ref)) == -1) {
240+
perror("Error during workload distribution");
241+
return EXIT_FAILURE;
215242
}
243+
if (++worker_idx >= tf->args.num_processes) worker_idx = 0;
216244
}
217245

218246
/* Close all pipes to signal workers to exit */
@@ -235,6 +263,7 @@ static int tf_init(struct TestFramework* tf, int argc, char** argv)
235263
/* Initialize command-line options */
236264
tf->args.num_processes = 0;
237265
tf->args.custom_seed = NULL;
266+
tf->args.targets.size = 0;
238267

239268
/* Disable buffering for stdout to improve reliability of getting
240269
* diagnostic information. Happens right at the start of main because
@@ -282,11 +311,31 @@ static int tf_run(struct TestFramework* tf) {
282311
/* Initial test time */
283312
int64_t start_time = gettime_i64(); /* maybe move this after the slots set */
284313

314+
/* Populate targets with all tests if none were explicitly specified */
315+
if (tf->args.targets.size == 0) {
316+
TestRef ref;
317+
int slot = 0;
318+
for (ref.group = 0; ref.group < tf->num_modules; ref.group++) {
319+
struct TestModule* group = &tf->registry_modules[ref.group];
320+
for (ref.idx = 0; ref.idx < group->size; ref.idx++) {
321+
tf->args.targets.slots[slot++] = ref;
322+
if (slot >= MAX_ARGS) {
323+
fprintf(stderr, "Error: Number of tests (%d) exceeds MAX_ARGS (%d). "
324+
"Increase MAX_ARGS to accommodate all tests.\n", slot, MAX_ARGS);
325+
return EXIT_FAILURE;
326+
}
327+
}
328+
}
329+
tf->args.targets.size = slot;
330+
}
331+
285332
/* Run test RNG tests (must run before we really initialize the test RNG) */
286333
/* Note: currently, these tests are executed sequentially because there */
287334
/* is really only one test. */
288335
for (t = tf->registry_no_ctx; t->name; t++) {
289-
run_test(t);
336+
if (tf->args.targets.size == 0) { /* future: support filtering */
337+
run_test(t);
338+
}
290339
}
291340

292341
/* Initialize test RNG and library contexts */

src/unit_test.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,21 @@ typedef struct {
6969
int idx;
7070
} TestRef;
7171

72+
struct Targets {
73+
/* Target tests indexes */
74+
TestRef slots[MAX_ARGS];
75+
/* Next available slot */
76+
int size;
77+
};
78+
7279
/* --- Command-line args --- */
7380
struct Args {
7481
/* 0 => sequential; 1..MAX_SUBPROCESSES => parallel workers */
7582
int num_processes;
7683
/* Specific RNG seed */
7784
const char* custom_seed;
85+
/* Target tests indexes */
86+
struct Targets targets;
7887
};
7988

8089
struct TestFramework {

0 commit comments

Comments
 (0)