CMake: How to Unit-Test your own CMake Script Macros/Functions? -
i've written convenience wrappers around standard cmake commands , want unit-test cmake script code ensure functionality.
i've made progress, there 2 things hope with:
- is there "official" way of unit-testing own cmake script code? special mode run cmake in? goal "white-box testing" (as possible).
- how handle global variables , variable scopes issues? inject global variables test via loading project's cache, configure test cmake file or pushing via -d command line option? simulation/testing of variable scopes (cached vs. non-cached, macros/functions/includes, parameters passed references)?
to start i've looked cmake source code (i'm using cmake version 2.8.10) under /tests , under tests/cmaketests. there huge number of varieties found , looks lot of them specialized on single test case.
so looked available cmake script libraries cmake++ see solution, - when have unit tests - heavily depending on own library functions.
here current solution unit-testing own cmake script code.
by assumption using cmake script processing mode best catch , have mock cmake commands not usable in script mode - far - came following.
the helper functions
utilizing own global properties, have written helper functions store , 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 "") , (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 _ret parameter in not defined variable. please specify proper variable name 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 "") , (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 test 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)
and include cmake projects with:
cmakelists.txt
add_test( name testmycmake command ${cmake_command} -p "myunittests.cmake" )