Skip to content

ClassCastException when processing UtEnumConstantModel #230

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

Closed
dtim opened this issue Jun 17, 2022 · 1 comment · Fixed by #611
Closed

ClassCastException when processing UtEnumConstantModel #230

dtim opened this issue Jun 17, 2022 · 1 comment · Fixed by #611
Assignees
Labels
comp-symbolic-engine Issue is related to the symbolic execution engine ctg-bug Issue is a bug

Comments

@dtim
Copy link
Collaborator

dtim commented Jun 17, 2022

Description

Processing of Enum types may result in ClassCastException, as UtEnumConstantModel is incorrectly cast to UtReferenceModel in several situations, in particular:

  • ArrayObjectWrappers.kt: AssociativeArrayWrapper.value() (the location related to the reproducible example below).
  • CollectionWrappers.kt: constructKeysAndValues() (a similar type cast).

To Reproduce

Generate the test suite for the MapExamples class.

Color.java:

public enum Color {
    UNDEFINED,
    RED,
    YELLOW,
    GREEN
}

MapExamples.java:

public class MapExamples {
    public Color[] transduce(@NotNull Color[] inputs, @NotNull HashMap<Color, Color> fst) {
        if (inputs.length > 0) {
            Color[] result = new Color[inputs.length];
            for (int i = 0; i < inputs.length; i++) {
                result[i] = fst.getOrDefault(inputs[i], Color.UNDEFINED);
            }
            return result;
        } else {
            return new Color[0];
        }
    }

    public Color[] trafficLightTransduce(@NotNull Color[] inputs) {
        HashMap<Color, Color> trafficLight = new HashMap<>();
        trafficLight.put(Color.RED, Color.GREEN);
        trafficLight.put(Color.GREEN, Color.YELLOW);
        trafficLight.put(Color.YELLOW, Color.RED);

        return transduce(inputs, trafficLight);
    }
}

Expected behavior

Test suite is generated, no ClassCastException errors are reported.

Actual behavior

No tests are generated, the plugin fails with the following exception:

java.lang.ClassCastException: class org.utbot.framework.plugin.api.UtEnumConstantModel cannot be cast to class org.utbot.framework.plugin.api.UtReferenceModel (org.utbot.framework.plugin.api.UtEnumConstantModel and org.utbot.framework.plugin.api.UtReferenceModel are in unnamed module of loader com.intellij.ide.plugins.cl.PluginClassLoader @56d6d8d8)
	at org.utbot.engine.AssociativeArrayWrapper.value(ArrayObjectWrappers.kt:382)
	at org.utbot.engine.Resolver.resolveObject(Resolver.kt:454)
	at org.utbot.engine.Resolver.resolveReferenceValue(Resolver.kt:424)
	at org.utbot.engine.Resolver.resolveModel(Resolver.kt:395)
	at org.utbot.engine.Resolver.collectFieldModels(Resolver.kt:581)
	at org.utbot.engine.MapWrapper.resolveValueModels(CollectionWrappers.kt:309)
	at org.utbot.engine.BaseCollectionWrapper.value(CollectionWrappers.kt:108)
	at org.utbot.engine.BaseCollectionWrapper.value(CollectionWrappers.kt:99)
	at org.utbot.engine.Resolver.resolveObject(Resolver.kt:454)
	at org.utbot.engine.Resolver.resolveReferenceValue(Resolver.kt:424)
	at org.utbot.engine.Resolver.resolveModel(Resolver.kt:395)
	at org.utbot.engine.Resolver.internalResolveModel(Resolver.kt:233)
	at org.utbot.engine.Resolver.resolveModels$utbot_framework(Resolver.kt:204)
	at org.utbot.engine.UtBotSymbolicEngine$traverseImpl$1.invokeSuspend(UtBotSymbolicEngine.kt:466)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:274)
	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:84)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
	at org.utbot.common.ConcurrencyKt.runBlockingWithCancellationPredicate(Concurrency.kt:38)
	at org.utbot.framework.plugin.api.UtBotTestCaseGenerator$generateForSeveralMethods$4.invoke(UtBotTestCaseGenerator.kt:263)
	at org.utbot.framework.plugin.api.UtBotTestCaseGenerator$generateForSeveralMethods$4.invoke(UtBotTestCaseGenerator.kt:56)
	at org.utbot.common.ConcurrencyKt.runIgnoringCancellationException(Concurrency.kt:47)
	at org.utbot.framework.plugin.api.UtBotTestCaseGenerator.generateForSeveralMethods(UtBotTestCaseGenerator.kt:262)
	at org.utbot.framework.plugin.api.UtBotTestCaseGenerator.generateForSeveralMethods$default(UtBotTestCaseGenerator.kt:249)
	at org.utbot.intellij.plugin.generator.CodeGenerator.generateForSeveralMethods(CodeGenerator.kt:53)
	at org.utbot.intellij.plugin.ui.UtTestsDialogProcessor$createTests$2$1.run(UtTestsDialogProcessor.kt:164)
	at com.intellij.openapi.progress.impl.CoreProgressManager.startTask(CoreProgressManager.java:442)
	at com.intellij.openapi.progress.impl.ProgressManagerImpl.startTask(ProgressManagerImpl.java:114)
	at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$runProcessWithProgressAsynchronously$5(CoreProgressManager.java:493)
	at com.intellij.openapi.progress.impl.ProgressRunner.lambda$submit$3(ProgressRunner.java:244)
	at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$runProcess$2(CoreProgressManager.java:189)
	at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$executeProcessUnderProgress$12(CoreProgressManager.java:608)
	at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:683)
	at com.intellij.openapi.progress.impl.CoreProgressManager.computeUnderProgress(CoreProgressManager.java:639)
	at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:607)
	at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:60)
	at com.intellij.openapi.progress.impl.CoreProgressManager.runProcess(CoreProgressManager.java:176)
	at com.intellij.openapi.progress.impl.ProgressRunner.lambda$submit$4(ProgressRunner.java:244)
	at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:668)
	at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:665)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1.run(Executors.java:665)
	at java.base/java.lang.Thread.run(Thread.java:829)

It is possible that some tests are generated, but a non-empty error suite is generated as well (I encountered this case once but it seems hard to reproduce). See Additional context below for details.

Environment

Mock strategy: Other packages: Mockito
Mock static: No static mocking
Test framework: JUnit5

Additional context

I somehow managed to generate a non-empty test suite with several reported ClassCastException errors. It seems hard to reproduce (usually the generator just fails, so I am not sure that it is not a version mismatch from my side), but it might be useful to have this log.

public class MapExamplesTest {
    ///region Test suites for executable collections.MapExamples.transduce

    ///region SUCCESSFUL EXECUTIONS for method transduce(collections.Color[], java.util.HashMap)

    /**
     * <pre>
     * Test does not iterate {@code for(int i = 0; i < inputs.length; i++) }, executes conditions:
     *     {@code (inputs.length > 0): False }
     * returns from: {@code return new Color[0]; }
     * </pre>
     */
    @Test
    @DisplayName("transduce: inputs.length > 0 : False -> return new Color[0]")
    public void testTransduce_InputsLengthLessOrEqualZero() {
        MapExamples mapExamples = new MapExamples();
        Color[] colorArray = {};
        HashMap hashMap = new HashMap();

        Color[] actual = mapExamples.transduce(colorArray, hashMap);

        Color[] expected = {};

        int expectedSize = expected.length;
        assertEquals(expectedSize, actual.length);
        assertTrue(deepEquals(expected, actual));
    }

    /**
     * <pre>
     * Test executes conditions:
     *     {@code (inputs.length > 0): True }
     * iterates the loop {@code for(int i = 0; i < inputs.length; i++) } once.
     * Test then returns from: {@code return result; }
     * </pre>
     */
    @Test
    @DisplayName("transduce: inputs.length > 0 : True -> return result")
    public void testTransduce_InputsLengthGreaterThanZero() throws ClassNotFoundException, Exception {
        Color prevUNDEFINED = Color.UNDEFINED;
        try {
            Color undefined = Color.UNDEFINED;
            Class colorClazz = Class.forName("collections.Color");
            setStaticField(colorClazz, "UNDEFINED", undefined);
            MapExamples mapExamples = new MapExamples();
            Color[] colorArray = {null};
            HashMap hashMap = new HashMap();

            Color[] actual = mapExamples.transduce(colorArray, hashMap);

            Color[] expected = new Color[1];
            Color color = Color.UNDEFINED;
            expected[0] = color;

            int expectedSize = expected.length;
            assertEquals(expectedSize, actual.length);
            assertTrue(deepEquals(expected, actual));

            Color finalColorArray0 = colorArray[0];

            assertNull(finalColorArray0);
        } finally {
            setStaticField(Color.class, "UNDEFINED", prevUNDEFINED);
        }
    }
    ///endregion

    ///region Errors report for transduce

    public void testTransduce_errors() {
        // Couldn't generate some tests. List of errors:
        // 
        // 18 occurrences of:
        /* class org.utbot.framework.plugin.api.UtEnumConstantModel cannot be cast to class org.utbot.framework.plugin.api.UtReferenceModel (org.utbot.framework.plugin.api.UtEnumConstantModel and
        org.utbot.framework.plugin.api.UtReferenceModel are in unnamed module of loader com.intellij.ide.plugins.cl.PluginClassLoader @33114743) */

    }
    ///endregion

    ///endregion

    ///region Test suites for executable collections.MapExamples.trafficLightTransduce

    ///region SUCCESSFUL EXECUTIONS for method trafficLightTransduce(collections.Color[])

    /**
     * <pre>
     * Test calls MapExamples::transduce,
     *     there it does not iterate {@code for(int i = 0; i < inputs.length; i++) }, executes conditions:
     *         {@code (inputs.length > 0): False }
     *     returns from: {@code return new Color[0]; }
     *
     * Test afterwards returns from: {@code return transduce(inputs, trafficLight); }
     * </pre>
     */
    @Test
    @DisplayName("trafficLightTransduce: inputs.length > 0 : False -> return new Color[0]")
    public void testTrafficLightTransduce_InputsLengthLessOrEqualZero() throws ClassNotFoundException, Exception {
        Color prevRED = Color.RED;
        Color prevGREEN = Color.GREEN;
        Color prevYELLOW = Color.YELLOW;
        try {
            Color red = Color.RED;
            Class colorClazz = Class.forName("collections.Color");
            setStaticField(colorClazz, "RED", red);
            Color green = Color.GREEN;
            setStaticField(colorClazz, "GREEN", green);
            Color yellow = Color.YELLOW;
            setStaticField(colorClazz, "YELLOW", yellow);
            MapExamples mapExamples = new MapExamples();
            Color[] colorArray = {};

            Color[] actual = mapExamples.trafficLightTransducer(colorArray);

            Color[] expected = {};

            int expectedSize = expected.length;
            assertEquals(expectedSize, actual.length);
            assertTrue(deepEquals(expected, actual));
        } finally {
            setStaticField(Color.class, "RED", prevRED);
            setStaticField(Color.class, "GREEN", prevGREEN);
            setStaticField(Color.class, "YELLOW", prevYELLOW);
        }
    }

    /**
     * <pre>
     * Test calls MapExamples::transduce,
     *     there it executes conditions:
     *         {@code (inputs.length > 0): True }
     *     iterates the loop {@code for(int i = 0; i < inputs.length; i++) } once.
     *     Test then returns from: {@code return result; }
     *
     * Test next returns from: {@code return transduce(inputs, trafficLight); }
     * </pre>
     */
    @Test
    @DisplayName("trafficLightTransduce: inputs.length > 0 : True -> return result")
    public void testTrafficLightTransduce_InputsLengthGreaterThanZero() throws ClassNotFoundException, Exception {
        Color prevRED = Color.RED;
        Color prevGREEN = Color.GREEN;
        Color prevYELLOW = Color.YELLOW;
        Color prevUNDEFINED = Color.UNDEFINED;
        try {
            Color red = Color.RED;
            Class colorClazz = Class.forName("collections.Color");
            setStaticField(colorClazz, "RED", red);
            Color green = Color.GREEN;
            setStaticField(colorClazz, "GREEN", green);
            Color yellow = Color.YELLOW;
            setStaticField(colorClazz, "YELLOW", yellow);
            Color undefined = Color.UNDEFINED;
            setStaticField(colorClazz, "UNDEFINED", undefined);
            MapExamples mapExamples = new MapExamples();
            Color[] colorArray = {null};

            Color[] actual = mapExamples.trafficLightTransducer(colorArray);

            Color[] expected = new Color[1];
            Color color = Color.UNDEFINED;
            expected[0] = color;

            int expectedSize = expected.length;
            assertEquals(expectedSize, actual.length);
            assertTrue(deepEquals(expected, actual));

            Color finalColorArray0 = colorArray[0];

            assertNull(finalColorArray0);
        } finally {
            setStaticField(Color.class, "RED", prevRED);
            setStaticField(Color.class, "GREEN", prevGREEN);
            setStaticField(Color.class, "YELLOW", prevYELLOW);
            setStaticField(Color.class, "UNDEFINED", prevUNDEFINED);
        }
    }
    ///endregion

    ///endregion
}
@dtim dtim added ctg-bug Issue is a bug comp-symbolic-engine Issue is related to the symbolic execution engine labels Jun 17, 2022
@dtim dtim self-assigned this Jun 17, 2022
@korifey korifey moved this to Todo in UTBot Java Jun 17, 2022
@dtim dtim removed this from UTBot Java Jun 17, 2022
@dtim dtim moved this to Todo in UTBot Java Jun 22, 2022
@dtim dtim added this to UTBot Java Jun 22, 2022
@dtim dtim moved this from Todo to In Progress in UTBot Java Jun 22, 2022
@dtim
Copy link
Collaborator Author

dtim commented Jul 6, 2022

Related to #414

dtim added a commit that referenced this issue Jul 27, 2022
Historically `UtEnumConstModel` and `UtClassRefModel` have been
processed not as other reference models but in a special way,
more like to primitive types. This approach leads to several
problems, especially to class cast errors when processing generic
collections with enums or class references as elements.

This commit makes `UtEnumConstModel` and `UtClassRefModel` subtypes of
`UtReferenceModel`.

  * Concrete executor is modified to respect the identity of static
    fields to avoid rewriting enum values and `Class<?>` instances.

  * Special processing for enums is implemented.
    When a new enum value is created, or an `Object` is being cast
    to the enum type, static values for the enum class are initialized,
    and the set of hard constraint is added to require that the new
    instance has the same address and ordinal as obe of enum constants
    to implement reference equality for enums.
dtim added a commit that referenced this issue Jul 27, 2022
Historically `UtEnumConstModel` and `UtClassRefModel` have been
processed not as other reference models but in a special way,
more like to primitive types. This approach leads to several
problems, especially to class cast errors when processing generic
collections with enums or class references as elements.

This commit makes `UtEnumConstModel` and `UtClassRefModel` subtypes of
`UtReferenceModel`.

  * Concrete executor is modified to respect the identity of static
    fields to avoid rewriting enum values and `Class<?>` instances.

  * Special processing for enums is implemented.
    When a new enum value is created, or an `Object` is being cast
    to the enum type, static values for the enum class are initialized,
    and the set of hard constraint is added to require that the new
    instance has the same address and ordinal as obe of enum constants
    to implement reference equality for enums.
dtim added a commit that referenced this issue Jul 27, 2022
Historically `UtEnumConstModel` and `UtClassRefModel` have been
processed not as other reference models but in a special way,
more like to primitive types. This approach leads to several
problems, especially to class cast errors when processing generic
collections with enums or class references as elements.

This commit makes `UtEnumConstModel` and `UtClassRefModel` subtypes of
`UtReferenceModel`.

  * Concrete executor is modified to respect the identity of static
    fields to avoid rewriting enum values and `Class<?>` instances.

  * Special processing for enums is implemented.
    When a new enum value is created, or an `Object` is being cast
    to the enum type, static values for the enum class are initialized,
    and the set of hard constraint is added to require that the new
    instance has the same address and ordinal as any one of enum
    constants to implement reference equality for enums.
dtim added a commit that referenced this issue Jul 28, 2022
Historically `UtEnumConstModel` and `UtClassRefModel` have been
processed not as other reference models but in a special way,
more like to primitive types. This approach leads to several
problems, especially to class cast errors when processing generic
collections with enums or class references as elements.

This commit makes `UtEnumConstModel` and `UtClassRefModel` subtypes of
`UtReferenceModel`.

  * Concrete executor is modified to respect the identity of static
    fields to avoid rewriting enum values and `Class<?>` instances.

  * Special processing for enums is implemented.
    When a new enum value is created, or an `Object` is being cast
    to the enum type, static values for the enum class are initialized,
    and the set of hard constraint is added to require that the new
    instance has the same address and ordinal as any one of enum
    constants to implement reference equality for enums.
dtim added a commit that referenced this issue Jul 29, 2022
Historically `UtEnumConstModel` and `UtClassRefModel` have been
processed not as other reference models but in a special way,
more like to primitive types. This approach leads to several
problems, especially to class cast errors when processing generic
collections with enums or class references as elements.

This commit makes `UtEnumConstModel` and `UtClassRefModel` subtypes of
`UtReferenceModel`.

  * Concrete executor is modified to respect the identity of static
    fields to avoid rewriting enum values and `Class<?>` instances.

  * Special processing for enums is implemented.
    When a new enum value is created, or an `Object` is being cast
    to the enum type, static values for the enum class are initialized,
    and the set of hard constraint is added to require that the new
    instance has the same address and ordinal as any one of enum
    constants to implement reference equality for enums.
dtim added a commit that referenced this issue Jul 29, 2022
Historically `UtEnumConstModel` and `UtClassRefModel` have been
processed not as other reference models but in a special way,
more like to primitive types. This approach leads to several
problems, especially to class cast errors when processing generic
collections with enums or class references as elements.

This commit makes `UtEnumConstModel` and `UtClassRefModel` subtypes of
`UtReferenceModel`.

  * Concrete executor is modified to respect the identity of static
    fields to avoid rewriting enum values and `Class<?>` instances.

  * Special processing for enums is implemented.
    When a new enum value is created, or an `Object` is being cast
    to the enum type, static values for the enum class are initialized,
    and the set of hard constraint is added to require that the new
    instance has the same address and ordinal as any one of enum
    constants to implement reference equality for enums.

Fixes #414, #230
dtim added a commit that referenced this issue Jul 29, 2022
Historically `UtEnumConstModel` and `UtClassRefModel` have been
processed not as other reference models but in a special way,
more like to primitive types. This approach leads to several
problems, especially to class cast errors when processing generic
collections with enums or class references as elements.

This commit makes `UtEnumConstModel` and `UtClassRefModel` subtypes of
`UtReferenceModel`.

  * Concrete executor is modified to respect the identity of static
    fields to avoid rewriting enum values and `Class<?>` instances.

  * Special processing for enums is implemented.
    When a new enum value is created, or an `Object` is being cast
    to the enum type, static values for the enum class are initialized,
    and the set of hard constraint is added to require that the new
    instance has the same address and ordinal as any one of enum
    constants to implement reference equality for enums.

Fixes #414, #230, #300
@dtim dtim closed this as completed in #611 Aug 4, 2022
Repository owner moved this from In Progress to Done in UTBot Java Aug 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
comp-symbolic-engine Issue is related to the symbolic execution engine ctg-bug Issue is a bug
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

1 participant