JEP 281:HotSpot C++ 单元测试框架
概括
启用并鼓励为 HotSpot 开发 C++ 单元测试。
目标
-
支持为方法、类和子系统编写和执行单元测试
-
支持单元测试,仅测试一个单元,不运行其他任何内容
-
支持需要VM初始化的测试
-
支持快速测试,执行时间为毫秒级
-
支持正向和负向测试
-
允许测试隔离
-
支持与产品源代码位于同一位置的测试
-
支持与当前基础设施的集成
-
为每个测试生成单独的结果
-
能够轻松运行单独的测试(从命令行)
-
能够为测试失败提供最少的重现器
-
提供IDE支持
-
允许框架不断发展,包括对框架进行快速修复
-
支持测试选择和测试分组,粒度类似于
jtreg
-
允许测试任何编译目标、产品以及调试
-
允许测试依赖于平台的代码
-
提供最少的文档:操作方法 wiki 和存储库中的示例
-
允许通过修改测试的源或其他文件(例如排除列表)来从执行中排除测试
-
支持所有内部测试转换
-
支持Oracle支持的JDK 9构建平台
非目标
- 替换 Java 测试。 C++ 中的单元测试是对不同用例测试的补充。
动机
在经过充分测试的代码库中,进行更改会更容易。测试套件通过验证没有出现意外中断来支持进行更改的工程师。
如今HotSpot有很多测试,但最直接类型的测试并不多,而且编写这样的测试并不容易。
引入 C++ 测试框架是迈向更好的测试套件的第一步。 C++ 的测试框架支持使用与 JVM 相同的语言编写测试,然后将内部结构直接暴露给测试代码,与使用 java 进行功能测试相比,这为轻松编写小型尖锐测试提供了另一层可能性jtreg
。
为现有功能开发单元测试的可能性将使单独测试新功能的 C++ 代码成为可能,并且更容易为一些更深奥的问题编写回归测试。
描述
Google 测试框架 (GTest) 是最符合我们目标的 C++ 单元测试框架,它是一个 xUnit 测试框架,在社区中具有很大的吸引力。 GTest框架:
- 由其他人开发和支持
- 提供与 Eclipse IDE 的 IDE 集成
- 是一个久经考验的完整 API
- 拥有功能丰富的执行模型
- 有现有的文档和示例
- 支持 JUnit 风格的测试结果,并与 Hudson 和 Jenkins 集成
需要几个任务来允许使用 GTest 为 HotSpot 编写测试,并且需要一些额外的任务来增强它。在GTest的当前状态下:
- 使用 HotSpot 不使用且在 HotSpot 中禁用 的 C++ 结构,例如异常、模板和 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。