comportamiento extraño de cmake ctest para CTEST_PARALLEL_LEVEL más grande

Aug 19 2020

Soy nuevo en el SO. Tengo un código de prueba unitario simple en el que estoy realizando las siguientes operaciones:

  1. calcular la raíz cuadrada del número usando la mysqrtbiblioteca.
  2. 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 = 1todos 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.txtarchivos.

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

Tsyvarev Aug 20 2020 at 13:51

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 makeen 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_LEVELvariable 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. ctestPor sí misma, recopilaría la salida de la prueba, por lo que puede leerla si es necesario).