CMake & CTest: fazer teste não cria testes

88

Estou tentando o CTest no CMake para executar automaticamente alguns dos meus testes usando o make testalvo. O problema é que o CMake não "entende" que o teste que estou disposto a executar precisa ser construído, pois faz parte do projeto.

Portanto, estou procurando uma maneira de especificar explicitamente essa dependência.

claf
fonte

Respostas:

78

É indiscutivelmente um bug no CMake (previamente rastreado aqui ) que isso não funciona fora da caixa. Uma solução alternativa é fazer o seguinte:

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

Então você pode executar make checke ele irá compilar e executar o teste. Se você tiver vários testes, terá que usar DEPENDS exe1 exe2 exe3 ...a linha acima.

richq
fonte
1
então eu acho que o destino "make test" permanecerá sem uso, pois parece que você tem que escolher um nome de destino diferente no comando add_custom_target?
claf
Sim. A única diferença entre "make test" e "make check" é o primeiro mostra "Running tests ..." primeiro e não verifica nenhuma dependência de construção.
richq de
2
@rq - mas como posso fazer isso com vários projetos (quando um CMakeLists.txt é um subprojeto de outro) para que cada um defina o checkalvo e eles possam colidir
Artyom
2
@Artyom - nesse caso, é melhor você usar apenas o equivalente "fazer todos os testes". Na verdade, isso é o que eu faço de qualquer maneira.
richq
4
Na verdade, alguns consideram um recurso (não um bug) do cmake que você pode executar "make test" e apenas executar os testes como estão, sem fazer nenhuma reconstrução primeiro ...
DLRdave
53

Na verdade, existe uma maneira de usar make test. Você precisa definir a construção do executável de teste como um dos testes e, em seguida, adicionar dependências entre os testes. Isso é:

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
fonte
11
Este é o único que aumenta a escala e não força você a construir os alvos "make all" apenas para executar o teste. Uma possível desvantagem: detalhes de erros de compilação nos binários só aparecem no arquivo LastTest.log gerado e não em stdout / stderr
Dave Abrahams
2
Boa resposta! Você deve adicionar a configuração ao destino de construção. Caso contrário, não é possível executar os testes em todas as configurações. add_test (NAME "$ {ARGV0} _BUILD" COMMAND "$ {CMAKE_COMMAND}" --build $ {CMAKE_BINARY_DIR} --target $ {target} "--config" "$ <CONFIG>")
Daniel
1
Isso entope o repórter de teste com um monte de testes falsos.
Se você estiver usando CMake> = 3.7, a abordagem recomendada é usar acessórios. Veja minha resposta abaixo.
John Freeman
13

Eu uso uma variante da resposta de richq. No nível superior CMakeLists.txt, adiciono um destino personalizado,, build_and_testpara criar e executar todos os testes:

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

Nos vários CMakeLists.txtarquivos de subprojeto em test/, adiciono cada executável de teste como uma dependência 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)

Com essa abordagem, eu só preciso em make build_and_testvez de make test(ou make all test), e tem a vantagem de apenas construir o código de teste (e suas dependências). É uma pena que não posso usar o nome do alvo test. No meu caso, não é tão ruim porque tenho um script de nível superior que faz a depuração fora da árvore e libera (e compila compilações cruzadas) chamando cmakee então make, e isso se traduz testem build_and_test.

Obviamente, o material GTest não é necessário. Acontece que eu uso / gosto do Google Test e queria compartilhar um exemplo completo de como usá-lo com o CMake / CTest. IMHO, essa abordagem também tem o benefício de me permitir usar ctest -V, que mostra a saída do Google Test enquanto os testes são executados:

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
fonte
Neste exemplo, há uma maneira de fazer o teste fazer o que ctest -V faz em vez de ctest? A saída do ctest parece muito incompleta e apenas diz que há um único teste.
Rajiv,
6

Se você está tentando emular make check, pode achar esta entrada do wiki útil:

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

Acabei de verificar se ele faz o que diz com sucesso (CMake 2.8.10).

Samuel
fonte
1
Isso criará todos os executáveis ​​durante a execução make check. Para testes com tempos de compilação dominantes, isso torna ctest -Rinútil.
usr1234567
4

Evite a dor de cabeça:

make all test

Funciona imediatamente para mim e cria dependências antes de executar o teste. Dado o quão simples isso é, isso quase torna a make testfuncionalidade nativa conveniente porque dá a você a opção de executar os últimos testes de compilação, mesmo se seu código estiver corrompido.

quant
fonte
1
Não funciona com CDash. Você tem que chamar make all && ctest e então o edifício não faz parte do teste carregado. Portanto, os avisos ou erros de construção não são visíveis.
usr1234567
2
Também não funciona bem se você quiser uma construção paralela, já que as duas rodarão em paralelo: você precisa make -j4 all && make test. E também fica difícil usar uma ferramenta de compilação não-Make.
poolie de
4

Se você estiver usando CMake> = 3.7, a abordagem recomendada é usar acessórios :

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)

Isso faz o seguinte:

  • Adiciona um testalvo executável criado a partir detest.cpp
  • Adiciona um test_build"teste" que executa o Cmake para construir o destinotest
  • Marca o test_buildteste como uma tarefa de configuração de fixaçãotest_fixture
  • Adicione um testteste que apenas execute o testexecutável
  • Marca o testteste para precisar de fixação test_fixture.

Portanto, toda vez que o teste testdeve ser executado, ele primeiro executa o teste test_build, que cria o executável necessário.

John Freeman
fonte
Se $<CONFIG>não for definido, o --targetse tornará o argumento para o --config.
loshad vtapkah
Eu acredito que $<CONFIG>sempre não está vazio. É uma expressão geradora para o nome da configuração: cmake.org/cmake/help/latest/manual/… Vou editar a resposta para colocá-la entre aspas de qualquer maneira, porque não faz diferença.
John Freeman
Como você corre cmake? Eu faço desta forma: mkdir build; cd build; cmake ..; make. E parece que não há nenhum padrão e todas as variáveis ​​relacionadas estão vazias, até que CMAKE_BUILD_TYPEseja definido manualmente. (atualmente no Debian 10, não verifiquei outras plataformas)
loshad vtapkah
1

Isso é o que eu martelei e tenho usado:

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

Torre
fonte
0

Resposta de Derrick, simplificada e 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"
)

Não é perfeitamente correto, pois não resolve o problema de concorrência de execução ninja all test, caso alguém o faça. Pelo contrário, porque agora, você tem dois processos ninja.

A solução final, se você me perguntar, é usar Meson em vez de CMake. Meson acertou, entre muitas outras coisas.

user2394284
fonte
-3

Todas as respostas acima são perfeitas. Mas, na verdade, o CMake usa o CTest como suas ferramentas de teste, então o método padrão (eu acho que é) para fazer a missão é:

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

Em seguida, execute cmake e make para construir os alvos. Depois disso, você pode executar make test ou apenas executar

ctest

você obterá o resultado. Isso é testado no CMake 2.8.

Verifique os detalhes em: http://cmake.org/Wiki/CMake/Testing_With_CTest#Simple_Testing

Holmescn
fonte
5
Votos negados porque às vezes você deseja apenas construir os alvos necessários para os testes que estão realmente sendo executados.
Dave Abrahams
12
Esta resposta parece não entender a pergunta: O OP já está fazendo exatamente como esta resposta recomenda: Usando CTest, enable_testing(), add_test(), etc. O problema é que ele tem que emitir manualmente o comando de compilação antes de executar testes. Ele deseja que o make testdestino crie automaticamente os executáveis ​​de teste conforme necessário.
bames53
-4

Todas as respostas são boas, mas implicam em uma violação da tradição de executar um teste por comando make test. Eu fiz este truque:

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

Isso significa que o teste consiste na construção (opcional) e na execução do destino executável.

diomas
fonte
6
:-D Regra # 1: Não use o sistema sem sh. Você conhece esse sistema?
dyomas
11
Sim, o Windows é um deles.
David Faure
3
Isso também é codificado para makee perde o recurso do CMake de geração de scripts para outras ferramentas de construção.
poolie de