I have inherited a not too big C++ legacy code which I am currently reengineering. So far I understand most of the code quite well and am able to use it, though the maintenance is hell. I think the main difficulty lies in the MASSIVE use of preprocessor directives to control the behaviour of the program.
Consider the following examples:
void function(){
... // lots of code
#ifdef PARAMETER == 1
do_one_thing();
#elif PARAMETER == 2
do_another_thing();
...//etc
#endif
...//lots of code
}
or
void function(double arg1,
#ifdef SOME_PP_VAR1 == 5
double arg2,
#endif
)
and stuff like
#ifdef SOME_PP_VAR2 == 2
typedef myVector std::vector<double>;
#elif SOME_PP_VAR2 == 7
typedef myVector std::vector<int>;
#endif
in the global scope. Or even
#ifdef SOME_PP_VAR2 == 2
#include "some_header.hpp"
#elif SOME_PP_VAR2 == 7
#include "some_other_header.hpp"
#endif
About 30 of such preprocessor variables are set in a configuration file which is passed to the build system and then to the compiler. It basically controls everything and is present in almost any file. By the way, in some places the #ifs are even nested.
Therefore, it is quite difficult to write unit tests. I would have to build all of the possible combinations of the preprocessor variables and test each.
My (poor) ideas so far are:
Did you ever encounter such a situation and how did you handle it?
How to cover a C++ legacy code controlled by preprocessor #ifdefs with unittests?
Just write the unit tests for each needed tested case, compile each test case with different #ifdefs
needed to test it, and run them.
Did you ever encounter such a situation
I think I had a similar case. I was (trying to...) writing tests for software that used GuruxDLMS.c library (and here).
and how did you handle it?
Deciding how to compile stuff and running tests is a job for accommodating build system integrated around the project. Assuming for example CMake build system that I am comfortable with, I would just write the tests for each case and compile the library for each case:
function(add_cpp_legacy_code_library name)
add_library(${name} sources.....cpp)
target_compile_definitions(${name} PUBLIC ${ARGN})
endfunction()
function(add_cpp_legacy_code_test name sourcefile)
add_cpp_legacy_code_library(${name}_library ${ARGN})
add_executable(${name} ${sourcefile})
target_link_libraries(${name} PRIVATE ${name}_library)
add_test(NAME ${name} COMMAND ${name})
endfunction()
add_cpp_legacy_code_test(test1 some_test1.cpp
PARAMETER=1
SOME_PP_VAR1=4
SOME_PP_VAR2=4
)
add_cpp_legacy_code_test(test2 some_test2.cpp
PARAMETER=100
SOME_PP_VAR1=1234
SOME_PP_VAR2=7890
)
# etc...
Etc. for each macros combination that you want to test, where some_test1.cpp
some_test2.cpp
would either have #if
for each macro combination or would be separate files.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With