더 큰 CTEST_PARALLEL_LEVEL에 대한 cmake ctest의 이상한 동작

Aug 19 2020

나는 SO에 처음입니다. 다음 작업을 수행하는 간단한 단위 테스트 코드가 있습니다.

  1. mysqrtlibrary를 사용하여 숫자의 제곱근을 계산 합니다.
  2. 제곱근의 출력을 사용하여이 결과를 같은 수로 더하고 결과를 표시합니다.

CTEST_PARALLEL_LEVEL = 1내 모든 테스트 케이스가 통과 하는 코드를 실행할 때 .

그러나 내가 할 CTEST_PARALLEL_LEVEL = 8때 내 테스트 케이스는 모든 실행에서 수정되지 않는 일부 입력에 대해 얼마 동안 실패합니다.

99 % ALL 결과는 통과했지만 1 %는 실패했습니다.

오류:

mysqrt.o: file not recognized: File truncated

rm * .o를 사용하여 명시 적으로 개체 파일을 삭제했지만 몇 번 실행 한 후에도 여전히이 오류가 발생합니다.

이 오류가 왜 발생하는지 잘 모르겠습니다. CTEST_PARALLEL_LEVEL = 8

CMakeListStack Overflow 전문가 중 일부가이 3 개의 CMakeLists.txt파일 을 확인하여 문제를 이해할 수 있기 때문에 저의 유일한 파일을 첨부하고 있습니다.

참고 : 스택 오버플로 지침 에 따라 질문의 더 큰 길이를 피하기 위해 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자체적으로 테스트의 출력을 수집하므로 필요한 경우 읽을 수 있습니다).