Initial Commit

This commit is contained in:
2026-08-21 06:00:10 -05:00
commit b8537c9e13
6 changed files with 758 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
+3
View File
@@ -0,0 +1,3 @@
.chinook/
.environ/
.vscode/
+28
View File
@@ -0,0 +1,28 @@
name: microtest
gpid: malunal
semv: 1.0.0
requires:
- remote: git@git.erasit.com:malunal/config
branch: v1.0.2
- remote: git@git.erasit.com:malunal/types
branch: v1.0.0
targets:
- name: malunal.microtest
type: interface
deps:
- malunal.config
- malunal.types
tests:
- name: malunal.microtest.allinone
type: program
deps:
- malunal.microtest
srcs:
- ./tests/general.c
- ./tests/entry.c
exports:
- malunal.microtest
+577
View File
@@ -0,0 +1,577 @@
/**
* @file microtest.h
* @brief Contains the macros, variables, and functions needed to provide a
* minimal testing framework for C99.
* @author John Christman (sorakatadzuma@gmail.com)
* @copyright Malunal Studios, LLC.
* @remarks This library is heavily inspired and written after the Tau testing
* framework. Support the original project.
* @sa https://github.com/jasmcaus/tau
*/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "malunal/config.h"
#include "malunal/types/bool.h"
#include "malunal/types/integer.h"
#include "malunal/types/string.h"
#ifndef MALUNAL_MICROTEST_HEADER
#define MALUNAL_MICROTEST_HEADER
/**
* @brief A structure to define a single test suite.
* @details Tests suites are how microtest tracks what tests need execution.
* That is, they are not ignored. It tracks the actual function that
* will be executed and the name of that test as defined by the user.
*/
typedef struct {
/**
* @brief A pointer to a function which runs the test suite.
* @details This should be populated by microtest to be the function which the
* user defines for a given test suite. When the tests are run, this
* is what microtest will actually call.
*/
malunal_void_t (*execute)();
/**
* @brief The name of the test suite.
* @details This is also set by microtest based on the suite name and test
* name that the user defines for it.
*/
malunal_str_t name;
/**
* @brief Whether the test should be ignored.
* @details This is set by microtest when using the @c IGNORE_MICROTEST macro
* to create the test instead of the regular @c MICROTEST macro.
* Test suites that are marked as ignored will not be executed and
* will be indicated in the output log of the runner.
*/
malunal_bool_t ignored;
} microtest_suite_t;
/**
* @brief Type defines a pointer to a mutable microtest test suite.
* @details This is provided to make function declarations simpler and align the
* naming conventions of other types within our libraries.
*/
typedef microtest_suite_t* microtest_suite_mptr_t;
/**
* @brief Type defines a pointer to an immutable microtest test suite.
* @details This is provided to make function declarations simpler and align the
* naming conventions of other types within our libraries.
*/
typedef const microtest_suite_t* microtest_suite_iptr_t;
/**
* @brief A structure to define the microtest state object.
* @details The state object is used to easily track what tests suites are
* contained and need execution.
*/
typedef struct {
/**
* @brief The list of tests to execute.
* @details This will be automatically resized at startup by the registration
* functions, to make sure that microtest finds them all.
*/
microtest_suite_mptr_t tests;
/**
* @brief The number of tests in the @c tests list.
* @details This will be automatically set at startup by the registration
* functions, to make sure that microtest knows how many there are.
*/
malunal_size_t count;
} microtest_state_t;
/**
* @brief Type defines a pointer to a mutable microtest state.
* @details This is provided to make function declarations simpler and align the
* naming conventions of other types within our libraries.
*/
typedef microtest_state_t* microtest_state_mptr_t;
/**
* @brief Type defines a pointer to an immutable microtest state.
* @details This is provided to make function declarations simpler and align the
* naming conventions of other types within our libraries.
*/
typedef const microtest_state_t* microtest_state_iptr_t;
static malunal_size_t microtest_ran_count = 0;
static malunal_size_t microtest_failed_count = 0;
static malunal_size_t microtest_skipped_count = 0;
static malunal_size_t* microtest_failed_suites = null;
static malunal_size_t microtest_failed_suites_count = 0;
static malunal_size_t microtest_total_suites_count = 0;
extern microtest_state_t microtest_context;
extern malunal_size_t microtest_warnings_count;
extern volatile malunal_bool_t microtest_inside_test_suite;
extern volatile malunal_bool_t microtest_current_test_failed;
extern volatile malunal_bool_t microtest_current_test_ignored;
extern volatile malunal_bool_t microtest_should_fail_test;
extern volatile malunal_bool_t microtest_should_abort_test;
/**
* @brief Tells microtest that the current test should fail without aborting.
* @details This will cause the test to fail and increment the failure count but
* will not force microtest to abort all the other tests in the suite.
*/
static
malunal_void_t
microtest_fail_inside_test() {
if (!microtest_inside_test_suite)
return;
microtest_current_test_failed = true;
microtest_should_fail_test = true;
}
/**
* @brief Tells microtest that the current test should fail and abort.
* @details This will cause the test to fail and increment the failure count as
* well as force microtest to abort all the other tests in the suite.
*/
static
malunal_void_t
microtest_abort_inside_test() {
if (!microtest_inside_test_suite)
return;
microtest_current_test_failed = true;
microtest_should_abort_test = true;
}
/**
* @brief Checks if we should decompose a macro.
* @details This is for when we are inside of macro
*/
static
malunal_bool_t
microtest_should_decompose_macro(
malunal_cstr_t const actual,
malunal_cstr_t const expected,
malunal_bool_t strcmp
) {
malunal_int32_t dots_count = 0;
malunal_int32_t actual_digits_count = 0;
malunal_int32_t expected_digits_count = 0;
if (!strcmp) {
malunal_size_t index;
for (index = 0; index < strlen(actual); index++) {
if (isdigit(actual[index])) {
actual_digits_count++;
} else if (actual[index] == '.') {
if (++dots_count > 1)
return true;
} else {
return true;
}
}
dots_count = 0;
for(index = 0; index < strlen(expected); index++) {
if(isdigit(expected[index])) {
expected_digits_count++;
} else if(expected[index] == '.') {
if(++dots_count > 1)
return true;
} else {
return true;
}
}
}
return
strchr(actual, '(') != null ||
strchr(expected, '(') != null ||
actual [0] != '"' ||
expected[0] != '"';
}
#define MICROTEST_FAIL_INSIDE_TEST microtest_fail_inside_test()
#define MICROTEST_ABORT_INSIDE_TEST microtest_abort_inside_test()
// This is a little hack that allows a form of "polymorphism" to a macro - it allows a user to optionally pass
// an extra argument to a macro in {EXPECT|ASSERT}.
// The first argument is always the condition to expect/assert. If this condition fails, by default a `FAILED`
// message is sent to STDOUT. If a second argument is passed to the macro (a string), this will be outputted
// instead.
//
// The MACRO_CHOOSER uses the list of arguments twice, first to form the name of the helper macro, and then to
// pass the arguments to that helper macro. It uses a standard trick to count the number of arguments to a macro.
//
// Neat hack from:
// https://stackoverflow.com/questions/3046889/optional-parameters-with-c-macros, and
// https://stackoverflow.com/questions/11761703/overloading-macro-on-number-of-arguments
// https://stackoverflow.com/questions/2124339/c-preprocessor-va-args-number-of-arguments
#define GET_64TH_ARG( \
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, \
a11,a12,a13,a14,a15,a16,a17,a18,a19,a20, \
a21,a22,a23,a24,a25,a26,a27,a28,a29,a30, \
a31,a32,a33,a34,a35,a36,a37,a38,a39,a40, \
a41,a42,a43,a44,a45,a46,a47,a48,a49,a50, \
a51,a52,a53,a54,a55,a56,a57,a58,a59,a60, \
a61,a62,a63,a64,... \
) a64
#define MACRO_CHOOSER(F1, F2, F3, FN, ...) GET_64TH_ARG( __VA_ARGS__, \
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, \
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, \
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, \
FN, F3, F2, F1, )
#define FIXED1_F1(NAME, cond) NAME##_(cond, "FAILED")
#define FIXED1_FN(NAME, cond, ...) NAME##_(cond, __VA_ARGS__)
#define FIXED1_CHOOSER(...) MACRO_CHOOSER(FIXED1_F1, FIXED1_FN, FIXED1_FN, FIXED1_FN, __VA_ARGS__)
#define FIXED2_F1(NAME, act) NAME##_(act) /* will fail */
#define FIXED2_F2(NAME, act, exp) NAME##_(act, exp, "FAILED")
#define FIXED2_FN(NAME, act, exp, ...) NAME##_(act, exp, __VA_ARGS__)
#define FIXED2_CHOOSER(...) MACRO_CHOOSER(FIXED2_F1, FIXED2_F2, FIXED2_FN, FIXED2_FN, __VA_ARGS__)
#define FIXED3_F1(NAME, act) NAME##_(act) /* will fail */
#define FIXED3_F2(NAME, act, exp) NAME##_(act, exp) /* will fail */
#define FIXED3_F3(NAME, act, exp, n) NAME##_(act, exp, n, "FAILED")
#define FIXED3_FN(NAME, act, exp, n, ...) NAME##_(act, exp, n, __VA_ARGS__)
#define FIXED3_CHOOSER(...) MACRO_CHOOSER(FIXED3_F1, FIXED3_F2, FIXED3_F3, FIXED3_FN, __VA_ARGS__)
#define MICROTEST_COMPARE(actual, expected, condition, macro, handler, ...) \
do { \
if(!((actual) condition (expected))) { \
printf("%s:%u: ", __FILE__, __LINE__); \
printf(__VA_ARGS__); \
printf("\n"); \
if(microtest_should_decompose_macro(#actual, #expected, 0)) { \
printf(" In macro : "); \
printf("%s( %s, %s )\n", #macro, #actual, #expected); \
} \
printf(" Expected : %s", #actual); \
printf(" %s ", #condition); \
printf(#expected); \
printf("\n"); \
printf(" Actual : %s", #actual); \
printf(" == "); \
printf(#actual); \
printf("\n"); \
handler; \
if (microtest_should_abort_test) \
return; \
} \
} \
while(0)
#define MICROTEST_EXPECT_ASSERT(cond, handler, macro, ...) \
do { \
if(!(cond)) { \
printf("%s:%u: ", __FILE__, __LINE__); \
printf(__VA_ARGS__); \
printf("\n"); \
printf("The following assertion failed: \n"); \
printf(" %s( %s )\n", #macro, #cond); \
handler; \
if(microtest_should_abort_test) \
return; \
} \
} \
while(0)
#define MICROTEST_EXPECT_(cond, ...) MICROTEST_EXPECT_ASSERT(cond, MICROTEST_FAIL_INSIDE_TEST, MICROTEST_EXPECT, __VA_ARGS__)
#define MICROTEST_EXPECT_EQ_(actual, expected, ...) MICROTEST_COMPARE(actual, expected, ==, MICROTEST_EXPECT_EQ, MICROTEST_FAIL_INSIDE_TEST, __VA_ARGS__)
#define MICROTEST_EXPECT_NE_(actual, expected, ...) MICROTEST_COMPARE(actual, expected, !=, MICROTEST_EXPECT_EQ, MICROTEST_FAIL_INSIDE_TEST, __VA_ARGS__)
#define MICROTEST_EXPECT_LT_(actual, expected, ...) MICROTEST_COMPARE(actual, expected, <, MICROTEST_EXPECT_EQ, MICROTEST_FAIL_INSIDE_TEST, __VA_ARGS__)
#define MICROTEST_EXPECT_LE_(actual, expected, ...) MICROTEST_COMPARE(actual, expected, <=, MICROTEST_EXPECT_EQ, MICROTEST_FAIL_INSIDE_TEST, __VA_ARGS__)
#define MICROTEST_EXPECT_GT_(actual, expected, ...) MICROTEST_COMPARE(actual, expected, >, MICROTEST_EXPECT_EQ, MICROTEST_FAIL_INSIDE_TEST, __VA_ARGS__)
#define MICROTEST_EXPECT_GE_(actual, expected, ...) MICROTEST_COMPARE(actual, expected, >=, MICROTEST_EXPECT_EQ, MICROTEST_FAIL_INSIDE_TEST, __VA_ARGS__)
#define MICROTEST_EXPECT_TRUE_(actual, ...) MICROTEST_COMPARE(actual, true, ==, MICROTEST_EXPECT_EQ, MICROTEST_FAIL_INSIDE_TEST, __VA_ARGS__)
#define MICROTEST_EXPECT_FALSE_(actual, ...) MICROTEST_COMPARE(actual, false, ==, MICROTEST_EXPECT_EQ, MICROTEST_FAIL_INSIDE_TEST, __VA_ARGS__)
#define MICROTEST_EXPECT_NULL_(actual, ...) MICROTEST_EXPECT_(actual == null, __VA_ARGS__)
#define MICROTEST_EXPECT_NOT_NULL_(actual, ...) MICROTEST_EXPECT_(actual != null, __VA_ARGS__)
#define MICROTEST_ASSERT_(cond, ...) MICROTEST_EXPECT_ASSERT(cond, MICROTEST_ABORT_INSIDE_TEST, MICROTEST_ASSERT, __VA_ARGS__)
#define MICROTEST_ASSERT_EQ_(actual, expected, ...) MICROTEST_COMPARE(actual, expected, ==, MICROTEST_ASSERT_EQ, MICROTEST_ABORT_INSIDE_TEST, __VA_ARGS__)
#define MICROTEST_ASSERT_NE_(actual, expected, ...) MICROTEST_COMPARE(actual, expected, !=, MICROTEST_ASSERT_EQ, MICROTEST_ABORT_INSIDE_TEST, __VA_ARGS__)
#define MICROTEST_ASSERT_LT_(actual, expected, ...) MICROTEST_COMPARE(actual, expected, <, MICROTEST_ASSERT_EQ, MICROTEST_ABORT_INSIDE_TEST, __VA_ARGS__)
#define MICROTEST_ASSERT_LE_(actual, expected, ...) MICROTEST_COMPARE(actual, expected, <=, MICROTEST_ASSERT_EQ, MICROTEST_ABORT_INSIDE_TEST, __VA_ARGS__)
#define MICROTEST_ASSERT_GT_(actual, expected, ...) MICROTEST_COMPARE(actual, expected, >, MICROTEST_ASSERT_EQ, MICROTEST_ABORT_INSIDE_TEST, __VA_ARGS__)
#define MICROTEST_ASSERT_GE_(actual, expected, ...) MICROTEST_COMPARE(actual, expected, >=, MICROTEST_ASSERT_EQ, MICROTEST_ABORT_INSIDE_TEST, __VA_ARGS__)
#define MICROTEST_ASSERT_TRUE_(actual, ...) MICROTEST_COMPARE(actual, true, ==, MICROTEST_ASSERT_EQ, MICROTEST_ABORT_INSIDE_TEST, __VA_ARGS__)
#define MICROTEST_ASSERT_FALSE_(actual, ...) MICROTEST_COMPARE(actual, false, ==, MICROTEST_ASSERT_EQ, MICROTEST_ABORT_INSIDE_TEST, __VA_ARGS__)
#define MICROTEST_ASSERT_NULL_(actual, ...) MICROTEST_ASSERT_(actual == null, __VA_ARGS__)
#define MICROTEST_ASSERT_NOT_NULL_(actual, ...) MICROTEST_ASSERT_(actual != null, __VA_ARGS__)
#define MICROTEST_EXPECT(...) FIXED1_CHOOSER(__VA_ARGS__)(MICROTEST_EXPECT, __VA_ARGS__)
#define MICROTEST_EXPECT_EQ(...) FIXED2_CHOOSER(__VA_ARGS__)(MICROTEST_EXPECT_EQ, __VA_ARGS__)
#define MICROTEST_EXPECT_NE(...) FIXED2_CHOOSER(__VA_ARGS__)(MICROTEST_EXPECT_NE, __VA_ARGS__)
#define MICROTEST_EXPECT_LT(...) FIXED2_CHOOSER(__VA_ARGS__)(MICROTEST_EXPECT_LT, __VA_ARGS__)
#define MICROTEST_EXPECT_LE(...) FIXED2_CHOOSER(__VA_ARGS__)(MICROTEST_EXPECT_LE, __VA_ARGS__)
#define MICROTEST_EXPECT_GT(...) FIXED2_CHOOSER(__VA_ARGS__)(MICROTEST_EXPECT_GT, __VA_ARGS__)
#define MICROTEST_EXPECT_GE(...) FIXED2_CHOOSER(__VA_ARGS__)(MICROTEST_EXPECT_GE, __VA_ARGS__)
#define MICROTEST_EXPECT_TRUE(...) FIXED1_CHOOSER(__VA_ARGS__)(MICROTEST_EXPECT_TRUE, __VA_ARGS__)
#define MICROTEST_EXPECT_FALSE(...) FIXED1_CHOOSER(__VA_ARGS__)(MICROTEST_EXPECT_FALSE, __VA_ARGS__)
#define MICROTEST_EXPECT_NULL(...) FIXED1_CHOOSER(__VA_ARGS__)(MICROTEST_EXPECT_NULL, __VA_ARGS__)
#define MICROTEST_EXPECT_NOT_NULL(...) FIXED1_CHOOSER(__VA_ARGS__)(MICROTEST_EXPECT_NOT_NULL, __VA_ARGS__)
#define MICROTEST_ASSERT(...) FIXED1_CHOOSER(__VA_ARGS__)(MICROTEST_ASSERT, __VA_ARGS__)
#define MICROTEST_ASSERT_EQ(...) FIXED2_CHOOSER(__VA_ARGS__)(MICROTEST_ASSERT_EQ, __VA_ARGS__)
#define MICROTEST_ASSERT_NE(...) FIXED2_CHOOSER(__VA_ARGS__)(MICROTEST_ASSERT_NE, __VA_ARGS__)
#define MICROTEST_ASSERT_LT(...) FIXED2_CHOOSER(__VA_ARGS__)(MICROTEST_ASSERT_LT, __VA_ARGS__)
#define MICROTEST_ASSERT_LE(...) FIXED2_CHOOSER(__VA_ARGS__)(MICROTEST_ASSERT_LE, __VA_ARGS__)
#define MICROTEST_ASSERT_GT(...) FIXED2_CHOOSER(__VA_ARGS__)(MICROTEST_ASSERT_GT, __VA_ARGS__)
#define MICROTEST_ASSERT_GE(...) FIXED2_CHOOSER(__VA_ARGS__)(MICROTEST_ASSERT_GE, __VA_ARGS__)
#define MICROTEST_ASSERT_TRUE(...) FIXED1_CHOOSER(__VA_ARGS__)(MICROTEST_ASSERT_TRUE, __VA_ARGS__)
#define MICROTEST_ASSERT_FALSE(...) FIXED1_CHOOSER(__VA_ARGS__)(MICROTEST_ASSERT_FALSE, __VA_ARGS__)
#define MICROTEST_ASSERT_NULL(...) FIXED1_CHOOSER(__VA_ARGS__)(MICROTEST_ASSERT_NULL, __VA_ARGS__)
#define MICROTEST_ASSERT_NOT_NULL(...) FIXED1_CHOOSER(__VA_ARGS__)(MICROTEST_ASSERT_NOT_NULL, __VA_ARGS__)
/**
* @def MICROTEST_INITIALIZER
* @brief Responsible for registering a function with the c-runtime.
* @details By registering the function in this manner, we are able to make sure
* they are executed at the start up of the application such that
* microtest understands what test suites it will need to run.
* @param func The name of the function which will be registered.
*/
#if MALUNAL_COMPILER_MSVC
#if defined(_WIN64)
# define PREFIX
#else
# define PREFIX "_"
#endif // _WIN64
#pragma section(".CRT$XCU", read)
#define MICROTEST_INITIALIZER(func) \
static void __cdecl f(void); \
__declspec(allocate(".CRT$XCU")) \
__pragma(comment(linker, "/include:" PREFIX #func "_")) \
malunal_void_t(__cdecl * func##_)(malunal_void_t) = func; \
static void __cdecl func(void)
#undef PREFIX
#else /* Compiler is not MSVC */
#define MICROTEST_INITIALIZER(func) \
static void func(malunal_void_t) __attribute__((constructor)); \
static void func(malunal_void_t)
#endif /* Compiler check for microtest registry */
/**
* @def MICROTEST
* @brief Defines a test under a test suite and registers it with the global
* microtest context.
* @details This is responsible for registering the test in the c-runtime global
* constructor section, using either the GCC built in attribute or a
* pragma trick on MSVC.
* @param suite_name The name of the test suite; what the test belongs under.
* @param test_name The name of the test itself.
*/
#define MICROTEST(suite_name, test_name) \
extern microtest_state_t microtest_context; \
static malunal_void_t MICROTEST_##suite_name##_##test_name(malunal_void_t); \
MICROTEST_INITIALIZER(microtest_register_##suite_name##_##test_name) { \
malunal_size_t index = microtest_context.count++; \
malunal_size_t bytes = microtest_context.count * sizeof(microtest_suite_t); \
\
malunal_cstr_t part = #suite_name "." #test_name; \
malunal_size_t size = strlen(part) + 1; \
malunal_str_t name = malloc(size); \
snprintf(name, size, "%s", part); \
\
microtest_context.tests = (microtest_suite_mptr_t) \
realloc(microtest_context.tests, bytes); \
microtest_context.tests[index].execute = &MICROTEST_##suite_name##_##test_name; \
microtest_context.tests[index].name = name; \
microtest_context.tests[index].ignored = false; \
} \
\
malunal_void_t MICROTEST_##suite_name##_##test_name(malunal_void_t)
/**
* @def IGNORE_MICROTEST
* @brief Defines a test under a test suite and registers it with the global
* microtest context, but marking it as ignored.
* @details This is responsible for registering the test in the c-runtime global
* constructor section, using either the GCC built in attribute or a
* pragma trick on MSVC.
* @param suite_name The name of the test suite; what the test belongs under.
* @param test_name The name of the test itself.
*/
#define IGNORE_MICROTEST(suite_name, test_name) \
extern microtest_state_t microtest_context; \
static malunal_void_t MICROTEST_##suite_name##_##test_name(malunal_void_t); \
MICROTEST_INITIALIZER(microtest_register_##suite_name##_##test_name) { \
malunal_size_t index = microtest_context.count++; \
malunal_size_t bytes = microtest_context.count * sizeof(microtest_suite_t); \
\
malunal_cstr_t part = #suite_name "." #test_name; \
malunal_size_t size = strlen(part) + 1; \
malunal_str_t name = malloc(size); \
snprintf(name, size, "%s", part); \
\
microtest_context.tests = (microtest_suite_mptr_t) \
realloc(microtest_context.tests, bytes); \
microtest_context.tests[index].execute = &MICROTEST_##suite_name##_##test_name; \
microtest_context.tests[index].name = name; \
microtest_context.tests[index].ignored = true; \
} \
\
malunal_void_t MICROTEST_##suite_name##_##test_name(malunal_void_t)
/**
* @brief Responsible for shutting down the microtest framework.
* @details This will assure that all allocated resources are freed and that the
* framework returns cleanly.
*/
static
malunal_int32_t
microtest_cleanup() {
malunal_size_t index;
for (index = 0; index < microtest_context.count; index++)
free(microtest_context.tests[index].name);
free(microtest_failed_suites);
free(microtest_context.tests);
return microtest_failed_count;
}
/**
* @brief Runs the tests that are registered with microtest.
* @details This will execute all tests and track which ones failed and which
* ones passed, printing the results as it processes them.
*/
static
malunal_void_t
microtest_run_tests() {
malunal_size_t index;
for (index = 0; index < microtest_context.count; index++) {
microtest_inside_test_suite = true;
microtest_current_test_failed = false;
microtest_current_test_ignored = microtest_context.tests[index].ignored;
// TODO: add test filtering?
if (microtest_current_test_ignored) {
// TODO: add logging library.
printf("[ IGNORED ] ");
printf("%s\n", microtest_context.tests[index].name);
microtest_skipped_count++;
continue;
}
// TODO: add file output?
// TODO: track execution time.
printf("[ RUN ] ");
printf("%s\n", microtest_context.tests[index].name);
microtest_context.tests[index].execute();
if (microtest_current_test_failed) {
malunal_size_t failed = microtest_failed_suites_count++;
malunal_size_t bytes = microtest_failed_suites_count * sizeof(malunal_size_t);
microtest_failed_suites = (malunal_size_t*)
realloc(microtest_failed_suites, bytes);
microtest_failed_suites[failed] = index;
microtest_failed_count++;
printf("[ FAILED ]");
printf("%s (", microtest_context.tests[index].name);
printf(")\n");
} else {
printf("[ OK ]");
printf("%s (", microtest_context.tests[index].name);
printf(")\n");
}
}
printf("[==========] ");
printf("%zu test suites ran\n", microtest_ran_count);
}
/**
* @brief Responsible for handling any command line options and running all
* tests that are registered with microtest.
* @param argc The number of command line arguments.
* @param argv The array of command line arguments.
* @returns The result of running microtest.
*/
static
malunal_int32_t
microtest_main(
malunal_int32_t argc,
malunal_str_t* argv
) {
microtest_total_suites_count = microtest_context.count;
microtest_ran_count = microtest_total_suites_count;
printf("[==========] ");
printf("Running %zu test suites.\n", microtest_ran_count);
microtest_run_tests();
malunal_size_t failed = microtest_failed_count;
malunal_size_t passed = microtest_ran_count - failed;
printf("[ PASSED ] %zu %s\n", passed, passed == 1 ? "suite" : "suites");
printf("[ FAILED ] %zu %s\n", failed, failed == 1 ? "suite" : "suites");
printf("\nSummary:\n");
printf(" Total test suites: %zu\n", microtest_total_suites_count);
printf(" Total suites run: %zu\n", microtest_ran_count);
printf(" Total warnings generated: %zu\n", microtest_warnings_count);
printf(" Total suites skipped: %zu\n", microtest_skipped_count);
printf(" Total suites failed: %zu\n", failed);
return microtest_cleanup();
}
/**
* @def MICROTEST_GLOBALS
* @brief This defines the globals that microtest needs to function.
* @details This isn't designed to be used by anyone unless they are manually
* specifying the main function, but we recommend that you simply use
* @c MICROTEST_MAIN
*/
#define MICROTEST_GLOBALS() \
microtest_state_t microtest_context = { null }; \
malunal_size_t microtest_warnings_count = 0; \
\
volatile malunal_bool_t microtest_inside_test_suite; \
volatile malunal_bool_t microtest_current_test_failed; \
volatile malunal_bool_t microtest_current_test_ignored; \
volatile malunal_bool_t microtest_should_fail_test; \
volatile malunal_bool_t microtest_should_abort_test;
/**
* @def MICROTEST_NO_MAIN()
* @brief Sets up microtest without specifying the main function.
* @details Use this if you plan to write your own main function or to modify
* the manner in which microtest executes its main function.
*/
#define MICROTEST_NO_MAIN() \
MICROTEST_GLOBALS()
/**
* @def MICROTEST_MAIN
* @brief This defines the microtest globals and the main function.
* @details This is provided to make defining the main test easier. It will
* automatically define all needed globals, and the main function which
* will call the actual @c microtest_main.
*/
#define MICROTEST_MAIN() \
MICROTEST_GLOBALS() \
\
malunal_int32_t \
main( \
malunal_int32_t argc, \
malunal_str_t* argv \
) { \
return microtest_main(argc, argv); \
}
#endif /* MALUNAL_MICROTEST_HEADER */
+2
View File
@@ -0,0 +1,2 @@
#include "malunal/microtest.h"
MICROTEST_MAIN()
+139
View File
@@ -0,0 +1,139 @@
#include "malunal/microtest.h"
MICROTEST(c, MICROTEST_EXPECT_TF) {
MICROTEST_EXPECT_FALSE(0);
MICROTEST_EXPECT_TRUE(1);
}
MICROTEST(c, MICROTEST_ASSERT_TF) {
MICROTEST_ASSERT_FALSE(0);
MICROTEST_ASSERT_TRUE(1);
}
MICROTEST(c, MemAlloc) {
malunal_mptr_t mem;
mem = malloc(5);
MICROTEST_EXPECT(mem != NULL, "Mem ideally should not be NULL");
mem = realloc(mem, 10);
MICROTEST_EXPECT(mem != NULL, "Mem ideally should not be NULL");
}
malunal_int32_t retu() {
return 10000000;
}
MICROTEST(c, ForLoop) {
malunal_int32_t count = 0;
malunal_int32_t index = 0;
for(index; index < 10000000; index++)
count++;
MICROTEST_ASSERT_EQ(count, retu());
}
MICROTEST(c, MICROTEST_EXPECT) {
MICROTEST_EXPECT(1 < 2, "1 < 2");
}
MICROTEST(c, MICROTEST_ASSERT_LT) {
MICROTEST_ASSERT_LT(1, 2);
}
MICROTEST(c, MICROTEST_ASSERT_LE) {
MICROTEST_ASSERT_LE(1, 1);
MICROTEST_ASSERT_LE(1, 2);
}
MICROTEST(c, MICROTEST_ASSERT_GT) {
MICROTEST_ASSERT_GT(2, 1);
}
MICROTEST(c, MICROTEST_ASSERT_GE) {
MICROTEST_ASSERT_GE(1, 1);
MICROTEST_ASSERT_GE(2, 1);
}
MICROTEST(c, MICROTEST_EXPECT_EQ1) {
MICROTEST_EXPECT_EQ(345, 345);
}
MICROTEST(c, MICROTEST_EXPECT_NE) {
MICROTEST_EXPECT_NE(1, 2);
}
MICROTEST(c, MICROTEST_EXPECT_LT) {
MICROTEST_EXPECT_LT(1, 2);
}
MICROTEST(c, MICROTEST_EXPECT_LE) {
MICROTEST_EXPECT_LE(1, 1);
MICROTEST_EXPECT_LE(1, 2);
}
MICROTEST(c, MICROTEST_EXPECT_GT) {
MICROTEST_EXPECT_GT(2, 1);
}
MICROTEST(c, MICROTEST_EXPECT_GE) {
MICROTEST_EXPECT_GE(1, 1);
MICROTEST_EXPECT_GE(2, 1);
}
MICROTEST(c11, MICROTEST_ASSERT_EQ) {
MICROTEST_ASSERT_EQ(1, 1);
}
MICROTEST(c11, MICROTEST_ASSERT_NE) {
MICROTEST_ASSERT_NE(1, 2);
}
MICROTEST(c11, MICROTEST_ASSERT_LT) {
MICROTEST_ASSERT_LT(1, 2);
}
MICROTEST(c11, MICROTEST_ASSERT_LE) {
MICROTEST_ASSERT_LE(1, 1);
MICROTEST_ASSERT_LE(1, 2);
}
MICROTEST(c11, MICROTEST_ASSERT_GT) {
MICROTEST_ASSERT_GT(2, 1);
}
MICROTEST(c11, MICROTEST_ASSERT_GE) {
MICROTEST_ASSERT_GE(1, 1);
MICROTEST_ASSERT_GE(2, 1);
}
MICROTEST(c11, MICROTEST_EXPECT_EQ2) {
MICROTEST_EXPECT_EQ(1, 1);
}
MICROTEST(c11, MICROTEST_EXPECT_NE) {
MICROTEST_EXPECT_NE(1, 2);
}
MICROTEST(c11, MICROTEST_EXPECT_LT) {
MICROTEST_EXPECT_LT(1, 2);
}
MICROTEST(c11, MICROTEST_EXPECT_LE) {
MICROTEST_EXPECT_LE(1, 1);
MICROTEST_EXPECT_LE(1, 2);
}
MICROTEST(c11, MICROTEST_EXPECT_GT) {
MICROTEST_EXPECT_GT(3, 1);
}
MICROTEST(c11, MICROTEST_EXPECT_GE) {
MICROTEST_EXPECT_GE(1, 1);
MICROTEST_EXPECT_GE(2, 1);
}
MICROTEST(c11, no_double_eval) {
malunal_int32_t i = 0;
MICROTEST_ASSERT_EQ(i++, 0);
MICROTEST_ASSERT_EQ(i, 1);
}