Jacks: Java compatibility testing, the open source way
An automated Java compiler regression test suite from the Jikes project
Maya Stodte
Contributing editor, developerWorks
February 2001
Maya Stodte takes a look at Jacks, the regression
testing suite from the people at Jikes. She goes through the
installation process on UNIX and Windows, talks about how to run the
tests and interpret test results, and gives a code example from the
project leader, Mo DeJong, of how to add new tests to the suite. She
also talks with some of the people behind the project about Jacks and
how Java compiler projects are making use of the scripting language
based implementation..
Ever wish that Sun would finally make good on its promise and hand
the Java Compatibility Kit over? Despairing of that ever happening, the
people at the open source Jikes project have developed Jacks, a Java
test suite. "Regression testing," according to Mo DeJong, the principal
designer of Jacks, "is nothing new. The gcc and gdb projects make use
of a Tcl-based automated regression testing framework called DejaGnu.
While the DejaGnu framework is very powerful, it is also rather complex
and a bit inflexible. DejaGNU has a number of features that are just
not needed for something simple like testing a Java compiler. In
developing Jacks, I wanted to keep the good ideas in DejaGnu without
carrying along the extra baggage."
What is Jacks?
The Jacks test suite checks a Java compiler's conformance to the JLS
(Java Language Specification). It's made up of a large number of small
test cases, with each test focused on a specific section of the JLS.
Eric Blake, a principal contributor to the Jacks project, describes the
benefits of this type of testing in terms of its detail-oriented scope.
"By generating small test cases with specified compilation behavior,
then automating the execution of each of these cases, a compiler writer
or debugger can quickly pinpoint problems in the translation of Java
source to bytecodes."
The concept behind the development of Jacks is to simplify running
tests with multiple compilers or multiple compiler configurations (a
single set of tests, for example, against the last two releases of
Jikes and the JDK 1.3 release of Javac). By hand you'd have to
repeatedly set environmental variables and check test results against
expected results. Using Jacks, you simply cd to the directory that
holds the tests, invoke the Jacks framework, and indicate which
compiler configuration should be used.
Sun's broken promises to Java developers helped motivate the Jikes team
to get the Jacks project up and running. Sun has repeatedly stated that
it would turn Java, the JCK (Java Compatibility Kit), and related Java
technology over to a standards body. Because this has yet to happen,
developers working on Java projects can't use the JCK to test for
regressions in day to day development. And when faced with an
artificial scarcity of code because of unreasonable license
restrictions, they tend to simply replace the old systems with new and
better ones. This is exactly what has happened with Jacks. (Although
Jacks is hosted by developerWorks, it is covered by the GPL, not the
IBM Public License.)
Working with Jacks
Jacks is written in Tcl, so you want to make sure you have Tcl 8.3.
(You want the 8.3 version to ensure you have the tcltest extension and
Unicode support, both of which Jacks requires). You can download the
installer for Windows and the RPM for Red Hat x86, or simply build from
the source code. See the Resources
section later in this article if you're not sure where to download
from; if you're using Red Hat 7, it's likely that Tcl 8.3 is already
installed.
Once Tcl is installed, you need to fetch Jacks from the CVS. Then
configure Jacks by including your compiler(s) path name(s) in Jacks'
_setup configuration file for the compiler(s) you want to test. You
will need one _setup file for each configuration you want to support.
For example, Jacks comes with a javac_setup file. You would edit that
file to set the path for javac. "The hardest part," according to Eric
Blake, "was figuring out how to test Jikes, since I had JIKESPATH set
in my environment. But I figured out what to change in the jikes_setup
configuration file and all was well."
Fetching the Jacks source code from the CVS module
setenv CVSROOT :pserver:anoncvs@oss.software.ibm.com:/usr/cvs/jikes
cvs login
paswsd anoncvs
cvs checkout jacks
|
All code samples are taken from the Jacks home page (see Resources) unless otherwise noted.
You can use Jacks with as many compilers or compiler configurations
as you like. To get rid of a configuration for a compiler, simply
delete its _setup file.
Once you've fetched the source code from the CVS, you'll want to
include the top Jacks directory on your path so that you can run the
Jacks shell script. As a preliminary precaution, you should run the
shell script with no arguments to make sure everything is configured
properly.
If everything is working, you'll see a listing of the command line
options that the Jacks script accepts. If you get an error, check that
the executable tclsh8.3 can be found on your path. Windows users will
need to run tclsh83 directly, and pass it the jacks.tcl argument before
the normal flags. You should also consider installing the Cygwin UNIX
compatibility layer so that, like UNIX users, you can run Jacks with
the provided shell script. The following instructions assume you are
using the shell script.
For a test example, you might want to run all the tests in a given
subdirectory using the Jikes compiler with the following commands:
% cd tests/jls/packages/package-declarations/unnamed-packages
% jacks jikes
|
Developing a new regression test
Developing
a new Jacks test case is straightforward. "Basically," in Eric Blake's
words, "you design a simple source file to test a problem, put it in
the specified Jacks format, then run Jacks. If the compiler results
don't match the expected result, it prints an error." Here's an example
from the tutorial on adding a new test case on the Jacks home page:
// File SynchronizedInterface.java
public synchronized interface SynchronizedInterface {}
|
When compiled with Jikes, the following error is generated:
% jikes SynchronizedInterface.java
Found 1 semantic error compiling "SynchronizedInterface.java":
3. public synchronized interface SynchronizedInterface {}
<---------->
*** Error: synchronized is not a valid interface modifier.
|
A quick look at section 9.1.1 of the JLS indicates that synchronized
is not a legal modifier in this context. If you try to compile this
same class with the Javac compiler from an early release of the JDK, no
error is generated (this bug was fixed in later releases).
% javac SynchronizedInterface.java
|
Now that the problem can be reproduced, you can add a regression test case to the Jacks test suite by:
- Figuring out what directory the test case should go in
- Writing the regression test
- Running the new test in the Jacks framework
The format of a regression test in the tcltest framework is:
tcltest::test NAME DESCRIPTION {
COMMANDS
} EXPECTED_RESULT
|
This is the first test for section 9.1.1 of the JLS, so the NAME is 9.1.1-1.
The test case belongs in the directory
tests/jls/interfaces/interface-declarations/interface-modifiers (the
location is based on the name of the JLS section).
The DESCRIPTION can be anything you want it to be.
The COMMANDS section contains any Tcl commands, but the saveas and compile methods from Jacks are all you'll need most of the time.
The saveas command takes two arguments: the file name and the data to be saved in the file:
saveas SynchronizedInterface.java \
{public synchronized interface SynchronizedInterface {}}
|
The compile command takes any number of command line arguments and passes them to the java compiler. It will return PASS, FAIL, or WARN to indicate the exit status of the compiler.
The EXPECTED_RESULT is the result you expect from the compile command.
In this interface example, the compile is expected to fail. So the complete regression test would look like this:
tcltest::test 9.1.1-1 {should generate error on synchronized interface} {
saveas SynchronizedInterface.java \
{synchronized interface SynchronizedInterface {}}
compile SynchronizedInterface.java
} FAIL
|
Examining results
Running the tests and checking the results is completely automated, so
you can actually just sit back and watch the results roll in. The Jacks
framework will recursively descend through test directories and run all
of the tests it finds.
If nothing goes wrong, nothing is printed. If a test fails, a description of the failure will be printed as shown in Listing 1
by Mo Dejong. This example demonstrates a bug in Javac because the
first constructor calls the second, and the second calls the first. The
JLS states that this is illegal (section 8.8.5.1), so a Java compiler
must signal an error if this case is detected.
Let's see how Jikes does on this same test case. In Listing 2
we'll use a feature in Jacks that allows you to pass a pattern as the
third argument to the jacks script. Test cases with names that do not
match the pattern will be skipped. In this trivial case, the pattern is
simply the name of the test case. In this example, note how the one
test case we were interested in passed and the others were skipped.
The above output shows that the bug found in the Javac compiler does
not exist in Jikes.
While human readable results are very useful, they can quickly
become hard to manage when you have a lot of test cases to deal with.
Jacks recently celebrated a major milestone because it now contains
over 1,000 individual JLS test cases. With this many test cases, nobody
could be expected to remember which ones passed and which ones failed
at any one point in time. But fear not, Jacks includes a set of logging
and test result analysis features that track test results over time.
This is a critical feature, since it provides a way for Java compiler
developers to track the status of bug fixes and possible regressions.
How Jacks was written, and why Tcl was used
A scripting language is the natural choice when implementing a testing
suite like Jacks, and Tcl was used for a number of reasons:
- Tcl is open source, so it'll still be around in the future.
- It's easy to install, and scripts don't need to be compiled.
- It's easy to read and write, and scripting languages are far easier to learn than C/C++.
- It's easy to use the string manipulation and regular expression features.
- It's highly portable and runs on more platforms than Java.
- It's been used successfully in
thousands of organizations over the last decade.
- Ironically, it used to be a Sun project :)
"One of Jacks' best features", according to Mo DeJong, "is the self
generating documentation. On the Jacks home page you'll find a link to
a test case index page which lists all of the available test cases.
It's indexed and cross-referenced in a number of useful ways. You can
easily look up a test case by name or find out how well a given JLS
chapter is covered by the existing tests. And Tcl's highly dynamic
language features made implementing self-documenting test cases easy
To date, Jacks supports the following Java compilers:
- JDK 1.3 (1.1 and 1.2 also work but are out of date)
- Jikes, the Open Source Java compiler from IBM
- Kaffe, which makes use of the Kopi compiler
- GCJ, the Java front end to gcc
Improving Java compilers everywhere
Jacks
was initially focused on providing compiler testing for the Jikes
project only. The original goal was to replace a homemade test system
that was created for Jikes but had been abandoned because it was too
hard to set up and use. It soon became clear that if the test suite was
made a bit more generic, it could also be used by the other Java
compiler projects. That could lead to an increase in the number of
submitted test cases. At the very least, it couldn't hurt to have other
Java experts evaluating the correctness test cases.
Naturally, the Jikes project makes heavy use of Jacks, but what about
the GCJ and Kopi compiler projects? Tom Tromey, a resident Java guru at
Red Hat, already sees the effect of Jacks development on the GCJ
project. "Jacks already has actual usefulness to the GCJ project. I run
Jacks whenever I make a front end compiler change, and I regularly use
Jacks to find bugs in GCJ. I've found adding tests to be trivial. The
framework seems easy to use and well thought out."
After running Jacks for the first time, Thomas Graf, Project Manager
for the Kopi compiler, may also have become a devotee. "The first run
resulted in 169 failed tests. After applying a few fixes based on the
analysis of some of the failed tests, 147 tests fail. These results are
very encouraging (for Jacks:). Conclusion: the Jacks test suite is
really a very valuable tool for improving the quality of our compiler!"
The Jacks team
While a number of people have contributed to the Jacks project, the
majority of the code contributions have come from Mo DeJong and Eric
Blake. Mo DeJong works for Red Hat Inc. While not biking around the San
Francisco area, Mo works on Red Hat's Source-Navigator IDE. Eric Blake
is a student at Brigham Young University. Eric works in the
Configurable Computing Lab as
a research programmer. Eric's favorite thing about open source software
is that if you don't like it, you can fix it yourself. Both made
substantial contributions to the writing of this article.
Resources
About the author
Maya Stodte is a contributing writer and editor for developerWorks. She can be reached
at mstodte@pop.rcn.com.
|