Skip to content

JEP 281: HotSpot C++ Unit-Test Framework | HotSpot C++ 单元测试框架

摘要

为 HotSpot 启用和鼓励 C++ 单元测试的开发。

目标

  • 支持编写和执行方法、类和子系统的单元测试

  • 支持只测试单元而不运行其他内容的单元测试

  • 支持需要 VM 初始化的测试

  • 支持执行时间在毫秒级别的快速测试

  • 支持正向和负向测试

  • 允许测试隔离

  • 支持与产品源代码一起放置的测试

  • 支持与当前基础设施集成

  • 为每个测试产生单独的结果

  • 能够轻松地从命令行运行单个测试

  • 能够为测试失败提供最小的重现代码

  • 提供 IDE 支持

  • 允许框架的演进,包括对框架进行快速修复

  • 支持与 jtreg 类似粒度的测试选择和分组

  • 允许测试任何编译目标,包括产品和调试版本

  • 允许测试与平台相关的代码

  • 提供最小文档:在存储库中提供教程和示例

  • 通过修改测试源码或其他文件(如排除列表)来排除测试的执行

  • 支持将所有内部测试转换为新框架

  • 支持由 Oracle 支持的 JDK 9 构建平台(参见 Oracle 支持的构建平台

非目标

  • 替换 Java 测试。在 C++ 中进行单元测试是对不同用例进行测试的补充。

动机

在经过充分测试的代码库中,更容易进行更改。测试套件通过验证没有发生意外故障来支持进行更改的工程师。

如今,HotSpot 有许多测试,但没有太多直接类型的测试,并且编写这样的测试并不容易。

引入一个 C++ 的测试框架是迈向更好测试套件的第一步。C++ 的测试框架支持使用与 JVM 相同的语言编写测试,然后内部结构直接暴露给测试代码,与使用 jtreg 从 Java 进行功能测试相比,这提供了另一层可能性,可以更容易地编写小而精确的测试。

开发现有功能的单元测试的可能性将使得能够单独测试 C++ 代码的新功能,并更容易为一些更神秘的问题编写回归测试。

描述

Google Test 框架(GTest)是最符合我们目标的 C++ 单元测试框架,它是一个 xUnit 测试框架,在社区中有很大的影响力。GTest 框架:

  • 是由其他人开发和支持的
  • 与 Eclipse IDE 集成提供了 IDE 集成
  • 是经过实战检验的,提供了完整的 API
  • 具有功能丰富的执行模型
  • 有现有的文档和示例
  • 支持 JUnit 风格的测试结果,并与 Hudson 和 Jenkins 集成

为了允许使用 GTest 为 HotSpot 编写测试,需要完成几项任务,还需要一些额外的任务来增强它。在当前的 GTest 状态下:

  • 使用了 HotSpot 不使用的 C++ 构造,而且在 HotSpot 中被禁用,例如异常、模板和 STL
  • Solaris/Oracle Solaris Studio 不是受支持的操作系统 / 编译器

可以毫无疑问地说,GTest 是第三方工具,因此增加了现有构建和测试流程的另一个依赖关系。GTest 也相当庞大(71K LOC),并且将来可能会发生不兼容的变化。为了避免测试框架本身的变化导致问题,我们需要控制使用的 GTest 的版本,并且能够指定其作为构建的一部分(尽管应该可以覆盖)。具有依赖系统自动下载和安装正确版本的 GTest 将是有益的。

HotSpot 测试目录布局

新的测试需要一个在源代码树中存放的位置。测试的根目录应该放在距离产品源码很近的地方,但不是在产品源码中,就像现有的测试目录结构一样。为了清晰起见,测试不应该与现有的 jtreg 测试混合在一起;相反,它们应该分成两个目录。我们建议将当前的 jdk9/hotspot/test 目录拆分为两个子目录:

  • jdk9/hotspot/test/java
  • jdk9/hotspot/test/native

现有的 jtreg 测试将移到 java 目录下(包括 JNI 代码和 shell 脚本)。TEST.ROOT 文件将保留在顶层。

构建目标和二进制文件

测试代码对产品二进制文件不能有任何可见的影响。例如,不应该额外导出任何符号,并且产品捆绑包中不应该包含任何测试。编译后的测试将被放入单独的测试捆绑包中,每个配置一个。测试将链接到从未剥离的 JVM 库中导出的符号,该库是从与常规库相同的对象文件创建的。

调用测试

必须能够轻松地使用 make 命令行运行测试。为了使测试结果与其他测试的结果兼容,调用可能会潜在地使用 jtreg 测试包装程序,该程序又调用 GTest。GTest 本身可以生成符合 JUnit 风格的结果,与 Hudson/Jenkins 和类似工具很好地集成。

备选方案

备选方案 1:HUTT。之前创建了一个名为“HotSpot Unit Test Tool”(HUTT)的原型框架,它是一个 xUnit 框架。它的体积比 GTest 小得多(2K LOC),并且不是外部依赖。这是一个可行但更昂贵的解决方案。它也缺乏 IDE 支持。

备选方案 2:继续在 Java 中实现测试。可以使用 Whitebox API 访问 JVM 内部。与之相比,添加 Whitebox API 是繁琐且执行缓慢的。它适用于一些内省,但远不能满足所有测试需求。由于要编写和执行高质量的针对特定功能的测试,Java 测试成本更高,通常很难保证确定性。

备选方案 3:继续使用内部测试。这种解决方案将无法满足许多已说明的目标。

风险和假设

风险:GTest 可能会发展成一个不适合作为 HotSpot 单元测试框架的方向。该风险被估计为较低。

缓解计划:分叉 GTest 框架,或使用 HUTT。

风险:回溯 GTest 修复将被证明成本很高。

缓解计划:分叉 GTest 框架,或使用 HUTT。