From 866567d9f33e6b343abd242087555a6e6c29e29b Mon Sep 17 00:00:00 2001 From: Abhishek Chakravarti Date: Mon, 4 Feb 2019 08:19:48 +0530 Subject: [PATCH] Add unit tests for exception handling module Unit tests have been prepared for the following interfaces of the exception handling module: * sol_assert() * sol_try() * sol_erno_now() The provision of these unit tests has also resulted in the indirect testing of the following exception handling and unit testing module interfaces: * SOL_TRY * SOL_CATCH * sol_throw() * sol_tsuite_init2() * sol_tsuite_term() * sol_tsuite_register() * sol_tsuite_exec() * sol_tsuite_pass() * sol_tsuite_total() Even so, unit tests for the unit testing module need to be incorporated. Additionally, CLang has been introduced to the development process; this will allow continuous integration to be done with CLang in addition to GCC once llvm-cov is incorporated in the development process. This commit has been squashed from the 'issue/18' branch. --- .travis.yml | 10 +- Makefile | 34 +++--- inc/error.h | 14 +++ inc/test.h | 6 +- src/test.c | 23 ++-- test/error.t.c | 294 +++++++++++++++++++++++++++++++++++++++++++++ test/runner.c | 315 ++++++++++++++++++++++++++++++++++++++++++++++++- test/suite.h | 59 +++++++++ 8 files changed, 719 insertions(+), 36 deletions(-) create mode 100644 test/error.t.c create mode 100644 test/suite.h diff --git a/.travis.yml b/.travis.yml index 8f5fd52..12e6c54 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,12 +15,12 @@ # # # License: -# Released under the GNU General Public License version 3 (GPLv3). -# -# See the accompanying README.md file for complete licensing details. +# Released under the GNU General Public License version 3 (GPLv3) +# . See the accompanying LICENSE +# file for complete licensing details. # -# By continuing to use and/or distribute this file, you acknowledge that -# you have understood these license terms and accept them. +# BY CONTINUING TO USE AND/OR DISTRIBUTE THIS FILE, YOU ACKNOWLEDGE THAT +# YOU HAVE UNDERSTOOD THESE LICENSE TERMS AND ACCEPT THEM. ################################################################################ diff --git a/Makefile b/Makefile index 9b323b5..840af2b 100644 --- a/Makefile +++ b/Makefile @@ -4,22 +4,22 @@ # File: sol/Makefile # # Description: -# This Makefile is part of the Sol Library. It defines the build rules +# This Makefile is part of the Sol Library. It defines the build rules. # # Authors: # Abhishek Chakravarti # # Copyright: -# (c) 2018, Abhishek Chakravarti. +# (c) 2019, Abhishek Chakravarti. # # # License: -# Released under the GNU General Public License version 3 (GPLv3). -# -# See the accompanying README.md file for complete licensing details. +# Released under the GNU General Public License version 3 (GPLv3) +# . See the accompanying LICENSE +# file for complete licensing details. # -# By continuing to use and/or distribute this file, you acknowledge that -# you have understood these license terms and accept them. +# BY CONTINUING TO USE AND/OR DISTRIBUTE THIS FILE, YOU ACKNOWLEDGE THAT +# YOU HAVE UNDERSTOOD THESE LICENSE TERMS AND ACCEPT THEM. ################################################################################ @@ -34,18 +34,19 @@ DIR_TEST = test # Set commands -CMD_CC = $(CC) -CMD_SO = $(CC) -CMD_LD = $(CC) +CMD_CC = $(CC) +CMD_SO = $(CC) +CMD_LD = $(CC) CMD_COV = gcov +CMD_RUN = ./$(OUT_LD) # Set command options -OPT_CC = -c -fPIC -std=c99 -Wall -g -O0 -coverage -OPT_SO = -shared -g -O0 -coverage -OPT_LD = -std=c99 -Wall -g -O0 -coverage +OPT_CC = -c -fPIC -std=c99 -Wall -g -O0 -coverage +OPT_SO = -shared -g -O0 -coverage +OPT_LD = -std=c99 -Wall -g -O0 -coverage OPT_COV = -o $(DIR_BLD) @@ -53,8 +54,9 @@ OPT_COV = -o $(DIR_BLD) # Set command inputs INP_SO = $(DIR_BLD)/test.o -INP_LD = $(DIR_TEST)/runner.c -INP_COV = $(DIR_BLD)/runner.gcda $(DIR_BLD)/test.gcda +INP_LD = $(DIR_TEST)/runner.c $(DIR_TEST)/error.t.c +INP_COV = $(DIR_BLD)/test.gcda $(DIR_BLD)/runner.gcda $(DIR_BLD)/error.t.gcda +INP_RUN = $(DIR_BLD)/test.log @@ -97,7 +99,7 @@ $(DIR_BLD)/%.o: $(DIR_SRC)/%.c # Rule to generate integration build integration: $(OUT_LD) - ./$(OUT_LD) + $(CMD_RUN) $(INP_RUN) mv $(DEP_COV) $(DIR_BLD) $(CMD_COV) $(OPT_COV) $(INP_COV) mv $(OUT_COV) $(DIR_BLD) diff --git a/inc/error.h b/inc/error.h index 2deaf3e..23ea283 100644 --- a/inc/error.h +++ b/inc/error.h @@ -109,6 +109,20 @@ typedef size_t sol_erno; +/* + * SOL_ERNO_TEST - unit test failure + * + * The SOL_ERNO_TEST symbolic constant indicates that a unit test failed. + * Note that this error code does not indicate an exception with the + * exception handling system, but rather that the condition that a unit + * test is checking has not been fulfilled. This error code is reserved by + * the Sol Library, and should **not** be redefined by client code. + */ +#define SOL_ERNO_TEST ( (sol_erno)0x4 ) + + + + /* * SOL_TRY - start of try block * diff --git a/inc/test.h b/inc/test.h index 5f2e2b8..80c107f 100644 --- a/inc/test.h +++ b/inc/test.h @@ -173,8 +173,8 @@ sol_tsuite_init(sol_tsuite *tsuite); * - SOL_ERNO_PTR if an invalid pointer is passed as an argument */ extern sol_erno -sol_tsuite_init2(sol_tsuite *tsuite, - sol_tlog const *tlog +sol_tsuite_init2(sol_tsuite *tsuite, + sol_tlog *tlog ); @@ -221,7 +221,7 @@ sol_tsuite_term(sol_tsuite *tsuite); */ extern sol_erno sol_tsuite_register(sol_tsuite *tsuite, - sol_tcase const *tcase, + sol_tcase *tcase, char const *desc ); diff --git a/src/test.c b/src/test.c index 697c8a1..ef5effe 100644 --- a/src/test.c +++ b/src/test.c @@ -38,8 +38,8 @@ * tsuite_init() - initialises test suite member fields */ static void -tsuite_init(sol_tsuite *tsuite, /* contextual test suite */ - sol_tlog const *tlog /* logging callback */ +tsuite_init(sol_tsuite *tsuite, /* contextual test suite */ + sol_tlog *tlog /* logging callback */ ) { register int i; @@ -85,8 +85,8 @@ sol_tsuite_init(sol_tsuite *tsuite) * sol_tsuite_init2() - declared in sol/inc/test.h */ extern sol_erno -sol_tsuite_init2(sol_tsuite *tsuite, - sol_tlog const *tlog +sol_tsuite_init2(sol_tsuite *tsuite, + sol_tlog *tlog ) { SOL_TRY: @@ -113,8 +113,9 @@ sol_tsuite_term(sol_tsuite *tsuite) { /* reset member fields, including logging callback, if @tsuite * is valid */ - if (tsuite) + if (tsuite) { tsuite_init (tsuite, 0); + } } @@ -125,7 +126,7 @@ sol_tsuite_term(sol_tsuite *tsuite) */ extern sol_erno sol_tsuite_register(sol_tsuite *tsuite, - sol_tcase const *tcase, + sol_tcase *tcase, char const *desc ) { @@ -148,7 +149,9 @@ sol_tsuite_register(sol_tsuite *tsuite, * same as that of the test case */ itr = tsuite->desc [tsuite -> total]; len = SOL_TCASE_MAXDESCLEN; - while (len-- && (*itr++ = *desc++)); + while (len-- && (*itr++ = *desc++)) { + ; + } /* update total number of registered test cases */ tsuite->total++; @@ -250,11 +253,13 @@ sol_tsuite_exec(sol_tsuite *tsuite) * logging it if the logging callback is available; update count * of failed test cases as required */ for (i = 0; i < tsuite->total; i++) { - if ((erno = tsuite->tcase [i] ())) + if ((erno = tsuite->tcase [i] ())) { tsuite->fail++; + } - if (tsuite->tlog) + if (tsuite->tlog) { tsuite->tlog (tsuite->desc [i], erno); + } } SOL_CATCH: diff --git a/test/error.t.c b/test/error.t.c new file mode 100644 index 0000000..8ff577e --- /dev/null +++ b/test/error.t.c @@ -0,0 +1,294 @@ +/****************************************************************************** + * SOL LIBRARY v1.0.0+41 + * + * File: sol/test/error.t.c + * + * Description: + * This file is part of the internal quality checking of the Sol Library. + * It implements the test suite for the exception handling module. + * + * Authors: + * Abhishek Chakravarti + * + * Copyright: + * (c) 2019 Abhishek Chakravarti + * + * + * License: + * Released under the GNU General Public License version 3 (GPLv3) + * . See the accompanying LICENSE + * file for complete licensing details. + * + * BY CONTINUING TO USE AND/OR DISTRIBUTE THIS FILE, YOU ACKNOWLEDGE THAT + * YOU HAVE UNDERSTOOD THESE LICENSE TERMS AND ACCEPT THEM. + ******************************************************************************/ + + + + +#include "../inc/test.h" +#include "./suite.h" + + + + +/* + * DESC_ASSERT_01 - description for sol_assert() unit test #1 + */ +#define DESC_ASSERT_01 "sol_assert() must not throw an error for a" \ + " predicate that evaluates to true" + + + + +/* + * DESC_ASSERT_02 - description for sol_assert() unit test #2 + */ +#define DESC_ASSERT_02 "sol_assert() must throw an error for a" \ + " predicate that evaluates to false" + + + + +/* + * DESC_TRY_01 - description for sol_try() unit test #1 + */ +#define DESC_TRY_01 "sol_try() must not throw an error for a function that" \ + " returns a SOL_ERNO_NULL error code" + + + + +/* + * DESC_TRY_02 - description for sol_try() unit test #2 + */ +#define DESC_TRY_02 "sol_try() must throw an error for a function that" \ + " returns an error code other than SOL_ERNO_NULL" + + + + +/* + * DESC_NOW_01 - description for sol_erno_now() unit test #1 + */ +#define DESC_NOW_01 "sol_erno_now() must return the current error code that" \ + " was thrown by the last calling function" + + + + +/* + * assert_pass() - simulates success of sol_assert() + */ +static sol_erno +assert_pass(void) +{ +SOL_TRY: + /* this condition will always pass */ + sol_assert (1 == 1, SOL_ERNO_TEST); + +SOL_CATCH: + /* control will never reach here */ + sol_throw (); +} + + + + +/* + * assert_fail() - simulates failure of sol_assert() + */ +static sol_erno +assert_fail(void) +{ +SOL_TRY: + /* this condition will always fail */ + sol_assert (1 != 1, SOL_ERNO_TEST); + +SOL_CATCH: + /* control will always reach here */ + sol_throw (); +} + + + + +/* + * try_pass() - simulates success of sol_try() + */ +static sol_erno +try_pass(void) +{ +SOL_TRY: + /* assert_pass() is guaranteed to succeed */ + sol_try (assert_pass ()); + +SOL_CATCH: + /* control will never reach here */ + sol_throw (); +} + + + + +/* + * try_fail() - simulates failure of sol_try() + */ +static sol_erno +try_fail(void) +{ +SOL_TRY: + /* assert_fail() is guaranteed to fail */ + sol_try (assert_fail ()); + +SOL_CATCH: + /* control will always reach here */ + sol_throw (); +} + + + + +/* + * test_assert_01() - sol_assert() unit test #1 + */ +static sol_erno +test_assert_01(void) +{ +SOL_TRY: + /* check test condition described by DESC_ASSERT_01 */ + sol_assert (!assert_pass (), SOL_ERNO_TEST); + +SOL_CATCH: + /* catch exceptions */ + sol_throw (); +} + + + + +/* + * test_assert_02() - sol_assert() unit test #2 + */ +static sol_erno +test_assert_02(void) +{ +SOL_TRY: + /* check test condition described by DESC_ASSERT_02 */ + sol_assert (assert_fail (), SOL_ERNO_TEST); + +SOL_CATCH: + /* catch exceptions */ + sol_throw (); +} + + + + +/* + * test_try_01() - sol_try() unit test #1 + */ +static sol_erno +test_try_01(void) +{ +SOL_TRY: + /* check test condition described by DESC_TRY_01 */ + sol_assert (!try_pass (), SOL_ERNO_TEST); + +SOL_CATCH: + /* catch exceptions */ + sol_throw (); +} + + + + +/* + * test_try_02() - sol_try() unit test #2 + */ +static sol_erno +test_try_02(void) +{ +SOL_TRY: + /* check test condition described by DESC_TRY_01 */ + sol_assert (try_fail (), SOL_ERNO_TEST); + +SOL_CATCH: + /* catch exceptions */ + sol_throw (); +} + + + + +/* + * test_now_01() - sol_erno_now() unit test #1 + */ +static sol_erno +test_now_01(void) +{ +SOL_TRY: + /* assert_fail () is guaranteed to fail with SOL_ERNO_TEST */ + sol_try (assert_fail ()); + +SOL_CATCH: + /* check test condition described by DESC_NOW_01 */ + return SOL_ERNO_TEST == sol_erno_now () + ? SOL_ERNO_NULL + : SOL_ERNO_TEST; +} + + + + +/* + * __sol_tsuite_error() - declared in sol/test/suite.h + */ +extern sol_erno +__sol_tsuite_error(sol_tlog *log, + int *pass, + int *fail, + int *total + ) +{ + auto sol_tsuite __ts, *ts = &__ts; + +SOL_TRY: + /* check preconditions */ + sol_assert (log && pass && fail && total, SOL_ERNO_PTR); + + /* initialise test suite */ + sol_try (sol_tsuite_init2 (ts, log)); + + /* register test cases */ + sol_try (sol_tsuite_register (ts, test_assert_01, DESC_ASSERT_01)); + sol_try (sol_tsuite_register (ts, test_assert_02, DESC_ASSERT_02)); + sol_try (sol_tsuite_register (ts, test_try_01, DESC_TRY_01)); + sol_try (sol_tsuite_register (ts, test_try_02, DESC_TRY_02)); + sol_try (sol_tsuite_register (ts, test_now_01, DESC_NOW_01)); + + /* execute test cases */ + sol_try (sol_tsuite_exec (ts)); + + /* return test counts */ + sol_try (sol_tsuite_pass (ts, pass)); + sol_try (sol_tsuite_fail (ts, fail)); + sol_try (sol_tsuite_total (ts, total)); + + /* wind up */ + sol_tsuite_term (ts); + +SOL_CATCH: + /* wind up and throw current exception */ + sol_tsuite_term (ts); + sol_throw (); +} + + + + +/****************************************************************************** + * EOF + * Built on hyperion [Tue Jan 29 02:37:24 UTC 2019] + ******************************************************************************/ + diff --git a/test/runner.c b/test/runner.c index 9f707b0..6664f01 100644 --- a/test/runner.c +++ b/test/runner.c @@ -1,10 +1,319 @@ +/****************************************************************************** + * SOL LIBRARY v1.0.0+41 + * + * File: sol/test/runner.c + * + * Description: + * This file is part of the internal quality checking of the Sol Library. + * It implements the test runner to execute all the module test suites. + * + * Authors: + * Abhishek Chakravarti + * + * Copyright: + * (c) 2019 Abhishek Chakravarti + * + * + * License: + * Released under the GNU General Public License version 3 (GPLv3) + * . See the accompanying LICENSE + * file for complete licensing details. + * + * BY CONTINUING TO USE AND/OR DISTRIBUTE THIS FILE, YOU ACKNOWLEDGE THAT + * YOU HAVE UNDERSTOOD THESE LICENSE TERMS AND ACCEPT THEM. + ******************************************************************************/ + + + + +#include "./suite.h" +#include #include -#include "../inc/error.h" -#include "../inc/test.h" -int main(void) + + +/* + * suite - function pointer to test suites + */ +typedef sol_erno /* error code */ +(suite)(sol_tlog *log, /* logging callback */ + int *pass, /* passed test cases */ + int *fail, /* failed test cases */ + int *total /* total test cases executed */ + ); + + + + +/* + * SUITE_COUNT - count of test suites + */ +#define SUITE_COUNT 1 + + + + +/* + * SUITE_ERROR - index of exception handling test suite + */ +#define SUITE_ERROR 0 + + + + +/* + * LOG_ERRMSG - message to indicate error in opening log file + */ +#define LOG_ERRMSG "[!] Warning: couldn\'t open log file for unit tests\n" + + + + +/* + * LOG_SIGMAMSG - log message for sigma test counters + */ +#define LOG_SIGMAMSG "\n%d test(s) run, %d passed, %d failed\n" + + + + +/* + * LOG_PASSMSG - log message for passed test cases + */ +#define LOG_PASSMSG "OK: %s\n" + + + + +/* + * LOG_FAILMSG - log message for failed test cases + */ +#define LOG_FAILMSG "[!] FAILED: %s [0x%.8lx]\n" + + + + +/* + * suite_hnd - test suite handles + */ +static suite *suite_hnd [SUITE_COUNT]; + + + + +/* + * stat__suite - test case statistics for each test suite + */ +static struct { + int pass [SUITE_COUNT]; /* passed test cases per suite */ + int fail [SUITE_COUNT]; /* failed test cases per suite */ + int total [SUITE_COUNT]; /* total test cases per suite */ +} stat_suite; + + + + +/* + * stat_sigma - summation statistics for all test suites + */ +static struct { + int pass; /* sigma of passed test cases */ + int fail; /* sigma of failed test cases */ + int total; /* sigma of total test cases */ +} stat_sigma; + + + + +/* + * log_hnd - handle to test log file + */ +static FILE *log_hnd; + + + + +/* + * log_init() - initialise test log file + */ +static inline void +log_init(int argc, /* count of command line arguments */ + char **argv /* command line arguments */ + ) +{ + /* open test log file; show error if failed */ + if (argc == 2 && !(log_hnd = fopen (argv [1], "a+e"))) { + printf (LOG_ERRMSG); + } +} + + + + +/* + * log_term() - terminate test log file + */ +static inline void +log_term(void) +{ + /* release log file if it's open */ + if (log_hnd) { + fclose (log_hnd); + } +} + + + + +/* + * log_tcase() - callback to log test case result + */ +static inline void +log_tcase(char const *desc, /* test case description */ + sol_erno const erno /* error code returned by test case */ + ) +{ + /* log message according to test execution status */ + if (log_hnd) { + erno ? fprintf (log_hnd, LOG_FAILMSG, desc, erno) + : fprintf (log_hnd, LOG_PASSMSG, desc); + } +} + + + + +/* + * log_sigma() - prints and logs sigma statistics + */ +static void +log_sigma(void) +{ + + /* print sigma statistics */ + printf (LOG_SIGMAMSG, + stat_sigma.total, + stat_sigma.pass, + stat_sigma.fail + ); + + /* log sigma statistics */ + if (log_hnd) { + fprintf (log_hnd, + LOG_SIGMAMSG, + stat_sigma.total, + stat_sigma.pass, + stat_sigma.fail + ); + } +} + + + + +/* + * stat_init() - initialise test statistics + */ +static void +stat_init(void) { + register int i; /* iterator */ + + /* initialise test suite statistics */ + for (i = 0; i < SUITE_COUNT; i++) { + stat_suite.pass [i] = 0; + stat_suite.fail [i] = 0; + stat_suite.total [i] = 0; + } + + /* initialise sigma statistics */ + stat_sigma.pass = 0; + stat_sigma.fail = 0; + stat_sigma.total = 0; +} + + + + +/* + * stat_sum() - calculates sigma statistics + */ +static void +stat_sum(void) +{ + register int i; /* iterator */ + + /* sum test suite statistics */ + for (i = 0; i < SUITE_COUNT; i++) { + stat_sigma.pass += stat_suite.pass [i]; + stat_sigma.fail += stat_suite.fail [i]; + stat_sigma.total += stat_suite.total [i]; + } +} + + + + +/* + * suite_init() - initialises test suites + */ +static void +suite_init(void) +{ + /* register test suites */ + suite_hnd [SUITE_ERROR] = __sol_tsuite_error; +} + + + + +/* + * suite_exec() - executes test suites + */ +static void +suite_exec(void) +{ + register int i; /* iterator */ + + /* execute test suites */ + for (i = 0; i < SUITE_COUNT; i++) { + suite_hnd [i] (log_tcase, + stat_suite.pass + i, + stat_suite.fail + i, + stat_suite.total + i + ); + } +} + + + + +/* + * main() - main entry point of test runner + */ +int main(int argc, char **argv) +{ + /* initialise */ + log_init (argc, argv); + stat_init (); + suite_init (); + + /* execute */ + suite_exec (); + stat_sum (); + log_sigma (); + + /* terminate */ + log_term (); return 0; } + + + +/****************************************************************************** + * EOF + * Built on hyperion [Tue Jan 29 02:37:24 UTC 2019] + ******************************************************************************/ + diff --git a/test/suite.h b/test/suite.h new file mode 100644 index 0000000..7bff8d7 --- /dev/null +++ b/test/suite.h @@ -0,0 +1,59 @@ +/****************************************************************************** + * SOL LIBRARY v1.0.0+41 + * + * File: sol/test/suite.h + * + * Description: + * This file is part of the internal quality checking of the Sol Library. + * It declares the test suites for all the component modules. + * + * Authors: + * Abhishek Chakravarti + * + * Copyright: + * (c) 2019 Abhishek Chakravarti + * + * + * License: + * Released under the GNU General Public License version 3 (GPLv3) + * . See the accompanying LICENSE + * file for complete licensing details. + * + * BY CONTINUING TO USE AND/OR DISTRIBUTE THIS FILE, YOU ACKNOWLEDGE THAT + * YOU HAVE UNDERSTOOD THESE LICENSE TERMS AND ACCEPT THEM. + ******************************************************************************/ + + + + +#if !defined __SOL_LIBRARY_TEST_SUITES +#define __SOL_LIBRARY_TEST_SUITES + + + + +#include "../inc/test.h" + + + + +extern sol_erno +__sol_tsuite_error(sol_tlog *log, + int *pass, + int *fail, + int *total + ); + + + + +#endif /* !defined __SOL_LIBRARY_TEST_SUITES */ + + + + +/****************************************************************************** + * EOF + * Built on hyperion [Tue Jan 29 02:37:24 UTC 2019] + ******************************************************************************/ +