The types in this package are used in providing test data for JML/JUnit testing. These types constitute a small framework, and users should not hesitate to extend them in ways that are useful for their purposes.

Overview

In this package are mainly two kinds of types: iterators and strategies. These follow the design patterns of the same names. There are also some other miscellaneous types related to JML/JUnit testing.

Test data are provided to the test drivers through iterators. In this package, strategies are mainly used as ways to compute iterators for providing test data. We now describe iterators and strategies in more detail. We began by describing the iterators, because the strategies build on them. However, readers may want to skip directly to the discussion of strategies, because the strategies are most often used by testers, whereas the iterators are usually only used indirectly.

Iterators

The iterators describe the mechanisms for iterating over data of a given type. The fundamental interface for these iterators is the type IndefiniteIterator. An indefinite iterator can be thought of as a cursor that points into a sequence. Unlike normal Java iterators (such as {@link java.util.Iterator}), an indefinite iterator does not advance when the get() method is called; instead, the advance() method must be called explicitly to advance the cursor. This is useful for the loops in the test drivers, which must provide data for an outer loop's type for each iteration of an inner loop.

They are two kinds of iterators that implement or extend this interface. The first is a set of iterators that can iterate over objects (i.e., references to objects). The second is a set of iterators that can iterate over primitive values (such as booleans or ints).

Iterators That Iterate Over Objects

The concrete class ImmutableObjectArrayIterator can be used in testing objects of types that have immutable objects (such as {@link java.lang.String}). Its get() method simply returns the current object from the array of immutable objects passed to its constructor.

The abstract class CloneableObjectArrayAbstractIterator can be used in testing objects of types that have a clone() method. The get() method of this iteratoro returns a clone of the current object from the array of immutable objects passed to its constructor. Unfortunately, because Java doesn't have a standard interface with a public clone() method ({@link java.lang.Cloneable} should have this, but doesn't), there is no way to make this class concrete. Instead it relies on a cloneElement(Object) method to clone the objects of the type. The cloneElement(Object) method is specified to be "pure", and so it cannot call a clone() method that is not pure. If that is not possible, then one should use the following iterator type.

The abstract class NewObjectAbstractIterator describes a family of iterators that provide test data by creating an object each time the get() method is called. Generally this would be done to avoid aliasing between the objects used in testing. (However one can also use this kind of iterator to create intentional aliasing if desired, by returning the same object multiple times when the get() method is called.) This package does not provide any useful concrete subclasses of NewObjectAbstractIterator. However, there is an subclass that defines an empty new object iterator in EmptyNewObjectIterator, and an anonymous subclass in NewObjectAbstractStrategy.

The classes ImmutableObjectArrayIterator, CloneableObjectArrayAbstractIterator, and NewObjectAbstractIterator correspond to three of the basic strategies for producing test data, which are embodied in this package by the classes ImmutableObjectAbstractStrategy, CloneableObjectAbstractStrategy, and NewObjectAbstractStrategy, respectively. There also some classes that are convenient for constructing other indefinite iterators that return objects. These are described below.

The concrete class CompositeIterator allows one to compose several indefinite iterators into one larger indefinite iteration.

The concrete class NonNullIteratorDecorator allows one to filter out null values from an indefinite iteration. It is built as a subclass of AbstractFilteringIteratorDecorator from which you may build other filtering iterator decorators.

Iterators That Iterate Over Primitive Values

To provide efficient access to indefinite iteration elements of primitive value types, this package also provides several other implementations of the IndefiniteIterator interface. Indeed, this interface is extended by an interface for each primitive value type in Java: namely BooleanIterator, ByteIterator, CharIterator, DoubleIterator, FloatIterator, IntIterator, LongIterator, and ShortIterator. These extended interfaces iterate over values of the corresponding primitive value types. For example, ShortIterator can iterate over the primitive type short. For each such primitive value type VT, the corresponding interface VTIterator has a method, getVT(), that returns a value of the type VT. For example, ShortIterator has a method getShort() that returns a value of type short.

For each of the primitive value types, VT, the corresponding interface is implemented by an abstract class, named, VTAbstractIterator. These abstract classes simply define the get() method required by the interface using the getVT() method. For example, the get() method required by the interface ShortIterator (inherited from IndefiniteIterator) is defined to call the getShort() method, and to make the result the "boxed" version of that number, by calling the constructor for the boxed type Short.

Each such abstract class, VTAbstractIterator, is in turn extended by a concrete class named VTArrayIterator, which can iterate over arrays of the type VT. For example, ShortAbstractIterator has a subclass ShortArrayIterator, which can iterator over arrays of type short.

Also, for each such value type, VT, there is a composite iterator, VTCompositeIterator. For example ShortCompositeIterator is one such class. There is also an abstract filtering iterator decorator VTAbstractFilteringIteratorDecorator -- for example ShortAbstractFilteringIteratorDecorator. These can be used to make your own filtering decorators. An example, which also exists for each of the numeric types, is ShortNonNegativeIteratorDecorator.

Strategies

A strategy describes a way to compute an iterator. Think of strategies as embodying decisions about how to provide test data. The fundamental interface for strategies is the type StrategyType. This interface consists of a single method iterator(), which can be called to return an IndefiniteIterator object. This indefinite iterator provides the actual test data. Thus the strategy's sole responsibility is to compute this iterator. One way to do this is to provide the actual data (e.g., in an array) to the constructor of an appropriate indefinite iterator class.

In what follows we first describe how one should pick a strategy, and then describe the strategies in detail.

Choosing a Strategy

From the user's perspective, the choice of a strategy depends on the kind of test data to be provided.

Strategies for Immutable Objects or Primitive Values

For types with immutable objects that are built-in to Java, one can use a strategy that is provided in this package. For example, for the type String one would use the strategy StringStrategy. For a primitive value type, such as short one would use the corresponding strategy, such as ShortStrategy. For both kinds of strategies, the strategy itself provides only a very small set of test data. You can either use the slightly more extensive test data provided in a class like ShortBigStrategy or you can extend one of these strategies yourself by defining a subclass that overrides the addData() method to provide the additional test data. The addData() method returns an array of objects, and by using an anonymous subclass and an array allocation expression, in the body of the method override it is easy to provide test data of such types. For example, the following shows how to make a subclass of StringStrategy that provides three additional pieces of test data. Note that this is an expression in Java of type StringStrategy.

            new StringStrategy() {
                protected Object[] addData() {
                    return new String[] {
                        "Baby",
                        "Cortez",
                        "Yoonsik",
                    };
                }
            }

For types with immutable objects for which no strategy is provided in this package, the user can subclass the framework's abstract class ImmutableObjectAbstractStrategy. By default this strategy provides only the null object.

This abstract strategy is extended to by overriding the addData() method, as shown above. Examples of how to extend this strategy with your own data are found in the class ImmutableObjectAbstractStrategyTest.

Strategies for Cloneable Types

For types with mutable objects that have a clone() method, one can make a subclass of the class CloneableObjectAbstractStrategy, and provide test data by overriding the addData() method, as above. This strategy clones each object in the array of objects provided as it is returned by the get() method. By default this strategy provides only the null object.

This abstract strategy is extended to by overriding the addData() method, in the same was as the StringStrategy example above. However, subclasses of CloneableObjectAbstractStrategy must also provide a cloneElement(Object) method.

Examples of how to extend this abstract strategy with your own data are found in the class CloneableObjectAbstractStrategyTest.

Strategies for Non-Cloneable Types with Mutable Objects

For the case of a type with objects that are both mutable and not cloneable, one can make a subclass of NewObjectAbstractStrategy. One has to override the make(int) method to provide test data. This can also be done using an anonymous subclass. For example, one can write the following expression to make such a subclass and override the make(int) method.

            new NewObjectAbstractStrategy() {
                public Object make(int n) {
                    switch (n) {
                    case 0:
                        return new Person("Baby");
                    case 1:
                        return new Person("Cortez");
                    case 2:
                        return new Person("Isabella");
                    default:
                        break;
                    }
                    throw new java.util.NoSuchElementException();
                }
            }
Note that NewObjectAbstractStrategy automatically adds null as a default piece of test data, even though it is not returned the make(int) method. In essence, the indefinite iterator returned by this strategy is the composite of an iterator just returns null and one that iterates over the test data provided by the override of the make(int).

There are also a few pre-defined strategies with the same extension mechanism in this framework. These are described below.

If the type you want to provide test data for is actually {@link java.lang.Object}, then you can extend the type ObjectStrategy, in the same way as described above. This strategy, by default, provides the null object and an empty object, made with new Object(). When you extend it with appropriate data by overriding the make(int), it can be useful for testing an equals method.

An alternative to extending ObjectStrategy directly is to make a composite of this strategy and a strategy for some other type (e.g., the type whose objects you are actually testing). This is especially useful for testing the equals(Object) method, since it will provide some test data for which the equals method will return true. For example, if vMyTypeStrategy is a fields that holds a strategy for providing test data of type MyType, the the following would be an expression that computes a strategy that is a composite of ObjectStrategy and vMyTypeStrategy.

        new org.jmlspecs.jmlunit.strategies.CompositeStrategy
        (new org.jmlspecs.jmlunit.strategies.StrategyType[] {
            new org.jmlspecs.jmlunit.strategies.ObjectStrategy(),
            vMyTypeStrategy,
         }
        )
Note that vMyTypeStrategy has to already have a value for this exprssion to be well-defined; hence vMyTypeStrategy should have a definition that appears earlier than this expression in the program.

In the same ways, you can extend the type CollectionStrategy to provide test data of type {@link java.util.Collection}. By default, this strategy only provides a small amount of test data. CollectionStrategy is a good example of how to extend NewObjectAbstractStrategy to provide useful test data of some type. (See also NewObjectAbstractStrategyTest for a small example.)

Combining and Filtering Strategies

You may sometimes find it convenient to combine strategies, or to filter them. The basic way to combine strategies is to use the CompositeStrategy type. This class lets you combine two or more strategies by sequencing the test data they provide. Such composites also exist for each primitive value type; for example, you can use ShortCompositeStrategy to combine strategies that provide test data of type short, etc.

The strategy NonNullStrategyDecorator is a Decorator that takes a strategy and filters it for non-null elements; that is, it only passes along the elements that are not null, effectively removing those that are null. As a decorator, it takes a strategy as an argument to its constructor. This strategy is built from the abstract class AbstractFilteringStrategyDecorator, which you can use to build your own filtering strategy decorators. Such filtering strategies also exist for each primitive value type; for example, you can use ShortAbstractFilteringStrategyDecorator to define your own filtering decorators on strategies that provide data of type short. An example of this is found in ShortNonNegativeStrategyDecorator.

Details

They are two kinds of strategies that implement or extend the StrategyType interface. The first is a set of strategies that can be used to provide test data of reference types (subtypes of Object). The second is a set of strategies that can be used to provide test data of primitive value types (such as booleans or ints).

Strategies for Objects

The abstract class ImmutableObjectAbstractStrategy is a strategy for producing test data of types with immutable objects, like String. The strategy JMLCollectionStrategy is built from this class.

The abstract class CloneableAbstractStrategy is a strategy for producing test data for types with mutable objects that have a clone() method. The strategy JMLTypeStrategy is built from this class.

The abstract class NewObjectAbstractStrategy is a strategy for producing test data for types with mutable objects that do not have a clone() method.

Strategies for Primitive Values

For each of the primitive value types, VT, there are two corresponding strategies: VTStrategy and VTBigStrategy. For example, for the primitive value type short there are the strategies ShortStrategy and ShortStrategy. The classes with names of the form VTStrategy provide the barest minimum of test data. For the integral types this consists of just the numbers 0, 1, and -1. The floating point types provide these along with the "not a number" value of the type. The classes with names of the form VTBigStrategy provide a slightly larger amount of test data; for the numeric types this includes the numbers 3, -5, and the minimum and maximum values of each type. For the floating point types, the VTBigStrategy classes also include the various infinities and smallest values.

Making Extensible Strategies Yourself

You may sometimes find it convenient to make a strategy extensible yourself, in order to allow further specialization. For immutable and cloneable objects, you can do this in the same way as many of the standard strategies by using the ImmutableObjectExtensibleStrategyDecorator or the CloneableObjectAbstractExtensibleStrategyDecorator. These decorators are built from AbstractExtensibleStrategyDecorator, which may be useful for more general cases. Versions of such extensible strategy decorators also exist for all the primitive value types; for example, there is ShortExtensibleStrategyDecorator.

For types with mutable objects that have no clone method, you can subclass NewObjectAbstractExtensibleStrategyDecorator. For example, ObjectStrategy is built as a subclass of this type.

Miscellaneous Types

The class LimitedTestSuite is used in testing to "throttle" the tests. It is a kind of test suite that accumulates test data up to a certain point, and then becomes "full". Its addTest method throws a TestSuiteFullException when a test is added to a full test suite.

The class ConstructorFailed is used to track errors that happen during test suite construction. These typically result from a constructor that is implemented incorrectly or doesn't satisfy its specification. When called during construction of the test suite, i.e., when the failure is part of providing test data, then the problem is recorded as an instance of this class. Such a test, when run, simply reports the problem it stores.

The abstract class IteratorAbstractAdapter can be used to adapt an iterator to the indefinite iterator protocol. This is an abstract class because iterators do not provide a clone() method.

The class IndefiniteIteratorUtilities has various method that are used in various JUnit test classes in this directory for testing the strategies. The class CharStrategyTypeTest and the classes such as ShortStrategyTypeTest use these classes in their testing of the strategies.

The class TracingTestSuite is another subclass of {@link junit.framework.TestSuite}. It is primarily intended for debugging.

Coding and the Makefiles

The main problem of coding in this package is how to avoid duplication between value types, for example between ShortIterator and LongIterator. The way this is done is by coding these types once, and using a sed script to make the different versions of a type. For example, ShortIterator and LongIterator, as well as several other types are generated by the file _ValueType_Iterator.java-generic using the script _ValueType_.sh. Fragments of code that must be different in each type are defined using one of the strings in the sed script. Thus, it is important, when making changes to these types, to change the corresponding .java-generic file, instead of the generated .java files. The Makefile is designed to make the generated files read-only to prevent editing the wrong files.

With the Makefile, you can use make generate to generate the files from the scripts. See the targets in the makefile and the definition of the VALUETYPEGENERICS macro for all of the generics.

Note, however that the types named, ...Strategy and ...BigStrategy are not generated from a single file, but rather handcrafted for each type. For example ShortStrategy and LongStrategy, are written in this way.

The source code for this package uses the GNU Lesser General Public License, since it is part of the JML runtime libraries.

Acknowledgments

These types were originally designed by Gary T. Leavens. David Cok helped by running ESC/Java 2 on the types and prompting corrections.

At Iowa State, the development of JML was partially funded by the (US) National Science Foundation under grants CCR-9503168, CCR-9803843, CCR-0097907, and CCR-0113181.