perilaku aneh cmake ctest untuk CTEST_PARALLEL_LEVEL yang lebih besar

Aug 19 2020

Saya baru mengenal SO. Saya memiliki kode pengujian unit sederhana tempat saya melakukan operasi berikut:

  1. menghitung akar kuadrat dari nomor tersebut menggunakan mysqrtperpustakaan.
  2. Menggunakan output dari akar kuadrat, menambahkan hasil ini dengan angka yang sama dan menampilkan hasilnya.

Ketika saya menjalankan kode dengan CTEST_PARALLEL_LEVEL = 1semua kasus pengujian saya lewat.

Tetapi ketika saya melakukannya CTEST_PARALLEL_LEVEL = 8maka kasus pengujian saya gagal beberapa waktu untuk beberapa input yang tidak diperbaiki di setiap proses.

99% hasil SEMUA lulus tetapi 1% gagal.

Kesalahan:

mysqrt.o: file not recognized: File truncated

Saya telah menghapus file objek secara eksplisit dengan menggunakan rm * .o, Tapi tetap saja kesalahan ini muncul setelah beberapa kali berjalan.

Saya tidak yakin mengapa kesalahan ini datang CTEST_PARALLEL_LEVEL = 8

Saya melampirkan CMakeListsatu - satunya karena beberapa pakar Stack Overflow dapat memahami masalah ini dengan memeriksa 3 CMakeLists.txtfile ini.

CATATAN: Sesuai dengan pedoman Stack overflow, saya tidak melampirkan kode sumber sqrt dan fungsi penambahan untuk menghindari panjang pertanyaan yang lebih besar.

Struktur folder saya:

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)

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

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()

folder 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)

Hasil: Lari Pertama:

      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)

Proses Kedua:

      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

Jawaban

Tsyvarev Aug 20 2020 at 13:51

Pengujian Anda sedang dijalankan

COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target ...

yang secara efektif berjalan make(atau alat build apa pun yang Anda gunakan) di direktori build proyek.

Tapi doa bersamaan dari makedalam direktori yang sama tidak pernah menjamin untuk bekerja dengan benar. Inilah sebabnya mengapa Anda mendapat kesalahan aneh saat menjalankan pengujian secara paralel (dengan CTEST_PARALLEL_LEVELvariabel yang disetel).

Misalnya, semua pengujian ini mencoba membuat file objek yang sama mysqrt.o, dan kreasi ini jelas tidak aman untuk thread .

Dengan berlari

make sqrtlib

sebelum

ctest

Anda mungkin yakin bahwa file objek sudah dibuat saat pengujian dijalankan, dan pengujian tidak akan mencoba membuatnya lagi. Tapi Anda masih bisa mendapatkan konflik lain dalam pengujian paralel.


Itu tergantung pada apa yang sebenarnya ingin Anda periksa dengan pengujian, tetapi biasanya tes memeriksa perilaku beberapa program atau pustaka, dan tidak bermaksud untuk memeriksa kompilasi (pembangunan) program itu. Karena itu, perintah kompilasi (membangun) dilakukan sebelum pengujian.

Biasanya lebih mudah untuk mengikuti (mengimplementasikan) alur kerja ini untuk pengujian:

# Configure the project
cmake <source-directory>
# Build the project.
# It builds both program/library intended, and the tests themselves.
make
# run tests
ctest <params>

Dalam hal ini, tes dapat memiliki definisi berikut:

add_test(NAME ctest_execute_${input} COMMAND ${exe_generator})

(Kecuali jika Anda ingin memeriksa keluaran pengujian dengan cara otomatis, tidak perlu menyimpan keluaran ini secara eksplisit dengan mengalihkan ke dalam file. ctestDengan sendirinya akan mengumpulkan keluaran dari pengujian, jadi Anda dapat membacanya jika perlu).