test: introduce (mini) unit test framework
Lightweight unit testing framework, providing a structured way to define, execute, and report tests. It includes a central test registry, a flexible command-line argument parser of the form "--key=value" / "-k=value" / "-key=value" (facilitating future framework extensions), ability to run tests in parallel and accumulated test time logging reports. So far the supported command-line args are: - "--jobs=<num>" or "-j=<num>" to specify the number of parallel workers. - "--seed=<hex>" to specify the RNG seed (random if not set). - "--iterations=<num>" or "-i=<num>" to specify the number of iterations. Compatibility Note: To stay compatible with previous versions, the framework also supports the two original positional arguments: the iterations count and the RNG seed (in that order).
This commit is contained in:
120
src/unit_test.h
Normal file
120
src/unit_test.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/***********************************************************************
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef SECP256K1_UNIT_TEST_H
|
||||
#define SECP256K1_UNIT_TEST_H
|
||||
|
||||
/* --------------------------------------------------------- */
|
||||
/* Configurable constants */
|
||||
/* --------------------------------------------------------- */
|
||||
|
||||
/* Maximum number of command-line arguments.
|
||||
* Must be at least as large as the total number of tests
|
||||
* to allow specifying all tests individually. */
|
||||
#define MAX_ARGS 150
|
||||
/* Maximum number of parallel jobs */
|
||||
#define MAX_SUBPROCESSES 16
|
||||
|
||||
/* --------------------------------------------------------- */
|
||||
/* Test Framework Registry Macros */
|
||||
/* --------------------------------------------------------- */
|
||||
|
||||
#define CASE(name) { #name, run_##name }
|
||||
|
||||
#define MAKE_TEST_MODULE(name) { \
|
||||
#name, \
|
||||
tests_##name, \
|
||||
sizeof(tests_##name) / sizeof(tests_##name[0]) \
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------- */
|
||||
/* Test Framework API */
|
||||
/* --------------------------------------------------------- */
|
||||
|
||||
typedef void (*test_fn)(void);
|
||||
|
||||
struct tf_test_entry {
|
||||
const char* name;
|
||||
test_fn func;
|
||||
};
|
||||
|
||||
struct tf_test_module {
|
||||
const char* name;
|
||||
const struct tf_test_entry* data;
|
||||
int size;
|
||||
};
|
||||
|
||||
typedef int (*setup_ctx_fn)(void);
|
||||
typedef int (*teardown_fn)(void);
|
||||
|
||||
/* Reference to a test in the registry. Group index and test index */
|
||||
typedef struct {
|
||||
int group;
|
||||
int idx;
|
||||
} tf_test_ref;
|
||||
|
||||
/* --- Command-line args --- */
|
||||
struct tf_args {
|
||||
/* 0 => sequential; 1..MAX_SUBPROCESSES => parallel workers */
|
||||
int num_processes;
|
||||
/* Specific RNG seed */
|
||||
const char* custom_seed;
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------- */
|
||||
/* Public API */
|
||||
/* --------------------------------------------------------- */
|
||||
|
||||
struct tf_framework {
|
||||
/* Command-line args */
|
||||
struct tf_args args;
|
||||
/* Test modules registry */
|
||||
const struct tf_test_module* registry_modules;
|
||||
/* Num of modules */
|
||||
int num_modules;
|
||||
/* Registry for tests that require no RNG init */
|
||||
const struct tf_test_module* registry_no_rng;
|
||||
/* Specific context setup and teardown functions */
|
||||
setup_ctx_fn fn_setup;
|
||||
teardown_fn fn_teardown;
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize the test framework.
|
||||
*
|
||||
* Must be called before tf_run() and before any output is performed to
|
||||
* stdout or stderr, because this function disables buffering on both
|
||||
* streams to ensure reliable diagnostic output.
|
||||
*
|
||||
* Parses command-line arguments and configures the framework context.
|
||||
* The caller must initialize the following members of 'tf' before calling:
|
||||
* - tf->registry_modules
|
||||
* - tf->num_modules
|
||||
*
|
||||
* Side effects:
|
||||
* - stdout and stderr are set to unbuffered mode via setbuf().
|
||||
* This allows immediate flushing of diagnostic messages but may
|
||||
* affect performance for other output operations.
|
||||
*
|
||||
* Returns:
|
||||
* EXIT_SUCCESS (0) on success,
|
||||
* EXIT_FAILURE (non-zero) on error.
|
||||
*/
|
||||
static int tf_init(struct tf_framework* tf, int argc, char** argv);
|
||||
|
||||
/*
|
||||
* Run tests based on the provided test framework context.
|
||||
*
|
||||
* This function uses the configuration stored in the tf_framework
|
||||
* (targets, number of processes, iteration count, etc.) to determine
|
||||
* which tests to execute and how to execute them.
|
||||
*
|
||||
* Returns:
|
||||
* EXIT_SUCCESS (0) if all tests passed,
|
||||
* EXIT_FAILURE (non-zero) otherwise.
|
||||
*/
|
||||
static int tf_run(struct tf_framework* tf);
|
||||
|
||||
#endif /* SECP256K1_UNIT_TEST_H */
|
||||
Reference in New Issue
Block a user