From: Leandro Lucarella Date: Wed, 10 Dec 2008 15:50:59 +0000 (-0200) Subject: Factor out most C code from mkmutest to mutest.c X-Git-Tag: 1.0~14 X-Git-Url: https://git.llucax.com/software/mutest.git/commitdiff_plain/5ce20f06cfd85f7498652f86a4111ed6c456b9f9 Factor out most C code from mkmutest to mutest.c This makes easier to improve the tester code, without having to code it as a shell script embedded string. Now the only generated code is the one that runs the test suites. You can also implement your own test suites execution function; just write it in a separated C module and don't use the mkmutest generator. --- diff --git a/mkmutest b/mkmutest index 82d71d1..5e8db82 100755 --- a/mkmutest +++ b/mkmutest @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # This file is part of mutest, a micro testing framework for C. # @@ -10,110 +10,32 @@ # # Please, read the README file for more details. -TESTER=' -#include /* printf(), fprintf() */ - -/* macro for running a single test case */ -#define run_case(name) \ - do { \ - if (mutest_verbose > 2) \ - printf("\\t- executing test case \"" #name "\"\\n"); \ - void name(); \ - mutest_case_name = #name; \ - name(); \ - if (mutest_case_failed) { \ - ++mutest_failed_cases; \ - mutest_suite_failed = 1; \ - } else ++mutest_passed_cases; \ - mutest_case_failed = 0; \ - } while (0) - -/* globals for managing test suites */ -static const char* mutest_suite_name; -static int mutest_failed_suites; -static int mutest_passed_suites; -static int mutest_suite_failed; - -/* globals for managing test cases */ -static const char* mutest_case_name; -static int mutest_failed_cases; -static int mutest_passed_cases; -int mutest_case_failed; - -/* globals for managing checks */ -int mutest_failed_checks; -int mutest_passed_checks; - -/* - * verbosity level (each level shows all the previous levels too): - * 0 shows only errors - * 1 shows a summary - * 2 shows test suites progress - * 3 shows test cases progress - */ -int mutest_verbose; - -static void run_suites(); - -int main(int argc, char* argv[]) { - - /* - * arguments checking, both -v -v and -vv are accepted for setting - * verbosity to 2. - */ - while (*++argv) - if (strncmp(*argv, "-v", 2) == 0) { - ++mutest_verbose; - char* c = (*argv) + 1; - while (*++c) - if (*c == '"'v'"') - ++mutest_verbose; - else - break; - } - - run_suites(); - - if (mutest_verbose) { - printf("\\n"); - printf("Tests done:\\n"); - printf("\\t%d test suite(s) passed, %d failed.\\n", - mutest_passed_suites, - mutest_failed_suites); - printf("\\t%d test case(s) passed, %d failed.\\n", - mutest_passed_cases, - mutest_failed_cases); - printf("\\t%d check(s) passed, %d failed.\\n", - mutest_passed_checks, mutest_failed_checks); - } - - return mutest_failed_checks ? 1 : 0; -} - -' # the trick here is getting all the test cases present in an object file using # nm. All the tests must take and return void, start with "mutest_" and, of # course, should not be static, which leads to a small limitation: all test # cases must have unique names, even across test suites. -echo "$TESTER" -echo "static void run_suites() {" + +# the first argument should be mutest.h +mutest_h="$1" +shift +echo "#include \"$mutest_h\"" +echo "void mu_run_suites() {" echo for file in "$@" do - suite="`basename $file .o`" - echo "\tmutest_suite_name = \"$suite\";" - echo "\tif (mutest_verbose > 1)" - echo "\t\tprintf(\"\\\\nRunning suite \\\"$suite\\\"\\\\n\");" + suite="`basename "$file" .o | sed 's/\"/\\\\\"/g'`" + echo -e '\tmutest_suite_name = "'"$suite"'";' + echo -e '\tmu_print(MU_SUITE, "\\nRunning suite \"'"$suite"'\"\\n");' for symbol in `nm -p "$file" \ | egrep '^[[:xdigit:]]{8} T mu_test_\w+$' \ | cut -c12-` do - echo "\trun_case($symbol);" + echo -e "\tmu_run_case($symbol);" done - echo "\tif (mutest_suite_failed) ++mutest_failed_suites;" - echo "\telse ++mutest_passed_suites;" - echo "\tmutest_suite_failed = 0;" + echo -e "\tif (mutest_suite_failed) ++mutest_failed_suites;" + echo -e "\telse ++mutest_passed_suites;" + echo -e "\tmutest_suite_failed = 0;" echo done echo "}" diff --git a/mutest.c b/mutest.c new file mode 100644 index 0000000..2971480 --- /dev/null +++ b/mutest.c @@ -0,0 +1,73 @@ + +#include "mutest.h" /* MU_* constants, mu_print() */ +#include /* printf(), fprintf() */ +#include /* strncmp() */ + +/* + * note that all global variables are public because they need to be accessed + * from other modules, like the test suites or the module implementing + * mu_run_suites() + */ + +/* globals for managing test suites */ +const char* mutest_suite_name; +int mutest_failed_suites; +int mutest_passed_suites; +int mutest_suite_failed; + + +/* globals for managing test cases */ +const char* mutest_case_name; +int mutest_failed_cases; +int mutest_passed_cases; +int mutest_case_failed; + + +/* globals for managing checks */ +int mutest_failed_checks; +int mutest_passed_checks; + + +/* verbosity level, see mutest.h */ +int mutest_verbose_level = 1; /* exported for use in test suites */ + + + +/* + * only -v is supported right now, both "-v -v" and "-vv" are accepted for + * increasing the verbosity by 2. + */ +void parse_args(int argc, char* argv[]) { + while (*++argv) { + if (strncmp(*argv, "-v", 2) == 0) { + ++mutest_verbose_level; + char* c = (*argv) + 1; + while (*++c) { + if (*c != 'v') + break; + ++mutest_verbose_level; + } + } + } +} + + +int main(int argc, char* argv[]) { + + parse_args(argc, argv); + + mu_run_suites(); + + mu_print(MU_SUMMARY, "\n" + "Tests done:\n" + "\t%d test suite(s) passed, %d failed.\n" + "\t%d test case(s) passed, %d failed.\n" + "\t%d check(s) passed, %d failed.\n" + "\n", + mutest_passed_suites, mutest_failed_suites, + mutest_passed_cases, mutest_failed_cases, + mutest_passed_checks, mutest_failed_checks); + + return mutest_failed_checks ? 1 : 0; +} + diff --git a/mutest.h b/mutest.h index e0467e8..1f5561d 100644 --- a/mutest.h +++ b/mutest.h @@ -1,39 +1,110 @@ -#include // fprintf +#include /* fprintf() */ #ifdef __cplusplus extern "C" { #endif -extern int mutest_failed_checks; -extern int mutest_passed_checks; -extern int mutest_case_failed; - +/* check that an expression evaluates to true, continue if the check fails */ #define mu_check(exp) \ do { \ + mu_print(MU_CHECK, "\t\t* Checking mu_check(%s)...\n", #exp); \ if (exp) ++mutest_passed_checks; \ else { \ ++mutest_failed_checks; \ mutest_case_failed = 1; \ - fprintf(stderr, "%s:%d: mu_check(%s) failed, " \ + mu_print(MU_ERROR, "%s:%d: mu_check(%s) failed, " \ "resuming test case\n", __FILE__, \ __LINE__, #exp); \ } \ } while (0) +/* + * ensure that an expression evaluates to true, abort the current test + * case if the check fails + */ #define mu_ensure(exp) \ do { \ + mu_print(MU_CHECK, "\t\t* Checking mu_ensure(%s)...\n", #exp);\ if (exp) ++mutest_passed_checks; \ else { \ ++mutest_failed_checks; \ mutest_case_failed = 1; \ - fprintf(stderr, "%s:%d: mu_ensure(%s) failed, " \ + mu_print(MU_ERROR, "%s:%d: mu_ensure(%s) failed, " \ "aborting test case\n", __FILE__, \ __LINE__, #exp); \ return; \ } \ } while (0) +/* + * you don't need to pay any attention to what's next, unless you want to do + * some customization, of course, in which case, you're encouraged to take + * a look an play =) + */ + +/* verbosity level (each level shows all the previous levels too) */ +enum { + MU_QUIET = 0, /* be completely quiet */ + MU_ERROR, /* shows errors only */ + MU_SUMMARY, /* shows a summary */ + MU_SUITE, /* shows test suites progress */ + MU_CASE, /* shows test cases progress */ + MU_CHECK /* shows the current running check */ +}; + +/* print a message according to the verbosity level */ +#define mu_print(level, ...) \ + do { \ + if (mutest_verbose_level >= level) { \ + if (mutest_verbose_level == MU_ERROR) \ + fprintf(stderr, __VA_ARGS__); \ + else \ + fprintf(stdout, __VA_ARGS__); \ + } \ + } while (0) + +/* + * this function implements the test suites execution, you should generate + * a module with this function using mkmutest, or take a look to that script + * if you want to implement your own customized version */ +void mu_run_suites(); + +/* macro for running a single test case */ +#ifndef mu_run_case +#define mu_run_case(name) \ + do { \ + mu_print(MU_CASE, "\t- executing test case \"" #name "\"\n"); \ + void name(); \ + mutest_case_name = #name; \ + name(); \ + if (mutest_case_failed) { \ + ++mutest_failed_cases; \ + mutest_suite_failed = 1; \ + } else ++mutest_passed_cases; \ + mutest_case_failed = 0; \ + } while (0) +#endif /* mu_run_case */ + +/* + * mutest exported variables for internal use, do not use directly unless you + * know what you're doing. + */ +extern const char* mutest_suite_name; +extern int mutest_failed_suites; +extern int mutest_passed_suites; +extern int mutest_suite_failed; +/* test cases */ +extern const char* mutest_case_name; +extern int mutest_failed_cases; +extern int mutest_passed_cases; +extern int mutest_case_failed; +/* checks */ +extern int mutest_failed_checks; +extern int mutest_passed_checks; +/* verbosity */ +extern int mutest_verbose_level; + #ifdef __cplusplus } #endif diff --git a/sample/Makefile b/sample/Makefile index d352654..e67a320 100644 --- a/sample/Makefile +++ b/sample/Makefile @@ -2,13 +2,16 @@ # Show the tests summary V=-v +CFLAGS = -Wall -std=c89 + TARGET=tester OBJS = factorial.o sum.o TESTS = factorial_test.o sum_test.o +MUTEST = mutest.o TESTER = tester.o SO = factorial.so sum.so -ALL = $(TESTER) $(OBJS) $(TESTS) +ALL = $(TESTER) $(OBJS) $(TESTS) $(MUTEST) all: $(TARGET) @@ -16,13 +19,16 @@ py: $(SO) $(TARGET): $(ALL) -$(TESTER): $(OBJS) $(TESTS) - ../mkmutest $(TESTS) | gcc -xc -o $(TESTER) -c - +$(TESTER): $(OBJS) $(TESTS) $(MUTEST) + ../mkmutest ../mutest.h $(TESTS) | gcc -xc -o $(TESTER) -c - factorial.so: factorial_test.c sum.so: sum_test.c +$(MUTEST): ../mutest.c + $(CC) $(CFLAGS) -c -o mutest.o $^ + test: $(TARGET) ./$(TARGET) $(V)