Skip to content

Commit 77b51a9

Browse files
committed
Fuzzing doesn't generate test for package-private parameters
1 parent acc590b commit 77b51a9

File tree

5 files changed

+89
-33
lines changed

5 files changed

+89
-33
lines changed

utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,8 @@ class UtBotSymbolicEngine(
406406

407407
val methodUnderTestDescription = FuzzedMethodDescription(executableId, collectConstantsForFuzzer(graph)).apply {
408408
compilableName = if (methodUnderTest.isMethod) executableId.name else null
409+
className = executableId.classId.simpleName
410+
packageName = executableId.classId.packageName
409411
val names = graph.body.method.tags.filterIsInstance<ParamNamesTag>().firstOrNull()?.names
410412
parameterNameMap = { index -> names?.getOrNull(index) }
411413
}

utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/FuzzedMethodDescription.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ class FuzzedMethodDescription(
2525
*/
2626
var compilableName: String? = null
2727

28+
/**
29+
* Class Name
30+
*/
31+
var className: String? = null
32+
33+
/**
34+
* Package Name
35+
*/
36+
var packageName: String? = null
37+
2838
/**
2939
* Returns parameter name by its index in the signature
3040
*/

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

Lines changed: 27 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ import org.utbot.fuzzer.objectModelProviders
2424
import org.utbot.fuzzer.providers.ConstantsModelProvider.fuzzed
2525
import java.lang.reflect.Constructor
2626
import java.lang.reflect.Field
27+
import java.lang.reflect.Member
2728
import java.lang.reflect.Method
28-
import java.lang.reflect.Modifier
29+
import java.lang.reflect.Modifier.*
2930
import java.util.function.BiConsumer
3031
import java.util.function.IntSupplier
3132

@@ -67,7 +68,7 @@ class ObjectModelProvider : ModelProvider {
6768
.filterNot { it == stringClassId || it.isPrimitiveWrapper }
6869
.flatMap { classId ->
6970
collectConstructors(classId) { javaConstructor ->
70-
isPublic(javaConstructor)
71+
isAccessible(javaConstructor, description.packageName)
7172
}.sortedWith(
7273
primitiveParameterizedConstructorsFirstAndThenByParameterCount
7374
).take(limit)
@@ -81,7 +82,7 @@ class ObjectModelProvider : ModelProvider {
8182
.flatMap { (constructorId, fuzzedParameters) ->
8283
if (constructorId.parameters.isEmpty()) {
8384
sequenceOf(assembleModel(idGenerator.asInt, constructorId, emptyList())) +
84-
generateModelsWithFieldsInitialization(constructorId, concreteValues)
85+
generateModelsWithFieldsInitialization(constructorId, description, concreteValues)
8586
}
8687
else {
8788
fuzzedParameters.map { params ->
@@ -98,8 +99,8 @@ class ObjectModelProvider : ModelProvider {
9899
}
99100
}
100101

101-
private fun generateModelsWithFieldsInitialization(constructorId: ConstructorId, concreteValues: Collection<FuzzedConcreteValue>): Sequence<FuzzedValue> {
102-
val fields = findSuitableFields(constructorId.classId)
102+
private fun generateModelsWithFieldsInitialization(constructorId: ConstructorId, description: FuzzedMethodDescription, concreteValues: Collection<FuzzedConcreteValue>): Sequence<FuzzedValue> {
103+
val fields = findSuitableFields(constructorId.classId, description)
103104
val syntheticClassFieldsSetterMethodDescription = FuzzedMethodDescription(
104105
"${constructorId.classId.simpleName}<syntheticClassFieldSetter>",
105106
voidClassId,
@@ -115,16 +116,16 @@ class ObjectModelProvider : ModelProvider {
115116
fieldValues.asSequence().mapIndexedNotNull { index, value ->
116117
val field = fields[index]
117118
when {
118-
field.setter != null -> UtExecutableCallModel(
119-
fuzzedModel.model,
120-
MethodId(constructorId.classId, field.setter.name, field.setter.returnType.id, listOf(field.classId)),
121-
listOf(value.model)
122-
)
123119
field.canBeSetDirectly -> UtDirectSetFieldModel(
124120
fuzzedModel.model,
125121
FieldId(constructorId.classId, field.name),
126122
value.model
127123
)
124+
field.setter != null -> UtExecutableCallModel(
125+
fuzzedModel.model,
126+
MethodId(constructorId.classId, field.setter.name, field.setter.returnType.id, listOf(field.classId)),
127+
listOf(value.model)
128+
)
128129
else -> null
129130
}
130131
}.forEach(modificationChain::add)
@@ -141,8 +142,16 @@ class ObjectModelProvider : ModelProvider {
141142
}
142143
}
143144

144-
private fun isPublic(javaConstructor: Constructor<*>): Boolean {
145-
return javaConstructor.modifiers and Modifier.PUBLIC != 0
145+
private fun isAccessible(member: Member, packageName: String?): Boolean {
146+
return isPublic(member.modifiers) ||
147+
(isPackagePrivate(member.modifiers) && member.declaringClass.`package`.name == packageName)
148+
}
149+
150+
private fun isPackagePrivate(modifiers: Int): Boolean {
151+
val hasAnyAccessModifier = isPrivate(modifiers)
152+
|| isProtected(modifiers)
153+
|| isProtected(modifiers)
154+
return !hasAnyAccessModifier
146155
}
147156

148157
private fun FuzzedMethodDescription.fuzzParameters(constructorId: ConstructorId, vararg modelProviders: ModelProvider): Sequence<List<FuzzedValue>> {
@@ -168,26 +177,26 @@ class ObjectModelProvider : ModelProvider {
168177
}
169178
}
170179

171-
private fun findSuitableFields(classId: ClassId): List<FieldDescription> {
180+
private fun findSuitableFields(classId: ClassId, description: FuzzedMethodDescription): List<FieldDescription> {
172181
val jClass = classId.jClass
173182
return jClass.declaredFields.map { field ->
174183
FieldDescription(
175184
field.name,
176185
field.type.id,
177-
field.isPublic && !field.isFinal && !field.isStatic,
178-
jClass.findPublicSetterIfHasPublicGetter(field)
186+
isAccessible(field, description.packageName) && !isFinal(field.modifiers) && !isStatic(field.modifiers),
187+
jClass.findPublicSetterIfHasPublicGetter(field, description)
179188
)
180189
}
181190
}
182191

183-
private fun Class<*>.findPublicSetterIfHasPublicGetter(field: Field): Method? {
192+
private fun Class<*>.findPublicSetterIfHasPublicGetter(field: Field, description: FuzzedMethodDescription): Method? {
184193
val postfixName = field.name.capitalize()
185194
val setterName = "set$postfixName"
186195
val getterName = "get$postfixName"
187196
val getter = try { getDeclaredMethod(getterName) } catch (_: NoSuchMethodException) { return null }
188-
return if (getter has Modifier.PUBLIC && getter.returnType == field.type) {
197+
return if (isAccessible(getter, description.packageName) && getter.returnType == field.type) {
189198
declaredMethods.find {
190-
it has Modifier.PUBLIC &&
199+
isAccessible(it, description.packageName) &&
191200
it.name == setterName &&
192201
it.parameterCount == 1 &&
193202
it.parameterTypes[0] == field.type
@@ -196,18 +205,6 @@ class ObjectModelProvider : ModelProvider {
196205
null
197206
}
198207
}
199-
private val Field.isPublic
200-
get() = has(Modifier.PUBLIC)
201-
202-
private val Field.isFinal
203-
get() = has(Modifier.FINAL)
204-
205-
private val Field.isStatic
206-
get() = has(Modifier.STATIC)
207-
208-
private infix fun Field.has(modifier: Int) = (modifiers and modifier) != 0
209-
210-
private infix fun Method.has(modifier: Int) = (modifiers and modifier) != 0
211208

212209
private val primitiveParameterizedConstructorsFirstAndThenByParameterCount =
213210
compareByDescending<ConstructorId> { constructorId ->
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.utbot.framework.plugin.api.samples;
2+
3+
@SuppressWarnings("All")
4+
public class PackagePrivateFieldAndClass {
5+
6+
volatile int pkgField = 0;
7+
8+
PackagePrivateFieldAndClass() {
9+
10+
}
11+
12+
PackagePrivateFieldAndClass(int value) {
13+
pkgField = value;
14+
}
15+
16+
}

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

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import org.utbot.fuzzer.providers.StringConstantModelProvider
2424
import org.junit.jupiter.api.Assertions.*
2525
import org.junit.jupiter.api.Test
2626
import org.utbot.framework.plugin.api.samples.FieldSetterClass
27+
import org.utbot.framework.plugin.api.samples.PackagePrivateFieldAndClass
2728
import org.utbot.framework.plugin.api.util.primitiveByWrapper
2829
import org.utbot.framework.plugin.api.util.primitiveWrappers
2930
import org.utbot.framework.plugin.api.util.voidWrapperClassId
@@ -469,20 +470,50 @@ class ModelProviderTest {
469470
assertEquals(expectedModificationSize, actualModificationSize) { "In target class there's only $expectedModificationSize fields that can be changed, but generated $actualModificationSize modifications" }
470471

471472
assertEquals("pubField", (modificationsChain[0] as UtDirectSetFieldModel).fieldId.name)
472-
assertEquals("setPubFieldWithSetter", (modificationsChain[1] as UtExecutableCallModel).executable.name)
473+
assertEquals("pubFieldWithSetter", (modificationsChain[1] as UtDirectSetFieldModel).fieldId.name)
473474
assertEquals("setPrvFieldWithSetter", (modificationsChain[2] as UtExecutableCallModel).executable.name)
474475
}
475476
}
476477

478+
@Test
479+
fun `test complex object is created with setters and package private field and constructor`() {
480+
val j = PackagePrivateFieldAndClass::class.java
481+
assertEquals(1, j.declaredFields.size)
482+
assertTrue(
483+
setOf(
484+
"pkgField",
485+
).containsAll(j.declaredFields.map { it.name })
486+
)
487+
488+
withUtContext(UtContext(this::class.java.classLoader)) {
489+
val result = collect(ObjectModelProvider { 0 }.apply {
490+
modelProvider = PrimitiveDefaultsModelProvider
491+
}, parameters = listOf(PackagePrivateFieldAndClass::class.java.id)) {
492+
packageName = PackagePrivateFieldAndClass::class.java.`package`.name
493+
}
494+
assertEquals(1, result.size)
495+
assertEquals(3, result[0]!!.size)
496+
assertEquals(0, (result[0]!![0] as UtAssembleModel).modificationsChain.size) { "One of models must be without any modifications" }
497+
assertEquals(0, (result[0]!![2] as UtAssembleModel).modificationsChain.size) { "Modification by constructor doesn't change fields" }
498+
val expectedModificationSize = 1
499+
val modificationsChain = (result[0]!![1] as UtAssembleModel).modificationsChain
500+
val actualModificationSize = modificationsChain.size
501+
assertEquals(expectedModificationSize, actualModificationSize) { "In target class there's only $expectedModificationSize fields that can be changed, but generated $actualModificationSize modifications" }
502+
503+
assertEquals("pkgField", (modificationsChain[0] as UtDirectSetFieldModel).fieldId.name)
504+
}
505+
}
506+
477507
private fun collect(
478508
modelProvider: ModelProvider,
479509
name: String = "testMethod",
480510
returnType: ClassId = voidClassId,
481511
parameters: List<ClassId>,
482-
constants: List<FuzzedConcreteValue> = emptyList()
512+
constants: List<FuzzedConcreteValue> = emptyList(),
513+
block: FuzzedMethodDescription.() -> Unit = {}
483514
): Map<Int, List<UtModel>> {
484515
return mutableMapOf<Int, MutableList<UtModel>>().apply {
485-
modelProvider.generate(FuzzedMethodDescription(name, returnType, parameters, constants)) { i, m ->
516+
modelProvider.generate(FuzzedMethodDescription(name, returnType, parameters, constants).apply(block)) { i, m ->
486517
computeIfAbsent(i) { mutableListOf() }.add(m.model)
487518
}
488519
}

0 commit comments

Comments
 (0)