I'm trying to build my new project in a very modular fashion,
with unit tests for each module.
https://github.com/luser-dr00g/inca/tree/master/olmec
Starting with the minimal unit-testing framework from here:
http://www.jera.com/techinfo/jtns/jtn002.html
I've add main() functions to my 2 modules io.c and st.c
guarded by #ifdef TESTMODULE.
Without TESTMODULE defined, the module will compile to a .o
file and link normally with the rest of the application
(once that's written). A testing program of a single unit
is coordinated by a short "test" source file. The io_test.c
program looks like this
#define TESTMODULE
#include "io.c"
and similarly for the other one, st_test.c.
Now I wanted to compose these two programs into one program
which executes all the tests sequentially.
So I wrote this all_tests.c program to coordinate name-mangling
and avoid conflicts.
# include <stdio.h>
# define main io_main
# define tests_run io_tests_run
# define all_tests io_all_tests
# include "io_test.c"
# undef main
# undef tests_run
# undef all_tests
int io_test(){
printf("running io_test\n");
return io_main();
}
# define main st_main
# define tests_run st_tests_run
# define all_tests st_all_tests
# include "st_test.c"
# undef main
# undef tests_run
# undef all_tests
int st_test(){
printf("running st_test\n");
return st_main();
}
int main(){
return
0 || io_test() || st_test() ;
}
Now this was fine for just 2 modules. But I didn't want to
have to keep this updated as I add new ones. I tried
using X-macros, knowing I could pass everything through
`cpp -P | indent -gnu -i4 -br -ce -cdw -nbc -brf -brs -l100 -bbo`.
But, a cpp macro cannot expand with an embedded newline.
So even though I can generate a #define line, I can't make 2.
So, I wrote this in m4 to generate the C program based on a
macro called UNITS. At first I defined it explicitly with
define(`UNITS', (io,st))
But then I added more machinery to generate this list with make.
Unfortunately, I couldn't get make to create a comma-separated
list. Thus, still more machinery had to go into the m4 file to
do the conversion.
all_tests.m4:
divert(`-1')
#
http://www.gnu.org/savannah-checkouts/gnu/m4/manual/m4-1.4.17/html_node/Foreach.html#Foreach
# foreach(x, (item_1, item_2, ..., item_n), stmt)
# parenthesized list, simple version
define(`foreach', `pushdef(`$1')_foreach($@)popdef(`$1')')
define(`_arg1', `$1')
define(`_foreach', `ifelse(`$2', `()', `',
`define(`$1', _arg1$2)$3`'$0(`$1', (shift$2), `$3')')')
define(`UNITS', (patsubst(UNITS,`\W',`,')))
divert`'dnl
`#' include <stdio.h>
foreach(`unit', UNITS, `
`#' define main unit`'_main
`#' define tests_run unit`'_tests_run
`#' define all_tests unit`'_all_tests
`#' include "unit`'_test.c"
`#' undef main
`#' undef tests_run
`#' undef all_tests
int unit`'_test(){
printf("running unit`'_test\n");
return unit`'_main();
}
')dnl
int main(){
return
0 foreach(`unit', UNITS, ` || unit`'_test() ') ;
}
makefile:
testprogs= $(notdir $(wildcard ./*_test.c))
unitprogs= $(subst _test,,$(testprogs))
units= $(basename $(unitprogs))
test:all_tests
./all_tests
all_tests.c:all_tests.m4 makefile $(unitprogs)
m4 -D UNITS="$(units)" $< >$@
And the funny part is I spent the whole day on this
instead of fixing the failing module.
josh@cadabra ~/inca/olmec
$ touch st.c
josh@cadabra ~/inca/olmec
$ make test
m4 -D UNITS="io st" all_tests.m4 >all_tests.c
cc all_tests.c -o all_tests
./all_tests
running io_test
ALL TESTS PASSED
Tests run: 4
running st_test
t->key != 42
Tests run: 1
makefile:7: recipe for target 'test' failed
make: *** [test] Error 1