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 4b41a6dd88..45d5272ec5 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,7 +11,6 @@ data class UtExecutionSuccess(val model: UtModel) : UtExecutionResult() { sealed class UtExecutionFailure : UtExecutionResult() { abstract val exception: Throwable - val isCheckedException get() = !(exception is RuntimeException || exception is Error) } data class UtOverflowFailure( diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/ThrowableUtils.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/ThrowableUtils.kt new file mode 100644 index 0000000000..48186cb8f7 --- /dev/null +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/ThrowableUtils.kt @@ -0,0 +1,10 @@ +package org.utbot.framework.plugin.api.util + +val Throwable.description + get() = message?.replace('\n', '\t') ?: "" + +val Throwable.isCheckedException + get() = !(this is RuntimeException || this is Error) + +val Class<*>.isCheckedException + get() = !(RuntimeException::class.java.isAssignableFrom(this) || Error::class.java.isAssignableFrom(this)) \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index 053e1228f4..992267afa8 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -153,7 +153,7 @@ import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.signature import org.utbot.framework.plugin.api.util.utContext -import org.utbot.framework.util.description +import org.utbot.framework.plugin.api.util.description import org.utbot.framework.util.executableId import org.utbot.fuzzer.FuzzedMethodDescription import org.utbot.fuzzer.ModelProvider 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 a7c3dc4216..37e3b3f2f8 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 @@ -46,7 +46,6 @@ import org.utbot.framework.plugin.api.UtMethod import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtReferenceModel import org.utbot.framework.plugin.api.UtTestCase -import org.utbot.framework.plugin.api.util.executableId import java.util.IdentityHashMap import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.PersistentMap @@ -55,6 +54,11 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.persistentSetOf import org.utbot.framework.codegen.model.constructor.builtin.streamsDeepEqualsMethodId +import org.utbot.framework.plugin.api.util.executableId +import org.utbot.framework.plugin.api.util.id +import org.utbot.framework.plugin.api.util.isCheckedException +import org.utbot.framework.plugin.api.util.isSubtypeOf +import org.utbot.framework.plugin.api.util.jClass /** * Interface for all code generation context aware entities @@ -232,7 +236,22 @@ internal interface CgContextOwner { currentExecutable = method.callable.executableId } - fun addException(exception: ClassId) { + fun addExceptionIfNeeded(exception: ClassId) { + if (exception !is BuiltinClassId) { + require(exception isSubtypeOf Throwable::class.id) { + "Class $exception which is not a Throwable was passed" + } + + val isUnchecked = !exception.jClass.isCheckedException + val alreadyAdded = + collectedExceptions.any { existingException -> exception isSubtypeOf existingException } + + if (isUnchecked || alreadyAdded) return + + collectedExceptions + .removeIf { existingException -> existingException isSubtypeOf exception } + } + if (collectedExceptions.add(exception)) { importIfNeeded(exception) } @@ -416,9 +435,9 @@ internal data class CgContext( val simpleName = testClassCustomName ?: "${createTestClassName(classUnderTest.name)}Test" val name = "$packagePrefix$simpleName" BuiltinClassId( - name = name, - canonicalName = name, - simpleName = simpleName + name = name, + canonicalName = name, + simpleName = simpleName ) } 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 19acd0ba98..65473b6f44 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 @@ -127,15 +127,12 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA //Builtin methods does not have jClass, so [methodId.method] will crash on it, //so we need to collect required exceptions manually from source codes if (methodId is BuiltinMethodId) { - methodId.findExceptionTypes().forEach { addException(it) } + methodId.findExceptionTypes().forEach { addExceptionIfNeeded(it) } return } - //If [InvocationTargetException] is thrown manually in test, we need - // to add "throws Throwable" and other exceptions are not required so on. + if (methodId == getTargetException) { - collectedExceptions.clear() - addException(Throwable::class.id) - return + addExceptionIfNeeded(Throwable::class.id) } val methodIsUnderTestAndThrowsExplicitly = methodId == currentExecutable @@ -148,16 +145,18 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA return } - methodId.method.exceptionTypes.forEach { addException(it.id) } + methodId.method.exceptionTypes.forEach { addExceptionIfNeeded(it.id) } } private fun newConstructorCall(constructorId: ConstructorId) { importIfNeeded(constructorId.classId) for (exception in constructorId.exceptions) { - addException(exception) + addExceptionIfNeeded(exception) } } + //WARN: if you make changes in the following sets of exceptions, + //don't forget to change them in hardcoded [UtilMethods] as well private fun BuiltinMethodId.findExceptionTypes(): Set { if (!this.isUtil) return emptySet() @@ -167,9 +166,9 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA getStaticFieldValueMethodId, getFieldValueMethodId, setStaticFieldMethodId, - setFieldMethodId, - createInstanceMethodId, - getUnsafeInstanceMethodId -> setOf(Exception::class.id) + setFieldMethodId -> setOf(IllegalAccessException::class.id, NoSuchFieldException::class.id) + createInstanceMethodId -> setOf(Exception::class.id) + getUnsafeInstanceMethodId -> setOf(ClassNotFoundException::class.id, NoSuchFieldException::class.id, IllegalAccessException::class.id) createArrayMethodId -> setOf(ClassNotFoundException::class.id) deepEqualsMethodId, arraysDeepEqualsMethodId, 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 57ea4d2ffb..cd564e3016 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 @@ -1300,14 +1300,15 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c utTestCase: UtTestCase, dataProviderMethodName: String ): CgParameterizedTestDataProviderMethod { - val parametersStatements = mutableListOf() + val dataProviderStatements = mutableListOf() + val dataProviderExceptions = mutableSetOf() val argListLength = utTestCase.executions.size val argListDeclaration = createArgList(argListLength) val argListVariable = argListDeclaration.variable - parametersStatements += argListDeclaration - parametersStatements += CgEmptyLine() + dataProviderStatements += argListDeclaration + dataProviderStatements += CgEmptyLine() for ((execIndex, execution) in utTestCase.executions.withIndex()) { withTestMethodScope(execution) { @@ -1347,22 +1348,25 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } //create a block for current test case - parametersStatements += innerBlock( + dataProviderStatements += innerBlock( {}, block(executionArgumentsBody) + createArgumentsCallRepresentation(execIndex, argListVariable, arguments) ) + + dataProviderExceptions += collectedExceptions } } - parametersStatements.addEmptyLineIfNeeded() - parametersStatements += CgReturnStatement(argListVariable) + dataProviderStatements.addEmptyLineIfNeeded() + dataProviderStatements += CgReturnStatement(argListVariable) return buildParameterizedTestDataProviderMethod { name = dataProviderMethodName returnType = argListClassId() - statements = parametersStatements + statements = dataProviderStatements annotations = createDataProviderAnnotations(dataProviderMethodName) + exceptions = dataProviderExceptions } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgTestClassConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgTestClassConstructor.kt index 83c77d7d6e..8475b4363c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgTestClassConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgTestClassConstructor.kt @@ -26,7 +26,7 @@ import org.utbot.framework.codegen.model.visitor.importUtilMethodDependencies import org.utbot.framework.plugin.api.MethodId import org.utbot.framework.plugin.api.UtMethod import org.utbot.framework.plugin.api.UtTestCase -import org.utbot.framework.util.description +import org.utbot.framework.plugin.api.util.description import kotlin.reflect.KClass internal class CgTestClassConstructor(val context: CgContext) : diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/Builders.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/Builders.kt index 55963cbb48..93dca48c75 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/Builders.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/Builders.kt @@ -101,10 +101,10 @@ class CgParameterizedTestDataProviderBuilder : CgMethodBuilder = mutableListOf() override lateinit var statements: List override lateinit var annotations: MutableList - override val exceptions: MutableSet = mutableSetOf() + override lateinit var exceptions: MutableSet override var documentation: CgDocumentationComment = CgDocumentationComment(emptyList()) - override fun build() = CgParameterizedTestDataProviderMethod(name, statements, returnType, annotations) + override fun build() = CgParameterizedTestDataProviderMethod(name, statements, returnType, annotations, exceptions) } fun buildParameterizedTestDataProviderMethod( diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/CgElement.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/CgElement.kt index c3630a7192..c36aac6f28 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/CgElement.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/CgElement.kt @@ -223,9 +223,8 @@ class CgParameterizedTestDataProviderMethod( override val statements: List, override val returnType: ClassId, override val annotations: List, + override val exceptions: Set, ) : CgMethod(isStatic = true) { - override val exceptions: Set = emptySet() - override val parameters: List = emptyList() override val documentation: CgDocumentationComment = CgDocumentationComment(emptyList()) override val requiredFields: List = emptyList() diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgJavaRenderer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgJavaRenderer.kt index 751057a1ab..dd6882690c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgJavaRenderer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgJavaRenderer.kt @@ -213,7 +213,8 @@ internal class CgJavaRenderer(context: CgContext, printer: CgPrinter = CgPrinter //we do not have a good string representation for two-dimensional array, so this strange if-else is required val returnType = if (element.returnType.simpleName == "Object[][]") "java.lang.Object[][]" else "${element.returnType}" - print("public static $returnType ${element.name}() throws Exception") + print("public static $returnType ${element.name}()") + renderExceptions(element) } override fun visit(element: CgInnerBlock) { 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 c45e10e18b..2098303280 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 @@ -329,8 +329,7 @@ fun createInstance(language: CodegenLanguage): String = when (language) { CodegenLanguage.JAVA -> { """ - private static Object createInstance(String className) - throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException { + private static Object createInstance(String className) throws Exception { Class clazz = Class.forName(className); return Class.forName("sun.misc.Unsafe").getDeclaredMethod("allocateInstance", Class.class) .invoke(getUnsafeInstance(), clazz); diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/util/ThrowableUtils.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/util/ThrowableUtils.kt deleted file mode 100644 index ce92971319..0000000000 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/util/ThrowableUtils.kt +++ /dev/null @@ -1,4 +0,0 @@ -package org.utbot.framework.util - -val Throwable.description - get() = message?.replace('\n', '\t') ?: "" \ No newline at end of file diff --git a/utbot-framework/src/test/kotlin/org/utbot/framework/codegen/BaseTestCodeGeneratorPipeline.kt b/utbot-framework/src/test/kotlin/org/utbot/framework/codegen/BaseTestCodeGeneratorPipeline.kt index 58bb70ac5f..cd715c73c8 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/framework/codegen/BaseTestCodeGeneratorPipeline.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/framework/codegen/BaseTestCodeGeneratorPipeline.kt @@ -16,7 +16,7 @@ import org.utbot.framework.plugin.api.UtMethod import org.utbot.framework.plugin.api.UtTestCase import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.withUtContext -import org.utbot.framework.util.description +import org.utbot.framework.plugin.api.util.description import kotlin.reflect.KClass import mu.KotlinLogging import org.junit.jupiter.api.Assertions.assertTrue 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 862d2a261e..c8ea78c86e 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.UtTestCase import org.utbot.framework.plugin.api.UtTimeoutException +import org.utbot.framework.plugin.api.util.isCheckedException import org.utbot.summary.UtSummarySettings.MIN_NUMBER_OF_EXECUTIONS_FOR_CLUSTERING import org.utbot.summary.clustering.MatrixUniqueness import org.utbot.summary.clustering.SplitSteps @@ -146,8 +147,8 @@ enum class ClusterKind { private fun UtExecutionResult.clusterKind() = when (this) { is UtExecutionSuccess -> ClusterKind.SUCCESSFUL_EXECUTIONS - is UtImplicitlyThrownException -> if (this.isCheckedException) ClusterKind.CHECKED_EXCEPTIONS else ClusterKind.ERROR_SUITE - is UtExplicitlyThrownException -> if (this.isCheckedException) ClusterKind.CHECKED_EXCEPTIONS else ClusterKind.EXPLICITLY_THROWN_UNCHECKED_EXCEPTIONS + is UtImplicitlyThrownException -> if (this.exception.isCheckedException) ClusterKind.CHECKED_EXCEPTIONS else ClusterKind.ERROR_SUITE + is UtExplicitlyThrownException -> if (this.exception.isCheckedException) ClusterKind.CHECKED_EXCEPTIONS else ClusterKind.EXPLICITLY_THROWN_UNCHECKED_EXCEPTIONS is UtOverflowFailure -> ClusterKind.OVERFLOWS is UtTimeoutException -> ClusterKind.TIMEOUTS is UtConcreteExecutionFailure -> ClusterKind.CRASH_SUITE