2024-11-15 00:31:10 +08:00

306 lines
9.5 KiB
C

/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* 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);
}
}