CMake: структура проекта с юнит-тестами

Я пытаюсь структурировать свой проект, чтобы включить источники производства (в src подпапка) и тесты (в test вложенная папка). Я использую CMake, чтобы построить это. В качестве минимального примера у меня есть следующие файлы:

CMakeLists.txt:

cmake_minimum_required (VERSION 2.8)
project (TEST)

add_subdirectory (src)
add_subdirectory (test)

SRC / CMakeLists.txt:

add_executable (demo main.cpp sqr.cpp)

SRC / sqr.h

#ifndef SQR_H
#define SQR_H
double sqr(double);
#endif // SQR_H

SRC / sqr.cpp

#include "sqr.h"double sqr(double x) { return x*x; }

src / main.cpp — использует sqr, на самом деле не имеет значения

тест / CMakeLists.txt:

find_package(Boost COMPONENTS system filesystem unit_test_framework REQUIRED)

include_directories (${TEST_SOURCE_DIR}/src)

ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK)

add_executable (test test.cpp ${TEST_SOURCE_DIR}/src/sqr.cpp)

target_link_libraries(test
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
)

enable_testing()
add_test(MyTest test)

тест / test.cpp:

#define BOOST_TEST_MODULE SqrTests
#include <boost/test/unit_test.hpp>

#include "sqr.h"
BOOST_AUTO_TEST_CASE(FailTest)
{
BOOST_CHECK_EQUAL(5, sqr(2));
}

BOOST_AUTO_TEST_CASE(PassTest)
{
BOOST_CHECK_EQUAL(4, sqr(2));
}

Несколько вопросов:

  1. Имеет ли эта структура смысл? Каковы лучшие практики при структурировании этого кода? (Я иду из C # и Java, и там это в некотором смысле легче)
  2. Мне не нравится тот факт, что я должен перечислить все файлы из src папка в test/CMakeLists.txt файл. Если бы это был проект библиотеки, я бы просто связал библиотеку. Есть ли способ избежать перечисления всех файлов cpp из другого проекта?
  3. Какие линии enable_testing() а также add_test(MyTest test) делать? Я не видел никакого эффекта. Как я могу запустить тесты из CMake (или CTest)?
  4. Пока я только что побежал cmake . в корневой папке, но это создало беспорядок с временными файлами везде. Как я могу получить результаты компиляции в разумной структуре?

105

Решение

По вопросам 1 & 2, я бы порекомендовал сделать библиотеку из ваших не тестовых файлов, исключая main.cpp (в данном случае просто src / sqr.cpp и src / sqr.h), и тогда вы можете избежать листинга (и, что более важно, перекомпиляции) все источники в два раза.

Для вопроса 3 эти команды добавляют тест под названием «MyTest», который вызывает ваш исполняемый файл «test» без каких-либо аргументов. Однако, поскольку вы добавили эти команды в test / CMakeLists.txt, а не в CMakeLists.txt верхнего уровня, вы можете вызывать тест только из подкаталога test вашего дерева сборки (попробуйте cd test && ctest -N). Если вы хотите, чтобы тест выполнялся из вашего каталога сборки верхнего уровня, вам нужно вызвать add_test из верхнего уровня CMakeLists.txt. Это также означает, что вы должны использовать более подробную форму add_test так как ваш тестовый exe не определен в том же CMakeLists.txt

В вашем случае, так как вы запускаете cmake в корневой папке, ваше дерево сборки и дерево исходного кода — это одно и то же. Это называется сборкой в ​​исходном коде и не является идеальной, что приводит к вопросу 4.

Предпочтительный метод генерации дерева сборки — это сборка вне исходного кода, то есть создать каталог где-нибудь за пределами вашего исходного дерева и выполнить cmake оттуда. Даже создание каталога «build» в корне вашего проекта и выполнение cmake .. обеспечит чистую структуру, которая не будет мешать вашему исходному дереву.

И последнее: избегайте называть исполняемые файлы «тестовыми» (чувствительными к регистру). По причинам, почему, см этот ответ.

Чтобы добиться этих изменений, я бы сделал следующее:

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)

тест / 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}
)
100

Другие решения

Мне нравится пример @Fraser, но я бы использовал команду add_test в test / CMakeLists.txt и использовал enable_testing перед add_subdirectory (test).

Таким образом, вы можете запускать свои тесты из директории сборки верхнего уровня, указав свои тесты в test / CMakeLists.txt.

Результат будет выглядеть следующим образом (я использовал пример @Fraser):

CMakeLists.txt

cmake_minimum_required (VERSION 2.8)
project (TEST)
add_subdirectory (src)

enable_testing ()
add_subdirectory (test)

SRC / CMakeLists.txt

add_library (Sqr sqr.cpp sqr.h)
add_executable (demo main.cpp)
target_link_libraries (demo Sqr)

Тест / 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}
)
add_test (NAME MyTest COMMAND Test)
39