/*
CUTest - A unit test runner for C
Copyright (C) 2022 Pascal Baillehache baillehache.pascal@gmail.com
https://baillehachepascal.dev
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
// Conversion from macro to string
#define CUTEST_TO_STR_(x) #x
#define CUTEST_TO_STR(x) CUTEST_TO_STR_(x)
// Escape sequence to colorize the output
#define CUTEST_START_RED "\033[38;2;255;0;0m"
#define CUTEST_END_RED "\033[39m"
#define CUTEST_START_GREEN "\033[38;2;0;255;0m"
#define CUTEST_END_GREEN "\033[39m"
#define CUTEST_START_BOLD "\033[1m"
#define CUTEST_END_BOLD "\033[0m"
// Print current date and time
#define CUTEST_TIME \
{ time_t now = time(NULL); printf("%.19s\n", asctime(localtime(&now))); }
// Variables to memorise the number of tests and failed test
size_t cutestIdxTest;
size_t cutestNbTest;
size_t cutestNbTestFailed;
bool cutestTestSuccess;
// Wrapper for the test condition in the unit tests. To be used as follow:
// CUTEST_ASSERT(cond, msg, msg_arg1, msg_arg2, ...)
// where 'cond' is the condition to be respected by the test, and 'msg', etc...
// are the arguments passed to a 'printf' call in case the condition
// is not satisfy with the view to explain why the condition wasn't satisfied.
#define CUTEST_ASSERT(cond, ...) \
if(!(cond)) { \
if(cutestTestSuccess) \
printf(CUTEST_START_RED "NG" CUTEST_END_RED "\n"); \
printf( \
CUTEST_START_RED " -- Unsatisfied condition on line %u:\n%s\n", \
__LINE__, CUTEST_TO_STR(cond)); \
printf("While:\n"); \
printf(__VA_ARGS__); \
printf(CUTEST_END_RED "\n"); \
cutestTestSuccess = false; \
}
// Print the OK result for a successful test and increase the counter of
// failed test for an unsuccessful test
#define CUTEST_OK \
if(cutestTestSuccess) { \
printf(CUTEST_START_GREEN "OK" CUTEST_END_GREEN "\n"); \
} else { \
++cutestNbTestFailed; \
}
// Unit tests declarations macro. To be used in the unit tests file as follow:
// CUTEST(test002, "Second unit test") {
// int a = 2;
// a *= a;
// CUTEST_ASSERT(a == 4, "a equals to %d", a);
// }
#define CUTEST(name, lbl) void name(void)
#include CUTEST_TO_STR(CUTEST_FILE)
// Helper function to print and pad the label of the executed tests
void CutestPrintLbl(char const* const lbl) {
size_t length = strlen(lbl);
size_t lengthMax = 80;
printf("%03lu/%03lu ", cutestIdxTest, cutestNbTest);
size_t i = 0;
for(; i < length; ++i) {
if(i > 0 && (i % lengthMax) == 0) printf("\n ");
printf("%c", lbl[i]);
}
i %= lengthMax;
if(i != 0) for(; i < lengthMax; ++i) printf(".");
printf(" ");
fflush(stdout);
}
// Main function
int main(int argc, char** argv) {
// Variable to memorise the unit test to be executed
// (if null, all are executed)
char* nameTestToExecute = NULL;
for(int iArg = 0; iArg < argc; ++iArg) {
if(strcmp(argv[iArg], "-t") == 0 && (iArg + 1) < argc)
nameTestToExecute = argv[iArg + 1];
}
// Print the unit test file name and start time
printf(
CUTEST_START_BOLD "\n ==== %s ====" CUTEST_END_BOLD "\n",
CUTEST_TO_STR(CUTEST_FILE));
CUTEST_TIME
// Init the counters
cutestIdxTest = 0;
cutestNbTest = 0;
cutestNbTestFailed = 0;
// Calculate the number of test
#undef CUTEST
#define CUTEST(name, lbl) \
if( \
nameTestToExecute == NULL || \
strcmp(nameTestToExecute, CUTEST_TO_STR(name)) == 0 \
) { \
++cutestNbTest; \
} if(0)
#include CUTEST_TO_STR(CUTEST_FILE)
// Unit tests call
#undef CUTEST
#define CUTEST(name, lbl) \
if( \
nameTestToExecute == NULL || \
strcmp(nameTestToExecute, CUTEST_TO_STR(name)) == 0 \
) { \
++cutestIdxTest; cutestTestSuccess = true; \
CutestPrintLbl(lbl); try { name(); } \
catchDefault {CUTEST_ASSERT(false, \
"Uncaught exception: %s", \
CapyExcToStr(CapyGetLastExcId()));} \
endCatch; CUTEST_OK \
} \
if(0)
#include CUTEST_TO_STR(CUTEST_FILE)
// Variable to memorise the returned code
int ret = 0;
// Print a summary
if(cutestNbTestFailed == 0) {
printf(
CUTEST_START_GREEN "All tests succeeded." CUTEST_END_GREEN "\n");
ret = EXIT_SUCCESS;
} else {
printf(
CUTEST_START_RED "%03lu/%03lu test(s) failed." CUTEST_END_RED "\n",
cutestNbTestFailed, cutestNbTest);
ret = EXIT_FAILURE;
}
// Print the end time and return the exit code
CUTEST_TIME
return ret;
}
// Example of Makefile:
// test:
// for file in `ls UnitTests/test_*.c`; do
// gcc -o cutest cutest.c -DCUTEST_FILE=$$file -lcapy && cutest; \rm -f cutest; done
//
// To execute only the unit test 'test002', you can use:
// cutest -t test002