CMake & CTest: make test ne construit pas de tests

88

J'essaye CTest dans CMake afin d'exécuter automatiquement certains de mes tests en utilisant make testtarget. Le problème est que CMake ne «comprend» pas que le test que je suis prêt à exécuter doit être construit car il fait partie du projet.

Je cherche donc un moyen de spécifier explicitement cette dépendance.

claf
la source

Réponses:

78

C'est sans doute un bogue dans CMake (précédemment suivi ici ) que cela ne fonctionne pas hors de la boîte. Une solution de contournement consiste à effectuer les opérations suivantes:

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

Ensuite, vous pouvez exécuter make checket il compilera et exécutera le test. Si vous avez plusieurs tests, vous devrez utiliser DEPENDS exe1 exe2 exe3 ...dans la ligne ci-dessus.

richq
la source
1
donc je suppose que la cible "make test" restera inutilisée car il semble que vous deviez choisir un nom de cible différent dans la commande add_custom_target?
claf le
Oui. La seule différence entre "make test" et "make check" est que le premier montre "Running tests ..." en premier et ne vérifie aucune dépendance de construction.
richq
2
@rq - mais comment puis-je faire cela avec plusieurs projets (quand un CMakeLists.txt est un sous-projet d'un autre) afin que chacun définisse la checkcible et qu'ils puissent entrer en collision
Artyom
2
@Artyom - dans ce cas, il vaut probablement mieux utiliser l'équivalent "make all test". En fait, c'est ce que je fais de toute façon.
richq
4
En fait, certains considèrent que c'est une fonctionnalité (pas un bogue) de cmake que vous pouvez exécuter "make test" et simplement exécuter les tests tels quels sans faire de reconstruction au préalable ...
DLRdave
53

Il existe en fait un moyen d'utiliser make test. Vous devez définir la construction de l'exécutable de test comme l'un des tests, puis ajouter des dépendances entre les tests. C'est:

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)
Iakov Nakhimovski
la source
11
C'est le seul qui évolue et ne vous oblige pas à construire les cibles "make all" juste pour exécuter le test. Un inconvénient possible: les détails des erreurs de construction sur les binaires n'apparaissent que dans le fichier LastTest.log généré et non sur stdout / stderr
Dave Abrahams
2
Bonne réponse! Vous devez cependant ajouter la configuration à la cible de construction. Sinon, il n'est pas possible d'exécuter les tests dans toutes les configurations. add_test (NAME "$ {ARGV0} _BUILD" COMMAND "$ {CMAKE_COMMAND}" --build $ {CMAKE_BINARY_DIR} --target $ {target} "--config" "$ <CONFIG>")
Daniel
1
Cela obstrue le journaliste de test avec un tas de tests bidons.
Si vous utilisez CMake> = 3.7, l'approche recommandée est d'utiliser des appareils. Voir ma réponse ci-dessous.
John Freeman le
13

J'utilise une variante de la réponse de richq. Au niveau supérieur CMakeLists.txt, j'ajoute une cible personnalisée,, build_and_testpour créer et exécuter tous les tests:

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

Dans les différents CMakeLists.txtfichiers de sous-projets ci- dessous test/, j'ajoute chaque exécutable de test en tant que dépendance de 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)

Avec cette approche, j'ai juste besoin de le faire make build_and_testau lieu de make test(ou make all test), et cela a l'avantage de créer uniquement du code de test (et ses dépendances). C'est dommage que je ne puisse pas utiliser le nom de la cible test. Dans mon cas, ce n'est pas si mal parce que j'ai un script de haut niveau qui débogue et publie hors de l'arbre (et compilé de manière croisée) en appelant cmakeet ensuite make, et cela se traduit testpar build_and_test.

De toute évidence, les éléments GTest ne sont pas nécessaires. Il se trouve que j'utilise / aime Google Test, et je voulais partager un exemple complet de son utilisation avec CMake / CTest. À mon humble avis, cette approche a également l'avantage de me permettre d'utiliser ctest -V, ce qui montre la sortie de Google Test pendant que les tests s'exécutent:

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
Trevor Robinson
la source
Dans cet exemple, y a-t-il un moyen d'obtenir make test pour faire ce que fait ctest -V au lieu de ctest? La sortie ctest semble très incomplète et indique simplement qu'il n'y a qu'un seul test.
Rajiv
6

Si vous essayez d'émuler make check, vous trouverez peut-être cette entrée de wiki utile:

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

Je viens de vérifier que c'est fait ce qu'il dit avec succès (CMake 2.8.10).

Samuel
la source
1
Cela construira tous les exécutables lors de l'exécution make check. Pour les tests avec des temps de compilation dominants, cela rend ctest -Rinutile.
usr1234567
4

Épargnez-vous le mal de tête:

make all test

Fonctionne hors de la boîte pour moi et construira des dépendances avant d'exécuter le test. Compte tenu de la simplicité, cela rend presque la make testfonctionnalité native pratique car elle vous donne la possibilité d'exécuter les derniers tests de compilation même si votre code est cassé.

quant
la source
1
Ne fonctionne pas avec CDash. Vous devez appeler make all && ctest et le bâtiment ne fait pas partie des tests téléchargés. Les avertissements ou erreurs de construction ne sont donc pas visibles.
usr1234567
2
Cela ne fonctionne pas non plus si vous voulez une construction parallèle, car les deux fonctionneront en parallèle: vous en avez besoin make -j4 all && make test. Et c'est aussi floconneux en utilisant un outil de construction non-Make.
poolie
4

Si vous utilisez CMake> = 3.7, l'approche recommandée consiste à utiliser des appareils :

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)

Cela fait ce qui suit:

  • Ajoute une testcible exécutable construite à partir detest.cpp
  • Ajoute un test_build"test" qui exécute Cmake pour générer la cibletest
  • Marque le test_buildtest comme une tâche de configuration de l'appareiltest_fixture
  • Ajouter un testtest qui exécute simplement l' testexécutable
  • Marque le testtest comme ayant besoin d'un appareil test_fixture.

Ainsi, chaque fois qu'un test testdoit être exécuté, il exécute d'abord test test_build, qui construit l'exécutable nécessaire.

John Freeman
la source
Si ce $<CONFIG>n'est pas défini --target, deviendra l'argument du --config.
loshad vtapkah
Je crois que $<CONFIG>c'est toujours non vide. C'est une expression de générateur pour le nom de la configuration: cmake.org/cmake/help/latest/manual/ ... Je vais éditer la réponse pour la mettre entre guillemets de toute façon juste parce que cela ne fait aucune différence.
John Freeman
Comment courez-vous cmake? Je fais de cette façon: mkdir build; cd build; cmake ..; make. Et il semble qu'il n'y ait aucune valeur par défaut et que toutes les variables associées sont vides, jusqu'à ce CMAKE_BUILD_TYPEqu'elles soient définies manuellement. (actuellement sur Debian 10, n'a pas vérifié d'autres plates-formes)
loshad vtapkah
1

C'est ce que j'ai martelé et que j'utilise:

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

Derrick
la source
0

Réponse de Derrick, simplifiée et commentée:

# 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"
)

Ce n'est pas tout à fait correct, car cela ne résout pas le problème de concurrence de l'exécution ninja all test, au cas où quelqu'un le ferait. Au contraire, parce que maintenant, vous avez deux processus ninja.

La solution ultime, si vous me le demandez, est d'utiliser Meson au lieu de CMake. Meson a raison, entre autres choses.

user2394284
la source
-3

Toutes les réponses ci-dessus sont parfaites. Mais en fait CMake utilise CTest comme outil de test, donc la méthode standard (je pense que c'est le cas) pour faire la mission est:

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

Exécutez ensuite cmake et faites pour construire les cibles. Après cela, vous pouvez exécuter make test ou simplement exécuter

ctest

vous obtiendrez le résultat. Ceci est testé sous CMake 2.8.

Vérifiez les détails sur: http://cmake.org/Wiki/CMake/Testing_With_CTest#Simple_Testing

holmescn
la source
5
Évalué car parfois vous ne souhaitez créer que les cibles requises pour les tests en cours d'exécution.
Dave Abrahams
12
Cette réponse semble mal comprendre la question: L'OP est déjà en train de faire exactement comme cette réponse recommande: L' utilisation CTest, enable_testing(), add_test(), etc. Le problème est qu'il doit émettre manuellement la commande de construction avant l' exécution des tests. Il souhaite que la make testcible crée automatiquement les exécutables de test si nécessaire.
bames53
-4

Toutes les réponses sont bonnes, mais elles impliquent une violation de la tradition d'exécuter un test par commande make test. J'ai fait cette astuce:

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

Cela signifie que le test consiste à construire (facultativement) et à exécuter la cible exécutable.

dyomes
la source
6
:-D Règle n ° 1: N'utilisez pas le système sans sh. Connaissez-vous un tel système?
dyomas
11
Oui, Windows en fait partie.
David Faure
3
Ceci est également codé en dur makeet perd la fonctionnalité de CMake de génération de scripts pour d'autres outils de construction.
poolie