16、CTest仪表板与CDash:CDash是什么?配置CTest上传结果、查看历史趋势

说实话,我第一次接触CDash的时候,心里想的是:「这不就是个网页版测试报告吗?」后来在团队里真正用起来,才发现这东西远不止「报告」那么简单。

咱们先理清一个概念:CTest负责跑测试、收集结果,CDash负责展示、归档、对比。两者配合,才能形成完整的持续测试闭环。

CDash到底是什么?

CDash是一个开源的Web仪表板系统。它专门用来聚合、展示、分析CTest上传的测试结果。你可以把它理解成「测试数据的中央仓库」。

我个人习惯把CDash比作「测试界的GitHub」——你提交代码到GitHub,别人能看到你的代码变更;你上传测试结果到CDash,团队就能看到你的测试状态变化。

核心价值:CDash让你从「单次测试通过/失败」的视角,升级到「测试历史趋势」的视角。你能看到:这个测试昨天是绿的,今天怎么红了?这个模块的测试覆盖率是上升还是下降?

CDash能做什么?

  • 聚合多平台结果:Windows、Linux、macOS的测试结果可以汇总到同一个仪表板
  • 历史对比:查看任意测试在过去N次提交中的状态变化
  • 失败分析:自动归类相似的测试失败,帮你定位是代码变更还是环境问题
  • 覆盖率追踪:配合gcov/lcov,展示代码覆盖率的趋势曲线
  • 性能回归检测:记录测试执行时间,发现性能退化

嗯,这里要注意:CDash不是CI系统。它不负责触发构建和测试,只负责「收数据、展示数据」。CI系统(比如Jenkins、GitLab CI)负责跑CTest,CTest再把结果推给CDash。

配置CTest上传结果到CDash

配置过程其实不复杂,但有几个坑。我曾经在一个项目里折腾了整整一下午,就是因为漏了一个环境变量。

第一步:准备CDash服务器

你可以自己搭建CDash,也可以用Kitware官方提供的公共CDash(用于开源项目)。自己搭建的话,需要PHP和MySQL/PostgreSQL支持。我个人建议先用Docker快速跑起来:

docker run -d -p 80:80 \
  -e CDASH_DB_HOST=mysql \
  -e CDASH_DB_NAME=cdash \
  -e CDASH_DB_USER=cdash \
  -e CDASH_DB_PASS=cdash_pass \
  kitware/cdash:latest

启动后访问 http://localhost ,按向导完成初始化配置。你会得到一个CDash项目的URL,比如 http://cdash.example.com/submit.php?project=MyProject

第二步:在CMakeLists.txt中配置CTest

你需要告诉CTest:测试结果要往哪里传。在项目的顶层CMakeLists.txt里加上:

# 启用测试
enable_testing()

# 设置CDash服务器地址
set(CTEST_DROP_METHOD "http")
set(CTEST_DROP_SITE "cdash.example.com")
set(CTEST_DROP_LOCATION "/submit.php?project=MyProject")
set(CTEST_DROP_SITE_CDASH TRUE)

# 可选:设置构建名称和站点名称
set(CTEST_BUILD_NAME "Linux-GCC-Release")
set(CTEST_SITE "build-server-01")
小技巧:CTEST_BUILD_NAME 建议包含「操作系统-编译器-构建类型」三要素。这样在CDash仪表板上,你能一眼区分不同平台的测试结果。

第三步:编写CTest配置脚本

我建议单独写一个 CTestConfig.cmake 文件,放在项目根目录:

# CTestConfig.cmake
set(CTEST_PROJECT_NAME "MyProject")
set(CTEST_NIGHTLY_START_TIME "00:00:00 UTC")

set(CTEST_DROP_METHOD "http")
set(CTEST_DROP_SITE "cdash.example.com")
set(CTEST_DROP_LOCATION "/submit.php?project=MyProject")
set(CTEST_DROP_SITE_CDASH TRUE)

然后在运行CTest时,用 -S 参数指定一个驱动脚本。比如 dashboard.cmake

# dashboard.cmake
cmake_minimum_required(VERSION 3.20)

# 包含项目配置
include("${CTEST_SCRIPT_DIRECTORY}/CTestConfig.cmake")

# 设置源码和构建目录
set(CTEST_SOURCE_DIRECTORY "${CTEST_SCRIPT_DIRECTORY}")
set(CTEST_BINARY_DIRECTORY "${CTEST_SCRIPT_DIRECTORY}/build")

# 开始仪表板测试
ctest_start("Experimental")
ctest_configure()
ctest_build()
ctest_test()
ctest_submit()

第四步:运行并上传

ctest -S dashboard.cmake -V

-V 参数会输出详细日志。你会看到类似这样的信息:

Submit files (using http)
   Submit URL: http://cdash.example.com/submit.php?project=MyProject
   Submit file: /path/to/Testing/20250320-1234/Build.xml
   Uploaded: Build.xml
   Submit file: /path/to/Testing/20250320-1234/Test.xml
   Uploaded: Test.xml
   Submission successful

看到「Submission successful」就说明上传成功了。去CDash网页上刷新,就能看到测试结果。

我曾经踩过的坑:防火墙或代理会阻止CTest的HTTP请求。如果你在公司内网,记得检查 http_proxy 环境变量。另外,CDash服务器的时间要和构建机器同步,否则时间戳错乱会导致仪表板显示异常。

查看历史趋势

数据上传到CDash之后,怎么用?我挑几个最常用的功能说说。

测试通过率趋势图

CDash会自动生成测试通过率的折线图。横轴是时间/构建次数,纵轴是通过率百分比。你能直观看到:

  • 某次提交后通过率突然下降——说明引入了回归
  • 通过率长期在90%徘徊——说明有顽固的失败测试需要处理

测试耗时变化

每个测试的执行时间都会被记录。如果某个测试从1秒突然变成10秒,CDash会标红。我遇到过几次,都是因为测试代码里不小心加了死循环或者大数据量操作。

覆盖率趋势

如果你在CTest中配置了覆盖率收集,CDash会展示行覆盖率、函数覆盖率、分支覆盖率的趋势。我个人习惯每周看一次覆盖率曲线——如果连续两周下降,就该补测试了。

SVG:CDash与CTest协作流程

CTest + CDash 协作流程 开发者 提交代码 代码仓库 Git/SVN CI服务器 Jenkins/GitLab CI CTest 运行测试 CDash仪表板 聚合展示 触发构建 上传XML结果 团队 查看趋势 Web访问 反馈循环:修复失败测试 → 再次提交

CDash的几种测试模式

模式 用途 我常用的场景
Experimental 开发中的实验性测试 本地调试,不想影响主线
Nightly 每日定时构建测试 团队每日集成检查
Continuous 每次提交触发 CI流水线中自动运行
Coverage 覆盖率收集 每周覆盖率趋势分析

ctest_start() 中指定模式:

ctest_start("Experimental")   # 实验模式
ctest_start("Nightly")        # 每日构建
ctest_start("Continuous")     # 持续集成
我的建议:团队刚开始用CDash时,先用Experimental模式跑一周。等流程稳定了,再切到Nightly或Continuous。别一上来就全自动,出了问题排查起来很头疼。

避坑指南

最后分享几个我踩过的坑:

  • XML文件过大:如果测试用例特别多(上千个),生成的XML可能几十MB。CDash默认限制上传大小,需要在服务器端调整 upload_max_filesizepost_max_size
  • 重复提交:同一个构建ID重复提交,CDash会覆盖旧数据。如果你在CI中不小心循环调用了 ctest_submit(),历史数据就乱了。我建议在脚本里加个锁或者状态检查。
  • 时区问题:CDash的「每日构建」按UTC 00:00切分。如果你的构建在UTC 23:59跑完,上传后会被归到当天;如果00:01跑完,就归到第二天。这个边界情况容易让人困惑。

好了,CDash的基本用法就这些。说白了,它就是帮你把零散的测试结果变成可视化的历史趋势。配置一次,后面就省心了。


公众号:蓝海资料掘金营,微信deep3321