"Runnability" testing of Java programs

A unique aspect of Java technology is that programs can run anywhere without recompilation. Here's how to test your programs so that you can find any runnability problems before your customers do

The word portability has, as its root, the verb "to port," which in software terms means to adapt and recompile software to run on a different computing platform. Portability is the ability of a program to be recompiled for another platform. Because creating and maintaining several independent versions of a program is very expensive, portability has long been one of the holy grails of the software industry. Software vendors would like to be able to develop and maintain a single version of their product that can run on many different systems and serve many different market segments.

Previous efforts to enable portability have included a multitude of standards efforts, some interpreters, and a few portable runtime systems. Most have required recompilation; none have achieved mainstream success. In the meantime, economic pressures on software developers and the urgency of delivering a multiple-platform solution in a timely fashion have made the goal more desirable than ever.

Portability and Unix

The Unix programming environment, as codified in the POSIX standards, took a major step toward portability by delivering "source code" portability. Basically, a software product that runs on one POSIX platform can be ported to a new POSIX system by recompiling the source programs -- ideally, with no required source code changes. In reality, source code changes and the consequent burden of multiple versions are often required and unavoidable.

Portability and X Window

Other systems -- such as the X Window System -- address portability issues by providing a layered architecture that isolates system-dependent functionalities. The X architecture successfully abstracts the details of the window and display system. A program using the X Window System can use any X Window display hardware, although that program is still tied to a specific instruction set and operating system architecture.

Portability and Java

Neither the Unix or X Window efforts went far enough to provide universal portability. The Java programming environment, however, makes a significant step toward achieving this elusive goal. From the beginning, Java technology was intended to provide a programming environment that supports the write-once, run-anywhere (WORA) concept. Java technology has largely delivered on this promise by ensuring that Java programs will run across all Java-enabled platforms. Achieving this goal has reduced system dependencies to a very large degree.

When faced with a new computer architecture, a Java program does not need to be ported to it, or recompiled -- one delivery format serves as a universal program representation, usable on all Java-enabled computers. This capability has been achieved by a combination of a well-documented binary representation, the class file format, and a class library that makes the underlying platform functionality available through a universal abstraction layer, the Java Core APIs. We, therefore, prefer to speak of a Java program's runnability rather than its portability. A Java program does not need to be ported to a new computer -- it can simply be run. A Java program can contain constructs, however, that will deprive it of this property of runnability across all platforms. It is possible to unintentionally write a platform-specific Java program, since not all digital systems can be perfectly abstracted. For example, the file-naming convention in operating systems and display resolutions in hardware devices present different behaviors that may impede runnability.

It is also possible to use the Java programming language to write a program that is intentionally tied to a specific platform. If the nonportability of a program is intentional, there is little need to discover it by testing. Such programs fall outside the scope of this article.

In this article we discuss aspects of Java programs that need to be carefully examined and tested to identify where runnability problems may occur, and how to test for runnability. We'll also discuss how to measure the runnability of a Java program, keeping in mind the question: How well does the program deliver on the WORA promise? First, we'll examine some of the details of runnability, then we'll discuss how runnability testing can be incorporated into the software quality assurance process.

Differences in Java environments

In one vision of a perfect world, no testing would be necessary, and there would be no decision to make about the target environments for a program. All computer platforms' Java Runtime Environments (JREs) would be exactly the same, and a program that worked on one computer would work identically on all other computers. But that vision isn't the one promised by Java technology; it is, in fact, counter to the premise on which Java technology is based.

That premise is that the Java Runtime Environment represents the behavior of all computers with a common abstracted interface. However, the details of the actual computer must still show through the interface in some places -- so Java technology doesn't pretend to offer exactly the same behavior on all computers. Different computing platforms have different behavior, and Java technology makes it possible for Java programs to adjust to these differences.

We'll discuss how these differences may occur, what effect they may have on the behavior of a Java program, and how to ensure that the program fulfills its intended functionality on various platforms. The measurements we suggest will also help to delimit and control any expected differences in behavior.

Platform differences

The specification of the Java Runtime Environment (JRE) -- that which a program may depend on -- is quite complete, but it is not hermetically sealed. Platform differences visible from within Java programs include the size and appearance of GUI elements (as abstracted by the Abstract Windowing Toolkit (AWT)), the syntax of file names, and the details of thread-scheduling behavior. For example, a program that looks great on a 1024x800 24-bit display may not be usable on a 640x480 8-bit display. A program that looks great at one window size may, if certain crucial buttons aren't displayed, be unusable at another. A program that depends on a specific font may not work at all on a machine that doesn't have that font installed. Similarly, the syntax of a file name varies widely among the various Java platforms. The java.io.File class provides a useful level of abstraction that, if used correctly, can insulate your program from these platform specifics. However, if you write

    File t = new File("/dev/tty");

you can't expect your program to work on anything but a Unix machine (or, at least, a POSIX machine). (See Sidebar 1 for the source code for a helper class that makes it easy to use java.io.File in a portable way.) A more subtle kind of platform variation occurs in thread scheduling. The Java Language Specification1 provides quite a lot of detail about what may be expected from the thread scheduler of all JRE implementations. Due to the nature of multithreaded programming, however, it's entirely possible to write a program that will run under one scheduling policy but will either hang or produce invalid results under another policy. (See Sidebar 2 for examples of code that will work under some, but not all, scheduling policies.)

Despite these differences, it's entirely possible to write Java programs that operate correctly on all Java platforms. The purpose of runnability testing is to raise a programmer's confidence that deploying the tested programs to a wide spectrum of computers won't result in disaster.

1 2 3 4 Page 1
Page 1 of 4