Fuzzer generates method input values to improve method coverage or find unexpected errors. In UTBot next strategies can be used to find values like:
- Default values for objects, e.g. 0, 0.0, empty string or null values.
- Corner case values of primitives types, e.g. Integer.MAX_VALUE, Double.POSITIVE_INFINITY, etc.
- Method constants and their simple mutations of primitive types.
- Objects created via its constructors or field mutators.
After values are found fuzzer creates all its possible combinations and runs methods with these combinations.
For example, if a method has two parameters of types boolean
and int
the follow values can be found:
boolean = [false, true]
int = [0, MAX_VALUE, MIN_VALUE]
Now, fuzzer creates 2 * 3 = 6
combinations of them:
[false, 0], [false, MAX_VALUE], [false, MIN_VALUE], [true, 0], [true, MAX_VALUE], [true, MIN_VALUE]
To find more branches of execution as fast as possible fuzzer also shuffles combinations and supplies them for the running.
Fuzzer requires model providers that create a set of UtModel
for a given ClassId
.
Fuzzer iterates through these providers and creates models, which are used for generating combinations later.
Each combination contains concrete values that can be accepted by the method.
For example, if a method has signature with String, double, int
as parameters then fuzzer can create combination "sometext", Double.POSITIVE_INFINITY, 0
.
Fuzzer's entry point is:
// org.utbot.fuzzer.FuzzerKt
fun fuzz(method: FuzzedMethodDescription, vararg models: ModelProvider): Sequence<List<FuzzedValue>>
FuzzedMethodDescription
stores comprehensive information about a method:
- signature (parameters and return types)
- name/package/class (optional)
- constants found in the method body (should be replaced with CGF when possible)
ModelProvider
provides models for a give parameters set as described below.
Fuzz method returns a sequence of acceptable values for the method in random order. The sequence is lazy.
Model provider should implement
fun generate(description: FuzzedMethodDescription): Sequence<FuzzedParameter>
For every parameter should exist at least one UtModel
. ModelProvider.withFallback
can be used to process those classes which cannot be processed by provider.
Several providers can be combined into one by using ModelProvider.with(anotherModel: ModelProvider)
.
Common way to generate all combinations is:
ObjectModelProvider()
.with(PrimitiveModelProvider)
// ...
.with(ObjectModelProvider)
.withFallback { classId ->
createDefaultModelByClass(classID)
}
or
// org.utbot.fuzzer.FuzzerKt
fun defaultModelProviders(idGenerator: IntSupplier)
Creates default values for every primitive types:
boolean: false
byte: 0
short: 0
int: 0
long: 0
float: 0.0
double: 0.0
char: \u0000
string: ""
Creates default values and some corner case values such as Integer.MAX_VALUE, 0.0, Double.NaN, empty string, etc.
boolean: false, true
byte: 0, 1, -1, Byte.MIN_VALUE, Byte.MAX_VALUE
short: 0, 1, -1, Short.MIN_VALUE, Short.MAX_VALUE
int: 0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE
long: 0, 1, -1, Long.MIN_VALUE, Long.MAX_VALUE
float: 0.0, 1.1, -1.1, Float.MIN_VALUE, Float.MAX_VALUE, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, Float.NaN
double: 0.0, 1.1, -1.1, Double.MIN_VALUE, Double.MAX_VALUE, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN
char: Char.MIN_VALUE, Char.MAX_VALUE
string: "", " ", "string", "\n\t\r"
Creates primitive models for boxed types: Boolean
, Byte
, Short
, Integer
, Long
, Double
, Float
, Character
, String
Uses information about concrete values from FuzzedMethodDescription#concreteValues
to generate simple values.
Only primitive values are supported.
Creates UtNullModel
for every reference class.
Creates models for any enum type.
Creates concrete collections for collection interfaces: List
, Set
, Map
, Collection
, Iterable
, Iterator
Creates an empty and non-empty for any type.
ObjectModelProvider is the most sophisticated provider. It creates model of class that has public constructors
and public mutators (fields or setters/getters). If class has constructor that accepts another object within an argument that value
is created recursively. Depth of recursion is limited to 1. Thus, for inner object fuzzing doesn't try to use every
constructor but find the one with the least number of parameters and, if it is possible, only
constructor with primitives values. If there is available only constructor with another object as a parameter then
null
is passed to it.
Let's look at this example:
class A {
private int a;
private Object object;
public A(int a, A o) {
this.a = a;
this.o = o;
}
}
For it fuzzing create these models:
new Object(0, new A(0, null));
new Object(Integer.MIN_VALUE, new A(0, null));
new Object(Integer.MAX_VALUE, new A(0, null));
new Object(0, new A(Integer.MIN_VALUE, null));
new Object(Integer.MIN_VALUE, new A(Integer.MIN_VALUE, null));
new Object(Integer.MAX_VALUE, new A(Integer.MIN_VALUE, null));
new Object(0, new A(Integer.MAX_VALUE, null));
new Object(Integer.MIN_VALUE, new A(Integer.MAX_VALUE, null));
new Object(Integer.MAX_VALUE, new A(Integer.MAX_VALUE, null));
For classes that have empty public constructor and field mutators all those mutators will be fuzzed as well. Field mutators are listed below:
- public or package-private (and accessible) non-final non-static fields
- pairs of setter/getter that satisfy the common agreement:
- setter/getter is public or package-private (and accessible)
- have field name as a postfix, e.g.:
int myField -> * setMyField(int v)/int getMyField()
, where * means any returned type
For example, fields a, b and d will be fuzzed, but c and e will not:
class A {
int a;
public char b;
public final int c = 0;
private String d;
private boolean e;
public A setD(String s) {
this.d = s;
return this;
}
public String getD() {
return d;
}
public boolean getE() {
return e;
}
}
There are several other providers that can find some values, using addition information,
like CharToStringModelProvider
that takes all chars found in charAt(i) == c
statement
and merge them into several strings.