/********************************************************************************************************* * ------------------------------------------------------------------------------------------------------ * file description * ------------------------------------------------------------------------------------------------------ * \file unitt.c * \unit unitt * \brief This is a simple unit test module for C language * \author Lamdonn * \version v1.0.0 * \license GPL-2.0 * \copyright Copyright (C) 2023 Lamdonn. ********************************************************************************************************/ #include "unitt.h" /** * \brief Assert that a condition is true. * * This function checks if the given condition is true. If it is false, * it outputs an assertion failure message and returns a failure code. * * \param[in] condition: The condition to evaluate. * \param[in] message: The message to display if the assertion fails. * \return Returns UNITT_E_OK if the assertion passes, otherwise UNITT_E_FAIL. */ int unitt_det_true(int condition, const char* message) { if (!condition) { fprintf(stderr, "Assertion Failed: %s\n", message); return UNITT_E_FAIL; } return UNITT_E_OK; } /** * \brief Assert that a condition is false. * * This function checks if the given condition is false. If it is true, * it outputs an assertion failure message and returns a failure code. * * \param[in] condition: The condition to evaluate. * \param[in] message: The message to display if the assertion fails. * \return Returns UNITT_E_OK if the assertion passes, otherwise UNITT_E_FAIL. */ int unitt_det_false(int condition, const char* message) { if (condition) { fprintf(stderr, "Assertion Failed: %s\n", message); return UNITT_E_FAIL; } return UNITT_E_OK; } /** * \brief Assert that two integers are equal. * * This function checks whether the expected and actual values are equal. * If they are not equal, it outputs an assertion failure message with * both values and returns a failure code. * * \param[in] expected: The expected value. * \param[in] actual: The actual value. * \param[in] message: The message to display if the assertion fails. * \return Returns UNITT_E_OK if the assertion passes, otherwise UNITT_E_FAIL. */ int unitt_det_equal(int expected, int actual, const char* message) { if (expected != actual) { fprintf(stderr, "Assertion Failed: %s (Expected: %d, Actual: %d)\n", message, expected, actual); return UNITT_E_FAIL; } return UNITT_E_OK; } /** * \brief Assert that two integers are not equal. * * This function checks whether the expected and actual values are not equal. * If they are equal, it outputs an assertion failure message and returns a failure code. * * \param[in] expected: The expected value. * \param[in] actual: The actual value. * \param[in] message: The message to display if the assertion fails. * \return Returns UNITT_E_OK if the assertion passes, otherwise UNITT_E_FAIL. */ int unitt_det_nequal(int expected, int actual, const char* message) { if (expected == actual) { fprintf(stderr, "Assertion Failed: %s (Expected not equal to Actual)\n", message); return UNITT_E_FAIL; } return UNITT_E_OK; } /** * \brief Assert that a pointer is NULL. * * This function checks whether the given pointer is NULL. If it is not NULL, * it outputs an assertion failure message with the pointer's value and returns a failure code. * * \param[in] pointer: The pointer to evaluate. * \param[in] message: The message to display if the assertion fails. * \return Returns UNITT_E_OK if the assertion passes, otherwise UNITT_E_FAIL. */ int unitt_det_null(void* pointer, const char* message) { if (pointer != NULL) { fprintf(stderr, "Assertion Failed: %s (Expected NULL, Actual: %p)\n", message, pointer); return UNITT_E_FAIL; } return UNITT_E_OK; } /** * \brief Assert that a pointer is not NULL. * * This function checks whether the given pointer is not NULL. If it is NULL, * it outputs an assertion failure message and returns a failure code. * * \param[in] pointer: The pointer to evaluate. * \param[in] message: The message to display if the assertion fails. * \return Returns UNITT_E_OK if the assertion passes, otherwise UNITT_E_FAIL. */ int unitt_det_nnull(void* pointer, const char* message) { if (pointer == NULL) { fprintf(stderr, "Assertion Failed: %s (Expected non-NULL, Actual: NULL)\n", message); return UNITT_E_FAIL; } return UNITT_E_OK; } /** * \brief Assert that two floats are approximately equal. * * This function checks whether the expected and actual float values are approximately equal * within a given epsilon range. If they are not, it outputs an assertion failure message * with both values and returns a failure code. * * \param[in] expected: The expected float value. * \param[in] actual: The actual float value. * \param[in] epsilon: The acceptable difference between the two values. * \param[in] message: The message to display if the assertion fails. * \return Returns UNITT_E_OK if the assertion passes, otherwise UNITT_E_FAIL. */ int unitt_det_float(float expected, float actual, float epsilon, const char* message) { if (fabs(expected - actual) > epsilon) { fprintf(stderr, "Assertion Failed: %s (Expected: %.6f, Actual: %.6f)\n", message, expected, actual); return UNITT_E_FAIL; } return UNITT_E_OK; } /** * \brief Assert that two strings are equal. * * This function checks whether the expected and actual strings are equal. * If they are not equal, it outputs an assertion failure message with both strings * and returns a failure code. * * \param[in] expected: The expected string. * \param[in] actual: The actual string. * \param[in] message: The message to display if the assertion fails. * \return Returns UNITT_E_OK if the assertion passes, otherwise UNITT_E_FAIL. */ int unitt_det_string(const char* expected, const char* actual, const char* message) { if (strcmp(expected, actual) != 0) { fprintf(stderr, "Assertion Failed: %s (Expected: '%s', Actual: '%s')\n", message, expected, actual); return UNITT_E_FAIL; } return UNITT_E_OK; } /** * \brief Handle a test failure. * * This function is called when a test fails. It outputs the name of the failed test. * * \param[in] name: The name of the failed test. * \return Returns UNITT_E_OK. */ int unitt_fail_handle(const char* name) { printf("Failure in test: %s\n", name); return UNITT_E_OK; } /** * \brief Execute a test suite. * * This function runs all the test cases within the provided test suites. * It measures the execution time for each test and outputs a summary report. * * \param[in] suites: Array of test suites to execute. * \param[in] count: Number of test suites in the array. * \param[in] failcb: Callback function to handle test failures. * \param[in] specific: Specific test name to execute (NULL for all). * \param[in] filename: Output filename for results (NULL for stdout). * \return none. */ void unitt_execute(UNITT* suites, uint32_t count, unitt_failcb_t failcb, const char* specific, const char* filename) { UNITT* suite = NULL; UNITT_TCASE* tcase = NULL; uint64_t cstart = 0, cend = 0; // Record the start and end time of the test FILE* output = stdout; // Default output to standard output if (filename != NULL) { output = fopen(filename, "w"); if (output == NULL) { perror("Failed to open output file"); return; } } // Iterate over each test suite for (int i = 0; i < count; i++) { suite = &suites[i]; fprintf(output, "Running test suite: %s\n", suite->name); // Iterate over each test case for (int j = 0; j < suite->count; j++) { tcase = &suite->tests[j]; if (specific && strcmp(tcase->name, specific) != 0) { continue; // Skip non-matching tests } // Execute setup function before the test if (tcase->setup) tcase->setup(); // Record start time if (suite->clock) cstart = suite->clock(); // Execute test function if (UNITT_E_OK == tcase->func()) { tcase->passed++; } else { if (failcb) failcb(tcase->name); } // Record end time if (suite->clock) cend = suite->clock(); // Accumulate the time taken for the test if (suite->clock) tcase->time += (cend - cstart); // Execute teardown function after the test if (tcase->teardown) tcase->teardown(); tcase->tested++; } } // Output summary of the tests for (int i = 0; i < count; i++) { suite = &suites[i]; fprintf(output, "Test suite `%s` report: \r\n", suite->name); // Iterate over each test case for (int j = 0; j < suite->count; j++) { tcase = &suite->tests[j]; fprintf(output, "[%s] Summary: tested %u, passed %u, time %llu us.\r\n", tcase->name, tcase->tested, tcase->passed, tcase->time ); } } if (filename != NULL) { fclose(output); } }