Issue
I want to write a shell script that will only compile and execute the unit test binaries in my project. All targets in the Makefile
(generated by CMake) associated with unit tests begin with UnitTest
, e.g. UnitTestArray
. How can I write a for loop in the shell script to individually compile all unit test binaries? I know how to sequentially run the pre-compiled binaries in a loop. It's just the compilation part that I am uncertain about. See my shell script below.
WORKDIR=$PWD
BUILD=~/Dropbox/Projects/Apeiron/build
BIN=$BUILD/bin
# Compile test binaries
cd $BUILD
for target in [UnitTest* in Makefile] # I'm not sure how to write this for loop.
do
echo ""
echo "Compiling $target"
make -j 8 $target
done
# Run tests
cd $BIN
for test in UnitTest*
do
echo ""
echo "Running $test"
./$test
done
cd $WORKDIR
The following are some example target rules in the generated Makefile:
#=============================================================================
# Target rules for targets named UnitTestApeiron
# Build rule for target.
UnitTestApeiron: cmake_check_build_system
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 UnitTestApeiron
.PHONY : UnitTestApeiron
# fast build rule for target.
UnitTestApeiron/fast:
$(MAKE) $(MAKESILENT) -f CMakeFiles/UnitTestApeiron.dir/build.make CMakeFiles/UnitTestApeiron.dir/build
.PHONY : UnitTestApeiron/fast
#=============================================================================
# Target rules for targets named UnitTestArray
# Build rule for target.
UnitTestArray: cmake_check_build_system
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 UnitTestArray
.PHONY : UnitTestArray
# fast build rule for target.
UnitTestArray/fast:
$(MAKE) $(MAKESILENT) -f CMakeFiles/UnitTestArray.dir/build.make CMakeFiles/UnitTestArray.dir/build
.PHONY : UnitTestArray/fast
Solution
I resorted to the solutions suggested by users @MadScientist and @Saboteur in the above comments. The following is the modification of my shell script using @Saboteur's solution:
WORKDIR=$PWD
BUILD=~/Dropbox/Projects/Apeiron/build
BIN=$BUILD/bin
# Compile test binaries
cd $BUILD
declare -a TargetList=$(grep "cmake_check_build_system" $BUILD/Makefile|cut -d":" -f1)
for target in ${TargetList[@]}; do
# Check if target keyword contains the substring "UnitTest"
if grep -q "UnitTest" <<< "$target"; then
printf "\nCompiling $target\n"
make -j 16 $target
fi
done
# Run tests
cd $BIN
for test in UnitTest*
do
printf "\nRunning $test\n"
./$test
done
cd $WORKDIR
This yields the following output:
$ ./unit_test.sh
Compiling UnitTestArray
[ 16%] Building CXX object _deps/googletest-build/googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.o
[ 33%] Linking CXX static library ../../../lib/libgtest.a
[ 33%] Built target gtest
[ 50%] Building CXX object _deps/googletest-build/googletest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.o
[ 66%] Linking CXX static library ../../../lib/libgtest_main.a
[ 66%] Built target gtest_main
[ 83%] Building CXX object CMakeFiles/UnitTestArray.dir/libs/DataContainers/test/UnitTestArray.cpp.o
[100%] Linking CXX executable bin/UnitTestArray
[100%] Built target UnitTestArray
Compiling UnitTestApeiron
Consolidate compiler generated dependencies of target gtest
[ 33%] Built target gtest
Consolidate compiler generated dependencies of target gtest_main
[ 66%] Built target gtest_main
[ 83%] Building CXX object CMakeFiles/UnitTestApeiron.dir/test/UnitTestApeiron.cpp.o
[100%] Linking CXX executable bin/UnitTestApeiron
[100%] Built target UnitTestApeiron
Running UnitTestApeiron
Running main() from /home/niran90/Dropbox/Projects/Apeiron/build/_deps/googletest-src/googletest/src/gtest_main.cc
[==========] Running 15 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 15 tests from ApeironTest
[ RUN ] ApeironTest.Min
[ OK ] ApeironTest.Min (0 ms)
[ RUN ] ApeironTest.Max
[ OK ] ApeironTest.Max (0 ms)
[ RUN ] ApeironTest.MinMax
[ OK ] ApeironTest.MinMax (0 ms)
[ RUN ] ApeironTest.Bound
[ OK ] ApeironTest.Bound (0 ms)
[ RUN ] ApeironTest.Sgn
[ OK ] ApeironTest.Sgn (0 ms)
[ RUN ] ApeironTest.Abs
[ OK ] ApeironTest.Abs (0 ms)
[ RUN ] ApeironTest.Floor
[ OK ] ApeironTest.Floor (0 ms)
[ RUN ] ApeironTest.Ceil
[ OK ] ApeironTest.Ceil (0 ms)
[ RUN ] ApeironTest.Round
[ OK ] ApeironTest.Round (0 ms)
[ RUN ] ApeironTest.isEqual
[ OK ] ApeironTest.isEqual (0 ms)
[ RUN ] ApeironTest.isLess
[ OK ] ApeironTest.isLess (0 ms)
[ RUN ] ApeironTest.isLessEqual
[ OK ] ApeironTest.isLessEqual (0 ms)
[ RUN ] ApeironTest.isLarger
[ OK ] ApeironTest.isLarger (0 ms)
[ RUN ] ApeironTest.isLargerEqual
[ OK ] ApeironTest.isLargerEqual (1 ms)
[ RUN ] ApeironTest.isBounded
[ OK ] ApeironTest.isBounded (0 ms)
[----------] 15 tests from ApeironTest (1 ms total)
[----------] Global test environment tear-down
[==========] 15 tests from 1 test suite ran. (1 ms total)
[ PASSED ] 15 tests.
Running UnitTestArray
Running main() from /home/niran90/Dropbox/Projects/Apeiron/build/_deps/googletest-src/googletest/src/gtest_main.cc
[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from ArrayTest
[ RUN ] ArrayTest.Initialisation
[ OK ] ArrayTest.Initialisation (0 ms)
[ RUN ] ArrayTest.AssignmentOperator
[ OK ] ArrayTest.AssignmentOperator (0 ms)
[----------] 2 tests from ArrayTest (0 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (0 ms total)
[ PASSED ] 2 tests.
I can see @MadScientist's suggestion to use CTest being very useful when the number of gtest
unit tests is quite large. This is because CTest provides a concise summary of the passed/failed tests. I had to configure my project CMakeLists.txt as follows:
enable_testing()
# Fetch GoogleTest directories
include(FetchContent)
FetchContent_Declare(GoogleTest URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip)
FetchContent_MakeAvailable(GoogleTest)
# Add unit test executables
add_executable(UnitTestApeiron ${PROJECT_SOURCE_DIR}/test/UnitTestApeiron.cpp)
add_executable(UnitTestArray ${PROJECT_SOURCE_DIR}/libs/DataContainers/test/UnitTestArray.cpp)
# Link with gtest, gtest_main, and associated libraries.
target_link_libraries(UnitTestApeiron gtest gtest_main)
target_link_libraries(UnitTestArray gtest gtest_main)
add_test(UnitTestApeiron ${BIN_DIRECTORY}/UnitTestApeiron)
add_test(UnitTestArray ${BIN_DIRECTORY}/UnitTestArray)
Then, simply running ctest
in the project build directory gives:
$ ctest
Test project /home/niran90/Dropbox/Projects/Apeiron/build
Start 1: UnitTestApeiron
1/2 Test #1: UnitTestApeiron .................. Passed 0.01 sec
Start 2: UnitTestArray
2/2 Test #2: UnitTestArray .................... Passed 0.00 sec
100% tests passed, 0 tests failed out of 2
Total Test time (real) = 0.01 sec
Answered By - niran90