Skip to content

Commit 9150f43

Browse files
committed
feat: add @ElementOf annotation
This allows picking from a fixed set of values during mutation.
1 parent 315fe56 commit 9150f43

6 files changed

Lines changed: 584 additions & 17 deletions

File tree

docs/mutation-framework.md

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -87,22 +87,23 @@ string. This is done using annotations directly on the parameters.
8787
All annotations reside in the `com.code_intelligence.jazzer.mutation.annotation`
8888
package.
8989

90-
| Annotation | Applies To | Notes |
91-
|-------------------|----------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------|
92-
| `@Ascii` | `java.lang.String` | `String` should only contain ASCII characters |
93-
| `@InRange` | `byte`, `Byte`, `char`, `Character`, `short`, `Short`, `int`, `Integer`, `long`, `Long` | Specifies `min` and `max` values of generated integrals |
94-
| `@FloatInRange` | `float`, `Float` | Specifies `min` and `max` values of generated floats |
95-
| `@DoubleInRange` | `double`, `Double` | Specifies `min` and `max` values of generated doubles |
96-
| `@Positive` | `byte`, `Byte`, `short`, `Short`, `int`, `Integer`, `long`, `Long`, `float`, `Float`, `double`, `Double` | Specifies that only positive values are generated |
97-
| `@Negative` | `byte`, `Byte`, `short`, `Short`, `int`, `Integer`, `long`, `Long`, `float`, `Float`, `double`, `Double` | Specifies that only negative values are generated |
98-
| `@NonPositive` | `byte`, `Byte`, `short`, `Short`, `int`, `Integer`, `long`, `Long`, `float`, `Float`, `double`, `Double` | Specifies that only non-positive values are generated |
99-
| `@NonNegative` | `byte`, `Byte`, `short`, `Short`, `int`, `Integer`, `long`, `Long`, `float`, `Float`, `double`, `Double` | Specifies that only non-negative values are generated |
100-
| `@Finite` | `float`, `Float`, `double`, `Double` | Specifies that only finite values are generated |
101-
| `@NotNull` | | Specifies that a reference type should not be `null` |
102-
| `@WithLength` | `byte[]` | Specifies the length of the generated byte array |
103-
| `@WithUtf8Length` | `java.lang.String` | Specifies the length of the generated string in UTF-8 bytes, see annotation Javadoc for further information |
104-
| `@WithSize` | `java.util.List`, `java.util.Map` | Specifies the size of the generated collection |
105-
| `@UrlSegment` | `java.lang.String` | `String` should only contain valid URL segment characters |
90+
| Annotation | Applies To | Notes |
91+
|-------------------|-----------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|
92+
| `@Ascii` | `java.lang.String` | `String` should only contain ASCII characters |
93+
| `@InRange` | `byte`, `Byte`, `char`, `Character`, `short`, `Short`, `int`, `Integer`, `long`, `Long` | Specifies `min` and `max` values of generated integrals |
94+
| `@FloatInRange` | `float`, `Float` | Specifies `min` and `max` values of generated floats |
95+
| `@DoubleInRange` | `double`, `Double` | Specifies `min` and `max` values of generated doubles |
96+
| `@Positive` | `byte`, `Byte`, `short`, `Short`, `int`, `Integer`, `long`, `Long`, `float`, `Float`, `double`, `Double` | Specifies that only positive values are generated |
97+
| `@Negative` | `byte`, `Byte`, `short`, `Short`, `int`, `Integer`, `long`, `Long`, `float`, `Float`, `double`, `Double` | Specifies that only negative values are generated |
98+
| `@NonPositive` | `byte`, `Byte`, `short`, `Short`, `int`, `Integer`, `long`, `Long`, `float`, `Float`, `double`, `Double` | Specifies that only non-positive values are generated |
99+
| `@NonNegative` | `byte`, `Byte`, `short`, `Short`, `int`, `Integer`, `long`, `Long`, `float`, `Float`, `double`, `Double` | Specifies that only non-negative values are generated |
100+
| `@Finite` | `float`, `Float`, `double`, `Double` | Specifies that only finite values are generated |
101+
| `@ElementOf` | `byte`, `Byte`, `short`, `Short`, `int`, `Integer`, `long`, `Long`, `char`, `Character`, `float`, `Float`, `double`, `Double`, `String` | Restricts the value to a fixed set; populate only the array matching the parameter type with at least one entry |
102+
| `@NotNull` | | Specifies that a reference type should not be `null` |
103+
| `@WithLength` | `byte[]` | Specifies the length of the generated byte array |
104+
| `@WithUtf8Length` | `java.lang.String` | Specifies the length of the generated string in UTF-8 bytes, see annotation Javadoc for further information |
105+
| `@WithSize` | `java.util.List`, `java.util.Map` | Specifies the size of the generated collection |
106+
| `@UrlSegment` | `java.lang.String` | `String` should only contain valid URL segment characters |
106107

107108
The example below shows how Fuzz Test parameters can be annotated to provide
108109
additional information to the mutation framework.
@@ -116,6 +117,15 @@ public void testSimpleTypeRecord(@NotNull @WithSize(min = 3, max = 100) List<Sim
116117
}
117118
```
118119

120+
Use `@ElementOf` when a parameter should only take one of a few constant values.
121+
122+
```java title="Example" showLineNumbers
123+
@FuzzTest
124+
void fuzz(@ElementOf(strings = {"one", "two", "three"}) String value) {
125+
// value is always "one", "two", "three" or null
126+
}
127+
```
128+
119129
### Annotation constraints
120130

121131
Often, annotations should be applied to a type and all it's nested component
@@ -347,4 +357,3 @@ class ParserTests {
347357
}
348358
}
349359
```
350-
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2024 Code Intelligence GmbH
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.code_intelligence.jazzer.mutation.annotation;
18+
19+
import static java.lang.annotation.ElementType.TYPE_USE;
20+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
21+
22+
import com.code_intelligence.jazzer.mutation.utils.AppliesTo;
23+
import java.lang.annotation.Retention;
24+
import java.lang.annotation.Target;
25+
26+
/**
27+
* Restricts generated values to a fixed set.
28+
*
29+
* <p>Populate exactly one of the type-specific arrays with at least one value. Only the array that
30+
* matches the annotated parameter type is used; all others are ignored. For {@link String} and
31+
* wrapper types, the mutator may still emit {@code null}; add {@link NotNull} (the only supported
32+
* annotation in combination with {@code @ElementOf}) to prevent that.
33+
*
34+
* <p>Example usage:
35+
*
36+
* <pre>{@code
37+
* @FuzzTest
38+
* void fuzz(
39+
* @ElementOf(integers = {1, 2, 3}) int option,
40+
* @ElementOf(strings = {"one", "two"}) String label) {
41+
* // option is always 1, 2, or 3; label is always "one" or "two".
42+
* }
43+
* }</pre>
44+
*/
45+
@Target(TYPE_USE)
46+
@Retention(RUNTIME)
47+
@AppliesTo({
48+
byte.class,
49+
Byte.class,
50+
short.class,
51+
Short.class,
52+
int.class,
53+
Integer.class,
54+
long.class,
55+
Long.class,
56+
char.class,
57+
Character.class,
58+
float.class,
59+
Float.class,
60+
double.class,
61+
Double.class,
62+
String.class
63+
})
64+
public @interface ElementOf {
65+
byte[] bytes() default {};
66+
67+
short[] shorts() default {};
68+
69+
int[] integers() default {};
70+
71+
long[] longs() default {};
72+
73+
char[] chars() default {};
74+
75+
float[] floats() default {};
76+
77+
double[] doubles() default {};
78+
79+
String[] strings() default {};
80+
}

0 commit comments

Comments
 (0)