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 ...в строке выше.


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, я также разделял это решение здесь .)


-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.


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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.