From 176bbc66ad68516152b5546fc00aea5c944b86ae Mon Sep 17 00:00:00 2001 From: Leonardo Date: Wed, 26 Jun 2019 01:32:44 -0300 Subject: [PATCH] Added UnitTest utility and removed unnecessary logging functions --- src/macro_collections.h | 1 + src/utl/log.h | 97 +++++++--------------- src/utl/test.h | 176 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 208 insertions(+), 66 deletions(-) create mode 100644 src/utl/test.h diff --git a/src/macro_collections.h b/src/macro_collections.h index cb797f1..3cfb625 100644 --- a/src/macro_collections.h +++ b/src/macro_collections.h @@ -47,6 +47,7 @@ #include "utl/foreach.h" #include "utl/log.h" +#include "utl/test.h" #include "utl/timer.h" #endif /* CMC_MACRO_COLLECTIONS */ diff --git a/src/utl/log.h b/src/utl/log.h index 5b96bf2..fe5ca40 100644 --- a/src/utl/log.h +++ b/src/utl/log.h @@ -11,7 +11,10 @@ /** * Simple logging macros * You can toggle colors defining the macro CMC_LOG_COLOR but be sure that - * your terminal supports ANSI color escape codes + * your terminal supports ANSI color escape codes. + * + * You can customize the logging utility by accessing cmc_log_config and + * directly modifying its values to better suit your needs. */ /** @@ -42,21 +45,6 @@ * ERROR - 5 * FATAL - 6 * - * Functions API - * - * void cmc_log_tlevel(int level) - * Set level for the Terminal Output to stderr. - * void cmc_log_flevel(int level) - * Set level for the File Output to FILE pointer. - * void cmc_log_tenable(bool enabled) - * Enables or disables Terminal Output to stderr. - * void cmc_log_fenable(bool enabled) - * Enables or disables File Output. - * void cmc_log_file(FILE *file) - * Set a file for writing the logging messages to it. - * void cmc_log_set(bool enabled) - * Sets on or off all the logging messages. - * * How the Log Level is calculated * * - If the log level is 0 all logs are enabled. @@ -76,8 +64,13 @@ #include #include -static const char *names[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"}; -static const char *color[] = {"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"}; +static const char *cmc_log_names[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"}; + +#ifdef CMC_LOG_COLOR + +static const char *cmc_log_color[] = {"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"}; + +#endif static struct { @@ -87,9 +80,10 @@ static struct bool fenabled; /* File Output Enabled */ FILE *file; /* File for Output */ bool enabled; /* Logging enabled */ -} log_config = {0, 0, true, true, NULL, true}; -typedef enum log_type_e +} cmc_log_config = {0, 0, true, true, NULL, true}; + +typedef enum cmc_log_type_e { LOG_TRACE = 1, LOG_DEBUG = 2, @@ -97,7 +91,8 @@ typedef enum log_type_e LOG_WARN = 4, LOG_ERROR = 5, LOG_FATAL = 6 -} log_type; + +} cmc_log_type; #define log_trace(fmt, ...) cmc_log(LOG_TRACE, __FILE__, __func__, __LINE__, fmt, __VA_ARGS__) #define log_debug(fmt, ...) cmc_log(LOG_DEBUG, __FILE__, __func__, __LINE__, fmt, __VA_ARGS__) @@ -106,46 +101,16 @@ typedef enum log_type_e #define log_error(fmt, ...) cmc_log(LOG_ERROR, __FILE__, __func__, __LINE__, fmt, __VA_ARGS__) #define log_fatal(fmt, ...) cmc_log(LOG_FATAL, __FILE__, __func__, __LINE__, fmt, __VA_ARGS__) -static void cmc_log_tlevel(int terminal_level) +static void cmc_log(cmc_log_type log, const char *filename, const char *funcname, int line, const char *fmt, ...) { - log_config.tlevel = terminal_level; -} - -static void cmc_log_flevel(int file_level) -{ - log_config.flevel = file_level; -} - -static void cmc_log_tenable(bool enabled) -{ - log_config.tenabled = enabled; -} - -static void cmc_log_fenable(bool enabled) -{ - log_config.fenabled = enabled; -} - -static void cmc_log_file(FILE *file) -{ - log_config.file = file; -} - -static void cmc_log_set(bool enabled) -{ - log_config.enabled = enabled; -} - -static void cmc_log(log_type log, const char *filename, const char *funcname, int line, const char *fmt, ...) -{ - if (!log_config.enabled) + if (!cmc_log_config.enabled) return; - if (log_config.tenabled) + if (cmc_log_config.tenabled) { - if (log_config.tlevel < 0 && ((int)log * -1) < log_config.tlevel) + if (cmc_log_config.tlevel < 0 && ((int)log * -1) < cmc_log_config.tlevel) return; - else if (log_config.tlevel > 0 && (int)log < log_config.tlevel) + else if (cmc_log_config.tlevel > 0 && (int)log < cmc_log_config.tlevel) return; int i = (int)log - 1; @@ -156,14 +121,14 @@ static void cmc_log(log_type log, const char *filename, const char *funcname, in char time[16]; time[strftime(time, sizeof(time), "%H:%M:%S", lt)] = '\0'; -#if defined(CMC_LOG_COLOR) +#ifdef CMC_LOG_COLOR fprintf(stderr, "%s %s%5s\x1b[0m \x1b[90m%s at %s:%d:\x1b[0m ", - time, color[i], names[i], filename, funcname, line); + time, cmc_log_color[i], cmc_log_names[i], filename, funcname, line); #else fprintf(stderr, "%s %5s %s at %s:%d: ", - time, names[i], filename, funcname, line); + time, cmc_log_names[i], filename, funcname, line); #endif va_list args; @@ -175,11 +140,11 @@ static void cmc_log(log_type log, const char *filename, const char *funcname, in fflush(stderr); } - if (log_config.file && log_config.fenabled) + if (cmc_log_config.file && cmc_log_config.fenabled) { - if (log_config.flevel < 0 && ((int)log * -1) < log_config.flevel) + if (cmc_log_config.flevel < 0 && ((int)log * -1) < cmc_log_config.flevel) return; - else if (log_config.flevel > 0 && (int)log < log_config.flevel) + else if (cmc_log_config.flevel > 0 && (int)log < cmc_log_config.flevel) return; int i = (int)log - 1; @@ -190,15 +155,15 @@ static void cmc_log(log_type log, const char *filename, const char *funcname, in char time[32]; time[strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; - fprintf(log_config.file, "%s %5s %s at %s:%d: ", time, names[i], filename, funcname, line); + fprintf(cmc_log_config.file, "%s %5s %s at %s:%d: ", time, cmc_log_names[i], filename, funcname, line); va_list file_args; va_start(file_args, fmt); - vfprintf(log_config.file, fmt, file_args); + vfprintf(cmc_log_config.file, fmt, file_args); va_end(file_args); - fprintf(log_config.file, "\n"); - fflush(log_config.file); + fprintf(cmc_log_config.file, "\n"); + fflush(cmc_log_config.file); } } diff --git a/src/utl/test.h b/src/utl/test.h new file mode 100644 index 0000000..d39643b --- /dev/null +++ b/src/utl/test.h @@ -0,0 +1,176 @@ +/** + * test.h + * + * Creation Date: 26/06/2019 + * + * Authors: + * Leonardo Vencovsky (https://github.com/LeoVen) + * + */ + +/** + * Simple Unit Test Utility + * + * CMC_CREATE_UNIT + * Create a UnitTest + * Parameters: + * - UNAME : UnitTest name (const char *) + * - VERBOSE : Print PASSED / FAILED messages if true (bool) + * - BODY : Body containing all tests related to this UnitTest + * + * CMC_CREATE_TEST + * Create a Test inside a UnitTest. + * Parameters: + * - TNAME : Test name (const char *) + * - BODY : Block of code containing a specific test + * Inside the BODY use the following macros: + * - CMC_TEST_PASS + * Pass current test. + * - CMC_TEST_FAIL + * Fail current test. + * - CMC_TEST_PASS_ELSE_FAIL + * If given expression is true, pass current test, else fail. + * - CMC_TEST_ABORT + * Abort current unit test and displays error message. All tests + * after the abort won't be called. + * All of these macros can only be called once per test. + * + * CMC_TEST_COLOR + * Define this macro to allow colored output. + */ + +#ifndef CMC_TEST_H +#define CMC_TEST_H + +#include "timer.h" +#include +#include +#include +#include +#include +#include + +#ifdef CMC_TEST_COLOR +static const char *cmc_test_color[] = {"\x1b[32m", "\x1b[31m", "\x1b[35m"}; +#endif + +typedef struct cmc_test_info_s +{ + uintmax_t total; + uintmax_t passed; + uintmax_t failed; + bool aborted; + bool verbose; +} cmc_test_info; + +/* Logging function */ +static void cmc_test_log(const char *unit_name, const char *current_test, bool aborted, bool passed) +{ + if (aborted) + { +#ifdef CMC_TEST_COLOR + printf("UNIT_TEST %s %sABORTED\x1b[0m AT %s\n", + unit_name, + cmc_test_color[2], + current_test); +#else + printf("UNIT_TEST %s ABORTED at %s\n", unit_name, current_test); +#endif + } + else + { +#ifdef CMC_TEST_COLOR + printf("Test from %s %s%7s\x1b[0m %s\n", + unit_name, + passed ? cmc_test_color[0] : cmc_test_color[1], + passed ? "PASSED" : "FAILED", + current_test); +#else + printf("Test from %s %7s %s\n", unit_name, passed ? "PASSED" : "FAILED", current_test); +#endif + } +} + +#define CMC_CREATE_UNIT(UNAME, VERBOSE, BODY) \ + void UNAME(void) \ + { \ + const char *unit_name = #UNAME; \ + const char *current_test = NULL; \ + \ + cmc_test_info tinfo = {0}; \ + timer_t timer = {0}; \ + \ + tinfo.verbose = VERBOSE; \ + \ + /* Tests */ \ + TIMER_START(timer); \ + BODY; \ + TIMER_STOP(timer); \ + TIMER_CALC(timer); \ + \ + unittest_abort: \ + if (tinfo.aborted) \ + { \ + cmc_test_log(unit_name, current_test, true, false); \ + } \ + \ + printf("+--------------------------------------------------+\n"); \ + printf("| Unit Test Report : %-30s|\n", unit_name); \ + printf("| TOTAL : %10" PRIuMAX " |\n", tinfo.total); \ + printf("| PASSED : %10" PRIuMAX " |\n", tinfo.passed); \ + printf("| FAILED : %10" PRIuMAX " |\n", tinfo.failed); \ + printf("| |\n"); \ + printf("| Total Unit Test Runtime : %9.0f milliseconds |\n", timer.result); \ + printf("+--------------------------------------------------+\n"); \ + } + +#define CMC_CREATE_TEST(TNAME, BODY) \ + do \ + { \ + current_test = #TNAME; \ + \ + tinfo.total += 1; \ + \ + BODY; \ + \ + } while (0); + +#define CMC_TEST_PASS() \ + do \ + { \ + tinfo.passed += 1; \ + if (tinfo.verbose) \ + cmc_test_log(unit_name, current_test, false, true); \ + \ + } while (0) + +#define CMC_TEST_FAIL() \ + do \ + { \ + tinfo.failed += 1; \ + if (tinfo.verbose) \ + cmc_test_log(unit_name, current_test, false, false); \ + \ + } while (0) + +#define CMC_TEST_PASS_ELSE_FAIL(EXPRESSION) \ + do \ + { \ + if (EXPRESSION) \ + CMC_TEST_PASS(); \ + else \ + CMC_TEST_FAIL(); \ + \ + } while (0) + +#define CMC_TEST_ABORT() \ + do \ + { \ + \ + tinfo.aborted = true; \ + tinfo.total -= 1; \ + goto unittest_abort; \ + \ + } while (0) + +#endif /* CMC_TEST_H */