22、CDash持续集成:如何将CTest结果上传到CDash仪表盘,实现跨平台的构建与测试监控。

说实话,CTest 跑完测试只是第一步。你想想看,团队里五个人,分别在 Windows、macOS、Linux 上开发,每个人跑完测试都说「我这边过了」,结果合到一起就崩。这种事我遇到过太多次了。

CDash 就是来解决这个问题的。它把 CTest 的结果收集起来,展示在一个网页仪表盘上。谁在什么平台上跑了什么测试,通过了多少、失败了多少,一目了然。说白了,它就是 CTest 的「可视化报告中心」。

22.1 CDash 是什么?为什么需要它?

CDash 是一个开源的 Web 应用,专门用来接收和展示 CTest 提交的测试结果。它支持:

  • 跨平台测试汇总:Windows、Linux、macOS 的结果集中展示
  • 历史趋势分析:看看测试通过率是上升还是下降
  • 构建失败定位:哪个文件、哪个测试用例挂了,点进去就能看到日志
  • 团队协作:每个人都能看到最新的构建状态

我个人习惯是把 CDash 作为 CI 流程的「最后一公里」。CI 跑完测试,CTest 把结果推送到 CDash,然后整个团队打开浏览器就能看到状态。比翻 Jenkins 日志舒服多了。

核心价值:CDash 不是替代 CI,而是让 CI 的结果更直观、更可追溯。

22.2 搭建 CDash 服务器

CDash 可以自己部署,也可以用 Kitware 提供的托管服务。自己部署的话,我建议用 Docker,省心。

这里是一个简单的 docker-compose 配置:

version: '3'
services:
  cdash:
    image: kitware/cdash:latest
    ports:
      - "8080:80"
    environment:
      - CDASH_DB_HOST=mysql
      - CDASH_DB_NAME=cdash
      - CDASH_DB_USER=cdash
      - CDASH_DB_PASS=cdash_secret
    volumes:
      - ./cdash-backup:/var/lib/cdash/backup
    depends_on:
      - mysql

  mysql:
    image: mysql:5.7
    environment:
      - MYSQL_ROOT_PASSWORD=root_secret
      - MYSQL_DATABASE=cdash
      - MYSQL_USER=cdash
      - MYSQL_PASSWORD=cdash_secret
    volumes:
      - ./mysql-data:/var/lib/mysql

启动之后,访问 http://localhost:8080 就能看到 CDash 的界面了。第一次访问需要创建管理员账号,然后创建一个项目。项目创建好之后,会得到一个 项目 ID提交令牌,这两个东西后面要用。

小提示:如果你只是测试,可以用 Kitware 的公共 CDash 实例(https://my.cdash.org)。但生产环境我建议自己部署,数据安全可控。

22.3 配置 CTest 提交到 CDash

要让 CTest 把结果推送到 CDash,需要在 CMake 中配置几个变量。嗯,这里要注意,这些变量必须在 enable_testing() 之前设置。

# CMakeLists.txt 中配置 CDash 提交
set(CTEST_PROJECT_NAME "MyProject")
set(CTEST_NIGHTLY_START_TIME "00:00:00 UTC")

# CDash 服务器地址
set(CTEST_DROP_SITE "my-cdash-server.com")
set(CTEST_DROP_SITE_USER "anonymous")
set(CTEST_DROP_SITE_PASSWORD "")
set(CTEST_DROP_SITE_METHOD "http")
set(CTEST_DROP_SITE_CDASH TRUE)

# 或者使用 CDash 的提交 API(推荐)
set(CTEST_SUBMIT_URL "http://my-cdash-server.com/submit.php?project=MyProject")

enable_testing()
add_test(NAME my_test COMMAND my_test_executable)

配置好之后,在构建目录中运行:

ctest -D Experimental   # 提交一次实验性构建
ctest -D Nightly        # 提交夜间构建
ctest -D Continuous     # 提交持续构建

CTest 会自动执行测试,然后把结果打包成 XML,通过 HTTP POST 提交到 CDash。

注意:如果 CDash 服务器要求认证,需要在 CTEST_DROP_SITE_USERCTEST_DROP_SITE_PASSWORD 中填写正确的凭据。我曾经因为忘了配密码,折腾了半天才发现是认证问题。

22.4 使用 CTest 脚本实现自动化提交

手动敲 ctest -D Experimental 不是长久之计。我建议写一个 CTest 脚本,把配置、构建、测试、提交全部自动化。

创建一个 ctest_script.cmake 文件:

# ctest_script.cmake
cmake_minimum_required(VERSION 3.20)

# 项目配置
set(CTEST_PROJECT_NAME "MyProject")
set(CTEST_SOURCE_DIRECTORY "/path/to/source")
set(CTEST_BINARY_DIRECTORY "/path/to/build")

# CDash 配置
set(CTEST_DROP_SITE "my-cdash-server.com")
set(CTEST_DROP_SITE_USER "anonymous")
set(CTEST_DROP_SITE_PASSWORD "")
set(CTEST_DROP_SITE_METHOD "http")
set(CTEST_DROP_SITE_CDASH TRUE)

# 开始构建
ctest_start("Experimental")

# 配置
ctest_configure()

# 构建
ctest_build()

# 测试
ctest_test()

# 提交结果
ctest_submit()

然后运行:

ctest -S ctest_script.cmake

这个脚本可以放在 CI 流水线中,每次代码提交后自动执行。我个人习惯把它放在 GitHub Actions 或 GitLab CI 中,配合 cron 定时触发夜间构建。

22.5 CDash 仪表盘解读

提交成功后,打开 CDash 页面,你会看到类似这样的仪表盘:

CDash 仪表盘布局示意 项目: MyProject 构建类型: Experimental 日期: 2024-01-15 42 通过测试 3 失败测试 2 未运行 95.7% 通过率 各平台构建状态 ✅ Linux (gcc 11) - 全部通过 构建时间: 2m 34s ❌ Windows (MSVC 2022) - 3 个测试失败 构建时间: 3m 12s ⚠️ macOS (clang 15) - 2 个测试未运行 构建时间: 2m 55s 失败测试详情 (Windows) test_network.cpp - 断言失败: 连接超时 查看日志 →

仪表盘主要分几个区域:

  • 统计概览:总测试数、通过数、失败数、通过率
  • 平台状态:每个平台的构建和测试结果
  • 失败详情:点击可以查看具体的测试日志和错误信息
  • 历史趋势:通过率随时间的变化曲线

我最常用的是「失败详情」功能。点进去能看到完整的测试输出,包括标准输出、标准错误、以及 CTest 捕获的异常信息。定位问题非常方便。

22.6 跨平台构建与测试监控实战

假设你的项目需要在三个平台上构建:Linux、Windows、macOS。每个平台有不同的编译器、不同的依赖库。CDash 可以把这些结果统一展示。

下面是一个完整的 CI 配置示例(以 GitHub Actions 为例):

# .github/workflows/cdash.yml
name: CDash CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
  schedule:
    - cron: '0 2 * * *'  # 每天凌晨2点执行夜间构建

jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        config: [Debug, Release]

    runs-on: ${{ matrix.os }}

    steps:
    - uses: actions/checkout@v4

    - name: Configure
      run: |
        cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.config }} \
          -DCTEST_PROJECT_NAME="MyProject" \
          -DCTEST_DROP_SITE="my-cdash-server.com" \
          -DCTEST_SUBMIT_URL="http://my-cdash-server.com/submit.php?project=MyProject"

    - name: Build
      run: cmake --build build --config ${{ matrix.config }}

    - name: Test and Submit
      run: ctest --test-dir build -D Experimental --output-on-failure

这个配置会:

  1. 在三个操作系统上分别构建 Debug 和 Release 版本
  2. 运行所有测试
  3. 把结果提交到 CDash

在 CDash 上,你会看到 6 个构建条目(3 个系统 × 2 种配置)。每个条目都可以展开查看详细的测试结果。

经验之谈:我建议把 CDash 的提交令牌放在 CI 的 secrets 中,不要硬编码在配置文件里。另外,夜间构建(Nightly)和持续构建(Continuous)可以分开配置——夜间构建跑全量测试,持续构建只跑核心测试。

22.7 避坑指南

我在使用 CDash 的过程中踩过不少坑,分享几个典型的:

  • 提交失败:检查 CDash 服务器地址和项目名称是否正确。我曾经把项目名写成了大小写不一致,结果提交了但 CDash 上不显示。
  • 测试超时:CTest 默认超时时间是 10 分钟。如果测试用例跑得久,记得在 add_test() 中设置 TIMEOUT 参数。
  • XML 格式错误:CTest 生成的 XML 如果不符合 CDash 的要求,提交会失败。通常是因为测试输出中包含了特殊字符。可以在 CTest 脚本中设置 CTEST_SUBMIT_RETRY_COUNTCTEST_SUBMIT_RETRY_DELAY 来自动重试。
  • 磁盘空间不足:CDash 会保存历史构建数据,时间长了会占用大量磁盘。建议定期清理旧数据,或者配置数据保留策略。
重要提醒:不要把 CDash 的提交令牌提交到代码仓库中。如果有人拿到了令牌,可以向你的 CDash 项目提交伪造的构建数据。我见过有人因此被刷了上千条假记录,清理起来非常痛苦。

22.8 总结

CDash 让 CTest 的测试结果变得可视化、可追溯。它解决了跨平台测试监控的痛点——你不用再登录不同的 CI 平台去翻日志,打开 CDash 就能看到全局状态。

配置起来其实不复杂:部署 CDash 服务器、配置 CTest 提交、写一个自动化脚本、集成到 CI 流水线。做完这些,你的项目就有了一个专业的测试监控中心。

嗯,最后说一句:CDash 和 CTest 是天生一对。如果你已经在用 CTest,那 CDash 绝对是值得投入的下一站。

一句话总结:CTest 负责跑测试,CDash 负责展示结果。两者结合,让跨平台测试监控变得简单而直观。

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