10#if defined(SUPPORTS_CONCURRENCY)
21# pragma GCC diagnostic push
22# pragma GCC diagnostic warning "-Wunused-function"
25#define UNUSED(x) (void)(x)
61 printf(
"iterations = %d\n",
COUNT);
62 printf(
"jobs = %d. %s execution.\n",
args->num_processes,
args->num_processes > 1 ?
"Parallel" :
"Sequential");
87 printf(
"Usage: ./tests [options]\n\n");
88 printf(
"Run the test suite for the project with optional configuration.\n\n");
90 printf(
" --help, -h Show this help message\n");
91 printf(
" --list_tests, -l Display list of all available tests and modules\n");
92 printf(
" --jobs=<num>, -j=<num> Number of parallel worker processes (default: 0 = sequential)\n");
93 printf(
" --iterations=<num>, -i=<num> Number of iterations for each test (default: 16)\n");
94 printf(
" --seed=<hex> Set a specific RNG seed (default: random)\n");
95 printf(
" --target=<test name>, -t=<name> Run a specific test (can be provided multiple times)\n");
96 printf(
" --target=<module name>, -t=<module> Run all tests within a specific module (can be provided multiple times)\n");
97 printf(
" --log=<0|1> Enable or disable test execution logging (default: 0 = disabled)\n");
100 printf(
" - All arguments must be provided in the form '--key=value', '-key=value' or '-k=value'.\n");
101 printf(
" - Single or double dashes are allowed for multi character options.\n");
102 printf(
" - Unknown arguments are reported but ignored.\n");
103 printf(
" - Sequential execution occurs if -jobs=0 or unspecified.\n");
104 printf(
" - Iterations and seed can also be passed as positional arguments before any other argument for backward compatibility.\n");
110 printf(
"\nAvailable tests (%d modules):\n",
tf->num_modules);
111 printf(
"========================================\n");
112 for (
m = 0;
m <
tf->num_modules;
m++) {
114 printf(
"Module: %s (%d tests)\n",
mod->name,
mod->size);
115 for (
t = 0;
t <
mod->size;
t++) {
116 printf(
"\t[%3d] %s\n", total + 1,
mod->data[
t].name);
119 printf(
"----------------------------------------\n");
121 printf(
"\nRun specific module: ./tests -t=<module_name>\n");
122 printf(
"Run specific test: ./tests -t=<test_name>\n\n");
136 tf->args.num_processes = (
int) val;
142 if (!value)
return 0;
145 fputs(
"An iteration count of 0 or less is not allowed.\n",
stderr);
153 tf->args.custom_seed = (!value ||
strcmp(value,
"NULL") == 0) ?
NULL : value;
159 tf->args.logging = value &&
strcmp(value,
"1") == 0;
166 if (!arg || arg[0] !=
'-') {
167 *err_msg =
"missing initial dash";
171 if (arg[1] !=
'-')
return arg + 1;
174 if (arg[2] ==
'\0') {
175 *err_msg =
"missing option name after double dash";
180 *err_msg =
"too many leading dashes";
185 if (key[1] ==
'\0') {
186 *err_msg =
"short option cannot use double dash";
197 for (group = 0; group <
tf->num_modules; group++) {
198 const struct tf_test_module*
module = &tf->registry_modules[group];
200 for (idx = 0; idx <
module->size; idx++) {
201 entry = &
module->data[idx];
207 tf->args.targets.slots[
tf->args.targets.size++] = entry;
215 fprintf(
stderr,
"Error: target '%s' not found (missing or module disabled).\n"
216 "Run program with -list_tests option to display available tests and modules.\n", value);
226 const char* err_msg =
"unknown error";
227 for (i = start; i <
argc; i++) {
235 if (!key || *key ==
'\0') {
243 if (
strcmp(key,
"h") == 0 ||
strcmp(key,
"help") == 0) {
247 if (
strcmp(key,
"l") == 0 ||
strcmp(key,
"list_tests") == 0) {
248 tf->args.list_tests = 1;
257 if (!value || *value ==
'\0') {
269 printf(
"Running %s..\n",
t->name);
271 printf(
"Test %s PASSED (%.3f sec)\n",
t->name, (
double)(
gettime_i64() - start_time) / 1000000);
279 for (it = 0; it <
tf->args.targets.size; it++) {
280 tf->fn_run_test(
tf->args.targets.slots[it]);
285#if defined(SUPPORTS_CONCURRENCY)
298 fprintf(
stderr,
"Internal Error: the number of targets (%d) exceeds the maximum supported (%d). "
299 "If you need more, extend 'run_concurrent()' to handle additional targets.\n",
306 perror(
"Error during pipe setup");
311 for (it = 0; it <
tf->args.num_processes; it++) {
314 perror(
"Error during process fork");
320 while (read(
pipefd[0], &idx,
sizeof(idx)) ==
sizeof(idx)) {
321 tf->fn_run_test(
tf->args.targets.slots[(
int)idx]);
332 for (it = 0; it <
tf->args.targets.size; it++) {
333 idx = (
unsigned char)it;
334 if (write(
pipefd[1], &idx,
sizeof(idx)) == -1) {
335 perror(
"Error during workload distribution");
343 for (it = 0; it <
tf->args.num_processes; it++) {
357 if (
tf->registry_modules ==
NULL ||
tf->num_modules <= 0) {
358 fprintf(
stderr,
"Error: tests registry not provided or empty\n");
363 tf->args.num_processes = 0;
364 tf->args.custom_seed =
NULL;
366 tf->args.targets.size = 0;
367 tf->args.list_tests = 0;
368 tf->args.logging = 0;
391 if (
argv[1][0] !=
'-') {
406 if (
tf->args.list_tests) {
426 if (!
tf->fn_run_test) {
427 fprintf(
stderr,
"Error: No test runner set. You must call 'tf_init' first to initialize the framework "
428 "or manually assign 'fn_run_test' before calling 'tf_run'.\n");
436 for (group = 0; group <
tf->num_modules; group++) {
437 const struct tf_test_module*
module = &tf->registry_modules[group];
438 for (idx = 0; idx <
module->size; idx++) {
440 fprintf(
stderr,
"Internal Error: Number of tests (%d) exceeds MAX_ARGS (%d). "
441 "Increase MAX_ARGS to accommodate all tests.\n",
tf->args.targets.size,
MAX_ARGS);
444 tf->args.targets.slots[
tf->args.targets.size++] = &
module->data[idx];
449 if (!
tf->args.logging) printf(
"Tests running silently. Use '-log=1' to enable detailed logging\n");
457 for (it = 0;
tf->registry_no_rng && it <
tf->registry_no_rng->size; it++) {
459 tf->fn_run_test(&
tf->registry_no_rng->data[it]);
468 if (
tf->args.num_processes <= 1) {
471#if defined(SUPPORTS_CONCURRENCY)
474 fputs(
"Parallel execution not supported on your system. Running sequentially...\n",
stderr);
480 printf(
"Total execution time: %.3f seconds\n", (
double)(
gettime_i64() - start_time) / 1000000);
487# pragma GCC diagnostic pop
static void testrand_init(const char *hexseed)
Initialize the test RNG using (hex encoded) array up to 16 bytes, or randomly if hexseed is NULL.
static int64_t gettime_i64(void)
static struct ArgMap arg_map[]
static const char * normalize_key(const char *arg, const char **err_msg)
static void run_test_log(const struct tf_test_entry *t)
static int parse_arg(const char *key, const char *value, struct tf_framework *tf)
static int parse_seed(const char *key, const char *value, struct tf_framework *tf)
static void run_test(const struct tf_test_entry *t)
static int tf_init(struct tf_framework *tf, int argc, char **argv)
static int parse_iterations(const char *key, const char *value, struct tf_framework *tf)
static int parse_logging(const char *key, const char *value, struct tf_framework *tf)
int(* ArgHandler)(const char *key, const char *value, struct tf_framework *tf)
static int run_sequential(struct tf_framework *tf)
static int parse_target(const char *key, const char *value, struct tf_framework *tf)
static int read_env(struct tf_framework *tf)
static int read_args(int argc, char **argv, int start, struct tf_framework *tf)
static void print_test_list(struct tf_framework *tf)
static int tf_run(struct tf_framework *tf)
static int parse_jobs_count(const char *key, const char *value, struct tf_framework *tf)
static void print_args(const struct tf_args *args)
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.