Как покрыть унаследованный код C++, управляемый препроцессором #ifdefs, юнит-тестами?

Я унаследовал не слишком большой унаследованный код C++, который в настоящее время реинжиниринг. До сих пор я достаточно хорошо понимаю большую часть кода и могу использовать его, хотя обслуживание - ад. Я думаю, что основная трудность заключается в МАССИВНОМ использовании директив препроцессора для управления поведением программы.

Рассмотрим следующие примеры:

void function(){
    ... // lots of code 
    #ifdef PARAMETER == 1
        do_one_thing();

    #elif PARAMETER == 2
        do_another_thing();
    ...//etc
    #endif
    ...//lots of code
}

или же

void function(double arg1,
             #ifdef SOME_PP_VAR1 == 5
             double arg2,
             #endif
             )

и тому подобное

#ifdef SOME_PP_VAR2 == 2
    typedef myVector std::vector<double>;
#elif SOME_PP_VAR2 == 7
    typedef myVector std::vector<int>;
#endif

в глобальном масштабе. Или даже

#ifdef SOME_PP_VAR2 == 2
    #include "some_header.hpp"
#elif SOME_PP_VAR2 == 7
    #include "some_other_header.hpp"
#endif

Около 30 таких переменных препроцессора устанавливаются в файле конфигурации, который передается в систему сборки, а затем в компилятор. Он в основном контролирует все и присутствует практически в любом файле. Кстати, в некоторых местах #ifs даже вложены.

Поэтому довольно сложно писать юнит-тесты. Я должен был бы построить все возможные комбинации переменных препроцессора и проверить каждую.

Мои (плохие) идеи пока таковы:

  • Перепишите код с нуля, возможно, на другом языке. (Высокий риск, слишком много времени).
  • Замените переменные препроцессора константами. (Не всегда выполнимо).

Вы когда-нибудь сталкивались с такой ситуацией и как вы справились с этим?

С наилучшими пожеланиями и спасибо заранее.

1 ответ

Как покрыть устаревший код C++, управляемый препроцессором #ifdefs, с помощью модульных тестов?

Просто напишите модульные тесты для каждого необходимого тестового примера, скомпилируйте каждый тестовый пример с разными #ifdefs нужно было протестировать и запустить их.

Вы когда-нибудь сталкивались с такой ситуацией

Думаю, у меня был похожий случай. Я (пытался ...) писал тесты для программного обеспечения, которое использовало библиотеку GuruxDLMS.cздесь).

и как ты с этим справился?

Решение о том, как компилировать материал и запускать тесты, - это работа по адаптации системы сборки, интегрированной в проект. Предполагая, например, систему сборки CMake, с которой мне комфортно, я бы просто написал тесты для каждого случая и скомпилировал библиотеку для каждого случая:

      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...

И т. Д. Для каждой комбинации макросов, которую вы хотите протестировать, где some_test1.cpp some_test2.cpp либо имел бы #if для каждой комбинации макросов или будут отдельными файлами.

Другие вопросы по тегам