Using CXXTest with VC6 and VC7 at the command line
This can be a pig to set up out of the box. I did get it working, tho, and here's how. I was using Eclipse as my editor.
c:---> workspace----->cxxtest | --------->engine----->src | --------->tests | --------->testDict | <files>
The cxxtest directory is where cxxtest is unpacked. It contains the supplied directories cxxtest, docs and sample. This is all out of the box.
My code (.cpp, .h) is in the src directory.
My unit tests are in .h files in the tests directory.
The testDict directory is not necessary. It contains a VC6 console project, created using the VS6 IDE, which has a single .cpp file, and includes other files from the src directory. The point of this is to ensure that I can debug code using the IDE when I want to. It has no other connection to cxxtest. However the instructions to compile it are also in the Makefile below.
The <files> are:
The Makefile works with Microsofts nmake, cl and link commands. Here it is:
# # To run: # Start|Run|cmd # cd \workspace\engine # vcvars32.bat (for 6.0 ) [or vsvars32.bat for 7 -- perf tests show code runs faster under 6] # nmake clean # nmake # nmake tests # # # Common problems: # 1: If you get a link error with XLen in std lib, it's because you have VC6 includes ahead of VC7 ones in the include variable # 2: error: runner.cpp # atlconv.h(48) : error C2065: '_ASSERTE' : undeclared identifier # This one is cured by #include "windows.h" -- if you want to do that! # HOME_DIR=.\testDict OBJ_DIR=.\obj BIN_DIR=.\bin SRC_DIR=.\src ALL : testDict @echo erasing unwanted files -@erase "$(BIN_DIR)\testDict.ilk" -@erase "$(BIN_DIR)\testDict.pdb" -@erase /Q "tests\*op*.txt" @echo "Running code" "$(BIN_DIR)\testDict.exe" CLEAN : -@erase /Q "tests\*tempfiles*.txt" -@erase "$(SRC_DIR)\runner.cpp" -@erase /Q "$(OBJ_DIR)\*.*" -@erase /Q "$(BIN_DIR)\*.exe" #--------------------------------------------------------------------------------- # # * CXX_Test # #--------------------------------------------------------------------------------- CXXTEST_DIR=..\cxxtest TEST_DIR=./tests TESTS = $(TEST_DIR)/*.h TESTGEN = perl -w $(CXXTEST_DIR)/cxxtestgen.pl #--------------------------------------------------------------------------------- # # * Create temporary directories # #--------------------------------------------------------------------------------- !IF "$(OS)" == "Windows_NT" NULL= !ELSE NULL=nul !ENDIF # # These targets are dependencies on the compile and link, just to check directories exist # "$(BIN_DIR)" : if not exist "$(BIN_DIR)/$(NULL)" mkdir "$(BIN_DIR)" "$(OBJ_DIR)" : if not exist "$(OBJ_DIR)/$(NULL)" mkdir "$(OBJ_DIR)" #--------------------------------------------------------------------------------- # # Link # #--------------------------------------------------------------------------------- LINK32=link.exe LINK32_LIBS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib LINK32_OPTS_DEBUG=/nologo /subsystem:console /incremental:yes /debug /machine:I386 #LINK32_OPTS_RELEASE= /nologo /subsystem:console /incremental:no /machine:I386 LINK32_OPTS_RELEASE= /INCREMENTAL:NO /NOLOGO /SUBSYSTEM:CONSOLE /MACHINE:X86 BINARY=/out:"$(BIN_DIR)\testDict.exe" LINK32_OBJS= \ "$(OBJ_DIR)\myfile1.obj" \ "$(OBJ_DIR)\myfile2.obj" \ "$(OBJ_DIR)\testDict.obj" testDict : "$(BIN_DIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) $(BINARY) $(LINK32_LIBS) $(LINK32_OPTS_RELEASE) $(LINK32_OBJS) #--------------------------------------------------------------------------------- # # Include file of other file dependencies for link step # #--------------------------------------------------------------------------------- !IF "$(NO_EXTERNAL_DEPS)" != "1" !IF EXISTS("$(HOME_DIR)\testDict.dep") !INCLUDE "$(HOME_DIR)\testDict.dep" !ELSE !MESSAGE Warning: cannot find "$(HOME_DIR)\testDict.dep" !ENDIF !ENDIF #--------------------------------------------------------------------------------- # # Compile step (/c = no link) # #--------------------------------------------------------------------------------- CPP=cl.exe MYINCLUDES=/I $(CXXTEST_DIR) /I $(SRC_DIR) /I $(BIN_DIR) /I $(HOME_DIR) /I $(TEST_DIR) /I "." #MYDEBUGDEFINES= /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" #CPP_PROJ=/nologo /MLd /W3 /Gm /GX /ZI /Od $(MYINCLUDES) $(MYDEBUGDEFINES) /Fo"$(OBJ_DIR)\\" /Fd"$(OBJ_DIR)\\" /FD /GZ /c MYRELEASEDEFINES= /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" CPP_PROJ=/EHsc /nologo /ML /W3 /GX /O2 $(MYINCLUDES) $(MYRELEASEDEFINES) /Fo"$(OBJ_DIR)\\" /Fd"$(OBJ_DIR)\\" /FD /c # # Each step must have a target with the .obj name (referenced in the testDict dependencies above) # Each target is of the form: # SOURCE=$(SRC_DIR)\myfile1.cpp "$(OBJ_DIR)\myfile1.obj" : $(SOURCE) "$(OBJ_DIR)" $(CPP) $(CPP_PROJ) $(SOURCE) SOURCE=$(SRC_DIR)\myfile2.cpp "$(OBJ_DIR)\myfile2.obj" : $(SOURCE) "$(OBJ_DIR)" $(CPP) $(CPP_PROJ) $(SOURCE) SOURCE=$(HOME_DIR)\testDict.cpp "$(OBJ_DIR)\testDict.obj" : $(SOURCE) "$(OBJ_DIR)" $(CPP) $(CPP_PROJ) $(SOURCE) #--------------------------------------------------------------------------------- # # Test Runner # #--------------------------------------------------------------------------------- RUNNER32_OBJS= \ "$(OBJ_DIR)\runner.obj" \ "$(OBJ_DIR)\myfile1.obj" \ "$(OBJ_DIR)\myfile2.obj" RUNNER_SOURCE=$(SRC_DIR)\runner.cpp # Main tests target tests: $(BIN_DIR)\runner.exe @echo "running tests" -@erase $(RUNNER_SOURCE) $(BIN_DIR)\runner.exe "$(BIN_DIR)\runner.exe" : "$(BIN_DIR)" $(RUNNER32_OBJS) $(SRC_DIR)\runner.cpp $(LINK32) /out:"$(BIN_DIR)\runner.exe" $(LINK32_LIBS) $(LINK32_OPTS) $(RUNNER32_OBJS) # Runner.cpp depends on anything in the tests dir changing # Create runner.cpp "$(OBJ_DIR)\runner.obj": $(RUNNER_SOURCE) "$(OBJ_DIR)" $(TESTS) $(CPP) $(CPP_PROJ) $(RUNNER_SOURCE) CXXTESTGEN_FLAGS = \ --runner=ParenPrinter \ --have-eh \ --abort-on-fail "$(RUNNER_SOURCE)": $(TESTS) @echo "Creating runner.cpp" $(TESTGEN) $(CXXTESTGEN_FLAGS) -o $(RUNNER_SOURCE) --error-printer $(TESTS)
To run this:
This file is some of the tests for some code of mine called timer_util.h and timer_util.cpp, which contains a class QLTimer. This is a stopwatch class, which measures time. It has two methods, StartTimer() and StopTimer(), and two others getTicksElapsed() and getSeconds() to tell you how long the time interval between the two.
#ifndef __TIMERUTILTEST_H #define __TIMERUTILTEST_H #include#include // for cout #include // for sleep() #include "timer_util.h" using namespace std; class TimerUtilTest : public CxxTest::TestSuite { public: QLTimer * myObj; void setUp( void ) { myObj = new QLTimer(); } void tearDown( void ) { delete myObj; } void testTimer( void ) { TS_ASSERT_EQUALS( myObj->getTickFrequency(), "Ticks are 0 3579545 per second" ); TS_ASSERT_EQUALS( myObj->getTicksElapsed(), 0 ); TS_ASSERT_EQUALS( myObj->getSeconds(), 0.0 ); myObj->startTimer(); for (int i=0;i<10000;i++){} myObj->stopTimer(); TS_ASSERT( myObj->getTicksElapsed() < 200 ); } void testTimer2( void ) { myObj->startTimer(); Sleep(1000); // millisecs; sleep() on unix myObj->stopTimer(); cerr << "Time in Seconds to wait 1000 millisec=" << myObj->getSeconds() << endl; TS_ASSERT( myObj->getSeconds() > 0.9 ); } }; #endif // __TIMERUTILTEST_H
I hope that is helpful. Getting that makefile to work took two days, but it really isn't too hard once you have all the options for the compiler.
You can add extra include directories on to the end easily enough, if you have your .h's in other places. If you have .cpp's in other places which you need to link in, create a MYSRC_DIR variable, and point it to that directory; then make sure the extra .cpp files are specified being in $(MYSRC_DIR) rather than in $(SRC_DIR) as mine are in the above example.
Constructive feedback is welcomed to Roger Pearse.
This page has been online since 30th November 2006.
Return to Roger Pearse's Pages