如何使我的单元测试适应cmake和ctest?

Ste*_*314 20 cmake ctest

到目前为止,我已经使用了一个简易的单元测试程序 - 基本上整个负载的单元测试程序由批处理文件自动运行.虽然其中很多都明确地检查了他们的结果,但更多的欺骗 - 他们将结果转储到版本化的文本文件.测试结果中的任何更改都会被subversion标记,我可以轻松识别出更改的内容.许多测试输出点文件或其他一些形式,允许我获得输出的直观表示.

麻烦的是我正在转向使用cmake.使用cmake流意味着使用源外构建,这意味着转储在共享源/构建文件夹中的便利性以及将它们与源一起版本化并不真正起作用.

作为替代,有什么我喜欢做的是告诉单元测试工具在哪里可以找到预期的结果(源树)的文件,并得到它做比较.失败时,它应该提供实际结果和差异列表.

这是可能的,还是应该采取完全不同的方法?

显然,我可以忽略ctest,只是调整我一直在做的源外构建.我可以对我的文件夹进行版本控制 - 例如,所有的构建 - 实时(当然,自由使用'忽略').这样理智吗?可能不是,因为每个构建最终会得到预期结果的单独副本.

此外,对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;
}
Run Code Online (Sandbox Code Playgroud)

典型的单元测试现在看起来像......

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);
}
Run Code Online (Sandbox Code Playgroud)

在需要可重用数据转储功能的地方,它需要一个类型的参数std::ostream&,因此它可以转储到实际结果流.

ric*_*chq 19

我将使用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)
Run Code Online (Sandbox Code Playgroud)

这将运行一个脚本(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()
Run Code Online (Sandbox Code Playgroud)

第一个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()
Run Code Online (Sandbox Code Playgroud)

这会在构建输出失败的情况下覆盖源树,然后在其上运行svn diff.问题是你不应该以这种方式改变源树.当你第二次运行测试时,它会通过!更好的方法是安装一些可视化差异工具并在输出和预期文件上运行它.