# cases must have unique names, even across test suites.
# the first argument should be mutest.h
+if [ -z "$1" ]
+then
+ echo "Too few arguments" >&2
+ echo "Usage: $0 mutest_h_location [object files...]" >&2
+ exit 1
+fi
mutest_h="$1"
shift
echo "#include \"$mutest_h\""
echo
for file in "$@"
do
- 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-`
+ pr_file=`echo "$file" | sed 's/\"/\\\\\"/g'`
+ suite=`basename "$file" .o | sed 's/\"/\\\\\"/g'`
+ symbols=`nm -p "$file" | egrep '^[[:xdigit:]]{8} T mu_\w+$' | cut -c12-`
+ tests=`echo "$symbols" | egrep '^mu_test'`
+ inits=`echo "$symbols" | egrep '^mu_init'`
+ terms=`echo "$symbols" | egrep '^mu_term'`
+ echo -e '\tdo {'
+ echo -e '\t\tmutest_suite_name = "'"$suite"'";'
+ echo -e '\t\tmu_print(MU_SUITE, "\\nRunning suite '"'$suite'"'\\n");'
+ for init in $inits
do
- echo -e "\tmu_run_case($symbol);"
+ echo -e "\\t\\tmu_run_init($init);"
done
- echo -e "\tif (mutest_suite_failed) ++mutest_failed_suites;"
- echo -e "\telse ++mutest_passed_suites;"
- echo -e "\tmutest_suite_failed = 0;"
+ for testcase in $tests
+ do
+ echo -e "\t\tmu_run_case($testcase);"
+ done
+ for term in $terms
+ do
+ echo -e "\t\tmu_run_term($term);"
+ done
+ echo -e "\t\tif (mutest_suite_failed) ++mutest_failed_suites;"
+ echo -e "\t\telse ++mutest_passed_suites;"
+ echo -e "\t\tmutest_suite_failed = 0;"
+ echo -e '\t} while (0);'
echo
done
echo "}"
const char* mutest_suite_name;
int mutest_failed_suites;
int mutest_passed_suites;
+int mutest_skipped_suites;
int mutest_suite_failed;
mu_print(MU_SUMMARY, "\n"
"Tests done:\n"
- "\t%d test suite(s) passed, %d failed.\n"
+ "\t%d test suite(s) passed, %d failed, %d skipped.\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_skipped_suites,
mutest_passed_cases, mutest_failed_cases,
mutest_passed_checks, mutest_failed_checks);
- return mutest_failed_checks ? 1 : 0;
+ return (mutest_failed_suites + mutest_skipped_suites) ? 1 : 0;
}
* if you want to implement your own customized version */
void mu_run_suites();
+/* macro for running a single initialization function */
+#ifndef mu_run_init
+#define mu_run_init(name) \
+ { \
+ int name(); \
+ int r; \
+ mu_print(MU_CASE, "\t+ Executing initialization function " \
+ "'" #name "'...\n"); \
+ if ((r = name())) { \
+ mu_print(MU_ERROR, "%s:" #name ": initialization " \
+ "function failed (returned %d), " \
+ "skipping test suite...\n", \
+ mutest_suite_name, r); \
+ ++mutest_skipped_suites; \
+ break; \
+ } \
+ } do { } while (0)
+#endif /* mu_run_init */
+
/* 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(); \
+ mu_print(MU_CASE, "\t* Executing test case '" #name "'...\n");\
mutest_case_name = #name; \
+ void name(); \
name(); \
if (mutest_case_failed) { \
++mutest_failed_cases; \
} while (0)
#endif /* mu_run_case */
+/* macro for running a single termination function */
+#ifndef mu_run_term
+#define mu_run_term(name) \
+ do { \
+ mu_print(MU_CASE, "\t- Executing termination function '" \
+ #name "'...\n"); \
+ void name(); \
+ name(); \
+ } while (0)
+#endif /* mu_run_term */
+
/*
* 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_skipped_suites;
extern int mutest_suite_failed;
/* test cases */
extern const char* mutest_case_name;
V_CASE = 4
V_CHECK = 5
+R_OK = 1
+R_FAILED = 2
+R_SKIPPED = 3
+
verbose_level = V_ERROR
return (self.passed_count, self.failed_count)
+class TestSuiteInfo (object):
+
+ inits_re = re.compile(r'[0-9a-f]{8} T (mu_init\w*)', re.I)
+ terms_re = re.compile(r'[0-9a-f]{8} T (mu_term\w*)', re.I)
+ cases_re = re.compile(r'[0-9a-f]{8} T (mu_test\w*)', re.I)
+
+ def __init__(self, so_name):
+ proc = Popen(['nm', '-p', so_name], stdout=PIPE)
+ output = proc.communicate()[0]
+ self.inits = self.inits_re.findall(output)
+ self.terms = self.terms_re.findall(output)
+ self.cases = self.cases_re.findall(output)
+
+
class TestSuiteResult (object):
- failed = False
+ result = R_OK
passed_cases = 0
failed_cases = 0
passed_checks = 0
def __repr__(self):
return 'TestSuiteResult(failed=%s, passed_cases=%s, '\
'failed_cases=%s, passed_checks=%s, failed_checks=%s)'\
- % (self.failed,
+ % (self.result,
self.passed_cases, self.failed_cases,
self.passed_checks, self.failed_checks)
class TestSuite (object):
- def __init__(self, so, name, case_names):
+ def __init__(self, so, name, info):
self.name = name
self.so = so
try:
except ValueError:
self.api_version = 0
return
- self.cases = [TestCase(self.so, name) for name in case_names]
+ self.inits = dict([(name, get_fun(so, name, restype=c_int))
+ for name in info.inits])
+ self.terms = dict([(name, get_fun(so, name))
+ for name in info.terms])
+ self.cases = [TestCase(self.so, name)
+ for name in info.cases]
def run(self):
r = TestSuiteResult()
+
+ for name, func in self.inits.items():
+ log(V_CASE, "\t+ Executing initialization function "
+ "'%s'...", name);
+ res = func()
+ if res:
+ log(V_ERROR, "%s:%s: initialization function "
+ "failed (returned %d), "
+ "skipping test suite...\n",
+ self.name, name, res);
+ r.result = R_SKIPPED
+ return r
+
for case in self.cases:
log(V_CASE, "\t* Executing test case '%s'...",
case.name)
'failed.', case_passed_checks,
case_failed_checks)
if case_failed_checks:
- r.failed = True
+ r.result = R_FAILED
r.failed_cases += 1
else:
r.passed_cases += 1
r.passed_checks += case_passed_checks
r.failed_checks += case_failed_checks
- return r
-
-case_names_re = re.compile(r'[0-9a-f]{8} T (mu_test\w*)', re.I)
+ for name, func in self.terms.items():
+ log(V_CASE, "\t- Executing termination function "
+ "'%s'...", name)
+ func()
-def get_case_names(so_name):
- proc = Popen(['nm', '-p', so_name], stdout=PIPE)
- output = proc.communicate()[0]
- return case_names_re.findall(output)
+ return r
def parse_arguments(args):
results['skipped_suites'] += 1
continue
- case_names = get_case_names(so_name)
+ info = TestSuiteInfo(so_name)
- suite = TestSuite(so, suite_name, case_names)
+ suite = TestSuite(so, suite_name, info)
if suite.api_version != API_VERSION:
log(V_ERROR, 'Wrong API version (%s expected, %s '
r.passed_cases, r.failed_cases,
r.passed_checks, r.failed_checks)
- if r.failed:
+ if r.result == R_FAILED:
results['failed_suites'] += 1
+ elif r.result == R_SKIPPED:
+ results['skipped_suites'] += 1
else:
results['passed_suites'] += 1
results['failed_cases'] += r.failed_cases
*.o
*.so
tester
+test_suite_runner.c
CFLAGS = -Wall -std=c89
-TARGET=tester
+TARGET = tester
+SUITES_RUNNER_SRC = test_suite_runner.c
+MKMUTEST = ../mkmutest
+MUTEST_H = ../mutest.h
+MUTEST_C = ../mutest.c
OBJS = factorial.o sum.o
-TESTS = factorial_test.o sum_test.o
+TESTS = factorial_test.o sum_test.o init_fail_test.o
MUTEST = mutest.o
-TESTER = tester.o
-SO = factorial.so sum.so
-ALL = $(TESTER) $(OBJS) $(TESTS) $(MUTEST)
+SUITES_RUNNER = $(SUITES_RUNNER_SRC:.c=.o)
+SO = factorial.so sum.so init_fail_test.so
+ALL = $(SUITES_RUNNER) $(OBJS) $(TESTS) $(MUTEST)
all: $(TARGET)
py: $(SO)
$(TARGET): $(ALL)
+ $(CC) $(LDFLAGS) -o $@ $^
-$(TESTER): $(OBJS) $(TESTS) $(MUTEST)
- ../mkmutest ../mutest.h $(TESTS) | gcc -xc -o $(TESTER) -c -
+$(SUITES_RUNNER_SRC): $(MKMUTEST) $(MUTEST_H) $(TESTS)
+ $(MKMUTEST) $(MUTEST_H) $(TESTS) > $@
factorial.so: factorial_test.c
../py/mutest $(V) -a
clean:
- $(RM) $(TARGET) $(SO) $(ALL)
+ $(RM) $(TARGET) $(SO) $(ALL) $(SUITES_RUNNER_SRC)
.c.so:
$(CC) $(CFLAGS) $(LDFLAGS) -DMUTEST_PY -fPIC -shared -o $@ $^
--- /dev/null
+
+/* dummy test to illustrate how a test suite initialization could fail */
+
+#ifdef MUTEST_PY
+#include "../py/mutest.h"
+#else
+#include "../mutest.h"
+#endif
+
+static int ret = 0;
+
+int mu_init_success() {
+ return ret++;
+}
+
+int mu_init_fail() {
+ return ret;
+}
+
+/* this test will never be executed because the initialization failed */
+void mu_test_dummy() {
+}
+
+/* this test will never be executed either */
+void mu_term_success() {
+}
+
/* see factorial_test.c for more complete examples, this file is mostly to show
- * how to have multiple test suites, and a test suite that succeed. */
+ * how to have multiple test suites, test suite initialization and
+ * termination, and a test suite that succeed. */
#include "sum.h"
+#include <stdlib.h> /* malloc(), free() */
#ifdef MUTEST_PY
#include "../py/mutest.h"
#include "../mutest.h"
#endif
+/* unused, just for ilustrate the test suite initialization/termination */
+static char* global;
+
+int mu_init_sum() {
+ global = (char*) malloc(1024);
+
+ return 0; /* initialization OK */
+}
+
void mu_test_sum() {
mu_check(sum(4, 5) == 9);
mu_check(sum(-4, -5) == -9);
mu_check(sum(1, -1) == 0);
}
+void mu_term_sum() {
+ free(global);
+}
+