The scons-users mailing list has a few references to how people have
implemented unit tests in their build, very few of those are
accompanied by code examples. The SCons
wiki has a page on unit
tests, which sucks to
say the least.
What I want is what Greg Ward
wants:
all the tests should run when one types scons check
.
if SCons builds nothing, the unit tests should also not run
if the unit test source changes, the unit test should be built and run
if code that the test links or includes changes, the test should be rebuilt and run
The second part is pretty easy, actually. SCons’ dependency handling
is so awesome it just works. You can ensure that a program is run
after it is built by using AddPostAction
:
1
2
| test = env.Program('test.c')
env.AddPostAction(test, test[0].abspath)
|
and as long as your test program is self contained (i.e. it’s a real
unit test and not a subsystem test framework, i.e. testing various
inputs as they go through your parser, say) then it just works.
You can build the first rule with an Alias
– SCons will let you
append targets to an Alias
to accumulate what that alias does:
1
| env.Alias('check', test)
|
and you can make sure that the test is always “run” (i.e. built) by
using AlwaysBuild
:
(In the latest version of SCons, the soon to be 0.97 that is currently
still in CVS, you can call AlwaysBuild
on an alias, making this last
line look instead like:
1
| env.AlwaysBuild('check')
|
and then you only need it once.)
The problem with this approach is that you really want to alias the
post action, not the build of the test. Consider this:
% scons check
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
gcc -o input/filtergen/t/scan input/filtergen/t/scan.o
/home/jaq/src/filtergen/filtergen--unittests/input/filtergen/t/scan
scons: done building targets.
Notice how scan
is relinked before execution? That’s because we
told SCons to AlwaysBuild
the test program. But when we’re running
the test, we only want to relink the program iff its source or
dependencies have changed. I haven’t yet found a way to make this
work the “right” way – I’ve tried to use Command
in place of the
post action with no success.
Anyway, you can bundle this all together with a new Tool like so:
1
2
3
4
5
6
7
8
9
10
| from SCons.Script.SConscript import SConsEnvironment
def UnitTest(env, source, **kwargs):
test = env.Program(source, **kwargs)
env.AddPostAction(test, test[0].abspath)
env.Alias('check', test)
env.AlwaysBuild(test)
return test
SConsEnvironment.UnitTest = UnitTest
|
Then use it in your code like so:
1
2
3
4
5
| scan = env.UnitTest('scan.c',
CPPPATH=testcpppath,
LIBPATH=testlibpath,
LIBS=testlibs,
CPPFLAGS=testcppflags)
|