comportamiento extraño de cmake ctest para CTEST_PARALLEL_LEVEL más grande
Soy nuevo en el SO. Tengo un código de prueba unitario simple en el que estoy realizando las siguientes operaciones:
- calcular la raíz cuadrada del número usando la
mysqrt
biblioteca. - Usando la salida de la raíz cuadrada, agregue este resultado con el mismo número y muestre el resultado.
Cuando estoy ejecutando el código con CTEST_PARALLEL_LEVEL = 1
todos mis casos de prueba están pasando.
Pero cuando lo hago CTEST_PARALLEL_LEVEL = 8
, mis casos de prueba fallan algún tiempo para alguna entrada que no se corrige en cada ejecución.
El 99% de los resultados de TODOS están pasando, pero el 1% está fallando.
Error:
mysqrt.o: file not recognized: File truncated
He eliminado el archivo de objeto explícitamente usando rm * .o, pero aún así, este error se produce después de algunas ejecuciones.
No estoy seguro de por qué este error viene con CTEST_PARALLEL_LEVEL = 8
Adjunto mi CMakeList
único, ya que algunos de los expertos de Stack Overflow pueden comprender el problema al verificar estos 3 CMakeLists.txt
archivos.
NOTA: De acuerdo con las pautas de desbordamiento de Stack, no adjunto mi código fuente de sqrt y la función de adición para evitar la mayor longitud de la pregunta.
Mi estructura de carpetas:
SAMPLE_TEST
├── CMakeLists.txt
├── MathFunctions
│ ├── CMakeLists.txt
│ ├── MathFunctions.h
│ └── mysqrt.cpp
└── unit_test
├── CMakeLists.txt
└── step2
├── CMakeLists.txt
├── execute.cpp
└── tutorial.cpp
SAMPLE_TEST
CMakeLists.txt
cmake_minimum_required(VERSION 3.1)
project(Tutorial)
ENABLE_TESTING()
add_subdirectory(MathFunctions)
add_subdirectory(unit_test)
Carpeta MathFunctions
CMakeLists.txt
add_library(MathFunctions mysqrt.cpp)
set(REF_FILES mysqrt.cpp)
add_definitions(-Wall -Wextra -pedantic -std=c++11)
add_custom_target(build_reference_library
DEPENDS sqrtlib
COMMENT "Generating sqrtlib")
ADD_LIBRARY(sqrtlib OBJECT ${REF_FILES})
carpeta unit_test
CMakeLists.txt
set(REF_MATHLIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../MathFunctions)
macro(GENERATION file input)
set(ip_generator ctest_input_${input}) add_executable(${ip_generator}
${file} $<TARGET_OBJECTS:sqrtlib>
)
target_compile_options(${ip_generator} PUBLIC -Wall -Wextra -g -std=c++11 -DCTEST_INPUT=${input})
target_link_libraries(${ip_generator} PUBLIC dl pthread ) target_include_directories(${ip_generator} PUBLIC
${REF_MATHLIB_DIR} ) set(INPUT_FILE0 ip0_${input}.y)
set(INPUT_FILE0_TXT ip0_${input}.txt) add_custom_command( OUTPUT ${INPUT_FILE0} ${INPUT_FILE0_TXT} COMMAND ${ip_generator} > ${INPUT_FILE0_TXT} MAIN_DEPENDENCY ${sqrtlib}
COMMENT "Generating output files of for testcase")
add_custom_target(gen_input_${input} DEPENDS ${INPUT_FILE0}
COMMENT "Generated output files")
endmacro()
####################
macro(EXECUTE file input)
get_filename_component(main_base_name ${file} NAME_WE) set(main_base_name_mangled ${main_base_name}_${input}) set(exe_generator ctest_ref_${input})
add_executable(${exe_generator} ${file}
$<TARGET_OBJECTS:sqrtlib> ) target_compile_options(${exe_generator} PUBLIC
-Wall -Wextra -g -std=c++11
-DCTEST_INPUT=${input}) target_link_libraries(${exe_generator} PUBLIC
dl pthread
)
target_include_directories(${exe_generator} PUBLIC ${REF_MATHLIB_DIR}
)
set(INPUT_FILE0 ip0_${input}.y) set(EXE_FILE0 exeadd_${input}.y)
set(EXE_FILE_TXT exeadd_${input}.txt) add_custom_command( OUTPUT ${EXE_FILE0} ${EXE_FILE_TXT} COMMAND ${exe_generator} > ${EXE_FILE_TXT} MAIN_DEPENDENCY ${INPUT_FILE0} ${sqrtlib} COMMENT "Generating output files of for testcase") add_custom_target(gen_execute_${input}
DEPENDS ${EXE_FILE0} COMMENT "Generated output files") # add test to simulate add_test(NAME ctest_execute_${input}
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}
--target gen_execute_${input}) #add_dependencies(execute_${main_base_name_mangled}
#gen_input)
endmacro()
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
# add test directories
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
set(TEST_DIRECTORIES
step2
)
foreach(dir ${TEST_DIRECTORIES}) add_subdirectory(${dir})
endforeach()
carpeta step2
CMakeLists.txt
set(UT_IPGEN_FILES tutorial.cpp)
set(UT_EXECUTE_FILES execute.cpp)
set(input_integer_range 1 4 9 16 25 36 49 64 81 100 121 144 )
foreach(ip_integer ${input_integer_range}) GENERATION(${UT_IPGEN_FILES} ${ip_integer}) EXECUTE(${UT_EXECUTE_FILES} ${ip_integer})
endforeach(ip_integer)
Resultado: 1ra carrera:
Start 1: ctest_execute_1
Start 2: ctest_execute_4
Start 3: ctest_execute_9
Start 4: ctest_execute_16
Start 5: ctest_execute_25
Start 6: ctest_execute_36
Start 7: ctest_execute_49
Start 8: ctest_execute_64
1/12 Test #4: ctest_execute_16 .................***Failed 1.14 sec
2/12 Test #6: ctest_execute_36 ................. Passed 1.27 sec
3/12 Test #7: ctest_execute_49 ................. Passed 1.32 sec
4/12 Test #8: ctest_execute_64 ................. Passed 1.32 sec
Start 9: ctest_execute_81
Start 10: ctest_execute_100
Start 11: ctest_execute_121
Start 12: ctest_execute_144
5/12 Test #1: ctest_execute_1 .................. Passed 1.33 sec
6/12 Test #2: ctest_execute_4 .................. Passed 1.33 sec
7/12 Test #3: ctest_execute_9 .................. Passed 1.33 sec
8/12 Test #5: ctest_execute_25 ................. Passed 1.33 sec
9/12 Test #10: ctest_execute_100 ................ Passed 0.54 sec
10/12 Test #11: ctest_execute_121 ................ Passed 0.55 sec
11/12 Test #9: ctest_execute_81 ................. Passed 0.55 sec
12/12 Test #12: ctest_execute_144 ................ Passed 0.55 sec
92% tests passed, 1 tests failed out of 12
Total Test time (real) = 1.88 sec
The following tests FAILED:
4 - ctest_execute_16 (Failed)
2da carrera:
Start 1: ctest_execute_1
Start 2: ctest_execute_4
Start 3: ctest_execute_9
Start 4: ctest_execute_16
Start 5: ctest_execute_25
Start 6: ctest_execute_36
Start 7: ctest_execute_49
Start 8: ctest_execute_64
1/12 Test #6: ctest_execute_36 ................. Passed 1.31 sec
2/12 Test #7: ctest_execute_49 ................. Passed 1.36 sec
3/12 Test #8: ctest_execute_64 ................. Passed 1.36 sec
Start 9: ctest_execute_81
Start 10: ctest_execute_100
Start 11: ctest_execute_121
4/12 Test #1: ctest_execute_1 .................. Passed 1.37 sec
5/12 Test #2: ctest_execute_4 .................. Passed 1.37 sec
6/12 Test #3: ctest_execute_9 .................. Passed 1.36 sec
7/12 Test #4: ctest_execute_16 ................. Passed 1.36 sec
8/12 Test #5: ctest_execute_25 ................. Passed 1.37 sec
Start 12: ctest_execute_144
9/12 Test #11: ctest_execute_121 ................ Passed 0.50 sec
10/12 Test #10: ctest_execute_100 ................ Passed 0.51 sec
11/12 Test #9: ctest_execute_81 ................. Passed 0.51 sec
12/12 Test #12: ctest_execute_144 ................ Passed 0.34 sec
100% tests passed, 0 tests failed out of 12
Total Test time (real) = 2.01 sec
Respuestas
Tus pruebas ejecutándose
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target ...
que se ejecuta de manera efectiva make
(o cualquier herramienta de compilación que use) en el directorio de compilación del proyecto.
Pero las invocaciones simultáneas de make
en el mismo directorio nunca garantizan que funcionen correctamente. Es por eso que tiene errores extraños cuando ejecuta pruebas en paralelo (con la CTEST_PARALLEL_LEVEL
variable configurada).
Por ejemplo, todas estas pruebas intentan crear el mismo archivo de objeto mysqrt.o
, y esta creación definitivamente no es segura para subprocesos .
Mediante la ejecución
make sqrtlib
antes de
ctest
puede estar seguro de que el archivo objeto ya está creado cuando se ejecutan las pruebas, y las pruebas no intentarán crearlo de nuevo. Pero aún podría tener otros conflictos en las pruebas paralelas.
Depende de lo que realmente desee comprobar mediante la prueba, pero normalmente una prueba comprueba el comportamiento de algún programa o biblioteca, y no pretende comprobar una compilación (construcción) de ese programa. Por eso, los comandos de compilación (construcción) se ejecutan antes de la prueba.
Por lo general, es conveniente seguir (implementar) este flujo de trabajo para las pruebas:
# Configure the project
cmake <source-directory>
# Build the project.
# It builds both program/library intended, and the tests themselves.
make
# run tests
ctest <params>
En ese caso, una prueba podría tener la siguiente definición:
add_test(NAME ctest_execute_${input} COMMAND ${exe_generator})
(A menos que desee verificar la salida de la prueba de alguna manera automática, no es necesario guardar explícitamente esta salida redirigiéndola al archivo. ctest
Por sí misma, recopilaría la salida de la prueba, por lo que puede leerla si es necesario).