พฤติกรรมแปลก ๆ ของ cmake ctest สำหรับ CTEST_PARALLEL_LEVEL ที่ใหญ่กว่า

Aug 19 2020

ฉันยังใหม่กับ SO ฉันมีรหัสทดสอบหน่วยง่ายๆที่ฉันกำลังดำเนินการต่อไปนี้:

  1. คำนวณรากที่สองของจำนวนโดยใช้mysqrtไลบรารี
  2. ใช้เอาต์พุตของรากที่สองเพิ่มผลลัพธ์นี้ด้วยตัวเลขเดียวกันและแสดงผลลัพธ์

เมื่อฉันรันโค้ดด้วยCTEST_PARALLEL_LEVEL = 1กรณีทดสอบทั้งหมดของฉันกำลังผ่าน

แต่เมื่อฉันทำCTEST_PARALLEL_LEVEL = 8แล้วกรณีทดสอบของฉันล้มเหลวในบางครั้งสำหรับอินพุตบางอย่างซึ่งไม่ได้รับการแก้ไขในทุกครั้งที่รัน

99% ที่ผลลัพธ์ทั้งหมดผ่านไป แต่ 1% ล้มเหลว

ข้อผิดพลาด:

mysqrt.o: file not recognized: File truncated

ฉันได้ลบไฟล์ออบเจ็กต์อย่างชัดเจนโดยใช้ rm * .o แต่ข้อผิดพลาดนี้ยังคงเกิดขึ้นหลังจากการรันเพียงไม่กี่ครั้ง

ฉันไม่แน่ใจว่าเหตุใดจึงเกิดข้อผิดพลาดนี้ CTEST_PARALLEL_LEVEL = 8

ฉันกำลังแนบCMakeListคนเดียวของฉันเนื่องจากผู้เชี่ยวชาญ Stack Overflow บางคนสามารถเข้าใจปัญหาได้โดยการตรวจสอบCMakeLists.txtไฟล์3 ไฟล์นี้

หมายเหตุ: ตามแนวทาง Stack overflow ฉันไม่ได้แนบซอร์สโค้ดของsqrtและฟังก์ชันเพิ่มเติมเพื่อหลีกเลี่ยงความยาวของคำถามที่มากขึ้น

โครงสร้างโฟลเดอร์ของฉัน:

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)

โฟลเดอร์ 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()

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)

ผลลัพธ์: รอบที่ 1:

      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)

รอบที่ 2:

      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

คำตอบ

Tsyvarev Aug 20 2020 at 13:51

การทดสอบของคุณกำลังดำเนินการ

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

ซึ่งทำงานได้อย่างมีประสิทธิภาพmake(หรือเครื่องมือสร้างอะไรก็ตามที่คุณใช้) ในไดเร็กทอรีบิลด์ของโครงการ

แต่สวดพร้อมกันของmakeในไดเรกทอรีเดียวกันจะไม่รับประกันในการทำงานได้อย่างถูกต้อง นี่คือสาเหตุที่คุณได้รับข้อผิดพลาดแปลก ๆ เมื่อรันการทดสอบพร้อมกัน (โดยมีCTEST_PARALLEL_LEVELการตั้งค่าตัวแปร)

เช่นการทดสอบทั้งหมดนี้พยายามสร้างอ็อบเจ็กต์ไฟล์เดียวกันmysqrt.oและการสร้างนี้ไม่ปลอดภัยต่อเธรด

โดยการเรียกใช้

make sqrtlib

ก่อน

ctest

คุณอาจแน่ใจว่าไฟล์ออบเจ็กต์ถูกสร้างขึ้นแล้วเมื่อรันการทดสอบและการทดสอบจะไม่พยายามสร้างขึ้นอีก แต่คุณยังอาจได้รับความขัดแย้งอื่น ๆ ในการทดสอบคู่ขนาน


ขึ้นอยู่กับสิ่งที่คุณต้องการตรวจสอบโดยการทดสอบ แต่โดยปกติแล้วการทดสอบจะตรวจสอบพฤติกรรมของโปรแกรมหรือไลบรารีบางโปรแกรมและไม่ได้ตั้งใจที่จะตรวจสอบการคอมไพล์ (การสร้าง) ของโปรแกรมนั้น ด้วยเหตุนี้คำสั่งคอมไพล์ (การสร้าง) จึงถูกดำเนินการก่อนการทดสอบ

โดยปกติแล้วจะสะดวกในการปฏิบัติตาม (ใช้) เวิร์กโฟลว์นี้สำหรับการทดสอบ:

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

ในกรณีนั้นการทดสอบอาจมีคำจำกัดความดังต่อไปนี้:

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

(เว้นแต่คุณต้องการตรวจสอบผลลัพธ์ของการทดสอบโดยวิธีอัตโนมัติไม่จำเป็นต้องบันทึกผลลัพธ์นี้อย่างชัดเจนโดยการเปลี่ยนเส้นทางไปยังไฟล์ctestด้วยตัวมันเองจะรวบรวมผลลัพธ์ของการทดสอบดังนั้นคุณสามารถอ่านได้หากจำเป็น)