Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd514a7b

Browse files
authored
Improve detection of NPEs caused by this instance fields being null in Spring unit tests#2589 (#2617)
* Add `JavaLangObjectValueProvider` that uses class under test in place of `java.lang.Object`s* Use `RemovingConstructFailsUtExecutionInstrumentation` in all Spring tests* Replace `ConstructorAnalyzer` with `ExecutableAnalyzer`* Improve Spring-specific non-null speculation and make it work with `FieldId`* Make `InjectMockValueProvider` respect `NonNullSpeculator`* Update `SpringModelUtils`* Log "Injected fields for..." message with debug level* Fix `ExecutableAnalyzer` to avoid storing `-1` in param index to field id map* Fix `ServiceOfBeansWithSameTypeTest`* Fix typo in `JavaLangObjectValueProvider` class name
1 parentd4564dd commitd514a7b

File tree

16 files changed

+322
-237
lines changed

16 files changed

+322
-237
lines changed

‎utbot-framework-api/src/main/kotlin/org/utbot/framework/TrustedPackages.kt‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
packageorg.utbot.framework
22

3+
importorg.utbot.framework.plugin.api.ClassId
34
importsoot.SootClass
45

56
/**
@@ -10,7 +11,17 @@ private val isPackageTrusted: MutableMap<String, Boolean> = mutableMapOf()
1011
/**
1112
* Determines whether [this] class is from trusted libraries as defined in [TrustedLibraries].
1213
*/
13-
fun SootClass.isFromTrustedLibrary():Boolean {
14+
fun SootClass.isFromTrustedLibrary():Boolean= isFromTrustedLibrary(packageName)
15+
16+
/**
17+
* Determines whether [this] class is from trusted libraries as defined in [TrustedLibraries].
18+
*/
19+
fun ClassId.isFromTrustedLibrary():Boolean= isFromTrustedLibrary(packageName)
20+
21+
/**
22+
* Determines whether [packageName] is from trusted libraries as defined in [TrustedLibraries].
23+
*/
24+
funisFromTrustedLibrary(packageName:String):Boolean {
1425
isPackageTrusted[packageName]?.let {
1526
return it
1627
}

‎utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,13 @@ val ClassId.allDeclaredFieldIds: Sequence<FieldId>
468468
valSootField.fieldId:FieldId
469469
get()=FieldId(declaringClass.id, name)
470470

471+
/**
472+
* For some lambdas class names in byte code and in Soot don't match, so we may fail
473+
* to convert some soot fields to Java fields, in such case `null` is returned.
474+
*/
475+
valSootField.jFieldOrNull:Field?
476+
get()= runCatching { fieldId.jField }.getOrNull()
477+
471478
// FieldId utils
472479
valFieldId.safeJField:Field?
473480
get()= declaringClass.jClass.declaredFields.firstOrNull { it.name== name }

‎utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ private val logger = KotlinLogging.logger {}
2222

2323
object SpringModelUtils {
2424
val autowiredClassId=ClassId("org.springframework.beans.factory.annotation.Autowired")
25+
val injectClassIds= getClassIdFromEachAvailablePackage(
26+
packages=listOf("javax","jakarta"),
27+
classNameFromPackage="inject.Inject"
28+
)
29+
val componentClassId=ClassId("org.springframework.stereotype.Component")
2530

2631
val applicationContextClassId=ClassId("org.springframework.context.ApplicationContext")
2732
val repositoryClassId=ClassId("org.springframework.data.repository.Repository")

‎utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import org.utbot.engine.ResolvedModels
88
importorg.utbot.framework.UtSettings
99
importorg.utbot.framework.codegen.util.isAccessibleFrom
1010
importorg.utbot.modifications.AnalysisMode.SettersAndDirectAccessors
11-
importorg.utbot.modifications.ConstructorAnalyzer
12-
importorg.utbot.modifications.ConstructorAssembleInfo
11+
importorg.utbot.modifications.ExecutableAnalyzer
12+
importorg.utbot.modifications.ExecutableAssembleInfo
1313
importorg.utbot.modifications.UtBotFieldsModificatorsSearcher
1414
importorg.utbot.framework.plugin.api.ClassId
1515
importorg.utbot.framework.plugin.api.ConstructorId
@@ -77,7 +77,7 @@ class AssembleModelGenerator(private val basePackageName: String) {
7777
UtBotFieldsModificatorsSearcher(
7878
fieldInvolvementMode=FieldInvolvementMode.WriteOnly
7979
)
80-
privatevalconstructorAnalyzer=ConstructorAnalyzer()
80+
privatevalexecutableAnalyzer=ExecutableAnalyzer()
8181

8282
/**
8383
* Clears state before and after block execution.
@@ -284,8 +284,8 @@ class AssembleModelGenerator(private val basePackageName: String) {
284284
modelsInAnalysis.add(compositeModel)
285285

286286
val constructorInfo=
287-
if (shouldAnalyzeConstructor)constructorAnalyzer.analyze(constructorId)
288-
elseConstructorAssembleInfo(constructorId)
287+
if (shouldAnalyzeConstructor)executableAnalyzer.analyze(constructorId)
288+
elseExecutableAssembleInfo(constructorId)
289289

290290
val instantiationCall= constructorCall(compositeModel, constructorInfo)
291291
returnUtAssembleModel(
@@ -406,9 +406,9 @@ class AssembleModelGenerator(private val basePackageName: String) {
406406
*/
407407
privatefunconstructorCall(
408408
compositeModel:UtCompositeModel,
409-
constructorInfo:ConstructorAssembleInfo,
409+
constructorInfo:ExecutableAssembleInfo,
410410
):UtExecutableCallModel {
411-
val constructorParams= constructorInfo.constructorId.parameters.withIndex()
411+
val constructorParams= constructorInfo.executableId.parameters.withIndex()
412412
.map { (index, param)->
413413
val modelOrNull= compositeModel.fields
414414
.filter { it.key== constructorInfo.params[index] }
@@ -418,7 +418,7 @@ class AssembleModelGenerator(private val basePackageName: String) {
418418
assembleModel(fieldModel)
419419
}
420420

421-
returnUtExecutableCallModel(instance=null, constructorInfo.constructorId, constructorParams)
421+
returnUtExecutableCallModel(instance=null, constructorInfo.executableId, constructorParams)
422422
}
423423

424424
/**
@@ -445,11 +445,11 @@ class AssembleModelGenerator(private val basePackageName: String) {
445445
val fromUtilPackage= classId.packageName.startsWith("java.util")
446446
constructorIds
447447
.sortedBy { it.parameters.size }
448-
.firstOrNull { it.parameters.isEmpty()&& fromUtilPackage||constructorAnalyzer.isAppropriate(it) }
448+
.firstOrNull { it.parameters.isEmpty()&& fromUtilPackage||executableAnalyzer.isAppropriate(it) }
449449
}else {
450450
constructorIds
451451
.sortedByDescending { it.parameters.size }
452-
.firstOrNull {constructorAnalyzer.isAppropriate(it) }
452+
.firstOrNull {executableAnalyzer.isAppropriate(it) }
453453
}
454454
}
455455

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,28 @@
11
packageorg.utbot.framework.context
22

33
importorg.utbot.framework.plugin.api.ClassId
4+
importorg.utbot.framework.plugin.api.FieldId
45
importsoot.SootField
56

7+
/**
8+
* Checks whether accessing [field] (with a method invocation or field access) speculatively
9+
* cannot produce [NullPointerException] (according to its finality or accessibility).
10+
*
11+
* @see docs/SpeculativeFieldNonNullability.md for more information.
12+
*
13+
* NOTE: methods for both [FieldId] and [SootField] are provided, because for some lambdas
14+
* class names in byte code and in Soot do not match, making conversion between two field
15+
* representations not always possible, which in turn makes us to support both [FieldId]
16+
* and [SootField] to be useful for both fuzzer and symbolic engine respectively.
17+
*/
618
interfaceNonNullSpeculator {
7-
/**
8-
* Checks whether accessing [field] (with a method invocation or field access) speculatively
9-
* cannot produce [NullPointerException] (according to its finality or accessibility).
10-
*
11-
* @see docs/SpeculativeFieldNonNullability.md for more information.
12-
*/
1319
funspeculativelyCannotProduceNullPointerException(
1420
field:SootField,
1521
classUnderTest:ClassId,
1622
):Boolean
23+
24+
funspeculativelyCannotProduceNullPointerException(
25+
field:FieldId,
26+
classUnderTest:ClassId,
27+
):Boolean
1728
}

‎utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleNonNullSpeculator.kt‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import org.utbot.framework.UtSettings
44
importorg.utbot.framework.context.NonNullSpeculator
55
importorg.utbot.framework.isFromTrustedLibrary
66
importorg.utbot.framework.plugin.api.ClassId
7+
importorg.utbot.framework.plugin.api.FieldId
8+
importorg.utbot.framework.plugin.api.util.isFinal
9+
importorg.utbot.framework.plugin.api.util.isPublic
710
importsoot.SootField
811

912
classSimpleNonNullSpeculator :NonNullSpeculator {
@@ -14,4 +17,12 @@ class SimpleNonNullSpeculator : NonNullSpeculator {
1417
!UtSettings.maximizeCoverageUsingReflection&&
1518
field.declaringClass.isFromTrustedLibrary()&&
1619
(field.isFinal||!field.isPublic)
20+
21+
overridefunspeculativelyCannotProduceNullPointerException(
22+
field:FieldId,
23+
classUnderTest:ClassId
24+
):Boolean=
25+
!UtSettings.maximizeCoverageUsingReflection&&
26+
field.declaringClass.isFromTrustedLibrary()&&
27+
(field.isFinal||!field.isPublic)
1728
}

‎utbot-framework/src/main/kotlin/org/utbot/framework/context/utils/ApplicationContextUtils.kt‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.utbot.framework.context.utils
22

33
importorg.utbot.framework.context.ApplicationContext
44
importorg.utbot.framework.context.ConcreteExecutionContext
5+
importorg.utbot.framework.context.ConcreteExecutionContext.FuzzingContextParams
56
importorg.utbot.fuzzing.JavaValueProvider
67

78
fun ApplicationContext.transformConcreteExecutionContext(
@@ -18,5 +19,5 @@ fun ApplicationContext.transformConcreteExecutionContext(
1819
}
1920

2021
fun ApplicationContext.transformValueProvider(
21-
transformer: (JavaValueProvider)->JavaValueProvider
22+
transformer:FuzzingContextParams.(JavaValueProvider)->JavaValueProvider
2223
)= transformConcreteExecutionContext { it.transformValueProvider(transformer) }

‎utbot-framework/src/main/kotlin/org/utbot/framework/context/utils/ConcreteExecutionContextUtils.kt‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ import org.utbot.framework.context.ConcreteExecutionContext
44
importorg.utbot.framework.context.ConcreteExecutionContext.FuzzingContextParams
55
importorg.utbot.framework.context.JavaFuzzingContext
66
importorg.utbot.fuzzing.JavaValueProvider
7+
importorg.utbot.instrumentation.instrumentation.execution.UtExecutionInstrumentation
8+
9+
fun ConcreteExecutionContext.transformInstrumentationFactory(
10+
transformer: (UtExecutionInstrumentation.Factory<*>)->UtExecutionInstrumentation.Factory<*>
11+
)=object:ConcreteExecutionContext bythis {
12+
overrideval instrumentationFactory:UtExecutionInstrumentation.Factory<*>=
13+
transformer(this@transformInstrumentationFactory.instrumentationFactory)
14+
}
715

816
fun ConcreteExecutionContext.transformJavaFuzzingContext(
917
transformer:FuzzingContextParams.(JavaFuzzingContext)->JavaFuzzingContext
@@ -14,5 +22,7 @@ fun ConcreteExecutionContext.transformJavaFuzzingContext(
1422
}
1523

1624
fun ConcreteExecutionContext.transformValueProvider(
17-
transformer: (JavaValueProvider)->JavaValueProvider
18-
)= transformJavaFuzzingContext { it.transformValueProvider(transformer) }
25+
transformer:FuzzingContextParams.(JavaValueProvider)->JavaValueProvider
26+
)= transformJavaFuzzingContext { javaFuzzingContext->
27+
javaFuzzingContext.transformValueProvider { valueProvider-> transformer(valueProvider) }
28+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
packageorg.utbot.fuzzing.spring
2+
3+
importorg.utbot.framework.plugin.api.ClassId
4+
importorg.utbot.framework.plugin.api.util.jClass
5+
importorg.utbot.framework.plugin.api.util.objectClassId
6+
importorg.utbot.fuzzer.FuzzedType
7+
importorg.utbot.fuzzer.FuzzedValue
8+
importorg.utbot.fuzzer.toTypeParametrizedByTypeVariables
9+
importorg.utbot.fuzzing.FuzzedDescription
10+
importorg.utbot.fuzzing.JavaValueProvider
11+
importorg.utbot.fuzzing.Routine
12+
importorg.utbot.fuzzing.Seed
13+
importorg.utbot.fuzzing.providers.nullRoutine
14+
importorg.utbot.fuzzing.toFuzzerType
15+
16+
classJavaLangObjectValueProvider(
17+
privatevalclassesToTryUsingAsJavaLangObject:List<ClassId>,
18+
) : JavaValueProvider {
19+
overridefunaccept(type:FuzzedType):Boolean {
20+
return type.classId== objectClassId
21+
}
22+
23+
overridefungenerate(description:FuzzedDescription,type:FuzzedType):Sequence<Seed<FuzzedType,FuzzedValue>>=
24+
classesToTryUsingAsJavaLangObject.map { classToUseAsObject->
25+
val fuzzedType= toFuzzerType(
26+
type= classToUseAsObject.jClass.toTypeParametrizedByTypeVariables(),
27+
cache= description.typeCache
28+
)
29+
Seed.Recursive(
30+
construct=Routine.Create(listOf(fuzzedType)) { (value)-> value },
31+
modify= emptySequence(),
32+
empty= nullRoutine(type.classId)
33+
)
34+
}.asSequence()
35+
}

‎utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/spring/unit/InjectMocks.kt‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
packageorg.utbot.fuzzing.spring.unit
22

33
importorg.utbot.framework.plugin.api.ClassId
4+
importorg.utbot.framework.plugin.api.FieldId
45
importorg.utbot.framework.plugin.api.UtCompositeModel
56
importorg.utbot.framework.plugin.api.util.allDeclaredFieldIds
67
importorg.utbot.framework.plugin.api.util.isFinal
@@ -30,7 +31,8 @@ val INJECT_MOCK_FLAG = ScopeProperty<Unit>(
3031
*/
3132
classInjectMockValueProvider(
3233
privatevalidGenerator:IdGenerator<Int>,
33-
privatevalclassUnderTest:ClassId
34+
privatevalclassUnderTest:ClassId,
35+
privatevalisFieldNonNull: (FieldId)->Boolean
3436
) : JavaValueProvider {
3537
overridefunenrich(description:FuzzedDescription,type:FuzzedType,scope:Scope) {
3638
if (description.description.isStatic==false&& scope.parameterIndex==0&& scope.recursionDepth==1) {
@@ -43,14 +45,24 @@ class InjectMockValueProvider(
4345
overridefungenerate(description:FuzzedDescription,type:FuzzedType):Sequence<Seed<FuzzedType,FuzzedValue>> {
4446
if (description.scope?.getProperty(INJECT_MOCK_FLAG)==null)return emptySequence()
4547
val fields= type.classId.allDeclaredFieldIds.filterNot { it.isStatic&& it.isFinal }.toList()
48+
val (nonNullFields, nullableFields)= fields.partition(isFieldNonNull)
4649
return sequenceOf(Seed.Recursive(
47-
construct=Routine.Create(types= fields.map { toFuzzerType(it.jField.genericType, description.typeCache) }) { values->
50+
construct=Routine.Create(
51+
types= nonNullFields.map { toFuzzerType(it.jField.genericType, description.typeCache) }
52+
) { values->
4853
emptyFuzzedValue(type.classId).also {
4954
(it.modelasUtCompositeModel).fields.putAll(
50-
fields.zip(values).associate { (field, value)-> field to value.model }
55+
nonNullFields.zip(values).associate { (field, value)-> field to value.model }
5156
)
5257
}
5358
},
59+
modify= nullableFields.map { field->
60+
Routine.Call<FuzzedType,FuzzedValue>(
61+
types=listOf(toFuzzerType(field.jField.genericType, description.typeCache))
62+
) { instance, (value)->
63+
(instance.modelasUtCompositeModel).fields[field]= value.model
64+
}
65+
}.asSequence(),
5466
empty=Routine.Empty { emptyFuzzedValue(type.classId) }
5567
))
5668
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp