什么是 Jacks? Jacks 测试套件检查 Java 编译器是否符合 JLS(Java 语言规范)它由大量小测试案例组成每个测试都侧重于 JLS 中特定的部分Eric Blake 为 Jacks 项目作出了很大贡献他从面向细节领域描述了这种类型测试的好处通过生成带有指定编译行为的小测试案例然后将每个案例的执行自动化编译器作者或调试者可以快速找出 Java 源码到字节码转换中存在的问题
开发 Jacks 背后的概念是要简化对多编译器或多编译器配置所运行的测试(例如对上两个发行版的 Jikes 和 Javac 的 JDK 发行版所进行的一组测试)如果手工进行您必须重复地设置环境变量然后根据所期望的结果来检查测试结果而通过使用 Jacks只需要更改到存放测试的目录 调用 Jacks 框架然后表明应该使用哪个编译器配置
Sun 没有履行对 Java 开发者所做的承诺激发了 Jikes 小组对 Jacks 项目的设置和运行Sun 再三声明它会把 JavaJCK(Java Compatibility Kit)和相关 Java 技术交到一个标准主体的手中但因为这还没有实现从事 Java 项目的开发者就不能使用 JCK 来对日常的开发进行回归测试当面对由于不合理的许可证限制而导致的代码人为不足时他们倾向于用新的更完善的系统来替换旧系统这就是发生在 Jacks 上的故事(尽管 Jacks 由 developerWorks 主持它受 GPL 而非 IBM Public License 约束)
使用 Jacks
Jacks 是以 Tcl 编写的因此需要确保拥有 Tcl (需要版本 来确保具有 tcltest 扩展和 Unicode 支持这两者都是 Jacks 所必需的)可以下载用于 Windows 的安装程序和用于 Red Hat x 的 RPM也可以更方便地从源代码中构建如果您不知道到什么地方下载请参阅本文稍后的 参考资料 部分如果使用的是 Red Hat 很可能已安装了 Tcl
安装了 Tcl 后需要从 CVS 取出 Jacks然后通过将编译器路径名包括在要测试的编译器的 Jacks _setup 配置文件中来配置 Jacks对于每个希望支持的配置都需要一个 _setup 文件例如Jacks 带有 javac_setup 文件需要编辑该文件来为 javac 设置路径Eric Blake 说最困难的部分是断定如何测试 Jikes因为我在环境中已设置了 JIKESPATH但我想出了要在 jikes_setup 配置文件中更改什么内容一切都很顺利
从 CVS 模块中取出 Jacks 源代码
setenv CVSROOT :pserver:anoncvs@:/usr/cvs/jikes
cvs login
paswsd anoncvs
cvs checkout jacks
可以对数量不限的编译器或编译器配置使用 Jacks要除去某一编译器的配置只需要删除其 _setup 文件
从 CVS 中取出源代码后就需要在路径中包括顶层 Jacks 目录这样才能运行 Jacks shell 脚本为谨慎起见最初运行 shell 脚本时应该不带任何自变量以确保每项都经过正确配置
% jacks
如果一切正常将看到 Jacks 脚本所接受的命令行选项的清单如果收到错误请检查在路径中是否能找到可执行文件 tclshWindows 用户需要直接运行 tclsh并将 jackstcl 自变量在一般标志之前传递给它还应该考虑安装 Cygwin UNIX 兼容性层这样象 Unix 用户一样您就可以使用提供的 shell 脚本来运行 Jacks 了下面的指令假设您使用的是 shell 脚本
对于测试示例需要使用 Jikes 编译器来运行给定子目录中的所有测试命令如下
% cd tests/jls/packages/packagedeclarations/unnamedpackages
% jacks jikes
开发新的回归测试
开发新的 Jacks 测试案例非常简便照 Eric Blakes 的话说基本上您设计一个简单的源文件来测试问题将它放在特定的 Jacks 格式中然后运行 Jacks如果编译器结果与所期望的结果不一样它打印出错误这里是 Jacks 主页上教程中有关添加新测试案例的一例
// File SynchronizedInterfacejava
public synchronized interface SynchronizedInterface {}
使用 Jikes 编译时生成以下错误
% jikes SynchronizedInterfacejava
Found semantic error compiling SynchronizedInterfacejava:
public synchronized interface SynchronizedInterface {}
<>
*** Error: synchronized is not a valid interface modifier
如果很快看一下 JLS 的第 节会发现 synchronized 在该上下文中不是合法的修饰符如果尝试使用早期发行版 JDK 中的 Javac 编译器来编译相同的类则不会生成错误(该错误在稍后的发行版中得到修正)
% javac SynchronizedInterfacejava
现在既然问题得以重现可以通过以下步骤来对 Jacks 测试套件添加回归测试案例
了解应该将测试案例放在哪个目录中
编写回归测试
在 Jacks 框架中运行新测试
tcltest 框架中回归测试的格式是
tcltest::test NAME DESCRIPTION {
COMMANDS
} EXPECTED_RESULT
这是 JLS 第 节中的第一个测试所以 NAME 是
该测试案例在目录 tests/jls/interfaces/interfacedeclarations/interfacemodifiers (位置基于 JLS 节的名称)中
DESCRIPTION 可以是任何想要的内容
COMMANDS 一节包含了所有 Tcl 命令但大多数情况只需要 Jacks 中的 saveas 和 compile 方法
saveas 命令使用两个自变量文件名和将保存到文件中的数据
saveas SynchronizedInterfacejava \
{public synchronized interface SynchronizedInterface {}}
compile 命令使用任意数量的命令行自变量并将它们传递给 Java 编译器它将返回 PASSFAIL 或 WARN 来表明编译器的退出状态
EXPECTED_RESULT 是希望从 compile 命令获得的结果
在该接口示例中编译应该不成功因此完整的回归测试应该类似于
tcltest::test {should generate error on synchronized interface} {
saveas SynchronizedInterfacejava \
{synchronized interface SynchronizedInterface {}}
compile SynchronizedInterfacejava
} FAIL
检验结果
运行测试并检查结果是完全自动的因此可以真正地休息一下看看出现的结果Jacks 框架在测试目录中递归下降运行它所找到的所有测试
如果一切正常就不打印任何消息如果测试失败将打印有关失败的描述如 Mo Dejong 在 清单 中显示的那样该例演示了 Javac 中因为第一个构造器调用第二个构造器第二个又调用第一个所造成的错误JLS 规定这是非法的(第 节)因此如果检测到这种情况Java 编译器必须用信号通知该错误
让我们看看 Jikes 对于同一测试案例是如何做的在 清单 中我们将使用 Jacks 中的一些特性可以让您将模式作为 Jacks 脚本的第三个自变量传递将跳过那些名称与模式不匹配的测试案例在这个小案例中模式就是测试案例的名称在该例中请注意我们所感兴趣的那个测试案例是如何通过的其它测试案例是如何跳过的上面的输出表明在 Javac 编译器中找到的错误在 Jikes 中并不存在
尽管人类可读的结果非常有用但在您有许多要处理的测试案例的情况下它们很快就会变得非常难于管理Jacks 最近庆祝了一个重要的里程碑现在它包含了逾 个 JLS 独立测试案例有了这么多的测试案例没人能够记住在某一时刻哪些案例通过了哪些又失败了但不用害怕Jacks 包括了一系列记录和测试结果分析特性能够随时间跟蹤测试结果这是一项关键特性因为它为 Java 编译器开发者提供了一种跟蹤错误修正状态和可能回归的方法
如何编写 Jacks以及为什么使用 Tcl
当实现例如 Jacks 这样的测试套件时脚本语言是个很自然的选择而使用 Tcl 也有以下几个原因
Tcl 是开放源码因此在今后的一段时间内仍然会继续存在
易于安装不需要编译脚本
易于读写脚本语言远比 C/C++ 更易于掌握
易于使用字符串处理和常规规则表达式特性
高度可移植在比 Java 多的平台上运行
过去十年中成功地在几千个组织中使用过
具有讽刺意味的是它曾是 Sun 项目 :)
Mo DeJong 说Jacks 最了不起的一个特性是自生成文档 在 Jacks 主页上您可以找到到达测试案例索引页面的链接这些页面列出了所有可用的测试案例它以几种有用的方式进行索引和交叉引用可以方便地通过名称查找测试案例也可以通过现有测试来发现某个 JLS 章节的内容是多么完善Tcl 高度动态的语言特性使自记录测试案例的实现更容易
到目前为止Jacks 支持以下几种 Java 编译器
JDK ( 和 也可以使用但已经过时了)
JikesIBM 的开放源码 Java 编译器
Kaffe利用了 Kopi 编译器
GCJ到 gcc 的 Java 前端
随处改进 Java 编译器
Jacks 最初着重只为 Jikes 项目提供编译器测试原来的目标是要替换为 Jikes 创建的自制测试系统但这个初衷由于太难建立和使用而被放弃了人们很快发现如果测试套件变得更常规一些就可以为其它 Java 编译器项目使用这样将会导致已提交测试案例在数量上的增加至少让其它 Java 专家评估一下正确性测试案例也并无大碍
Jikes 项目自然大大利用鑒了 Jacks但 GCJ 和 Kopi 编译器项目又如何呢?Tom TromeyRed Hat 的常任 Java 领导者已经意识到了 Jacks 开发对于 GCJ 项目的作用Jacks 对于 GCJ 项目已经有了实际意义每当我在进行前端编译器更改时就会运行 Jacks并定期使用 Jacks 查找 GCJ 中的错误我发现添加测试是桩小事框架非常易于使用考虑也很周到
在第一次运行了 Jacks 后Kopi 编译器的项目经理 Thomas Graf 也成了一个拥戴者第一次运行产生了 个失败的测试在基于对某些失败测试的分析而应用一些修正后失败的测试数为 个这些结果非常振奋人心(对于 Jacks:)结论是Jacks 测试套件对于提高编译器的质量来说确实是一种非常有价值的工具!