blob: 51ef75f1d9d904f42a8a9d493a2be9d2ad0290cb [file] [log] [blame] [view] [edit]
# FuzzTest Implementation Notes
IMPORTANT: this document is an internal team document for extending
FuzzTest. Do not extend your own types for FuzzTest without first
consulting the
FuzzTest team.
Instead, use the existing domains or combinators of existing domains as noted
within the [Domains Reference](../domains-reference.md).
FuzzTest is an extensible framework. This guide documents how to extend
FuzzTest to fuzz types appropriately and intelligently using custom
domains.
## Domains
This is a skeleton for a Domain implementation. Note that a Domain object is not
designed to retain state, but only to create an instance of a type, and provide
mutation services for an existing object of that type.
Conceptually, a Domain represents a space of possible values, and a method for
traversing that space. A concept of 'nearness' is useful if possible; for
example, a Domain that mutates the string `cheese` to `chease` is useful in a
way that one which is as likely to mutate it to `xyzzy` is not.
```c++
template <typename T>
class MyDomain {
public:
using value_type = T;
MyDomain(/* params */) {
/* set up space of possible values here */
}
T Init(absl::BitGenRef prng) {}
void Mutate(T& val, absl::BitGenRef prng, bool only_shrink) {}
private:
/* params stored here */
}
```
### Init
```c++
T Init(absl::BitGenRef prng);
```
The `Init` method returns an arbitrary value in the domain. This may be, for
example, an empty container for a container type, or a random element for an
`ElementOf` domain. Note that this is separate from a *constructor*; a
constructor sets up a Domain, and `Init` produces an initial value from it.
### Mutate
```c++
void Mutate(T& val, absl::BitGenRef prng, bool only_shrink);
```
The `Mutate` method makes a small change on an existing value. The `only_shrink`
parameter is used for case reduction; the precise semantics of what it means to
grow or shrink a value of a given type are type-specific.
There is no guarantee that `val` is within the Domain's possibility space. For
example, a Domain representing positive integers could be given the number -1.
In cases where this is possible, it is recommended that `Mutate` simply call
`Init` on nonconforming inputs.
## Custom `corpus_type`
Domains have a `value_type`; in many cases--for example, a domain consisting of
integers--this value uniquely defines a point in the space of possible values.
However, other types may not allow for this; for example, a domain which chooses
one element from a list where they're not all necessarily unique. In this case,
the domain can have a `corpus_type` representing its internal state, and a
`value_type` representing its output.
The `DomainTraits` class, which takes a `Domain` as a type parameter, wraps this
idea. If your `Domain` sets `has_custom_corpus_type` to `true`, you can set a
custom `corpus_type` property different from the `value_type`, and add a
`GetValue` function to convert a `corpus_type` value into the corresponding
`value_type`.
For example, consider a domain which represents strings generated by a regular
expression. It's much easier to mutate an explicit DFA path than to convert a
string back and forth, so we use `std::string` as the `value_type` and a
representation of the path as the `corpus_type`.
```c++
class RegexDomain {
public:
using value_type = std::string;
static constexpr bool has_custom_corpus_type = true;
using corpus_type = DFAPath;
MyVectorDomain(std::string regex) : dfa_(DFA::Parse(regex)) {}
corpus_type Init(absl::BitGenRef prng) {
return dfa_.Generate(prng);
}
void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) {
dfa_.Mutate(prng, val);
}
value_type GetValue(const corpus_type& value) const {
static_assert(has_custom_corpus_type);
return dfa_.ToString(value);
}
private:
DFA dfa_;
}
```
Whether or not your domain (say, `MyDomain`) defines these explicitly, you can
evaluate:
| Function | Default value |
| ------------------------------------------------ | ---------------------- |
| `DomainTraits<MyDomain>::has_custom_corpus_type` | `false` |
| `DomainTraits<MyDomain>::corpus_type` | `MyDomain::value_type` |
| `DomainTraits<MyDomain>::GetValue(Domain& d, | Returns `value`. |
: const corpus_type& value)` : :