测试覆盖率

设计初衷

软件测试是一种常见做法,可帮助团队持续交付 质量代码。测试对软件行为、捕获和 防止功能或其他所需属性出现衰退,并帮助扩展 工程流程。

衡量测试覆盖率的源代码行覆盖率有助于工程师 找出测试解决方案中的不足之处使用测试覆盖率作为指标 推动实现更高质量的软件和更安全的开发实践。测量测试 帮助工程师保持较高的质量水准。

测试覆盖率并不保证代码不会出现错误。测试应该与 模糊测试、静态和动态测试等其他工具 分析等

绝对测试覆盖率

绝对测试覆盖率衡量的是所涵盖的所有源代码行 测试。Fuchsia 的持续集成 (CI) 基础架构会生成绝对覆盖率报告, 。覆盖率报告通常最多会在几个小时前过时。

绝对覆盖率信息中心

点击此处可查看最新的绝对覆盖率报告。 此信息中心以树形布局显示发现的所有代码 作为源代码树的一个子集,包含在经过的所有测试中。您 可以按目录结构浏览树,并查看总覆盖率指标 。

此外,覆盖范围信息还会作为 Google 的 内部代码搜索。

覆盖率信息中心屏幕截图

增量测试覆盖率

增量测试覆盖率会在 Gerrit 代码审核网页界面。增量覆盖率显示,尤其是在 给定更改的上下文,其中修改后的行将用于测试和 修改后的代码行。

增量测试覆盖率由 Fuchsia 的提交队列 (CQ) 收集 基础架构将更改发送到 CQ (Commit-Queue+1) 时,您可以点击 “检查”部分标签页,然后在三点状菜单下点击“显示其他结果” 然后在过滤条件文本框中输入“fuchsia-coverage”找到那个名为 负责收集要在 Gerrit 中展示的增量覆盖率。当 tryjob 完成,您的补丁集应具有绝对覆盖率 (|Cov.|), 增量覆盖率(升幅)

对于影响项目的更改,保持较高的增量测试覆盖率有助于 持续保持较高的测试覆盖率。特别是,它可以防止引入新的 将未经测试的代码复制到项目中更改作者可以查看增量覆盖率 更改相关信息,以确保测试范围足够大。 代码审核人员可以查看关于更改的增量测试覆盖率信息, 请作者弥补所有他们认为重要的测试缺口。

显示覆盖率统计信息的 Gerrit 屏幕截图

显示线条覆盖率的 Gerrit 屏幕截图

覆盖率驱动型开发工作流

您可以在浏览器或 VS Code 中查看本地修改的覆盖率。 您可以使用它来建立覆盖率驱动型开发工作流。

准备测试环境

首先,配置构建以使用覆盖率变体并包含 这些示例将用于演示该工作流。

C++

fx set core.x64 --variant coverage --with examples/hello_world
fx build

Rust

fx set core.x64 --variant coverage-rust --with examples/hello_world
fx build

让我们启动一个作为您的目标设备的模拟器,然后启动 我们将使用两个终端来完成此步骤。如果您已有 无论是模拟器还是真实硬件,则可以跳过 此步骤。

在您的第一个终端中:

fx qemu -kN

接下来,启动软件包服务器,用于发布 测试软件包,此过程将在后台运行。如果你已经运行了一个软件包服务器,则可以跳过 此步骤。

在第二个终端中:

fx serve

在浏览器中查看覆盖率

在此工作流程中,我们将运行测试并生成覆盖率报告, 。

执行测试并导出覆盖率 HTML 报告

执行测试并生成 HTML 报告。

C++

fx coverage --html-output-dir $HOME/fx_coverage hello-world-cpp-unittests

Rust

fx coverage --html-output-dir $HOME/fx_coverage hello-world-rust-tests

在浏览器中查看覆盖率摘要

使用浏览器打开 $HOME/fx_coverage/index.html。您应该会看到覆盖率选项 摘要页面。

“索引涵盖范围”报告的屏幕截图

点击任一文件即可查看该文件的行覆盖率。通过 “计数”列显示的是测试行被访问的次数(1 次或多次)。否 “计数”的值表示 0,即未覆盖该行。

在 VS Code 中查看覆盖率

只有在准备好测试环境后才能开始本部分。

  1. 从 Visual Studio Marketplace 安装 coverage-gutters 扩展程序。
  2. 通过将以下属性添加到 settings.json
{
    "coverage-gutters.coverageBaseDir": ".",
    "coverage-gutters.showLineCoverage": true,
    "coverage-gutters.coverageFileNames": [ "lcov.info" ]
}

运行测试并查看覆盖率

我们来执行测试并导出 LCOV 文件,VS Code 将使用该文件显示 覆盖率。

C++

fx coverage --lcov-output-path $FUCHSIA_DIR/lcov.info hello-world-cpp-unittests

Rust

fx coverage --lcov-output-path $FUCHSIA_DIR/lcov.info hello-world-rust-tests

在 VS Code 中查看覆盖率

  1. 找到您要查看其覆盖率的文件。
  2. 右键点击文件的修改区域,然后选择“Coverage Gutters: Display [覆盖范围:显示间距] 覆盖率。”
  3. 已覆盖的线条为绿色,未覆盖的线条为红色。
  4. 您可以重新导出 LCOV 并重新执行第 2 步,以查看更新后的覆盖率(对于某些 因为“watch”不起作用。)

展示覆盖率

VS Code 覆盖率

针对更改重新运行测试

最后,您可以使用此命令来监控文件系统更改并重新运行 都会在每次保存代码时都自动进行测试

C++

fx -i coverage --lcov-output-path $FUCHSIA_DIR/lcov.info hello-world-cpp-unittests

Rust

fx -i coverage --lcov-output-path $FUCHSIA_DIR/lcov.info hello-world-rust-tests

端到端 (E2E) 测试排除项

只有单元测试和封闭集成测试被视为可靠来源 测试覆盖率。对于端到端测试,系统不会收集或显示测试覆盖范围。

E2E 测试是指对整个产品进行测试的大型系统测试, 必须涵盖源代码中明确定义的部分。例如, 在 Fuchsia 上进行 E2E 测试的常见方式,以便在模拟器中启动系统, 并期望某些行为。

原因

由于 E2E 测试会将系统作为一个整体来进行测试:

  • 观察到它们经常会在运行之间触发不同的代码路径,因此 其覆盖率也会导致不稳定
  • 它们经常会在覆盖率构建器上超时,从而导致构建器不稳定。端到端 测试的运行速度远远慢于单元测试和小型集成测试, 通常需要几分钟才能完成。此外,它们在覆盖率构建器上的运行速度甚至会更慢 而过度耗用资源。

具体做法

对于顶级 buildbot 软件包(例如 core), 提供了副本 core_no_e2e,因此聊天机器人 可以使用 no_e2e 软件包来避免构建和运行 所有 E2E 测试

目前,没有可靠的方法来识别树内的所有 E2E 测试。作为 代理,no_e2e 软件包会保持不变,即它们没有任何已知 通过 GN 的 e2e 测试库的递归依赖项中的 e2e 测试 assert_no_deps。端到端测试库列表 它经过人工挑选和维护,并假设环境变化非常大 频率:

e2e_test_libs = [
  "//sdk/testing/sl4f/client",
  "//third_party/mobly($host_toolchain)",
  "//src/testing/end_to_end/honeydew($host_toolchain)",
]
if (is_linux) {
  e2e_test_libs += [ "//tools/emulator($host_toolchain)" ]
}

限制

目前,只有在满足以下条件时,系统才会收集测试覆盖率:

  • 代码使用 C、C++ 或 Rust 编写。
  • 该代码会在用户模式下在 Fuchsia 上运行,或在主机上运行。内核覆盖率 尚不支持(跟踪错误)。
  • 该测试在 qemu 上运行。尚不支持在硬件上进行测试。
  • 该测试作为 core 产品配置的一部分运行。
  • 不支持端到端 (e2e) 测试。

最后要注意的是,e2e 测试在整个系统中会执行大量代码, 它们的运行方式不一致(或“不稳定”)。为了实现 代码测试覆盖率更高,但实际上建议这样做 使用单元测试和集成测试

实验功能

默认情况下,系统仅收集增量覆盖率core.x64 中。针对两个core.x64中的更改收集合并覆盖率 和 core.arm64,请按以下步骤操作:

  1. 在 Gerrit 中,转到“Checks”标签页。
  2. 按“选择 tryjobs”。
  3. 添加了 fuchsia-coverage-x64-arm64

在 Gerrit 中手动选择 x64-arm64 覆盖率

系统会显示一个对勾。待其从待处理状态变为已完成状态后,刷新 Gerrit 即可看到 覆盖率结果。

另请参阅: 问题 91893:仅在 Gerrit 中针对 x64 收集增量覆盖率

即将发布的功能

对以下附加用例的支持目前正在开发中:

  • 内核代码覆盖率。
  • 涵盖除core以外的产品配置,例如 bringupworkstation_eng
  • 硬件目标的覆盖率,即从不在测试中运行的测试收集数据 qemu。

问题排查

不支持的配置 / 语言 / 运行时

如果您看不到绝对或增量覆盖率信息, 请先查看限制,并确保您的代码 预计首先获得保修支持

为帮助您排查问题,请检查是否缺少覆盖率(各行应 已覆盖但显示为未覆盖)或者您是否未覆盖 信息(任何文件都不会显示在覆盖率报告中,或者行是 未对这些内容是否涵盖加注注释)。

缺少覆盖率表示代码是使用插桩构建的,但 实际上并未包含在运行的测试中。可能没有任何覆盖率信息 例如,表明您的代码在构建时未使用覆盖率或测试 不在保修范围内(详见下文)。

过时报告 / 延迟时间

绝对覆盖率报告在代码合并后生成,可能需要 需要几个小时才能完全编译。信息中心会显示 已生成报告。如果您在信息中心内没有看到预期结果,请确保 数据是在可能影响到相关的最新更改之后生成的, 覆盖率。如果数据已过时,请稍后返回并刷新页面。

增量覆盖率报告由 CQ 生成。确保您查看的是 发送到 CQ 的补丁集您可以点击“显示实验性 tryjobs”更改为 显示名为 fuchsia-coverage 的 tryjob。如果 tryjob 仍在运行, 请稍后返回并刷新页面。

确保您的测试已运行

如果您的代码未包含您期望的覆盖率,请选择一项测试, 应该已覆盖您的代码,并确保它在覆盖率 tryjob 上运行。

  1. 在 Gerrit 中查找 tryjob,或者查找fuchsia-coverage最近在 CI 信息中心
  2. 在“Overview”(概览)标签页中,找到“collect build”(收集 build)然后展开,找到 指向显示不同覆盖率、构建和测试运行 不同的配置
  3. 每个页面都应有一个“Test Results”标签页,显示 。确保您预期的测试已运行,最好能通过测试。

如果您的测试没有按预期针对任何覆盖率 tryjob 运行,可能有一种原因 只是它仅在 CI/CQ 当前未涵盖的配置中运行。 另一种情况是,测试在覆盖率变体中明确选择不参与。对于 引用您的测试的 BUILD.gn 文件可能如下所示:

fuchsia_test_package("foo_test") {
  test_components = [ ":test" ]
  deps = [ ":foo" ]

  # TODO(https://fxbug.dev/42074368): This test is intentionally disabled on coverage.
  if (is_coverage) {
    test_specs = {
      environments = [
        {
          dimensions = emu_env.dimensions
          tags = [ "disabled" ]
        },
      ]
    }
  }
}

查找相关背景信息,了解测试在覆盖率方面被禁用的原因,并进行调查。

排查测试未显示覆盖率的情况的示例 (因为未设置为在 CQ 上运行)。请参阅此处

测试仅在覆盖率上失败或不稳定

与上述内容相关,测试在覆盖率范围内更有可能不稳定 example。因在运行时收集覆盖率而产生的额外开销 拖慢性能,进而影响时间,而时间通常是 以进一步增强稳定性

另一个原因可能是测试期间未在 常规测试运行实验结果表明,测试平均可运行 2.3 倍 因为收集 运行时配置文件。为了适应这一情况,测试运行程序提供了一个更长的 超时。不过,测试 可能会受此影响,设置了各自的内部操作超时。

一般来说,测试不应出现超时。等待时 执行测试中的异步操作,最好无限期等待 测试运行程序的总体超时时间会过期。

最后,在覆盖率上,变体组件可能会使用 fuchsia.debug.DebugData 协议。这会干扰 并准确假设组件使用的功能。查看 实例:

一种立竿见影的解决方法是停用覆盖率测试(请参阅 GN 代码),但无需收集覆盖率信息 。根据最佳做法,您应该对覆盖的不稳定进行相同的处理。 就像您在其他地方处理碎片一样,主要是修复不稳定问题。

另请参阅:不稳定的测试政策

在 Gerrit 中没有看到预期的覆盖率

如果您发现某些线路没有被覆盖,但确信 这些行应该被正在运行的测试覆盖,请尝试收集覆盖率 再次按下“选择 Tryjobs”,找到fuchsia-coverage并添加它。

添加紫红色覆盖效果的 Gerrit 屏幕截图

如果 fuchsia-coverage 完成(变为绿色),但您看到的是其他行 则出现以下情况之一:

  1. 测试以不一致的方式执行被测代码 不同运行。这通常也会导致测试结果不稳定, 出现的问题。
  2. 覆盖率的生成和收集方式存在问题, 结果不一致。请提交错误。

测试覆盖率的工作原理

Fuchsia 的代码覆盖率构建、测试运行时支持和处理工具使用 基于 LLVM 源代码的代码覆盖率。紫红色 平台受 compile-rt 配置文件运行时支持

"coverage" build 变体处于以下状态时,会启用配置文件插桩 已选择。然后,编译器会生成计数器,每个计数器对应一个 分支,并发出关于分支条目的指令, 递增相关计数器。此外,配置文件插桩 运行时库链接到可执行文件

如需了解实现详情,请参阅 LLVM 代码覆盖映射格式

请注意,插桩会导致二进制文件大小增加、内存增加 并且测试执行时间会延长我们采取了一些措施来抵消这种情况:

  • 配置文件变体中的测试具有更长的超时时间。
  • 配置文件变体中的测试在编译时进行了一些优化。
  • 目前覆盖率在模拟器上运行,模拟器对存储空间的限制较小。
  • 对于增量覆盖率,只有受此项变更影响的来源才会纳入统计范围 插桩。

Fuchsia 上的配置文件运行时库将配置数据存储在 VMO 中,以及 使用 fuchsia.debug.DebugData 向 VMO 发布句柄 协议。此协议可用于在运行时使用 组件框架,由 Test Runner 框架的 设备端控制器测试管理器

系统会在测试领域终止后收集配置文件以及 以及托管在其中的组件这些分析文件随后会处理成一个摘要 进行测试。这是一项重要的优化 总配置文件大小。然后,经过优化的配置文件会发送到主机端测试 控制器。

主机使用 covargs 工具,该工具本身使用 llvm-profdatallvm-cov 工具,用于将原始配置文件转换为摘要 格式,并生成测试覆盖率报告。此外,covargs 会将 将数据转换为 protobuf 格式,该格式用作各种 信息中心

路线图

正在进行的工作:

  • 提升了覆盖率运行时的性能和可靠性。
  • 对 ZBI 测试的源代码覆盖率的内核支持。
  • 自定义覆盖率信息中心和提醒:为您的团队构建信息中心。
  • 本地工作流:在本地运行测试,生成覆盖率报告。
  • IDE 集成:在 VS Code 中查看覆盖层。

近期作业:

  • 树外支持:涵盖 Fuchsia CI/CQ 以外的服务。

深入阅读