Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more correct type resolving for collections and maps #2610

Merged

Conversation

Markoutte
Copy link
Collaborator

Description

Fixes #2571

The generic retrieving logic for collections and maps was naive. This PR improves that logic using guava type resolving to find out correct types. For every type it tries to resolve a type of returned values when calling such methods as Map.keySet, Map.values and Collection.iterator. A generic used in the returned type of those methods is a target type used for building a collection or a map.

Note, that the fix reveals another problem in the code generator. Because it uses untyped model it fails to generate a compilable test. For example, the result of fuzzing getAnyString method from the issue is follow:

    @Test
    @DisplayName("getAnyString: ")
    public void testGetAnyString() {
        Issue2571 issue2571 = new Issue2571();
        issue2571.put(((Object) "XZ"), ((Object) "abc"));

        String actual = issue2571.getAnyString();

        String expected = "XZ";

        assertEquals(expected, actual);
    }

This line issue2571.put(((Object) "XZ"), ((Object) "abc")); has syntax error because the map expects a string, not an object. @EgorkaKulikov , please, check this one.

How to test

Automated tests

New tests added:

The proposed changes are verified with tests:
utbot-java-fuzzing/src/test/kotlin/org/utbot/fuzzing/JavaValueProviderTest.kt`

Manual tests

All manual tests from collections samples should pass.

Self-check list

  • I've set the proper labels for my PR (at least, for category and component).
  • PR title and description are clear and intelligible.
  • I've added enough comments to my code, particularly in hard-to-understand areas.
  • The functionality I've repaired, changed or added is covered with automated tests.
  • Manual tests have been provided optionally.
  • The documentation for the functionality I've been working on is up-to-date.

@Markoutte Markoutte added comp-fuzzing Issue is related to the fuzzing ctg-bug-fix PR is fixing a bug labels Sep 20, 2023
import org.utbot.framework.plugin.api.*
import org.utbot.framework.plugin.api.util.*
import org.utbot.fuzzer.FuzzedType
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If TypeUtils are used here, then I think they should be moved out of spring package, similarly to how it's done in #2584

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved that

Comment on lines 84 to 104
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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was expecting that you'd add something like this to TypeUtils:

private fun resolveGenericsOfSuperClass(
    type: FuzzedType,
    description: FuzzedDescription,
    superClass: Class<*>,
): List<FuzzedType> {
    return try {
        check(superClass.isAssignableFrom(type.classId.jClass)) { "$superClass isn't super class of $type" }
        @Suppress("UNCHECKED_CAST")
        toFuzzerType(
            type.jType.typeToken.getSupertype(superClass as Class<in Any>).type,
            description.typeCache
        ).generics
    } catch (e: Throwable) {
        logger.error("Failed to resolve $superClass generics for $type, using unresolved type parameters")
        superClass.typeParameters.map { toFuzzerType(it, description.typeCache) }
    }
}

And use it like this:

val (keyGeneric, valueGeneric) = resolveGenericsOfSuperClass(type, description, Map::class.java)

But your approach should also work.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, this is much better. Fixed.

/**
* 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? {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In your PR this method is only used for Maps and Collections, but we also have an IteratorValueProvider, that also needs to resolve generics.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not really necessary, because IteratorValueProvider accepts only Iterator class itself, so, it has no modifications that can cause a problem. But I added the same call to unify these providers.

Copy link
Collaborator

@IlyaMuravjov IlyaMuravjov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly LGTM

TypeToken.of(type.jType).getSupertype(superClass as Class<in Any>).type,
description.typeCache
).generics
} catch (e: Throwable) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think e should be logged here, since it should never be thrown if everything goes right. Also, without logging check(superClass.isAssignableFrom(type.classId.jClass)) is pretty much useless.

@Markoutte Markoutte force-pushed the pelevin/2571-Fuzzer-ignores-generics-of-class-StringMap branch from 89ddbe6 to a9ca3e0 Compare September 20, 2023 11:00
@Markoutte Markoutte merged commit be47d83 into main Sep 21, 2023
@Markoutte Markoutte deleted the pelevin/2571-Fuzzer-ignores-generics-of-class-StringMap branch September 21, 2023 04:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
comp-fuzzing Issue is related to the fuzzing ctg-bug-fix PR is fixing a bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fuzzer ignores generics of class StringMap extends HashMap<String, String>
2 participants