From e328d255b3ff9bcb5d64c82fa7b32b36b8a6e266 Mon Sep 17 00:00:00 2001 From: Yury Kamenev Date: Mon, 24 Oct 2022 16:33:07 +0800 Subject: [PATCH 1/7] Supported assemble models for streams in concrete execution --- .../kotlin/org/utbot/common/KClassUtil.kt | 41 ++++++- .../framework/plugin/api/UtExecutionResult.kt | 11 ++ .../utbot/framework/plugin/api/util/IdUtil.kt | 29 ++++- .../examples/stream/BaseStreamExampleTest.kt | 14 --- .../stream/DoubleStreamExampleTest.kt | 12 -- .../examples/stream/IntStreamExampleTest.kt | 12 -- .../examples/stream/LongStreamExampleTest.kt | 12 -- .../StreamsAsMethodResultExampleTest.kt | 83 ++++++++++++++ .../kotlin/org/utbot/engine/StreamWrappers.kt | 12 +- .../constructor/builtin/UtilMethodBuiltins.kt | 9 ++ .../model/constructor/context/CgContext.kt | 3 + .../tree/CgCallableAccessManager.kt | 43 +++++++- .../constructor/tree/CgMethodConstructor.kt | 104 +++++++++++++++--- .../codegen/model/visitor/UtilMethods.kt | 19 ++++ .../framework/concrete/StreamConstructors.kt | 79 +++++++++++++ .../concrete/UtAssembleModelConstructors.kt | 18 ++- .../concrete/UtExecutionInstrumentation.kt | 15 ++- .../framework/concrete/UtModelConstructor.kt | 19 ++++ .../CodeGenerationIntegrationTest.kt | 7 +- .../infrastructure/UtValueTestCaseChecker.kt | 6 +- .../examples/stream/BaseStreamExample.java | 10 -- .../examples/stream/DoubleStreamExample.java | 13 --- .../examples/stream/IntStreamExample.java | 13 --- .../examples/stream/LongStreamExample.java | 13 --- .../stream/StreamsAsMethodResultExample.java | 83 ++++++++++++++ .../kotlin/org/utbot/summary/Summarization.kt | 14 ++- .../kotlin/org/utbot/summary/TagGenerator.kt | 2 + 27 files changed, 552 insertions(+), 144 deletions(-) create mode 100644 utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/concrete/StreamConstructors.kt create mode 100644 utbot-sample/src/main/java/org/utbot/examples/stream/StreamsAsMethodResultExample.java diff --git a/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt index 1c0a88d02a..63be7d20ea 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt @@ -1,5 +1,6 @@ package org.utbot.common +import java.lang.reflect.Array import java.lang.reflect.InvocationTargetException import java.lang.reflect.Method import kotlin.reflect.KClass @@ -7,10 +8,48 @@ import kotlin.reflect.KClass val Class<*>.nameOfPackage: String get() = `package`?.name?:"" fun Method.invokeCatching(obj: Any?, args: List) = try { - Result.success(invoke(obj, *args.toTypedArray())) + val invocation = if (isVarArgs) { + // In java only last parameter could be vararg + val firstNonVarargParametersCount = parameterCount - 1 + + val varargArray = constructVarargParameterArray(firstNonVarargParametersCount, args) + + if (firstNonVarargParametersCount == 0) { + // Only vararg parameter, just pass only it as an array + invoke(obj, varargArray) + } else { + // Pass first non vararg parameters as vararg, and the vararg parameter as an array + val firstNonVarargParameters = args.take(firstNonVarargParametersCount) + + invoke(obj, *firstNonVarargParameters.toTypedArray(), varargArray) + } + } else { + invoke(obj, *args.toTypedArray()) + } + + Result.success(invocation) } catch (e: InvocationTargetException) { Result.failure(e.targetException) } +// https://stackoverflow.com/a/59857242 +private fun Method.constructVarargParameterArray(firstNonVarargParametersCount: Int, args: List): Any { + val varargCount = args.size - firstNonVarargParametersCount + val varargElements = args.drop(firstNonVarargParametersCount) + + val varargElementType = parameterTypes.last().componentType + requireNotNull(varargElementType) { + "Vararg parameter of method $this was expected to be array but ${parameterTypes.last()} found" + } + + val varargArray = Array.newInstance(parameterTypes.last().componentType, varargCount) + + varargElements.forEachIndexed { index, value -> + Array.set(varargArray, index, value) + } + + return varargArray +} + val KClass<*>.allNestedClasses: List> get() = listOf(this) + nestedClasses.flatMap { it.allNestedClasses } diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt index b242ab39ec..19bf988a0e 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt @@ -21,6 +21,10 @@ data class UtSandboxFailure( override val exception: Throwable ) : UtExecutionFailure() +data class UtStreamConsumingFailure( + override val exception: Throwable +) : UtExecutionFailure() + /** * unexpectedFail (when exceptions such as NPE, IOBE, etc. appear, but not thrown by a user, applies both for function under test and nested calls ) * expectedCheckedThrow (when function under test or nested call explicitly says that checked exception could be thrown and throws it) @@ -41,6 +45,13 @@ class TimeoutException(s: String) : Exception(s) data class UtTimeoutException(override val exception: TimeoutException) : UtExecutionFailure() +/** + * Represents an exception that occurs during consuming a stream. Stores it in [innerException]. + */ +data class UtStreamConsumingException(val innerException: Exception) : RuntimeException() { + override fun toString(): String = innerException.toString() +} + /** * Indicates failure in concrete execution. * For now it is explicitly throwing by ConcreteExecutor in case child process death. diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt index 57dbb004c0..4b782b9b10 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt @@ -1,5 +1,6 @@ package org.utbot.framework.plugin.api.util +import org.utbot.common.withAccessibility import org.utbot.framework.plugin.api.BuiltinClassId import org.utbot.framework.plugin.api.BuiltinConstructorId import org.utbot.framework.plugin.api.BuiltinMethodId @@ -114,12 +115,20 @@ infix fun ClassId.isSubtypeOf(type: ClassId): Boolean { // unwrap primitive wrappers val left = primitiveByWrapper[this] ?: this val right = primitiveByWrapper[type] ?: type + if (left == right) { return true } + val leftClass = this + val superTypes = leftClass.allSuperTypes() + + return right in superTypes +} + +fun ClassId.allSuperTypes(): Sequence { val interfaces = sequence { - var types = listOf(leftClass) + var types = listOf(this@allSuperTypes) while (types.isNotEmpty()) { yieldAll(types) types = types @@ -127,9 +136,10 @@ infix fun ClassId.isSubtypeOf(type: ClassId): Boolean { .map { it.id } } } - val superclasses = generateSequence(leftClass) { it.superclass?.id } - val superTypes = interfaces + superclasses - return right in superTypes + + val superclasses = generateSequence(this) { it.superclass?.id } + + return interfaces + superclasses } infix fun ClassId.isNotSubtypeOf(type: ClassId): Boolean = !(this isSubtypeOf type) @@ -267,6 +277,17 @@ val atomicIntegerGetAndIncrement = MethodId(atomicIntegerClassId, "getAndIncreme val iterableClassId = java.lang.Iterable::class.id val mapClassId = java.util.Map::class.id +val baseStreamClassId = java.util.stream.BaseStream::class.id +val streamClassId = java.util.stream.Stream::class.id +val intStreamClassId = java.util.stream.IntStream::class.id +val longStreamClassId = java.util.stream.LongStream::class.id +val doubleStreamClassId = java.util.stream.DoubleStream::class.id + +val intStreamToArrayMethodId = methodId(intStreamClassId, "toArray", intArrayClassId) +val longStreamToArrayMethodId = methodId(longStreamClassId, "toArray", longArrayClassId) +val doubleStreamToArrayMethodId = methodId(doubleStreamClassId, "toArray", doubleArrayClassId) +val streamToArrayMethodId = methodId(streamClassId, "toArray", objectArrayClassId) + val dateClassId = java.util.Date::class.id @Suppress("RemoveRedundantQualifierName") diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/BaseStreamExampleTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/BaseStreamExampleTest.kt index b1dbb5a64d..ff0407947e 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/BaseStreamExampleTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/BaseStreamExampleTest.kt @@ -29,20 +29,6 @@ class BaseStreamExampleTest : UtValueTestCaseChecker( TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration) ) ) { - @Test - fun testReturningStreamExample() { - withoutConcrete { - check( - BaseStreamExample::returningStreamExample, - eq(2), - // NOTE: the order of the matchers is important because Stream could be used only once - { c, r -> c.isNotEmpty() && c == r!!.toList() }, - { c, r -> c.isEmpty() && c == r!!.toList() }, - coverage = FullWithAssumptions(assumeCallsNumber = 1) - ) - } - } - @Test fun testReturningStreamAsParameterExample() { withoutConcrete { diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/DoubleStreamExampleTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/DoubleStreamExampleTest.kt index b480daa90c..53216b306f 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/DoubleStreamExampleTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/DoubleStreamExampleTest.kt @@ -21,18 +21,6 @@ class DoubleStreamExampleTest : UtValueTestCaseChecker( TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration) ) ) { - @Test - fun testReturningStreamExample() { - check( - DoubleStreamExample::returningStreamExample, - ignoreExecutionsNumber, - // NOTE: the order of the matchers is important because Stream could be used only once - { c, r -> c.isNotEmpty() && c.doubles().contentEquals(r!!.toArray()) }, - { c, r -> c.isEmpty() && r!!.count() == 0L }, - coverage = FullWithAssumptions(assumeCallsNumber = 1) - ) - } - @Test fun testReturningStreamAsParameterExample() { withoutConcrete { diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/IntStreamExampleTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/IntStreamExampleTest.kt index 62a1ecbca0..5aef3eb609 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/IntStreamExampleTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/IntStreamExampleTest.kt @@ -22,18 +22,6 @@ class IntStreamExampleTest : UtValueTestCaseChecker( TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration) ) ) { - @Test - fun testReturningStreamExample() { - check( - IntStreamExample::returningStreamExample, - ignoreExecutionsNumber, - // NOTE: the order of the matchers is important because Stream could be used only once - { c, r -> c.isNotEmpty() && c.ints().contentEquals(r!!.toArray()) }, - { c, r -> c.isEmpty() && r!!.count() == 0L }, - coverage = FullWithAssumptions(assumeCallsNumber = 1) - ) - } - @Test fun testReturningStreamAsParameterExample() { withoutConcrete { diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/LongStreamExampleTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/LongStreamExampleTest.kt index 8864db2a3a..0ac7785d6c 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/LongStreamExampleTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/LongStreamExampleTest.kt @@ -22,18 +22,6 @@ class LongStreamExampleTest : UtValueTestCaseChecker( TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration) ) ) { - @Test - fun testReturningStreamExample() { - check( - LongStreamExample::returningStreamExample, - ignoreExecutionsNumber, - // NOTE: the order of the matchers is important because Stream could be used only once - { c, r -> c.isNotEmpty() && c.longs().contentEquals(r!!.toArray()) }, - { c, r -> c.isEmpty() && r!!.count() == 0L }, - coverage = FullWithAssumptions(assumeCallsNumber = 1) - ) - } - @Test fun testReturningStreamAsParameterExample() { withoutConcrete { diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt new file mode 100644 index 0000000000..caeb9b6e32 --- /dev/null +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt @@ -0,0 +1,83 @@ +package org.utbot.examples.stream + +import org.junit.jupiter.api.Test +import org.utbot.framework.codegen.ParametrizedTestSource +import org.utbot.framework.plugin.api.CodegenLanguage +import org.utbot.framework.plugin.api.UtStreamConsumingException +import org.utbot.testcheckers.eq +import org.utbot.tests.infrastructure.CodeGeneration +import org.utbot.tests.infrastructure.FullWithAssumptions +import org.utbot.tests.infrastructure.UtValueTestCaseChecker +import kotlin.streams.toList + +// TODO 1 instruction is always uncovered https://github.com/UnitTestBot/UTBotJava/issues/193 +// TODO failed Kotlin compilation (generics) JIRA:1332 +class StreamsAsMethodResultExampleTest : UtValueTestCaseChecker( + testClass = StreamsAsMethodResultExample::class, + testCodeGeneration = true, + pipelines = listOf( + TestLastStage(CodegenLanguage.JAVA), + TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration) + ), + codeGenerationModes = listOf(ParametrizedTestSource.DO_NOT_PARAMETRIZE) // TODO exception from concrete is passed to arguments list somehow +) { + @Test + fun testReturningStreamExample() { + check( + StreamsAsMethodResultExample::returningStreamExample, + eq(2), + { c, r -> c.isEmpty() && c == r!!.toList() }, + { c, r -> c.isNotEmpty() && c == r!!.toList() }, + coverage = FullWithAssumptions(assumeCallsNumber = 1) + ) + } + + @Test + fun testReturningIntStreamExample() { + checkWithException( + StreamsAsMethodResultExample::returningIntStreamExample, + eq(3), + { c, r -> c.isEmpty() && c == r.getOrThrow().toList() }, + { c, r -> c.isNotEmpty() && c.none { it == null } && c.toIntArray().contentEquals(r.getOrThrow().toArray()) }, + { c, r -> c.isNotEmpty() && c.any { it == null } && r.isNPE() }, + coverage = FullWithAssumptions(assumeCallsNumber = 2) + ) + } + + @Test + fun testReturningLongStreamExample() { + checkWithException( + StreamsAsMethodResultExample::returningLongStreamExample, + eq(3), + { c, r -> c.isEmpty() && c == r.getOrThrow().toList() }, + { c, r -> c.isNotEmpty() && c.none { it == null } && c.map { it.toLong() }.toLongArray().contentEquals(r.getOrThrow().toArray()) }, + { c, r -> c.isNotEmpty() && c.any { it == null } && r.isNPE() }, + coverage = FullWithAssumptions(assumeCallsNumber = 2) + ) + } + + @Test + fun testReturningDoubleStreamExample() { + checkWithException( + StreamsAsMethodResultExample::returningDoubleStreamExample, + eq(3), + { c, r -> c.isEmpty() && c == r.getOrThrow().toList() }, + { c, r -> c.isNotEmpty() && c.none { it == null } && c.map { it.toDouble() }.toDoubleArray().contentEquals(r.getOrThrow().toArray()) }, + { c, r -> c.isNotEmpty() && c.any { it == null } && r.isNPE() }, + coverage = FullWithAssumptions(assumeCallsNumber = 2) + ) + } + + /** + * Checks the result in [NullPointerException] from the engine or + * [UtStreamConsumingException] with [NullPointerException] from concrete execution. + */ + private fun Result<*>.isNPE(): Boolean = + exceptionOrNull()?.let { + if (it is UtStreamConsumingException) { + return@let it.innerException is NullPointerException + } + + return@let it is NullPointerException + } ?: false +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/StreamWrappers.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/StreamWrappers.kt index f28937fcd0..dda122b03b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/StreamWrappers.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/StreamWrappers.kt @@ -18,16 +18,20 @@ import org.utbot.framework.plugin.api.classId import org.utbot.framework.plugin.api.util.defaultValueModel import org.utbot.framework.plugin.api.util.doubleArrayClassId import org.utbot.framework.plugin.api.util.doubleClassId +import org.utbot.framework.plugin.api.util.doubleStreamClassId import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.intArrayClassId import org.utbot.framework.plugin.api.util.intClassId +import org.utbot.framework.plugin.api.util.intStreamClassId import org.utbot.framework.plugin.api.util.isArray import org.utbot.framework.plugin.api.util.isPrimitiveWrapper import org.utbot.framework.plugin.api.util.longArrayClassId import org.utbot.framework.plugin.api.util.longClassId +import org.utbot.framework.plugin.api.util.longStreamClassId import org.utbot.framework.plugin.api.util.methodId import org.utbot.framework.plugin.api.util.objectArrayClassId import org.utbot.framework.plugin.api.util.objectClassId +import org.utbot.framework.plugin.api.util.streamClassId import org.utbot.framework.util.nextModelName /** @@ -58,10 +62,10 @@ enum class UtStreamClass { val overriddenStreamClassId: ClassId get() = when (this) { - UT_STREAM -> java.util.stream.Stream::class.java.id - UT_INT_STREAM -> java.util.stream.IntStream::class.java.id - UT_LONG_STREAM -> java.util.stream.LongStream::class.java.id - UT_DOUBLE_STREAM -> java.util.stream.DoubleStream::class.java.id + UT_STREAM -> streamClassId + UT_INT_STREAM -> intStreamClassId + UT_LONG_STREAM -> longStreamClassId + UT_DOUBLE_STREAM -> doubleStreamClassId } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/builtin/UtilMethodBuiltins.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/builtin/UtilMethodBuiltins.kt index 19d1237d98..ab5d94172b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/builtin/UtilMethodBuiltins.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/builtin/UtilMethodBuiltins.kt @@ -9,6 +9,7 @@ import org.utbot.framework.plugin.api.BuiltinClassId import org.utbot.framework.plugin.api.BuiltinConstructorId import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.MethodId +import org.utbot.framework.plugin.api.util.baseStreamClassId import org.utbot.framework.plugin.api.util.booleanClassId import org.utbot.framework.plugin.api.util.builtinConstructorId import org.utbot.framework.plugin.api.util.classClassId @@ -48,6 +49,7 @@ internal abstract class UtilMethodProvider(val utilClassId: ClassId) { mapsDeepEqualsMethodId, hasCustomEqualsMethodId, getArrayLengthMethodId, + consumeBaseStreamMethodId, buildStaticLambdaMethodId, buildLambdaMethodId, getLookupInMethodId, @@ -165,6 +167,13 @@ internal abstract class UtilMethodProvider(val utilClassId: ClassId) { arguments = arrayOf(objectClassId) ) + val consumeBaseStreamMethodId: MethodId + get() = utilClassId.utilMethodId( + name = "consumeBaseStream", + returnType = voidClassId, + arguments = arrayOf(baseStreamClassId) + ) + val buildStaticLambdaMethodId: MethodId get() = utilClassId.utilMethodId( name = "buildStaticLambda", diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt index fb1d621e6a..4a2f433f36 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt @@ -396,6 +396,9 @@ internal interface CgContextOwner { val getArrayLength: MethodId get() = utilMethodProvider.getArrayLengthMethodId + val consumeBaseStream: MethodId + get() = utilMethodProvider.consumeBaseStreamMethodId + val buildStaticLambda: MethodId get() = utilMethodProvider.buildStaticLambdaMethodId diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt index 8a985e0c39..9574074b0f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt @@ -17,6 +17,7 @@ import org.utbot.framework.codegen.model.constructor.tree.CgCallableAccessManage import org.utbot.framework.codegen.model.constructor.tree.CgCallableAccessManagerImpl.FieldAccessorSuitability.Suitable import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getStatementConstructorBy import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getVariableConstructorBy +import org.utbot.framework.codegen.model.constructor.util.arrayInitializer import org.utbot.framework.codegen.model.constructor.util.getAmbiguousOverloadsOf import org.utbot.framework.codegen.model.constructor.util.importIfNeeded import org.utbot.framework.codegen.model.constructor.util.isUtil @@ -55,6 +56,7 @@ import org.utbot.framework.plugin.api.util.isAbstract import org.utbot.framework.plugin.api.util.isArray import org.utbot.framework.plugin.api.util.isStatic import org.utbot.framework.plugin.api.util.isSubtypeOf +import org.utbot.framework.plugin.api.util.isVarArgs import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.method import org.utbot.framework.plugin.api.util.objectArrayClassId @@ -485,8 +487,17 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA executable: ExecutableId, args: List, ambiguousOverloads: List - ): List = - args.withIndex().map { (i ,arg) -> + ): List { + val parametersCount = executable.parameters.size + + val (nonVarargParameters, varargParameters) = if (executable.isVarArgs) { + // Vararg is always the last parameter + args.take(parametersCount - 1) to args.drop(parametersCount - 1) + } else { + args to emptyList() + } + + val castedNonVarargs = nonVarargParameters.withIndex().map { (i ,arg) -> val targetType = executable.parameters[i] // always cast nulls @@ -503,6 +514,33 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA if (ancestors.isNotEmpty()) typeCast(targetType, arg) else arg } + if (varargParameters.isEmpty()) { + return castedNonVarargs + } + + // Vararg exist, create an array for them without inner casts + + val varargClassId = executable.parameters.last() + val varargElementClassId = varargClassId.elementClassId + ?: error("Vararg parameter of $executable has to be an array but ${executable.parameters.last()} found") + + // Use a simple array initializer here since this situation is pretty rare + val initializer = arrayInitializer( + arrayType = varargClassId, + elementType = varargElementClassId, + values = varargParameters + ) + + val arrayForVarargs = variableConstructor.newVar( + baseType = varargElementClassId, + baseName = "varargParameters" + ) { + initializer + } + + return castedNonVarargs + arrayForVarargs + } + /** * Receives a list of [CgExpression]. * Transforms it into a list of [CgExpression] where: @@ -611,6 +649,7 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA mapsDeepEqualsMethodId, hasCustomEqualsMethodId, getArrayLengthMethodId, + consumeBaseStreamMethodId, getLambdaCapturedArgumentTypesMethodId, getLambdaCapturedArgumentValuesMethodId, getInstantiatedMethodTypeMethodId, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt index 2b126861bd..f57c46789c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt @@ -146,6 +146,18 @@ import java.lang.reflect.InvocationTargetException import java.security.AccessControlException import java.lang.reflect.ParameterizedType import org.utbot.framework.UtSettings +import org.utbot.framework.plugin.api.UtStreamConsumingException +import org.utbot.framework.plugin.api.util.allSuperTypes +import org.utbot.framework.plugin.api.util.baseStreamClassId +import org.utbot.framework.plugin.api.util.doubleStreamClassId +import org.utbot.framework.plugin.api.util.doubleStreamToArrayMethodId +import org.utbot.framework.plugin.api.util.intStreamClassId +import org.utbot.framework.plugin.api.util.intStreamToArrayMethodId +import org.utbot.framework.plugin.api.util.isSubtypeOf +import org.utbot.framework.plugin.api.util.longStreamClassId +import org.utbot.framework.plugin.api.util.longStreamToArrayMethodId +import org.utbot.framework.plugin.api.util.streamClassId +import org.utbot.framework.plugin.api.util.streamToArrayMethodId import org.utbot.framework.plugin.api.util.isStatic private const val DEEP_EQUALS_MAX_DEPTH = 5 // TODO move it to plugin settings? @@ -308,21 +320,13 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } } - private fun processExecutionFailure(exception: Throwable) { - val methodInvocationBlock = { - with(currentExecutable) { - when (this) { - is MethodId -> thisInstance[this](*methodArguments.toTypedArray()).intercepted() - is ConstructorId -> this(*methodArguments.toTypedArray()).intercepted() - else -> {} // TODO: check this specific case - } - } - } + private fun processExecutionFailure(exceptionFromAnalysis: Throwable) { + val (methodInvocationBlock, expectedException) = constructExceptionProducingBlock(exceptionFromAnalysis) when (methodType) { - SUCCESSFUL -> error("Unexpected successful without exception method type for execution with exception $exception") + SUCCESSFUL -> error("Unexpected successful without exception method type for execution with exception $expectedException") PASSED_EXCEPTION -> { - testFrameworkManager.expectException(exception::class.id) { + testFrameworkManager.expectException(expectedException::class.id) { methodInvocationBlock() } } @@ -332,23 +336,63 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c methodInvocationBlock() } } - CRASH -> when (exception) { + CRASH -> when (expectedException) { is ConcreteExecutionFailureException -> { writeWarningAboutCrash() methodInvocationBlock() } is AccessControlException -> { // exception from sandbox - writeWarningAboutFailureTest(exception) + writeWarningAboutFailureTest(expectedException) } - else -> error("Unexpected crash suite for failing execution with $exception exception") + else -> error("Unexpected crash suite for failing execution with $expectedException exception") } FAILING -> { - writeWarningAboutFailureTest(exception) + writeWarningAboutFailureTest(expectedException) methodInvocationBlock() } - PARAMETRIZED -> error("Unexpected $PARAMETRIZED method type for failing execution with $exception exception") + PARAMETRIZED -> error("Unexpected $PARAMETRIZED method type for failing execution with $expectedException exception") + } + } + + private fun constructExceptionProducingBlock(exceptionFromAnalysis: Throwable): Pair<() -> Unit, Throwable> { + if (exceptionFromAnalysis is UtStreamConsumingException) { + return constructStreamConsumingBlock() to exceptionFromAnalysis.innerException + } + + return { + with(currentExecutable) { + when (this) { + is MethodId -> thisInstance[this](*methodArguments.toTypedArray()).intercepted() + is ConstructorId -> this(*methodArguments.toTypedArray()).intercepted() + else -> {} // TODO: check this specific case + } + } + } to exceptionFromAnalysis + } + + private fun constructStreamConsumingBlock(): () -> Unit { + val executable = currentExecutable + + require((executable is MethodId) && (executable.returnType isSubtypeOf baseStreamClassId)) { + "Unexpected non-stream returning executable $executable" + } + + val allSuperTypesOfReturn = executable.returnType.allSuperTypes().toSet() + + val streamConsumingMethodId = when { + // The order is important since all streams implement BaseStream + intStreamClassId in allSuperTypesOfReturn -> intStreamToArrayMethodId + longStreamClassId in allSuperTypesOfReturn -> longStreamToArrayMethodId + doubleStreamClassId in allSuperTypesOfReturn -> doubleStreamToArrayMethodId + streamClassId in allSuperTypesOfReturn -> streamToArrayMethodId + else -> { + // BaseStream, use util method to consume it + return { +utilsClassId[consumeBaseStream](actual) } + } } + + return { +actual[streamConsumingMethodId]() } } private fun shouldTestPassWithException(execution: UtExecution, exception: Throwable): Boolean { @@ -1248,6 +1292,32 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } else -> {} // TODO: check this specific case } + }.onFailure { + processActualInvocationFailure(it) + } + } + + private fun processActualInvocationFailure(e: Throwable) { + when (e) { + is UtStreamConsumingException -> processStreamConsumingException(e.innerException) + else -> throw e + } + } + + private fun processStreamConsumingException(innerException: Exception) { + val executable = currentExecutable + + require((executable is MethodId) && (executable.returnType isSubtypeOf baseStreamClassId)) { + "Unexpected exception $innerException during stream consuming in non-stream returning executable $executable" + } + + emptyLineIfNeeded() + + actual = newVar( + CgClassId(executable.returnType, isNullable = false), + "actual" + ) { + thisInstance[executable](*methodArguments.toTypedArray()) } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/UtilMethods.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/UtilMethods.kt index fb51419a41..85ba4dcbe5 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/UtilMethods.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/UtilMethods.kt @@ -65,6 +65,7 @@ internal fun UtilMethodProvider.utilMethodTextById( mapsDeepEqualsMethodId -> mapsDeepEquals(visibility, codegenLanguage) hasCustomEqualsMethodId -> hasCustomEquals(visibility, codegenLanguage) getArrayLengthMethodId -> getArrayLength(visibility, codegenLanguage) + consumeBaseStreamMethodId -> consumeBaseStream(visibility, codegenLanguage) buildStaticLambdaMethodId -> buildStaticLambda(visibility, codegenLanguage) buildLambdaMethodId -> buildLambda(visibility, codegenLanguage) // the following methods are used only by other util methods, so they can always be private @@ -828,6 +829,23 @@ private fun getArrayLength(visibility: Visibility, language: CodegenLanguage) = """.trimIndent() } +private fun consumeBaseStream(visibility: Visibility, language: CodegenLanguage) = + when (language) { + CodegenLanguage.JAVA -> + """ + ${visibility by language}static void consumeBaseStream(java.util.stream.BaseStream stream) { + stream.iterator().forEachRemaining(value -> {}); + } + """.trimIndent() + CodegenLanguage.KOTLIN -> { + """ + ${visibility by language}fun consumeBaseStream(stream: java.util.stream.BaseStream<*, *>) { + stream.iterator().forEachRemaining {} + } + """.trimIndent() + } + } + private fun buildStaticLambda(visibility: Visibility, language: CodegenLanguage) = when (language) { CodegenLanguage.JAVA -> @@ -1381,6 +1399,7 @@ private fun TestClassUtilMethodProvider.regularImportsByUtilMethod( } hasCustomEqualsMethodId -> emptyList() getArrayLengthMethodId -> listOf(java.lang.reflect.Array::class.id) + consumeBaseStreamMethodId -> listOf(java.util.stream.BaseStream::class.id) buildStaticLambdaMethodId -> when (codegenLanguage) { CodegenLanguage.JAVA -> listOf( MethodHandles::class.id, Method::class.id, MethodType::class.id, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/StreamConstructors.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/StreamConstructors.kt new file mode 100644 index 0000000000..e21f2d15df --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/StreamConstructors.kt @@ -0,0 +1,79 @@ +package org.utbot.framework.concrete + +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.MethodId +import org.utbot.framework.plugin.api.UtAssembleModel +import org.utbot.framework.plugin.api.UtExecutableCallModel +import org.utbot.framework.plugin.api.UtStatementModel +import org.utbot.framework.plugin.api.util.doubleArrayClassId +import org.utbot.framework.plugin.api.util.doubleStreamClassId +import org.utbot.framework.plugin.api.util.intArrayClassId +import org.utbot.framework.plugin.api.util.intStreamClassId +import org.utbot.framework.plugin.api.util.longArrayClassId +import org.utbot.framework.plugin.api.util.longStreamClassId +import org.utbot.framework.plugin.api.util.methodId +import org.utbot.framework.plugin.api.util.objectArrayClassId +import org.utbot.framework.plugin.api.util.streamClassId +import org.utbot.framework.util.valueToClassId + +/** + * Max number of elements in any concrete stream. + */ +private const val STREAM_ELEMENTS_LIMIT: Int = 1_000_000 + +internal abstract class AbstractStreamConstructor(private val streamClassId: ClassId, elementsClassId: ClassId) : UtAssembleModelConstructorBase() { + override fun provideInstantiationCall( + internalConstructor: UtModelConstructorInterface, + value: Any, + classId: ClassId + ): UtExecutableCallModel { + value as java.util.stream.BaseStream<*, *> + + // If [value] constructed incorrectly (some inner transient fields are null, etc.) this may fail. + // This value will be constructed as UtCompositeModel. + val models = value + .iterator() + .asSequence() + .map { internalConstructor.construct(it, valueToClassId(it)) } + .take(STREAM_ELEMENTS_LIMIT) + .toList() + + if (models.isEmpty()) { + return UtExecutableCallModel( + instance = null, + executable = emptyMethodId, + params = emptyList() + ) + } + + return UtExecutableCallModel( + instance = null, + executable = ofMethodId, + params = models + ) + } + + override fun UtAssembleModel.provideModificationChain( + internalConstructor: UtModelConstructorInterface, + value: Any + ): List = emptyList() + + private val emptyMethodId: MethodId = methodId( + classId = this.streamClassId, + name = "empty", + returnType = this.streamClassId, + arguments = emptyArray() + ) + + private val ofMethodId: MethodId = methodId( + classId = this.streamClassId, + name = "of", + returnType = this.streamClassId, + arguments = arrayOf(elementsClassId) // vararg + ) +} + +internal class BaseStreamConstructor : AbstractStreamConstructor(streamClassId, objectArrayClassId) +internal class IntStreamConstructor : AbstractStreamConstructor(intStreamClassId, intArrayClassId) +internal class LongStreamConstructor : AbstractStreamConstructor(longStreamClassId, longArrayClassId) +internal class DoubleStreamConstructor : AbstractStreamConstructor(doubleStreamClassId, doubleArrayClassId) 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 e870870492..82be902336 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 @@ -4,10 +4,15 @@ import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtExecutableCallModel import org.utbot.framework.plugin.api.UtStatementModel +import org.utbot.framework.plugin.api.util.id 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.stream.BaseStream +import java.util.stream.DoubleStream +import java.util.stream.IntStream +import java.util.stream.LongStream private val predefinedConstructors = mutableMapOf, () -> UtAssembleModelConstructorBase>( /** @@ -27,7 +32,6 @@ private val predefinedConstructors = mutableMapOf, () -> UtAssembleMode java.util.List::class.java to { CollectionConstructor() }, java.util.concurrent.CopyOnWriteArrayList::class.java to { CollectionConstructor() }, - /** * Queues, deques */ @@ -40,7 +44,6 @@ private val predefinedConstructors = mutableMapOf, () -> UtAssembleMode java.util.Queue::class.java to { CollectionConstructor() }, java.util.Deque::class.java to { CollectionConstructor() }, - /** * Sets */ @@ -50,8 +53,6 @@ private val predefinedConstructors = mutableMapOf, () -> UtAssembleMode java.util.AbstractSet::class.java to { CollectionConstructor() }, java.util.Set::class.java to { CollectionConstructor() }, - - /** * Maps */ @@ -69,7 +70,6 @@ private val predefinedConstructors = mutableMapOf, () -> UtAssembleMode */ java.util.Hashtable::class.java to { MapConstructor() }, - /** * String wrapper */ @@ -90,6 +90,14 @@ private val predefinedConstructors = mutableMapOf, () -> UtAssembleMode internal fun findUtAssembleModelConstructor(classId: ClassId): UtAssembleModelConstructorBase? = predefinedConstructors[classId.jClass]?.invoke() +internal fun findStreamConstructor(stream: BaseStream<*, *>): UtAssembleModelConstructorBase = + when (stream) { + is IntStream -> IntStreamConstructor() + is LongStream -> LongStreamConstructor() + is DoubleStream -> DoubleStreamConstructor() + else -> BaseStreamConstructor() + } + internal abstract class UtAssembleModelConstructorBase { fun constructAssembleModel( internalConstructor: UtModelConstructorInterface, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt index 16eed51791..9c374beea9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt @@ -23,6 +23,8 @@ import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation import org.utbot.framework.plugin.api.UtSandboxFailure import org.utbot.framework.plugin.api.UtStaticMethodInstrumentation +import org.utbot.framework.plugin.api.UtStreamConsumingException +import org.utbot.framework.plugin.api.UtStreamConsumingFailure import org.utbot.framework.plugin.api.UtTimeoutException import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.id @@ -189,7 +191,12 @@ object UtExecutionInstrumentation : Instrumentation { val utModelConstructor = UtModelConstructor(cache, utCompositeModelStrategy) utModelConstructor.run { val concreteUtModelResult = concreteResult.fold({ - UtExecutionSuccess(construct(it, returnClassId)) + try { + val model = construct(it, returnClassId) + UtExecutionSuccess(model) + } catch (e: Exception) { + processExceptionDuringModelConstruction(e) + } }) { sortOutException(it) } @@ -230,6 +237,12 @@ object UtExecutionInstrumentation : Instrumentation { } } + private fun processExceptionDuringModelConstruction(e: Exception): UtExecutionResult = + when (e) { + is UtStreamConsumingException -> UtStreamConsumingFailure(e) + else -> throw e + } + override fun getStaticField(fieldId: FieldId): Result = delegateInstrumentation.getStaticField(fieldId).map { value -> val cache = IdentityHashMap() diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtModelConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtModelConstructor.kt index a2194dedfe..c7f1d6edc4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtModelConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtModelConstructor.kt @@ -14,6 +14,7 @@ import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtNullModel import org.utbot.framework.plugin.api.UtPrimitiveModel import org.utbot.framework.plugin.api.UtReferenceModel +import org.utbot.framework.plugin.api.UtStreamConsumingException import org.utbot.framework.plugin.api.UtVoidModel import org.utbot.framework.plugin.api.isMockModel import org.utbot.framework.plugin.api.util.booleanClassId @@ -33,6 +34,7 @@ import org.utbot.framework.util.isInaccessibleViaReflection import org.utbot.framework.util.valueToClassId import java.lang.reflect.Modifier import java.util.IdentityHashMap +import java.util.stream.BaseStream /** * Represents common interface for model constructors. @@ -111,6 +113,7 @@ class UtModelConstructor( is Array<*> -> constructFromArray(value) is Enum<*> -> constructFromEnum(value) is Class<*> -> constructFromClass(value) + is BaseStream<*, *> -> constructFromStream(value) else -> constructFromAny(value) } } @@ -239,6 +242,22 @@ class UtModelConstructor( utModel } + private fun constructFromStream(stream: BaseStream<*, *>): UtModel = + constructedObjects.getOrElse(stream) { + val streamConstructor = findStreamConstructor(stream) + + try { + streamConstructor.constructAssembleModel(this, stream, valueToClassId(stream), handleId(stream)) { + constructedObjects[stream] = it + } + } catch (e: Exception) { + // An exception occurs during consuming of the stream - + // remove the constructed object and throw this exception as a result + constructedObjects.remove(stream) + throw UtStreamConsumingException(e) + } + } + /** * First tries to construct UtAssembleModel. If failure, constructs UtCompositeModel. */ diff --git a/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/CodeGenerationIntegrationTest.kt b/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/CodeGenerationIntegrationTest.kt index fd0333d5c0..00c6ef0868 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/CodeGenerationIntegrationTest.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/CodeGenerationIntegrationTest.kt @@ -33,7 +33,8 @@ abstract class CodeGenerationIntegrationTest( private val languagesLastStages: List = listOf( TestLastStage(CodegenLanguage.JAVA), TestLastStage(CodegenLanguage.KOTLIN) - ) + ), + private val codeGenerationModes: List = parameterizationModes ) { private val testSets: MutableList = arrayListOf() @@ -99,7 +100,7 @@ abstract class CodeGenerationIntegrationTest( // TODO: leave kotlin & parameterized mode configuration alone for now val pipelineConfigurations = languages - .flatMap { language -> parameterizationModes.map { mode -> language to mode } } + .flatMap { language -> codeGenerationModes.map { mode -> language to mode } } .filterNot { it == CodegenLanguage.KOTLIN to ParametrizedTestSource.PARAMETRIZE } for ((language, parameterizationMode) in pipelineConfigurations) { @@ -162,7 +163,7 @@ abstract class CodeGenerationIntegrationTest( private val languages = listOf(CodegenLanguage.JAVA, CodegenLanguage.KOTLIN) - private val parameterizationModes = listOf(ParametrizedTestSource.DO_NOT_PARAMETRIZE, ParametrizedTestSource.PARAMETRIZE) + internal val parameterizationModes = listOf(ParametrizedTestSource.DO_NOT_PARAMETRIZE, ParametrizedTestSource.PARAMETRIZE) data class CodeGenerationTestCases( val testClass: KClass<*>, diff --git a/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/UtValueTestCaseChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/UtValueTestCaseChecker.kt index 1cd5467b06..0c7d4ffe85 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/UtValueTestCaseChecker.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/UtValueTestCaseChecker.kt @@ -14,6 +14,7 @@ import org.utbot.framework.UtSettings.daysLimitForTempFiles import org.utbot.framework.UtSettings.testDisplayName import org.utbot.framework.UtSettings.testName import org.utbot.framework.UtSettings.testSummary +import org.utbot.framework.codegen.ParametrizedTestSource import org.utbot.framework.coverage.Coverage import org.utbot.framework.coverage.counters import org.utbot.framework.coverage.methodCoverage @@ -72,8 +73,9 @@ abstract class UtValueTestCaseChecker( pipelines: List = listOf( TestLastStage(CodegenLanguage.JAVA), TestLastStage(CodegenLanguage.KOTLIN) - ) -) : CodeGenerationIntegrationTest(testClass, testCodeGeneration, pipelines) { + ), + codeGenerationModes: List = parameterizationModes +) : CodeGenerationIntegrationTest(testClass, testCodeGeneration, pipelines, codeGenerationModes) { // contains already analyzed by the engine methods private val analyzedMethods: MutableMap = mutableMapOf() diff --git a/utbot-sample/src/main/java/org/utbot/examples/stream/BaseStreamExample.java b/utbot-sample/src/main/java/org/utbot/examples/stream/BaseStreamExample.java index 8760f36f55..b636394152 100644 --- a/utbot-sample/src/main/java/org/utbot/examples/stream/BaseStreamExample.java +++ b/utbot-sample/src/main/java/org/utbot/examples/stream/BaseStreamExample.java @@ -21,16 +21,6 @@ @SuppressWarnings({"IfStatementWithIdenticalBranches", "RedundantOperationOnEmptyContainer"}) public class BaseStreamExample { - Stream returningStreamExample(List list) { - UtMock.assume(list != null); - - if (list.isEmpty()) { - return list.stream(); - } else { - return list.stream(); - } - } - Stream returningStreamAsParameterExample(Stream s) { UtMock.assume(s != null); return s; diff --git a/utbot-sample/src/main/java/org/utbot/examples/stream/DoubleStreamExample.java b/utbot-sample/src/main/java/org/utbot/examples/stream/DoubleStreamExample.java index a86d6f953d..dc8801f498 100644 --- a/utbot-sample/src/main/java/org/utbot/examples/stream/DoubleStreamExample.java +++ b/utbot-sample/src/main/java/org/utbot/examples/stream/DoubleStreamExample.java @@ -18,19 +18,6 @@ @SuppressWarnings("IfStatementWithIdenticalBranches") public class DoubleStreamExample { - DoubleStream returningStreamExample(List list) { - UtMock.assume(list != null); - - final ToDoubleFunction shortToDoubleFunction = value -> value == null ? 0 : value.doubleValue(); - final DoubleStream doubles = list.stream().mapToDouble(shortToDoubleFunction); - - if (list.isEmpty()) { - return doubles; - } else { - return doubles; - } - } - DoubleStream returningStreamAsParameterExample(DoubleStream s) { UtMock.assume(s != null); diff --git a/utbot-sample/src/main/java/org/utbot/examples/stream/IntStreamExample.java b/utbot-sample/src/main/java/org/utbot/examples/stream/IntStreamExample.java index 58e57e8843..2e3e33f122 100644 --- a/utbot-sample/src/main/java/org/utbot/examples/stream/IntStreamExample.java +++ b/utbot-sample/src/main/java/org/utbot/examples/stream/IntStreamExample.java @@ -21,19 +21,6 @@ @SuppressWarnings("IfStatementWithIdenticalBranches") public class IntStreamExample { - IntStream returningStreamExample(List list) { - UtMock.assume(list != null); - - final ToIntFunction shortToIntFunction = value -> value == null ? 0 : value.intValue(); - final IntStream ints = list.stream().mapToInt(shortToIntFunction); - - if (list.isEmpty()) { - return ints; - } else { - return ints; - } - } - IntStream returningStreamAsParameterExample(IntStream s) { UtMock.assume(s != null); diff --git a/utbot-sample/src/main/java/org/utbot/examples/stream/LongStreamExample.java b/utbot-sample/src/main/java/org/utbot/examples/stream/LongStreamExample.java index 1a071fa69c..c821714069 100644 --- a/utbot-sample/src/main/java/org/utbot/examples/stream/LongStreamExample.java +++ b/utbot-sample/src/main/java/org/utbot/examples/stream/LongStreamExample.java @@ -20,19 +20,6 @@ @SuppressWarnings("IfStatementWithIdenticalBranches") public class LongStreamExample { - LongStream returningStreamExample(List list) { - UtMock.assume(list != null); - - final ToLongFunction shortToLongFunction = value -> value == null ? 0 : value.longValue(); - final LongStream longs = list.stream().mapToLong(shortToLongFunction); - - if (list.isEmpty()) { - return longs; - } else { - return longs; - } - } - LongStream returningStreamAsParameterExample(LongStream s) { UtMock.assume(s != null); diff --git a/utbot-sample/src/main/java/org/utbot/examples/stream/StreamsAsMethodResultExample.java b/utbot-sample/src/main/java/org/utbot/examples/stream/StreamsAsMethodResultExample.java new file mode 100644 index 0000000000..566a57f091 --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/stream/StreamsAsMethodResultExample.java @@ -0,0 +1,83 @@ +package org.utbot.examples.stream; + +import org.utbot.api.mock.*; + +import java.util.List; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +@SuppressWarnings({"IfStatementWithIdenticalBranches", "RedundantOperationOnEmptyContainer"}) +public class StreamsAsMethodResultExample { + Stream returningStreamExample(List list) { + UtMock.assume(list != null); + + if (list.isEmpty()) { + return list.stream(); + } + + return list.stream(); + } + + IntStream returningIntStreamExample(List list) { + UtMock.assume(list != null); + + final int size = list.size(); + + if (size == 0) { + return list.stream().mapToInt(value -> value); + } + + UtMock.assume(size == 1); + + final Integer integer = list.get(0); + + if (integer == null) { + return list.stream().mapToInt(value -> value); + } + + return list.stream().mapToInt(value -> value); + } + + LongStream returningLongStreamExample(List list) { + UtMock.assume(list != null); + + final int size = list.size(); + + if (size == 0) { + return list.stream().mapToLong(value -> value); + } + + UtMock.assume(size == 1); + + final Integer integer = list.get(0); + + if (integer == null) { + return list.stream().mapToLong(value -> value); + } + + return list.stream().mapToLong(value -> value); + } + + DoubleStream returningDoubleStreamExample(List list) { + UtMock.assume(list != null); + + final int size = list.size(); + + if (size == 0) { + return list.stream().mapToDouble(value -> value); + } + + UtMock.assume(size == 1); + + final Integer integer = list.get(0); + + if (integer == null) { + return list.stream().mapToDouble(value -> value); + } + + return list.stream().mapToDouble(value -> value); + } +} + diff --git a/utbot-summary/src/main/kotlin/org/utbot/summary/Summarization.kt b/utbot-summary/src/main/kotlin/org/utbot/summary/Summarization.kt index b67d08e4fd..d05c71a84e 100644 --- a/utbot-summary/src/main/kotlin/org/utbot/summary/Summarization.kt +++ b/utbot-summary/src/main/kotlin/org/utbot/summary/Summarization.kt @@ -29,6 +29,7 @@ import org.utbot.framework.plugin.api.UtExplicitlyThrownException import org.utbot.framework.plugin.api.UtImplicitlyThrownException import org.utbot.framework.plugin.api.UtOverflowFailure import org.utbot.framework.plugin.api.UtSandboxFailure +import org.utbot.framework.plugin.api.UtStreamConsumingFailure import org.utbot.framework.plugin.api.UtTimeoutException import org.utbot.framework.plugin.api.util.humanReadableName import org.utbot.framework.plugin.api.util.jClass @@ -244,12 +245,13 @@ class Summarization(val sourceFile: File?, val invokeDescriptions: List unsuccessfulFuzzerExecutions.add(utExecution) - is UtExplicitlyThrownException -> unsuccessfulFuzzerExecutions.add(utExecution) - is UtImplicitlyThrownException -> unsuccessfulFuzzerExecutions.add(utExecution) - is UtOverflowFailure -> unsuccessfulFuzzerExecutions.add(utExecution) - is UtSandboxFailure -> unsuccessfulFuzzerExecutions.add(utExecution) - is UtTimeoutException -> unsuccessfulFuzzerExecutions.add(utExecution) + is UtConcreteExecutionFailure, + is UtExplicitlyThrownException, + is UtImplicitlyThrownException, + is UtOverflowFailure, + is UtSandboxFailure, + is UtTimeoutException, + is UtStreamConsumingFailure -> unsuccessfulFuzzerExecutions.add(utExecution) is UtExecutionSuccess -> successfulFuzzerExecutions.add(utExecution) } } diff --git a/utbot-summary/src/main/kotlin/org/utbot/summary/TagGenerator.kt b/utbot-summary/src/main/kotlin/org/utbot/summary/TagGenerator.kt index 486df9a83f..5bced41064 100644 --- a/utbot-summary/src/main/kotlin/org/utbot/summary/TagGenerator.kt +++ b/utbot-summary/src/main/kotlin/org/utbot/summary/TagGenerator.kt @@ -10,6 +10,7 @@ import org.utbot.framework.plugin.api.UtImplicitlyThrownException import org.utbot.framework.plugin.api.UtOverflowFailure import org.utbot.framework.plugin.api.UtMethodTestSet import org.utbot.framework.plugin.api.UtSandboxFailure +import org.utbot.framework.plugin.api.UtStreamConsumingFailure import org.utbot.framework.plugin.api.UtTimeoutException import org.utbot.framework.plugin.api.util.humanReadableName import org.utbot.framework.plugin.api.util.isCheckedException @@ -186,6 +187,7 @@ private fun UtExecutionResult.clusterKind() = when (this) { is UtExecutionSuccess -> ExecutionGroup.SUCCESSFUL_EXECUTIONS is UtImplicitlyThrownException -> if (this.exception.isCheckedException) ExecutionGroup.CHECKED_EXCEPTIONS else ExecutionGroup.ERROR_SUITE is UtExplicitlyThrownException -> if (this.exception.isCheckedException) ExecutionGroup.CHECKED_EXCEPTIONS else ExecutionGroup.EXPLICITLY_THROWN_UNCHECKED_EXCEPTIONS + is UtStreamConsumingFailure -> ExecutionGroup.ERROR_SUITE is UtOverflowFailure -> ExecutionGroup.OVERFLOWS is UtTimeoutException -> ExecutionGroup.TIMEOUTS is UtConcreteExecutionFailure -> ExecutionGroup.CRASH_SUITE From 5b7ffc70f2b97a090a57e38dcee4664889e77213 Mon Sep 17 00:00:00 2001 From: Yury Kamenev Date: Wed, 26 Oct 2022 14:26:40 +0800 Subject: [PATCH 2/7] Got rid of varargs processing stuff --- .../kotlin/org/utbot/common/KClassUtil.kt | 43 ++--------------- .../tree/CgCallableAccessManager.kt | 41 +--------------- .../framework/concrete/StreamConstructors.kt | 48 ++++++++++++++++++- 3 files changed, 53 insertions(+), 79 deletions(-) diff --git a/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt index 63be7d20ea..b1371c8015 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt @@ -1,55 +1,22 @@ package org.utbot.common -import java.lang.reflect.Array import java.lang.reflect.InvocationTargetException import java.lang.reflect.Method import kotlin.reflect.KClass val Class<*>.nameOfPackage: String get() = `package`?.name?:"" +/** + * Invokes [this] method of passed [obj] instance (null for static methods) with the passed [args] arguments. + * NOTE: vararg parameters must be passed as an array of the corresponding type. + */ fun Method.invokeCatching(obj: Any?, args: List) = try { - val invocation = if (isVarArgs) { - // In java only last parameter could be vararg - val firstNonVarargParametersCount = parameterCount - 1 - - val varargArray = constructVarargParameterArray(firstNonVarargParametersCount, args) - - if (firstNonVarargParametersCount == 0) { - // Only vararg parameter, just pass only it as an array - invoke(obj, varargArray) - } else { - // Pass first non vararg parameters as vararg, and the vararg parameter as an array - val firstNonVarargParameters = args.take(firstNonVarargParametersCount) - - invoke(obj, *firstNonVarargParameters.toTypedArray(), varargArray) - } - } else { - invoke(obj, *args.toTypedArray()) - } + val invocation = invoke(obj, *args.toTypedArray()) Result.success(invocation) } catch (e: InvocationTargetException) { Result.failure(e.targetException) } -// https://stackoverflow.com/a/59857242 -private fun Method.constructVarargParameterArray(firstNonVarargParametersCount: Int, args: List): Any { - val varargCount = args.size - firstNonVarargParametersCount - val varargElements = args.drop(firstNonVarargParametersCount) - - val varargElementType = parameterTypes.last().componentType - requireNotNull(varargElementType) { - "Vararg parameter of method $this was expected to be array but ${parameterTypes.last()} found" - } - - val varargArray = Array.newInstance(parameterTypes.last().componentType, varargCount) - - varargElements.forEachIndexed { index, value -> - Array.set(varargArray, index, value) - } - - return varargArray -} - val KClass<*>.allNestedClasses: List> get() = listOf(this) + nestedClasses.flatMap { it.allNestedClasses } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt index 9574074b0f..82b75f8609 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt @@ -56,7 +56,6 @@ import org.utbot.framework.plugin.api.util.isAbstract import org.utbot.framework.plugin.api.util.isArray import org.utbot.framework.plugin.api.util.isStatic import org.utbot.framework.plugin.api.util.isSubtypeOf -import org.utbot.framework.plugin.api.util.isVarArgs import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.method import org.utbot.framework.plugin.api.util.objectArrayClassId @@ -487,17 +486,8 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA executable: ExecutableId, args: List, ambiguousOverloads: List - ): List { - val parametersCount = executable.parameters.size - - val (nonVarargParameters, varargParameters) = if (executable.isVarArgs) { - // Vararg is always the last parameter - args.take(parametersCount - 1) to args.drop(parametersCount - 1) - } else { - args to emptyList() - } - - val castedNonVarargs = nonVarargParameters.withIndex().map { (i ,arg) -> + ): List = + args.withIndex().map { (i ,arg) -> val targetType = executable.parameters[i] // always cast nulls @@ -514,33 +504,6 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA if (ancestors.isNotEmpty()) typeCast(targetType, arg) else arg } - if (varargParameters.isEmpty()) { - return castedNonVarargs - } - - // Vararg exist, create an array for them without inner casts - - val varargClassId = executable.parameters.last() - val varargElementClassId = varargClassId.elementClassId - ?: error("Vararg parameter of $executable has to be an array but ${executable.parameters.last()} found") - - // Use a simple array initializer here since this situation is pretty rare - val initializer = arrayInitializer( - arrayType = varargClassId, - elementType = varargElementClassId, - values = varargParameters - ) - - val arrayForVarargs = variableConstructor.newVar( - baseType = varargElementClassId, - baseName = "varargParameters" - ) { - initializer - } - - return castedNonVarargs + arrayForVarargs - } - /** * Receives a list of [CgExpression]. * Transforms it into a list of [CgExpression] where: diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/StreamConstructors.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/StreamConstructors.kt index e21f2d15df..82cb2908e5 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/StreamConstructors.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/StreamConstructors.kt @@ -2,18 +2,25 @@ package org.utbot.framework.concrete import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.MethodId +import org.utbot.framework.plugin.api.UtArrayModel import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtExecutableCallModel +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtNullModel +import org.utbot.framework.plugin.api.UtPrimitiveModel import org.utbot.framework.plugin.api.UtStatementModel +import org.utbot.framework.plugin.api.util.defaultValueModel import org.utbot.framework.plugin.api.util.doubleArrayClassId import org.utbot.framework.plugin.api.util.doubleStreamClassId import org.utbot.framework.plugin.api.util.intArrayClassId import org.utbot.framework.plugin.api.util.intStreamClassId +import org.utbot.framework.plugin.api.util.isPrimitiveWrapper import org.utbot.framework.plugin.api.util.longArrayClassId import org.utbot.framework.plugin.api.util.longStreamClassId import org.utbot.framework.plugin.api.util.methodId import org.utbot.framework.plugin.api.util.objectArrayClassId import org.utbot.framework.plugin.api.util.streamClassId +import org.utbot.framework.util.modelIdCounter import org.utbot.framework.util.valueToClassId /** @@ -21,7 +28,12 @@ import org.utbot.framework.util.valueToClassId */ private const val STREAM_ELEMENTS_LIMIT: Int = 1_000_000 -internal abstract class AbstractStreamConstructor(private val streamClassId: ClassId, elementsClassId: ClassId) : UtAssembleModelConstructorBase() { +internal abstract class AbstractStreamConstructor(private val streamClassId: ClassId, private val elementsClassId: ClassId) : UtAssembleModelConstructorBase() { + private val singleElementClassId: ClassId = elementsClassId.elementClassId + ?: error("Stream $streamClassId elements have to be an array but $elementsClassId found") + + private val elementDefaultValueModel: UtModel = singleElementClassId.defaultValueModel() + override fun provideInstantiationCall( internalConstructor: UtModelConstructorInterface, value: Any, @@ -46,10 +58,18 @@ internal abstract class AbstractStreamConstructor(private val streamClassId: Cla ) } + val varargModelsArray = UtArrayModel( + id = modelIdCounter.incrementAndGet(), + classId = elementsClassId, + length = models.size, + constModel = elementDefaultValueModel, + stores = models.mapIndexed { i, model -> i to model.wrapperModelToPrimitiveModel() }.toMap(mutableMapOf()) + ) + return UtExecutableCallModel( instance = null, executable = ofMethodId, - params = models + params = listOf(varargModelsArray) ) } @@ -71,6 +91,30 @@ internal abstract class AbstractStreamConstructor(private val streamClassId: Cla returnType = this.streamClassId, arguments = arrayOf(elementsClassId) // vararg ) + + /** + * Transforms [this] to [UtPrimitiveModel] if it is an [UtAssembleModel] for the corresponding wrapper + * (primitive int and wrapper Integer, etc.), and throws an error otherwise. + */ + private fun UtModel.wrapperModelToPrimitiveModel(): UtModel { + if (!classId.isPrimitiveWrapper) { + // We do not need to transform classes other than primitive wrappers + return this + } + + require(this !is UtNullModel) { + "Unexpected null value in wrapper for primitive stream ${this@AbstractStreamConstructor}" + } + + require(this is UtAssembleModel) { + "Unexpected not wrapper assemble model $this for value in wrapper " + + "for primitive stream ${this@AbstractStreamConstructor.streamClassId}" + } + + return (instantiationCall.params.firstOrNull() as? UtPrimitiveModel) + ?: error("No primitive value parameter for wrapper constructor $instantiationCall in model $this " + + "in wrapper for primitive stream ${this@AbstractStreamConstructor.streamClassId}") + } } internal class BaseStreamConstructor : AbstractStreamConstructor(streamClassId, objectArrayClassId) From baded68500a0e6cc174f18f78679dff8fa716377 Mon Sep 17 00:00:00 2001 From: Yury Kamenev Date: Wed, 26 Oct 2022 14:27:33 +0800 Subject: [PATCH 3/7] Used a specific exception for intermediate modifiable operations in streams --- .../framework/plugin/api/UtExecutionResult.kt | 13 +++-- .../StreamsAsMethodResultExampleTest.kt | 20 ++------ .../overrides/stream/UtDoubleStream.java | 49 +++++++++++++++---- .../engine/overrides/stream/UtIntStream.java | 49 +++++++++++++++---- .../engine/overrides/stream/UtLongStream.java | 49 +++++++++++++++---- .../engine/overrides/stream/UtStream.java | 48 ++++++++++++++---- .../constructor/tree/CgMethodConstructor.kt | 8 +-- .../org/utbot/framework/util/SootUtils.kt | 1 + 8 files changed, 175 insertions(+), 62 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt index 19bf988a0e..59df3e249f 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt @@ -46,10 +46,17 @@ class TimeoutException(s: String) : Exception(s) data class UtTimeoutException(override val exception: TimeoutException) : UtExecutionFailure() /** - * Represents an exception that occurs during consuming a stream. Stores it in [innerException]. + * Represents an exception that occurs during consuming a stream. + * [innerException] stores original exception (if possible), null if [UtStreamConsumingException] was constructed by the engine. */ -data class UtStreamConsumingException(val innerException: Exception) : RuntimeException() { - override fun toString(): String = innerException.toString() +data class UtStreamConsumingException(private val innerException: Exception?) : RuntimeException() { + /** + * Returns the original exception [innerException] if possible, and any [RuntimeException] otherwise. + */ + val innerExceptionOrAny: Throwable + get() = innerException ?: RuntimeException("Unknown runtime exception during consuming stream") + + override fun toString(): String = innerExceptionOrAny.toString() } /** diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt index caeb9b6e32..dfd8178981 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt @@ -8,6 +8,7 @@ import org.utbot.testcheckers.eq import org.utbot.tests.infrastructure.CodeGeneration import org.utbot.tests.infrastructure.FullWithAssumptions import org.utbot.tests.infrastructure.UtValueTestCaseChecker +import org.utbot.tests.infrastructure.isException import kotlin.streams.toList // TODO 1 instruction is always uncovered https://github.com/UnitTestBot/UTBotJava/issues/193 @@ -39,7 +40,7 @@ class StreamsAsMethodResultExampleTest : UtValueTestCaseChecker( eq(3), { c, r -> c.isEmpty() && c == r.getOrThrow().toList() }, { c, r -> c.isNotEmpty() && c.none { it == null } && c.toIntArray().contentEquals(r.getOrThrow().toArray()) }, - { c, r -> c.isNotEmpty() && c.any { it == null } && r.isNPE() }, + { c, r -> c.isNotEmpty() && c.any { it == null } && r.isException() }, coverage = FullWithAssumptions(assumeCallsNumber = 2) ) } @@ -51,7 +52,7 @@ class StreamsAsMethodResultExampleTest : UtValueTestCaseChecker( eq(3), { c, r -> c.isEmpty() && c == r.getOrThrow().toList() }, { c, r -> c.isNotEmpty() && c.none { it == null } && c.map { it.toLong() }.toLongArray().contentEquals(r.getOrThrow().toArray()) }, - { c, r -> c.isNotEmpty() && c.any { it == null } && r.isNPE() }, + { c, r -> c.isNotEmpty() && c.any { it == null } && r.isException() }, coverage = FullWithAssumptions(assumeCallsNumber = 2) ) } @@ -63,21 +64,8 @@ class StreamsAsMethodResultExampleTest : UtValueTestCaseChecker( eq(3), { c, r -> c.isEmpty() && c == r.getOrThrow().toList() }, { c, r -> c.isNotEmpty() && c.none { it == null } && c.map { it.toDouble() }.toDoubleArray().contentEquals(r.getOrThrow().toArray()) }, - { c, r -> c.isNotEmpty() && c.any { it == null } && r.isNPE() }, + { c, r -> c.isNotEmpty() && c.any { it == null } && r.isException() }, coverage = FullWithAssumptions(assumeCallsNumber = 2) ) } - - /** - * Checks the result in [NullPointerException] from the engine or - * [UtStreamConsumingException] with [NullPointerException] from concrete execution. - */ - private fun Result<*>.isNPE(): Boolean = - exceptionOrNull()?.let { - if (it is UtStreamConsumingException) { - return@let it.innerException is NullPointerException - } - - return@let it is NullPointerException - } ?: false } diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtDoubleStream.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtDoubleStream.java index d1e9768c2f..960f673e63 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtDoubleStream.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtDoubleStream.java @@ -3,6 +3,7 @@ import org.jetbrains.annotations.NotNull; import org.utbot.engine.overrides.collections.RangeModifiableUnlimitedArray; import org.utbot.engine.overrides.collections.UtGenericStorage; +import org.utbot.framework.plugin.api.*; import java.util.DoubleSummaryStatistics; import java.util.NoSuchElementException; @@ -122,8 +123,13 @@ public DoubleStream filter(DoublePredicate predicate) { int j = 0; for (int i = 0; i < size; i++) { double element = elementData.get(i); - if (predicate.test(element)) { - filtered[j++] = element; + + try { + if (predicate.test(element)) { + filtered[j++] = element; + } + } catch (Exception e) { + throw new UtStreamConsumingException(e); } } @@ -137,7 +143,11 @@ public DoubleStream map(DoubleUnaryOperator mapper) { int size = elementData.end; Double[] mapped = new Double[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsDouble(elementData.get(i)); + try { + mapped[i] = mapper.applyAsDouble(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtDoubleStream(mapped, size); @@ -153,7 +163,11 @@ public Stream mapToObj(DoubleFunction mapper) { int size = elementData.end; Object[] mapped = new Object[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.apply(elementData.get(i)); + try { + mapped[i] = mapper.apply(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtStream<>((U[]) mapped, size); @@ -166,7 +180,11 @@ public IntStream mapToInt(DoubleToIntFunction mapper) { int size = elementData.end; Integer[] mapped = new Integer[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsInt(elementData.get(i)); + try { + mapped[i] = mapper.applyAsInt(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtIntStream(mapped, size); @@ -179,7 +197,11 @@ public LongStream mapToLong(DoubleToLongFunction mapper) { int size = elementData.end; Long[] mapped = new Long[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsLong(elementData.get(i)); + try { + mapped[i] = mapper.applyAsLong(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtLongStream(mapped, size); @@ -256,7 +278,11 @@ public DoubleStream peek(DoubleConsumer action) { int size = elementData.end; for (int i = 0; i < size; i++) { - action.accept(elementData.get(i)); + try { + action.accept(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } // returned stream should be opened, so we "reopen" this stream to return it @@ -315,13 +341,16 @@ public DoubleStream skip(long n) { @SuppressWarnings("ResultOfMethodCallIgnored") @Override public void forEach(DoubleConsumer action) { - peek(action); + try { + peek(action); + } catch (UtStreamConsumingException e) { + // Since this is a terminal operation, we should throw an original exception + } } - @SuppressWarnings("ResultOfMethodCallIgnored") @Override public void forEachOrdered(DoubleConsumer action) { - peek(action); + forEach(action); } @Override diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtIntStream.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtIntStream.java index 322859d6b4..5acbccc29b 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtIntStream.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtIntStream.java @@ -3,6 +3,7 @@ import org.jetbrains.annotations.NotNull; import org.utbot.engine.overrides.collections.RangeModifiableUnlimitedArray; import org.utbot.engine.overrides.collections.UtGenericStorage; +import org.utbot.framework.plugin.api.*; import java.util.IntSummaryStatistics; import java.util.NoSuchElementException; @@ -123,8 +124,13 @@ public IntStream filter(IntPredicate predicate) { int j = 0; for (int i = 0; i < size; i++) { int element = elementData.get(i); - if (predicate.test(element)) { - filtered[j++] = element; + + try { + if (predicate.test(element)) { + filtered[j++] = element; + } + } catch (Exception e) { + throw new UtStreamConsumingException(e); } } @@ -138,7 +144,11 @@ public IntStream map(IntUnaryOperator mapper) { int size = elementData.end; Integer[] mapped = new Integer[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsInt(elementData.get(i)); + try { + mapped[i] = mapper.applyAsInt(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtIntStream(mapped, size); @@ -154,7 +164,11 @@ public Stream mapToObj(IntFunction mapper) { int size = elementData.end; U[] mapped = (U[]) new Object[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.apply(elementData.get(i)); + try { + mapped[i] = mapper.apply(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtStream<>(mapped, size); @@ -167,7 +181,11 @@ public LongStream mapToLong(IntToLongFunction mapper) { int size = elementData.end; Long[] mapped = new Long[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsLong(elementData.get(i)); + try { + mapped[i] = mapper.applyAsLong(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtLongStream(mapped, size); @@ -180,7 +198,11 @@ public DoubleStream mapToDouble(IntToDoubleFunction mapper) { int size = elementData.end; Double[] mapped = new Double[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsDouble(elementData.get(i)); + try { + mapped[i] = mapper.applyAsDouble(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtDoubleStream(mapped, size); @@ -257,7 +279,11 @@ public IntStream peek(IntConsumer action) { int size = elementData.end; for (int i = 0; i < size; i++) { - action.accept(elementData.get(i)); + try { + action.accept(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } // returned stream should be opened, so we "reopen" this stream to return it @@ -316,13 +342,16 @@ public IntStream skip(long n) { @SuppressWarnings("ResultOfMethodCallIgnored") @Override public void forEach(IntConsumer action) { - peek(action); + try { + peek(action); + } catch (UtStreamConsumingException e) { + // Since this is a terminal operation, we should throw an original exception + } } - @SuppressWarnings("ResultOfMethodCallIgnored") @Override public void forEachOrdered(IntConsumer action) { - peek(action); + forEach(action); } @Override diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtLongStream.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtLongStream.java index a7fb04e385..af169ad194 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtLongStream.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtLongStream.java @@ -3,6 +3,7 @@ import org.jetbrains.annotations.NotNull; import org.utbot.engine.overrides.collections.RangeModifiableUnlimitedArray; import org.utbot.engine.overrides.collections.UtGenericStorage; +import org.utbot.framework.plugin.api.*; import java.util.LongSummaryStatistics; import java.util.NoSuchElementException; @@ -123,8 +124,13 @@ public LongStream filter(LongPredicate predicate) { int j = 0; for (int i = 0; i < size; i++) { long element = elementData.get(i); - if (predicate.test(element)) { - filtered[j++] = element; + + try { + if (predicate.test(element)) { + filtered[j++] = element; + } + } catch (Exception e) { + throw new UtStreamConsumingException(e); } } @@ -138,7 +144,11 @@ public LongStream map(LongUnaryOperator mapper) { int size = elementData.end; Long[] mapped = new Long[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsLong(elementData.get(i)); + try { + mapped[i] = mapper.applyAsLong(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtLongStream(mapped, size); @@ -154,7 +164,11 @@ public Stream mapToObj(LongFunction mapper) { int size = elementData.end; Object[] mapped = new Object[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.apply(elementData.get(i)); + try { + mapped[i] = mapper.apply(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtStream<>((U[]) mapped, size); @@ -167,7 +181,11 @@ public IntStream mapToInt(LongToIntFunction mapper) { int size = elementData.end; Integer[] mapped = new Integer[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsInt(elementData.get(i)); + try { + mapped[i] = mapper.applyAsInt(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtIntStream(mapped, size); @@ -180,7 +198,11 @@ public DoubleStream mapToDouble(LongToDoubleFunction mapper) { int size = elementData.end; Double[] mapped = new Double[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsDouble(elementData.get(i)); + try { + mapped[i] = mapper.applyAsDouble(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtDoubleStream(mapped, size); @@ -257,7 +279,11 @@ public LongStream peek(LongConsumer action) { int size = elementData.end; for (int i = 0; i < size; i++) { - action.accept(elementData.get(i)); + try { + action.accept(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } // returned stream should be opened, so we "reopen" this stream to return it @@ -316,13 +342,16 @@ public LongStream skip(long n) { @SuppressWarnings("ResultOfMethodCallIgnored") @Override public void forEach(LongConsumer action) { - peek(action); + try { + peek(action); + } catch (UtStreamConsumingException e) { + // Since this is a terminal operation, we should throw an original exception + } } - @SuppressWarnings("ResultOfMethodCallIgnored") @Override public void forEachOrdered(LongConsumer action) { - peek(action); + forEach(action); } @Override diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtStream.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtStream.java index 132635a43e..b70b2d3910 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtStream.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtStream.java @@ -4,6 +4,7 @@ import org.utbot.engine.overrides.UtArrayMock; import org.utbot.engine.overrides.collections.RangeModifiableUnlimitedArray; import org.utbot.engine.overrides.collections.UtGenericStorage; +import org.utbot.framework.plugin.api.UtStreamConsumingException; import java.util.Comparator; import java.util.Iterator; @@ -120,8 +121,13 @@ public Stream filter(Predicate predicate) { int j = 0; for (int i = 0; i < size; i++) { E element = elementData.get(i); - if (predicate.test(element)) { - filtered[j++] = element; + + try { + if (predicate.test(element)) { + filtered[j++] = element; + } + } catch (Exception e) { + throw new UtStreamConsumingException(e); } } @@ -136,7 +142,11 @@ public Stream map(Function mapper) { int size = elementData.end; Object[] mapped = new Object[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.apply(elementData.get(i)); + try { + mapped[i] = mapper.apply(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtStream<>((R[]) mapped, size); @@ -149,7 +159,11 @@ public IntStream mapToInt(ToIntFunction mapper) { int size = elementData.end; Integer[] data = new Integer[size]; for (int i = 0; i < size; i++) { - data[i] = mapper.applyAsInt(elementData.getWithoutClassCastExceptionCheck(i)); + try { + data[i] = mapper.applyAsInt(elementData.getWithoutClassCastExceptionCheck(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtIntStream(data, size); @@ -162,7 +176,11 @@ public LongStream mapToLong(ToLongFunction mapper) { int size = elementData.end; Long[] data = new Long[size]; for (int i = 0; i < size; i++) { - data[i] = mapper.applyAsLong(elementData.getWithoutClassCastExceptionCheck(i)); + try { + data[i] = mapper.applyAsLong(elementData.getWithoutClassCastExceptionCheck(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtLongStream(data, size); @@ -175,7 +193,11 @@ public DoubleStream mapToDouble(ToDoubleFunction mapper) { int size = elementData.end; Double[] data = new Double[size]; for (int i = 0; i < size; i++) { - data[i] = mapper.applyAsDouble(elementData.getWithoutClassCastExceptionCheck(i)); + try { + data[i] = mapper.applyAsDouble(elementData.getWithoutClassCastExceptionCheck(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtDoubleStream(data, size); @@ -319,7 +341,11 @@ public Stream peek(Consumer action) { int size = elementData.end; for (int i = 0; i < size; i++) { - action.accept(elementData.get(i)); + try { + action.accept(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } // returned stream should be opened, so we "reopen" this stream to return it @@ -389,12 +415,16 @@ public Stream skip(long n) { @Override public void forEach(Consumer action) { - peek(action); + try { + peek(action); + } catch (UtStreamConsumingException e) { + // Since this is a terminal operation, we should throw an original exception + } } @Override public void forEachOrdered(Consumer action) { - peek(action); + forEach(action); } @NotNull diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt index f57c46789c..7e10279e52 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt @@ -357,7 +357,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c private fun constructExceptionProducingBlock(exceptionFromAnalysis: Throwable): Pair<() -> Unit, Throwable> { if (exceptionFromAnalysis is UtStreamConsumingException) { - return constructStreamConsumingBlock() to exceptionFromAnalysis.innerException + return constructStreamConsumingBlock() to exceptionFromAnalysis.innerExceptionOrAny } return { @@ -1299,12 +1299,12 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c private fun processActualInvocationFailure(e: Throwable) { when (e) { - is UtStreamConsumingException -> processStreamConsumingException(e.innerException) - else -> throw e + is UtStreamConsumingException -> processStreamConsumingException(e.innerExceptionOrAny) + else -> {} // Do nothing for now } } - private fun processStreamConsumingException(innerException: Exception) { + private fun processStreamConsumingException(innerException: Throwable) { val executable = currentExecutable require((executable is MethodId) && (executable.returnType isSubtypeOf baseStreamClassId)) { 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 a1de0a1bdb..fcf3cf5549 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 @@ -183,6 +183,7 @@ private val classesToLoad = arrayOf( org.utbot.engine.overrides.stream.Arrays::class, org.utbot.engine.overrides.collections.Collection::class, org.utbot.engine.overrides.collections.List::class, + org.utbot.framework.plugin.api.UtStreamConsumingException::class, org.utbot.engine.overrides.stream.UtStream::class, org.utbot.engine.overrides.stream.UtIntStream::class, org.utbot.engine.overrides.stream.UtLongStream::class, From 80057243bd06d8269c78cb8709aea747e802c70f Mon Sep 17 00:00:00 2001 From: Yury Kamenev Date: Thu, 27 Oct 2022 10:28:54 +0800 Subject: [PATCH 4/7] Fixed parametrized tests --- .../main/kotlin/org/utbot/common/HackUtil.kt | 9 ++ .../framework/plugin/api/UtExecutionResult.kt | 22 ++-- .../StreamsAsMethodResultExampleTest.kt | 1 - .../main/kotlin/org/utbot/engine/Resolver.kt | 8 ++ .../constructor/tree/CgMethodConstructor.kt | 100 +++++++++++++----- .../CodeGenerationIntegrationTest.kt | 7 +- .../infrastructure/UtValueTestCaseChecker.kt | 6 +- 7 files changed, 109 insertions(+), 44 deletions(-) diff --git a/utbot-core/src/main/kotlin/org/utbot/common/HackUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/HackUtil.kt index 881b708f55..e375e2ee0b 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/HackUtil.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/HackUtil.kt @@ -67,4 +67,13 @@ enum class WorkaroundReason { * requires thorough [investigation](https://github.com/UnitTestBot/UTBotJava/issues/716). */ IGNORE_STATICS_FROM_TRUSTED_LIBRARIES, + /** + * Methods that return [java.util.stream.BaseStream] as a result, can return them ”dirty” - consuming of them lead to the exception. + * The symbolic engine and concrete execution create UtStreamConsumingFailure executions in such cases. To warn a + * user about unsafety of using such “dirty” streams, code generation consumes them (mostly with `toArray` methods) + * and asserts exception. Unfortunately, it doesn't work well for parametrized tests - they create assertions relying on + * such-called “generic execution”, so resulted tests always contain `deepEquals` for streams, and we cannot easily + * construct `toArray` invocation (because streams cannot be consumed twice). + */ + CONSUME_DIRTY_STREAMS, } \ No newline at end of file diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt index 59df3e249f..a2e6c5fec5 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt @@ -11,6 +11,13 @@ data class UtExecutionSuccess(val model: UtModel) : UtExecutionResult() { sealed class UtExecutionFailure : UtExecutionResult() { abstract val exception: Throwable + + /** + * Represents the most inner exception in the failure. + * Often equals to [exception], but is wrapped exception in [UtStreamConsumingException]. + */ + open val rootCauseException: Throwable + get() = exception } data class UtOverflowFailure( @@ -22,8 +29,11 @@ data class UtSandboxFailure( ) : UtExecutionFailure() data class UtStreamConsumingFailure( - override val exception: Throwable -) : UtExecutionFailure() + override val exception: UtStreamConsumingException, +) : UtExecutionFailure() { + override val rootCauseException: Throwable + get() = exception.innerExceptionOrAny +} /** * unexpectedFail (when exceptions such as NPE, IOBE, etc. appear, but not thrown by a user, applies both for function under test and nested calls ) @@ -46,8 +56,8 @@ class TimeoutException(s: String) : Exception(s) data class UtTimeoutException(override val exception: TimeoutException) : UtExecutionFailure() /** - * Represents an exception that occurs during consuming a stream. - * [innerException] stores original exception (if possible), null if [UtStreamConsumingException] was constructed by the engine. + * An artificial exception that stores an exception that would be thrown in case of consuming stream by invoking terminal operations. + * [innerException] stores this possible exception (null if [UtStreamConsumingException] was constructed by the engine). */ data class UtStreamConsumingException(private val innerException: Exception?) : RuntimeException() { /** @@ -101,7 +111,7 @@ inline fun UtExecutionResult.onSuccess(action: (model: UtModel) -> Unit): UtExec } inline fun UtExecutionResult.onFailure(action: (exception: Throwable) -> Unit): UtExecutionResult { - if (this is UtExecutionFailure) action(exception) + if (this is UtExecutionFailure) action(rootCauseException) return this } @@ -111,6 +121,6 @@ fun UtExecutionResult.getOrThrow(): UtModel = when (this) { } fun UtExecutionResult.exceptionOrNull(): Throwable? = when (this) { - is UtExecutionFailure -> exception + is UtExecutionFailure -> rootCauseException is UtExecutionSuccess -> null } diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt index dfd8178981..b0c1b59446 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt @@ -20,7 +20,6 @@ class StreamsAsMethodResultExampleTest : UtValueTestCaseChecker( TestLastStage(CodegenLanguage.JAVA), TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration) ), - codeGenerationModes = listOf(ParametrizedTestSource.DO_NOT_PARAMETRIZE) // TODO exception from concrete is passed to arguments list somehow ) { @Test fun testReturningStreamExample() { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt index 24876fe55f..2cb7e2ddaa 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt @@ -90,6 +90,8 @@ import kotlin.math.max import kotlin.math.min import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentSetOf +import org.utbot.framework.plugin.api.UtStreamConsumingException +import org.utbot.framework.plugin.api.UtStreamConsumingFailure // hack const val MAX_LIST_SIZE = 10 @@ -370,6 +372,12 @@ class Resolver( */ private fun SymbolicFailure.resolve(): UtExecutionFailure { val exception = concreteException() + + if (exception is UtStreamConsumingException) { + // This exception is artificial and is not really thrown + return UtStreamConsumingFailure(exception) + } + return if (explicit) { UtExplicitlyThrownException(exception, inNestedMethod) } else { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt index 7e10279e52..191876b223 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt @@ -1,7 +1,9 @@ package org.utbot.framework.codegen.model.constructor.tree import org.utbot.common.PathUtil +import org.utbot.common.WorkaroundReason import org.utbot.common.isStatic +import org.utbot.common.workaround import org.utbot.framework.assemble.assemble import org.utbot.framework.codegen.ForceStaticMocking import org.utbot.framework.codegen.ParametrizedTestSource @@ -146,7 +148,8 @@ import java.lang.reflect.InvocationTargetException import java.security.AccessControlException import java.lang.reflect.ParameterizedType import org.utbot.framework.UtSettings -import org.utbot.framework.plugin.api.UtStreamConsumingException +import org.utbot.framework.plugin.api.UtExecutionResult +import org.utbot.framework.plugin.api.UtStreamConsumingFailure import org.utbot.framework.plugin.api.util.allSuperTypes import org.utbot.framework.plugin.api.util.baseStreamClassId import org.utbot.framework.plugin.api.util.doubleStreamClassId @@ -183,6 +186,12 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c private val fieldsOfExecutionResults = mutableMapOf, MutableList>() + /** + * Contains whether [UtStreamConsumingFailure] is in [CgMethodTestSet] for parametrized tests. + * See [WorkaroundReason.CONSUME_DIRTY_STREAMS]. + */ + private var containsStreamConsumingFailureForParametrizedTests: Boolean = false + private fun setupInstrumentation() { if (currentExecution is UtSymbolicExecution) { val execution = currentExecution as UtSymbolicExecution @@ -300,28 +309,30 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c emptyLineIfNeeded() val method = currentExecutable as MethodId val currentExecution = currentExecution!! + val executionResult = currentExecution.result + // build assertions - currentExecution.result - .onSuccess { result -> + executionResult + .onSuccess { resultModel -> methodType = SUCCESSFUL - // TODO possible engine bug - void method return type and result not UtVoidModel - if (result.isUnit() || method.returnType == voidClassId) { + // TODO possible engine bug - void method return type and result model not UtVoidModel + if (resultModel.isUnit() || method.returnType == voidClassId) { +thisInstance[method](*methodArguments.toTypedArray()) } else { - resultModel = result - val expected = variableConstructor.getOrCreateVariable(result, "expected") + this.resultModel = resultModel + val expected = variableConstructor.getOrCreateVariable(resultModel, "expected") assertEquality(expected, actual) } } - .onFailure { exception -> processExecutionFailure(exception) } + .onFailure { exception -> processExecutionFailure(exception, executionResult) } } else -> {} // TODO: check this specific case } } - private fun processExecutionFailure(exceptionFromAnalysis: Throwable) { - val (methodInvocationBlock, expectedException) = constructExceptionProducingBlock(exceptionFromAnalysis) + private fun processExecutionFailure(exceptionFromAnalysis: Throwable, executionResult: UtExecutionResult) { + val (methodInvocationBlock, expectedException) = constructExceptionProducingBlock(exceptionFromAnalysis, executionResult) when (methodType) { SUCCESSFUL -> error("Unexpected successful without exception method type for execution with exception $expectedException") @@ -355,9 +366,12 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } } - private fun constructExceptionProducingBlock(exceptionFromAnalysis: Throwable): Pair<() -> Unit, Throwable> { - if (exceptionFromAnalysis is UtStreamConsumingException) { - return constructStreamConsumingBlock() to exceptionFromAnalysis.innerExceptionOrAny + private fun constructExceptionProducingBlock( + exceptionFromAnalysis: Throwable, + executionResult: UtExecutionResult + ): Pair<() -> Unit, Throwable> { + if (executionResult is UtStreamConsumingFailure) { + return constructStreamConsumingBlock() to executionResult.rootCauseException } return { @@ -461,14 +475,16 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c is ConstructorId -> generateConstructorCall(currentExecutable!!, currentExecution!!) is MethodId -> { val method = currentExecutable as MethodId - currentExecution!!.result - .onSuccess { result -> - if (result.isUnit()) { + val executionResult = currentExecution!!.result + + executionResult + .onSuccess { resultModel -> + if (resultModel.isUnit()) { +thisInstance[method](*methodArguments.toTypedArray()) } else { //"generic" expected variable is represented with a wrapper if //actual result is primitive to support cases with exceptions. - resultModel = if (result is UtPrimitiveModel) assemble(result) else result + this.resultModel = if (resultModel is UtPrimitiveModel) assemble(resultModel) else resultModel val expectedVariable = currentMethodParameters[CgParameterKind.ExpectedResult]!! val expectedExpression = CgNotNullAssertion(expectedVariable) @@ -476,7 +492,15 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c assertEquality(expectedExpression, actual) } } - .onFailure { thisInstance[method](*methodArguments.toTypedArray()).intercepted() } + .onFailure { + workaround(WorkaroundReason.CONSUME_DIRTY_STREAMS) { + if (containsStreamConsumingFailureForParametrizedTests) { + constructStreamConsumingBlock().invoke() + } else { + thisInstance[method](*methodArguments.toTypedArray()).intercepted() + } + } + } } else -> {} // TODO: check this specific case } @@ -1208,7 +1232,9 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c // we cannot generate any assertions for constructor testing // but we need to generate a constructor call val constructorCall = currentExecutableId as ConstructorId - currentExecution.result + val executionResult = currentExecution.result + + executionResult .onSuccess { methodType = SUCCESSFUL @@ -1220,7 +1246,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c constructorCall(*methodArguments.toTypedArray()) } } - .onFailure { exception -> processExecutionFailure(exception) } + .onFailure { exception -> processExecutionFailure(exception, executionResult) } } /** @@ -1241,7 +1267,15 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c actual.type.isPrimitive -> generateDeepEqualsAssertion(expected, actual) else -> ifStatement( CgEqualTo(expected, nullLiteral()), - trueBranch = { +testFrameworkManager.assertions[testFramework.assertNull](actual).toStatement() }, + trueBranch = { + workaround(WorkaroundReason.CONSUME_DIRTY_STREAMS) { + if (containsStreamConsumingFailureForParametrizedTests) { + constructStreamConsumingBlock().invoke() + } else { + +testFrameworkManager.assertions[testFramework.assertNull](actual).toStatement() + } + } + }, falseBranch = { +testFrameworkManager.assertions[testFrameworkManager.assertNotNull](actual).toStatement() generateDeepEqualsAssertion(expected, actual) @@ -1270,7 +1304,9 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } private fun recordActualResult() { - currentExecution!!.result.onSuccess { result -> + val executionResult = currentExecution!!.result + + executionResult.onSuccess { resultModel -> when (val executable = currentExecutable) { is ConstructorId -> { // there is nothing to generate for constructors @@ -1278,13 +1314,13 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } is BuiltinMethodId -> error("Unexpected BuiltinMethodId $currentExecutable while generating actual result") is MethodId -> { - // TODO possible engine bug - void method return type and result not UtVoidModel - if (result.isUnit() || executable.returnType == voidClassId) return + // TODO possible engine bug - void method return type and result model not UtVoidModel + if (resultModel.isUnit() || executable.returnType == voidClassId) return emptyLineIfNeeded() actual = newVar( - CgClassId(result.classId, isNullable = result is UtNullModel), + CgClassId(resultModel.classId, isNullable = resultModel is UtNullModel), "actual" ) { thisInstance[executable](*methodArguments.toTypedArray()) @@ -1293,13 +1329,13 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c else -> {} // TODO: check this specific case } }.onFailure { - processActualInvocationFailure(it) + processActualInvocationFailure(executionResult) } } - private fun processActualInvocationFailure(e: Throwable) { - when (e) { - is UtStreamConsumingException -> processStreamConsumingException(e.innerExceptionOrAny) + private fun processActualInvocationFailure(executionResult: UtExecutionResult) { + when (executionResult) { + is UtStreamConsumingFailure -> processStreamConsumingException(executionResult.rootCauseException) else -> {} // Do nothing for now } } @@ -1423,6 +1459,12 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c //may be a heuristic to select a model with minimal number of internal nulls should be used val genericExecution = chooseGenericExecution(testSet.executions) + workaround(WorkaroundReason.CONSUME_DIRTY_STREAMS) { + containsStreamConsumingFailureForParametrizedTests = testSet.executions.any { + it.result is UtStreamConsumingFailure + } + } + val statics = genericExecution.stateBefore.statics return withTestMethodScope(genericExecution) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/CodeGenerationIntegrationTest.kt b/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/CodeGenerationIntegrationTest.kt index 00c6ef0868..fd0333d5c0 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/CodeGenerationIntegrationTest.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/CodeGenerationIntegrationTest.kt @@ -33,8 +33,7 @@ abstract class CodeGenerationIntegrationTest( private val languagesLastStages: List = listOf( TestLastStage(CodegenLanguage.JAVA), TestLastStage(CodegenLanguage.KOTLIN) - ), - private val codeGenerationModes: List = parameterizationModes + ) ) { private val testSets: MutableList = arrayListOf() @@ -100,7 +99,7 @@ abstract class CodeGenerationIntegrationTest( // TODO: leave kotlin & parameterized mode configuration alone for now val pipelineConfigurations = languages - .flatMap { language -> codeGenerationModes.map { mode -> language to mode } } + .flatMap { language -> parameterizationModes.map { mode -> language to mode } } .filterNot { it == CodegenLanguage.KOTLIN to ParametrizedTestSource.PARAMETRIZE } for ((language, parameterizationMode) in pipelineConfigurations) { @@ -163,7 +162,7 @@ abstract class CodeGenerationIntegrationTest( private val languages = listOf(CodegenLanguage.JAVA, CodegenLanguage.KOTLIN) - internal val parameterizationModes = listOf(ParametrizedTestSource.DO_NOT_PARAMETRIZE, ParametrizedTestSource.PARAMETRIZE) + private val parameterizationModes = listOf(ParametrizedTestSource.DO_NOT_PARAMETRIZE, ParametrizedTestSource.PARAMETRIZE) data class CodeGenerationTestCases( val testClass: KClass<*>, diff --git a/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/UtValueTestCaseChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/UtValueTestCaseChecker.kt index 0c7d4ffe85..1cd5467b06 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/UtValueTestCaseChecker.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/UtValueTestCaseChecker.kt @@ -14,7 +14,6 @@ import org.utbot.framework.UtSettings.daysLimitForTempFiles import org.utbot.framework.UtSettings.testDisplayName import org.utbot.framework.UtSettings.testName import org.utbot.framework.UtSettings.testSummary -import org.utbot.framework.codegen.ParametrizedTestSource import org.utbot.framework.coverage.Coverage import org.utbot.framework.coverage.counters import org.utbot.framework.coverage.methodCoverage @@ -73,9 +72,8 @@ abstract class UtValueTestCaseChecker( pipelines: List = listOf( TestLastStage(CodegenLanguage.JAVA), TestLastStage(CodegenLanguage.KOTLIN) - ), - codeGenerationModes: List = parameterizationModes -) : CodeGenerationIntegrationTest(testClass, testCodeGeneration, pipelines, codeGenerationModes) { + ) +) : CodeGenerationIntegrationTest(testClass, testCodeGeneration, pipelines) { // contains already analyzed by the engine methods private val analyzedMethods: MutableMap = mutableMapOf() From 96142b14a570c61a2c403e65055f20d00f10a1c5 Mon Sep 17 00:00:00 2001 From: Yury Kamenev Date: Fri, 28 Oct 2022 09:47:05 +0800 Subject: [PATCH 5/7] Fixed failed test --- .../src/main/kotlin/org/utbot/framework/plugin/api/Api.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index af70771bcc..3703940eab 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -1271,7 +1271,8 @@ enum class CodegenLanguage( "-d", buildDirectory, "-cp", classPath, "-XDignore.symbol.file", // to let javac use classes from rt.jar - "--add-exports", "java.base/sun.reflect.generics.repository=ALL-UNNAMED" + "--add-exports", "java.base/sun.reflect.generics.repository=ALL-UNNAMED", + "--add-exports", "java.base/sun.text=ALL-UNNAMED", ).plus(sourcesFiles) KOTLIN -> listOf("-d", buildDirectory, "-jvm-target", jvmTarget, "-cp", classPath).plus(sourcesFiles) From 9e9580fd582dcd101e78d97fda214fa91f3ded94 Mon Sep 17 00:00:00 2001 From: Yury Kamenev Date: Fri, 28 Oct 2022 11:05:24 +0800 Subject: [PATCH 6/7] Made UtStreamConsumingException visible for Soot but not for a user --- .../framework/plugin/api/UtExecutionResult.kt | 15 +-------------- .../api/visible/UtStreamConsumingException.kt | 15 +++++++++++++++ .../stream/StreamsAsMethodResultExampleTest.kt | 3 +-- .../engine/overrides/stream/UtDoubleStream.java | 2 +- .../engine/overrides/stream/UtIntStream.java | 2 +- .../engine/overrides/stream/UtLongStream.java | 2 +- .../utbot/engine/overrides/stream/UtStream.java | 2 +- .../src/main/kotlin/org/utbot/engine/Resolver.kt | 2 +- .../main/kotlin/org/utbot/engine/TypeResolver.kt | 9 +++++++-- .../concrete/UtExecutionInstrumentation.kt | 2 +- .../framework/concrete/UtModelConstructor.kt | 2 +- .../kotlin/org/utbot/framework/util/SootUtils.kt | 9 ++++++--- 12 files changed, 37 insertions(+), 28 deletions(-) create mode 100644 utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/visible/UtStreamConsumingException.kt diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt index a2e6c5fec5..cb6d6c9f61 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt @@ -1,5 +1,6 @@ package org.utbot.framework.plugin.api +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException import java.io.File import java.util.LinkedList @@ -55,20 +56,6 @@ class TimeoutException(s: String) : Exception(s) data class UtTimeoutException(override val exception: TimeoutException) : UtExecutionFailure() -/** - * An artificial exception that stores an exception that would be thrown in case of consuming stream by invoking terminal operations. - * [innerException] stores this possible exception (null if [UtStreamConsumingException] was constructed by the engine). - */ -data class UtStreamConsumingException(private val innerException: Exception?) : RuntimeException() { - /** - * Returns the original exception [innerException] if possible, and any [RuntimeException] otherwise. - */ - val innerExceptionOrAny: Throwable - get() = innerException ?: RuntimeException("Unknown runtime exception during consuming stream") - - override fun toString(): String = innerExceptionOrAny.toString() -} - /** * Indicates failure in concrete execution. * For now it is explicitly throwing by ConcreteExecutor in case child process death. diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/visible/UtStreamConsumingException.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/visible/UtStreamConsumingException.kt new file mode 100644 index 0000000000..fc25154daa --- /dev/null +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/visible/UtStreamConsumingException.kt @@ -0,0 +1,15 @@ +package org.utbot.framework.plugin.api.visible + +/** + * An artificial exception that stores an exception that would be thrown in case of consuming stream by invoking terminal operations. + * [innerException] stores this possible exception (null if [UtStreamConsumingException] was constructed by the engine). + */ +data class UtStreamConsumingException(private val innerException: Exception?) : RuntimeException() { + /** + * Returns the original exception [innerException] if possible, and any [RuntimeException] otherwise. + */ + val innerExceptionOrAny: Throwable + get() = innerException ?: RuntimeException("Unknown runtime exception during consuming stream") + + override fun toString(): String = innerExceptionOrAny.toString() +} \ No newline at end of file diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt index b0c1b59446..54706bb9bb 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt @@ -1,9 +1,8 @@ package org.utbot.examples.stream import org.junit.jupiter.api.Test -import org.utbot.framework.codegen.ParametrizedTestSource import org.utbot.framework.plugin.api.CodegenLanguage -import org.utbot.framework.plugin.api.UtStreamConsumingException +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException import org.utbot.testcheckers.eq import org.utbot.tests.infrastructure.CodeGeneration import org.utbot.tests.infrastructure.FullWithAssumptions diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtDoubleStream.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtDoubleStream.java index 960f673e63..1d1ef89ecf 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtDoubleStream.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtDoubleStream.java @@ -3,7 +3,7 @@ import org.jetbrains.annotations.NotNull; import org.utbot.engine.overrides.collections.RangeModifiableUnlimitedArray; import org.utbot.engine.overrides.collections.UtGenericStorage; -import org.utbot.framework.plugin.api.*; +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException; import java.util.DoubleSummaryStatistics; import java.util.NoSuchElementException; diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtIntStream.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtIntStream.java index 5acbccc29b..96788e9983 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtIntStream.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtIntStream.java @@ -3,7 +3,7 @@ import org.jetbrains.annotations.NotNull; import org.utbot.engine.overrides.collections.RangeModifiableUnlimitedArray; import org.utbot.engine.overrides.collections.UtGenericStorage; -import org.utbot.framework.plugin.api.*; +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException; import java.util.IntSummaryStatistics; import java.util.NoSuchElementException; diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtLongStream.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtLongStream.java index af169ad194..19166895f3 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtLongStream.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtLongStream.java @@ -3,7 +3,7 @@ import org.jetbrains.annotations.NotNull; import org.utbot.engine.overrides.collections.RangeModifiableUnlimitedArray; import org.utbot.engine.overrides.collections.UtGenericStorage; -import org.utbot.framework.plugin.api.*; +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException; import java.util.LongSummaryStatistics; import java.util.NoSuchElementException; diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtStream.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtStream.java index b70b2d3910..fd50aad64c 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtStream.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtStream.java @@ -4,7 +4,7 @@ import org.utbot.engine.overrides.UtArrayMock; import org.utbot.engine.overrides.collections.RangeModifiableUnlimitedArray; import org.utbot.engine.overrides.collections.UtGenericStorage; -import org.utbot.framework.plugin.api.UtStreamConsumingException; +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException; import java.util.Comparator; import java.util.Iterator; diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt index 2cb7e2ddaa..2116009cf1 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt @@ -90,7 +90,7 @@ import kotlin.math.max import kotlin.math.min import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentSetOf -import org.utbot.framework.plugin.api.UtStreamConsumingException +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException import org.utbot.framework.plugin.api.UtStreamConsumingFailure // hack diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/TypeResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/TypeResolver.kt index 535c117083..62868da773 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/TypeResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/TypeResolver.kt @@ -8,6 +8,7 @@ import org.utbot.engine.pc.mkAnd import org.utbot.engine.pc.mkEq import org.utbot.framework.plugin.api.id import org.utbot.framework.plugin.api.util.id +import org.utbot.framework.util.UTBOT_FRAMEWORK_API_VISIBLE_PACKAGE import soot.ArrayType import soot.IntType import soot.NullType @@ -192,7 +193,7 @@ class TypeResolver(private val typeRegistry: TypeRegistry, private val hierarchy } /** - * Remove wrapper types and, if any other type is available, artificial entities. + * Remove wrapper types, classes from the visible for Soot package and, if any other type is available, artificial entities. */ private fun TypeStorage.removeInappropriateTypes(): TypeStorage { val leastCommonSootClass = (leastCommonType as? RefType)?.sootClass @@ -211,11 +212,15 @@ class TypeResolver(private val typeRegistry: TypeRegistry, private val hierarchy return@filter keepArtificialEntities } - // All wrappers should filtered out because they could not be instantiated + // All wrappers and classes from the visible for Soot package should be filtered out because they could not be instantiated workaround(WorkaroundReason.HACK) { if (leastCommonSootClass == OBJECT_TYPE && sootClass.isOverridden) { return@filter false } + + if (sootClass.packageName == UTBOT_FRAMEWORK_API_VISIBLE_PACKAGE) { + return@filter false + } } return@filter true diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt index 9c374beea9..e8da79b550 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt @@ -23,7 +23,7 @@ import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation import org.utbot.framework.plugin.api.UtSandboxFailure import org.utbot.framework.plugin.api.UtStaticMethodInstrumentation -import org.utbot.framework.plugin.api.UtStreamConsumingException +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException import org.utbot.framework.plugin.api.UtStreamConsumingFailure import org.utbot.framework.plugin.api.UtTimeoutException import org.utbot.framework.plugin.api.util.UtContext diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtModelConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtModelConstructor.kt index c7f1d6edc4..c23fb7d052 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtModelConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtModelConstructor.kt @@ -14,7 +14,7 @@ import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtNullModel import org.utbot.framework.plugin.api.UtPrimitiveModel import org.utbot.framework.plugin.api.UtReferenceModel -import org.utbot.framework.plugin.api.UtStreamConsumingException +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException import org.utbot.framework.plugin.api.UtVoidModel import org.utbot.framework.plugin.api.isMockModel import org.utbot.framework.plugin.api.util.booleanClassId 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 fcf3cf5549..1a2c3da9ee 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 @@ -5,6 +5,7 @@ import org.utbot.engine.jimpleBody import org.utbot.engine.pureJavaSignature import org.utbot.framework.UtSettings import org.utbot.framework.plugin.api.ExecutableId +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException import org.utbot.framework.plugin.services.JdkInfo import soot.G import soot.PackManager @@ -98,9 +99,10 @@ private fun initSoot(buildDirs: List, classpath: String?, jdkInfo: JdkInfo val isOverriddenPackage = it.packageName.startsWith(UTBOT_OVERRIDDEN_PACKAGE_PREFIX) val isExamplesPackage = it.packageName.startsWith(UTBOT_EXAMPLES_PACKAGE_PREFIX) val isApiPackage = it.packageName.startsWith(UTBOT_API_PACKAGE_PREFIX) + val isVisiblePackage = it.packageName.startsWith(UTBOT_FRAMEWORK_API_VISIBLE_PACKAGE) - // remove if it is not a part of the examples (CUT), not a part of our API and not an override - if (!isOverriddenPackage && !isExamplesPackage && !isApiPackage) { + // remove if it is not a part of the examples (CUT), not a part of our API, not an override and not from visible for soot + if (!isOverriddenPackage && !isExamplesPackage && !isApiPackage && !isVisiblePackage) { Scene.v().removeClass(it) return@forEach } @@ -183,7 +185,7 @@ private val classesToLoad = arrayOf( org.utbot.engine.overrides.stream.Arrays::class, org.utbot.engine.overrides.collections.Collection::class, org.utbot.engine.overrides.collections.List::class, - org.utbot.framework.plugin.api.UtStreamConsumingException::class, + UtStreamConsumingException::class, org.utbot.engine.overrides.stream.UtStream::class, org.utbot.engine.overrides.stream.UtIntStream::class, org.utbot.engine.overrides.stream.UtLongStream::class, @@ -201,4 +203,5 @@ private const val UTBOT_PACKAGE_PREFIX = "org.utbot" private const val UTBOT_EXAMPLES_PACKAGE_PREFIX = "$UTBOT_PACKAGE_PREFIX.examples" private const val UTBOT_API_PACKAGE_PREFIX = "$UTBOT_PACKAGE_PREFIX.api" private const val UTBOT_OVERRIDDEN_PACKAGE_PREFIX = "$UTBOT_PACKAGE_PREFIX.engine.overrides" +internal const val UTBOT_FRAMEWORK_API_VISIBLE_PACKAGE = "$UTBOT_PACKAGE_PREFIX.framework.plugin.api.visible" private const val SOOT_PACKAGE_PREFIX = "soot." \ No newline at end of file From e36efcc13731416aa2af3d02e2a8ad8258994c1b Mon Sep 17 00:00:00 2001 From: Yury Kamenev Date: Fri, 28 Oct 2022 13:16:49 +0800 Subject: [PATCH 7/7] Disabled code generation testing for test with class from unvisible package --- .../org/utbot/examples/casts/CastExampleTest.kt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/casts/CastExampleTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/casts/CastExampleTest.kt index 7e48a85432..7206db0cea 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/casts/CastExampleTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/casts/CastExampleTest.kt @@ -80,12 +80,14 @@ internal class CastExampleTest : UtValueTestCaseChecker( @Test fun testComplicatedCast() { - check( - CastExample::complicatedCast, - eq(2), - { i, a, _ -> i == 0 && a != null && a[i] != null && a[i] !is CastClassFirstSucc }, - { i, a, r -> i == 0 && a != null && a[i] != null && a[i] is CastClassFirstSucc && r is CastClassFirstSucc }, - coverage = DoNotCalculate - ) + withEnabledTestingCodeGeneration(testCodeGeneration = false) { // error: package sun.text is not visible + check( + CastExample::complicatedCast, + eq(2), + { i, a, _ -> i == 0 && a != null && a[i] != null && a[i] !is CastClassFirstSucc }, + { i, a, r -> i == 0 && a != null && a[i] != null && a[i] is CastClassFirstSucc && r is CastClassFirstSucc }, + coverage = DoNotCalculate + ) + } } } \ No newline at end of file