@@ -4,8 +4,105 @@ package org.utbot.fuzzing
44
55import org.utbot.fuzzing.seeds.BitVectorValue
66import org.utbot.fuzzing.seeds.IEEE754Value
7+ import org.utbot.fuzzing.seeds.KnownValue
8+ import org.utbot.fuzzing.seeds.StringValue
9+ import org.utbot.fuzzing.utils.chooseOne
10+ import org.utbot.fuzzing.utils.flipCoin
711import kotlin.random.Random
812
13+ class MutationFactory <TYPE , RESULT > {
14+
15+ fun mutate (node : Node <TYPE , RESULT >, random : Random , configuration : Configuration ): Node <TYPE , RESULT > {
16+ if (node.result.isEmpty()) return node
17+ val indexOfMutatedResult = random.chooseOne(node.result.map(::rate).toDoubleArray())
18+ val recursive: NodeMutation <TYPE , RESULT > = NodeMutation { n, r, c ->
19+ mutate(n, r, c)
20+ }
21+ val mutated = when (val resultToMutate = node.result[indexOfMutatedResult]) {
22+ is Result .Simple <TYPE , RESULT > -> Result .Simple (resultToMutate.mutation(resultToMutate.result, random), resultToMutate.mutation)
23+ is Result .Known <TYPE , RESULT , * > -> {
24+ val mutations = resultToMutate.value.mutations()
25+ if (mutations.isNotEmpty()) {
26+ resultToMutate.mutate(mutations.random(random), random, configuration)
27+ } else {
28+ resultToMutate
29+ }
30+ }
31+ is Result .Recursive <TYPE , RESULT > -> {
32+ when {
33+ resultToMutate.modify.isEmpty() || random.flipCoin(configuration.probConstructorMutationInsteadModificationMutation) ->
34+ RecursiveMutations .Constructor <TYPE , RESULT >()
35+ random.flipCoin(configuration.probShuffleAndCutRecursiveObjectModificationMutation) ->
36+ RecursiveMutations .ShuffleAndCutModifications ()
37+ else ->
38+ RecursiveMutations .Mutate ()
39+ }.mutate(resultToMutate, recursive, random, configuration)
40+ }
41+ is Result .Collection <TYPE , RESULT > -> if (resultToMutate.modify.isNotEmpty()) {
42+ when {
43+ random.flipCoin(100 - configuration.probCollectionShuffleInsteadResultMutation) ->
44+ CollectionMutations .Mutate ()
45+ else ->
46+ CollectionMutations .Shuffle <TYPE , RESULT >()
47+ }.mutate(resultToMutate, recursive, random, configuration)
48+ } else {
49+ resultToMutate
50+ }
51+ is Result .Empty -> resultToMutate
52+ }
53+ return Node (node.result.toMutableList().apply {
54+ set(indexOfMutatedResult, mutated)
55+ }, node.parameters, node.builder)
56+ }
57+
58+ /* *
59+ * Rates somehow the result.
60+ *
61+ * For example, fuzzing should not try to mutate some empty structures, like empty collections or objects.
62+ */
63+ private fun <TYPE , RESULT > rate (result : Result <TYPE , RESULT >): Double {
64+ if (! canMutate(result)) {
65+ return ALMOST_ZERO
66+ }
67+ return when (result) {
68+ is Result .Recursive <TYPE , RESULT > -> if (result.construct.parameters.isEmpty() and result.modify.isEmpty()) ALMOST_ZERO else 0.5
69+ is Result .Collection <TYPE , RESULT > -> if (result.iterations == 0 ) return ALMOST_ZERO else 0.7
70+ is StringValue -> 2.0
71+ is Result .Known <TYPE , RESULT , * > -> 1.2
72+ is Result .Simple <TYPE , RESULT > -> 2.0
73+ is Result .Empty -> ALMOST_ZERO
74+ }
75+ }
76+
77+ private fun <TYPE , RESULT > canMutate (node : Result <TYPE , RESULT >): Boolean {
78+ return when (node) {
79+ is Result .Simple <TYPE , RESULT > -> node.mutation == = emptyMutation<RESULT >()
80+ is Result .Known <TYPE , RESULT , * > -> node.value.mutations().isNotEmpty()
81+ is Result .Recursive <TYPE , RESULT > -> node.modify.isNotEmpty()
82+ is Result .Collection <TYPE , RESULT > -> node.modify.isNotEmpty() && node.iterations > 0
83+ is Result .Empty <TYPE , RESULT > -> false
84+ }
85+ }
86+
87+ @Suppress(" UNCHECKED_CAST" )
88+ private fun <TYPE , RESULT , T : KnownValue <T >> Result.Known <TYPE , RESULT , * >.mutate (mutation : Mutation <T >, random : Random , configuration : Configuration ): Result .Known <TYPE , RESULT , T > {
89+ val source: T = value as T
90+ val mutate = mutation.mutate(source, random, configuration)
91+ return Result .Known (
92+ mutate,
93+ build as (T ) -> RESULT
94+ )
95+ }
96+ }
97+
98+ private const val ALMOST_ZERO = 1E- 7
99+ private val IDENTITY_MUTATION : (Any , random: Random ) -> Any = { f, _ -> f }
100+
101+ fun <RESULT > emptyMutation (): (RESULT , random: Random ) -> RESULT {
102+ @Suppress(" UNCHECKED_CAST" )
103+ return IDENTITY_MUTATION as (RESULT , random: Random ) -> RESULT
104+ }
105+
9106/* *
10107 * Mutations is an object which applies some changes to the source object
11108 * and then returns a new object (or old one without changes).
@@ -14,20 +111,14 @@ fun interface Mutation<T> {
14111 fun mutate (source : T , random : Random , configuration : Configuration ): T
15112}
16113
17- inline fun <reified T , reified F : T > Mutation<F>.adapt (): Mutation <T > {
18- return Mutation { s, r, c ->
19- if (s is F ) return @Mutation mutate(s, r, c) else s
20- }
21- }
22-
23114sealed class BitVectorMutations : Mutation <BitVectorValue > {
24115
25116 abstract fun rangeOfMutation (source : BitVectorValue ): IntRange
26117
27118 override fun mutate (source : BitVectorValue , random : Random , configuration : Configuration ): BitVectorValue {
28119 with (rangeOfMutation(source)) {
29120 val firstBits = random.nextInt(start, endInclusive.coerceAtLeast(1 ))
30- return BitVectorValue (source).apply { this [firstBits] = ! this [firstBits] }
121+ return BitVectorValue (source, this @BitVectorMutations ).apply { this [firstBits] = ! this [firstBits] }
31122 }
32123 }
33124
@@ -44,31 +135,194 @@ sealed class BitVectorMutations : Mutation<BitVectorValue> {
44135 }
45136}
46137
47- sealed class IEEE754Mutations : Mutation <IEEE754Value > {
138+ sealed interface IEEE754Mutations : Mutation <IEEE754Value > {
48139
49- object ChangeSign : IEEE754Mutations() {
140+ object ChangeSign : IEEE754Mutations {
50141 override fun mutate (source : IEEE754Value , random : Random , configuration : Configuration ): IEEE754Value {
51- return IEEE754Value (source).apply {
142+ return IEEE754Value (source, this ).apply {
52143 setRaw(0 , ! getRaw(0 ))
53144 }
54145 }
55146 }
56147
57- object Mantissa : IEEE754Mutations() {
148+ object Mantissa : IEEE754Mutations {
58149 override fun mutate (source : IEEE754Value , random : Random , configuration : Configuration ): IEEE754Value {
59150 val i = random.nextInt(0 , source.mantissaSize)
60- return IEEE754Value (source).apply {
151+ return IEEE754Value (source, this ).apply {
61152 setRaw(1 + exponentSize + i, ! getRaw(1 + exponentSize + i))
62153 }
63154 }
64155 }
65156
66- object Exponent : IEEE754Mutations() {
157+ object Exponent : IEEE754Mutations {
67158 override fun mutate (source : IEEE754Value , random : Random , configuration : Configuration ): IEEE754Value {
68159 val i = random.nextInt(0 , source.exponentSize)
69- return IEEE754Value (source).apply {
160+ return IEEE754Value (source, this ).apply {
70161 setRaw(1 + i, ! getRaw(1 + i))
71162 }
72163 }
73164 }
165+ }
166+
167+ sealed interface StringMutations : Mutation <StringValue > {
168+
169+ object AddCharacter : StringMutations {
170+ override fun mutate (source : StringValue , random : Random , configuration : Configuration ): StringValue {
171+ val value = source.value
172+ if (value.length >= configuration.maxStringLengthWhenMutated) {
173+ return source
174+ }
175+ val position = random.nextInt(value.length + 1 )
176+ val charToMutate = if (value.isNotEmpty()) {
177+ value.random(random)
178+ } else {
179+ // use any meaningful character from the ascii table
180+ random.nextInt(33 , 127 ).toChar()
181+ }
182+ val newString = buildString {
183+ append(value.substring(0 , position))
184+ // try to change char to some that is close enough to origin char
185+ val charTableSpread = 64
186+ if (random.nextBoolean()) {
187+ append(charToMutate - random.nextInt(1 , charTableSpread))
188+ } else {
189+ append(charToMutate + random.nextInt(1 , charTableSpread))
190+ }
191+ append(value.substring(position, value.length))
192+ }
193+ return StringValue (newString, lastMutation = this )
194+ }
195+ }
196+
197+ object RemoveCharacter : StringMutations {
198+ override fun mutate (source : StringValue , random : Random , configuration : Configuration ): StringValue {
199+ val value = source.value
200+ val position = random.nextInt(value.length + 1 )
201+ if (position >= value.length) return source
202+ val toRemove = random.nextInt(value.length)
203+ val newString = buildString {
204+ append(value.substring(0 , toRemove))
205+ append(value.substring(toRemove + 1 , value.length))
206+ }
207+ return StringValue (newString, this )
208+ }
209+ }
210+ }
211+
212+ fun interface NodeMutation <TYPE , RESULT > : Mutation <Node <TYPE , RESULT >>
213+
214+ sealed interface CollectionMutations <TYPE , RESULT > : Mutation <Pair <Result .Collection <TYPE , RESULT >, NodeMutation <TYPE , RESULT >>> {
215+
216+ override fun mutate (
217+ source : Pair <Result .Collection <TYPE , RESULT >, NodeMutation <TYPE , RESULT >>,
218+ random : Random ,
219+ configuration : Configuration
220+ ): Pair <Result .Collection <TYPE , RESULT >, NodeMutation<TYPE, RESULT>> {
221+ return mutate(source.first, source.second, random, configuration) to source.second
222+ }
223+
224+ fun mutate (
225+ source : Result .Collection <TYPE , RESULT >,
226+ recursive : NodeMutation <TYPE , RESULT >,
227+ random : Random ,
228+ configuration : Configuration
229+ ) : Result .Collection <TYPE , RESULT >
230+
231+ class Shuffle <TYPE , RESULT > : CollectionMutations <TYPE , RESULT > {
232+ override fun mutate (
233+ source : Result .Collection <TYPE , RESULT >,
234+ recursive : NodeMutation <TYPE , RESULT >,
235+ random : Random ,
236+ configuration : Configuration
237+ ): Result .Collection <TYPE , RESULT > {
238+ return Result .Collection (
239+ construct = source.construct,
240+ modify = source.modify.toMutableList().shuffled(random),
241+ iterations = source.iterations
242+ )
243+ }
244+ }
245+
246+ class Mutate <TYPE , RESULT > : CollectionMutations <TYPE , RESULT > {
247+ override fun mutate (
248+ source : Result .Collection <TYPE , RESULT >,
249+ recursive : NodeMutation <TYPE , RESULT >,
250+ random : Random ,
251+ configuration : Configuration
252+ ): Result .Collection <TYPE , RESULT > {
253+ return Result .Collection (
254+ construct = source.construct,
255+ modify = source.modify.toMutableList().apply {
256+ val i = random.nextInt(0 , source.modify.size)
257+ set(i, recursive.mutate(source.modify[i], random, configuration))
258+ },
259+ iterations = source.iterations
260+ )
261+ }
262+ }
263+ }
264+
265+ sealed interface RecursiveMutations <TYPE , RESULT > : Mutation <Pair <Result .Recursive <TYPE , RESULT >, NodeMutation <TYPE , RESULT >>> {
266+
267+ override fun mutate (
268+ source : Pair <Result .Recursive <TYPE , RESULT >, NodeMutation <TYPE , RESULT >>,
269+ random : Random ,
270+ configuration : Configuration
271+ ): Pair <Result .Recursive <TYPE , RESULT >, NodeMutation<TYPE, RESULT>> {
272+ return mutate(source.first, source.second, random, configuration) to source.second
273+ }
274+
275+ fun mutate (
276+ source : Result .Recursive <TYPE , RESULT >,
277+ recursive : NodeMutation <TYPE , RESULT >,
278+ random : Random ,
279+ configuration : Configuration
280+ ) : Result .Recursive <TYPE , RESULT >
281+
282+
283+ class Constructor <TYPE , RESULT > : RecursiveMutations <TYPE , RESULT > {
284+ override fun mutate (
285+ source : Result .Recursive <TYPE , RESULT >,
286+ recursive : NodeMutation <TYPE , RESULT >,
287+ random : Random ,
288+ configuration : Configuration
289+ ): Result .Recursive <TYPE , RESULT > {
290+ return Result .Recursive (
291+ construct = recursive.mutate(source.construct,random, configuration),
292+ modify = source.modify
293+ )
294+ }
295+ }
296+
297+ class ShuffleAndCutModifications <TYPE , RESULT > : RecursiveMutations <TYPE , RESULT > {
298+ override fun mutate (
299+ source : Result .Recursive <TYPE , RESULT >,
300+ recursive : NodeMutation <TYPE , RESULT >,
301+ random : Random ,
302+ configuration : Configuration
303+ ): Result .Recursive <TYPE , RESULT > {
304+ return Result .Recursive (
305+ construct = source.construct,
306+ modify = source.modify.shuffled(random).take(random.nextInt(source.modify.size + 1 ).coerceAtLeast(1 ))
307+ )
308+ }
309+ }
310+
311+ class Mutate <TYPE , RESULT > : RecursiveMutations <TYPE , RESULT > {
312+ override fun mutate (
313+ source : Result .Recursive <TYPE , RESULT >,
314+ recursive : NodeMutation <TYPE , RESULT >,
315+ random : Random ,
316+ configuration : Configuration
317+ ): Result .Recursive <TYPE , RESULT > {
318+ return Result .Recursive (
319+ construct = source.construct,
320+ modify = source.modify.toMutableList().apply {
321+ val i = random.nextInt(0 , source.modify.size)
322+ set(i, recursive.mutate(source.modify[i], random, configuration))
323+ }
324+ )
325+ }
326+
327+ }
74328}
0 commit comments