Skip to content

Commit 112ab04

Browse files
committed
feat: document @valuepool
1 parent 04eefa5 commit 112ab04

File tree

1 file changed

+157
-0
lines changed

1 file changed

+157
-0
lines changed

docs/mutation-framework.md

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

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

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

0 commit comments

Comments
 (0)