From 0c54c4edf9d20f4bf08b793f86f13188f35214ed Mon Sep 17 00:00:00 2001 From: Maksim Pelevin Date: Wed, 20 Sep 2023 09:40:01 +0300 Subject: [PATCH 1/3] Add more correct type resolves for collections and maps --- .../utbot/fuzzing/providers/Collections.kt | 51 +++++++++++++++- .../utbot/fuzzing/samples/ConcreateMap.java | 6 ++ .../utbot/fuzzing/samples/ConcreteList.java | 7 +++ .../utbot/fuzzing/JavaValueProviderTest.kt | 61 +++++++++++++++++++ 4 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 utbot-java-fuzzing/src/test/java/org/utbot/fuzzing/samples/ConcreateMap.java create mode 100644 utbot-java-fuzzing/src/test/java/org/utbot/fuzzing/samples/ConcreteList.java create mode 100644 utbot-java-fuzzing/src/test/kotlin/org/utbot/fuzzing/JavaValueProviderTest.kt diff --git a/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/Collections.kt b/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/Collections.kt index c860fbbd33..51aad4abfb 100644 --- a/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/Collections.kt +++ b/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/Collections.kt @@ -1,5 +1,6 @@ package org.utbot.fuzzing.providers +import com.google.common.reflect.TypeToken import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.util.* import org.utbot.fuzzer.FuzzedType @@ -7,7 +8,9 @@ import org.utbot.fuzzer.FuzzedValue import org.utbot.fuzzer.IdGenerator import org.utbot.fuzzer.fuzzed import org.utbot.fuzzing.* +import org.utbot.fuzzing.spring.utils.jType import org.utbot.fuzzing.utils.hex +import java.lang.reflect.Method import kotlin.reflect.KClass class EmptyCollectionValueProvider( @@ -80,9 +83,25 @@ class EmptyCollectionValueProvider( class MapValueProvider( idGenerator: IdGenerator ) : CollectionValueProvider(idGenerator, java.util.Map::class.id) { + + private enum class MethodCall { KEYS, VALUES } + + private fun findTypeByMethod(description: FuzzedDescription, type: FuzzedType, method: MethodCall): FuzzedType { + val methodName = when (method) { + MethodCall.KEYS -> "keySet" + MethodCall.VALUES -> "values" + } + val m = Map::class.java.getMethod(methodName) + return resolveTypeByMethod(description, type, m)?.let { + assert(it.classId.isSubtypeOf(collectionClassId)) + assert(it.generics.size == 1) + it.generics[0] + } ?: FuzzedType(objectClassId) + } + override fun resolveType(description: FuzzedDescription, type: FuzzedType) = sequence { - val keyGeneric = type.generics.getOrNull(0) ?: FuzzedType(objectClassId) - val valueGeneric = type.generics.getOrNull(1) ?: FuzzedType(objectClassId) + val keyGeneric = findTypeByMethod(description, type, MethodCall.KEYS) + val valueGeneric = findTypeByMethod(description, type, MethodCall.VALUES) when (type.classId) { java.util.Map::class.id -> { if (keyGeneric.classId isSubtypeOf Comparable::class) { @@ -108,8 +127,20 @@ class MapValueProvider( class ListSetValueProvider( idGenerator: IdGenerator ) : CollectionValueProvider(idGenerator, java.util.Collection::class.id) { + + private val iteratorClassId = java.util.Iterator::class.java.id + + private fun findTypeByMethod(description: FuzzedDescription, type: FuzzedType): FuzzedType { + val method = java.util.Collection::class.java.getMethod("iterator") + return resolveTypeByMethod(description, type, method)?.let { + assert(it.classId.isSubtypeOf(iteratorClassId)) + assert(it.generics.size == 1) + it.generics[0] + } ?: FuzzedType(objectClassId) + } + override fun resolveType(description: FuzzedDescription, type: FuzzedType) = sequence { - val generic = type.generics.firstOrNull() ?: FuzzedType(objectClassId) + val generic = findTypeByMethod(description, type) when (type.classId) { java.util.Queue::class.id, java.util.Deque::class.id-> { @@ -171,6 +202,20 @@ abstract class CollectionValueProvider( } } + /** + * Can be used to resolve some types using [type] and some method of this type + */ + protected fun resolveTypeByMethod(description: FuzzedDescription, type: FuzzedType, method: Method): FuzzedType? { + return try { + toFuzzerType( + TypeToken.of(type.jType).resolveType(method.genericReturnType).type, + description.typeCache + ) + } catch (t: Throwable) { + null + } + } + /** * Types should be resolved with type parameters */ diff --git a/utbot-java-fuzzing/src/test/java/org/utbot/fuzzing/samples/ConcreateMap.java b/utbot-java-fuzzing/src/test/java/org/utbot/fuzzing/samples/ConcreateMap.java new file mode 100644 index 0000000000..36a7891e10 --- /dev/null +++ b/utbot-java-fuzzing/src/test/java/org/utbot/fuzzing/samples/ConcreateMap.java @@ -0,0 +1,6 @@ +package org.utbot.fuzzing.samples; + +import java.util.HashMap; + +public class ConcreateMap extends HashMap { +} diff --git a/utbot-java-fuzzing/src/test/java/org/utbot/fuzzing/samples/ConcreteList.java b/utbot-java-fuzzing/src/test/java/org/utbot/fuzzing/samples/ConcreteList.java new file mode 100644 index 0000000000..14d1749cd8 --- /dev/null +++ b/utbot-java-fuzzing/src/test/java/org/utbot/fuzzing/samples/ConcreteList.java @@ -0,0 +1,7 @@ +package org.utbot.fuzzing.samples; + +import java.util.ArrayList; +import java.util.Collection; + +public class ConcreteList extends ArrayList> { +} diff --git a/utbot-java-fuzzing/src/test/kotlin/org/utbot/fuzzing/JavaValueProviderTest.kt b/utbot-java-fuzzing/src/test/kotlin/org/utbot/fuzzing/JavaValueProviderTest.kt new file mode 100644 index 0000000000..32fcf7b156 --- /dev/null +++ b/utbot-java-fuzzing/src/test/kotlin/org/utbot/fuzzing/JavaValueProviderTest.kt @@ -0,0 +1,61 @@ +package org.utbot.fuzzing + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.utbot.framework.plugin.api.Instruction +import org.utbot.framework.plugin.api.util.collectionClassId +import org.utbot.framework.plugin.api.util.id +import org.utbot.framework.plugin.api.util.stringClassId +import org.utbot.framework.plugin.api.util.voidClassId +import org.utbot.fuzzer.FuzzedMethodDescription +import org.utbot.fuzzer.FuzzedType +import org.utbot.fuzzing.providers.ListSetValueProvider +import org.utbot.fuzzing.providers.MapValueProvider +import org.utbot.fuzzing.samples.ConcreateMap +import org.utbot.fuzzing.samples.ConcreteList +import org.utbot.fuzzing.utils.Trie +import java.lang.reflect.Type +import kotlin.random.Random + +fun emptyFuzzerDescription(typeCache: MutableMap) = FuzzedDescription( + FuzzedMethodDescription("no name", voidClassId, emptyList()), + Trie(Instruction::id), + typeCache, + Random(42) +) + +class JavaValueProviderTest { + + @Test + fun `collection value provider correctly resolves types for concrete types of map`() { + val typeCache = mutableMapOf() + runBlockingWithContext { + val seed = MapValueProvider(TestIdentityPreservingIdGenerator).generate( + emptyFuzzerDescription(typeCache), + toFuzzerType(ConcreateMap::class.java, typeCache) + ).first() + val collection = seed as Seed.Collection + val types = collection.modify.types + Assertions.assertEquals(2, types.size) + Assertions.assertEquals(types[0].classId, stringClassId) + Assertions.assertEquals(types[1].classId, java.lang.Number::class.java.id) + } + } + + @Test + fun `collection value provider correctly resolves types for concrete types of list`() { + val typeCache = mutableMapOf() + runBlockingWithContext { + val seed = ListSetValueProvider(TestIdentityPreservingIdGenerator).generate( + emptyFuzzerDescription(typeCache), + toFuzzerType(ConcreteList::class.java, typeCache) + ).first() + val collection = seed as Seed.Collection + val types = collection.modify.types + Assertions.assertEquals(1, types.size) + Assertions.assertEquals(types[0].classId, collectionClassId) + Assertions.assertEquals(1, types[0].generics.size) + Assertions.assertEquals(types[0].generics[0].classId, stringClassId) + } + } +} \ No newline at end of file From c8643682087f6b73692a99dfa4168c17d4ed3f80 Mon Sep 17 00:00:00 2001 From: Maksim Pelevin Date: Wed, 20 Sep 2023 12:41:47 +0300 Subject: [PATCH 2/3] Better way to get generics --- .../utbot/fuzzing/providers/Collections.kt | 64 ++++++------------- 1 file changed, 20 insertions(+), 44 deletions(-) diff --git a/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/Collections.kt b/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/Collections.kt index 51aad4abfb..ad3583084a 100644 --- a/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/Collections.kt +++ b/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/Collections.kt @@ -84,24 +84,8 @@ class MapValueProvider( idGenerator: IdGenerator ) : CollectionValueProvider(idGenerator, java.util.Map::class.id) { - private enum class MethodCall { KEYS, VALUES } - - private fun findTypeByMethod(description: FuzzedDescription, type: FuzzedType, method: MethodCall): FuzzedType { - val methodName = when (method) { - MethodCall.KEYS -> "keySet" - MethodCall.VALUES -> "values" - } - val m = Map::class.java.getMethod(methodName) - return resolveTypeByMethod(description, type, m)?.let { - assert(it.classId.isSubtypeOf(collectionClassId)) - assert(it.generics.size == 1) - it.generics[0] - } ?: FuzzedType(objectClassId) - } - override fun resolveType(description: FuzzedDescription, type: FuzzedType) = sequence { - val keyGeneric = findTypeByMethod(description, type, MethodCall.KEYS) - val valueGeneric = findTypeByMethod(description, type, MethodCall.VALUES) + val (keyGeneric, valueGeneric) = resolveGenericsOfSuperClass>(description, type) when (type.classId) { java.util.Map::class.id -> { if (keyGeneric.classId isSubtypeOf Comparable::class) { @@ -128,19 +112,8 @@ class ListSetValueProvider( idGenerator: IdGenerator ) : CollectionValueProvider(idGenerator, java.util.Collection::class.id) { - private val iteratorClassId = java.util.Iterator::class.java.id - - private fun findTypeByMethod(description: FuzzedDescription, type: FuzzedType): FuzzedType { - val method = java.util.Collection::class.java.getMethod("iterator") - return resolveTypeByMethod(description, type, method)?.let { - assert(it.classId.isSubtypeOf(iteratorClassId)) - assert(it.generics.size == 1) - it.generics[0] - } ?: FuzzedType(objectClassId) - } - override fun resolveType(description: FuzzedDescription, type: FuzzedType) = sequence { - val generic = findTypeByMethod(description, type) + val (generic) = resolveGenericsOfSuperClass>(description, type) when (type.classId) { java.util.Queue::class.id, java.util.Deque::class.id-> { @@ -202,20 +175,6 @@ abstract class CollectionValueProvider( } } - /** - * Can be used to resolve some types using [type] and some method of this type - */ - protected fun resolveTypeByMethod(description: FuzzedDescription, type: FuzzedType, method: Method): FuzzedType? { - return try { - toFuzzerType( - TypeToken.of(type.jType).resolveType(method.genericReturnType).type, - description.typeCache - ) - } catch (t: Throwable) { - null - } - } - /** * Types should be resolved with type parameters */ @@ -267,7 +226,7 @@ class IteratorValueProvider(val idGenerator: IdGenerator) : JavaValueProvid } override fun generate(description: FuzzedDescription, type: FuzzedType): Sequence> { - val generic = type.generics.firstOrNull() ?: FuzzedType(objectClassId) + val (generic) = resolveGenericsOfSuperClass>(description, type) return sequenceOf(Seed.Recursive( construct = Routine.Create(listOf(FuzzedType(iterableClassId, listOf(generic)))) { v -> val id = idGenerator.createId() @@ -306,4 +265,21 @@ class IteratorValueProvider(val idGenerator: IdGenerator) : JavaValueProvid } )) } +} + +private inline fun resolveGenericsOfSuperClass( + description: FuzzedDescription, + type: FuzzedType, +): List { + val superClass = T::class.java + return try { + check(superClass.isAssignableFrom(type.classId.jClass)) { "$superClass isn't super class of $type" } + @Suppress("UNCHECKED_CAST") + toFuzzerType( + TypeToken.of(type.jType).getSupertype(superClass as Class).type, + description.typeCache + ).generics + } catch (e: Throwable) { + superClass.typeParameters.map { toFuzzerType(it, description.typeCache) } + } } \ No newline at end of file From a9ca3e03f012927925580114b7742bd55cdecc7a Mon Sep 17 00:00:00 2001 From: Maksim Pelevin Date: Wed, 20 Sep 2023 13:59:59 +0300 Subject: [PATCH 3/3] Fix compilation error --- .../src/main/kotlin/org/utbot/fuzzing/providers/Collections.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/Collections.kt b/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/Collections.kt index ad3583084a..f408d4c563 100644 --- a/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/Collections.kt +++ b/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/Collections.kt @@ -7,10 +7,9 @@ import org.utbot.fuzzer.FuzzedType import org.utbot.fuzzer.FuzzedValue import org.utbot.fuzzer.IdGenerator import org.utbot.fuzzer.fuzzed +import org.utbot.fuzzer.jType import org.utbot.fuzzing.* -import org.utbot.fuzzing.spring.utils.jType import org.utbot.fuzzing.utils.hex -import java.lang.reflect.Method import kotlin.reflect.KClass class EmptyCollectionValueProvider(