From 3fd3bcb100c1412d9fb3e51cc9ebf5cd7fd530a8 Mon Sep 17 00:00:00 2001 From: Vyacheslav Tamarin Date: Thu, 30 Mar 2023 10:43:24 +0300 Subject: [PATCH 1/2] Replace copyreg._reconstructor by real constructor or __new__ method: * group kind and module * add field for kwargs * improve rendering * add comparator for recursive objects --- .../python/PythonTestGenerationProcessor.kt | 5 +- .../evaluation/PythonCodeExecutorImpl.kt | 2 +- .../evaluation/PythonCodeSocketExecutor.kt | 3 +- .../ExecutionRequestSerializer.kt | 1 + .../ExecutionResultDeserializer.kt | 2 +- .../serialiation/PythonObjectParser.kt | 454 ++++++------------ .../python/framework/api/python/PythonTree.kt | 7 + .../api/python/util/PythonTreeComparator.kt | 3 +- .../tree/PythonCgMethodConstructor.kt | 5 +- .../tree/PythonCgVariableConstructor.kt | 24 +- .../constructor/util/ConstructorUtils.kt | 13 + .../fields/PythonExecutionStateAnalyzer.kt | 149 ------ .../python/utils/AnnotationNormalizer.kt | 92 ---- .../utbot/python/utils/RequirementsUtils.kt | 2 +- 14 files changed, 188 insertions(+), 574 deletions(-) delete mode 100644 utbot-python/src/main/kotlin/org/utbot/python/framework/fields/PythonExecutionStateAnalyzer.kt delete mode 100644 utbot-python/src/main/kotlin/org/utbot/python/utils/AnnotationNormalizer.kt 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 f904e49c30..97f1a69100 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/PythonTestGenerationProcessor.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/PythonTestGenerationProcessor.kt @@ -190,7 +190,10 @@ object PythonTestGenerationProcessor { testFrameworkModule, sysImport ) - ).filterNotNull().toSet() + ) + .filterNotNull() +// .filterNot { it.importName == pythonBuiltinsModuleName } + .toSet() val context = UtContext(this::class.java.classLoader) withUtContext(context) { diff --git a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCodeExecutorImpl.kt b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCodeExecutorImpl.kt index 1137bb1b15..c30f62dfb7 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCodeExecutorImpl.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCodeExecutorImpl.kt @@ -130,7 +130,7 @@ class PythonCodeExecutorImpl( calculateCoverage(executionResult.statements, executionResult.missedStatements), stateBefore, stateAfter, - executionResult.argsIds + executionResult.kwargsIds, + executionResult.argsIds + executionResult.kwargsIds.values, executionResult.resultId, ) } diff --git a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCodeSocketExecutor.kt b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCodeSocketExecutor.kt index 585d6052f7..e484ce8479 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCodeSocketExecutor.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCodeSocketExecutor.kt @@ -66,6 +66,7 @@ class PythonCodeSocketExecutor( additionalModulesToImport.toList(), syspathDirectories.toList(), arguments, + emptyMap(), // here can be only-kwargs arguments memory, method.moduleFilename, ) @@ -109,7 +110,7 @@ class PythonCodeSocketExecutor( calculateCoverage(executionResult.statements, executionResult.missedStatements), stateBefore, stateAfter, - executionResult.argsIds + executionResult.kwargsIds, + executionResult.argsIds + executionResult.kwargsIds.values, executionResult.resultId, ) } diff --git a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/ExecutionRequestSerializer.kt b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/ExecutionRequestSerializer.kt index 33d2251107..aa81e60087 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/ExecutionRequestSerializer.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/ExecutionRequestSerializer.kt @@ -21,6 +21,7 @@ data class ExecutionRequest( val imports: List, val syspaths: List, val argumentsIds: List, + val kwargumentsIds: Map, val serializedMemory: String, val filepath: String, ) diff --git a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/ExecutionResultDeserializer.kt b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/ExecutionResultDeserializer.kt index 3ffdbe7bce..875de20c5e 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/ExecutionResultDeserializer.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/ExecutionResultDeserializer.kt @@ -48,7 +48,7 @@ data class SuccessExecution( val stateBefore: String, val stateAfter: String, val argsIds: List, - val kwargsIds: List, + val kwargsIds: Map, val resultId: String, ): PythonExecutionResult() diff --git a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/PythonObjectParser.kt b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/PythonObjectParser.kt index b4edda2a64..f08a667cc3 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/PythonObjectParser.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/PythonObjectParser.kt @@ -42,96 +42,140 @@ class MemoryDump( } } +class TypeInfo( + val module: String, + val kind: String, +) { + val qualname: String = if (module.isEmpty()) kind else "$module.$kind" +} + sealed class MemoryObject( val id: String, - val kind: String, - val module: String, + val typeinfo: TypeInfo, val comparable: Boolean, ) { - val qualname: String = if (module.isEmpty()) kind else "$module.$kind" + val qualname: String = typeinfo.qualname } class ReprMemoryObject( id: String, - kind: String, - module: String, + typeinfo: TypeInfo, comparable: Boolean, val value: String, -): MemoryObject(id, kind, module, comparable) +) : MemoryObject(id, typeinfo, comparable) class ListMemoryObject( id: String, - kind: String, - module: String, + typeinfo: TypeInfo, comparable: Boolean, val items: List, -): MemoryObject(id, kind, module, comparable) +) : MemoryObject(id, typeinfo, comparable) class DictMemoryObject( id: String, - kind: String, - module: String, + typeinfo: TypeInfo, comparable: Boolean, val items: Map, -): MemoryObject(id, kind, module, comparable) +) : MemoryObject(id, typeinfo, comparable) class ReduceMemoryObject( id: String, - kind: String, - module: String, + typeinfo: TypeInfo, comparable: Boolean, - val constructor: String, + val constructor: TypeInfo, val args: String, val state: String, val listitems: String, val dictitems: String -): MemoryObject(id, kind, module, comparable) +) : MemoryObject(id, typeinfo, comparable) fun PythonTree.PythonTreeNode.toMemoryObject(memoryDump: MemoryDump): String { - val obj = when(this) { + val obj = when (this) { is PythonTree.PrimitiveNode -> { - ReprMemoryObject(this.id.toString(), this.type.name, this.type.moduleName, this.comparable, this.repr) + ReprMemoryObject( + this.id.toString(), + TypeInfo(this.type.moduleName, this.type.typeName), + this.comparable, + this.repr + ) } + is PythonTree.ListNode -> { val items = this.items.entries .sortedBy { it.key } .map { it.value.toMemoryObject(memoryDump) } - ListMemoryObject(this.id.toString(), this.type.name, this.type.moduleName, this.comparable, items) + ListMemoryObject( + this.id.toString(), + TypeInfo(this.type.moduleName, this.type.typeName), + this.comparable, + items + ) } + is PythonTree.TupleNode -> { val items = this.items.entries .sortedBy { it.key } .map { it.value.toMemoryObject(memoryDump) } - ListMemoryObject(this.id.toString(), this.type.name, this.type.moduleName, this.comparable, items) + ListMemoryObject( + this.id.toString(), + TypeInfo(this.type.moduleName, this.type.typeName), + this.comparable, + items + ) } + is PythonTree.SetNode -> { val items = this.items.map { it.toMemoryObject(memoryDump) } - ListMemoryObject(this.id.toString(), this.type.name, this.type.moduleName, this.comparable, items) + ListMemoryObject( + this.id.toString(), + TypeInfo(this.type.moduleName, this.type.typeName), + this.comparable, + items + ) } + is PythonTree.DictNode -> { val items = this.items.entries .associate { it.key.toMemoryObject(memoryDump) to it.value.toMemoryObject(memoryDump) } - DictMemoryObject(this.id.toString(), this.type.name, this.type.moduleName, this.comparable, items) + DictMemoryObject( + this.id.toString(), + TypeInfo(this.type.moduleName, this.type.typeName), + this.comparable, + items + ) } + is PythonTree.ReduceNode -> { - val stateObjId = PythonTree.DictNode(this.state.entries.associate { PythonTree.PrimitiveNode(pythonStrClassId, it.key) to it.value }.toMutableMap()) + val stateObjId = PythonTree.DictNode(this.state.entries.associate { + PythonTree.PrimitiveNode( + pythonStrClassId, + it.key + ) to it.value + }.toMutableMap()) val argsIds = PythonTree.ListNode(this.args.withIndex().associate { it.index to it.value }.toMutableMap()) - val listItemsIds = PythonTree.ListNode(this.listitems.withIndex().associate { it.index to it.value }.toMutableMap()) + val listItemsIds = + PythonTree.ListNode(this.listitems.withIndex().associate { it.index to it.value }.toMutableMap()) val dictItemsIds = PythonTree.DictNode(this.dictitems.toMutableMap()) ReduceMemoryObject( this.id.toString(), - this.type.name, - this.type.moduleName, + TypeInfo( + this.type.moduleName, + this.type.typeName, + ), this.comparable, - this.constructor.name, + TypeInfo( + this.constructor.moduleName, + this.constructor.typeName, + ), argsIds.toMemoryObject(memoryDump), stateObjId.toMemoryObject(memoryDump), listItemsIds.toMemoryObject(memoryDump), dictItemsIds.toMemoryObject(memoryDump), ) } + else -> { error("Invalid PythonTree.PythonTreeNode $this") } @@ -140,62 +184,79 @@ fun PythonTree.PythonTreeNode.toMemoryObject(memoryDump: MemoryDump): String { return obj.id } -fun MemoryObject.toPythonTree(memoryDump: MemoryDump): PythonTree.PythonTreeNode { - val obj = when(this) { - is ReprMemoryObject -> { - PythonTree.PrimitiveNode( - this.id.toLong(), - PythonClassId(this.module, this.kind), - this.value - ) - } - is DictMemoryObject -> { - PythonTree.DictNode( - this.id.toLong(), - items.entries.associate { - memoryDump.getById(it.key).toPythonTree(memoryDump) to memoryDump.getById(it.value).toPythonTree(memoryDump) - }.toMutableMap() - ) - } - is ListMemoryObject -> { - val elementsMap = items.withIndex().associate { - it.index to memoryDump.getById(it.value).toPythonTree(memoryDump) +fun MemoryObject.toPythonTree( + memoryDump: MemoryDump, + visited: MutableMap = mutableMapOf() +): PythonTree.PythonTreeNode { + val obj = visited.getOrPut(this.id) { + val obj = when (this) { + is ReprMemoryObject -> { + PythonTree.PrimitiveNode( + this.id.toLong(), + PythonClassId(this.typeinfo.module, this.typeinfo.kind), + value + ) + } + + is DictMemoryObject -> { + PythonTree.DictNode( + this.id.toLong(), + items.entries.associate { + memoryDump.getById(it.key).toPythonTree(memoryDump, visited) to + memoryDump.getById(it.value).toPythonTree(memoryDump, visited) + }.toMutableMap() + ) + } + + is ListMemoryObject -> { + val elementsMap = items.withIndex().associate { + it.index to + memoryDump.getById(it.value).toPythonTree(memoryDump, visited) }.toMutableMap() - when (this.qualname) { - "builtins.tuple" -> { - PythonTree.TupleNode(this.id.toLong(), elementsMap) - } - "builtins.set" -> { - PythonTree.SetNode(this.id.toLong(), elementsMap.values.toMutableSet()) + when (this.qualname) { + "builtins.tuple" -> { + PythonTree.TupleNode(this.id.toLong(), elementsMap) + } + + "builtins.set" -> { + PythonTree.SetNode(this.id.toLong(), elementsMap.values.toMutableSet()) + } + + else -> { + PythonTree.ListNode(this.id.toLong(), elementsMap) + } } - else -> { - PythonTree.ListNode(this.id.toLong(), elementsMap) + } + + is ReduceMemoryObject -> { + val stateObjs = memoryDump.getById(state) as DictMemoryObject + val arguments = memoryDump.getById(args) as ListMemoryObject + val listitemsObjs = memoryDump.getById(listitems) as ListMemoryObject + val dictitemsObjs = memoryDump.getById(dictitems) as DictMemoryObject + val prevObj = PythonTree.ReduceNode( + this.id.toLong(), + PythonClassId(this.typeinfo.module, this.typeinfo.kind), + PythonClassId(this.constructor.module, this.constructor.kind), + arguments.items.map { memoryDump.getById(it).toPythonTree(memoryDump, visited) }, + ) + visited[this.id] = prevObj + + prevObj.state = stateObjs.items.entries.associate { + (memoryDump.getById(it.key).toPythonTree(memoryDump, visited) as PythonTree.PrimitiveNode) + .repr.drop(1).dropLast(1) to + memoryDump.getById(it.value).toPythonTree(memoryDump, visited) + }.toMutableMap() + prevObj.listitems = listitemsObjs.items.map { memoryDump.getById(it).toPythonTree(memoryDump, visited) } + prevObj.dictitems = dictitemsObjs.items.entries.associate { + memoryDump.getById(it.key).toPythonTree(memoryDump, visited) to + memoryDump.getById(it.value).toPythonTree(memoryDump, visited) } + prevObj } } - is ReduceMemoryObject -> { - val stateObjs = memoryDump.getById(state) as DictMemoryObject - val arguments = memoryDump.getById(args) as ListMemoryObject - val listitemsObjs = memoryDump.getById(listitems) as ListMemoryObject - val dictitemsObjs = memoryDump.getById(dictitems) as DictMemoryObject - PythonTree.ReduceNode( - this.id.toLong(), - PythonClassId(this.module, this.kind), - PythonClassId(this.constructor), - arguments.items.map { memoryDump.getById(it).toPythonTree(memoryDump) }, - stateObjs.items.entries.associate { - (memoryDump.getById(it.key).toPythonTree(memoryDump) as PythonTree.PrimitiveNode).repr.drop(1).dropLast(1) to - memoryDump.getById(it.value).toPythonTree(memoryDump) - }.toMutableMap(), - listitemsObjs.items.map { memoryDump.getById(it).toPythonTree(memoryDump) }, - dictitemsObjs.items.entries.associate { - memoryDump.getById(it.key).toPythonTree(memoryDump) to - memoryDump.getById(it.value).toPythonTree(memoryDump) - }, - ) - } + obj.comparable = this.comparable + return obj } - obj.comparable = this.comparable return obj } @@ -204,242 +265,3 @@ fun serializeObjects(objs: List): Pair, val ids = objs.map { it.toMemoryObject(memoryDump) } return Pair(ids, PythonObjectParser.serializeMemory(memoryDump)) } - -fun main() { - val dump = """ - { - "objects": { - "140239390887040": { - "strategy": "list", - "id": "140239390887040", - "kind": "builtins.list", - "comparable": false, - "items": [ - "140239394832624", - "140239394832656", - "140239392627184", - "140239394012784", - "140239392795520", - "140239406001728", - "140239392839840", - "140239390894848" - ] - }, - "140239394832624": { - "strategy": "repr", - "id": "140239394832624", - "kind": "builtins.int", - "comparable": true, - "value": "1" - }, - "140239394832656": { - "strategy": "repr", - "id": "140239394832656", - "kind": "builtins.int", - "comparable": true, - "value": "2" - }, - "140239392627184": { - "strategy": "repr", - "id": "140239392627184", - "kind": "builtins.float", - "comparable": true, - "value": "float('inf')" - }, - "140239394012784": { - "strategy": "repr", - "id": "140239394012784", - "kind": "builtins.str", - "comparable": true, - "value": "'abc'" - }, - "140239392795520": { - "strategy": "dict", - "id": "140239392795520", - "kind": "builtins.dict", - "comparable": true, - "items": { - "140239394832624": "140239394832624" - } - }, - "140239406001728": { - "strategy": "repr", - "id": "140239406001728", - "kind": "types.NoneType", - "comparable": true, - "value": "None" - }, - "140239391427840": { - "strategy": "list", - "id": "140239391427840", - "kind": "builtins.tuple", - "comparable": true, - "items": [ - "94246249326576", - "140239405994592", - "140239406001728" - ] - }, - "94246249326576": { - "strategy": "repr", - "id": "94246249326576", - "kind": "builtins.type", - "comparable": true, - "value": "deep_serialization.example.B" - }, - "140239405994592": { - "strategy": "repr", - "id": "140239405994592", - "kind": "builtins.type", - "comparable": true, - "value": "builtins.object" - }, - "140239392839840": { - "strategy": "reduce", - "id": "140239392839840", - "kind": "deep_serialization.example.B", - "comparable": false, - "constructor": "copyreg._reconstructor", - "args": "140239391427840", - "state": "140239392795712", - "listitems": "140239391672832", - "dictitems": "140239391673280" - }, - "140239392795712": { - "strategy": "dict", - "id": "140239392795712", - "kind": "builtins.dict", - "comparable": true, - "items": { - "140239392797168": "140239394832624", - "140239392797232": "140239394832656", - "140239392797296": "140239394832688", - "140239393831920": "140239392849760" - } - }, - "140239392797168": { - "strategy": "repr", - "id": "140239392797168", - "kind": "builtins.str", - "comparable": true, - "value": "'b1'" - }, - "140239392797232": { - "strategy": "repr", - "id": "140239392797232", - "kind": "builtins.str", - "comparable": true, - "value": "'b2'" - }, - "140239392797296": { - "strategy": "repr", - "id": "140239392797296", - "kind": "builtins.str", - "comparable": true, - "value": "'b3'" - }, - "140239394832688": { - "strategy": "repr", - "id": "140239394832688", - "kind": "builtins.int", - "comparable": true, - "value": "3" - }, - "140239393831920": { - "strategy": "repr", - "id": "140239393831920", - "kind": "builtins.str", - "comparable": true, - "value": "'time'" - }, - "140239394159488": { - "strategy": "list", - "id": "140239394159488", - "kind": "builtins.tuple", - "comparable": true, - "items": [ - "140239391514208" - ] - }, - "140239391514208": { - "strategy": "repr", - "id": "140239391514208", - "kind": "builtins.bytes", - "comparable": true, - "value": "b'\\x07\\xe7\\x02\\r\\x0c(3\\x06\\x1eA'" - }, - "140239392849760": { - "strategy": "reduce", - "id": "140239392849760", - "kind": "datetime.datetime", - "comparable": true, - "constructor": "datetime.datetime", - "args": "140239394159488", - "state": "140239391671232", - "listitems": "140239391671872", - "dictitems": "140239391672448" - }, - "140239391671232": { - "strategy": "dict", - "id": "140239391671232", - "kind": "builtins.dict", - "comparable": true, - "items": {} - }, - "140239391671872": { - "strategy": "list", - "id": "140239391671872", - "kind": "builtins.list", - "comparable": true, - "items": [] - }, - "140239391672448": { - "strategy": "dict", - "id": "140239391672448", - "kind": "builtins.dict", - "comparable": true, - "items": {} - }, - "140239391672832": { - "strategy": "list", - "id": "140239391672832", - "kind": "builtins.list", - "comparable": true, - "items": [] - }, - "140239391673280": { - "strategy": "dict", - "id": "140239391673280", - "kind": "builtins.dict", - "comparable": true, - "items": {} - }, - "140239390894848": { - "strategy": "list", - "id": "140239390894848", - "kind": "builtins.list", - "comparable": true, - "items": [ - "140239392797552" - ] - }, - "140239392797552": { - "strategy": "repr", - "id": "140239392797552", - "kind": "builtins.str", - "comparable": true, - "value": "'Alex'" - } - } - } -""".trimIndent() - - val load = PythonObjectParser.parseDumpedObjects(dump) - val obj = load.getById("140239390887040").toPythonTree(load) - val newMemory = MemoryDump(emptyMap().toMutableMap()) - val newId = obj.toMemoryObject(newMemory) - val newDump = PythonObjectParser.serializeMemory(newMemory) - val newLoad = PythonObjectParser.parseDumpedObjects(newDump) - val newObj = newLoad.getById(newId).toPythonTree(newLoad) -} - diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/PythonTree.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/PythonTree.kt index ecaec2eb62..17d18c534e 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/PythonTree.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/PythonTree.kt @@ -219,6 +219,13 @@ object PythonTree { args: List, ) : this(PythonIdGenerator.createId(), type, constructor, args, emptyMap().toMutableMap(), emptyList(), emptyMap()) + constructor( + id: Long, + type: PythonClassId, + constructor: PythonClassId, + args: List, + ) : this(id, type, constructor, args, emptyMap().toMutableMap(), emptyList(), emptyMap()) + override val children: List get() = args + state.values + listitems + dictitems.values + dictitems.keys + PythonTreeNode(constructor) diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/util/PythonTreeComparator.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/util/PythonTreeComparator.kt index af34cdb541..7d5ac0637f 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/util/PythonTreeComparator.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/util/PythonTreeComparator.kt @@ -70,6 +70,7 @@ fun comparePythonTree( is PythonTree.ReduceNode -> { if (right !is PythonTree.ReduceNode) false else { + val type = left.type == right.type val state = left.state.size == right.state.size && left.state.keys.all { comparePythonTree( left.state[it]!!, @@ -84,7 +85,7 @@ fun comparePythonTree( val dictitems = left.dictitems.keys == right.dictitems.keys && left.dictitems.keys .all { comparePythonTree(left.dictitems[it]!!, right.dictitems[it]!!, visitedLeft, visitedRight, equals) } - state && listitems && dictitems + type && state && listitems && dictitems } } 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 8e0c4e29d1..a0311d8402 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 @@ -133,7 +133,7 @@ class PythonCgMethodConstructor(context: CgContext) : CgMethodConstructor(contex assertThisObject: MutableList>, executableId: ExecutableId, ) { - if (stateAssertions.size + assertThisObject.size > 0) { + if (stateAssertions.isNotEmpty()) { emptyLineIfNeeded() } stateAssertions.forEach { (index, it) -> @@ -141,6 +141,9 @@ class PythonCgMethodConstructor(context: CgContext) : CgMethodConstructor(contex val modifiedArgument = variableConstructor.getOrCreateVariable(it.second, name) assertEquality(modifiedArgument, it.first) } + if (assertThisObject.isNotEmpty()) { + emptyLineIfNeeded() + } assertThisObject.forEach { val name = it.first.name + "_modified" val modifiedThisObject = variableConstructor.getOrCreateVariable(it.second, name) diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgVariableConstructor.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgVariableConstructor.kt index dea85ddadb..05c76eab9c 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgVariableConstructor.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgVariableConstructor.kt @@ -6,6 +6,7 @@ import org.utbot.framework.codegen.domain.models.CgLiteral import org.utbot.framework.codegen.domain.models.CgMethodCall import org.utbot.framework.codegen.domain.models.CgStatement import org.utbot.framework.codegen.domain.models.CgValue +import org.utbot.framework.codegen.tree.CgComponents import org.utbot.framework.codegen.tree.CgVariableConstructor import org.utbot.framework.plugin.api.ConstructorId import org.utbot.framework.plugin.api.FieldId @@ -14,14 +15,17 @@ import org.utbot.python.framework.api.python.* import org.utbot.python.framework.api.python.util.comparePythonTree import org.utbot.python.framework.api.python.util.pythonNoneClassId import org.utbot.python.framework.codegen.PythonCgLanguageAssistant +import org.utbot.python.framework.codegen.model.constructor.util.dropBuiltins import org.utbot.python.framework.codegen.model.tree.* class PythonCgVariableConstructor(cgContext: CgContext) : CgVariableConstructor(cgContext) { + private val nameGenerator = CgComponents.getNameGeneratorBy(context) override fun getOrCreateVariable(model: UtModel, name: String?): CgValue { + val baseName = name ?: nameGenerator.nameFrom(model.classId) return valueByModel.getOrPut(model) { when (model) { is PythonTreeModel -> { - val (value, arguments) = pythonBuildObject(model.tree) + val (value, arguments) = pythonBuildObject(model.tree, baseName) CgPythonTree(model.classId, model.tree, value, arguments) } is PythonModel -> error("Unexpected PythonModel: ${model::class}") @@ -30,30 +34,30 @@ class PythonCgVariableConstructor(cgContext: CgContext) : CgVariableConstructor( } } - private fun pythonBuildObject(objectNode: PythonTree.PythonTreeNode): Pair> { + private fun pythonBuildObject(objectNode: PythonTree.PythonTreeNode, baseName: String): Pair> { return when (objectNode) { is PythonTree.PrimitiveNode -> { - Pair(CgLiteral(objectNode.type, objectNode.repr), emptyList()) + Pair(CgLiteral(objectNode.type.dropBuiltins(), objectNode.repr), emptyList()) } is PythonTree.ListNode -> { - val items = objectNode.items.values.map { pythonBuildObject(it) } + val items = objectNode.items.values.map { pythonBuildObject(it, baseName) } Pair(CgPythonList(items.map {it.first}), items.flatMap { it.second }) } is PythonTree.TupleNode -> { - val items = objectNode.items.values.map { pythonBuildObject(it) } + val items = objectNode.items.values.map { pythonBuildObject(it, baseName) } Pair(CgPythonTuple(items.map {it.first}), items.flatMap { it.second }) } is PythonTree.SetNode -> { - val items = objectNode.items.map { pythonBuildObject(it) } + val items = objectNode.items.map { pythonBuildObject(it, baseName) } Pair(CgPythonSet(items.map {it.first}.toSet()), items.flatMap { it.second }) } is PythonTree.DictNode -> { - val keys = objectNode.items.keys.map { pythonBuildObject(it) } - val values = objectNode.items.values.map { pythonBuildObject(it) } + val keys = objectNode.items.keys.map { pythonBuildObject(it, baseName) } + val values = objectNode.items.values.map { pythonBuildObject(it, baseName) } Pair( CgPythonDict( keys.zip(values).associate { (key, value) -> @@ -79,11 +83,11 @@ class PythonCgVariableConstructor(cgContext: CgContext) : CgVariableConstructor( getOrCreateVariable(PythonTreeModel(it, it.type)) } val constructor = ConstructorId( - objectNode.constructor, + objectNode.constructor.dropBuiltins(), initArgs.map { it.type } ) val constructorCall = CgConstructorCall(constructor, initArgs) - val obj = newVar(objectNode.type) { + val obj = newVar(objectNode.type, baseName) { constructorCall } diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/util/ConstructorUtils.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/util/ConstructorUtils.kt index 4ac0501fb7..06e0b3ba63 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/util/ConstructorUtils.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/util/ConstructorUtils.kt @@ -5,6 +5,7 @@ import kotlinx.collections.immutable.PersistentSet import org.utbot.framework.codegen.domain.context.CgContextOwner import org.utbot.python.framework.api.python.PythonClassId import org.utbot.python.framework.api.python.PythonMethodId +import org.utbot.python.framework.api.python.pythonBuiltinsModuleName import org.utbot.python.framework.codegen.model.PythonUserImport internal fun CgContextOwner.importIfNeeded(method: PythonMethodId) { @@ -27,3 +28,15 @@ internal operator fun PersistentSet.plus(element: T): PersistentSet = internal operator fun PersistentSet.plus(other: PersistentSet): PersistentSet = this.addAll(other) +internal fun PythonClassId.dropBuiltins(): PythonClassId { + return if (this.rootModuleName == pythonBuiltinsModuleName) { + val moduleParts = this.moduleName.split(".", limit = 2) + if (moduleParts.size > 1) { + PythonClassId(moduleParts[1], this.simpleName) + } else { + PythonClassId(this.name.split(".", limit = 2).last()) + } + } else + this +} + diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/fields/PythonExecutionStateAnalyzer.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/fields/PythonExecutionStateAnalyzer.kt deleted file mode 100644 index 38be94a6ce..0000000000 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/fields/PythonExecutionStateAnalyzer.kt +++ /dev/null @@ -1,149 +0,0 @@ -package org.utbot.python.framework.fields - -import org.utbot.framework.fields.FieldPath -import org.utbot.framework.fields.FieldStatesInfo -import org.utbot.framework.fields.ModifiedField -import org.utbot.framework.fields.ModifiedFields -import org.utbot.framework.fields.StateModificationInfo -import org.utbot.framework.plugin.api.UtArrayModel -import org.utbot.framework.plugin.api.UtAssembleModel -import org.utbot.framework.plugin.api.UtClassRefModel -import org.utbot.framework.plugin.api.UtCompositeModel -import org.utbot.framework.plugin.api.UtEnumConstantModel -import org.utbot.framework.plugin.api.UtExecution -import org.utbot.framework.plugin.api.UtLambdaModel -import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.plugin.api.UtNullModel -import org.utbot.framework.plugin.api.UtPrimitiveModel -import org.utbot.framework.plugin.api.UtVoidModel -import org.utbot.framework.util.UtModelVisitor -import org.utbot.fuzzer.UtFuzzedExecution -import org.utbot.python.framework.api.python.PythonTreeModel - -class PythonExecutionStateAnalyzer(val execution: UtExecution) { - fun findModifiedFields(): StateModificationInfo { - return StateModificationInfo() - .analyzeThisInstance() - .analyzeParameters() - } - - private fun StateModificationInfo.analyzeThisInstance(): StateModificationInfo { - return when (execution) { - is UtFuzzedExecution -> { - this - } - - else -> { - this - } - } - } - - private fun StateModificationInfo.analyzeParameters(): StateModificationInfo { - val parametersBefore = execution.stateBefore.parameters - val parametersAfter = execution.stateAfter.parameters - val parameterStates = parametersBefore zip parametersAfter - val parameterModifications = mutableListOf() - for ((before, after) in parameterStates) { - val info = analyzeModelStates(before, after) - parameterModifications += getModifiedFields(info) - } - return this.copy(parameters = parameterModifications) - } - - private fun analyzeModelStates(before: UtModel, after: UtModel): FieldStatesInfo { - if (before is PythonTreeModel && after is PythonTreeModel) { - val fieldsBefore = mutableMapOf() - val fieldsAfter = mutableMapOf() - - val dataBefore = FieldData(FieldsVisitorMode.BEFORE, fieldsBefore) - before.accept(FieldStateVisitor(), dataBefore) - - val dataAfter = FieldData(FieldsVisitorMode.AFTER, fieldsAfter, previousFields = fieldsBefore) - after.accept(FieldStateVisitor(), dataAfter) - - return FieldStatesInfo(fieldsBefore, fieldsAfter) - } else { - throw IllegalArgumentException("Support only PythonTreeModel") - } - } - - private fun getModifiedFields(info: FieldStatesInfo): ModifiedFields { - val (fieldsBefore, fieldsAfter) = info - val fields = fieldsBefore.keys intersect fieldsAfter.keys - return fields.associateWith { fieldsBefore[it]!! to fieldsAfter[it]!! } - .filterValues { (before, after) -> before != after } - .map { (path, states) -> ModifiedField(path, states.first, states.second) } - } -} - -private data class FieldData( - val mode: FieldsVisitorMode, - val fields: MutableMap, - val path: FieldPath = FieldPath(), - val previousFields: Map? = null -) - -private enum class FieldsVisitorMode { - BEFORE, - AFTER -} - -private class FieldStateVisitor : UtModelVisitor() { - private fun recordFieldState(data: FieldData, model: UtModel) { - val fields = data.fields - val path = data.path - fields[path] = model - } - - override fun visit(element: UtModel, data: FieldData) { - if (element is PythonTreeModel) { - recordFieldState(data, element) - } else { - throw IllegalArgumentException("Invalid non-python UtModel: ${element.classId}") - } - } - - override fun visit(element: UtNullModel, data: FieldData) { - throw IllegalArgumentException("Invalid non-python UtModel: ${element.classId}") - } - - override fun visit(element: UtPrimitiveModel, data: FieldData) { - throw IllegalArgumentException("Invalid non-python UtModel: ${element.classId}") - } - - override fun visit(element: UtVoidModel, data: FieldData) { - throw IllegalArgumentException("Invalid non-python UtModel: ${element.classId}") - } - - override fun visit(element: UtClassRefModel, data: FieldData) { - throw IllegalArgumentException("Invalid non-python UtModel: ${element.classId}") - } - - override fun visit(element: UtEnumConstantModel, data: FieldData) { - throw IllegalArgumentException("Invalid non-python UtModel: ${element.classId}") - } - - override fun visit(element: UtArrayModel, data: FieldData) { - throw IllegalArgumentException("Invalid non-python UtModel: ${element.classId}") - } - - override fun visit(element: UtAssembleModel, data: FieldData) { - throw IllegalArgumentException("Invalid non-python UtModel: ${element.classId}") - } - - override fun visit(element: UtCompositeModel, data: FieldData) { - throw IllegalArgumentException("Invalid non-python UtModel: ${element.classId}") - } - - override fun visit(element: UtLambdaModel, data: FieldData) { - throw IllegalArgumentException("Invalid non-python UtModel: ${element.classId}") - } -} - -fun UtModel.accept(visitor: UtModelVisitor, data: D) = visitor.run { - when (val element = this@accept) { - is PythonTreeModel -> visit(element, data) - else -> throw UnsupportedOperationException() - } -} diff --git a/utbot-python/src/main/kotlin/org/utbot/python/utils/AnnotationNormalizer.kt b/utbot-python/src/main/kotlin/org/utbot/python/utils/AnnotationNormalizer.kt deleted file mode 100644 index 6d004223b6..0000000000 --- a/utbot-python/src/main/kotlin/org/utbot/python/utils/AnnotationNormalizer.kt +++ /dev/null @@ -1,92 +0,0 @@ -package org.utbot.python.utils - -import org.utbot.python.framework.api.python.NormalizedPythonAnnotation -import org.utbot.python.framework.api.python.PythonClassId -import org.utbot.python.framework.api.python.util.pythonAnyClassId -import java.io.File - - -object AnnotationNormalizer { - private val scriptContent = AnnotationNormalizer::class.java - .getResource("/normalize_annotation_from_project.py") - ?.readText() - ?: error("Didn't find /normalize_annotation_from_project.py") - - private var normalizeAnnotationFromProjectScript_: File? = null - private val normalizeAnnotationFromProjectScript: File - get() { - val result = normalizeAnnotationFromProjectScript_ - if (result == null || !result.exists()) { - val result1 = TemporaryFileManager.createTemporaryFile(scriptContent, tag = "normalize_annotation.py") - normalizeAnnotationFromProjectScript_ = result1 - return result1 - } - return result - } - - private fun normalizeAnnotationFromProject( - annotation: String, - pythonPath: String, - curPythonModule: String, - fileOfAnnotation: String, - filesToAddToSysPath: Set - ): String { - val result = runCommand( - listOf( - pythonPath, - normalizeAnnotationFromProjectScript.path, - annotation, - curPythonModule, - fileOfAnnotation, - ) + filesToAddToSysPath, - ) - return if (result.exitValue == 0) result.stdout else annotation - } - - fun annotationFromProjectToClassId( - annotation: String?, - pythonPath: String, - curPythonModule: String, - fileOfAnnotation: String, - filesToAddToSysPath: Set - ): NormalizedPythonAnnotation = - if (annotation == null) - pythonAnyClassId - else - NormalizedPythonAnnotation( - substituteTypes( - normalizeAnnotationFromProject( - annotation, - pythonPath, - curPythonModule, - fileOfAnnotation, - filesToAddToSysPath - ) - ) - ) - - private val substitutionMapFirstStage = listOf( - "builtins.list" to "typing.List", - "builtins.dict" to "typing.Dict", - "builtins.set" to "typing.Set" - ) - - private val substitutionMapSecondStage = listOf( - Regex("typing.List *([^\\[]|$)") to "typing.List[typing.Any]", - Regex("typing.Dict *([^\\[]|$)") to "typing.Dict[typing.Any, typing.Any]", - Regex("typing.Set *([^\\[]|$)") to "typing.Set[typing.Any]" - ) - - private fun substituteTypes(annotation: String): String { - val firstStage = substitutionMapFirstStage.fold(annotation) { acc, (old, new) -> - acc.replace(old, new) - } - return substitutionMapSecondStage.fold(firstStage) { acc, (re, new) -> - acc.replace(re, new) - } - } - - fun pythonClassIdToNormalizedAnnotation(classId: PythonClassId): NormalizedPythonAnnotation { - return NormalizedPythonAnnotation(substituteTypes(classId.name)) - } -} \ No newline at end of file diff --git a/utbot-python/src/main/kotlin/org/utbot/python/utils/RequirementsUtils.kt b/utbot-python/src/main/kotlin/org/utbot/python/utils/RequirementsUtils.kt index 061ceb8833..44e85f7485 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/utils/RequirementsUtils.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/utils/RequirementsUtils.kt @@ -3,7 +3,7 @@ package org.utbot.python.utils object RequirementsUtils { val requirements: List = listOf( "mypy==1.0.0", - "utbot-executor==1.3.1", + "utbot-executor==1.4.15", "utbot-mypy-runner==0.2.8", ) From 8c0e757f7f46be0da72562629d321f72817cf795 Mon Sep 17 00:00:00 2001 From: Vyacheslav Tamarin Date: Thu, 30 Mar 2023 16:45:33 +0300 Subject: [PATCH 2/2] Add initState and diffIds --- .../kotlin/org/utbot/python/PythonEngine.kt | 7 +- .../utbot/python/PythonTestCaseGenerator.kt | 5 +- .../python/evaluation/CodeEvaluationApi.kt | 2 + .../evaluation/PythonCodeExecutorImpl.kt | 144 ------------------ .../evaluation/PythonCodeSocketExecutor.kt | 4 + .../ExecutionResultDeserializer.kt | 2 + .../serialiation/PythonObjectParser.kt | 7 +- .../python/framework/api/python/PythonApi.kt | 17 +++ .../tree/PythonCgMethodConstructor.kt | 15 +- .../tree/PythonCgVariableConstructor.kt | 12 +- .../org/utbot/python/fuzzing/PythonApi.kt | 4 +- .../utbot/python/utils/RequirementsUtils.kt | 2 +- 12 files changed, 55 insertions(+), 166 deletions(-) delete mode 100644 utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCodeExecutorImpl.kt 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 528cf1f560..20d5ac040c 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/PythonEngine.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/PythonEngine.kt @@ -4,7 +4,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import mu.KotlinLogging import org.utbot.framework.plugin.api.* -import org.utbot.fuzzer.UtFuzzedExecution import org.utbot.fuzzing.Control import org.utbot.fuzzing.fuzz import org.utbot.fuzzing.utils.Trie @@ -14,6 +13,7 @@ import org.utbot.python.evaluation.serialiation.toPythonTree import org.utbot.python.framework.api.python.PythonTree import org.utbot.python.framework.api.python.PythonTreeModel import org.utbot.python.framework.api.python.PythonTreeWrapper +import org.utbot.python.framework.api.python.PythonUtExecution import org.utbot.python.fuzzing.* import org.utbot.python.newtyping.PythonTypeStorage import org.utbot.python.newtyping.general.Type @@ -117,12 +117,15 @@ class PythonEngine( val testMethodName = suggestExecutionName(methodUnderTestDescription, executionResult) + val (thisObject, initModelList) = transformModelList(hasThisObject, evaluationResult.stateInit, evaluationResult.modelListIds) val (beforeThisObject, beforeModelList) = transformModelList(hasThisObject, evaluationResult.stateBefore, evaluationResult.modelListIds) val (afterThisObject, afterModelList) = transformModelList(hasThisObject, evaluationResult.stateAfter, evaluationResult.modelListIds) - val utFuzzedExecution = UtFuzzedExecution( + val utFuzzedExecution = PythonUtExecution( + stateInit = EnvironmentModels(thisObject, initModelList, emptyMap()), stateBefore = EnvironmentModels(beforeThisObject, beforeModelList, emptyMap()), stateAfter = EnvironmentModels(afterThisObject, afterModelList, emptyMap()), + diffIds = evaluationResult.diffIds, result = executionResult, coverage = evaluationResult.coverage, testMethodName = testMethodName.testName?.camelToSnakeCase(), 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 c131d2779e..c653e1f4cd 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/PythonTestCaseGenerator.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/PythonTestCaseGenerator.kt @@ -6,6 +6,7 @@ import org.utbot.framework.minimization.minimizeExecutions import org.utbot.framework.plugin.api.UtError import org.utbot.framework.plugin.api.UtExecution import org.utbot.framework.plugin.api.UtExecutionSuccess +import org.utbot.python.framework.api.python.PythonUtExecution import org.utbot.python.fuzzing.* import org.utbot.python.newtyping.* import org.utbot.python.newtyping.ast.visitor.Visitor @@ -123,7 +124,7 @@ class PythonTestCaseGenerator( typeStorage: PythonTypeStorage, coveredLines: MutableSet, errors: MutableList, - executions: MutableList, + executions: MutableList, initMissingLines: Set?, until: Long, additionalVars: String = "" @@ -218,7 +219,7 @@ class PythonTestCaseGenerator( val typeStorage = PythonTypeStorage.get(mypyStorage) - val executions = mutableListOf() + val executions = mutableListOf() val errors = mutableListOf() val coveredLines = mutableSetOf() diff --git a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/CodeEvaluationApi.kt b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/CodeEvaluationApi.kt index faa5f516a9..43744e2c63 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/CodeEvaluationApi.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/CodeEvaluationApi.kt @@ -35,8 +35,10 @@ data class PythonEvaluationTimeout( data class PythonEvaluationSuccess( val isException: Boolean, val coverage: Coverage, + val stateInit: MemoryDump, val stateBefore: MemoryDump, val stateAfter: MemoryDump, + val diffIds: List, val modelListIds: List, val resultId: String, ) : PythonEvaluationResult() diff --git a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCodeExecutorImpl.kt b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCodeExecutorImpl.kt deleted file mode 100644 index c30f62dfb7..0000000000 --- a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCodeExecutorImpl.kt +++ /dev/null @@ -1,144 +0,0 @@ -package org.utbot.python.evaluation - -import org.utbot.framework.plugin.api.Coverage -import org.utbot.framework.plugin.api.Instruction -import org.utbot.python.FunctionArguments -import org.utbot.python.PythonMethod -import org.utbot.python.code.PythonCodeGenerator -import org.utbot.python.evaluation.serialiation.ExecutionResultDeserializer -import org.utbot.python.evaluation.serialiation.FailExecution -import org.utbot.python.evaluation.serialiation.SuccessExecution -import org.utbot.python.framework.api.python.PythonTreeModel -import org.utbot.python.framework.api.python.util.pythonAnyClassId -import org.utbot.python.newtyping.pythonTypeRepresentation -import org.utbot.python.utils.TemporaryFileManager -import org.utbot.python.utils.getResult -import org.utbot.python.utils.startProcess -import java.io.File - -data class EvaluationFiles( - val executionFile: File, - val fileForOutput: File, -) - -class PythonCodeExecutorImpl( - override val method: PythonMethod, - override val moduleToImport: String, - override val pythonPath: String, - override val syspathDirectories: Set, - override val executionTimeout: Long, -) : PythonCodeExecutor { - - override fun run( - fuzzedValues: FunctionArguments, - additionalModulesToImport: Set, - ): PythonEvaluationResult { - val evaluationFiles = generateExecutionCode( - additionalModulesToImport, - fuzzedValues.allArguments, - ) - return getEvaluationResult(evaluationFiles) - } - - override fun stop() {} - - private fun generateExecutionCode( - additionalModulesToImport: Set, - methodArguments: List, - ): EvaluationFiles { - val fileForOutput = TemporaryFileManager.assignTemporaryFile( - tag = "out_" + method.name + ".py", - addToCleaner = false - ) - val coverageDatabasePath = TemporaryFileManager.assignTemporaryFile( - tag = "coverage_db_" + method.name, - addToCleaner = false, - ) - val runCode = PythonCodeGenerator.generateRunFunctionCode( - method, - methodArguments, - syspathDirectories, - moduleToImport, - additionalModulesToImport, - fileForOutput.path, - coverageDatabasePath.path, - ) - val executionFile = TemporaryFileManager.createTemporaryFile( - runCode, - tag = "run_" + method.name + ".py", - addToCleaner = false - ) - return EvaluationFiles(executionFile, fileForOutput) - } - - private fun calculateCoverage(statements: List, missedStatements: List): Coverage { - val covered = statements.filter { it !in missedStatements } - return Coverage( - coveredInstructions=covered.map { - Instruction( - method.containingPythonClass?.pythonTypeRepresentation() ?: pythonAnyClassId.name, - method.methodSignature(), - it, - it.toLong() - ) - }, - instructionsCount = statements.size.toLong(), - missedInstructions = missedStatements.map { - Instruction( - method.containingPythonClass?.pythonTypeRepresentation() ?: pythonAnyClassId.name, - method.methodSignature(), - it, - it.toLong() - ) - } - ) - } - - private fun getEvaluationResult(evaluationFiles: EvaluationFiles): PythonEvaluationResult { - val evaluationProcess = startProcess(listOf(pythonPath, evaluationFiles.executionFile.path)) - val result = getResult(evaluationProcess, timeout = executionTimeout) - - if (result.terminatedByTimeout) - return PythonEvaluationTimeout() - - if (result.exitValue != 0) - return PythonEvaluationError( - result.exitValue, - result.stdout, - result.stderr.split(System.lineSeparator()) - ) - - val content = evaluationFiles.fileForOutput.readText() - evaluationFiles.fileForOutput.delete() - - return parseExecutionResult(content) - } - - private fun parseExecutionResult(content: String): PythonEvaluationResult { - val parsingException = PythonEvaluationError( - 0, - "Incorrect format of output", - emptyList() - ) - val executionResult = ExecutionResultDeserializer.parseExecutionResult(content) ?: return parsingException - return when (executionResult) { - is SuccessExecution -> { - val stateBefore = ExecutionResultDeserializer.parseMemoryDump(executionResult.stateBefore) ?: return parsingException - val stateAfter = ExecutionResultDeserializer.parseMemoryDump(executionResult.stateAfter) ?: return parsingException - PythonEvaluationSuccess( - executionResult.isException, - calculateCoverage(executionResult.statements, executionResult.missedStatements), - stateBefore, - stateAfter, - executionResult.argsIds + executionResult.kwargsIds.values, - executionResult.resultId, - ) - } - is FailExecution -> PythonEvaluationError( - 0, - executionResult.exception, - emptyList(), - ) - } - } -} \ No newline at end of file diff --git a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCodeSocketExecutor.kt b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCodeSocketExecutor.kt index e484ce8479..4190d22c14 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCodeSocketExecutor.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCodeSocketExecutor.kt @@ -103,13 +103,17 @@ class PythonCodeSocketExecutor( ) return when (executionResult) { is SuccessExecution -> { + val stateInit = ExecutionResultDeserializer.parseMemoryDump(executionResult.stateInit) ?: return parsingException val stateBefore = ExecutionResultDeserializer.parseMemoryDump(executionResult.stateBefore) ?: return parsingException val stateAfter = ExecutionResultDeserializer.parseMemoryDump(executionResult.stateAfter) ?: return parsingException + val diffIds = executionResult.diffIds.map {it.toLong()} PythonEvaluationSuccess( executionResult.isException, calculateCoverage(executionResult.statements, executionResult.missedStatements), + stateInit, stateBefore, stateAfter, + diffIds, executionResult.argsIds + executionResult.kwargsIds.values, executionResult.resultId, ) diff --git a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/ExecutionResultDeserializer.kt b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/ExecutionResultDeserializer.kt index 875de20c5e..efd1347dc4 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/ExecutionResultDeserializer.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/ExecutionResultDeserializer.kt @@ -45,8 +45,10 @@ data class SuccessExecution( val isException: Boolean, val statements: List, val missedStatements: List, + val stateInit: String, val stateBefore: String, val stateAfter: String, + val diffIds: List, val argsIds: List, val kwargsIds: Map, val resultId: String, diff --git a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/PythonObjectParser.kt b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/PythonObjectParser.kt index f08a667cc3..844ba90b36 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/PythonObjectParser.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/evaluation/serialiation/PythonObjectParser.kt @@ -189,10 +189,11 @@ fun MemoryObject.toPythonTree( visited: MutableMap = mutableMapOf() ): PythonTree.PythonTreeNode { val obj = visited.getOrPut(this.id) { + val id = this.id.toLong() val obj = when (this) { is ReprMemoryObject -> { PythonTree.PrimitiveNode( - this.id.toLong(), + id, PythonClassId(this.typeinfo.module, this.typeinfo.kind), value ) @@ -200,7 +201,7 @@ fun MemoryObject.toPythonTree( is DictMemoryObject -> { PythonTree.DictNode( - this.id.toLong(), + id, items.entries.associate { memoryDump.getById(it.key).toPythonTree(memoryDump, visited) to memoryDump.getById(it.value).toPythonTree(memoryDump, visited) @@ -234,7 +235,7 @@ fun MemoryObject.toPythonTree( val listitemsObjs = memoryDump.getById(listitems) as ListMemoryObject val dictitemsObjs = memoryDump.getById(dictitems) as DictMemoryObject val prevObj = PythonTree.ReduceNode( - this.id.toLong(), + id, PythonClassId(this.typeinfo.module, this.typeinfo.kind), PythonClassId(this.constructor.module, this.constructor.kind), arguments.items.map { memoryDump.getById(it).toPythonTree(memoryDump, visited) }, 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 5b29dc2520..474f3d18e1 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 @@ -1,7 +1,12 @@ package org.utbot.python.framework.api.python import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.Coverage +import org.utbot.framework.plugin.api.DocStatement +import org.utbot.framework.plugin.api.EnvironmentModels import org.utbot.framework.plugin.api.MethodId +import org.utbot.framework.plugin.api.UtExecution +import org.utbot.framework.plugin.api.UtExecutionResult import org.utbot.framework.plugin.api.UtModel import org.utbot.python.framework.api.python.util.comparePythonTree import org.utbot.python.framework.api.python.util.moduleOfType @@ -84,3 +89,15 @@ class PythonTreeModel( return tree.hashCode() } } + +class PythonUtExecution( + val stateInit: EnvironmentModels, + stateBefore: EnvironmentModels, + stateAfter: EnvironmentModels, + val diffIds: List, + result: UtExecutionResult, + coverage: Coverage? = null, + summary: List? = null, + testMethodName: String? = null, + displayName: String? = null +) : UtExecution(stateBefore, stateAfter, result, coverage, summary, testMethodName, displayName) \ No newline at end of file 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 a0311d8402..4bc6cde1bc 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 @@ -11,7 +11,6 @@ import org.utbot.framework.codegen.domain.models.CgVariable import org.utbot.framework.codegen.tree.CgMethodConstructor import org.utbot.framework.plugin.api.* import org.utbot.python.framework.api.python.* -import org.utbot.python.framework.api.python.util.comparePythonTree 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 @@ -40,6 +39,8 @@ class PythonCgMethodConstructor(context: CgContext) : CgMethodConstructor(contex override fun createTestMethod(executableId: ExecutableId, execution: UtExecution): CgTestMethod = withTestMethodScope(execution) { + val constructorState = (execution as PythonUtExecution).stateInit + val diffIds = execution.diffIds (context.cgLanguageAssistant as PythonCgLanguageAssistant).clear() val testMethodName = nameGenerator.testMethodNameFor(executableId, execution.testMethodName) if (execution.testMethodName == null) { @@ -56,14 +57,15 @@ class PythonCgMethodConstructor(context: CgContext) : CgMethodConstructor(contex substituteStaticFields(statics) setupInstrumentation() // build this instance - thisInstance = execution.stateBefore.thisInstance?.let { + thisInstance = constructorState.thisInstance?.let { variableConstructor.getOrCreateVariable(it) } + val beforeThisInstance = execution.stateBefore.thisInstance val afterThisInstance = execution.stateAfter.thisInstance val assertThisObject = emptyList>().toMutableList() if (beforeThisInstance is PythonTreeModel && afterThisInstance is PythonTreeModel) { - if (!comparePythonTree(beforeThisInstance.tree, afterThisInstance.tree)) { + if (diffIds.contains(afterThisInstance.tree.id)) { thisInstance = thisInstance?.let { val newValue = if (it is CgPythonTree) { @@ -86,13 +88,14 @@ class PythonCgMethodConstructor(context: CgContext) : CgMethodConstructor(contex // build arguments val stateAssertions = emptyMap>().toMutableMap() - for ((index, param) in execution.stateBefore.parameters.withIndex()) { + for ((index, param) in constructorState.parameters.withIndex()) { val name = paramNames[executableId]?.get(index) var argument = variableConstructor.getOrCreateVariable(param, name) + val beforeValue = execution.stateBefore.parameters[index] val afterValue = execution.stateAfter.parameters[index] - if (afterValue is PythonTreeModel && param is PythonTreeModel) { - if (!comparePythonTree(afterValue.tree, param.tree)) { + if (afterValue is PythonTreeModel && beforeValue is PythonTreeModel) { + if (diffIds.contains(afterValue.tree.id)) { if (argument !is CgVariable) { argument = newVar(argument.type, name) {argument} } diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgVariableConstructor.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgVariableConstructor.kt index 05c76eab9c..5c74548563 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgVariableConstructor.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgVariableConstructor.kt @@ -34,30 +34,30 @@ class PythonCgVariableConstructor(cgContext: CgContext) : CgVariableConstructor( } } - private fun pythonBuildObject(objectNode: PythonTree.PythonTreeNode, baseName: String): Pair> { + private fun pythonBuildObject(objectNode: PythonTree.PythonTreeNode, baseName: String? = null): Pair> { return when (objectNode) { is PythonTree.PrimitiveNode -> { Pair(CgLiteral(objectNode.type.dropBuiltins(), objectNode.repr), emptyList()) } is PythonTree.ListNode -> { - val items = objectNode.items.values.map { pythonBuildObject(it, baseName) } + val items = objectNode.items.values.map { pythonBuildObject(it) } Pair(CgPythonList(items.map {it.first}), items.flatMap { it.second }) } is PythonTree.TupleNode -> { - val items = objectNode.items.values.map { pythonBuildObject(it, baseName) } + val items = objectNode.items.values.map { pythonBuildObject(it) } Pair(CgPythonTuple(items.map {it.first}), items.flatMap { it.second }) } is PythonTree.SetNode -> { - val items = objectNode.items.map { pythonBuildObject(it, baseName) } + val items = objectNode.items.map { pythonBuildObject(it) } Pair(CgPythonSet(items.map {it.first}.toSet()), items.flatMap { it.second }) } is PythonTree.DictNode -> { - val keys = objectNode.items.keys.map { pythonBuildObject(it, baseName) } - val values = objectNode.items.values.map { pythonBuildObject(it, baseName) } + val keys = objectNode.items.keys.map { pythonBuildObject(it) } + val values = objectNode.items.values.map { pythonBuildObject(it) } Pair( CgPythonDict( keys.zip(values).associate { (key, value) -> diff --git a/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/PythonApi.kt b/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/PythonApi.kt index 9e6cae35a5..a55e125d5c 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/PythonApi.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/PythonApi.kt @@ -4,7 +4,6 @@ import mu.KotlinLogging import org.utbot.framework.plugin.api.Instruction import org.utbot.framework.plugin.api.UtError import org.utbot.fuzzer.FuzzedContext -import org.utbot.fuzzer.UtFuzzedExecution import org.utbot.fuzzing.Configuration import org.utbot.fuzzing.Control import org.utbot.fuzzing.Description @@ -14,6 +13,7 @@ import org.utbot.fuzzing.Seed import org.utbot.fuzzing.Statistic import org.utbot.fuzzing.utils.Trie import org.utbot.python.framework.api.python.PythonTree +import org.utbot.python.framework.api.python.PythonUtExecution import org.utbot.python.fuzzing.provider.* import org.utbot.python.fuzzing.provider.utils.isAny import org.utbot.python.newtyping.* @@ -36,7 +36,7 @@ class PythonMethodDescription( ) : Description(parameters) sealed interface FuzzingExecutionFeedback -class ValidExecution(val utFuzzedExecution: UtFuzzedExecution): FuzzingExecutionFeedback +class ValidExecution(val utFuzzedExecution: PythonUtExecution): FuzzingExecutionFeedback class InvalidExecution(val utError: UtError): FuzzingExecutionFeedback class TypeErrorFeedback(val message: String) : FuzzingExecutionFeedback class ArgumentsTypeErrorFeedback(val message: String) : FuzzingExecutionFeedback diff --git a/utbot-python/src/main/kotlin/org/utbot/python/utils/RequirementsUtils.kt b/utbot-python/src/main/kotlin/org/utbot/python/utils/RequirementsUtils.kt index 44e85f7485..4689bb0d09 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/utils/RequirementsUtils.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/utils/RequirementsUtils.kt @@ -3,7 +3,7 @@ package org.utbot.python.utils object RequirementsUtils { val requirements: List = listOf( "mypy==1.0.0", - "utbot-executor==1.4.15", + "utbot-executor==1.4.21", "utbot-mypy-runner==0.2.8", )