问题描述
我已经围绕标准CMake命令编写了一些便利包装,并希望对该CMake脚本代码进行单元测试以确保其功能.
I've written some convenience wrappers around standard CMake commands and want to unit-test this CMake script code to ensure its functionality.
我已经取得了一些进步,但是我希望有两件事可以帮助您:
I've made some progress, but there are two things I hope to get help with:
- 是否有某种官方"方式对您自己的CMake脚本代码进行单元测试?像某种特殊模式可以运行CMake吗?我的目标是白盒测试"(尽可能).
- 如何处理全局变量和变量范围问题?通过加载项目的缓存,配置测试CMake文件或通过-D命令行选项推送它,将Global变量注入到测试中?模拟/测试可变范围(缓存与非缓存,宏/函数/包含,引用传递的参数)?
首先,我在/Tests尤其是Tests/CMakeTests下研究了CMake源代码(我使用的是CMake版本2.8.10).可以找到大量的变体,并且看起来其中很多是专门针对单个测试用例的.
To start with I've looked into the CMake source code (I'm using CMake version 2.8.10) under /Tests and especially under Tests/CMakeTests. There is a huge number of varieties to be found and it looks like a lot of them are specialized on a single test case.
因此,我还研究了一些可用的CMake脚本库,例如 CMake ++ ,以查看其解决方案,但是-当他们有单元测试时-很大程度上取决于他们自己的库函数.
So I looked also into some available CMake script libraries like CMake++ to see their solution, but those - when they have unit tests - are heavily depending on their own library functions.
推荐答案
这是我当前用于对自己的CMake脚本代码进行单元测试的解决方案.
Here is my current solution for unit-testing my own CMake script code.
假设使用CMake脚本处理模式是我的最佳选择,并且我不得不模拟在脚本模式下不可用的CMake命令,到目前为止,我提出了以下建议.
By the assumption that using CMake Script processing mode is my best catch and that I have to mock the CMake commands that are not usable in script mode I - so far - came up with the following.
利用我自己的全局属性,我编写了辅助函数来存储和比较函数调用:
Utilizing my own global properties, I have written helper functions to store and compare function calls:
function(cmakemocks_clearlists _message_type)
_get_property(_list_names GLOBAL PROPERTY MockLists)
if (NOT "${_list_names}" STREQUAL "")
foreach(_name IN ITEMS ${_list_names})
_get_property(_list_values GLOBAL PROPERTY ${_name})
if (NOT "${_list_values}" STREQUAL "")
foreach(_value IN ITEMS ${_list_values})
_message(${_message_type} "cmakemocks_clearlists(${_name}): \"${_value}\"")
endforeach()
endif()
_set_property(GLOBAL PROPERTY ${_name} "")
endforeach()
endif()
endfunction()
function(cmakemocks_pushcall _name _str)
_message("cmakemocks_pushcall(${_name}): \"${_str}\"")
_set_property(GLOBAL APPEND PROPERTY MockLists "${_name}")
_set_property(GLOBAL APPEND PROPERTY ${_name} "${_str}")
endfunction()
function(cmakemocks_popcall _name _str)
_get_property(_list GLOBAL PROPERTY ${_name})
set(_idx -1)
list(FIND _list "${_str}" _idx)
if ((NOT "${_list}" STREQUAL "") AND (NOT ${_idx} EQUAL -1))
_message("cmakemocks_popcall(${_name}): \"${_str}\"")
list(REMOVE_AT _list ${_idx})
_set_property(GLOBAL PROPERTY ${_name} ${_list})
else()
_message(FATAL_ERROR "cmakemocks_popcall(${_name}): No \"${_str}\"")
endif()
endfunction()
function(cmakemocks_expectcall _name _str)
_message("cmakemocks_expectcall(${_name}): \"${_str}\" -> \"${ARGN}\"")
_set_property(GLOBAL APPEND PROPERTY MockLists "${_name}")
string(REPLACE ";" "|" _value_str "${ARGN}")
_set_property(GLOBAL APPEND PROPERTY ${_name} "${_str} <<<${_value_str}>>>")
endfunction()
function(cmakemocks_getexpect _name _str _ret)
if(NOT DEFINED ${_ret})
_message(SEND_ERROR "cmakemocks_getexpect: ${_ret} given as _ret parameter in not a defined variable. Please specify a proper variable name as parameter.")
endif()
_message("cmakemocks_getexpect(${_name}): \"${_str}\"")
_get_property(_list_values GLOBAL PROPERTY ${_name})
set(_value_str "")
foreach(_value IN ITEMS ${_list_values})
set(_idx -1)
string(FIND "${_value}" "${_str}" _idx)
if ((NOT "${_value}" STREQUAL "") AND (NOT ${_idx} EQUAL -1))
list(REMOVE_ITEM _list_values "${_value}")
_set_property(GLOBAL PROPERTY ${_name} ${_list_values})
string(FIND "${_value}" "<<<" _start)
string(FIND "${_value}" ">>>" _end)
math(EXPR _start "${_start} + 3")
math(EXPR _len "${_end} - ${_start}")
string(SUBSTRING "${_value}" ${_start} ${_len} _value_str)
string(REPLACE "|" ";" _value_list "${_value_str}")
set(${_ret} "${_value_list}" PARENT_SCOPE)
break()
endif()
endforeach()
endfunction()
样机
通过添加类似的模型:
The Mockups
By adding mockups like:
macro(add_library)
string(REPLACE ";" " " _str "${ARGN}")
cmakemocks_pushcall(MockLibraries "${_str}")
endmacro()
macro(get_target_property _var)
string(REPLACE ";" " " _str "${ARGN}")
set(${_var} "[NULL]")
cmakemocks_getexpect(MockGetTargetProperties "${_str}" ${_var})
endmacro()
测试
我可以这样编写测试:
The Tests
I can write a test like this:
MyUnitTests.cmake
cmakemocks_expectcall(MockGetTargetProperties "MyLib TYPE" "STATIC_LIBRARY")
my_add_library(MyLib "src/Test1.cc")
cmakemocks_popcall(MockLibraries "MyLib src/Test1.cc")
...
cmakemocks_clearlists(STATUS)
并通过以下方式将其包含到我的CMake项目中:
And include it into my CMake projects with:
CMakeLists.txt
add_test(
NAME TestMyCMake
COMMAND ${CMAKE_COMMAND} -P "MyUnitTests.cmake"
)
这篇关于CMake:如何对自己的CMake脚本宏/功能进行单元测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!