CMake & CTest: make test не создает тесты

89

Я пробую CTest в CMake, чтобы автоматически запускать некоторые из моих тестов с использованием make testtarget. Проблема в том, что CMake не «понимает», что тест, который я хочу запустить, должен быть построен, поскольку он является частью проекта.

Поэтому я ищу способ явно указать эту зависимость.

Клаф
источник

Ответы:

79

Возможно , это ошибка в CMake (ранее отслеживавшаяся здесь ), которая не работает из коробки. Обходной путь - сделать следующее:

add_test(TestName ExeName)
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
                  DEPENDS ExeName)

Затем вы можете запустить, make checkи он скомпилирует и запустит тест. Если у вас есть несколько тестов, вам придется использовать DEPENDS exe1 exe2 exe3 ...в строке выше.

Richq
источник
1
поэтому я предполагаю, что цель "make test" останется неиспользованной, поскольку кажется, что вам нужно выбрать другое имя цели в команде add_custom_target?
claf
Ага. Единственная разница между «make test» и «make check» состоит в том, что первая из них сначала показывает «Выполняются тесты ...» и не проверяет какие-либо зависимости сборки.
richq
2
@rq - но как я могу сделать это с несколькими проектами (когда один CMakeLists.txt является подпроектом другого), чтобы каждый определял checkцель, и они могли столкнуться
Артем
2
@Artyom - в этом случае вам, вероятно, лучше просто использовать эквивалент "make all test". Фактически, я все равно этим занимаюсь.
richq 01
4
На самом деле, некоторые считают это особенностью (а не ошибкой) cmake, что вы можете запускать «make test» и просто запускать тесты, как они есть, без каких-либо предварительных сборок ...
DLRdave
55

На самом деле есть способ использовать make test. Вам необходимо определить сборку исполняемого файла теста как один из тестов, а затем добавить зависимости между тестами. То есть:

ADD_TEST(ctest_build_test_code
         "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target test_code)
ADD_TEST(ctest_run_test_code test_code)
SET_TESTS_PROPERTIES(ctest_run_test_code
                     PROPERTIES DEPENDS ctest_build_test_code)
Яков Нахимовский
источник
11
Это единственный вариант, который масштабируется и не заставляет вас создавать цели «сделать все» только для того, чтобы запустить тест. Возможный недостаток: детали ошибок сборки в двоичных файлах отображаются только в сгенерированном файле LastTest.log, а не в stdout / stderr
Дэйв Абрахамс
2
Хороший ответ! Однако вы должны добавить конфигурацию в цель сборки. В противном случае невозможно запустить тесты во всех конфигурациях. add_test (NAME "$ {ARGV0} _BUILD" COMMAND "$ {CMAKE_COMMAND}" --build $ {CMAKE_BINARY_DIR} --target $ {target} "--config" "$ <CONFIG>")
Daniel
1
Это забивает репортера тестов кучей фальшивых тестов.
Если вы используете CMake> = 3.7, рекомендуется использовать фикстуры. Смотрите мой ответ ниже.
Джон Фриман
13

Я использую вариант ответа Richq. На верхнем уровне CMakeLists.txtя добавляю настраиваемую цель build_and_testдля создания и запуска всех тестов:

find_package(GTest)
if (GTEST_FOUND)
    enable_testing()
    add_custom_target(build_and_test ${CMAKE_CTEST_COMMAND} -V)
    add_subdirectory(test)
endif()

В различных CMakeLists.txtфайлах подпроектов test/я добавляю каждый исполняемый файл теста в зависимости от build_and_test:

include_directories(${CMAKE_SOURCE_DIR}/src/proj1)
include_directories(${GTEST_INCLUDE_DIRS})
add_executable(proj1_test proj1_test.cpp)
target_link_libraries(proj1_test ${GTEST_BOTH_LIBRARIES} pthread)
add_test(proj1_test proj1_test)
add_dependencies(build_and_test proj1_test)

При таком подходе мне просто нужно make build_and_testвместо make test(или make all test), и он имеет преимущество только в создании тестового кода (и его зависимостей). Жаль, что я не могу использовать целевое имя test. В моем случае это не так уж и плохо, потому что у меня есть сценарий верхнего уровня, который выполняет отладку вне дерева и выпускает (и перекрестно компилирует) сборки, вызывая, cmakeа затем makeи переводя testв build_and_test.

Очевидно, что GTest не требуется. Я просто использую / люблю Google Test и хотел поделиться полным примером его использования с CMake / CTest. IMHO, этот подход также имеет то преимущество, что позволяет мне использовать ctest -V, который показывает вывод Google Test во время выполнения тестов:

1: Running main() from gtest_main.cc
1: [==========] Running 1 test from 1 test case.
1: [----------] Global test environment set-up.
1: [----------] 1 test from proj1
1: [ RUN      ] proj1.dummy
1: [       OK ] proj1.dummy (0 ms)
1: [----------] 1 test from proj1 (1 ms total)
1:
1: [----------] Global test environment tear-down
1: [==========] 1 test from 1 test case ran. (1 ms total)
1: [  PASSED  ] 1 test.
1/2 Test #1: proj1_test .......................   Passed    0.03 sec
Тревор Робинсон
источник
В этом примере, есть ли способ заставить make test выполнять то, что делает ctest -V вместо ctest? Вывод ctest выглядит очень неполным и просто говорит, что существует единственный тест.
Rajiv
6

Если вы пытаетесь подражать make check, вам может пригодиться эта вики-запись:

http://www.cmake.org/Wiki/CMakeEmulateMakeCheck

Я только что проверил, что он успешно выполняет то, что написано (CMake 2.8.10).

Самуэль
источник
1
Это создаст все исполняемые файлы при запуске make check. Для тестов с преобладающим временем компиляции это делает ctest -Rбесполезным.
usr1234567
4

Избавьте себя от головной боли:

make all test

Работает для меня "из коробки" и будет создавать зависимости перед запуском теста. Учитывая, насколько это просто, это почти делает встроенную make testфункциональность удобной, поскольку дает вам возможность запускать последние тесты компиляции, даже если ваш код сломан.

квант
источник
1
Не работает с CDash. Вы должны вызвать make all && ctest, и тогда здание не будет частью загруженного тестирования. Таким образом, предупреждения или ошибки сборки не видны.
usr1234567
2
Также не работает, если вы хотите параллельную сборку, поскольку они будут работать параллельно: вам нужно make -j4 all && make test. И это также ненадежно при использовании инструмента сборки, отличного от Make.
poolie
4

Если вы используете CMake> = 3.7, то рекомендуется использовать фикстуры :

add_executable(test test.cpp)
add_test(test_build
  "${CMAKE_COMMAND}"
  --build "${CMAKE_BINARY_DIR}"
  --config "$<CONFIG>"
  --target test
)
set_tests_properties(test_build PROPERTIES FIXTURES_SETUP    test_fixture)
add_test(test test)
set_tests_properties(test       PROPERTIES FIXTURES_REQUIRED test_fixture)

Это делает следующее:

  • Добавляет testисполняемую цель, созданную изtest.cpp
  • Добавляет test_build«тест», который запускает Cmake для построения цели.test
  • Помечает test_buildтест как задачу настройки приспособленияtest_fixture
  • Добавьте testтест, который просто запускает testисполняемый файл
  • Обозначает testтест как требующий приспособления test_fixture.

Итак, каждый раз, когда testнужно запустить тест test_build, он сначала запускает тест , который создает необходимый исполняемый файл.

Джон Фриман
источник
Если $<CONFIG>не установлен, то --targetстанет аргументом для --config.
лошад втапках
Я считаю, $<CONFIG>что всегда непусто. Это выражение генератора для имени конфигурации: cmake.org/cmake/help/latest/manual/ ... Я отредактирую ответ, чтобы заключить его в кавычки, потому что это не имеет значения.
Джон Фриман
Как ты бегаешь cmake? Я делаю это так: mkdir build; cd build; cmake ..; make. И похоже, что нет никаких значений по умолчанию, и все связанные переменные пусты, пока не CMAKE_BUILD_TYPEбудут установлены вручную. (в настоящее время на Debian 10, другие платформы не проверял)
loshad vtapkah
1

Вот что я придумал и использовал:

set(${PROJECT_NAME}_TESTS a b c)

enable_testing()
add_custom_target(all_tests)
foreach(test ${${PROJECT_NAME}_TESTS})
        add_executable(${test} EXCLUDE_FROM_ALL ${test}.cc)
        add_test(NAME ${test} COMMAND $<TARGET_FILE:${test}>)
        add_dependencies(all_tests ${test})
endforeach(test)

build_command(CTEST_CUSTOM_PRE_TEST TARGET all_tests)
string(CONFIGURE \"@CTEST_CUSTOM_PRE_TEST@\" CTEST_CUSTOM_PRE_TEST_QUOTED ESCAPE_QUOTES)
file(WRITE "${CMAKE_BINARY_DIR}/CTestCustom.cmake" "set(CTEST_CUSTOM_PRE_TEST ${CTEST_CUSTOM_PRE_TEST_QUOTED})" "\n")

YMMV

Деррик
источник
0

Ответ Деррика, упрощенный и прокомментированный:

# It is impossible to make target "test" depend on "all":
# https://gitlab.kitware.com/cmake/cmake/-/issues/8774
# Set a magic variable in a magic file that tells ctest
# to invoke the generator once before running the tests:
file(WRITE "${CMAKE_BINARY_DIR}/CTestCustom.cmake"
    "set(CTEST_CUSTOM_PRE_TEST ${CMAKE_MAKE_PROGRAM})\n"
)

Это не совсем правильно, так как не решает проблему параллелизма при запуске ninja all test, если кто-то это сделает. Напротив, потому что теперь у вас есть два процесса ниндзя.

(Ftr, я также разделял это решение здесь .)

user2394284
источник
-3

Все вышеперечисленные ответы идеальны. Но на самом деле CMake использует CTest в качестве инструментов тестирования, поэтому стандартный метод (я думаю, что это так) для выполнения этой миссии:

enable_testing ()
add_test (TestName TestCommand)
add_test (TestName2 AnotherTestCommand)

Затем запустите cmake и make для создания целей. После этого вы можете либо запустить make test , либо просто запустить

ctest

вы получите результат. Это проверено в CMake 2.8.

Подробности см. По адресу: http://cmake.org/Wiki/CMake/Testing_With_CTest#Simple_Testing.

Holmescn
источник
5
Проголосовали против, потому что иногда вы хотите создать только цели, необходимые для фактически выполняемых тестов.
Дэйв Абрахамс
12
Ответ на этот вопрос , кажется, неправильно вопрос: ОР уже делает именно так , как этот ответ рекомендует: Использование CTest, enable_testing(), add_test()и т.д. Проблема заключается в том, что он должен вручную выполнить команду сборки до запуска тестов. Он хочет, чтобы make testцель автоматически создавала тестовые исполняемые файлы по мере необходимости.
bames53
-4

Все ответы хороши, но они подразумевают нарушение традиции запускать тест по команде make test. Я проделал такой трюк:

add_test(NAME <mytest>
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMAND sh -c "make <mytarget>; $<TARGET_FILE:<mytarget>>")

Это означает, что тест состоит из сборки (необязательно) и запуска исполняемой цели.

диомы
источник
6
:-D Правило №1: Не используйте систему без sh. Вы знаете такую ​​систему?
dyomas
11
Да, Windows - одна из них.
Дэвид Фор
3
Это также жестко запрограммировано makeи теряет возможность CMake генерировать сценарии для других инструментов сборки.
poolie