cmake ctest对于较大的CTEST_PARALLEL_LEVEL的奇怪行为 [英] strange behavior of cmake ctest for bigger CTEST_PARALLEL_LEVEL
问题描述
我是SO新手。
我有一个简单的单元测试代码,在其中执行以下操作:
- 使用
mysqrt计算数字的平方根
库。 - 使用平方根的输出,将结果与相同的数字相加并显示结果。
当我使用 CTEST_PARALLEL_LEVEL = 1
运行代码时,我所有的测试用例都通过了。
但是当我执行 CTEST_PARALLEL_LEVEL = 8
,然后我的测试用例由于无法在每次运行中都不固定的某些输入而失败。
99%所有结果都通过了,但是失败了1%。
错误:
mysqrt.o:无法识别文件:文件被截断
我已经使用rm * .o明确删除了目标文件,但是仍然会在运行几次后出现此错误。
我不确定为什么此错误与 CTEST_PARALLEL_LEVEL = 8
我正在附加我的 CMakeList
,只有某些Stack Overflow专家可以通过检查这3个 CMakeLists.txt
文件来理解该问题。
注意:根据堆栈溢出准则,我不会附加源代码 sqrt 和 addition 函数,以避免问题的长度变长。
我的文件夹结构:
SAMPLE_TEST
├──CMakeLists.txt
├── MathFunctions
│├──CMakeLists.txt
│├──MathFunctions.h
│└──mysqrt.cpp
└──单元测试
├──CMakeLists .txt
└──step2
├──CMakeLists.txt
├──execute.cpp
└──tutorial.cpp
SAMPLE_TEST
CMakeLists.txt
cmake_minimum_required(版本3.1)
项目(教程)
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
评论生成sqrtlib)
ADD_LIBRARY(sqrtlib对象$ {REF_FILES})
unit_test文件夹
CMakeLists.txt
set(REF_MATHLIB_DIR $ {CMAKE_CURRENT_SOURCE_DIR} /../ MathFunctions)
宏(GENERATION文件输入)
设置(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}
注释为生成输出文件测试用例)
add_custom_target(gen_input _ $ {input}
DEPENDS $ {INPUT_FILE0}
COMMENT生成的输出文件)
endmacro()
###################
宏(执行文件输入)
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}
)
设置(INPUT_FILE0 ip0 _ $ {input} .y)
设置(EXE_FILE0 exeadd _ $ {input} .y)
设置(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}
注释为测试用例生成输出文件)
add_custom_target(gen_execute _ $ {input}
DEPENDS $ {EXE_FILE0}
注释生成的输出文件)
#添加测试以模拟
add_test(NAME ctest_execute _ $ {input}
COMMAND $ {CMAKE_COMMAND} --build $ {CMA KE_BINARY_DIR}
--target gen_execute _ $ {input})
#add_dependencies(execute _ $ {main_base_name_mangled}
#gen_input)
endmacro()
#++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++#
#添加测试目录
#++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++#
设置(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 81100121 144)
foreach(ip_integer $ {input_integer_range })
GENERATION($ {UT_IPGEN_FILES} $ {ip_integer})
EXECUTE($ {UT_EXECUTE_FILES} $ {ip_integer})
endforeach(ip_integer)
结果:
第一次运行:
开始1:ctest_execute_1
开始2:ctest_execute_4
开始3 :ctest_execute_9
开始4:ctest_execute_16
开始5:ctest_execute_25
开始6:ctest_execute_36
开始7:ctest_execute_49
开始8:ctest_execute_64
1/12测试#4:ctest_execute_16 ................. ***失败1.14秒
2/12测试#6:ctest_execute_36 ...... ......通过1.27秒
3/12测试#7:ctest_execute_49 .................通过1.32秒
4/12测试# 8:ctest_execute_64 .................通过了1.32秒
开始9:ctest_execute_81
开始10:ctest_execute_100
开始11:ctest_execute_121
开始12:ctest_execute_144
5/12测试#1:ctest_execute_1 ..................通过1.33秒
6/12测试#2:ctest_execute_4 .........通过了1.33秒
7/12测试#3:ctest_execute_9 .... ..............通过了1.33秒
8/12测试#5:ctest_execute_25 .................通过了1.33秒
9/12测试#10:ctest_execute_100 ................通过0.54秒
10/12测试#11:ctest_execute_121 ........ ........通过0.55秒
11/12测试#9:ctest_execute_81 .................通过0.55秒
12/12测试#12:ctest_execute_144 ................通过0.55秒
92%测试通过,12个测试中有1个测试失败
总测试时间(真实)= 1.88秒
下列测试失败:
4-ctest_execute_16(失败)
第二次运行:
开始1:ctest_execute_1
开始2:ctest_execute_4
开始3:ctest_execute_9
开始4:ctest_execute_16
开始5:ctest_execute_25
开始6:ctest_execute_36
开始7:ctest_execute_49
开始8:ctest_execute_64
1/12测试#6:ctest_execute_36。 ...... .....通过1.31秒
2/12测试#7:ctest_execute_49 .................通过1.36秒
3/12测试#8 :ctest_execute_64 .................通过1.36秒
开始9:ctest_execute_81
开始10:ctest_execute_100
开始11:ctest_execute_121
4/12测试#1:ctest_execute_1 ..................通过1.37秒
5/12测试#2:ctest_execute_4 ......... .........通过1.37秒
6/12测试#3:ctest_execute_9 ..................通过1.36秒
7 / 12测试#4:ctest_execute_16 .................通过1.36秒
8/12测试#5:ctest_execute_25 ...... ......通过1.37秒
开始12:ctest_execute_144
9/12测试#11:ctest_execute_121 ................通过0.50秒
10/12测试#10:ctest_execute_100 ................通过0.51秒
11/12测试#9:ctest_execute_81 ......... ........通过0.51秒
12/12测试#12:ctest_execute_144 ......... .......通过0.34秒
100%测试通过,12个测试中有0个失败
总测试时间(实际)= 2.01秒
您正在执行的测试
COMMAND $ {CMAKE_COMMAND} --build $ {CMAKE_BINARY_DIR} --target ...
可以有效地在项目的构建目录中运行 make
(或您使用的任何构建工具)。
但是并发调用在同一目录中的 make
绝对不能保证正常工作。这就是为什么在并行运行测试(设置 CTEST_PARALLEL_LEVEL
变量)时会出现奇怪的错误的原因。
例如所有这些测试都试图创建相同的目标文件 mysqrt.o
,并且此创建绝对不是线程安全的。
通过运行
使sqrtlib
之前
test
您可以确定在运行测试时已经创建了目标文件,并且测试不会尝试再次创建它。
但是在并行测试中您仍然可能会遇到其他冲突。
这取决于您实际上想通过测试检查什么,但是通常 test 会检查某些程序或库的行为,但它并不打算检查该程序的编译(构建)。因此,在测试之前执行命令。
通常,遵循(实现)该工作流程进行测试很方便:
#配置项目
cmake< source-directory>
#构建项目。
#它既可以构建计划的程序/库,也可以构建测试本身。
使
#运行测试
ctest< params>
在这种情况下,测试可能具有以下定义:
add_test(NAME ctest_execute _ $ {input}命令$ {exe_generator})
(除非您要 ctest
本身将收集测试的输出,因此您可以通过某种自动方式来检查测试的输出,而无需通过重定向到文件来显式保存此输出。如有需要,请阅读)。
I am new to the SO. I have a simple unit test code where I am doing following operations :
- calculating square root of the number using
mysqrt
library . - Using the output of square root, adding this result with same number and display the result.
When I am running the code with CTEST_PARALLEL_LEVEL = 1
my all test cases are passing.
But when I do CTEST_PARALLEL_LEVEL = 8
then my test cases are failing some time for some input which is not fixed in every run.
99% the ALL results are passing but 1% its failing.
Error:
mysqrt.o: file not recognized: File truncated
I have deleted the object file explicitly by using rm *.o ,But still this error is coming after few runs .
I am not sure why this error is coming with CTEST_PARALLEL_LEVEL = 8
I am attaching my CMakeList
only as some of Stack Overflow expert can understand the issue by checking these 3 CMakeLists.txt
files.
NOTE: As per the Stack overflow guidelines I am not attaching my source code of sqrt and addition function to avoid the bigger length of the question .
My folder structure:
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 folder
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 folder
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 folder
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)
Result: 1st Run:
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)
2nd Run:
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
Your tests executing
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target ...
which effectively runs make
(or whatever build tool you use) in the project's build directory.
But concurrent invocations of make
in the same directory are never guarantee to work correctly. This is why you got weird errors when run tests in parallel (with CTEST_PARALLEL_LEVEL
variable being set).
E.g. all these tests are trying to create the same object file mysqrt.o
, and this creation is definitely not thread-safe.
By running
make sqrtlib
before
ctest
you may be sure that the object file is already created when tests are run, and tests wouldn't attempt to create it again. But you still could get other conflicts in the parallel tests.
It depends on what actually you want to check by the testing, but usually a test checks behavior of some program or library, and it doesn't intend to check a compilation(building) of that program. Because of that, compilation(building) commands are performed before the testing.
Usually it is convenient to follow(implement) this workflow for testing:
# 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 that case a test could have the following definition:
add_test(NAME ctest_execute_${input} COMMAND ${exe_generator})
(Unless you want to check output of the test by some automatic way, no need to explicitely save this output by redirecting into the file. ctest
by itself would collect the output of the test, so you may read it if needed).
这篇关于cmake ctest对于较大的CTEST_PARALLEL_LEVEL的奇怪行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!