|
|||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |
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. |
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:
org.jwalk
is the entry point into the
JWalk component kit.org.jwalk.core
contains the core JWalk
test engine.org.jwalk.out
contains the test reports
emitted by JWalk.org.jwalk.gen
contains custom test
input generators.org.jwalk.test
contains sample test
classes.org.jwalk.tool
contains finished JWalk
testing tools.org.jwalk.tool
.
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:
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 CustomGenerator
s, 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 CustomGenerator
s are always loaded by the
JWalk 1.1 tools in the standard configuration, as delegates
of ObjectGenerator
. These include:
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:
boolean canCreate(Class<?> type)
, reporting whether
the generator can create values of the requested type;Object nextValue(Class<?> type)
, to synthesise the
next value of the requested type in the expected order; andvoid setOwner(MasterGenerator generator)
, to register its
owning MasterGenerator.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
.
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.
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.
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.
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.
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.
|
|||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |