Skip to content

Commit 25aab32

Browse files
committed
feat: document @valuepool
1 parent 0e73ce2 commit 25aab32

1 file changed

Lines changed: 164 additions & 0 deletions

File tree

docs/mutation-framework.md

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,170 @@ class SimpleClassFuzzTests {
317317
}
318318
```
319319

320+
## @ValuePool: Guide fuzzing with custom values
321+
322+
The `@ValuePool` annotation lets you provide concrete example values of any [supported type](#supported-types) (except for cache-based mutators) that Jazzer's mutators will use when generating test inputs.
323+
This helps guide fuzzing toward realistic or edge-case values relevant to your application.
324+
325+
### Basic Usage
326+
327+
You can apply `@ValuePool` in two places:
328+
- **On method parameter (sub-)types** - values apply only to the annotated types
329+
- **On the test method itself** - values propagate to all matching types across all parameters
330+
331+
**Example:**
332+
```java
333+
@FuzzTest
334+
void fuzzTest(Map<@ValuePool(value = {"mySupplier"}) String, Integer> foo) {
335+
// Strings from mySupplier feed the Map's String mutator
336+
}
337+
338+
@FuzzTest
339+
void anotherFuzzTest(@ValuePool(value = {"mySupplier"}) Map<String, Integer> foo) {
340+
// Strings from mySupplier feed the Map's String mutator
341+
// Integers from mySupplier feed the Map's Integer mutator
342+
}
343+
344+
@FuzzTest
345+
@ValuePool(value = {"mySupplier"})
346+
void yetAnotherFuzzTest(Map<String, Integer> foo, String bar) {
347+
// Values propagate to ALL matching types:
348+
// - String mutator for Map keys in 'foo'
349+
// - String mutator for 'bar'
350+
// - Integer mutator for Map values in 'foo'
351+
}
352+
353+
static Stream<?> mySupplier() {
354+
return Stream.of("example1", "example2", "example3", 1232187321, -182371);
355+
}
356+
```
357+
358+
### How Type Matching Works
359+
360+
Jazzer automatically routes values to mutators based on type:
361+
- Strings in your value pool → String mutators
362+
- Integers in your value pool → Integer mutators
363+
- Byte arrays in your value pool → byte[] mutators
364+
365+
**Type propagation happens recursively by default**, so a `@ValuePool` on a `Map<String, Integer>` will feed both the String mutator (for keys) and Integer mutator (for values).
366+
367+
---
368+
369+
### Supplying Values: Two Mechanisms
370+
371+
#### 1. Supplier Methods (`value` field)
372+
373+
Provide the names of static methods that return `Stream<?>`:
374+
```java
375+
@ValuePool(value = {"mySupplier", "anotherSupplier"})
376+
```
377+
378+
**Requirements:**
379+
- Methods must be `static`
380+
- Must return `Stream<?>`
381+
- Can contain mixed types (Jazzer routes by type automatically)
382+
383+
#### 2. File Patterns (`files` field)
384+
385+
Load files as `byte[]` arrays using glob patterns:
386+
```java
387+
@ValuePool(files = {"*.jpeg"}) // All JPEGs in working dir
388+
@ValuePool(files = {"**.xml"}) // All XMLs recursively
389+
@ValuePool(files = {"/absolute/path/**"}) // All files from absolute path
390+
@ValuePool(files = {"*.jpg", "**.png"}) // Multiple patterns
391+
```
392+
393+
**Glob syntax:** Follows `java.nio.file.PathMatcher` with `glob:` pattern rules.
394+
395+
**You can combine both mechanisms:**
396+
```java
397+
@ValuePool(value = {"mySupplier"}, files = {"test-data/*.json"})
398+
```
399+
400+
---
401+
402+
### Configuration Options
403+
404+
#### Mutation Probability (`p` field)
405+
Controls how often values from the pool are used versus other mutation strategies.
406+
```java
407+
@ValuePool(value = {"mySupplier"}, p = 0.3) // Use pool values 30% of the time
408+
```
409+
410+
**Default:** `p = 0.1` (10% of mutations use pool values)
411+
**Range:** 0.0 to 1.0
412+
413+
#### Type Propagation (`constraint` field)
414+
415+
Controls whether the annotation affects nested types:
416+
```java
417+
// Default: RECURSIVE - applies to all nested types
418+
@ValuePool(value = {"mySupplier"}, constraint = Constraint.RECURSIVE)
419+
420+
// DECLARATION - applies only to the annotated type, not subtypes
421+
@ValuePool(value = {"mySupplier"}, constraint = Constraint.DECLARATION)
422+
```
423+
424+
**Example of the difference:**
425+
```java
426+
// With RECURSIVE (default):
427+
@ValuePool(value = {"valuesSupplier"}) Map<String, Integer> data
428+
// The supplier feed both Map keys AND values
429+
430+
// With DECLARATION:
431+
@ValuePool(value = {"valuesSupplier"}, constraint = DECLARATION) Map<String, Integer> data
432+
// The supplier only feeds the Map, NOT keys or values---it should contain Map instances to have effect
433+
```
434+
435+
---
436+
437+
### Complete Example
438+
```java
439+
class MyFuzzTest {
440+
static Stream<?> edgeCases() {
441+
Map<String, Integer> map = new HashMap<>();
442+
map.put("one", 1);
443+
map.put("two", 2);
444+
return Stream.of(
445+
"", "null", "alert('xss')", // Strings
446+
0, -1, Integer.MAX_VALUE, // Integers
447+
new byte[]{0x00, 0x7F}, // A byte array
448+
map // A Map
449+
);
450+
}
451+
452+
@FuzzTest
453+
@ValuePool(value = {"edgeCases"},
454+
files = {"test-inputs/*.bin"},
455+
p = 0.25) // Use pool values 25% of the time
456+
void testParser(String input, Map<String, Integer> config, byte[] data) {
457+
// All three parameters get values from the pool:
458+
// - 'input' gets Strings
459+
// - 'config' keys get Strings, values get Integers, Map itself gets the `map` object
460+
// - 'data' gets bytes from both edgeCases() and *.bin files
461+
}
462+
}
463+
```
464+
465+
---
466+
467+
#### Max Mutations (`maxMutations` field)
468+
469+
After selecting a value from the pool, the mutator can apply additional random mutations to it.
470+
```java
471+
@ValuePool(value = {"mySupplier"}, maxMutations = 5)
472+
```
473+
474+
**Default:** `maxMutations = 1` (at most one additional mutation applied)
475+
**Range:** 0 or higher
476+
477+
**How it works:** If `maxMutations = 5`, and Jazzer selects the value pool as mutation strategy, Jazzer will:
478+
1. Select a random value from your pool (e.g., `"alert('xss')"`)
479+
2. Apply up to 5 random mutations in a row (e.g., `"alert('xss')"``"alert(x"``"AAAt(x"` → ...)
480+
481+
This helps explore variations of your seed values while staying close to realistic inputs.
482+
483+
320484
## FuzzedDataProvider
321485

322486
The `FuzzedDataProvider` is an alternative approach commonly used in programming

0 commit comments

Comments
 (0)