CMake: estrutura do projeto com testes de unidade

139

Estou tentando estruturar meu projeto para incluir as fontes de produção (na srcsubpasta) e testes (na testsubpasta). Estou usando o CMake para criar isso. Como um exemplo mínimo, tenho os seguintes arquivos:

CMakeLists.txt:

cmake_minimum_required (VERSION 2.8) 
project (TEST) 

add_subdirectory (src) 
add_subdirectory (test) 

src / CMakeLists.txt:

add_executable (demo main.cpp sqr.cpp) 

src / sqr.h

#ifndef SQR_H
#define SQR_H
double sqr(double);    
#endif // SQR_H

src / sqr.cpp

#include "sqr.h"
double sqr(double x) { return x*x; }

src / main.cpp - usa sqr, realmente não importa

test / CMakeLists.txt:

find_package(Boost COMPONENTS system filesystem unit_test_framework REQUIRED)

include_directories (${TEST_SOURCE_DIR}/src) 

ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK) 

add_executable (test test.cpp ${TEST_SOURCE_DIR}/src/sqr.cpp) 

target_link_libraries(test
                      ${Boost_FILESYSTEM_LIBRARY}
                      ${Boost_SYSTEM_LIBRARY}
                      ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
                      )

enable_testing()
add_test(MyTest test)

test / test.cpp:

#define BOOST_TEST_MODULE SqrTests
#include <boost/test/unit_test.hpp>

#include "sqr.h"

BOOST_AUTO_TEST_CASE(FailTest)
{
    BOOST_CHECK_EQUAL(5, sqr(2));
}

BOOST_AUTO_TEST_CASE(PassTest)
{
    BOOST_CHECK_EQUAL(4, sqr(2));
}

Algumas questões:

  1. Essa estrutura faz sentido? Quais são as melhores práticas ao estruturar este código? (Eu estou vindo de c # e java, e aí é mais fácil em um sentido)
  2. Não gosto do fato de precisar listar todos os arquivos da srcpasta no test/CMakeLists.txtarquivo. Se este fosse um projeto de biblioteca, eu apenas vincularia a biblioteca. Existe uma maneira de evitar listar todos os arquivos cpp do outro projeto?
  3. Quais são as linhas enable_testing()e add_test(MyTest test)fazendo? Eu não vi nenhum efeito. Como posso executar os testes do CMake (ou CTest)?
  4. Até agora, eu apenas corri cmake .na pasta raiz, mas isso criou uma confusão com arquivos temporários em todos os lugares. Como posso obter os resultados da compilação em uma estrutura razoável?
Grzenio
fonte
Eu me considero um novato do CMake, por isso não sei quais são as melhores práticas aceitas, mas o FWIW criaria uma biblioteca "sqr" * da qual dependiam tanto o principal quanto o teste. (* Ou o seu equivalente moral)
user786653

Respostas:

125

Para as perguntas 1 e 2, eu recomendaria criar uma biblioteca a partir dos arquivos que não são de teste, excluindo main.cpp (neste caso, apenas src / sqr.cpp e src / sqr.h) e, em seguida, evite listar (e mais importante) recompilação) todas as fontes duas vezes.

Para a pergunta 3, esses comandos adicionam um teste chamado "MyTest", que chama seu "teste" executável sem argumentos. No entanto, desde que você adicionou esses comandos ao test / CMakeLists.txt e não ao seu CMakeLists.txt de nível superior, você pode invocar o teste apenas dentro do subdiretório "test" da sua árvore de construção (try cd test && ctest -N). Se você deseja que o teste possa ser executado a partir do diretório de compilação de nível superior, é necessário chamar a add_testpartir do CMakeLists.txt de nível superior. Isso também significa que você precisa usar a forma mais detalhada, add_testjá que o exe de teste não está definido no mesmo arquivo CMakeLists.txt

No seu caso, como você está executando o cmake na pasta raiz, sua árvore de construção e sua árvore de origem são uma e a mesma. Isso é conhecido como uma compilação na fonte e não é o ideal, o que leva à pergunta 4.

O método preferido para gerar a árvore de construção é fazer uma construção fora da fonte, ou seja, criar um diretório em algum lugar fora da árvore de origem e executar cmake a partir daí. Mesmo a criação de um diretório "build" na raiz do seu projeto e a execução cmake ..forneceriam uma estrutura limpa que não interferirá na sua árvore de origem.

Um ponto final é evitar chamar os executáveis ​​de "teste" (diferencia maiúsculas de minúsculas). Por razões, veja esta resposta .

Para conseguir essas alterações, eu faria o seguinte:

CMakeLists.txt:

cmake_minimum_required (VERSION 2.8)
project (TEST)
add_subdirectory (src) 
add_subdirectory (test)
enable_testing ()
add_test (NAME MyTest COMMAND Test)


src / CMakeLists.txt:

add_library (Sqr sqr.cpp sqr.h)
add_executable (demo main.cpp)
target_link_libraries (demo Sqr)


test / CMakeLists.txt:

find_package (Boost COMPONENTS system filesystem unit_test_framework REQUIRED)
include_directories (${TEST_SOURCE_DIR}/src
                     ${Boost_INCLUDE_DIRS}
                     )
add_definitions (-DBOOST_TEST_DYN_LINK)
add_executable (Test test.cpp)
target_link_libraries (Test
                       Sqr
                       ${Boost_FILESYSTEM_LIBRARY}
                       ${Boost_SYSTEM_LIBRARY}
                       ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
                       )
Fraser
fonte
2
Acabei de perceber que você também adicionou os arquivos .h aos arquivos CMakeLists.txt. É necessário? E o que aconteceria se eu os deixasse de fora?
Grzenio 22/01
3
@Grzenio Este é apenas um recurso de conveniência - eles aparecem nos IDEs como MSVC como parte do destino, mas, caso contrário, isso não tem efeito.
Fraser
1
Onde está definido o TEST_SOURCE_DIR?
aggsol
6
É definido automaticamente pelo CMake ao chamar project (TEST)- ver cmake.org/cmake/help/v3.6/variable/PROJECT-NAME_SOURCE_DIR.html
Fraser
> eles aparecem nos IDE como o MSVC como parte do destino --- O MSVC não suporta marcar o diretório com cabeçalhos como "incluir diretório"?
Isnullxbh 26/1018
46

Eu gosto do exemplo do @Fraser, mas usaria o comando add_test no test / CMakeLists.txt e use enable_testing antes do add_subdirectory (test).

Dessa forma, você pode executar seus testes no diretório de compilação de nível superior enquanto especifica seus testes em test / CMakeLists.txt.

O resultado ficaria assim (reutilizei o exemplo do @Fraser):

CMakeLists.txt

cmake_minimum_required (VERSION 2.8)
project (TEST)
add_subdirectory (src)

enable_testing ()
add_subdirectory (test)

src / CMakeLists.txt

add_library (Sqr sqr.cpp sqr.h)
add_executable (demo main.cpp)
target_link_libraries (demo Sqr)

test / CMakeLists.txt

find_package (Boost COMPONENTS system filesystem unit_test_framework REQUIRED)
include_directories (${TEST_SOURCE_DIR}/src
                     ${Boost_INCLUDE_DIRS}
                     )
add_definitions (-DBOOST_TEST_DYN_LINK)
add_executable (Test test.cpp)
target_link_libraries (Test
                       Sqr
                       ${Boost_FILESYSTEM_LIBRARY}
                       ${Boost_SYSTEM_LIBRARY}
                       ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
                       )
add_test (NAME MyTest COMMAND Test)
Mathias
fonte
1
Obrigado, não mostrei nenhum teste ctest -Naté você receber a dica sobre como ativar o teste antes de adicionar o subdir.
alaferg 20/05
@alaferg: eles acabariam no subdiretório de teste dentro do diretório de construção.
gauteh
4
Eu gostaria que o CMake tivesse algo que se parecesse com estrutura.
ruipacheco