diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt index fca06f2837..0d16cd710e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt @@ -418,7 +418,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte return { +actual[streamConsumingMethodId]() } } - protected fun shouldTestPassWithException(execution: UtExecution, exception: Throwable): Boolean { + protected open 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 InstrumentedProcessDeathException) return false diff --git a/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonDialogProcessor.kt b/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonDialogProcessor.kt index cd3f348184..f8aa3ffb6d 100644 --- a/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonDialogProcessor.kt +++ b/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonDialogProcessor.kt @@ -205,6 +205,7 @@ object PythonDialogProcessor { val message = it.fold(StringBuilder()) { acc, line -> acc.appendHtmlLine(line) } WarningTestsReportNotifier.notify(message.toString()) }, + runtimeExceptionTestsBehaviour = model.runtimeExceptionTestsBehaviour, startedCleaningAction = { indicator.text = "Cleaning up..." } ) } finally { diff --git a/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonDialogWindow.kt b/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonDialogWindow.kt index d8d417880c..7f15e519f7 100644 --- a/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonDialogWindow.kt +++ b/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonDialogWindow.kt @@ -177,6 +177,7 @@ class PythonDialogWindow(val model: PythonTestsModel) : DialogWrapper(model.proj val settings = model.project.service() with(settings) { model.timeoutForRun = hangingTestsTimeout.timeoutMs + model.runtimeExceptionTestsBehaviour = runtimeExceptionTestsBehaviour } super.doOKAction() diff --git a/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonTestsModel.kt b/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonTestsModel.kt index ef655d88b8..1ea4f094f3 100644 --- a/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonTestsModel.kt +++ b/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonTestsModel.kt @@ -5,6 +5,7 @@ import com.intellij.openapi.project.Project import com.jetbrains.python.psi.PyClass import com.jetbrains.python.psi.PyFile import com.jetbrains.python.psi.PyFunction +import org.utbot.framework.codegen.domain.RuntimeExceptionTestsBehaviour import org.utbot.framework.codegen.domain.TestFramework import org.utbot.framework.codegen.services.language.CgLanguageAssistant import org.utbot.intellij.plugin.models.BaseTestsModel @@ -31,4 +32,5 @@ class PythonTestsModel( lateinit var testSourceRootPath: String lateinit var testFramework: TestFramework lateinit var selectedFunctions: Set + lateinit var runtimeExceptionTestsBehaviour: RuntimeExceptionTestsBehaviour } diff --git a/utbot-python/src/main/kotlin/org/utbot/python/PythonEngine.kt b/utbot-python/src/main/kotlin/org/utbot/python/PythonEngine.kt index 7621178348..0bb1086aad 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/PythonEngine.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/PythonEngine.kt @@ -47,7 +47,7 @@ class PythonEngine( // can be improved description.name } - is UtExplicitlyThrownException -> "${description.name}_with_exception" + is UtExecutionFailure -> "${description.name}_with_exception" else -> description.name } val testName = "test_$testSuffix" @@ -81,6 +81,37 @@ class PythonEngine( return Pair(stateThisObject, modelList) } + private fun handleTimeoutResult( + arguments: List, + methodUnderTestDescription: PythonMethodDescription, + ): FuzzingExecutionFeedback { + val summary = arguments + .zip(methodUnderTest.arguments) + .mapNotNull { it.first.summary?.replace("%var%", it.second.name) } + val executionResult = UtTimeoutException(TimeoutException("Execution is too long")) + val testMethodName = suggestExecutionName(methodUnderTestDescription, executionResult) + + val hasThisObject = methodUnderTest.hasThisArgument + val (beforeThisObjectTree, beforeModelListTree) = if (hasThisObject) { + arguments.first() to arguments.drop(1) + } else { + null to arguments + } + val beforeThisObject = beforeThisObjectTree?.let { PythonTreeModel(it.tree) } + val beforeModelList = beforeModelListTree.map { PythonTreeModel(it.tree) } + + val utFuzzedExecution = PythonUtExecution( + stateInit = EnvironmentModels(beforeThisObject, beforeModelList, emptyMap()), + stateBefore = EnvironmentModels(beforeThisObject, beforeModelList, emptyMap()), + stateAfter = EnvironmentModels(beforeThisObject, beforeModelList, emptyMap()), + diffIds = emptyList(), + result = executionResult, + testMethodName = testMethodName.testName?.camelToSnakeCase(), + displayName = testMethodName.displayName, + summary = summary.map { DocRegularStmt(it) } + ) + return ValidExecution(utFuzzedExecution) + } private fun handleSuccessResult( arguments: List, types: List, @@ -110,7 +141,7 @@ class PythonEngine( val executionResult = if (evaluationResult.isException) { - UtExplicitlyThrownException(Throwable(resultModel.type.toString()), false) + UtImplicitlyThrownException(Throwable(resultModel.type.toString()), false) } else { UtExecutionSuccess(PythonTreeModel(resultModel)) @@ -157,8 +188,7 @@ class PythonEngine( serverSocket, pythonPath, until, - { constructEvaluationInput(it) }, - ) + ) { constructEvaluationInput(it) } } catch (_: TimeoutException) { return@flow } @@ -199,8 +229,8 @@ class PythonEngine( } is PythonEvaluationTimeout -> { - val utError = UtError(evaluationResult.message, Throwable()) - PythonExecutionResult(InvalidExecution(utError), PythonFeedback(control = Control.PASS)) + val utTimeoutException = handleTimeoutResult(arguments, description) + PythonExecutionResult(utTimeoutException, PythonFeedback(control = Control.PASS)) } is PythonEvaluationSuccess -> { diff --git a/utbot-python/src/main/kotlin/org/utbot/python/PythonTestCaseGenerator.kt b/utbot-python/src/main/kotlin/org/utbot/python/PythonTestCaseGenerator.kt index c653e1f4cd..6131dcc98c 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/PythonTestCaseGenerator.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/PythonTestCaseGenerator.kt @@ -31,6 +31,7 @@ import java.io.File private val logger = KotlinLogging.logger {} private const val RANDOM_TYPE_FREQUENCY = 6 +private const val MAX_EMPTY_COVERAGE_TESTS = 5 class PythonTestCaseGenerator( private val withMinimization: Boolean = true, @@ -262,12 +263,15 @@ class PythonTestCaseGenerator( } logger.info("Collect all test executions for ${method.name}") - val (successfulExecutions, failedExecutions) = executions.partition { it.result is UtExecutionSuccess } + val (emptyCoverageExecutions, coverageExecutions) = executions.partition { it.coverage == null } + val (successfulExecutions, failedExecutions) = coverageExecutions.partition { it.result is UtExecutionSuccess } return PythonTestSet( method, if (withMinimization) - minimizeExecutions(successfulExecutions) + minimizeExecutions(failedExecutions) + minimizeExecutions(successfulExecutions) + + minimizeExecutions(failedExecutions) + + emptyCoverageExecutions.take(MAX_EMPTY_COVERAGE_TESTS) else executions, errors, diff --git a/utbot-python/src/main/kotlin/org/utbot/python/PythonTestGenerationProcessor.kt b/utbot-python/src/main/kotlin/org/utbot/python/PythonTestGenerationProcessor.kt index 97f1a69100..9ff97b4d9d 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/PythonTestGenerationProcessor.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/PythonTestGenerationProcessor.kt @@ -3,6 +3,7 @@ package org.utbot.python import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory import org.parsers.python.PythonParser +import org.utbot.framework.codegen.domain.RuntimeExceptionTestsBehaviour import org.utbot.python.framework.codegen.model.PythonSysPathImport import org.utbot.python.framework.codegen.model.PythonSystemImport import org.utbot.python.framework.codegen.model.PythonUserImport @@ -50,6 +51,7 @@ object PythonTestGenerationProcessor { pythonRunRoot: Path, doNotCheckRequirements: Boolean = false, withMinimization: Boolean = true, + runtimeExceptionTestsBehaviour: RuntimeExceptionTestsBehaviour = RuntimeExceptionTestsBehaviour.FAIL, isCanceled: () -> Boolean = { false }, checkingRequirementsAction: () -> Unit = {}, installingRequirementsAction: () -> Unit = {}, @@ -202,6 +204,7 @@ object PythonTestGenerationProcessor { paramNames = paramNames, testFramework = testFramework, testClassPackageName = "", + runtimeExceptionTestsBehaviour = runtimeExceptionTestsBehaviour, ) val testCode = codegen.pythonGenerateAsStringWithTestReport( notEmptyTests.map { testSet -> diff --git a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/UtExecutorThread.kt b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/UtExecutorThread.kt index d749c1883d..6dd49aff3b 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/UtExecutorThread.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/UtExecutorThread.kt @@ -1,8 +1,14 @@ package org.utbot.python.evaluation +import java.net.SocketException + class UtExecutorThread : Thread() { override fun run() { - response = pythonWorker?.receiveMessage() + response = try { + pythonWorker?.receiveMessage() + } catch (ex: SocketException) { + null + } } enum class Status { diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/PythonApi.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/PythonApi.kt index 474f3d18e1..272bf85965 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/PythonApi.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/PythonApi.kt @@ -34,6 +34,10 @@ class PythonClassId( override val simpleName: String = typeName override val canonicalName = name override val packageName = moduleName + val prettyName: String = if (rootModuleName == pythonBuiltinsModuleName) + name.split(".", limit=2).last() + else + name } open class RawPythonAnnotation( diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/util/PythonIdUtils.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/util/PythonIdUtils.kt index 7e84734e8d..5564841403 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/util/PythonIdUtils.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/util/PythonIdUtils.kt @@ -19,3 +19,4 @@ val pythonDictClassId = PythonClassId("builtins.dict") val pythonSetClassId = PythonClassId("builtins.set") val pythonBytearrayClassId = PythonClassId("builtins.bytearray") val pythonBytesClassId = PythonClassId("builtins.bytes") +val pythonExceptionClassId = PythonClassId("builtins.Exception") diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonDomain.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonDomain.kt index 867ff9c058..96f9290db4 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonDomain.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonDomain.kt @@ -41,6 +41,8 @@ object Pytest : TestFramework(displayName = "pytest", id = "pytest") { override val nestedClassesShouldBeStatic: Boolean = false override val argListClassId: ClassId = pythonAnyClassId + val skipDecoratorClassId = PythonClassId("pytest", "mark.skip") + @OptIn(ExperimentalStdlibApi::class) override fun getRunTestsCommand( executionInvoke: String, @@ -89,6 +91,8 @@ object Unittest : TestFramework(displayName = "Unittest", id = "Unittest") { override val nestedClassesShouldBeStatic: Boolean = false override val argListClassId: ClassId = pythonAnyClassId + val skipDecoratorClassId = PythonClassId("unittest.skip") + override fun getRunTestsCommand( executionInvoke: String, classPath: String, diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt index 4bc6cde1bc..e2b94f4b08 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt @@ -1,16 +1,24 @@ package org.utbot.python.framework.codegen.model.constructor.tree +import org.utbot.framework.codegen.domain.RuntimeExceptionTestsBehaviour import org.utbot.framework.codegen.domain.context.CgContext +import org.utbot.framework.codegen.domain.models.CgDocumentationComment import org.utbot.framework.codegen.domain.models.CgFieldAccess import org.utbot.framework.codegen.domain.models.CgGetLength import org.utbot.framework.codegen.domain.models.CgLiteral +import org.utbot.framework.codegen.domain.models.CgMultilineComment +import org.utbot.framework.codegen.domain.models.CgParameterDeclaration import org.utbot.framework.codegen.domain.models.CgReferenceExpression import org.utbot.framework.codegen.domain.models.CgTestMethod +import org.utbot.framework.codegen.domain.models.CgTestMethodType import org.utbot.framework.codegen.domain.models.CgValue import org.utbot.framework.codegen.domain.models.CgVariable +import org.utbot.framework.codegen.domain.models.convertDocToCg import org.utbot.framework.codegen.tree.CgMethodConstructor +import org.utbot.framework.codegen.tree.buildTestMethod import org.utbot.framework.plugin.api.* import org.utbot.python.framework.api.python.* +import org.utbot.python.framework.api.python.util.pythonExceptionClassId import org.utbot.python.framework.api.python.util.pythonIntClassId import org.utbot.python.framework.api.python.util.pythonNoneClassId import org.utbot.python.framework.codegen.PythonCgLanguageAssistant @@ -23,20 +31,6 @@ class PythonCgMethodConstructor(context: CgContext) : CgMethodConstructor(contex pythonDeepEquals(expected, actual) } - private fun generatePythonTestComments(execution: UtExecution) { - when (execution.result) { - is UtExplicitlyThrownException -> - (execution.result as UtExplicitlyThrownException).exception.message?.let { - emptyLineIfNeeded() - comment("raises $it") - } - - else -> { - // nothing - } - } - } - override fun createTestMethod(executableId: ExecutableId, execution: UtExecution): CgTestMethod = withTestMethodScope(execution) { val constructorState = (execution as PythonUtExecution).stateInit @@ -48,7 +42,7 @@ class PythonCgMethodConstructor(context: CgContext) : CgMethodConstructor(contex } // TODO: remove this line when SAT-1273 is completed execution.displayName = execution.displayName?.let { "${executableId.name}: $it" } - testMethod(testMethodName, execution.displayName) { + pythonTestMethod(testMethodName, execution.displayName) { val statics = currentExecution!!.stateBefore.statics rememberInitialStaticFields(statics) @@ -115,8 +109,6 @@ class PythonCgMethodConstructor(context: CgContext) : CgMethodConstructor(contex generateResultAssertions() generateFieldStateAssertions(stateAssertions, assertThisObject, executableId) - - generatePythonTestComments(execution) } if (statics.isNotEmpty()) { @@ -131,6 +123,44 @@ class PythonCgMethodConstructor(context: CgContext) : CgMethodConstructor(contex } } + override fun generateResultAssertions() { + if (currentExecutable is MethodId) { + val currentExecution = currentExecution!! + val executionResult = currentExecution.result + if (executionResult is UtExecutionFailure) { + val exceptionId = executionResult.rootCauseException.message?.let {PythonClassId(it)} ?: pythonExceptionClassId + val executionBlock = { + with(currentExecutable) { + when (this) { + is MethodId -> thisInstance[this](*methodArguments.toTypedArray()).intercepted() + else -> {} + } + } + } + when (methodType) { + CgTestMethodType.PASSED_EXCEPTION -> { + testFrameworkManager.expectException(exceptionId) { + executionBlock() + } + return + } + CgTestMethodType.FAILING -> { + val executable = currentExecutable!! as PythonMethodId + val executableName = "${executable.moduleName}.${executable.name}" + val warningLine = + "This test fails because function [$executableName] produces [${exceptionId.prettyName}]" + +CgMultilineComment(warningLine) + emptyLineIfNeeded() + executionBlock() + return + } + else -> {} + } + } + } + super.generateResultAssertions() + } + private fun generateFieldStateAssertions( stateAssertions: MutableMap>, assertThisObject: MutableList>, @@ -154,6 +184,45 @@ class PythonCgMethodConstructor(context: CgContext) : CgMethodConstructor(contex } } + override fun shouldTestPassWithException(execution: UtExecution, exception: Throwable): Boolean { + if (exception is TimeoutException || exception is InstrumentedProcessDeathException) return false + return runtimeExceptionTestsBehaviour == RuntimeExceptionTestsBehaviour.PASS + } + + private fun pythonTestMethod( + methodName: String, + displayName: String?, + params: List = emptyList(), + body: () -> Unit, + ): CgTestMethod { + displayName?.let { + testFrameworkManager.addTestDescription(displayName) + } + + val result = currentExecution!!.result + if (result is UtTimeoutException) { + testFrameworkManager.disableTestMethod( + "Disabled due to the fact that the execution is longer then ${hangingTestsTimeout.timeoutMs} ms" + ) + } + + val testMethod = buildTestMethod { + name = methodName + parameters = params + statements = block(body) + // Exceptions and annotations assignment must run after the statements block is build, + // because we collect info about exceptions and required annotations while building the statements + exceptions += collectedExceptions + annotations += collectedMethodAnnotations + methodType = this@PythonCgMethodConstructor.methodType + + val docComment = currentExecution?.summary?.map { convertDocToCg(it) }?.toMutableList() ?: mutableListOf() + documentation = CgDocumentationComment(docComment) + } + testMethods += testMethod + return testMethod + } + private fun pythonDeepEquals(expected: CgValue, actual: CgVariable) { require(expected is CgPythonTree) { "Expected value have to be CgPythonTree but `${expected::class}` found" diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonTestFrameworkManager.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonTestFrameworkManager.kt index 2ad3a2b881..49f7b96e29 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonTestFrameworkManager.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonTestFrameworkManager.kt @@ -10,21 +10,32 @@ import org.utbot.framework.codegen.domain.models.CgNamedAnnotationArgument import org.utbot.framework.codegen.domain.models.CgValue import org.utbot.framework.codegen.domain.models.CgVariable import org.utbot.framework.codegen.services.framework.TestFrameworkManager -import org.utbot.framework.codegen.util.resolve -import org.utbot.framework.plugin.api.BuiltinClassId import org.utbot.framework.plugin.api.ClassId +import org.utbot.python.framework.api.python.PythonClassId import org.utbot.python.framework.api.python.util.pythonAnyClassId import org.utbot.python.framework.api.python.util.pythonBoolClassId +import org.utbot.python.framework.api.python.util.pythonNoneClassId +import org.utbot.python.framework.api.python.util.pythonStrClassId import org.utbot.python.framework.codegen.model.Pytest import org.utbot.python.framework.codegen.model.Unittest +import org.utbot.python.framework.codegen.model.constructor.util.importIfNeeded import org.utbot.python.framework.codegen.model.tree.CgPythonAssertEquals import org.utbot.python.framework.codegen.model.tree.CgPythonFunctionCall +import org.utbot.python.framework.codegen.model.tree.CgPythonRepr import org.utbot.python.framework.codegen.model.tree.CgPythonTuple +import org.utbot.python.framework.codegen.model.tree.CgPythonWith internal class PytestManager(context: CgContext) : TestFrameworkManager(context) { override fun expectException(exception: ClassId, block: () -> Unit) { require(testFramework is Pytest) { "According to settings, Pytest was expected, but got: $testFramework" } - block() + require(exception is PythonClassId) { "Exceptions must be PythonClassId" } + context.importIfNeeded(PythonClassId("pytest.raises")) + val withExpression = CgPythonFunctionCall( + pythonNoneClassId, + "pytest.raises", + listOf(CgLiteral(exception, exception.prettyName)) + ) + +CgPythonWith(withExpression, null, context.block(block)) } override val isExpectedExceptionExecutionBreaking: Boolean = true @@ -48,6 +59,18 @@ internal class PytestManager(context: CgContext) : TestFrameworkManager(context) override fun addTestDescription(description: String) = Unit override fun disableTestMethod(reason: String) { + require(testFramework is Pytest) { "According to settings, Pytest was expected, but got: $testFramework" } + + context.importIfNeeded(testFramework.skipDecoratorClassId) + collectedMethodAnnotations += CgMultipleArgsAnnotation( + testFramework.skipDecoratorClassId, + mutableListOf( + CgNamedAnnotationArgument( + name = "reason", + value = CgPythonRepr(pythonStrClassId, "'${reason.replace("\"", "'")}'") + ) + ) + ) } override val dataProviderMethodsHolder: TestClassContext get() = TODO() @@ -87,7 +110,13 @@ internal class UnittestManager(context: CgContext) : TestFrameworkManager(contex override fun expectException(exception: ClassId, block: () -> Unit) { require(testFramework is Unittest) { "According to settings, Unittest was expected, but got: $testFramework" } - block() + require(exception is PythonClassId) { "Exceptions must be PythonClassId" } + val withExpression = CgPythonFunctionCall( + pythonNoneClassId, + "self.assertRaises", + listOf(CgLiteral(exception, exception.prettyName)) + ) + +CgPythonWith(withExpression, null, context.block(block)) } override fun createDataProviderAnnotations(dataProviderMethodName: String): MutableList { @@ -112,21 +141,16 @@ internal class UnittestManager(context: CgContext) : TestFrameworkManager(contex require(testFramework is Unittest) { "According to settings, Unittest was expected, but got: $testFramework" } collectedMethodAnnotations += CgMultipleArgsAnnotation( - skipAnnotationClassId, + testFramework.skipDecoratorClassId, mutableListOf( CgNamedAnnotationArgument( - name = "value", - value = reason.resolve() + name = "reason", + value = CgPythonRepr(pythonStrClassId, "'${reason.replace("\"", "'")}'") ) ) ) } - private val skipAnnotationClassId = BuiltinClassId( - canonicalName = "unittest.skip", - simpleName = "skip" - ) - fun assertIsinstance(types: List, actual: CgVariable) { +assertions[assertTrue]( CgPythonFunctionCall( diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/visitor/CgPythonRenderer.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/visitor/CgPythonRenderer.kt index 01ed426d20..3b1b24327d 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/visitor/CgPythonRenderer.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/visitor/CgPythonRenderer.kt @@ -40,6 +40,7 @@ import org.utbot.framework.codegen.domain.models.CgMethod import org.utbot.framework.codegen.domain.models.CgMethodCall import org.utbot.framework.codegen.domain.models.CgMultilineComment import org.utbot.framework.codegen.domain.models.CgMultipleArgsAnnotation +import org.utbot.framework.codegen.domain.models.CgNamedAnnotationArgument import org.utbot.framework.codegen.domain.models.CgNotNullAssertion import org.utbot.framework.codegen.domain.models.CgParameterDeclaration import org.utbot.framework.codegen.domain.models.CgParameterizedTestDataProviderMethod @@ -118,11 +119,26 @@ internal class CgPythonRenderer( } override fun visit(element: CgSingleArgAnnotation) { - print("") + print("@${element.classId.asString()}") + print("(") + element.argument.accept(this) + println(")") } override fun visit(element: CgMultipleArgsAnnotation) { - print("") + print("@${element.classId.asString()}") + if (element.arguments.isNotEmpty()) { + print("(") + element.arguments.renderSeparated() + print(")") + } + println() + } + + override fun visit(element: CgNamedAnnotationArgument) { + print(element.name) + print("=") + element.value.accept(this) } override fun visit(element: CgSingleLineComment) { @@ -542,6 +558,17 @@ internal class CgPythonRenderer( element.value.accept(this) } + override fun visit(element: CgPythonWith) { + print("with ") + element.expression.accept(this) + if (element.target != null) { + print(" as ") + element.target.accept(this) + } + println(":") + withIndent { element.statements.forEach { it.accept(this) } } + } + override fun visit(element: CgPythonDict) { print("{") element.elements.map { (key, value) -> diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/visitor/CgPythonVisitor.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/visitor/CgPythonVisitor.kt index 0829776a7a..904012191a 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/visitor/CgPythonVisitor.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/visitor/CgPythonVisitor.kt @@ -15,5 +15,5 @@ interface CgPythonVisitor : CgVisitor { fun visit(element: CgPythonList): R fun visit(element: CgPythonSet): R fun visit(element: CgPythonTree): R - + fun visit(element: CgPythonWith): R } \ No newline at end of file diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/tree/CgPythonElement.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/tree/CgPythonElement.kt index 2f6b62df19..8617f88f12 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/tree/CgPythonElement.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/tree/CgPythonElement.kt @@ -27,6 +27,7 @@ interface CgPythonElement : CgElement { is CgPythonDict -> visitor.visit(element) is CgPythonTuple -> visitor.visit(element) is CgPythonTree -> visitor.visit(element) + is CgPythonWith -> visitor.visit(element) else -> throw IllegalArgumentException("Can not visit element of type ${element::class}") } } else { @@ -107,4 +108,10 @@ class CgPythonDict( val elements: Map ) : CgValue, CgPythonElement { override val type: PythonClassId = pythonDictClassId -} \ No newline at end of file +} + +data class CgPythonWith( + val expression: CgExpression, + val target: CgExpression?, + val statements: List, +) : CgStatement, CgPythonElement diff --git a/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/ReduceValueProvider.kt b/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/ReduceValueProvider.kt index 4025dad6a1..f4f2274e66 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/ReduceValueProvider.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/ReduceValueProvider.kt @@ -72,7 +72,7 @@ object ReduceValueProvider : ValueProvider Routine.Call(listOf(field.type)) { instance, arguments -> val obj = instance.tree as PythonTree.ReduceNode - obj.state[field.meta.name.toPythonRepr()] = arguments.first().tree + obj.state[field.meta.name] = arguments.first().tree } }) yieldAll(callConstructors(type, it, modifications.asSequence()))