10#if defined(SUPPORTS_CONCURRENCY)
20#define UNUSED(x) (void)(x)
56 printf(
"iterations = %d\n",
COUNT);
57 printf(
"jobs = %d. %s execution.\n",
args->num_processes,
args->num_processes > 1 ?
"Parallel" :
"Sequential");
82 printf(
"Usage: ./tests [options]\n\n");
83 printf(
"Run the test suite for the project with optional configuration.\n\n");
85 printf(
" --help, -h Show this help message\n");
86 printf(
" --list_tests, -l Display list of all available tests and modules\n");
87 printf(
" --jobs=<num>, -j=<num> Number of parallel worker processes (default: 0 = sequential)\n");
88 printf(
" --iterations=<num>, -i=<num> Number of iterations for each test (default: 16)\n");
89 printf(
" --seed=<hex> Set a specific RNG seed (default: random)\n");
90 printf(
" --target=<test name>, -t=<name> Run a specific test (can be provided multiple times)\n");
91 printf(
" --target=<module name>, -t=<module> Run all tests within a specific module (can be provided multiple times)\n");
92 printf(
" --log=<0|1> Enable or disable test execution logging (default: 0 = disabled)\n");
95 printf(
" - All arguments must be provided in the form '--key=value', '-key=value' or '-k=value'.\n");
96 printf(
" - Single or double dashes are allowed for multi character options.\n");
97 printf(
" - Unknown arguments are reported but ignored.\n");
98 printf(
" - Sequential execution occurs if -jobs=0 or unspecified.\n");
99 printf(
" - Iterations and seed can also be passed as positional arguments before any other argument for backward compatibility.\n");
105 printf(
"\nAvailable tests (%d modules):\n",
tf->num_modules);
106 printf(
"========================================\n");
107 for (
m = 0;
m <
tf->num_modules;
m++) {
109 printf(
"Module: %s (%d tests)\n",
mod->name,
mod->size);
110 for (
t = 0;
t <
mod->size;
t++) {
111 printf(
"\t[%3d] %s\n", total + 1,
mod->data[
t].name);
114 printf(
"----------------------------------------\n");
116 printf(
"\nRun specific module: ./tests -t=<module_name>\n");
117 printf(
"Run specific test: ./tests -t=<test_name>\n\n");
131 tf->args.num_processes = (
int) val;
137 if (!value)
return 0;
140 fputs(
"An iteration count of 0 or less is not allowed.\n",
stderr);
148 tf->args.custom_seed = (!value ||
strcmp(value,
"NULL") == 0) ?
NULL : value;
154 tf->args.logging = value &&
strcmp(value,
"1") == 0;
161 if (!
arg ||
arg[0] !=
'-') {
162 *
err_msg =
"missing initial dash";
166 if (
arg[1] !=
'-')
return arg + 1;
169 if (
arg[2] ==
'\0') {
170 *
err_msg =
"missing option name after double dash";
175 *
err_msg =
"too many leading dashes";
180 if (key[1] ==
'\0') {
181 *
err_msg =
"short option cannot use double dash";
192 for (group = 0; group <
tf->num_modules; group++) {
193 const struct tf_test_module*
module = &tf->registry_modules[group];
195 for (idx = 0; idx <
module->size; idx++) {
196 entry = &
module->data[idx];
202 tf->args.targets.slots[
tf->args.targets.size++] = entry;
210 fprintf(
stderr,
"Error: target '%s' not found (missing or module disabled).\n"
211 "Run program with -list_tests option to display available tests and modules.\n", value);
221 const char*
err_msg =
"unknown error";
222 for (i = start; i <
argc; i++) {
230 if (!key || *key ==
'\0') {
238 if (
strcmp(key,
"h") == 0 ||
strcmp(key,
"help") == 0) {
242 if (
strcmp(key,
"l") == 0 ||
strcmp(key,
"list_tests") == 0) {
243 tf->args.list_tests = 1;
252 if (!value || *value ==
'\0') {
264 printf(
"Running %s..\n",
t->name);
266 printf(
"Test %s PASSED (%.3f sec)\n",
t->name, (
double)(
gettime_i64() - start_time) / 1000000);
274 for (it = 0; it <
tf->args.targets.size; it++) {
275 tf->fn_run_test(
tf->args.targets.slots[it]);
280#if defined(SUPPORTS_CONCURRENCY)
293 fprintf(
stderr,
"Internal Error: the number of targets (%d) exceeds the maximum supported (%d). "
294 "If you need more, extend 'run_concurrent()' to handle additional targets.\n",
301 perror(
"Error during pipe setup");
306 for (it = 0; it <
tf->args.num_processes; it++) {
309 perror(
"Error during process fork");
315 while (read(
pipefd[0], &idx,
sizeof(idx)) ==
sizeof(idx)) {
316 tf->fn_run_test(
tf->args.targets.slots[(
int)idx]);
327 for (it = 0; it <
tf->args.targets.size; it++) {
328 idx = (
unsigned char)it;
329 if (write(
pipefd[1], &idx,
sizeof(idx)) == -1) {
330 perror(
"Error during workload distribution");
338 for (it = 0; it <
tf->args.num_processes; it++) {
352 if (
tf->registry_modules ==
NULL ||
tf->num_modules <= 0) {
353 fprintf(
stderr,
"Error: tests registry not provided or empty\n");
358 tf->args.num_processes = 0;
359 tf->args.custom_seed =
NULL;
361 tf->args.targets.size = 0;
362 tf->args.list_tests = 0;
363 tf->args.logging = 0;
386 if (
argv[1][0] !=
'-') {
401 if (
tf->args.list_tests) {
421 if (!
tf->fn_run_test) {
422 fprintf(
stderr,
"Error: No test runner set. You must call 'tf_init' first to initialize the framework "
423 "or manually assign 'fn_run_test' before calling 'tf_run'.\n");
431 for (group = 0; group <
tf->num_modules; group++) {
432 const struct tf_test_module*
module = &tf->registry_modules[group];
433 for (idx = 0; idx <
module->size; idx++) {
435 fprintf(
stderr,
"Internal Error: Number of tests (%d) exceeds MAX_ARGS (%d). "
436 "Increase MAX_ARGS to accommodate all tests.\n",
tf->args.targets.size,
MAX_ARGS);
439 tf->args.targets.slots[
tf->args.targets.size++] = &
module->data[idx];
444 if (!
tf->args.logging) printf(
"Tests running silently. Use '-log=1' to enable detailed logging\n");
452 for (it = 0;
tf->registry_no_rng && it <
tf->registry_no_rng->size; it++) {
454 tf->fn_run_test(&
tf->registry_no_rng->data[it]);
463 if (
tf->args.num_processes <= 1) {
466#if defined(SUPPORTS_CONCURRENCY)
469 fputs(
"Parallel execution not supported on your system. Running sequentially...\n",
stderr);
475 printf(
"Total execution time: %.3f seconds\n", (
double)(
gettime_i64() - start_time) / 1000000);
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.