diff --git a/utbot-framework-test/src/main/java/org/utbot/examples/manual/KotlinWrappers.kt b/utbot-framework-test/src/main/java/org/utbot/examples/manual/KotlinWrappers.kt index 29566f0138..31aa00d2ae 100644 --- a/utbot-framework-test/src/main/java/org/utbot/examples/manual/KotlinWrappers.kt +++ b/utbot-framework-test/src/main/java/org/utbot/examples/manual/KotlinWrappers.kt @@ -1,25 +1,15 @@ package org.utbot.examples.manual -import org.utbot.common.FileUtil import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtPrimitiveModel -import java.nio.file.Path object SootUtils { @JvmStatic fun runSoot(clazz: Class<*>) { - val buildDir = FileUtil.locateClassPath(clazz.kotlin) ?: FileUtil.isolateClassFiles(clazz.kotlin) - val buildDirPath = buildDir.toPath() - - if (buildDirPath != previousBuildDir) { - org.utbot.framework.util.runSoot(buildDirPath, null) - previousBuildDir = buildDirPath - } + org.utbot.framework.util.SootUtils.runSoot(clazz.kotlin) } - - private var previousBuildDir: Path? = null } fun fields( diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/manual/UtBotJavaApiTest.java b/utbot-framework-test/src/test/java/org/utbot/examples/manual/UtBotJavaApiTest.java similarity index 99% rename from utbot-framework-test/src/test/kotlin/org/utbot/examples/manual/UtBotJavaApiTest.java rename to utbot-framework-test/src/test/java/org/utbot/examples/manual/UtBotJavaApiTest.java index 25ba275781..3620956a2a 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/manual/UtBotJavaApiTest.java +++ b/utbot-framework-test/src/test/java/org/utbot/examples/manual/UtBotJavaApiTest.java @@ -31,7 +31,13 @@ import java.lang.reflect.Method; import java.net.URISyntaxException; import java.net.URL; -import java.util.*; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import static org.utbot.external.api.UtModelFactoryKt.classIdForType; @@ -60,7 +66,6 @@ public class UtBotJavaApiTest { @BeforeEach public void setUp() { - SootUtils.runSoot(PrimitiveFields.class); context = UtContext.Companion.setUtContext(new UtContext(PrimitiveFields.class.getClassLoader())); modelFactory = new UtModelFactory(); } @@ -1221,6 +1226,7 @@ public void testOnObjectWithArrayOfComplexArrays() { @Test public void testFuzzingSimple() { + SootUtils.runSoot(StringSwitchExample.class); UtBotJavaApi.setStopConcreteExecutorOnExit(false); String classpath = getClassPath(StringSwitchExample.class); diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/collections/QueueUsagesTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/collections/QueueUsagesTest.kt new file mode 100644 index 0000000000..f218b23945 --- /dev/null +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/collections/QueueUsagesTest.kt @@ -0,0 +1,127 @@ +package org.utbot.examples.collections + +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.utbot.framework.plugin.api.CodegenLanguage +import org.utbot.testcheckers.eq +import org.utbot.tests.infrastructure.CodeGeneration +import org.utbot.tests.infrastructure.UtValueTestCaseChecker +import org.utbot.tests.infrastructure.isException + +class QueueUsagesTest : UtValueTestCaseChecker( + testClass = QueueUsages::class, + testCodeGeneration = true, + languagePipelines = listOf( + CodeGenerationLanguageLastStage(CodegenLanguage.JAVA), + CodeGenerationLanguageLastStage(CodegenLanguage.KOTLIN, CodeGeneration) + ) +) { + @Test + fun testCreateArrayDeque() { + checkWithException( + QueueUsages::createArrayDeque, + eq(3), + { init, next, r -> init == null && next == null && r.isException() }, + { init, next, r -> init != null && next == null && r.isException() }, + { init, next, r -> init != null && next != null && r.getOrNull() == 2 }, + ) + } + + @Test + fun testCreateLinkedList() { + checkWithException( + QueueUsages::createLinkedList, + eq(1), + { _, _, r -> r.getOrNull()!! == 2 }, + ) + } + + @Test + fun testCreateLinkedBlockingDeque() { + checkWithException( + QueueUsages::createLinkedBlockingDeque, + eq(3), + { init, next, r -> init == null && next == null && r.isException() }, + { init, next, r -> init != null && next == null && r.isException() }, + { init, next, r -> init != null && next != null && r.getOrNull() == 2 }, + ) + } + + @Test + fun testContainsQueue() { + checkWithException( + QueueUsages::containsQueue, + eq(3), + { q, _, r -> q == null && r.isException() }, + { q, x, r -> x in q && r.getOrNull() == 1 }, + { q, x, r -> x !in q && r.getOrNull() == 0 }, + ) + } + + @Test + fun testAddQueue() { + checkWithException( + QueueUsages::addQueue, + eq(3), + { q, _, r -> q == null && r.isException() }, + { q, x, r -> q != null && x in r.getOrNull()!! }, + { q, x, r -> q != null && x == null && r.isException() }, ) + } + + @Test + fun testAddAllQueue() { + checkWithException( + QueueUsages::addAllQueue, + eq(3), + { q, _, r -> q == null && r.isException() }, + { q, x, r -> q != null && x in r.getOrNull()!! }, // we can cover this line with x == null or x != null + { q, x, r -> q != null && x == null && r.isException() }, + ) + } + + @Test + fun testCastQueueToDeque() { + check( + QueueUsages::castQueueToDeque, + eq(2), + { q, r -> q !is java.util.Deque<*> && r == null }, + { q, r -> q is java.util.Deque<*> && r is java.util.Deque<*> }, + ) + } + + @Test + fun testCheckSubtypesOfQueue() { + check( + QueueUsages::checkSubtypesOfQueue, + eq(4), + { q, r -> q == null && r == 0 }, + { q, r -> q is java.util.LinkedList<*> && r == 1 }, + { q, r -> q is java.util.ArrayDeque<*> && r == 2 }, + { q, r -> q !is java.util.LinkedList<*> && q !is java.util.ArrayDeque && r == 3 } + ) + } + + @Test + @Disabled("TODO: Related to https://github.com/UnitTestBot/UTBotJava/issues/820") + fun testCheckSubtypesOfQueueWithUsage() { + check( + QueueUsages::checkSubtypesOfQueueWithUsage, + eq(4), + { q, r -> q == null && r == 0 }, + { q, r -> q is java.util.LinkedList<*> && r == 1 }, + { q, r -> q is java.util.ArrayDeque<*> && r == 2 }, + { q, r -> q !is java.util.LinkedList<*> && q !is java.util.ArrayDeque && r == 3 } // this is uncovered + ) + } + + @Test + fun testAddConcurrentLinkedQueue() { + checkWithException( + QueueUsages::addConcurrentLinkedQueue, + eq(3), + { q, _, r -> q == null && r.isException() }, + { q, x, r -> q != null && x != null && x in r.getOrNull()!! }, + { q, x, r -> q != null && x == null && r.isException() }, + ) + } +} \ No newline at end of file diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/structures/StandardStructuresTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/structures/StandardStructuresTest.kt index 673d033edd..aff33bc2d3 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/structures/StandardStructuresTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/structures/StandardStructuresTest.kt @@ -12,9 +12,17 @@ import java.util.LinkedList import java.util.TreeMap import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test +import org.utbot.framework.plugin.api.CodegenLanguage import org.utbot.testcheckers.eq +import org.utbot.tests.infrastructure.CodeGeneration -internal class StandardStructuresTest : UtValueTestCaseChecker(testClass = StandardStructures::class) { +internal class StandardStructuresTest : UtValueTestCaseChecker( + testClass = StandardStructures::class, + languagePipelines = listOf( + CodeGenerationLanguageLastStage(CodegenLanguage.JAVA), + CodeGenerationLanguageLastStage(CodegenLanguage.KOTLIN, CodeGeneration) + ) +) { @Test @Disabled("TODO down cast for object wrapper JIRA:1480") fun testGetList() { @@ -45,7 +53,6 @@ internal class StandardStructuresTest : UtValueTestCaseChecker(testClass = Stand } @Test - @Disabled("TODO use correct wrapper JIRA:1495") fun testGetDeque() { val dequeSummary = listOf( DocPreTagStatement( @@ -72,7 +79,7 @@ internal class StandardStructuresTest : UtValueTestCaseChecker(testClass = Stand { d, r -> d is LinkedList && r is LinkedList }, { d, r -> d == null && r == null }, { d, r -> - d !is ArrayDeque<*> && d !is LinkedList && d != null && r !is ArrayDeque<*> && r !is LinkedList && r != null + d !is java.util.ArrayDeque<*> && d !is LinkedList && d != null && r !is java.util.ArrayDeque<*> && r !is LinkedList && r != null }, coverage = DoNotCalculate, summaryTextChecks = listOf( diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/framework/SootUtils.kt b/utbot-framework-test/src/test/kotlin/org/utbot/framework/SootUtils.kt deleted file mode 100644 index bfd9ea4750..0000000000 --- a/utbot-framework-test/src/test/kotlin/org/utbot/framework/SootUtils.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.utbot.framework - -import org.utbot.common.FileUtil -import org.utbot.framework.util.runSoot -import java.nio.file.Path -import kotlin.reflect.KClass - -object SootUtils { - - /** - * Runs Soot in tests if it hasn't already been done. - */ - fun runSoot(clazz: KClass<*>) { - val buildDir = FileUtil.locateClassPath(clazz) ?: FileUtil.isolateClassFiles(clazz) - val buildDirPath = buildDir.toPath() - - if (buildDirPath != previousBuildDir) { - runSoot(buildDirPath, null) - previousBuildDir = buildDirPath - } - } - - private var previousBuildDir: Path? = null -} diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/framework/assemble/AssembleModelGeneratorTests.kt b/utbot-framework-test/src/test/kotlin/org/utbot/framework/assemble/AssembleModelGeneratorTests.kt index fdc2fc5044..c390e05654 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/framework/assemble/AssembleModelGeneratorTests.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/framework/assemble/AssembleModelGeneratorTests.kt @@ -31,7 +31,6 @@ import org.utbot.examples.assemble.constructors.PrivateConstructor import org.utbot.examples.assemble.defaults.DefaultFieldWithSetter import org.utbot.examples.assemble.defaults.DefaultPackagePrivateField import org.utbot.examples.assemble.statics.StaticField -import org.utbot.framework.SootUtils import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.MethodId @@ -56,6 +55,7 @@ import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test +import org.utbot.framework.util.SootUtils /** * Test classes must be located in the same folder as [AssembleTestUtils] class. diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/framework/modificators/UtBotFieldModificatorsTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/framework/modificators/UtBotFieldModificatorsTest.kt index 38b2b768b5..9962d76c05 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/framework/modificators/UtBotFieldModificatorsTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/framework/modificators/UtBotFieldModificatorsTest.kt @@ -12,7 +12,6 @@ import org.utbot.examples.modificators.StronglyConnectedComponents import org.utbot.examples.modificators.coupling.ClassA import org.utbot.examples.modificators.coupling.ClassB import org.utbot.examples.modificators.hierarchy.InheritedModifications -import org.utbot.framework.SootUtils import org.utbot.framework.modifications.AnalysisMode import org.utbot.framework.modifications.AnalysisMode.AllModificators import org.utbot.framework.modifications.AnalysisMode.SettersAndDirectAccessors @@ -25,6 +24,7 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.utbot.framework.util.SootUtils internal class UtBotFieldModificatorsTest { private lateinit var fieldsModificatorsSearcher: UtBotFieldsModificatorsSearcher diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtLinkedList.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtLinkedList.java index 55a577cb52..e4183cf698 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtLinkedList.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtLinkedList.java @@ -72,7 +72,7 @@ public UtLinkedList(Collection c) { *
  • elementData is marked as parameter
  • *
  • elementData.storage and it's elements are marked as parameters
  • */ - private void preconditionCheck() { + protected void preconditionCheck() { if (alreadyVisited(this)) { return; } @@ -88,13 +88,13 @@ private void preconditionCheck() { visit(this); } - private void rangeCheck(int index) { + protected void rangeCheck(int index) { if (index < 0 || index >= elementData.end - elementData.begin) { throw new IndexOutOfBoundsException(); } } - private void rangeCheckForAdd(int index) { + protected void rangeCheckForAdd(int index) { if (index < 0 || index > elementData.end - elementData.begin) { throw new IndexOutOfBoundsException(); } diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtLinkedListWithNullableCheck.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtLinkedListWithNullableCheck.java new file mode 100644 index 0000000000..4def825276 --- /dev/null +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtLinkedListWithNullableCheck.java @@ -0,0 +1,122 @@ +package org.utbot.engine.overrides.collections; + +import java.util.Collection; + +/** + * This list forbids inserting null elements to support some implementations of {@link java.util.Deque} like + * {@link java.util.ArrayDeque}. + * + * TODO: Support super calls in inherited wrappers + * + * @see UtLinkedList + * @param + */ +public class UtLinkedListWithNullableCheck extends UtLinkedList { + @SuppressWarnings("unused") + UtLinkedListWithNullableCheck(RangeModifiableUnlimitedArray elementData, int fromIndex, int toIndex) { + super(elementData, fromIndex, toIndex); + for (int i = elementData.begin; i < elementData.end; i++) { + if (elementData.get(i) == null) { + throw new NullPointerException(); + } + } + } + + @SuppressWarnings("unused") + public UtLinkedListWithNullableCheck() { + super(); + } + + @SuppressWarnings("unused") + public UtLinkedListWithNullableCheck(Collection c) { + super(c); + } + + @Override + public E set(int index, E element) { + if (element == null) { + throw new NullPointerException(); + } + preconditionCheck(); + rangeCheck(index); + E oldElement = elementData.get(index + elementData.begin); + elementData.set(index + elementData.begin, element); + return oldElement; + } + + @Override + public void addFirst(E e) { + if (e == null) { + throw new NullPointerException(); + } + preconditionCheck(); + elementData.set(--elementData.begin, e); + } + + @Override + public void addLast(E e) { + if (e == null) { + throw new NullPointerException(); + } + preconditionCheck(); + elementData.set(elementData.end++, e); + } + + @Override + public boolean offerFirst(E e) { + if (e == null) { + throw new NullPointerException(); + } + preconditionCheck(); + elementData.set(--elementData.begin, e); + return true; + } + + @Override + public boolean offerLast(E e) { + if (e == null) { + throw new NullPointerException(); + } + preconditionCheck(); + elementData.set(elementData.end++, e); + return true; + } + + @Override + public void add(int index, E element) { + if (element == null) { + throw new NullPointerException(); + } + preconditionCheck(); + rangeCheckForAdd(index); + elementData.end++; + elementData.insert(index + elementData.begin, element); + } + + @Override + public boolean addAll(Collection c) { + for (Object elem : c.toArray()) { + if (elem == null) { + throw new NullPointerException(); + } + } + preconditionCheck(); + elementData.setRange(elementData.end, c.toArray(), 0, c.size()); + elementData.end += c.size(); + return true; + } + + @Override + public boolean addAll(int index, Collection c) { + for (Object elem : c.toArray()) { + if (elem == null) { + throw new NullPointerException(); + } + } + preconditionCheck(); + rangeCheckForAdd(index); + elementData.insertRange(index + elementData.begin, c.toArray(), 0, c.size()); + elementData.end += c.size(); + return true; + } +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/CollectionWrappers.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/CollectionWrappers.kt index 2e2039dddc..fa0b5b89e8 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/CollectionWrappers.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/CollectionWrappers.kt @@ -8,6 +8,7 @@ import org.utbot.engine.overrides.collections.UtGenericStorage import org.utbot.engine.overrides.collections.UtHashMap import org.utbot.engine.overrides.collections.UtHashSet import org.utbot.engine.overrides.collections.UtLinkedList +import org.utbot.engine.overrides.collections.UtLinkedListWithNullableCheck import org.utbot.engine.pc.UtAddrExpression import org.utbot.engine.pc.UtExpression import org.utbot.engine.pc.select @@ -195,24 +196,29 @@ abstract class BaseGenericStorageBasedContainerWrapper(containerClassName: Strin */ enum class UtListClass { UT_ARRAY_LIST, - UT_LINKED_LIST; + UT_LINKED_LIST, + UT_LINKED_LIST_WITH_NULLABLE_CHECK; val className: String get() = when (this) { UT_ARRAY_LIST -> UtArrayList::class.java.canonicalName UT_LINKED_LIST -> UtLinkedList::class.java.canonicalName + UT_LINKED_LIST_WITH_NULLABLE_CHECK -> UtLinkedListWithNullableCheck::class.java.canonicalName } } /** - * BaseCollectionWrapper that uses implementation of [UtArrayList] or [UtLinkedList] + * BaseCollectionWrapper that uses implementation of [UtArrayList], [UtLinkedList], or [UtLinkedListWithNullableCheck] * depending on specified [utListClass] parameter. * + * This class is also used for wrapping [java.util.Queue], because [UtLinkedList] and [UtLinkedListWithNullableCheck] + * both implement [java.util.Queue]. + * * At resolving stage ListWrapper is resolved to [UtAssembleModel]. - * This model is instantiated by [java.util.ArrayList] constructor if utListClass is [UtListClass.UT_ARRAY_LIST] - * or by [java.util.LinkedList] constructor, if utListClass is [UtListClass.UT_LINKED_LIST] + * This model is instantiated by constructor which is taken from the type from the passed [ReferenceValue] in [value] + * function. * - * Modification chain consists of consequent [java.util.List.add] methods + * Modification chain consists of consequent [java.util.Collection.add] methods * that are arranged to iterating order of list. */ class ListWrapper(private val utListClass: UtListClass) : BaseGenericStorageBasedContainerWrapper(utListClass.className) { @@ -227,7 +233,7 @@ class ListWrapper(private val utListClass: UtListClass) : BaseGenericStorageBase } override val modificationMethodId: MethodId = methodId( - classId = java.util.List::class.id, + classId = java.util.Collection::class.id, name = "add", returnType = booleanClassId, arguments = arrayOf(objectClassId), diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Extensions.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Extensions.kt index edaec9a10b..2c7641b684 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Extensions.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Extensions.kt @@ -74,6 +74,13 @@ import soot.jimple.internal.JVirtualInvokeExpr import soot.jimple.internal.JimpleLocal import soot.tagkit.ArtificialEntityTag import java.lang.reflect.ParameterizedType +import java.util.LinkedList +import java.util.ArrayDeque +import java.util.Deque +import java.util.Queue +import kotlin.collections.ArrayList +import kotlin.collections.HashMap +import kotlin.collections.HashSet val JIdentityStmt.lines: String get() = tags.joinToString { "$it" } @@ -256,7 +263,9 @@ val libraryTargets: Map> = mapOf( Collection::class.java.name to listOf(ArrayList::class.java.name, HashSet::class.java.name), List::class.java.name to listOf(ArrayList::class.java.name), Set::class.java.name to listOf(HashSet::class.java.name), - Map::class.java.name to listOf(HashMap::class.java.name) + Map::class.java.name to listOf(HashMap::class.java.name), + Queue::class.java.name to listOf(LinkedList::class.java.name, ArrayDeque::class.java.name), + Deque::class.java.name to listOf(LinkedList::class.java.name, ArrayDeque::class.java.name) ) fun Collection<*>.prettify() = joinToString("\n", "\n", "\n") diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/ObjectWrappers.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/ObjectWrappers.kt index 7225af9c77..3fdeb2bfb9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/ObjectWrappers.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/ObjectWrappers.kt @@ -4,6 +4,7 @@ import org.utbot.common.WorkaroundReason.MAKE_SYMBOLIC import org.utbot.common.workaround import org.utbot.engine.UtListClass.UT_ARRAY_LIST import org.utbot.engine.UtListClass.UT_LINKED_LIST +import org.utbot.engine.UtListClass.UT_LINKED_LIST_WITH_NULLABLE_CHECK import org.utbot.engine.UtOptionalClass.UT_OPTIONAL import org.utbot.engine.UtOptionalClass.UT_OPTIONAL_DOUBLE import org.utbot.engine.UtOptionalClass.UT_OPTIONAL_INT @@ -71,8 +72,12 @@ val classToWrapper: MutableMap = putSootClass(CopyOnWriteArrayList::class, UT_ARRAY_LIST.className) putSootClass(java.util.LinkedList::class, UT_LINKED_LIST.className) putSootClass(java.util.AbstractSequentialList::class, UT_LINKED_LIST.className) - // TODO mistake JIRA:1495 - putSootClass(java.util.ArrayDeque::class, UT_LINKED_LIST.className) + + putSootClass(java.util.ArrayDeque::class, UT_LINKED_LIST_WITH_NULLABLE_CHECK.className) + putSootClass(java.util.concurrent.ConcurrentLinkedDeque::class, UT_LINKED_LIST_WITH_NULLABLE_CHECK.className) + putSootClass(java.util.concurrent.ConcurrentLinkedQueue::class, UT_LINKED_LIST_WITH_NULLABLE_CHECK.className) + putSootClass(java.util.concurrent.LinkedBlockingDeque::class, UT_LINKED_LIST_WITH_NULLABLE_CHECK.className) + putSootClass(java.util.concurrent.LinkedBlockingQueue::class, UT_LINKED_LIST_WITH_NULLABLE_CHECK.className) putSootClass(java.util.Set::class, UtHashSet::class) putSootClass(java.util.AbstractSet::class, UtHashSet::class) @@ -83,6 +88,7 @@ val classToWrapper: MutableMap = putSootClass(java.util.AbstractMap::class, UtHashMap::class) putSootClass(java.util.LinkedHashMap::class, UtHashMap::class) putSootClass(java.util.HashMap::class, UtHashMap::class) + putSootClass(java.util.concurrent.ConcurrentHashMap::class, UtHashMap::class) putSootClass(java.util.stream.BaseStream::class, UT_STREAM.className) putSootClass(java.util.stream.Stream::class, UT_STREAM.className) @@ -143,48 +149,47 @@ private val wrappers = mapOf( wrap(AssociativeArray::class) { type, addr -> objectValue(type, addr, AssociativeArrayWrapper()) }, + // list wrappers - wrap(java.util.List::class) { _, addr -> - objectValue(ARRAY_LIST_TYPE, addr, ListWrapper(UT_ARRAY_LIST)) - }, - wrap(java.util.AbstractList::class) { _, addr -> - objectValue(ARRAY_LIST_TYPE, addr, ListWrapper(UT_ARRAY_LIST)) - }, - wrap(java.util.ArrayList::class) { _, addr -> - objectValue(ARRAY_LIST_TYPE, addr, ListWrapper(UT_ARRAY_LIST)) - }, - wrap(CopyOnWriteArrayList::class) { _, addr -> - objectValue(ARRAY_LIST_TYPE, addr, ListWrapper(UT_ARRAY_LIST)) - }, + wrap(java.util.List::class) { _, addr -> objectValue(ARRAY_LIST_TYPE, addr, ListWrapper(UT_ARRAY_LIST)) }, + wrap(java.util.AbstractList::class) { _, addr -> objectValue(ARRAY_LIST_TYPE, addr, ListWrapper(UT_ARRAY_LIST)) }, + wrap(java.util.ArrayList::class) { _, addr -> objectValue(ARRAY_LIST_TYPE, addr, ListWrapper(UT_ARRAY_LIST)) }, - wrap(java.util.LinkedList::class) { _, addr -> - objectValue(LINKED_LIST_TYPE, addr, ListWrapper(UT_LINKED_LIST)) - }, - wrap(java.util.AbstractSequentialList::class) { _, addr -> - objectValue(LINKED_LIST_TYPE, addr, ListWrapper(UT_LINKED_LIST)) - }, - // TODO mistake JIRA:1495 - wrap(java.util.ArrayDeque::class) { _, addr -> - objectValue(LINKED_LIST_TYPE, addr, ListWrapper(UT_LINKED_LIST)) + + wrap(CopyOnWriteArrayList::class) { type, addr -> objectValue(type, addr, ListWrapper(UT_ARRAY_LIST)) }, + + wrap(java.util.LinkedList::class) { _, addr -> objectValue(LINKED_LIST_TYPE, addr, ListWrapper(UT_LINKED_LIST)) }, + wrap(java.util.AbstractSequentialList::class) { _, addr -> objectValue(LINKED_LIST_TYPE, addr, ListWrapper(UT_LINKED_LIST)) }, + + // queue, deque wrappers + wrap(java.util.ArrayDeque::class) { type, addr -> + objectValue(type, addr, ListWrapper(UT_LINKED_LIST_WITH_NULLABLE_CHECK)) }, - // set wrappers - wrap(java.util.Set::class) { _, addr -> - objectValue(LINKED_HASH_SET_TYPE, addr, SetWrapper()) + wrap(java.util.concurrent.ConcurrentLinkedDeque::class) { type, addr -> + objectValue(type, addr, ListWrapper(UT_LINKED_LIST_WITH_NULLABLE_CHECK)) }, - wrap(java.util.AbstractSet::class) { _, addr -> - objectValue(LINKED_HASH_SET_TYPE, addr, SetWrapper()) + wrap(java.util.concurrent.ConcurrentLinkedQueue::class) { type, addr -> + objectValue(type, addr, ListWrapper(UT_LINKED_LIST_WITH_NULLABLE_CHECK)) }, - wrap(java.util.HashSet::class) { _, addr -> - objectValue(HASH_SET_TYPE, addr, SetWrapper()) + wrap(java.util.concurrent.LinkedBlockingDeque::class) { type, addr -> + objectValue(type, addr, ListWrapper(UT_LINKED_LIST_WITH_NULLABLE_CHECK)) }, - wrap(java.util.LinkedHashSet::class) { _, addr -> - objectValue(LINKED_HASH_SET_TYPE, addr, SetWrapper()) + wrap(java.util.concurrent.LinkedBlockingQueue::class) { type, addr -> + objectValue(type, addr, ListWrapper(UT_LINKED_LIST_WITH_NULLABLE_CHECK)) }, + + // set wrappers + wrap(java.util.Set::class) { _, addr -> objectValue(LINKED_HASH_SET_TYPE, addr, SetWrapper()) }, + wrap(java.util.AbstractSet::class) { _, addr -> objectValue(LINKED_HASH_SET_TYPE, addr, SetWrapper()) }, + wrap(java.util.HashSet::class) { _, addr -> objectValue(HASH_SET_TYPE, addr, SetWrapper()) }, + wrap(java.util.LinkedHashSet::class) { _, addr -> objectValue(LINKED_HASH_SET_TYPE, addr, SetWrapper()) }, + // map wrappers wrap(java.util.Map::class) { _, addr -> objectValue(LINKED_HASH_MAP_TYPE, addr, MapWrapper()) }, wrap(java.util.AbstractMap::class) { _, addr -> objectValue(LINKED_HASH_MAP_TYPE, addr, MapWrapper()) }, wrap(java.util.LinkedHashMap::class) { _, addr -> objectValue(LINKED_HASH_MAP_TYPE, addr, MapWrapper()) }, wrap(java.util.HashMap::class) { _, addr -> objectValue(HASH_MAP_TYPE, addr, MapWrapper()) }, + wrap(java.util.concurrent.ConcurrentHashMap::class) { _, addr -> objectValue(HASH_MAP_TYPE, addr, MapWrapper()) }, // stream wrappers wrap(java.util.stream.BaseStream::class) { _, addr -> objectValue(STREAM_TYPE, addr, CommonStreamWrapper()) }, diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt index f4bdeb29b7..3ce10a2527 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -2437,6 +2437,9 @@ class Traverser( val method = invokeExpr.retrieveMethod() val parameters = resolveParameters(invokeExpr.args, method.parameterTypes) val invocation = Invocation(instance, method, parameters, InvocationTarget(instance, method)) + + // Calls with super syntax are represented by invokeSpecial instruction, but we don't support them in wrappers + // TODO: https://github.com/UnitTestBot/UTBotJava/issues/819 -- Support super calls in inherited wrappers return commonInvokePart(invocation) } @@ -2455,7 +2458,23 @@ class Traverser( * Returns results of native calls cause other calls push changes directly to path selector. */ private fun TraversalContext.commonInvokePart(invocation: Invocation): List { - // First, check if there is override for the invocation itself, not for the targets + /** + * First, check if there is override for the invocation itself, not for the targets. + * + * Note that calls with super syntax are represented by invokeSpecial instruction, but we don't support them in wrappers, + * so here we resolve [invocation] to the inherited method invocation if it's something like: + * + * ```java + * public class InheritedWrapper extends BaseWrapper { + * public void add(Object o) { + * // some stuff + * super.add(o); // this resolves to InheritedWrapper::add instead of BaseWrapper::add + * } + * } + * ``` + * + * TODO: https://github.com/UnitTestBot/UTBotJava/issues/819 -- Support super calls in inherited wrappers + */ val artificialMethodOverride = overrideInvocation(invocation, target = null) // If so, return the result of the override diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt index 8a0456f3e3..db372f0da7 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt @@ -357,7 +357,11 @@ class AssembleModelGenerator(private val methodUnderTest: UtMethod<*>) { /** * Finds most appropriate constructor in class. * - * We prefer constructor that allows to set more fields than others + * If the [compositeModel].fields is empty, we don't care about affected fields, we would like to take an empty + * constructor if the declaring class is from [java.util] package or an appropriate constructor with the least + * number of arguments. + * + * Otherwise, we prefer constructor that allows to set more fields than others * and use only simple assignments like "this.a = a". * * Returns null if no one appropriate constructor is found. @@ -366,11 +370,20 @@ class AssembleModelGenerator(private val methodUnderTest: UtMethod<*>) { val classId = compositeModel.classId if (!classId.isVisible || classId.isInner) return null - return classId.jClass.declaredConstructors + val constructorIds = classId.jClass.declaredConstructors .filter { it.isVisible } - .sortedByDescending { it.parameterCount } .map { it.executableId } - .firstOrNull { constructorAnalyzer.isAppropriate(it) } + + return if (compositeModel.fields.isEmpty()) { + val fromUtilPackage = classId.packageName.startsWith("java.util") + constructorIds + .sortedBy { it.parameters.size } + .firstOrNull { it.parameters.isEmpty() && fromUtilPackage || constructorAnalyzer.isAppropriate(it) } + } else { + constructorIds + .sortedByDescending { it.parameters.size } + .firstOrNull { constructorAnalyzer.isAppropriate(it) } + } } private val ClassId.isVisible : Boolean diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtAssembleModelConstructors.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtAssembleModelConstructors.kt index 0d22eb6268..9a335798a6 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtAssembleModelConstructors.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtAssembleModelConstructors.kt @@ -7,6 +7,7 @@ import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.primitiveWrappers import org.utbot.framework.plugin.api.util.voidWrapperClassId import org.utbot.framework.util.nextModelName +import java.util.concurrent.CopyOnWriteArrayList private val predefinedConstructors = mutableMapOf, () -> UtAssembleModelConstructorBase>( /** @@ -24,18 +25,33 @@ private val predefinedConstructors = mutableMapOf, () -> UtAssembleMode java.util.ArrayList::class.java to { CollectionConstructor() }, java.util.AbstractList::class.java to { CollectionConstructor() }, java.util.List::class.java to { CollectionConstructor() }, - java.util.Deque::class.java to { CollectionConstructor() }, + java.util.concurrent.CopyOnWriteArrayList::class.java to { CollectionConstructor() }, + + + /** + * Queues, deques + */ + java.util.PriorityQueue::class.java to { CollectionConstructor() }, java.util.ArrayDeque::class.java to { CollectionConstructor() }, + java.util.concurrent.LinkedBlockingQueue::class.java to { CollectionConstructor() }, java.util.concurrent.LinkedBlockingDeque::class.java to { CollectionConstructor() }, + java.util.concurrent.ConcurrentLinkedQueue::class.java to { CollectionConstructor() }, + java.util.concurrent.ConcurrentLinkedDeque::class.java to { CollectionConstructor() }, + java.util.Queue::class.java to { CollectionConstructor() }, + java.util.Deque::class.java to { CollectionConstructor() }, + /** * Sets */ java.util.HashSet::class.java to { CollectionConstructor() }, + java.util.TreeSet::class.java to { CollectionConstructor() }, java.util.LinkedHashSet::class.java to { CollectionConstructor() }, java.util.AbstractSet::class.java to { CollectionConstructor() }, java.util.Set::class.java to { CollectionConstructor() }, + + /** * Maps */ diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/minimization/Minimization.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/minimization/Minimization.kt index 78850acc31..380ab74d2c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/minimization/Minimization.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/minimization/Minimization.kt @@ -109,6 +109,7 @@ private fun groupByBranchInstructions( * 2. {2, 3, 2, 6} * 3. {2, 3, 4, 3} * branch instructions are {2 -> (3, 4, 5, 6), 3 -> (2, 4), 4 -> (2, 3)} + * * we will build these lists representing their behaviour: * 1. {2 -> 3, 3 -> 2} (because of {__2__, __3__, 2, 4, 2, 5}) * 2. {2 -> 3, 3 -> 2} (because of {__2__, __3__, 2, 6}) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/ConstructorAnalyzer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/ConstructorAnalyzer.kt index bfc5aa9b66..2960364095 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/ConstructorAnalyzer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/ConstructorAnalyzer.kt @@ -172,7 +172,7 @@ class ConstructorAnalyzer { for (assn in assignments) { val jimpleLocal = assn.rightOp as? JimpleLocal ?: continue - val field = (assn.leftOp as JInstanceFieldRef).field + val field = (assn.leftOp as? JInstanceFieldRef)?.field ?: continue val parameterIndex = jimpleBody.locals.indexOfFirst { it.name == jimpleLocal.name } indexedFields[parameterIndex - 1] = FieldId(field.declaringClass.id, field.name) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt index ab7eb7e770..5aff213ab4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt @@ -34,8 +34,8 @@ import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.intArrayClassId import org.utbot.framework.plugin.api.util.utContext import org.utbot.framework.plugin.api.util.withUtContext +import org.utbot.framework.util.SootUtils import org.utbot.framework.util.jimpleBody -import org.utbot.framework.util.runSoot import org.utbot.framework.util.toModel import org.utbot.instrumentation.ConcreteExecutor import org.utbot.instrumentation.warmup @@ -78,7 +78,7 @@ open class TestCaseGenerator( } timeoutLogger.trace().bracket("Soot initialization") { - runSoot(buildDir, classpath) + SootUtils.runSoot(buildDir, classpath) } //warmup diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/util/SootUtils.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/util/SootUtils.kt index 65953d900d..ed389b7ed4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/util/SootUtils.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/util/SootUtils.kt @@ -34,6 +34,7 @@ import org.utbot.engine.overrides.collections.UtOptionalDouble import org.utbot.engine.overrides.collections.UtOptionalInt import org.utbot.engine.overrides.collections.UtOptionalLong import org.utbot.engine.overrides.collections.AbstractCollection +import org.utbot.engine.overrides.collections.UtLinkedListWithNullableCheck import org.utbot.engine.overrides.stream.Arrays import org.utbot.engine.overrides.stream.Stream import org.utbot.engine.overrides.stream.UtStream @@ -51,10 +52,35 @@ import soot.jimple.JimpleBody import soot.options.Options import soot.toolkits.graph.ExceptionalUnitGraph +object SootUtils { + /** + * Runs Soot in tests if it hasn't already been done. + */ + fun runSoot(clazz: KClass<*>) { + val buildDir = FileUtil.locateClassPath(clazz) ?: FileUtil.isolateClassFiles(clazz) + val buildDirPath = buildDir.toPath() + + runSoot(buildDirPath, null) + } + + fun runSoot(buildDirPath: Path, classPath: String?) { + synchronized(this) { + if (buildDirPath != previousBuildDir || classPath != previousClassPath) { + org.utbot.framework.util.runSoot(buildDirPath, classPath) + previousBuildDir = buildDirPath + previousClassPath = classPath + } + } + } + + private var previousBuildDir: Path? = null + private var previousClassPath: String? = null +} + /** Convert code to Jimple */ -fun runSoot(buildDir: Path, classpath: String?) { +private fun runSoot(buildDir: Path, classpath: String?) { G.reset() val options = Options.v() @@ -129,6 +155,7 @@ private val classesToLoad = arrayOf( UtArrayList::class, UtArrayList.UtArrayListIterator::class, UtLinkedList::class, + UtLinkedListWithNullableCheck::class, UtLinkedList.UtLinkedListIterator::class, UtLinkedList.ReverseIteratorWrapper::class, UtHashSet::class, diff --git a/utbot-sample/src/main/java/org/utbot/examples/collections/QueueUsages.java b/utbot-sample/src/main/java/org/utbot/examples/collections/QueueUsages.java new file mode 100644 index 0000000000..5a2586e844 --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/collections/QueueUsages.java @@ -0,0 +1,92 @@ +package org.utbot.examples.collections; + +import org.utbot.examples.objects.WrappedInt; + +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.LinkedList; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.LinkedBlockingDeque; + +public class QueueUsages { + public int createArrayDeque(WrappedInt init, WrappedInt next) { + Queue q = new ArrayDeque<>(Collections.singletonList(init)); + q.add(next); + return q.size(); + } + + public int createLinkedList(WrappedInt init, WrappedInt next) { + Queue q = new LinkedList<>(Collections.singletonList(init)); + q.add(next); + return q.size(); + } + + public int createLinkedBlockingDeque(WrappedInt init, WrappedInt next) { + Queue q = new LinkedBlockingDeque<>(Collections.singletonList(init)); + q.add(next); + return q.size(); + } + + public int containsQueue(Queue q, int x) { + if (q.contains(x)) { + return 1; + } else { + return 0; + } + } + + public Queue addQueue(Queue q, WrappedInt x) { + q.add(x); + return q; + } + + public Queue addAllQueue(Queue q, WrappedInt x) { + Collection lst = Arrays.asList(new WrappedInt(1), x); + q.addAll(lst); + return q; + } + + public Deque castQueueToDeque(Queue q) { + if (q instanceof Deque) { + return (Deque)q; + } else { + return null; + } + } + + public int checkSubtypesOfQueue(Queue q) { + if (q == null) { + return 0; + } + if (q instanceof LinkedList) { + return 1; + } else if (q instanceof ArrayDeque) { + return 2; + } else { + return 3; + } + } + + public int checkSubtypesOfQueueWithUsage(Queue q) { + if (q == null) { + return 0; + } + q.add(1); + if (q instanceof LinkedList) { + return 1; + } else if (q instanceof ArrayDeque) { + return 2; + } else { + return 3; + } + } + + public ConcurrentLinkedQueue addConcurrentLinkedQueue(ConcurrentLinkedQueue q, WrappedInt o) { + q.add(o); + return q; + } +}