Cmake: Project Structure with Unit Tests

CMake: Project structure with unit tests

For questions 1 & 2, I would recommend making a library from your non-test files excluding main.cpp (in this case just src/sqr.cpp and src/sqr.h), and then you can avoid listing (and more importantly re-compiling) all the sources twice.

For question 3, these commands add a test called "MyTest" which invokes your executable "test" without any arguments. However, since you've added these commands to test/CMakeLists.txt and not your top-level CMakeLists.txt, you can only invoke the test from within the "test" subdirectory of your build tree (try cd test && ctest -N). If you want the test to be runnable from your top-level build directory, you'd need to call add_test from the top-level CMakeLists.txt. This also means you have to use the more verbose form of add_test since your test exe isn't defined in the same CMakeLists.txt

In your case, since you're running cmake in the root folder, your build tree and your source tree are one and the same. This is known as an in-source build and isn't ideal, which leads to question 4.

The preferred method for generating the build tree is to do an out-of-source build, i.e. create a directory somewhere outside of your source tree and execute cmake from there. Even creating a "build" directory in the root of your project and executing cmake .. would provide a clean structure which won't interfere with your source tree.

One final point is to avoid calling executables "test" (case-sensitive). For reasons why, see this answer.

To achieve these changes, I'd do the following:

CMakeLists.txt:

cmake_minimum_required (VERSION 2.8)
project (TEST)
add_subdirectory (src)
add_subdirectory (test)
enable_testing ()
add_test (NAME MyTest COMMAND Test)


src/CMakeLists.txt:
add_library (Sqr sqr.cpp sqr.h)
add_executable (demo main.cpp)
target_link_libraries (demo Sqr)


test/CMakeLists.txt:
find_package (Boost COMPONENTS system filesystem unit_test_framework REQUIRED)
include_directories (${TEST_SOURCE_DIR}/src
${Boost_INCLUDE_DIRS}
)
add_definitions (-DBOOST_TEST_DYN_LINK)
add_executable (Test test.cpp)
target_link_libraries (Test
Sqr
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
)

CMake unit structure with many tests

This is the solution I use in my library:

FILE( GLOB log_testprograms *.cpp )

FOREACH( testsource ${log_testprograms} )
GET_FILENAME_COMPONENT( filename ${testsource} NAME_WE )
celma_add_celma_boost_testprogram( ${filename} )
ENDFOREACH()

celma_add_celma_boost_testprogram is a macro like this:

macro( celma_add_celma_boost_testprogram  filename )
add_executable( ${filename} ${filename}.cpp )
target_link_libraries( ${filename} celma ${Boost_Test_Link_Libs} )
add_test( ${filename} ${CMAKE_CURRENT_BINARY_DIR}/${filename} )
endmacro( celma_add_celma_boost_testprogram )

Celma is the name of my library, the variable Boost_Test_Link_Libs contains the list of Boost libraries to link against, including the Boost.Test library.

Project Structure for unit testing (qtest) when using target_sources() command in sub directory

As you described, you can make a new static library target Project_Lib. Take advantage of the fact that you parameterized the target name by using the project name (${PROJECT_NAME}), so you actually don't have to change all of the CMakeLists.txt files in the subfolders. Just change the project name.

As I commented, simply exclude the main.cpp file from the static library, and add it to a separate executable target instead.

In Project_Root/CMakeLists.txt:

cmake_minimum_required(VERSION 3.5)

set(SRC_DIR src)

# Change the project name, as now the static library is the primary target.
project(
Project_Lib
LANGUAGES CXX
)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Create the static library target, whose sources are populated in subdirectories.
add_library(${PROJECT_NAME} STATIC)
target_include_directories(${PROJECT_NAME} PRIVATE ${SRC_DIR})
# The only modification necessary in the subdirectories is to *exclude* the
# main.cpp file from the target_sources for the static library.
add_subdirectory(${SRC_DIR})

# Add *only* the main.cpp file to the executable target.
add_executable(Project_Exe src/main.cpp)
# Link the static library target to the executable.
target_link_libraries(Project_Exe PRIVATE ${PROJECT_NAME})

In tests/CMakeLists.txt:

...
# Link the static library to your Test executable also.
target_link_libraries(Test PRIVATE Project_Lib Qt5::Test)

How do I configure my CMake project to run all unit tests?

enable_testing() enables add_test() after it. So just make sure you call enable_testing() before any add_test() you want to enable.

CMake: How to setup unit testing of a library

add_library(DictionaryPath
...
src/WordsGraph.h
...
)

target_include_directories(DictionaryPath PUBLIC
...
PRIVATE src)

WordsGraph.h is in src, and you declared src as a private include directory for DictionaryPath.

If you don't want to do more than calling target_link_libraries when creating a unit test, you should either move WordsGraph.h into include, or declare src as a public or interface include directory.

If you don't want to move WordsGraph.h into include, nor declare src as a public or interface include directory, you should add a call to target_include_directories:

add_executable(WordsGraphTest test/WordsGraphTest.cpp)
target_link_libraries(WordsGraphTest GTest::main DictionaryPath)

target_include_directories(WordsGraphTest PRIVATE src)

add_test(NAME WordsGraphTest COMMAND WordsGraphTest)

Unit Test Output & Project Structure Advice --- CMake + Google Test Framework

  1. It is actually a recommended cmake practice called out-of-source build
  2. AFAIK it is not recommended to install recompiled googletest libraries in the system. So there should not be any problem if you compile it as shared or static library as part of your project build. I have used googletest 1.6.0 this way without problems on Windows, Linux, OSX and Android.
  3. I'm not sure about CTest but custom target is definitely able to run all tests for you. Here is a short solution I can suggest:

Add following lines into your top level CMakeLists.txt (before adding any tests):

add_custom_target(test)
macro(run_test test_target)
add_custom_target(${test_target}_runtest
COMMAND ${test_target} #cmake 2.6 required
DEPENDS ${test_target}
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")
add_dependencies(test ${test_target}_runtest)
endmacro()

Next for each test add a single line to append your test to the test target:

#add_executable(mytest ${mysources})
run_test(mytest)


Related Topics



Leave a reply



Submit