CMake y CTest: hacer prueba no crea pruebas

89

Estoy probando CTest en CMake para ejecutar automáticamente algunas de mis pruebas usando make testtarget. El problema es que CMake no "entiende" que la prueba que estoy dispuesto a ejecutar tiene que ser construida ya que es parte del proyecto.

Así que estoy buscando una forma de especificar explícitamente esta dependencia.

claf
fuente

Respuestas:

79

Podría decirse que es un error en CMake (anteriormente rastreado aquí ) que esto no funciona de inmediato. Una solución alternativa es hacer lo siguiente:

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

Luego, puede ejecutar make checky compilará y ejecutará la prueba. Si tiene varias pruebas, entonces debería usar DEPENDS exe1 exe2 exe3 ...en la línea anterior.

richq
fuente
1
así que supongo que el objetivo "hacer prueba" no se utilizará, ya que parece que tienes que elegir un nombre de objetivo diferente en el comando add_custom_target.
claf
Sí. La única diferencia entre "make test" y "make check" es que el primero muestra "Ejecutando pruebas ..." primero y no verifica ninguna dependencia de compilación.
richq
2
@rq, pero ¿cómo puedo hacer esto con varios proyectos (cuando un CMakeLists.txt es un subproyecto de otro) para que cada uno defina el checkobjetivo y puedan colisionar
Artyom
2
@Artyom: en ese caso, probablemente sea mejor usar el equivalente "hacer todas las pruebas". De hecho, esto es lo que hago de todos modos.
richq
4
En realidad, algunos consideran que es una característica (no un error) de cmake que puede ejecutar "make test" y simplemente ejecutar las pruebas como están sin hacer ninguna reconstrucción primero ...
DLRdave
55

De hecho, hay una forma de utilizarlo make test. Debe definir la compilación del ejecutable de prueba como una de las pruebas y luego agregar dependencias entre las pruebas. Es decir:

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
fuente
11
Este es el único que se amplía y no le obliga a crear los objetivos de "hacer todos" solo para ejecutar la prueba. Una posible desventaja: los detalles de los errores de compilación en los binarios solo se muestran en el archivo LastTest.log generado y no en stdout / stderr
Dave Abrahams
2
¡Buena respuesta! Sin embargo, debe agregar la configuración al destino de compilación. De lo contrario, no es posible ejecutar las pruebas en todas las configuraciones. add_test (NOMBRE "$ {ARGV0} _BUILD" COMMAND "$ {CMAKE_COMMAND}" --build $ {CMAKE_BINARY_DIR} --target $ {target} "--config" "$ <CONFIG>")
Daniel
1
Esto obstruye al reportero de la prueba con un montón de pruebas falsas.
Si está usando CMake> = 3.7, el enfoque recomendado es usar accesorios. Vea mi respuesta a continuación.
John Freeman
13

Utilizo una variante de la respuesta de richq. En el nivel superior CMakeLists.txt, agrego un objetivo personalizado build_and_test, para construir y ejecutar todas las pruebas:

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

En los diversos CMakeLists.txtarchivos de subproyecto a continuación test/, agrego cada ejecutable de prueba como una dependencia 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)

Con este enfoque, solo necesito en make build_and_testlugar de make test(o make all test), y tiene la ventaja de crear solo código de prueba (y sus dependencias). Es una pena que no pueda usar el nombre del objetivo test. En mi caso, no es tan malo porque tengo un script de nivel superior que depura y libera (y compila de forma cruzada) compilaciones fuera del árbol llamando cmakey luego make, y se traduce testen build_and_test.

Obviamente, las cosas de GTest no son necesarias. Simplemente uso / me gusta Google Test, y quería compartir un ejemplo completo de su uso con CMake / CTest. En mi humilde opinión, este enfoque también tiene la ventaja de permitirme usar ctest -V, que muestra el resultado de la prueba de Google mientras se ejecutan las pruebas:

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
fuente
En este ejemplo, ¿hay alguna manera de hacer que make test haga lo que hace ctest -V en lugar de ctest? La salida de ctest parece muy incompleta y solo dice que hay una sola prueba.
Rajiv
6

Si está tratando de emular make check, puede encontrar útil esta entrada de wiki:

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

Acabo de comprobar que hace lo que dice con éxito (CMake 2.8.10).

Samuel
fuente
1
Esto compilará todos los ejecutables cuando se ejecute make check. Para pruebas con tiempos de compilación dominantes, esto es ctest -Rinútil.
usr1234567
4

Ahórrese el dolor de cabeza:

make all test

Funciona de fábrica para mí y creará dependencias antes de ejecutar la prueba. Dado lo simple que es, casi hace que la make testfuncionalidad nativa sea conveniente porque le da la opción de ejecutar las últimas pruebas de compilación incluso si su código está roto.

cuantico
fuente
1
No funciona con CDash. Tienes que llamar a make all && ctest y luego el edificio no es parte de la prueba cargada. Por lo tanto, las advertencias o errores de compilación no son visibles.
usr1234567
2
Además, no funciona bien si desea una compilación en paralelo, ya que los dos se ejecutarán en paralelo: lo necesita make -j4 all && make test. Y también es inestable si se usa una herramienta de compilación que no es Make.
Poolie
4

Si está usando CMake> = 3.7, entonces el enfoque recomendado es usar accesorios :

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)

Esto hace lo siguiente:

  • Agrega un testdestino ejecutable construido a partir detest.cpp
  • Agrega una test_build"prueba" que ejecuta Cmake para construir el objetivotest
  • Marca la test_buildprueba como una tarea de configuración del accesoriotest_fixture
  • Agregue una testprueba que solo ejecute el testejecutable
  • Marca la testprueba para necesitar un accesorio test_fixture.

Por lo tanto, cada vez que se testva a ejecutar una prueba test_build, primero se ejecuta la prueba , que genera el ejecutable necesario.

John Freeman
fuente
Si $<CONFIG>no se establece, --targetse convertirá en el argumento de --config.
loshad vtapkah
Creo que $<CONFIG>siempre no está vacío. Es una expresión generadora para el nombre de configuración: cmake.org/cmake/help/latest/manual/… De todos modos editaré la respuesta para envolverla entre comillas solo porque no hace ninguna diferencia.
John Freeman
¿Cómo se ejecuta cmake? Hago de esta manera: mkdir build; cd build; cmake ..; make. Y parece que no hay valores predeterminados y todas las variables relacionadas están vacías, hasta que CMAKE_BUILD_TYPEse configura manualmente. (actualmente en Debian 10, no verifiqué otras plataformas)
loshad vtapkah
1

Esto es lo que resolví y he estado usando:

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
fuente
0

La respuesta de Derrick, simplificada y comentada:

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

No es perfectamente correcto, ya que no resuelve el problema de concurrencia de ejecución ninja all test, en caso de que alguien lo haga. Al contrario, porque ahora tienes dos procesos ninja.

(Ftr, también compartí esta solución aquí ).

user2394284
fuente
-3

Todas las respuestas anteriores son perfectas. Pero en realidad CMake usa CTest como sus herramientas de prueba, por lo que el método estándar (creo que lo es) para hacer la misión es:

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

Luego ejecute cmake y make para construir los objetivos. Después de eso, puede ejecutar make test o simplemente ejecutar

ctest

obtendrás el resultado. Esto se prueba en CMake 2.8.

Consulte los detalles en: http://cmake.org/Wiki/CMake/Testing_With_CTest#Simple_Testing

holmescn
fuente
5
Se votó en contra porque a veces solo desea crear los objetivos necesarios para las pruebas que se están ejecutando.
Dave Abrahams
12
Esta respuesta parece no entender la pregunta: ¿La OP ya está haciendo exactamente como se recomienda esta respuesta: el uso de CTest, enable_testing(), add_test(), etc. El problema es que tiene que emitir manualmente el comando de construcción antes de ejecutar las pruebas. Quiere que el make testobjetivo cree automáticamente los ejecutables de prueba según sea necesario.
bames53
-4

Todas las respuestas son buenas, pero implican una violación de la tradición de ejecutar una prueba por comando make test. He hecho este truco:

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

Esto significa que la prueba consiste en construir (opcionalmente) y ejecutar un destino ejecutable.

dyomas
fuente
6
:-D Regla # 1: No use el sistema sin sh. ¿Conoce tal sistema?
Dyomas
11
Sí, Windows es uno de ellos.
David Faure
3
Esto también está codificado makey pierde la función de CMake de generar scripts para otras herramientas de compilación.
Poolie