CMake: estructura del proyecto con pruebas unitarias

139

Estoy tratando de estructurar mi proyecto para incluir las fuentes de producción (en la srcsubcarpeta) y las pruebas (en la testsubcarpeta). Estoy usando CMake para construir esto. Como ejemplo mínimo, tengo los siguientes archivos:

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 no importa

prueba / 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)

prueba / prueba.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));
}

Unas cuantas preguntas:

  1. ¿Tiene sentido esta estructura? ¿Cuáles son las mejores prácticas al estructurar este código? (Vengo de C # y Java, y allí es más fácil en cierto sentido)
  2. No me gusta el hecho de que tengo que enumerar todos los archivos de la srccarpeta en eltest/CMakeLists.txt archivo. Si este fuera un proyecto de biblioteca, simplemente vincularía la biblioteca. ¿Hay alguna manera de evitar enumerar todos los archivos cpp del otro proyecto?
  3. ¿Cuáles son las líneas enable_testing()yadd_test(MyTest test) hacer? No he visto ningún efecto. ¿Cómo puedo ejecutar las pruebas de CMake (o CTest)?
  4. Hasta ahora solo corrí cmake .en la carpeta raíz, pero esto creó un desastre con archivos temporales en todas partes. ¿Cómo puedo obtener los resultados de la compilación en una estructura razonable?
Grzenio
fuente
Me considero un novato de CMake, así que no sé cuáles son las mejores prácticas aceptadas, pero FWIW haría una biblioteca "sqr" * de la que dependían tanto la prueba principal como la principal. (* o su equivalente moral)
usuario786653

Respuestas:

125

Para las preguntas 1 y 2, recomendaría hacer una biblioteca a partir de sus archivos que no sean de prueba, excluyendo main.cpp (en este caso solo src / sqr.cpp y src / sqr.h), y luego puede evitar el listado (y lo más importante volver a compilar) todas las fuentes dos veces.

Para la pregunta 3, estos comandos agregan una prueba llamada "MyTest" que invoca su "prueba" ejecutable sin ningún argumento. Sin embargo, como ha agregado estos comandos a test / CMakeLists.txt y no a su CMakeLists.txt de nivel superior, solo puede invocar la prueba desde el subdirectorio "test" de su árbol de compilación (try cd test && ctest -N). Si desea que la prueba se pueda ejecutar desde su directorio de compilación de nivel superior, deberá llamar add_testdesde CMakeLists.txt de nivel superior. Esto también significa que debe usar la forma más detallada add_testya que su exe de prueba no está definido en el mismo CMakeLists.txt

En su caso, dado que está ejecutando cmake en la carpeta raíz, su árbol de compilación y su árbol de origen son uno y el mismo. Esto se conoce como una compilación en la fuente y no es ideal, lo que lleva a la pregunta 4.

El método preferido para generar el árbol de compilación es hacer una compilación fuera de la fuente, es decir, crear un directorio en algún lugar fuera de su árbol de fuente y ejecutar cmake desde allí. Incluso crear un directorio de "compilación" en la raíz de su proyecto y ejecutarlo cmake ..proporcionaría una estructura limpia que no interferirá con su árbol de origen.

Un último punto es evitar llamar a los ejecutables "prueba" (distingue entre mayúsculas y minúsculas). Por razones, vea esta respuesta .

Para lograr estos cambios, haría lo siguiente:

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)


prueba / 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
fuente
2
Acabo de notar que también agregó los archivos .h a los archivos CMakeLists.txt en consecuencia. ¿Se requiere? ¿Y qué pasaría si los dejo fuera?
Grzenio
3
@Grzenio Esta es solo una característica de conveniencia: aparecen en IDE como MSVC como parte del objetivo, pero por lo demás esto no tiene ningún efecto.
Fraser
1
¿Dónde se establece TEST_SOURCE_DIR?
aggsol
66
CMake lo configura automáticamente cuando llama project (TEST); consulte cmake.org/cmake/help/v3.6/variable/PROJECT-NAME_SOURCE_DIR.html
Fraser el
> aparecen en IDE como MSVC como parte del objetivo --- ¿MSVC no admite marcar directorios con encabezados como "incluir directorio"?
isnullxbh
46

Me gusta el ejemplo de @Fraser pero usaría el comando add_test en test / CMakeLists.txt y usar enable_testing antes de add_subdirectory (test).

De esta manera, puede ejecutar sus pruebas desde el directorio de compilación de nivel superior mientras especifica sus pruebas en test / CMakeLists.txt.

El resultado se vería así (reutilicé el ejemplo 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)

prueba / 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
fuente
1
Gracias, no mostré ninguna prueba a través de ctest -Nhasta su consejo sobre habilitar la prueba antes de agregar el subdirectorio.
alaferg
@alaferg: de lo contrario terminarían en el subdirectorio de prueba dentro del directorio de compilación.
Gauteh
44
Desearía que CMake tuviera algo parecido a la estructura.
ruipacheco