I've created a Python hack so I don't have to create/change my CppUnit
test names in three places. The script reads a .cpp file ( eg tests.cpp ), finds the names of all your tests, and writes the appropriate declarations to the corresponding .h file.
Note that this is a personal hack, released here in case it's useful to anyone, and comes with no guarantees, assurances, or claims to being good code : ) This might also duplicate known techniques, be an inferior solution, be brittle code, etc, etc.
However, I have found it extremely useful, and thus I share it here.
- Save the script: paste this Python source code into a text editor and save in the dir with your tests
- Remove spaces: Delete the first 2 spaces from each line
- Change the filenames ( tests.cpp and tests.h ) to match your test file names
- ( Note that only one .cpp and one .h file are supported for now )
- Insert markers into your header file, to denote the blocks that will be autogenerated
- Run it: You can run it from the command line, but much cooler to make it part of your build process:
- In Visual Studio, add a "Pre Build" event that is simply "python cppUnitHack.py"
- In unixland, do something with your makefiles, I guess : )
- Celebrate: You should now be able to write and change test names once and only once!
Note that the script expects spaces for your ( C++ ) source code indentation; if you use tabs this script may need modification.
Feel free to refactor this source code, or tell me why it sucks, or whatever you like : )
Python script follows:
import os, re
# change these to the name your test files
testSourceFileName = 'tests.cpp'
testHeaderFileName = 'tests.h'
# gather test declarations from the source file:
testSourceFile = file( testSourceFileName, 'r')
source = testSourceFile.read()
# note that we ignore tests whose names are line-commented out with '//'
# but would still include multi-line commented out tests, if the comment began on an earlier line
# so use line-comments '//' if you want to comment out tests
testNames = re.findall( r'\ns*void Tests::(test\w+)\(\)', source )
testMacros = [ ( ' CPPUNIT_TEST( %s );' % testName ) for testName in testNames ]
memberDeclarations = [ ( ' void %s();' % testName ) for testName in testNames ]
# read header file, delete old version:
testHeaderFile = file( testHeaderFileName, 'r')
header = testHeaderFile.read()
os.remove( testHeaderFileName )
# substitute in the test names:
cre = re.compile( '( *// BEGIN_GENERATED_BLOCK_TEST_MACROS //)' +
'( *// END_GENERATED_BLOCK_TEST_MACROS //)',
header = cre.sub( r'\1\n\n' + '\n'.join( testMacros ) + r'\n\n\3', header )
cre = re.compile( '( *// BEGIN_GENERATED_BLOCK_MEMBER_DECLARATIONS //)' +
'( *// END_GENERATED_BLOCK_MEMBER_DECLARATIONS //)',
header = cre.sub( r'\1\n\n' + '\n'.join( memberDeclarations ) + r'\n\n\3', header )
# finally, create a 'new' header file with the generated contents:
testHeaderFile = file( testHeaderFileName, 'w')
testHeaderFile.write( header )