Skip to content

Commit a2675b5

Browse files
authored
Improve fuzzer recursive model providers (#1039)
1 parent a4cb23f commit a2675b5

File tree

5 files changed

+31
-23
lines changed

5 files changed

+31
-23
lines changed

utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/ArrayModelProvider.kt

+10-5
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,28 @@ import org.utbot.fuzzer.fuzzNumbers
1212

1313
class ArrayModelProvider(
1414
idGenerator: IdentityPreservingIdGenerator<Int>,
15-
recursionDepthLeft: Int = 1
15+
recursionDepthLeft: Int = 2
1616
) : RecursiveModelProvider(idGenerator, recursionDepthLeft) {
1717

18-
override fun newInstance(parentProvider: RecursiveModelProvider): RecursiveModelProvider =
19-
ArrayModelProvider(parentProvider.idGenerator, parentProvider.recursionDepthLeft - 1)
20-
.copySettings(parentProvider)
18+
override fun newInstance(parentProvider: RecursiveModelProvider, constructor: ModelConstructor): RecursiveModelProvider {
19+
val provider = ArrayModelProvider(parentProvider.idGenerator, parentProvider.recursionDepthLeft - 1)
20+
provider.copySettings(parentProvider)
21+
provider.totalLimit = minOf(parentProvider.totalLimit, constructor.limit)
22+
return provider
23+
}
2124

2225
override fun generateModelConstructors(
2326
description: FuzzedMethodDescription,
2427
parameterIndex: Int,
2528
classId: ClassId,
2629
): Sequence<ModelConstructor> = sequence {
2730
if (!classId.isArray) return@sequence
28-
val lengths = fuzzNumbers(description.concreteValues, 0, 3) { it in 1..10 }
31+
val lengths = fuzzNumbers(description.concreteValues, 0, 3) { it in 1..10 }.toList()
2932
lengths.forEach { length ->
3033
yield(ModelConstructor(listOf(FuzzedType(classId.elementClassId!!)), repeat = length) { values ->
3134
createFuzzedArrayModel(classId, length, values.map { it.model } )
35+
}.apply {
36+
limit = (totalLimit / lengths.size).coerceAtLeast(1)
3237
})
3338
}
3439
}

utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/CollectionWithModificationModelProvider.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import org.utbot.fuzzer.objects.create
1515

1616
class CollectionWithModificationModelProvider(
1717
idGenerator: IdentityPreservingIdGenerator<Int>,
18-
recursionDepthLeft: Int = 1,
18+
recursionDepthLeft: Int = 2,
1919
private var defaultModificationCount: IntArray = intArrayOf(0, 1, 3)
2020
) : RecursiveModelProvider(idGenerator, recursionDepthLeft) {
2121

@@ -55,7 +55,7 @@ class CollectionWithModificationModelProvider(
5555
)
5656
private var modificationCount = 7
5757

58-
override fun newInstance(parentProvider: RecursiveModelProvider): RecursiveModelProvider {
58+
override fun newInstance(parentProvider: RecursiveModelProvider, constructor: ModelConstructor): RecursiveModelProvider {
5959
val newInstance = CollectionWithModificationModelProvider(
6060
parentProvider.idGenerator, parentProvider.recursionDepthLeft - 1
6161
)

utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/ObjectModelProvider.kt

+8-5
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ import org.utbot.fuzzer.objects.assembleModel
3434
*/
3535
class ObjectModelProvider(
3636
idGenerator: IdentityPreservingIdGenerator<Int>,
37-
recursionDepthLeft: Int = 1,
37+
recursionDepthLeft: Int = 2,
3838
) : RecursiveModelProvider(idGenerator, recursionDepthLeft) {
39-
override fun newInstance(parentProvider: RecursiveModelProvider): RecursiveModelProvider {
39+
override fun newInstance(parentProvider: RecursiveModelProvider, constructor: ModelConstructor): RecursiveModelProvider {
4040
val newInstance = ObjectModelProvider(parentProvider.idGenerator, parentProvider.recursionDepthLeft - 1)
4141
newInstance.copySettings(parentProvider)
4242
newInstance.branchingLimit = 1
@@ -62,9 +62,9 @@ class ObjectModelProvider(
6262
)
6363

6464
constructors.forEach { constructorId ->
65-
yield(ModelConstructor(constructorId.parameters.map { classId -> FuzzedType(classId) }) {
66-
assembleModel(idGenerator.createId(), constructorId, it)
67-
})
65+
// When branching limit = 1 this block tries to create new values
66+
// and mutate some fields. Only if there's no option next block
67+
// with empty constructor should be used.
6868
if (constructorId.parameters.isEmpty()) {
6969
val fields = findSuitableFields(constructorId.classId, description)
7070
if (fields.isNotEmpty()) {
@@ -75,6 +75,9 @@ class ObjectModelProvider(
7575
)
7676
}
7777
}
78+
yield(ModelConstructor(constructorId.parameters.map { classId -> FuzzedType(classId) }) {
79+
assembleModel(idGenerator.createId(), constructorId, it)
80+
})
7881
}
7982
}
8083

utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/RecursiveModelProvider.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ abstract class RecursiveModelProvider(
5454
/**
5555
* Creates instance of the class on which it is called, assuming that it will be called recursively from [parentProvider]
5656
*/
57-
protected abstract fun newInstance(parentProvider: RecursiveModelProvider): RecursiveModelProvider
57+
protected abstract fun newInstance(parentProvider: RecursiveModelProvider, constructor: ModelConstructor): RecursiveModelProvider
5858

5959
/**
6060
* Creates [ModelProvider]s that will be used to generate values recursively. The order of elements in returned list is important:
@@ -101,16 +101,16 @@ abstract class RecursiveModelProvider(
101101
neededTypes[index % neededTypes.size] // because we can repeat neededTypes several times
102102
}
103103
}
104-
return fuzz(syntheticMethodDescription, nextModelProvider())
104+
return fuzz(syntheticMethodDescription, nextModelProvider(this))
105105
.map { createModel(it) }
106106
.take(limit)
107107
}
108108

109-
private fun nextModelProvider(): ModelProvider =
109+
private fun nextModelProvider(constructor: ModelConstructor): ModelProvider =
110110
if (recursionDepthLeft > 0) {
111111
modelProviderForRecursiveCalls.map {
112112
if (it is RecursiveModelProvider) {
113-
it.newInstance(this)
113+
it.newInstance(this, constructor)
114114
} else { it }
115115
}
116116
} else {

utbot-fuzzers/src/test/kotlin/org/utbot/framework/plugin/api/ModelProviderTest.kt

+7-7
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ class ModelProviderTest {
389389

390390
withUtContext(UtContext(this::class.java.classLoader)) {
391391
val result = collect(
392-
ObjectModelProvider(ReferencePreservingIntIdGenerator(0)),
392+
ObjectModelProvider(ReferencePreservingIntIdGenerator(0), recursionDepthLeft = 1),
393393
parameters = listOf(MyA::class.java.id)
394394
)
395395
assertEquals(1, result.size)
@@ -478,14 +478,14 @@ class ModelProviderTest {
478478
)
479479

480480
withUtContext(UtContext(this::class.java.classLoader)) {
481-
val result = collect(ObjectModelProvider(ReferencePreservingIntIdGenerator(0)).apply {
481+
val result = collect(ObjectModelProvider(ReferencePreservingIntIdGenerator(0), recursionDepthLeft = 1).apply {
482482
modelProviderForRecursiveCalls = PrimitiveDefaultsModelProvider
483483
}, parameters = listOf(FieldSetterClass::class.java.id))
484484
assertEquals(1, result.size)
485485
assertEquals(2, result[0]!!.size)
486-
assertEquals(0, (result[0]!![0] as UtAssembleModel).modificationsChain.size) { "One of models must be without any modifications" }
486+
assertEquals(0, (result[0]!![1] as UtAssembleModel).modificationsChain.size) { "One of models must be without any modifications" }
487487
val expectedModificationSize = 3
488-
val modificationsChain = (result[0]!![1] as UtAssembleModel).modificationsChain
488+
val modificationsChain = (result[0]!![0] as UtAssembleModel).modificationsChain
489489
val actualModificationSize = modificationsChain.size
490490
assertEquals(expectedModificationSize, actualModificationSize) { "In target class there's only $expectedModificationSize fields that can be changed, but generated $actualModificationSize modifications" }
491491

@@ -513,10 +513,10 @@ class ModelProviderTest {
513513
}
514514
assertEquals(1, result.size)
515515
assertEquals(3, result[0]!!.size)
516-
assertEquals(0, (result[0]!![0] as UtAssembleModel).modificationsChain.size) { "One of models must be without any modifications" }
517-
assertEquals(0, (result[0]!![2] as UtAssembleModel).modificationsChain.size) { "Modification by constructor doesn't change fields" }
516+
assertEquals(0, (result[0]!![2] as UtAssembleModel).modificationsChain.size) { "One of models must be without any modifications" }
517+
assertEquals(0, (result[0]!![1] as UtAssembleModel).modificationsChain.size) { "Modification by constructor doesn't change fields" }
518518
val expectedModificationSize = 1
519-
val modificationsChain = (result[0]!![1] as UtAssembleModel).modificationsChain
519+
val modificationsChain = (result[0]!![0] as UtAssembleModel).modificationsChain
520520
val actualModificationSize = modificationsChain.size
521521
assertEquals(expectedModificationSize, actualModificationSize) { "In target class there's only $expectedModificationSize fields that can be changed, but generated $actualModificationSize modifications" }
522522

0 commit comments

Comments
 (0)