Как адаптировать мои модульные тесты к cmake и ctest?

До сих пор я использовал импровизированную процедуру поблочного тестирования - в основном целая загрузка программ модульного теста, запущенных автоматически пакетным файлом. Хотя многие из них явно проверяют их результаты, намного больше обмана - они разгрузили результаты в текстовые файлы, которые являются имеющими версию. Любое изменение в результатах испытаний отмечается подверсией, и я могу легко определить, каково изменение было. Многие из тестового вывода отмечают точкой файлы или некоторую другую форму, которая позволяет мне получать визуальное представление вывода.

Проблема состоит в том, что я переключаюсь на использование cmake. Движение с потоком cmake означает использовать сборки из источника, что означает, что удобство разгрузки результатов в общей папке источника/сборки и управлении версиями их наряду с источником действительно не работает.

Как замена, что я хотел бы сделать, должен сказать инструмент модульного теста, где найти файлы ожидаемых результатов (в исходном дереве) и заставить его делать сравнение. При отказе это должно обеспечить фактические результаты и различные списки.

Действительно ли это возможно, или я должен проявить совершенно другой подход?

Очевидно, я мог проигнорировать ctest и просто адаптировать то, что я всегда делал к сборкам из источника. Я мог присвоить версию своему folder-where-all-the-builds-live, например (со свободным использованием 'игнорируют', конечно). Это нормально? Вероятно, не, поскольку каждая сборка закончилась бы с отдельной копией ожидаемых результатов.

Кроме того, любой совет относительно рекомендуемого способа сделать поблочное тестирование с cmake/ctest, с благодарностью полученным. Я потратил впустую немного времени с cmake, не потому что это плохо, но потому что я не понял, как лучше всего работать с ним.

Править

В конце я решил сохранить cmake/ctest сторону поблочного тестирования максимально простой. Для тестирования фактический против ожидаемых результатов я нашел дом для следующей функции в моей библиотеке...

bool Check_Results (std::ostream              &p_Stream  ,
                    const char                *p_Title   ,
                    const char               **p_Expected,
                    const std::ostringstream  &p_Actual   )
{
  std::ostringstream l_Expected_Stream;

  while (*p_Expected != 0)
  {
    l_Expected_Stream << (*p_Expected) << std::endl;
    p_Expected++;
  }

  std::string l_Expected (l_Expected_Stream.str ());
  std::string l_Actual   (p_Actual.str ());

  bool l_Pass = (l_Actual == l_Expected);

  p_Stream << "Test: " << p_Title << " : ";

  if (l_Pass)
  {
    p_Stream << "Pass" << std::endl;
  }
  else
  {
    p_Stream << "*** FAIL ***" << std::endl;
    p_Stream << "===============================================================================" << std::endl;
    p_Stream << "Expected Results For: " << p_Title << std::endl;
    p_Stream << "-------------------------------------------------------------------------------" << std::endl;
    p_Stream << l_Expected;
    p_Stream << "===============================================================================" << std::endl;
    p_Stream << "Actual Results For: " << p_Title << std::endl;
    p_Stream << "-------------------------------------------------------------------------------" << std::endl;
    p_Stream << l_Actual;
    p_Stream << "===============================================================================" << std::endl;
  }

  return l_Pass;
}

Типичный модульный тест теперь смотрит что-то как...

bool Test0001 ()
{
  std::ostringstream l_Actual;

  const char* l_Expected [] =
  {
    "Some",
    "Expected",
    "Results",
    0
  };

  l_Actual << "Some" << std::endl
           << "Actual" << std::endl
           << "Results" << std::endl;

  return Check_Results (std::cout, "0001 - not a sane test", l_Expected, l_Actual);
}

Где мне нужна допускающая повторное использование выводящая данные функция, она берет параметр типа std::ostream&, таким образом, это может вывести к потоку фактических результатов.

20
задан Steve314 28 July 2010 в 22:22
поделиться

1 ответ

Я бы использовал автономный режим сценариев CMake для запуска тестов и сравнения результатов. Обычно для программы модульного тестирования вы должны написать add_test (testname testexecutable) , но вы можете запустить любую команду в качестве теста.

Если вы напишете сценарий "runtest.cmake" и выполните свою программу модульного тестирования через него, то сценарий runtest.cmake сможет делать все, что угодно, включая использование утилиты cmake -E compare_files . Вам нужно что-то вроде следующего в вашем файле CMakeLists.txt:

enable_testing()
add_executable(testprog main.c)
add_test(NAME runtestprog
    COMMAND ${CMAKE_COMMAND}
    -DTEST_PROG=$<TARGET_FILE:testprog>
    -DSOURCEDIR=${CMAKE_CURRENT_SOURCE_DIR}
    -P ${CMAKE_CURRENT_SOURCE_DIR}/runtest.cmake)

Это запускает сценарий (cmake -P runtest.cmake) и определяет 2 переменные: TEST_PROG, установленный на путь к исполняемому файлу теста, и SOURCEDIR, установленный на текущий исходный каталог. Первый нужен, чтобы знать, какую программу запускать, а второй - где найти файлы с ожидаемыми результатами теста. Содержимое runtest.cmake будет следующим:

execute_process(COMMAND ${TEST_PROG}
                RESULT_VARIABLE HAD_ERROR)
if(HAD_ERROR)
    message(FATAL_ERROR "Test failed")
endif()

execute_process(COMMAND ${CMAKE_COMMAND} -E compare_files
    output.txt ${SOURCEDIR}/expected.txt
    RESULT_VARIABLE DIFFERENT)
if(DIFFERENT)
    message(FATAL_ERROR "Test failed - files differ")
endif()

Первый execute_process запускает тестовую программу, которая напишет «output.txt». Если это сработает, то следующий execute_process эффективно запускает cmake -E compare_files output.txt expected.txt . Файл "expected.txt" - это заведомо хороший результат в вашем дереве исходных текстов. Если есть различия, он выдает ошибку, чтобы вы могли увидеть неудавшийся тест.

Чего он не делает, так это распечатки различий; CMake не имеет скрытой полной реализации "различий".В настоящий момент вы используете Subversion, чтобы увидеть, какие строки были изменены, поэтому очевидным решением является изменение последней части на:

if(DIFFERENT)
    configure_file(output.txt ${SOURCEDIR}/expected.txt COPYONLY)
    execute_process(COMMAND svn diff ${SOURCEDIR}/expected.txt)
    message(FATAL_ERROR "Test failed - files differ")
endif()

Это перезаписывает исходное дерево выводом сборки при ошибке, а затем запускает для него svn diff. Проблема в том, что вам не стоит так менять дерево исходных текстов. При повторном запуске теста он проходит! Лучший способ - установить какой-нибудь инструмент визуального сравнения и запустить его в своем выходном и ожидаемом файле.

19
ответ дан 30 November 2019 в 01:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: