Skip to content

ClassCastException when processing UtEnumConstantModel #230

Closed
@dtim

Description

@dtim

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
}

Metadata

Metadata

Assignees

Labels

comp-symbolic-engineIssue is related to the symbolic execution enginectg-bugIssue is a bug

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions