web-dev-qa-db-fra.com

CMake: Structure de projet avec tests unitaires

J'essaie de structurer mon projet pour inclure les sources de production (dans le sous-dossier src) et les tests (dans le sous-dossier test). J'utilise CMake pour construire cela. Comme exemple minimal, j'ai les fichiers suivants:

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 - utilise sqr, cela n'a pas d'importance

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));
}

Quelques questions:

  1. Cette structure a-t-elle un sens? Quelles sont les meilleures pratiques pour structurer ce code? (Je viens de C # et Java, et là c'est plus facile dans un sens)
  2. Je n'aime pas le fait que je doive répertorier tous les fichiers du dossier src du fichier test/CMakeLists.txt. S'il s'agissait d'un projet de bibliothèque, je lierais simplement la bibliothèque. Y a-t-il un moyen d'éviter de lister tous les fichiers cpp de l'autre projet?
  3. Que font les lignes enable_testing() et add_test(MyTest test)? Je n'ai vu aucun effet. Comment puis-je exécuter les tests à partir de CMake (ou CTest)?
  4. Jusqu'ici, j'ai juste lancé cmake . Dans le dossier racine, mais cela a créé un désordre avec des fichiers temporaires partout. Comment puis-je obtenir les résultats de la compilation dans une structure raisonnable?
123
Grzenio

Pour les questions 1 et 2, je vous recommanderais de créer une bibliothèque à partir de vos fichiers non-test en excluant le fichier main.cpp (dans ce cas, simplement src/sqr.cpp et src/sqr.h), afin d'éviter toute liste (et plus important encore). recompiler) toutes les sources deux fois.

Pour la question 3, ces commandes ajoutent un test appelé "MyTest" qui appelle votre "test" exécutable sans aucun argument. Cependant, puisque vous avez ajouté ces commandes pour tester/CMakeLists.txt et non votre CMakeLists.txt de niveau supérieur, vous ne pouvez appeler le test que dans le sous-répertoire "test" de votre arborescence de génération (try cd test && ctest -N). Si vous souhaitez que le test puisse être exécuté à partir de votre répertoire de construction de niveau supérieur, vous devez appeler add_test du fichier CMakeLists.txt de niveau supérieur. Cela signifie également que vous devez utiliser la forme la plus verbeuse de add_test puisque votre test exe n'est pas défini dans le même fichier CMakeLists.txt

Dans votre cas, puisque vous exécutez cmake dans le dossier racine, votre arborescence de construction et votre arborescence source sont identiques. Ceci est connu comme une construction dans la source et n'est pas idéal, ce qui conduit à la question 4.

La méthode privilégiée pour générer l’arbre de construction consiste à créer un répertoire hors source, c’est-à-dire créer un répertoire quelque part en dehors de votre arborescence source et exécuter cmake à partir de cet emplacement. Même en créant un répertoire "build" à la racine de votre projet et en exécutant cmake .. fournirait une structure propre qui n’interférerait pas avec votre arbre source.

Un dernier point est d'éviter d'appeler les exécutables "test" (sensible à la casse). Pour des raisons, voir cette réponse .

Pour réaliser ces changements, je procéderais comme suit:

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}
                       )
116
Fraser

J'aime l'exemple de @Fraser mais j'utiliserais la commande add_test dans le test/CMakeLists.txt et utiliser enable_testing avant add_subdirectory (test).

De cette façon, vous pouvez exécuter vos tests à partir du répertoire de construction de niveau supérieur tout en spécifiant vos tests dans le fichier test/CMakeLists.txt.

Le résultat ressemblerait à ceci (j'ai réutilisé l'exemple de @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)
45
Mathias