Package org.jwalk.gen

This sub-package for JWalk 1.1 contains custom test input generators, © Anthony J H Simons, 2006-2011.

See:
          Description

Interface Summary
CustomGenerator CustomGenerator is the interface implemented by custom generators.
MasterGenerator MasterGenerator is the interface implemented by top-level generators.
 

Class Summary
EnumGenerator EnumGenerator is a custom generator to synthesise enumerated values.
InterfaceGenerator InterfaceGenerator is a custom generator of values for interface types.
PlatformGenerator PlatformGenerator is a custom generator of platform-dependent values.
RedirectInGenerator RedirectInGenerator is a custom generator that redirects standard input.
StringGenerator StringGenerator is a custom generator to synthesise string values.
 

Package org.jwalk.gen Description

This sub-package for JWalk 1.1 contains custom test input generators, © Anthony J H Simons, 2006-2011. Custom generators may be selected by the tester to control how the JWalk 1.1 tool suite synthesises test inputs. A library of custom generators is provided here, to which the tester may add their own custom generators. Several custom generators are loaded by default into the standard configuration of the JWalk 1.1 tool suite; others must be explicitly loaded. Other parts of the JWalk 1.1 tool suite are described in the following packages:

The rest of this package documentation describes the function of generators and how to construct custom generators. For information about how to load custom generators, see the package org.jwalk.tool.

Master and Custom Generators

A custom generator is any Java class that implements the CustomGenerator interface. Such a class may be loaded by any of the JWalk 1.1 tools to take control of how input values are synthesised during testing for the constructors and methods of the test class. Reasons for doing this might include:

The JWalk 1.1 tools use a combination of MasterGenerator and CustomGenerator generators to synthesise all test inputs. A MasterGenerator synthesises values using its own standard algorithms, which generate a quasi-unique value or object upon each request, but it also delegates requests to supply values of certain types to CustomGenerators, which were previously added as delegates of that master. The delegated generators may synthesise values for one type, or for a group of types, using a different custom algorithm. If the tester supplies a home-made custom generator, this can be made to synthesise values for particular types in any order desired. In this way, it is possible to tailor the testing process to meet the expectations of the test class.

The standard MasterGenerator used by the tools is ObjectGenerator. This generator synthesises all basic values in a quasi-unique monotonically increasing sequence. Values are only "quasi-unique", since boolean values will inevitably cycle between false and true. Values for other primitive types are typically unique, for example, numeric values increase steadily from 1. Object values will be uniquely instantiated using either their default constructor, or the first constructor with arguments that succeeds (the arguments are synthesised recursively). ObjectGenerator delegates the synthesis of values for certain types to custom generators.

Four standard CustomGenerators are always loaded by the JWalk 1.1 tools in the standard configuration, as delegates of ObjectGenerator. These include:

These take control of how specific types are instantiated. For example, there is no automatic means in Java for supplying instances of interface types, so this must be handled by a custom generator, which maps the interface type to a suitable concrete type and then requests an instance of this instead. Again, the default generation strategy would produce an empty string, so a custom strategy is used to cycle through a series of 26 conventional strings, sorted alphabetically.

Providing Custom Generators

If the tester wishes to supply original custom generators, the following conventions must be observed. The custom generator must satisfy the interface specified by CustomGenerator. In particular, it must implement the following abstract methods:

The Contract for boolean canCreate(Class<?> type)

This method reports whether the custom generator can synthesise instances of the requested type. It should return true for all the class types that the custom generator can handle, that is, for which it can generate instances. This method is called internally, when deciding whether to delegate a request to the custom generator. The method should return true, if the custom generator creates instances of the requested type, and false otherwise.

When the standard MasterGenerator receives a request to synthesise a value of a particular type, it checks first whether any of its delegates can create this value. If so, it forwards the request to the first delegate found in its list of delegates that is capable of creating the value. The list of delegates is ordered like a stack, such that delegates that were added most recently take priority over delegates that were added earlier by the MasterGenerator.

The Contract for Object nextValue(Class<?> type)

This method returns the next value in sequence for the requested type. Whereas by default this would be a quasi-unique, monotonically increasing value on each invocation, custom generators are free to return values in other orders, so long as the order is completely deterministic and not random. So, the sequence could skip or re-order values, or revisit values that were returned on a previous invocation. Random synthesis must be avoided, since this would defeat the capability of the JWalk 1.1 toolset to learn predictable behaviour.

The requested type must be a type that is acceptable to this custom generator, i.e. one for which canCreate() returns true. If not, the custom generator should raise a GeneratorException reporting failure to synthesise a value for the given type. The result of this method is an object, an array, or a "boxed" primitive value.

The Contract for void setOwner(MasterGenerator generator)

This method registers a MasterGenerator as the owner of this custom generator, and is automatically called when this custom generator is added as a delegate of the MasterGenerator. In this way, a custom generator may communicate with the master which owns it. This can be useful in the design of certain custom generators, which intercept requests to handle certain types, but forward some of the requests to their owner. An example is InterfaceGenerator, which maps from abstract interfaces to concrete classes, then recursively asks its owner to produce an instance of the concrete class.

A custom generator is free to ignore its owner if it so chooses. Many custom generators need not communicate with their owner. In this case, a nullop implementation of the method should be provided.

Example Custom Generators

The following is an example custom generator. IndexGenerator takes control of how values are synthesised for the int type. Instead of a monotonically increasing sequence, it generates repeating pairs of integers, for use when testing array and sequence types, which may put and get values at the same index. Repeating pairs of indices are generated, so that it might be possible to observe that the value returned by a get()-method is the value that was previously stored by a put()-method at the same index.

IndexGenerator

import org.jwalk.GeneratorException;
import org.jwalk.gen.CustomGenerator;
import org.jwalk.gen.MasterGenerator;

public class IndexGenerator implements CustomGenerator {
  private int seed = -1;
  private boolean repeat = false;
  
  public boolean canCreate(Class<?> type) {
    return type == int.class;
  }
  
  public Object nextValue(Class<?> type) throws GeneratorException {
    if (type != int.class)
      throw new GeneratorException(type);
    if (repeat) {
      repeat = false;
      return new Integer(seed);  // uses old value
    }
    else {
      repeat = true;
      result = new Integer(++seed);  // uses new value
    }
  }  
  
  public void setOwner(MasterGenerator generator) {
  }   // nullop implementation
}

This IndexGenerator returns pairs of integer values in the sequence: {0, 0, 1, 1, 2, 2, 3, 3, ...}. It works by synthesising a new value if the flag repeat is set to false, otherwise it returns the previous value. The flag flips between false and true on each call. The result is returned as a boxed-up Integer object. This is un-boxed automatically when supplied as an argument to any method expecting an int index. Notice how the generator handles the synthesis of integers locally and does not need to communicate with its owner. The implementation of setOwner() is therefore a null operation.

The following is an example of a different kind of generator, which synthesizes instances of a particular concrete DefaultListModel type whenever an interface type ListModel is requested. This is the pattern to follow, when dealing with the synthesis of concrete values for abstract interface types. The supplied InterfaceGenerator (which is always loaded, by default) works this way for a number of commonly-used collection interfaces.

ListModelGenerator

import org.jwalk.GeneratorException;
import org.jwalk.gen.CustomGenerator;
import org.jwalk.gen.MasterGenerator;

public class ListModelGenerator implements CustomGenerator {
  private MasterGenerator owner;
  
  public boolean canCreate(Class<?> type) {
    return type == ListModel.class;
  }
  
  public Object nextValue(Class<?> type) 
                             throws GeneratorException {
    if (type != ListModel.class)
      throw new GeneratorException(type);
    // return a suitable concrete type
    return owner.nextValue(DefaultListModel.class); 
  }  
  
  public void setOwner(MasterGenerator generator) {
    owner = generator;
}

Here, the custom generator needs to communicate with its owner to handle the synthesis of the concrete type, recursively. The generator therefore declares a field for its owner, which is initialised by the method setOwner(). Note that this mutually-recursive style of delegating requests only works, because the requested type has been changed. If you ask the owner for the same type, this will result in a recursive loop, since the owning master generator will delegate back to this custom generator ad infinitum.