strano comportamento di cmake ctest per CTEST_PARALLEL_LEVEL più grandi
Sono nuovo in SO. Ho un semplice codice di unit test in cui sto facendo le seguenti operazioni:
- calcolare la radice quadrata del numero utilizzando la
mysqrt
libreria. - Utilizzando l'output della radice quadrata, aggiungendo questo risultato con lo stesso numero e visualizzare il risultato.
Quando eseguo il codice con CTEST_PARALLEL_LEVEL = 1
tutti i miei casi di test vengono superati.
Ma quando lo faccio, i CTEST_PARALLEL_LEVEL = 8
miei casi di test falliscono un po 'di tempo per alcuni input che non vengono risolti in ogni esecuzione.
Il 99% di TUTTI i risultati sta passando ma l'1% non riesce.
Errore:
mysqrt.o: file not recognized: File truncated
Ho eliminato il file oggetto in modo esplicito utilizzando rm * .o, ma questo errore continua a verificarsi dopo poche esecuzioni.
Non sono sicuro del motivo per cui questo errore sta arrivando CTEST_PARALLEL_LEVEL = 8
Allego il mio CMakeList
unico perché alcuni esperti di Stack Overflow possono comprendere il problema controllando questi 3 CMakeLists.txt
file.
NOTA: Secondo le linee guida di Stack overflow, non allego il mio codice sorgente di sqrt e la funzione di addizione per evitare la maggiore lunghezza della domanda.
La mia struttura di cartelle:
SAMPLE_TEST
├── CMakeLists.txt
├── MathFunctions
│ ├── CMakeLists.txt
│ ├── MathFunctions.h
│ └── mysqrt.cpp
└── unit_test
├── CMakeLists.txt
└── step2
├── CMakeLists.txt
├── execute.cpp
└── tutorial.cpp
TEST DI ESEMPIO
CMakeLists.txt
cmake_minimum_required(VERSION 3.1)
project(Tutorial)
ENABLE_TESTING()
add_subdirectory(MathFunctions)
add_subdirectory(unit_test)
Cartella 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})
cartella 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()
cartella 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)
Risultato: 1a manche:
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)
2a manche:
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
Risposte
I tuoi test in esecuzione
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target ...
che viene eseguito in modo efficace make
(o qualsiasi strumento di compilazione utilizzato) nella directory di compilazione del progetto.
Ma non è mai garantito che le chiamate simultanee di make
nella stessa directory funzionino correttamente. Questo è il motivo per cui hai strani errori quando esegui test in parallelo (con la CTEST_PARALLEL_LEVEL
variabile impostata).
Ad esempio, tutti questi test stanno cercando di creare lo stesso file oggetto mysqrt.o
e questa creazione non è sicuramente thread-safe .
Correndo
make sqrtlib
prima
ctest
puoi essere certo che il file oggetto è già stato creato quando vengono eseguiti i test e i test non tenteranno di crearlo di nuovo. Ma potresti comunque ottenere altri conflitti nei test paralleli.
Dipende da ciò che si desidera effettivamente controllare con il test, ma di solito un test controlla il comportamento di qualche programma o libreria, e non intende controllare una compilazione (costruzione) di quel programma. Per questo motivo, i comandi di compilazione (costruzione) vengono eseguiti prima del test.
Di solito è conveniente seguire (implementare) questo flusso di lavoro per i test:
# Configure the project
cmake <source-directory>
# Build the project.
# It builds both program/library intended, and the tests themselves.
make
# run tests
ctest <params>
In tal caso un test potrebbe avere la seguente definizione:
add_test(NAME ctest_execute_${input} COMMAND ${exe_generator})
(A meno che tu non voglia controllare l'output del test in qualche modo automatico, non c'è bisogno di salvare esplicitamente questo output reindirizzando nel file. Da ctest
solo raccoglierebbe l'output del test, quindi puoi leggerlo se necessario).