Skip to content

Commit 953f7b0

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 0302c1a commit 953f7b0

File tree

2 files changed

+85
-24
lines changed

2 files changed

+85
-24
lines changed

src/unit_test.c

Lines changed: 77 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,26 +235,32 @@ 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
}
216244

217245
#if defined(SUPPORTS_CONCURRENCY)
246+
static const int MAX_TARGETS = 255;
247+
218248
/* Process tests in parallel */
219249
static int run_concurrent(struct tf_framework* tf) {
220250
/* Sub-processes info */
221251
pid_t workers[MAX_SUBPROCESSES];
222252
int pipefd[2];
223253
int status = EXIT_SUCCESS;
224254
int it; /* loop iterator */
225-
tf_test_ref ref; /* test index */
255+
unsigned char idx; /* test index */
256+
257+
if (tf->args.targets.size > MAX_TARGETS) {
258+
fprintf(stderr, "Internal Error: the number of targets (%d) exceeds the maximum supported (%d). "
259+
"If you need more, extend 'run_concurrent()' to handle additional targets.\n",
260+
tf->args.targets.size, MAX_TARGETS);
261+
exit(EXIT_FAILURE);
262+
}
263+
226264

227265
if (pipe(pipefd) != 0) {
228266
perror("Error during pipe setup");
@@ -239,8 +277,8 @@ static int run_concurrent(struct tf_framework* tf) {
239277
if (pid == 0) {
240278
/* Child worker: read jobs from the shared pipe */
241279
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]);
280+
while (read(pipefd[0], &idx, sizeof(idx)) == sizeof(idx)) {
281+
run_test(tf->args.targets.slots[(int)idx]);
244282
}
245283
_exit(EXIT_SUCCESS); /* finish child process */
246284
} else {
@@ -251,14 +289,12 @@ static int run_concurrent(struct tf_framework* tf) {
251289

252290
/* Parent: write all tasks into the pipe */
253291
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-
}
292+
for (it = 0; it < tf->args.targets.size; it++) {
293+
idx = (unsigned char)it;
294+
if (write(pipefd[1], &idx, sizeof(idx)) == -1) {
295+
perror("Error during workload distribution");
296+
close(pipefd[1]);
297+
return EXIT_FAILURE;
262298
}
263299
}
264300
/* Close write end to signal EOF */
@@ -287,6 +323,7 @@ static int tf_init(struct tf_framework* tf, int argc, char** argv)
287323
tf->args.num_processes = 0;
288324
tf->args.custom_seed = NULL;
289325
tf->args.help = 0;
326+
tf->args.targets.size = 0;
290327

291328
/* Disable buffering for stdout to improve reliability of getting
292329
* diagnostic information. Happens right at the start of main because
@@ -331,19 +368,40 @@ static int tf_init(struct tf_framework* tf, int argc, char** argv)
331368
static int tf_run(struct tf_framework* tf) {
332369
/* Process exit status */
333370
int status;
371+
/* Whether to run all tests */
372+
int run_all;
334373
/* Loop iterator */
335374
int it;
336375
/* Initial test time */
337376
int64_t start_time = gettime_i64();
338377

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

342398
/* Run test RNG tests (must run before we really initialize the test RNG) */
343399
/* Note: currently, these tests are executed sequentially because there */
344400
/* is really only one test. */
345401
for (it = 0; tf->registry_no_rng && it < tf->registry_no_rng->size; it++) {
346-
run_test(&tf->registry_no_rng->data[it]);
402+
if (run_all) { /* future: support filtering */
403+
run_test(&tf->registry_no_rng->data[it]);
404+
}
347405
}
348406

349407
/* 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)