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 07f0be271b..565bb51a25 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 @@ -98,6 +98,22 @@ data class UtMethodTestSet( val clustersInfo: List<Pair<UtClusterInfo?, IntRange>> = listOf(null to executions.indices) ) +data class CgMethodTestSet private constructor( + val executableId: ExecutableId, + val executions: List<UtExecution> = emptyList(), + val jimpleBody: JimpleBody? = null, + val errors: Map<String, Int> = emptyMap(), + val clustersInfo: List<Pair<UtClusterInfo?, IntRange>> = listOf(null to executions.indices) +) { + constructor(from: UtMethodTestSet) : this( + from.method.callable.executableId, + from.executions, + from.jimpleBody, + from.errors, + from.clustersInfo + ) +} + data class Step( val stmt: Stmt, val depth: Int, 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 45d5272ec5..dfb4235b37 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 @@ -17,6 +17,10 @@ data class UtOverflowFailure( override val exception: Throwable, ) : UtExecutionFailure() +data class UtSandboxFailure( + 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) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Extensions.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Extensions.kt index b760070557..cdeb6a0a87 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Extensions.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Extensions.kt @@ -46,6 +46,8 @@ import kotlin.reflect.jvm.javaMethod import kotlinx.collections.immutable.PersistentMap import kotlinx.collections.immutable.persistentHashMapOf import org.utbot.engine.pc.UtSolverStatusUNDEFINED +import org.utbot.framework.plugin.api.ExecutableId +import org.utbot.framework.plugin.api.util.executableId import soot.ArrayType import soot.PrimType import soot.RefLikeType @@ -326,15 +328,28 @@ val <R> UtMethod<R>.signature: String return "${methodName}()" } +val ExecutableId.displayName: String + get() { + val executableName = this.name + val parameters = this.parameters.joinToString(separator = ", ") { it.canonicalName } + return "$executableName($parameters)" + } + +val Constructor<*>.displayName: String + get() = executableId.displayName + +val Method.displayName: String + get() = executableId.displayName + val <R> UtMethod<R>.displayName: String get() { - val methodName = this.callable.name - val javaMethod = this.javaMethod ?: this.javaConstructor - if (javaMethod != null) { - val parameters = javaMethod.parameters.joinToString(separator = ", ") { "${it.type.canonicalName}" } - return "${methodName}($parameters)" + val executableId = this.javaMethod?.executableId ?: this.javaConstructor?.executableId + return if (executableId != null) { + executableId.displayName + } else { + val methodName = this.callable.name + return "${methodName}()" } - return "${methodName}()" } 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 93a1eba650..7797a686df 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt @@ -71,6 +71,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.UtSandboxFailure import soot.ArrayType import soot.BooleanType import soot.ByteType @@ -88,6 +89,7 @@ import soot.SootField import soot.Type import soot.VoidType import sun.java2d.cmm.lcms.LcmsServiceProvider +import java.security.AccessControlException // hack const val MAX_LIST_SIZE = 10 @@ -371,12 +373,11 @@ class Resolver( return if (explicit) { UtExplicitlyThrownException(exception, inNestedMethod) } else { - // TODO SAT-1561 - val isOverflow = exception is ArithmeticException && exception.message?.contains("overflow") == true - if (isOverflow) { - UtOverflowFailure(exception) - } else { - UtImplicitlyThrownException(exception, inNestedMethod) + when { + // TODO SAT-1561 + exception is ArithmeticException && exception.message?.contains("overflow") == true -> UtOverflowFailure(exception) + exception is AccessControlException -> UtSandboxFailure(exception) + else -> UtImplicitlyThrownException(exception, inNestedMethod) } } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/CodeGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/CodeGenerator.kt index 01a3199c7c..9904dc13bd 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/CodeGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/CodeGenerator.kt @@ -12,15 +12,16 @@ import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor import org.utbot.framework.codegen.model.constructor.tree.TestsGenerationReport import org.utbot.framework.codegen.model.tree.CgTestClassFile import org.utbot.framework.codegen.model.visitor.CgAbstractRenderer +import org.utbot.framework.plugin.api.CgMethodTestSet import org.utbot.framework.plugin.api.CodegenLanguage +import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.MockFramework -import org.utbot.framework.plugin.api.UtMethod import org.utbot.framework.plugin.api.UtMethodTestSet import org.utbot.framework.plugin.api.util.id class CodeGenerator( private val classUnderTest: Class<*>, - params: MutableMap<UtMethod<*>, List<String>> = mutableMapOf(), + paramNames: MutableMap<ExecutableId, List<String>> = mutableMapOf(), testFramework: TestFramework = TestFramework.defaultItem, mockFramework: MockFramework? = MockFramework.defaultItem, staticsMocking: StaticsMocking = StaticsMocking.defaultItem, @@ -35,7 +36,7 @@ class CodeGenerator( ) { private var context: CgContext = CgContext( classUnderTest = classUnderTest.id, - paramNames = params, + paramNames = paramNames, testFramework = testFramework, mockFramework = mockFramework ?: MockFramework.MOCKITO, codegenLanguage = codegenLanguage, @@ -57,13 +58,20 @@ class CodeGenerator( fun generateAsStringWithTestReport( testSets: Collection<UtMethodTestSet>, testClassCustomName: String? = null, - ): TestsCodeWithTestReport = - withCustomContext(testClassCustomName) { - context.withClassScope { - val testClassFile = CgTestClassConstructor(context).construct(testSets) - TestsCodeWithTestReport(renderClassFile(testClassFile), testClassFile.testsGenerationReport) - } - } + ): TestsCodeWithTestReport { + val cgTestSets = testSets.map { CgMethodTestSet(it) }.toList() + return generateAsStringWithTestReport(cgTestSets, testClassCustomName) + } + + fun generateAsStringWithTestReport( + cgTestSets: List<CgMethodTestSet>, + testClassCustomName: String? = null, + ): TestsCodeWithTestReport = withCustomContext(testClassCustomName) { + context.withClassScope { + val testClassFile = CgTestClassConstructor(context).construct(cgTestSets) + TestsCodeWithTestReport(renderClassFile(testClassFile), testClassFile.testsGenerationReport) + } + } /** * Wrapper function that configures context as needed for utbot-online: 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 c271cce902..bfd2d49d15 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 @@ -42,10 +42,8 @@ import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.MethodId import org.utbot.framework.plugin.api.MockFramework import org.utbot.framework.plugin.api.UtExecution -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.UtMethodTestSet import java.util.IdentityHashMap import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.PersistentMap @@ -55,7 +53,7 @@ import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.persistentSetOf import org.utbot.framework.codegen.model.constructor.builtin.streamsDeepEqualsMethodId import org.utbot.framework.codegen.model.tree.CgParameterKind -import org.utbot.framework.plugin.api.util.executableId +import org.utbot.framework.plugin.api.CgMethodTestSet import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.isCheckedException import org.utbot.framework.plugin.api.util.isSubtypeOf @@ -121,7 +119,7 @@ internal interface CgContextOwner { val prevStaticFieldValues: MutableMap<FieldId, CgVariable> // names of parameters of methods under test - val paramNames: Map<UtMethod<*>, List<String>> + val paramNames: Map<ExecutableId, List<String>> // UtExecution we currently generate a test method for. // It is null when no test method is being generated at the moment. @@ -176,7 +174,7 @@ internal interface CgContextOwner { // map from a set of tests for a method to another map // which connects code generation error message // with the number of times it occurred - val codeGenerationErrors: MutableMap<UtMethodTestSet, MutableMap<String, Int>> + val codeGenerationErrors: MutableMap<CgMethodTestSet, MutableMap<String, Int>> // package for generated test class val testClassPackageName: String @@ -233,8 +231,8 @@ internal interface CgContextOwner { currentBlock = currentBlock.add(it) } - fun updateCurrentExecutable(method: UtMethod<*>) { - currentExecutable = method.callable.executableId + fun updateCurrentExecutable(executableId: ExecutableId) { + currentExecutable = executableId } fun addExceptionIfNeeded(exception: ClassId) { @@ -399,7 +397,7 @@ internal data class CgContext( override val testMethods: MutableList<CgTestMethod> = mutableListOf(), override val existingMethodNames: MutableSet<String> = mutableSetOf(), override val prevStaticFieldValues: MutableMap<FieldId, CgVariable> = mutableMapOf(), - override val paramNames: Map<UtMethod<*>, List<String>>, + override val paramNames: Map<ExecutableId, List<String>>, override var currentExecution: UtExecution? = null, override val testFramework: TestFramework, override val mockFramework: MockFramework, @@ -415,7 +413,7 @@ internal data class CgContext( override var declaredExecutableRefs: PersistentMap<ExecutableId, CgVariable> = persistentMapOf(), override var thisInstance: CgValue? = null, override val methodArguments: MutableList<CgValue> = mutableListOf(), - override val codeGenerationErrors: MutableMap<UtMethodTestSet, MutableMap<String, Int>> = mutableMapOf(), + override val codeGenerationErrors: MutableMap<CgMethodTestSet, MutableMap<String, Int>> = mutableMapOf(), override val testClassPackageName: String = classUnderTest.packageName, override var shouldOptimizeImports: Boolean = false, override var testClassCustomName: String? = null, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/name/CgNameGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/name/CgNameGenerator.kt index aabaae88ff..3fd130deec 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/name/CgNameGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/name/CgNameGenerator.kt @@ -7,9 +7,8 @@ import org.utbot.framework.codegen.model.constructor.util.infiniteInts import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.CodegenLanguage import org.utbot.framework.plugin.api.ConstructorId +import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.MethodId -import org.utbot.framework.plugin.api.UtMethod -import org.utbot.framework.plugin.api.util.executableId import org.utbot.framework.plugin.api.util.isArray /** @@ -45,7 +44,7 @@ internal interface CgNameGenerator { /** * Generate a new test method name. */ - fun testMethodNameFor(method: UtMethod<*>, customName: String? = null): String + fun testMethodNameFor(executableId: ExecutableId, customName: String? = null): String /** * Generates a new parameterized test method name by data provider method name. @@ -55,12 +54,12 @@ internal interface CgNameGenerator { /** * Generates a new data for parameterized test provider method name */ - fun dataProviderMethodNameFor(method: UtMethod<*>): String + fun dataProviderMethodNameFor(executableId: ExecutableId): String /** * Generate a new error method name */ - fun errorMethodNameFor(method: UtMethod<*>): String + fun errorMethodNameFor(executableId: ExecutableId): String } /** @@ -86,11 +85,9 @@ internal class CgNameGeneratorImpl(private val context: CgContext) return variableName(baseName.decapitalize(), isMock) } - override fun testMethodNameFor(method: UtMethod<*>, customName: String?): String { - val executableName = when (val id = method.callable.executableId) { - is ConstructorId -> id.classId.prettifiedName // TODO: maybe we need some suffix e.g. "Ctor"? - is MethodId -> id.name - } + override fun testMethodNameFor(executableId: ExecutableId, customName: String?): String { + val executableName = createExecutableName(executableId) + // no index suffix allowed only when there's a vacant custom name val name = if (customName != null && customName !in existingMethodNames) { customName @@ -107,18 +104,16 @@ internal class CgNameGeneratorImpl(private val context: CgContext) override fun parameterizedTestMethodName(dataProviderMethodName: String) = dataProviderMethodName.replace(dataProviderMethodPrefix, "parameterizedTestsFor") - override fun dataProviderMethodNameFor(method: UtMethod<*>): String { - val indexedName = nextIndexedMethodName(method.callable.name.capitalize(), skipOne = true) + override fun dataProviderMethodNameFor(executableId: ExecutableId): String { + val executableName = createExecutableName(executableId) + val indexedName = nextIndexedMethodName(executableName.capitalize(), skipOne = true) existingMethodNames += indexedName return "$dataProviderMethodPrefix$indexedName" } - override fun errorMethodNameFor(method: UtMethod<*>): String { - val executableName = when (val id = method.callable.executableId) { - is ConstructorId -> id.classId.prettifiedName - is MethodId -> id.name - } + override fun errorMethodNameFor(executableId: ExecutableId): String { + val executableName = createExecutableName(executableId) val newName = when (val base = "test${executableName.capitalize()}_errors") { !in existingMethodNames -> base else -> nextIndexedMethodName(base) @@ -152,6 +147,13 @@ internal class CgNameGeneratorImpl(private val context: CgContext) if (baseName !in existingVariableNames) "`$baseName`" else nextIndexedVarName(baseName) } } + + private fun createExecutableName(executableId: ExecutableId): String { + return when (executableId) { + is ConstructorId -> executableId.classId.prettifiedName // TODO: maybe we need some suffix e.g. "Ctor"? + is MethodId -> executableId.name + } + } } /** 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 165134a0c5..bdf20c7920 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,8 +1,6 @@ package org.utbot.framework.codegen.model.constructor.tree import org.utbot.common.PathUtil -import org.utbot.common.packageName -import org.utbot.engine.isStatic import org.utbot.framework.assemble.assemble import org.utbot.framework.codegen.ForceStaticMocking import org.utbot.framework.codegen.Junit4 @@ -86,6 +84,7 @@ import org.utbot.framework.fields.ExecutionStateAnalyzer import org.utbot.framework.fields.FieldPath import org.utbot.framework.plugin.api.BuiltinClassId import org.utbot.framework.plugin.api.BuiltinMethodId +import org.utbot.framework.plugin.api.CgMethodTestSet import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.CodegenLanguage import org.utbot.framework.plugin.api.ConcreteExecutionFailureException @@ -106,13 +105,12 @@ import org.utbot.framework.plugin.api.UtExecution import org.utbot.framework.plugin.api.UtExecutionFailure import org.utbot.framework.plugin.api.UtExecutionSuccess import org.utbot.framework.plugin.api.UtExplicitlyThrownException -import org.utbot.framework.plugin.api.UtMethod -import org.utbot.framework.plugin.api.UtMethodTestSet import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation 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.UtSandboxFailure import org.utbot.framework.plugin.api.UtStaticMethodInstrumentation import org.utbot.framework.plugin.api.UtTimeoutException import org.utbot.framework.plugin.api.UtVoidModel @@ -123,6 +121,7 @@ import org.utbot.framework.plugin.api.util.builtinStaticMethodId import org.utbot.framework.plugin.api.util.doubleArrayClassId import org.utbot.framework.plugin.api.util.doubleClassId import org.utbot.framework.plugin.api.util.doubleWrapperClassId +import org.utbot.framework.plugin.api.util.executable import org.utbot.framework.plugin.api.util.field import org.utbot.framework.plugin.api.util.floatArrayClassId import org.utbot.framework.plugin.api.util.floatClassId @@ -149,7 +148,7 @@ import org.utbot.framework.util.isUnit import org.utbot.summary.SummarySentenceConstants.TAB import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl import java.lang.reflect.InvocationTargetException -import kotlin.reflect.jvm.javaType +import java.security.AccessControlException private const val DEEP_EQUALS_MAX_DEPTH = 5 // TODO move it to plugin settings? @@ -284,30 +283,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c */ private fun generateResultAssertions() { when (currentExecutable) { - is ConstructorId -> { - // we cannot generate any assertions for constructor testing - // but we need to generate a constructor call - val constructorCall = currentExecutable as ConstructorId - val currentExecution = currentExecution!! - currentExecution.result - .onSuccess { - methodType = SUCCESSFUL - - // TODO engine returns UtCompositeModel sometimes (concrete execution?) - - // TODO support inner classes constructors testing JIRA:1461 - require(!constructorCall.classId.isInner) { - "Inner class ${constructorCall.classId} constructor testing is not supported yet" - } - - actual = newVar(constructorCall.classId, "actual") { - constructorCall(*methodArguments.toTypedArray()) - } - } - .onFailure { exception -> - processExecutionFailure(currentExecution, exception) - } - } + is ConstructorId -> generateConstructorCall(currentExecutable!!, currentExecution!!) is BuiltinMethodId -> error("Unexpected BuiltinMethodId $currentExecutable while generating result assertions") is MethodId -> { emptyLineIfNeeded() @@ -368,6 +344,11 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c methodType = CRASH writeWarningAboutCrash() } + is AccessControlException -> { + methodType = CRASH + writeWarningAboutFailureTest(exception) + return + } else -> { methodType = FAILING writeWarningAboutFailureTest(exception) @@ -378,6 +359,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } private fun shouldTestPassWithException(execution: UtExecution, exception: Throwable): Boolean { + if (exception is AccessControlException) return false // tests with timeout or crash should be processed differently if (exception is TimeoutException || exception is ConcreteExecutionFailureException) return false @@ -404,7 +386,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c val executableName = "${currentExecutable!!.classId.name}.${currentExecutable!!.name}" val warningLine = mutableListOf( - "This test fails because method [$executableName] produces [$exception]" + "This test fails because method [$executableName] produces [$exception]".escapeControlChars() ) val neededStackTraceLines = mutableListOf<String>() @@ -422,6 +404,10 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c +CgMultilineComment(warningLine + neededStackTraceLines.reversed()) } + private fun String.escapeControlChars() : String { + return this.replace("\b", "\\b").replace("\n", "\\n").replace("\t", "\\t").replace("\r", "\\r") + } + private fun writeWarningAboutCrash() { +CgSingleLineComment("This invocation possibly crashes JVM") } @@ -432,24 +418,30 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c */ private fun generateAssertionsForParameterizedTest() { emptyLineIfNeeded() - val method = currentExecutable as MethodId - currentExecution!!.result - .onSuccess { result -> - if (result.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 - val expectedVariable = currentMethodParameters[CgParameterKind.ExpectedResult]!! - val expectedExpression = CgNotNullAssertion(expectedVariable) + when (currentExecutable) { + is ConstructorId -> generateConstructorCall(currentExecutable!!, currentExecution!!) + is MethodId -> { + val method = currentExecutable as MethodId + currentExecution!!.result + .onSuccess { result -> + if (result.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 - assertEquality(expectedExpression, actual) - println() - } + val expectedVariable = currentMethodParameters[CgParameterKind.ExpectedResult]!! + val expectedExpression = CgNotNullAssertion(expectedVariable) + + assertEquality(expectedExpression, actual) + println() + } + } + .onFailure { thisInstance[method](*methodArguments.toTypedArray()).intercepted() } } - .onFailure { thisInstance[method](*methodArguments.toTypedArray()).intercepted() } + } } /** @@ -1022,6 +1014,27 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c generateDeepEqualsOrNullAssertion(expected.expression, actual) } + private fun generateConstructorCall(currentExecutableId: ExecutableId, currentExecution: UtExecution) { + // we cannot generate any assertions for constructor testing + // but we need to generate a constructor call + val constructorCall = currentExecutableId as ConstructorId + currentExecution.result + .onSuccess { + methodType = SUCCESSFUL + + require(!constructorCall.classId.isInner) { + "Inner class ${constructorCall.classId} constructor testing is not supported yet" + } + + actual = newVar(constructorCall.classId, "actual") { + constructorCall(*methodArguments.toTypedArray()) + } + } + .onFailure { exception -> + processExecutionFailure(currentExecution, exception) + } + } + /** * We can't use standard deepEquals method in parametrized tests * because nullable objects require different asserts. @@ -1076,7 +1089,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c emptyLineIfNeeded() actual = newVar( - CgClassId(executable.returnType, isNullable = result is UtNullModel), + CgClassId(result.classId, isNullable = result is UtNullModel), "actual" ) { thisInstance[executable](*methodArguments.toTypedArray()) @@ -1086,11 +1099,11 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } } - fun createTestMethod(utMethod: UtMethod<*>, execution: UtExecution): CgTestMethod = + fun createTestMethod(executableId: ExecutableId, execution: UtExecution): CgTestMethod = withTestMethodScope(execution) { - val testMethodName = nameGenerator.testMethodNameFor(utMethod, execution.testMethodName) + val testMethodName = nameGenerator.testMethodNameFor(executableId, execution.testMethodName) // TODO: remove this line when SAT-1273 is completed - execution.displayName = execution.displayName?.let { "${utMethod.callable.name}: $it" } + execution.displayName = execution.displayName?.let { "${executableId.name}: $it" } testMethod(testMethodName, execution.displayName) { rememberInitialStaticFields() val stateAnalyzer = ExecutionStateAnalyzer(execution) @@ -1105,7 +1118,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } // build arguments for ((index, param) in execution.stateBefore.parameters.withIndex()) { - val name = paramNames[utMethod]?.get(index) + val name = paramNames[executableId]?.get(index) methodArguments += variableConstructor.getOrCreateVariable(param, name) } rememberInitialEnvironmentState(modificationInfo) @@ -1165,7 +1178,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c private val expectedResultVarName = "expectedResult" private val expectedErrorVarName = "expectedError" - fun createParameterizedTestMethod(testSet: UtMethodTestSet, dataProviderMethodName: String): CgTestMethod { + fun createParameterizedTestMethod(testSet: CgMethodTestSet, dataProviderMethodName: String): CgTestMethod { //TODO: orientation on generic execution may be misleading, but what is the alternative? //may be a heuristic to select a model with minimal number of internal nulls should be used val genericExecution = testSet.executions @@ -1217,11 +1230,11 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } private fun createParameterDeclarations( - testSet: UtMethodTestSet, + testSet: CgMethodTestSet, genericExecution: UtExecution, ): List<CgParameterDeclaration> { - val methodUnderTest = testSet.method - val methodUnderTestParameters = testSet.method.callable.parameters + val executableUnderTest = testSet.executableId + val executableUnderTestParameters = testSet.executableId.executable.parameters return mutableListOf<CgParameterDeclaration>().apply { // this instance @@ -1240,9 +1253,8 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } // arguments for (index in genericExecution.stateBefore.parameters.indices) { - val argumentName = paramNames[methodUnderTest]?.get(index) - val paramIndex = if (methodUnderTest.isStatic) index else index + 1 - val paramType = methodUnderTestParameters[paramIndex].type.javaType + val argumentName = paramNames[executableUnderTest]?.get(index) + val paramType = executableUnderTestParameters[index].parameterizedType val argumentType = when { paramType is Class<*> && paramType.isArray -> paramType.id @@ -1261,9 +1273,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c currentMethodParameters[CgParameterKind.Argument(index)] = argument.parameter } - val method = currentExecutable as MethodId - val containsFailureExecution = containsFailureExecution(testSet) - + val method = currentExecutable!! val expectedResultClassId = wrapTypeIfRequired(method.returnType) if (expectedResultClassId != voidClassId) { @@ -1281,6 +1291,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c currentMethodParameters[CgParameterKind.ExpectedResult] = expectedResult.parameter } + val containsFailureExecution = containsFailureExecution(testSet) if (containsFailureExecution) { val classClassId = Class::class.id val expectedException = CgParameterDeclaration( @@ -1310,7 +1321,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c * Standard logic for generating each test case parameter code is used. */ fun createParameterizedTestDataProvider( - testSet: UtMethodTestSet, + testSet: CgMethodTestSet, dataProviderMethodName: String ): CgParameterizedTestDataProviderMethod { return withDataProviderScope { @@ -1335,18 +1346,18 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } } - private fun createExecutionArguments(testSet: UtMethodTestSet, execution: UtExecution): List<CgExpression> { + private fun createExecutionArguments(testSet: CgMethodTestSet, execution: UtExecution): List<CgExpression> { val arguments = mutableListOf<CgExpression>() execution.stateBefore.thisInstance?.let { arguments += variableConstructor.getOrCreateVariable(it) } for ((paramIndex, paramModel) in execution.stateBefore.parameters.withIndex()) { - val argumentName = paramNames[testSet.method]?.get(paramIndex) + val argumentName = paramNames[testSet.executableId]?.get(paramIndex) arguments += variableConstructor.getOrCreateVariable(paramModel, argumentName) } - val method = currentExecutable as MethodId + val method = currentExecutable!! val needsReturnValue = method.returnType != voidClassId val containsFailureExecution = containsFailureExecution(testSet) execution.result @@ -1567,7 +1578,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c arguments = arrayOf(objectArrayClassId) ) - private fun containsFailureExecution(testSet: UtMethodTestSet) = + private fun containsFailureExecution(testSet: CgMethodTestSet) = testSet.executions.any { it.result is UtExecutionFailure } @@ -1618,6 +1629,12 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c ) } + if (result is UtSandboxFailure) { + testFrameworkManager.disableTestMethod( + "Disabled due to sandbox" + ) + } + val testMethod = buildTestMethod { name = methodName parameters = params @@ -1668,8 +1685,8 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } } - fun errorMethod(method: UtMethod<*>, errors: Map<String, Int>): CgRegion<CgMethod> { - val name = nameGenerator.errorMethodNameFor(method) + fun errorMethod(executable: ExecutableId, errors: Map<String, Int>): CgRegion<CgMethod> { + val name = nameGenerator.errorMethodNameFor(executable) val body = block { comment("Couldn't generate some tests. List of errors:") comment() @@ -1697,7 +1714,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } } val errorTestMethod = CgErrorTestMethod(name, body) - return CgSimpleRegion("Errors report for ${method.callable.name}", listOf(errorTestMethod)) + return CgSimpleRegion("Errors report for ${executable.name}", listOf(errorTestMethod)) } private fun getJvmReportDocumentation(jvmReportPath: String): String { 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 b3bb20dc6a..4d2c5c9b2a 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 @@ -23,10 +23,12 @@ import org.utbot.framework.codegen.model.tree.buildTestClass import org.utbot.framework.codegen.model.tree.buildTestClassBody import org.utbot.framework.codegen.model.tree.buildTestClassFile import org.utbot.framework.codegen.model.visitor.importUtilMethodDependencies +import org.utbot.framework.plugin.api.CgMethodTestSet +import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.MethodId -import org.utbot.framework.plugin.api.UtMethod import org.utbot.framework.plugin.api.UtMethodTestSet import org.utbot.framework.plugin.api.util.description +import org.utbot.framework.plugin.api.util.kClass import kotlin.reflect.KClass internal class CgTestClassConstructor(val context: CgContext) : @@ -43,7 +45,7 @@ internal class CgTestClassConstructor(val context: CgContext) : /** * Given a list of test sets constructs CgTestClass */ - fun construct(testSets: Collection<UtMethodTestSet>): CgTestClassFile { + fun construct(testSets: Collection<CgMethodTestSet>): CgTestClassFile { return buildTestClassFile { testClass = buildTestClass { // TODO: obtain test class from plugin @@ -51,7 +53,7 @@ internal class CgTestClassConstructor(val context: CgContext) : body = buildTestClassBody { cgDataProviderMethods.clear() for (testSet in testSets) { - updateCurrentExecutable(testSet.method) + updateCurrentExecutable(testSet.executableId) val currentMethodUnderTestRegions = construct(testSet) ?: continue val executableUnderTestCluster = CgExecutableUnderTestCluster( "Test suites for executable $currentExecutable", @@ -76,7 +78,7 @@ internal class CgTestClassConstructor(val context: CgContext) : } } - private fun construct(testSet: UtMethodTestSet): List<CgRegion<CgMethod>>? { + private fun construct(testSet: CgMethodTestSet): List<CgRegion<CgMethod>>? { if (testSet.executions.isEmpty()) { return null } @@ -106,7 +108,7 @@ internal class CgTestClassConstructor(val context: CgContext) : } ParametrizedTestSource.PARAMETRIZE -> { runCatching { - val dataProviderMethodName = nameGenerator.dataProviderMethodNameFor(testSet.method) + val dataProviderMethodName = nameGenerator.dataProviderMethodNameFor(testSet.executableId) val parameterizedTestMethod = methodConstructor.createParameterizedTestMethod(testSet, dataProviderMethodName) @@ -126,14 +128,14 @@ internal class CgTestClassConstructor(val context: CgContext) : val errors = testSet.allErrors if (errors.isNotEmpty()) { - regions += methodConstructor.errorMethod(testSet.method, errors) + regions += methodConstructor.errorMethod(testSet.executableId, errors) testsGenerationReport.addMethodErrors(testSet, errors) } return regions } - private fun processFailure(testSet: UtMethodTestSet, failure: Throwable) { + private fun processFailure(testSet: CgMethodTestSet, failure: Throwable) { codeGenerationErrors .getOrPut(testSet) { mutableMapOf() } .merge(failure.description, 1, Int::plus) @@ -172,23 +174,23 @@ internal class CgTestClassConstructor(val context: CgContext) : /** * Engine errors + codegen errors for a given [UtMethodTestSet] */ - private val UtMethodTestSet.allErrors: Map<String, Int> + private val CgMethodTestSet.allErrors: Map<String, Int> get() = errors + codeGenerationErrors.getOrDefault(this, mapOf()) } -typealias MethodGeneratedTests = MutableMap<UtMethod<*>, MutableSet<CgTestMethod>> +typealias MethodGeneratedTests = MutableMap<ExecutableId, MutableSet<CgTestMethod>> typealias ErrorsCount = Map<String, Int> data class TestsGenerationReport( - val executables: MutableSet<UtMethod<*>> = mutableSetOf(), + val executables: MutableSet<ExecutableId> = mutableSetOf(), var successfulExecutions: MethodGeneratedTests = mutableMapOf(), var timeoutExecutions: MethodGeneratedTests = mutableMapOf(), var failedExecutions: MethodGeneratedTests = mutableMapOf(), var crashExecutions: MethodGeneratedTests = mutableMapOf(), - var errors: MutableMap<UtMethod<*>, ErrorsCount> = mutableMapOf() + var errors: MutableMap<ExecutableId, ErrorsCount> = mutableMapOf() ) { val classUnderTest: KClass<*> - get() = executables.firstOrNull()?.clazz + get() = executables.firstOrNull()?.classId?.kClass ?: error("No executables found in test report") val initialWarnings: MutableList<() -> String> = mutableListOf() @@ -215,12 +217,12 @@ data class TestsGenerationReport( appendHtmlLine("Not generated because of internal errors test methods: $overallErrors") } - fun addMethodErrors(testSet: UtMethodTestSet, errors: Map<String, Int>) { - this.errors[testSet.method] = errors + fun addMethodErrors(testSet: CgMethodTestSet, errors: Map<String, Int>) { + this.errors[testSet.executableId] = errors } - fun addTestsByType(testSet: UtMethodTestSet, testMethods: List<CgTestMethod>) { - with(testSet.method) { + fun addTestsByType(testSet: CgMethodTestSet, testMethods: List<CgTestMethod>) { + with(testSet.executableId) { executables += this testMethods.forEach { @@ -257,19 +259,19 @@ data class TestsGenerationReport( override fun toString(): String = toString(false) - private fun UtMethod<*>.countTestMethods(): TestMethodStatistic = TestMethodStatistic( + private fun ExecutableId.countTestMethods(): TestMethodStatistic = TestMethodStatistic( testMethodsNumber(successfulExecutions), testMethodsNumber(failedExecutions), testMethodsNumber(timeoutExecutions), testMethodsNumber(crashExecutions) ) - private fun UtMethod<*>.countErrors(): Int = errors.getOrDefault(this, emptyMap()).values.sum() + private fun ExecutableId.countErrors(): Int = errors.getOrDefault(this, emptyMap()).values.sum() - private fun UtMethod<*>.testMethodsNumber(executables: MethodGeneratedTests): Int = + private fun ExecutableId.testMethodsNumber(executables: MethodGeneratedTests): Int = executables.getOrDefault(this, emptySet()).size - private fun UtMethod<*>.updateExecutions(it: CgTestMethod, executions: MethodGeneratedTests) { + private fun ExecutableId.updateExecutions(it: CgTestMethod, executions: MethodGeneratedTests) { executions.getOrPut(this) { mutableSetOf() } += it } 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 5c587a48ee..0e1896b304 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 @@ -20,6 +20,7 @@ import org.utbot.framework.plugin.api.UtInstrumentation import org.utbot.framework.plugin.api.UtMethod 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.UtTimeoutException import org.utbot.framework.plugin.api.util.UtContext @@ -37,6 +38,7 @@ import org.utbot.instrumentation.instrumentation.et.ExplicitThrowInstruction import org.utbot.instrumentation.instrumentation.et.TraceHandler import org.utbot.instrumentation.instrumentation.instrumenter.Instrumenter import org.utbot.instrumentation.instrumentation.mock.MockClassVisitor +import java.security.AccessControlException import java.security.ProtectionDomain import java.util.IdentityHashMap import kotlin.reflect.jvm.javaMethod @@ -212,6 +214,9 @@ object UtExecutionInstrumentation : Instrumentation<UtConcreteExecutionResult> { if (exception is TimeoutException) { return UtTimeoutException(exception) } + if (exception is AccessControlException) { + return UtSandboxFailure(exception) + } val instrs = traceHandler.computeInstructionList() val isNested = if (instrs.isEmpty()) { false diff --git a/utbot-framework/src/test/kotlin/org/utbot/framework/codegen/TestCodeGeneratorPipeline.kt b/utbot-framework/src/test/kotlin/org/utbot/framework/codegen/TestCodeGeneratorPipeline.kt index 95af9e2a8d..213da4803d 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/framework/codegen/TestCodeGeneratorPipeline.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/framework/codegen/TestCodeGeneratorPipeline.kt @@ -7,13 +7,12 @@ import org.utbot.common.bracket import org.utbot.common.info import org.utbot.common.packageName import org.utbot.examples.TestFrameworkConfiguration -import org.utbot.examples.conflictTriggers import org.utbot.framework.codegen.ExecutionStatus.SUCCESS import org.utbot.framework.codegen.model.CodeGenerator import org.utbot.framework.plugin.api.CodegenLanguage +import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.MockFramework import org.utbot.framework.plugin.api.MockStrategyApi -import org.utbot.framework.plugin.api.UtMethod import org.utbot.framework.plugin.api.UtMethodTestSet import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.description @@ -217,12 +216,12 @@ class TestCodeGeneratorPipeline(private val testFrameworkConfiguration: TestFram testSets: List<UtMethodTestSet>, classUnderTest: KClass<*> ): String { - val params = mutableMapOf<UtMethod<*>, List<String>>() + val params = mutableMapOf<ExecutableId, List<String>>() val codeGenerator = with(testFrameworkConfiguration) { CodeGenerator( classUnderTest.java, - params = params, + paramNames = params, testFramework = testFramework, staticsMocking = staticsMocking, forceStaticMocking = forceStaticMocking, diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt index 77e1d9a674..5dc089e085 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt @@ -11,6 +11,7 @@ import java.io.File import java.io.OutputStream import java.io.PrintStream import java.net.URLClassLoader +import java.security.AllPermission import java.time.LocalDateTime import java.time.format.DateTimeFormatter import kotlin.system.exitProcess @@ -51,6 +52,12 @@ private val kryoHelper: KryoHelper = KryoHelper(System.`in`, System.`out`) * It should be compiled into separate jar file (child_process.jar) and be run with an agent (agent.jar) option. */ fun main() { + permissions { + // Enable all permissions for instrumentation. + // SecurityKt.sandbox() is used to restrict these permissions. + + AllPermission() + } + // We don't want user code to litter the standard output, so we redirect it. val tmpStream = PrintStream(object : OutputStream() { override fun write(b: Int) {} @@ -127,7 +134,7 @@ private fun loop(instrumentation: Instrumentation<*>) { } System.err.println("warmup finished in $time ms") } - is Protocol.InvokeMethodCommand -> { + is Protocol.InvokeMethodCommand -> sandbox { val resultCmd = try { val clazz = HandlerClassesLoader.loadClass(cmd.className) val res = instrumentation.invoke( diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/Security.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/Security.kt new file mode 100644 index 0000000000..fa1381459d --- /dev/null +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/Security.kt @@ -0,0 +1,151 @@ +package org.utbot.instrumentation.process + +import sun.security.provider.PolicyFile +import java.lang.reflect.ReflectPermission +import java.net.URI +import java.nio.file.Files +import java.nio.file.Paths +import java.security.AccessControlContext +import java.security.AccessController +import java.security.CodeSource +import java.security.Permission +import java.security.PermissionCollection +import java.security.Permissions +import java.security.Policy +import java.security.PrivilegedAction +import java.security.ProtectionDomain +import java.security.cert.Certificate +import java.util.PropertyPermission + +internal fun permissions(block: SimplePolicy.() -> Unit) { + val policy = Policy.getPolicy() + if (policy !is SimplePolicy) { + Policy.setPolicy(SimplePolicy(block)) + System.setSecurityManager(SecurityManager()) + } else { + policy.block() + } +} + +/** + * Run [block] in sandbox mode. + * + * When running in sandbox by default only necessary to instrumentation permissions are enabled. + * Other options are not enabled by default and rises [java.security.AccessControlException]. + * + * To add new permissions create and/or edit file "{user.home}/.utbot/sandbox.policy". + * + * For example to enable property reading (`System.getProperty("user.home")`): + * + * ``` + * grant { + * permission java.util.PropertyPermission "user.home", "read"; + * }; + * ``` + * Read more [about policy file and syntax](https://docs.oracle.com/javase/7/docs/technotes/guides/security/PolicyFiles.html#Examples) + */ +internal fun <T> sandbox(block: () -> T): T { + val policyPath = Paths.get(System.getProperty("user.home"), ".utbot", "sandbox.policy") + return sandbox(policyPath.toUri()) { block() } +} + +internal fun <T> sandbox(file: URI, block: () -> T): T { + val path = Paths.get(file) + val perms = mutableListOf<Permission>( + RuntimePermission("accessDeclaredMembers"), + RuntimePermission("getProtectionDomain"), + RuntimePermission("accessClassInPackage.*"), + RuntimePermission("getClassLoader"), + RuntimePermission("reflectionFactoryAccess"), + PropertyPermission("org.mockito.internal.*", "read,write"), + ReflectPermission("*"), + ) + if (Files.exists(path)) { + val policyFile = PolicyFile(file.toURL()) + val collection = policyFile.getPermissions(CodeSource(null, emptyArray<Certificate>())) + perms += collection.elements().toList() + } + return sandbox(perms) { block() } +} + +internal fun <T> sandbox(permission: List<Permission>, block: () -> T): T { + val perms = permission.fold(Permissions()) { acc, p -> acc.add(p); acc } + return sandbox(perms) { block() } +} + +internal fun <T> sandbox(perms: PermissionCollection, block: () -> T): T { + val acc = AccessControlContext(arrayOf(ProtectionDomain(null, perms))) + return AccessController.doPrivileged( + PrivilegedAction { + block() + }, + acc + ) +} + +/** + * This policy can add grant or denial rules for permissions. + * + * To add a grant permission use like this in any place: + * + * ``` + * permissions { + * + java.security.PropertyPolicy("user.home", "read,write") + * } + * ``` + * + * After first call [SecurityManager] is set with this policy + * + * To deny a permission: + * + * ``` + * permissions { + * - java.security.PropertyPolicy("user.home", "read,write") + * } + * ``` + * + * To delete all concrete permissions (if it was added before): + * + * ``` + * permissions { + * ! java.security.PropertyPolicy("user.home", "read,write") + * } + * ``` + * + * The last permission has priority. Enable all property read for "user.*", but forbid to read only "user.home": + * + * ``` + * permissions { + * + java.security.PropertyPolicy("user.*", "read,write") + * - java.security.PropertyPolicy("user.home", "read,write") + * } + * ``` + */ +internal class SimplePolicy(init: SimplePolicy.() -> Unit = {}) : Policy() { + sealed class Access(val permission: Permission) { + class Allow(permission: Permission) : Access(permission) + class Deny(permission: Permission) : Access(permission) + } + private var permissions = mutableListOf<Access>() + + init { apply(init) } + + operator fun Permission.unaryPlus() = permissions.add(Access.Allow(this)) + + operator fun Permission.unaryMinus() = permissions.add(Access.Deny(this)) + + operator fun Permission.not() = permissions.removeAll { it.permission == this } + + override fun getPermissions(codesource: CodeSource) = UNSUPPORTED_EMPTY_COLLECTION!! + override fun getPermissions(domain: ProtectionDomain) = UNSUPPORTED_EMPTY_COLLECTION!! + override fun implies(domain: ProtectionDomain, permission: Permission): Boolean { + // 0 means no info, < 0 is denied and > 0 is allowed + val result = permissions.lastOrNull { it.permission.implies(permission) }?.let { + when (it) { + is Access.Allow -> 1 + is Access.Deny -> -1 + } + } ?: 0 + return result > 0 + } +} \ No newline at end of file diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt index af33856772..e53d7fb37f 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt @@ -55,9 +55,10 @@ import org.utbot.framework.codegen.model.CodeGenerator import org.utbot.framework.codegen.model.TestsCodeWithTestReport import org.utbot.framework.codegen.model.constructor.tree.TestsGenerationReport import org.utbot.framework.plugin.api.CodegenLanguage -import org.utbot.framework.plugin.api.UtMethod +import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.UtMethodTestSet import org.utbot.framework.plugin.api.util.UtContext +import org.utbot.framework.plugin.api.util.executableId import org.utbot.framework.plugin.api.util.withUtContext import org.utbot.framework.util.Conflict import org.utbot.intellij.plugin.generator.CodeGenerationController.Target.* @@ -100,7 +101,7 @@ object CodeGenerationController { val file = testClass.containingFile runWriteCommandAction(model.project, "Generate tests with UtBot", null, { try { - generateCodeAndReport(testClass, file, testSets, model, latch, reports) + generateCodeAndReport(srcClass, testClass, file, testSets, model, latch, reports) } catch (e: IncorrectOperationException) { showCreatingClassError(model.project, createTestClassName(srcClass)) } @@ -250,6 +251,7 @@ object CodeGenerationController { } private fun generateCodeAndReport( + srcClass: PsiClass, testClass: PsiClass, file: PsiFile, testSets: List<UtMethodTestSet>, @@ -257,24 +259,19 @@ object CodeGenerationController { reportsCountDown: CountDownLatch, reports: MutableList<TestsGenerationReport>, ) { - val selectedMethods = TestIntegrationUtils.extractClassMethods(testClass, false) - val testFramework = model.testFramework - val mockito = model.mockFramework - val staticsMocking = model.staticsMocking - val classUnderTest = testSets.first().method.clazz - - val params = DumbService.getInstance(model.project) - .runReadActionInSmartMode(Computable { findMethodParams(classUnderTest, selectedMethods) }) + val classMethods = TestIntegrationUtils.extractClassMethods(srcClass, false) + val paramNames = DumbService.getInstance(model.project) + .runReadActionInSmartMode(Computable { findMethodParamNames(classUnderTest, classMethods) }) val codeGenerator = CodeGenerator( classUnderTest = classUnderTest.java, - params = params.toMutableMap(), - testFramework = testFramework, - mockFramework = mockito, + paramNames = paramNames.toMutableMap(), + testFramework = model.testFramework, + mockFramework = model.mockFramework, codegenLanguage = model.codegenLanguage, parameterizedTestSource = model.parametrizedTestSource, - staticsMocking = staticsMocking, + staticsMocking = model.staticsMocking, forceStaticMocking = model.forceStaticMocking, generateWarningsForStaticMocking = model.generateWarningsForStaticMocking, runtimeExceptionTestsBehaviour = model.runtimeExceptionTestsBehaviour, @@ -347,13 +344,11 @@ object CodeGenerationController { } } - private fun findMethodParams(clazz: KClass<*>, methods: List<MemberInfo>): Map<UtMethod<*>, List<String>> { + private fun findMethodParamNames(clazz: KClass<*>, methods: List<MemberInfo>): Map<ExecutableId, List<String>> { val bySignature = methods.associate { it.signature() to it.paramNames() } - return clazz.functions.mapNotNull { method -> - bySignature[method.signature()]?.let { params -> - UtMethod(method, clazz) to params - } - }.toMap() + return clazz.functions + .mapNotNull { method -> bySignature[method.signature()]?.let { params -> method.executableId to params } } + .toMap() } private fun MemberInfo.paramNames(): List<String> = 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 b757c70ecf..360cea7208 100644 --- a/utbot-summary/src/main/kotlin/org/utbot/summary/TagGenerator.kt +++ b/utbot-summary/src/main/kotlin/org/utbot/summary/TagGenerator.kt @@ -9,6 +9,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.UtMethodTestSet +import org.utbot.framework.plugin.api.UtSandboxFailure 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 @@ -140,7 +141,8 @@ enum class ClusterKind { EXPLICITLY_THROWN_UNCHECKED_EXCEPTIONS, OVERFLOWS, TIMEOUTS, - CRASH_SUITE; + CRASH_SUITE, + SECURITY; val displayName: String get() = toString().replace('_', ' ') } @@ -152,6 +154,7 @@ private fun UtExecutionResult.clusterKind() = when (this) { is UtOverflowFailure -> ClusterKind.OVERFLOWS is UtTimeoutException -> ClusterKind.TIMEOUTS is UtConcreteExecutionFailure -> ClusterKind.CRASH_SUITE + is UtSandboxFailure -> ClusterKind.SECURITY } /**