From 025a23be86800bcf8e5124dbeb7942bf97718e2d Mon Sep 17 00:00:00 2001 From: Egipti Pavel Date: Tue, 14 Feb 2023 11:54:40 +0300 Subject: [PATCH 1/4] pre-alfa of fix imports. Collecting of imports is done --- .../src/main/kotlin/org/utbot/go/GoEngine.kt | 57 +++++++++++-- .../kotlin/org/utbot/go/api/GoTypesApi.kt | 42 ++++++---- .../org/utbot/go/api/GoUtFunctionApi.kt | 10 ++- .../kotlin/org/utbot/go/api/GoUtModelsApi.kt | 65 +++++++-------- .../org/utbot/go/api/util/GoTypesApiUtil.kt | 11 +-- .../org/utbot/go/framework/api/go/GoApi.kt | 31 ++++++- .../fuzzer/providers/GoArrayValueProvider.kt | 3 +- .../fuzzer/providers/GoStructValueProvider.kt | 9 +- .../go/gocodeanalyzer/AnalysisResults.kt | 11 +-- .../go/gocodeanalyzer/GoSourceCodeAnalyzer.kt | 12 ++- .../AbstractGoUtTestsGenerationController.kt | 3 +- .../simplecodegeneration/GoFileCodeBuilder.kt | 25 +++--- .../GoTestCasesCodeGenerator.kt | 23 +++-- .../GoUtModelToCodeConverter.kt | 14 ++++ .../org/utbot/go/worker/GoCodeTemplates.kt | 40 +++++++-- .../kotlin/org/utbot/go/worker/GoWorker.kt | 20 ++--- .../go/worker/GoWorkerCodeGenerationHelper.kt | 83 ++++++++++++------- .../utbot/go/worker/RawExecutionResults.kt | 23 ++--- .../analysis_results.go | 3 +- .../go_source_code_analyzer/analyzer_core.go | 68 ++++++++++----- .../{cover.go => function_modifier.go} | 19 ++--- .../imports_collector.go | 74 +++++++++++++++++ .../resources/go_source_code_analyzer/main.go | 50 +++++++++-- 23 files changed, 500 insertions(+), 196 deletions(-) create mode 100644 utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoUtModelToCodeConverter.kt rename utbot-go/src/main/resources/go_source_code_analyzer/{cover.go => function_modifier.go} (84%) create mode 100644 utbot-go/src/main/resources/go_source_code_analyzer/imports_collector.go diff --git a/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt b/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt index e3aba5cd2d..5015b73b3f 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt @@ -8,6 +8,8 @@ import org.utbot.fuzzing.BaseFeedback import org.utbot.fuzzing.Control import org.utbot.fuzzing.utils.Trie import org.utbot.go.api.* +import org.utbot.go.framework.api.go.GoPackage +import org.utbot.go.framework.api.go.GoTypeId import org.utbot.go.logic.EachExecutionTimeoutsMillisConfig import org.utbot.go.util.executeCommandByNewProcessOrFailWithoutWaiting import org.utbot.go.worker.GoWorker @@ -19,6 +21,7 @@ import java.net.ServerSocket import java.net.SocketException import java.net.SocketTimeoutException import java.util.concurrent.TimeUnit +import kotlin.random.Random val logger = KotlinLogging.logger {} @@ -36,18 +39,58 @@ class GoEngine( val attemptsLimit = Int.MAX_VALUE ServerSocket(0).use { serverSocket -> var fileToExecute: File? = null + var fileWithModifiedFunction: File? = null try { + val random = Random(0) + val aliases = mutableMapOf() + val busyAliases = + GoWorkerCodeGenerationHelper.alwaysRequiredImports.map { it.goPackage.packageName }.toMutableSet() + + fun GoTypeId.getAllStructTypes(): Set = when (this) { + is GoStructTypeId -> fields.fold(setOf(this)) { acc: Set, field -> + acc + (field.declaringType).getAllStructTypes() + } + + is GoArrayTypeId -> elementTypeId!!.getAllStructTypes() + else -> emptySet() + } + + val structTypes = + methodUnderTest.parameters.fold(emptySet()) { acc: Set, functionParameter: GoUtFunctionParameter -> + acc + functionParameter.type.getAllStructTypes() + } + + structTypes.map { it.sourcePackage }.toSet().filter { it != methodUnderTest.sourcePackage } + .forEach { goPackage -> + if (goPackage.packageName !in busyAliases) { + busyAliases += goPackage.packageName + } else { + var suffix = "" + while (goPackage.packageName + suffix in busyAliases) { + suffix = random.nextInt().toString() + } + aliases[goPackage] = goPackage.packageName + suffix + } + } + + println(busyAliases) fileToExecute = GoWorkerCodeGenerationHelper.createFileToExecute( sourceFile, methodUnderTest, eachExecutionTimeoutsMillisConfig, - serverSocket.localPort + serverSocket.localPort, + aliases ) val testFunctionName = GoWorkerCodeGenerationHelper.workerTestFunctionName val command = listOf( goExecutableAbsolutePath, "test", "-run", testFunctionName ) val sourceFileDir = File(sourceFile.absoluteDirectoryPath) + + fileWithModifiedFunction = GoWorkerCodeGenerationHelper.createFileWithModifiedFunction( + methodUnderTest, sourceFileDir + ) + val processStartTime = System.currentTimeMillis() val process = executeCommandByNewProcessOrFailWithoutWaiting(command, sourceFileDir) val workerSocket = try { @@ -65,12 +108,12 @@ class GoEngine( } logger.debug { "Worker connected - completed in ${System.currentTimeMillis() - processStartTime} ms" } try { - val worker = GoWorker(workerSocket) + val worker = GoWorker(workerSocket, methodUnderTest) if (methodUnderTest.parameters.isEmpty()) { - worker.sendFuzzedParametersValues(listOf()) + worker.sendFuzzedParametersValues(listOf(), aliases) val rawExecutionResult = worker.receiveRawExecutionResult() val executionResult = convertRawExecutionResultToExecutionResult( - methodUnderTest.getPackageName(), + methodUnderTest.sourcePackage, rawExecutionResult, methodUnderTest.resultTypes, eachExecutionTimeoutsMillisConfig[methodUnderTest], @@ -83,14 +126,15 @@ class GoEngine( return@runGoFuzzing BaseFeedback(result = Trie.emptyNode(), control = Control.STOP) } val fuzzedFunction = GoUtFuzzedFunction(methodUnderTest, values) - worker.sendFuzzedParametersValues(values) + worker.sendFuzzedParametersValues(values, aliases) val rawExecutionResult = worker.receiveRawExecutionResult() val executionResult = convertRawExecutionResultToExecutionResult( - methodUnderTest.getPackageName(), + methodUnderTest.sourcePackage, rawExecutionResult, methodUnderTest.resultTypes, eachExecutionTimeoutsMillisConfig[methodUnderTest], ) + println(executionResult) if (executionResult.trace.isEmpty()) { logger.error { "Coverage is empty for [${methodUnderTest.name}] with $values}" } if (executionResult is GoUtPanicFailure) { @@ -147,6 +191,7 @@ class GoEngine( } } finally { fileToExecute?.delete() + fileWithModifiedFunction?.delete() } } } diff --git a/utbot-go/src/main/kotlin/org/utbot/go/api/GoTypesApi.kt b/utbot-go/src/main/kotlin/org/utbot/go/api/GoTypesApi.kt index eaa3a620c4..0b10b22121 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/api/GoTypesApi.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/api/GoTypesApi.kt @@ -1,16 +1,16 @@ package org.utbot.go.api import org.utbot.go.framework.api.go.GoFieldId +import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.framework.api.go.GoTypeId /** * Represents real Go primitive type. */ class GoPrimitiveTypeId(name: String) : GoTypeId(name) { - override val packageName: String = "" override val canonicalName: String = simpleName - override fun getRelativeName(packageName: String): String = simpleName + override fun getRelativeName(goPackage: GoPackage, alias: String): String = simpleName override fun equals(other: Any?): Boolean { if (this === other) return true @@ -25,16 +25,25 @@ class GoPrimitiveTypeId(name: String) : GoTypeId(name) { class GoStructTypeId( name: String, implementsError: Boolean, - override val packageName: String, - val packagePath: String, + override val sourcePackage: GoPackage, val fields: List, ) : GoTypeId(name, implementsError = implementsError) { - override val canonicalName: String = "$packageName.$name" - - override fun getRelativeName(packageName: String): String = if (this.packageName != packageName) { - canonicalName + val packageName: String = sourcePackage.packageName + val packagePath: String = sourcePackage.packagePath + override val canonicalName: String = "${sourcePackage.packageName}.$name" + + override fun getRelativeName(goPackage: GoPackage, alias: String): String = if (alias == "") { + if (sourcePackage == goPackage) { + simpleName + } else { + "$packageName.$simpleName" + } } else { - simpleName + when (alias) { + "." -> simpleName + "_" -> error("not supported") + else -> "$alias.$simpleName" + } } override fun equals(other: Any?): Boolean { @@ -53,14 +62,12 @@ class GoStructTypeId( } class GoArrayTypeId( - name: String, - elementTypeId: GoTypeId, - val length: Int + name: String, elementTypeId: GoTypeId, val length: Int ) : GoTypeId(name, elementTypeId = elementTypeId) { override val canonicalName: String = "[$length]${elementTypeId.canonicalName}" - override fun getRelativeName(packageName: String): String = - "[$length]${elementTypeId!!.getRelativeName(packageName)}" + override fun getRelativeName(goPackage: GoPackage, alias: String): String = + "[$length]${elementTypeId!!.getRelativeName(goPackage, alias)}" override fun equals(other: Any?): Boolean { if (this === other) return true @@ -75,16 +82,17 @@ class GoArrayTypeId( class GoInterfaceTypeId( name: String, implementsError: Boolean, - override val packageName: String, - val packagePath: String, + override val sourcePackage: GoPackage, ) : GoTypeId(name, implementsError = implementsError) { + val packageName: String = sourcePackage.packageName + val packagePath: String = sourcePackage.packagePath override val canonicalName: String = if (packageName != "") { "$packageName.$name" } else { simpleName } - override fun getRelativeName(packageName: String): String = if (this.packageName != packageName) { + override fun getRelativeName(goPackage: GoPackage, alias: String): String = if (this.packageName != packageName) { canonicalName } else { simpleName diff --git a/utbot-go/src/main/kotlin/org/utbot/go/api/GoUtFunctionApi.kt b/utbot-go/src/main/kotlin/org/utbot/go/api/GoUtFunctionApi.kt index dfcbac38ca..9b211338da 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/api/GoUtFunctionApi.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/api/GoUtFunctionApi.kt @@ -1,12 +1,13 @@ package org.utbot.go.api -import org.utbot.fuzzer.FuzzedConcreteValue +import org.utbot.go.framework.api.go.GoImport +import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.framework.api.go.GoTypeId import org.utbot.go.framework.api.go.GoUtModel import java.io.File import java.nio.file.Paths -data class GoUtFile(val absolutePath: String, val packageName: String) { +data class GoUtFile(val absolutePath: String, val sourcePackage: GoPackage) { val fileName: String get() = File(absolutePath).name val fileNameWithoutExtension: String get() = File(absolutePath).nameWithoutExtension val absoluteDirectoryPath: String get() = Paths.get(absolutePath).parent.toString() @@ -19,11 +20,14 @@ data class GoUtFunction( val modifiedName: String, val parameters: List, val resultTypes: List, + val requiredImports: List, val modifiedFunctionForCollectingTraces: String, val numberOfAllStatements: Int, val sourceFile: GoUtFile ) { - fun getPackageName(): String = sourceFile.packageName + val sourcePackage: GoPackage = sourceFile.sourcePackage + + fun getPackageName(): String = sourceFile.sourcePackage.packageName } data class GoUtFuzzedFunction(val function: GoUtFunction, val parametersValues: List) diff --git a/utbot-go/src/main/kotlin/org/utbot/go/api/GoUtModelsApi.kt b/utbot-go/src/main/kotlin/org/utbot/go/api/GoUtModelsApi.kt index d9337f51a8..8fac10377b 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/api/GoUtModelsApi.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/api/GoUtModelsApi.kt @@ -6,9 +6,7 @@ import org.utbot.go.api.util.goDefaultValueModel import org.utbot.go.api.util.goFloat64TypeId import org.utbot.go.api.util.goStringTypeId import org.utbot.go.api.util.neverRequiresExplicitCast -import org.utbot.go.framework.api.go.GoTypeId -import org.utbot.go.framework.api.go.GoUtFieldModel -import org.utbot.go.framework.api.go.GoUtModel +import org.utbot.go.framework.api.go.* // NEVER and DEPENDS difference is useful in code generation of assert.Equals(...). enum class ExplicitCastMode { @@ -24,12 +22,12 @@ open class GoUtPrimitiveModel( } else { ExplicitCastMode.DEPENDS }, - private val requiredImports: Set = emptySet(), + private val requiredPackages: Set = emptySet(), ) : GoUtModel(typeId) { override val typeId: GoPrimitiveTypeId get() = super.typeId as GoPrimitiveTypeId - override fun getRequiredImports(): Set = requiredImports + override fun getRequiredPackages(): Set = requiredPackages override fun isComparable(): Boolean = true @@ -44,64 +42,59 @@ open class GoUtPrimitiveModel( abstract class GoUtCompositeModel( typeId: GoTypeId, - val packageName: String, + val destinationPackage: GoPackage, ) : GoUtModel(typeId) class GoUtStructModel( val value: List, typeId: GoStructTypeId, - packageName: String, -) : GoUtCompositeModel(typeId, packageName) { + destinationPackage: GoPackage, + private val alias: String, +) : GoUtCompositeModel(typeId, destinationPackage) { override val typeId: GoStructTypeId get() = super.typeId as GoStructTypeId - override fun getRequiredImports(): Set { - val imports = if (typeId.packageName != packageName) { - mutableSetOf(typeId.packagePath) - } else { - mutableSetOf() - } - value.filter { packageName == typeId.packageName || it.fieldId.isExported } - .map { it.getRequiredImports() } - .forEach { imports += it } - return imports - } + // TODO delete this method because it seems like all fields are visible + private fun getVisibleFields(): List = + value.filter { destinationPackage == typeId.sourcePackage || it.fieldId.isExported } + + override fun getRequiredPackages(): Set = getVisibleFields() + .fold(setOf(typeId.sourcePackage)) { acc, fieldModel -> acc + fieldModel.getRequiredPackages() } override fun isComparable(): Boolean = value.all { it.isComparable() } - fun toStringWithoutStructName(): String = - value.filter { packageName == typeId.packageName || it.fieldId.isExported } - .joinToString(prefix = "{", postfix = "}") { "${it.fieldId.name}: ${it.model}" } + fun toStringWithoutStructName(): String = getVisibleFields() + .joinToString(prefix = "{", postfix = "}") { "${it.fieldId.name}: ${it.model}" } override fun toString(): String = - "${typeId.getRelativeName(packageName)}${toStringWithoutStructName()}" + "${typeId.getRelativeName(destinationPackage, alias)}${toStringWithoutStructName()}" } class GoUtArrayModel( val value: MutableMap, typeId: GoArrayTypeId, - packageName: String, -) : GoUtCompositeModel(typeId, packageName) { + destinationPackage: GoPackage, +) : GoUtCompositeModel(typeId, destinationPackage) { val length: Int = typeId.length override val typeId: GoArrayTypeId get() = super.typeId as GoArrayTypeId - override fun getRequiredImports(): Set { + override fun getRequiredPackages(): Set { val elementStructTypeId = typeId.elementTypeId as? GoStructTypeId - val imports = if (elementStructTypeId != null && elementStructTypeId.packageName != packageName) { - mutableSetOf(elementStructTypeId.packagePath) + val imports = if (elementStructTypeId != null && elementStructTypeId.sourcePackage != destinationPackage) { + mutableSetOf(elementStructTypeId.sourcePackage) } else { mutableSetOf() } - value.values.map { it.getRequiredImports() }.forEach { imports += it } + value.values.map { it.getRequiredPackages() }.forEach { imports += it } return imports } override fun isComparable(): Boolean = value.values.all { it.isComparable() } fun getElements(typeId: GoTypeId): List = (0 until length).map { - value[it] ?: typeId.goDefaultValueModel(packageName) + value[it] ?: typeId.goDefaultValueModel(destinationPackage) } fun toStringWithoutTypeName(): String = when (val typeId = typeId.elementTypeId!!) { @@ -118,17 +111,17 @@ class GoUtArrayModel( override fun toString(): String = when (val typeId = typeId.elementTypeId!!) { is GoStructTypeId -> getElements(typeId) - .joinToString(prefix = "[$length]${typeId.getRelativeName(packageName)}{", postfix = "}") { + .joinToString(prefix = "[$length]${typeId.getRelativeName(destinationPackage)}{", postfix = "}") { (it as GoUtStructModel).toStringWithoutStructName() } is GoArrayTypeId -> getElements(typeId) - .joinToString(prefix = "[$length]${typeId.getRelativeName(packageName)}{", postfix = "}") { + .joinToString(prefix = "[$length]${typeId.getRelativeName(destinationPackage)}{", postfix = "}") { (it as GoUtArrayModel).toStringWithoutTypeName() } else -> getElements(typeId) - .joinToString(prefix = "[$length]${typeId.getRelativeName(packageName)}{", postfix = "}") + .joinToString(prefix = "[$length]${typeId.getRelativeName(destinationPackage)}{", postfix = "}") } } @@ -142,7 +135,7 @@ class GoUtFloatNaNModel( } else { ExplicitCastMode.NEVER }, - requiredImports = setOf("math"), + requiredPackages = setOf(GoPackage("math", "math")), ) { override fun isComparable(): Boolean = false } @@ -158,7 +151,7 @@ class GoUtFloatInfModel( } else { ExplicitCastMode.NEVER }, - requiredImports = setOf("math"), + requiredPackages = setOf(GoPackage("math", "math")), ) class GoUtComplexModel( @@ -168,7 +161,7 @@ class GoUtComplexModel( ) : GoUtPrimitiveModel( "complex($realValue, $imagValue)", typeId, - requiredImports = realValue.getRequiredImports() + imagValue.getRequiredImports(), + requiredPackages = realValue.getRequiredPackages() + imagValue.getRequiredPackages(), explicitCastMode = ExplicitCastMode.NEVER ) { override fun isComparable(): Boolean = realValue.isComparable() && imagValue.isComparable() diff --git a/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoTypesApiUtil.kt b/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoTypesApiUtil.kt index 1740891fb2..173681c2ae 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoTypesApiUtil.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoTypesApiUtil.kt @@ -1,6 +1,7 @@ package org.utbot.go.api.util import org.utbot.go.api.* +import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.framework.api.go.GoTypeId import org.utbot.go.framework.api.go.GoUtModel import kotlin.reflect.KClass @@ -92,7 +93,7 @@ val GoPrimitiveTypeId.correspondingKClass: KClass else -> String::class // default way to hold GoUtPrimitiveModel's value is to use String } -fun GoTypeId.goDefaultValueModel(packageName: String): GoUtModel = when (this) { +fun GoTypeId.goDefaultValueModel(destinationPackage: GoPackage): GoUtModel = when (this) { is GoPrimitiveTypeId -> when (this) { goBoolTypeId -> GoUtPrimitiveModel(false, this) goRuneTypeId, goIntTypeId, goInt8TypeId, goInt16TypeId, goInt32TypeId, goInt64TypeId -> GoUtPrimitiveModel( @@ -107,8 +108,8 @@ fun GoTypeId.goDefaultValueModel(packageName: String): GoUtModel = when (this) { goFloat32TypeId, goFloat64TypeId -> GoUtPrimitiveModel(0.0, this) goComplex64TypeId, goComplex128TypeId -> GoUtComplexModel( - goFloat64TypeId.goDefaultValueModel(packageName) as GoUtPrimitiveModel, - goFloat64TypeId.goDefaultValueModel(packageName) as GoUtPrimitiveModel, + goFloat64TypeId.goDefaultValueModel(destinationPackage) as GoUtPrimitiveModel, + goFloat64TypeId.goDefaultValueModel(destinationPackage) as GoUtPrimitiveModel, this ) @@ -118,7 +119,7 @@ fun GoTypeId.goDefaultValueModel(packageName: String): GoUtModel = when (this) { else -> error("Go primitive ${this.javaClass} is not supported") } - is GoStructTypeId -> GoUtStructModel(listOf(), this, packageName) - is GoArrayTypeId -> GoUtArrayModel(hashMapOf(), this, packageName) + is GoStructTypeId -> GoUtStructModel(listOf(), this, destinationPackage, "") + is GoArrayTypeId -> GoUtArrayModel(hashMapOf(), this, destinationPackage) else -> GoUtNilModel(this) } \ No newline at end of file diff --git a/utbot-go/src/main/kotlin/org/utbot/go/framework/api/go/GoApi.kt b/utbot-go/src/main/kotlin/org/utbot/go/framework/api/go/GoApi.kt index 424e611318..86b5c3550e 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/framework/api/go/GoApi.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/framework/api/go/GoApi.kt @@ -10,11 +10,11 @@ abstract class GoTypeId( val elementTypeId: GoTypeId? = null, val implementsError: Boolean = false ) { - open val packageName: String = "" + open val sourcePackage: GoPackage = GoPackage("", "") val simpleName: String = name abstract val canonicalName: String - abstract fun getRelativeName(packageName: String): String + abstract fun getRelativeName(goPackage: GoPackage, alias: String = ""): String override fun toString(): String = canonicalName } @@ -26,7 +26,7 @@ abstract class GoTypeId( abstract class GoUtModel( open val typeId: GoTypeId, ) { - open fun getRequiredImports(): Set = emptySet() + open fun getRequiredPackages(): Set = emptySet() abstract fun isComparable(): Boolean } @@ -37,7 +37,7 @@ class GoUtFieldModel( val model: GoUtModel, val fieldId: GoFieldId, ) : GoUtModel(fieldId.declaringType) { - override fun getRequiredImports(): Set = model.getRequiredImports() + override fun getRequiredPackages(): Set = model.getRequiredPackages() override fun isComparable(): Boolean = model.isComparable() } @@ -49,3 +49,26 @@ class GoFieldId( val name: String, val isExported: Boolean ) + +/** + * Class for Go package. + */ +data class GoPackage( + val packageName: String, + val packagePath: String +) + +/** + * Class for Go import. + */ +data class GoImport( + val goPackage: GoPackage, + val alias: String +) { + override fun toString(): String { + if (alias == "") { + return "\"${goPackage.packagePath}\"" + } + return "$alias \"${goPackage.packagePath}\"" + } +} \ No newline at end of file diff --git a/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoArrayValueProvider.kt b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoArrayValueProvider.kt index f47e78c12a..5c4add445c 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoArrayValueProvider.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoArrayValueProvider.kt @@ -15,14 +15,13 @@ object GoArrayValueProvider : ValueProvider override fun generate(description: GoDescription, type: GoTypeId): Sequence> = sequence { type.let { it as GoArrayTypeId }.also { arrayType -> - val packageName = description.methodUnderTest.getPackageName() yield( Seed.Collection( construct = Routine.Collection { GoUtArrayModel( value = hashMapOf(), typeId = arrayType, - packageName = packageName + destinationPackage = description.methodUnderTest.sourcePackage ) }, modify = Routine.ForEach(listOf(arrayType.elementTypeId!!)) { self, i, values -> diff --git a/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoStructValueProvider.kt b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoStructValueProvider.kt index 9fc47d5693..9d1805d56c 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoStructValueProvider.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoStructValueProvider.kt @@ -16,7 +16,8 @@ object GoStructValueProvider : ValueProvider override fun generate(description: GoDescription, type: GoTypeId): Sequence> = sequence { type.let { it as GoStructTypeId }.also { structType -> - val packageName = description.methodUnderTest.getPackageName() + val function = description.methodUnderTest + val packageName = function.getPackageName() val fields = structType.fields .filter { structType.packageName == packageName || it.isExported } yield(Seed.Recursive( @@ -26,7 +27,8 @@ object GoStructValueProvider : ValueProvider GoUtFieldModel(value, field) }, typeId = structType, - packageName = packageName, + destinationPackage = function.sourcePackage, + alias = function.requiredImports.first { it.goPackage == structType.sourcePackage }.alias ) }, modify = sequence { @@ -42,7 +44,8 @@ object GoStructValueProvider : ValueProvider GoUtStructModel( value = emptyList(), typeId = structType, - packageName = packageName + destinationPackage = function.sourcePackage, + alias = function.requiredImports.first { it.goPackage == structType.sourcePackage }.alias ) } )) diff --git a/utbot-go/src/main/kotlin/org/utbot/go/gocodeanalyzer/AnalysisResults.kt b/utbot-go/src/main/kotlin/org/utbot/go/gocodeanalyzer/AnalysisResults.kt index 9f47c3b177..1f0eb8a246 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/gocodeanalyzer/AnalysisResults.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/gocodeanalyzer/AnalysisResults.kt @@ -8,6 +8,8 @@ import org.utbot.go.api.GoPrimitiveTypeId import org.utbot.go.api.GoStructTypeId import org.utbot.go.api.util.goPrimitives import org.utbot.go.framework.api.go.GoFieldId +import org.utbot.go.framework.api.go.GoImport +import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.framework.api.go.GoTypeId import kotlin.reflect.KClass @@ -21,8 +23,7 @@ data class AnalyzedInterfaceType( GoInterfaceTypeId( name = simpleName, implementsError = implementsError, - packageName = packageName, - packagePath = packagePath + sourcePackage = GoPackage(packageName, packagePath) ) private val simpleName: String = name.replaceFirst("interface ", "") @@ -49,8 +50,7 @@ data class AnalyzedStructType( override fun toGoTypeId(): GoTypeId = GoStructTypeId( name = name, - packageName = packageName, - packagePath = packagePath, + sourcePackage = GoPackage(packageName, packagePath), implementsError = implementsError, fields = fields.map { field -> GoFieldId(field.type.toGoTypeId(), field.name, field.isExported) } ) @@ -94,13 +94,14 @@ internal data class AnalyzedFunction( val modifiedName: String, val parameters: List, val resultTypes: List, + val requiredImports: List, val modifiedFunctionForCollectingTraces: String, val numberOfAllStatements: Int ) internal data class AnalysisResult( val absoluteFilePath: String, - val packageName: String, + val sourcePackage: GoPackage, val analyzedFunctions: List, val notSupportedFunctionsNames: List, val notFoundFunctionsNames: List diff --git a/utbot-go/src/main/kotlin/org/utbot/go/gocodeanalyzer/GoSourceCodeAnalyzer.kt b/utbot-go/src/main/kotlin/org/utbot/go/gocodeanalyzer/GoSourceCodeAnalyzer.kt index cc394dc2a5..836a40bb5b 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/gocodeanalyzer/GoSourceCodeAnalyzer.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/gocodeanalyzer/GoSourceCodeAnalyzer.kt @@ -65,7 +65,7 @@ object GoSourceCodeAnalyzer { val analysisResults = parseFromJsonOrFail(analysisResultsFile) GoPrimitivesValueProvider.intSize = analysisResults.intSize return analysisResults.results.map { analysisResult -> - GoUtFile(analysisResult.absoluteFilePath, analysisResult.packageName) to analysisResult + GoUtFile(analysisResult.absoluteFilePath, analysisResult.sourcePackage) to analysisResult }.associateBy({ (sourceFile, _) -> sourceFile }) { (sourceFile, analysisResult) -> val functions = analysisResult.analyzedFunctions.map { analyzedFunction -> val parameters = analyzedFunction.parameters.map { analyzedFunctionParameter -> @@ -80,6 +80,7 @@ object GoSourceCodeAnalyzer { analyzedFunction.modifiedName, parameters, resultTypes, + analyzedFunction.requiredImports, analyzedFunction.modifiedFunctionForCollectingTraces, analyzedFunction.numberOfAllStatements, sourceFile @@ -115,7 +116,14 @@ object GoSourceCodeAnalyzer { } private fun getGoCodeAnalyzerSourceFilesNames(): List { - return listOf("main.go", "analyzer_core.go", "analysis_targets.go", "analysis_results.go", "cover.go") + return listOf( + "main.go", + "analyzer_core.go", + "analysis_targets.go", + "analysis_results.go", + "function_modifier.go", + "imports_collector.go" + ) } private fun createAnalysisTargetsFileName(): String { diff --git a/utbot-go/src/main/kotlin/org/utbot/go/logic/AbstractGoUtTestsGenerationController.kt b/utbot-go/src/main/kotlin/org/utbot/go/logic/AbstractGoUtTestsGenerationController.kt index 3bc3b866fd..96a6350774 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/logic/AbstractGoUtTestsGenerationController.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/logic/AbstractGoUtTestsGenerationController.kt @@ -42,7 +42,8 @@ abstract class AbstractGoUtTestsGenerationController { testCasesBySourceFiles.forEach { (sourceFile, testCases) -> if (!onTestCasesFileCodeGenerationStart(sourceFile, testCases)) return - val generatedTestsFileCode = GoTestCasesCodeGenerator.generateTestCasesFileCode(sourceFile, testCases) + val generatedTestsFileCode = + GoTestCasesCodeGenerator.generateTestCasesFileCode(sourceFile, testCases, mapOf()) if (!onTestCasesFileCodeGenerationFinished(sourceFile, generatedTestsFileCode)) return } } diff --git a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoFileCodeBuilder.kt b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoFileCodeBuilder.kt index 53538c618d..9a7a00cb65 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoFileCodeBuilder.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoFileCodeBuilder.kt @@ -1,21 +1,24 @@ package org.utbot.go.simplecodegeneration +import org.utbot.go.framework.api.go.GoImport +import org.utbot.go.framework.api.go.GoPackage + class GoFileCodeBuilder( - packageName: String, - importNames: Set, + sourcePackage: GoPackage, + imports: Set, ) { - private val packageLine: String = "package $packageName" - private val importLines: String = importLines(importNames) + private val packageLine: String = "package ${sourcePackage.packageName}" + private val importLines: String = importLines(imports) private val topLevelElements: MutableList = mutableListOf() - private fun importLines(importNames: Set): String { - val sortedImportNames = importNames.toList().sorted() - if (sortedImportNames.isEmpty()) return "" - if (sortedImportNames.size == 1) { - return "import ${sortedImportNames.first()}" + private fun importLines(imports: Set): String { + if (imports.isEmpty()) return "" + if (imports.size == 1) { + return "import ${imports.first()}" } - return sortedImportNames.joinToString(separator = "", prefix = "import (\n", postfix = ")") { - "\t\"$it\"\n" + + return imports.sortedBy { it.toString() }.joinToString(separator = "", prefix = "import (\n", postfix = ")") { + "\t$it\n" } } diff --git a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoTestCasesCodeGenerator.kt b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoTestCasesCodeGenerator.kt index 639984aca3..09fcb63951 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoTestCasesCodeGenerator.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoTestCasesCodeGenerator.kt @@ -5,34 +5,41 @@ import org.utbot.go.api.util.containsNaNOrInf import org.utbot.go.api.util.doesNotContainNaNOrInf import org.utbot.go.api.util.goFloat64TypeId import org.utbot.go.api.util.goStringTypeId +import org.utbot.go.framework.api.go.GoImport +import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.framework.api.go.GoUtModel object GoTestCasesCodeGenerator { - fun generateTestCasesFileCode(sourceFile: GoUtFile, testCases: List): String { + fun generateTestCasesFileCode( + sourceFile: GoUtFile, + testCases: List, + aliases: Map + ): String { val imports = if (testCases.isNotEmpty()) { - mutableSetOf("github.com/stretchr/testify/assert", "testing") + mutableSetOf( + GoImport(GoPackage("assert", "github.com/stretchr/testify/assert"), ""), + GoImport(GoPackage("testing", "testing"), "") + ) } else { mutableSetOf() } testCases.forEach { testCase -> - testCase.parametersValues.forEach { - imports += it.getRequiredImports() - } when (val executionResult = testCase.executionResult) { is GoUtExecutionCompleted -> { executionResult.models.forEach { - imports += it.getRequiredImports() + imports += it.getRequiredPackages().map { pkg -> GoImport(pkg, aliases[pkg] ?: "") } } } is GoUtPanicFailure -> { - imports += executionResult.panicValue.getRequiredImports() + imports += executionResult.panicValue.getRequiredPackages() + .map { pkg -> GoImport(pkg, aliases[pkg] ?: "") } } } } - val fileBuilder = GoFileCodeBuilder(sourceFile.packageName, imports) + val fileBuilder = GoFileCodeBuilder(sourceFile.sourcePackage, imports) fun List.generateTestFunctions( generateTestFunctionForTestCase: (GoUtFuzzedFunctionTestCase, Int?) -> String diff --git a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoUtModelToCodeConverter.kt b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoUtModelToCodeConverter.kt new file mode 100644 index 0000000000..a0a4f950fd --- /dev/null +++ b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoUtModelToCodeConverter.kt @@ -0,0 +1,14 @@ +package org.utbot.go.simplecodegeneration + +import org.utbot.go.api.GoUtFloatNaNModel +import org.utbot.go.api.GoUtPrimitiveModel + +object GoUtModelToCodeConverter { + fun goUtModelToCode(goUtModel: GoUtPrimitiveModel): String { + return "1" + } + + fun goUtModelToCode(goUtModel: GoUtFloatNaNModel): String { + return "2" + } +} diff --git a/utbot-go/src/main/kotlin/org/utbot/go/worker/GoCodeTemplates.kt b/utbot-go/src/main/kotlin/org/utbot/go/worker/GoCodeTemplates.kt index 261d16dbc2..9d2354fa8f 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/worker/GoCodeTemplates.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/worker/GoCodeTemplates.kt @@ -1,10 +1,11 @@ package org.utbot.go.worker import org.utbot.go.api.GoStructTypeId +import org.utbot.go.framework.api.go.GoPackage object GoCodeTemplates { - private val traces = """ + val traces = """ var __traces__ []int """.trimIndent() @@ -207,7 +208,11 @@ object GoCodeTemplates { } """.trimIndent() - private fun convertStringToReflectType(structTypes: Set, packageName: String) = """ + private fun convertStringToReflectType( + structTypes: Set, + sourcePackage: GoPackage, + aliases: Map + ) = """ func __convertStringToReflectType__(typeName string) (reflect.Type, error) { var result reflect.Type @@ -274,7 +279,27 @@ object GoCodeTemplates { result = reflect.TypeOf("") case "uintptr": result = reflect.TypeOf(uintptr(0)) - ${structTypes.joinToString(separator = "\n") { "case \"${it.canonicalName}\": result = reflect.TypeOf(${it.getRelativeName(packageName)}{})" }} + ${ + structTypes.joinToString(separator = "\n") { + "case \"${ + if (it.sourcePackage == sourcePackage || aliases[it.sourcePackage] == ".") { + it.simpleName + } else if ((aliases[it.sourcePackage] ?: "") == "") { + "${it.packageName}.${it.simpleName}" + } else { + "${aliases[it.sourcePackage]}.${it.simpleName}" + } + }\": result = reflect.TypeOf(${ + if (it.sourcePackage == sourcePackage || aliases[it.sourcePackage] == ".") { + it.simpleName + } else if ((aliases[it.sourcePackage] ?: "") == "") { + "${it.packageName}.${it.simpleName}" + } else { + "${aliases[it.sourcePackage]}.${it.simpleName}" + } + }{})" + } + } default: return nil, fmt.Errorf("type '%s' not supported", typeName) } @@ -661,8 +686,11 @@ object GoCodeTemplates { } """.trimIndent() - fun getTopLevelHelperStructsAndFunctionsForWorker(structTypes: Set, packageName: String) = listOf( - traces, + fun getTopLevelHelperStructsAndFunctionsForWorker( + structTypes: Set, + goPackage: GoPackage, + aliases: Map + ) = listOf( rawValueInterface, primitiveValueStruct, primitiveValueToReflectValueMethod, @@ -671,7 +699,7 @@ object GoCodeTemplates { structValueToReflectValueMethod, arrayValueStruct, arrayValueToReflectValueMethod, - convertStringToReflectType(structTypes, packageName), + convertStringToReflectType(structTypes, goPackage, aliases), panicMessageStruct, rawExecutionResultStruct, checkErrorFunction, diff --git a/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorker.kt b/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorker.kt index 902c3acf55..e81571d43c 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorker.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorker.kt @@ -1,10 +1,8 @@ package org.utbot.go.worker import com.beust.klaxon.Klaxon -import org.utbot.go.api.GoUtArrayModel -import org.utbot.go.api.GoUtComplexModel -import org.utbot.go.api.GoUtPrimitiveModel -import org.utbot.go.api.GoUtStructModel +import org.utbot.go.api.* +import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.framework.api.go.GoUtModel import org.utbot.go.util.convertObjectToJsonString import java.io.BufferedReader @@ -15,11 +13,12 @@ import java.net.Socket class GoWorker( socket: Socket, + val function: GoUtFunction ) { private val reader: BufferedReader = BufferedReader(InputStreamReader(socket.getInputStream())) private val writer: BufferedWriter = BufferedWriter(OutputStreamWriter(socket.getOutputStream())) - private fun GoUtModel.convertToRawValue(): RawValue = when (val model = this) { + private fun GoUtModel.convertToRawValue(sourcePackage: GoPackage, aliases: Map): RawValue = when (val model = this) { is GoUtComplexModel -> PrimitiveValue( model.typeId.name, "${model.realValue.toValueGoCode()}@${model.imagValue.toValueGoCode()}" @@ -29,15 +28,15 @@ class GoWorker( model.typeId.name, model.typeId.elementTypeId!!.canonicalName, model.length, - model.getElements(model.typeId.elementTypeId!!).map { it.convertToRawValue() } + model.getElements(model.typeId.elementTypeId!!).map { it.convertToRawValue(sourcePackage, aliases) } ) is GoUtStructModel -> StructValue( - model.typeId.canonicalName, + model.typeId.getRelativeName(sourcePackage, aliases[model.typeId.sourcePackage] ?: ""), model.value.map { StructValue.FieldValue( it.fieldId.name, - it.model.convertToRawValue(), + it.model.convertToRawValue(sourcePackage, aliases), it.fieldId.isExported ) } @@ -48,9 +47,10 @@ class GoWorker( else -> error("Converting ${model.javaClass} to RawValue is not supported") } - fun sendFuzzedParametersValues(parameters: List) { - val rawValues = parameters.map { it.convertToRawValue() } + fun sendFuzzedParametersValues(parameters: List, aliases: Map) { + val rawValues = parameters.map { it.convertToRawValue(function.sourcePackage, aliases) } val json = convertObjectToJsonString(rawValues) + println(json) writer.write(json) writer.flush() } diff --git a/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorkerCodeGenerationHelper.kt b/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorkerCodeGenerationHelper.kt index e5c71f9d92..9710534b65 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorkerCodeGenerationHelper.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorkerCodeGenerationHelper.kt @@ -1,6 +1,8 @@ package org.utbot.go.worker import org.utbot.go.api.* +import org.utbot.go.framework.api.go.GoImport +import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.framework.api.go.GoTypeId import org.utbot.go.logic.EachExecutionTimeoutsMillisConfig import org.utbot.go.simplecodegeneration.GoFileCodeBuilder @@ -10,49 +12,65 @@ internal object GoWorkerCodeGenerationHelper { const val workerTestFunctionName = "TestGoFileFuzzedFunctionsByUtGoWorker" - private val alwaysRequiredImports = setOf( - "io", - "context", - "encoding/json", - "errors", - "fmt", - "log", - "math", - "net", - "reflect", - "strconv", - "strings", - "testing", - "time", - "unsafe" - ) + val alwaysRequiredImports = setOf( + GoPackage("io", "io"), + GoPackage("context", "context"), + GoPackage("json", "encoding/json"), + GoPackage("errors", "errors"), + GoPackage("fmt", "fmt"), + GoPackage("log", "log"), + GoPackage("math", "math"), + GoPackage("net", "net"), + GoPackage("reflect", "reflect"), + GoPackage("strconv", "strconv"), + GoPackage("strings", "strings"), + GoPackage("testing", "testing"), + GoPackage("time", "time"), + GoPackage("unsafe", "unsafe") + ).map { GoImport(it, "") }.toSet() fun createFileToExecute( sourceFile: GoUtFile, function: GoUtFunction, eachExecutionTimeoutsMillisConfig: EachExecutionTimeoutsMillisConfig, - port: Int + port: Int, + aliases: Map ): File { val fileToExecuteName = createFileToExecuteName(sourceFile) val sourceFileDir = File(sourceFile.absoluteDirectoryPath) val fileToExecute = sourceFileDir.resolve(fileToExecuteName) - val fileToExecuteGoCode = generateWorkerTestFileGoCode( - sourceFile, function, eachExecutionTimeoutsMillisConfig, port - ) + val fileToExecuteGoCode = + generateWorkerTestFileGoCode(function, eachExecutionTimeoutsMillisConfig, port, aliases) fileToExecute.writeText(fileToExecuteGoCode) return fileToExecute } + fun createFileWithModifiedFunction( + function: GoUtFunction, + sourceFileDir: File + ): File { + val fileWithModifiedFunctionName = createFileWithModifiedFunctionName() + val fileWithModifiedFunction = sourceFileDir.resolve(fileWithModifiedFunctionName) + + val fileWithModifiedFunctionGoCode = generateFileWithModifiedFunctionGoCode(function) + fileWithModifiedFunction.writeText(fileWithModifiedFunctionGoCode) + return fileWithModifiedFunction + } + private fun createFileToExecuteName(sourceFile: GoUtFile): String { return "utbot_go_worker_${sourceFile.fileNameWithoutExtension}_test.go" } + private fun createFileWithModifiedFunctionName(): String { + return "utbot_go_modified_function.go" + } + private fun generateWorkerTestFileGoCode( - sourceFile: GoUtFile, function: GoUtFunction, eachExecutionTimeoutsMillisConfig: EachExecutionTimeoutsMillisConfig, - port: Int + port: Int, + aliases: Map ): String { fun GoTypeId.getAllStructTypes(): Set = when (this) { is GoStructTypeId -> fields.fold(setOf(this)) { acc: Set, field -> @@ -67,22 +85,29 @@ internal object GoWorkerCodeGenerationHelper { function.parameters.fold(emptySet()) { acc: Set, functionParameter: GoUtFunctionParameter -> acc + functionParameter.type.getAllStructTypes() } - val imports = structTypes.fold(emptySet()) { acc: Set, goStructTypeId -> - if (goStructTypeId.packageName != sourceFile.packageName) acc + goStructTypeId.packagePath else acc - } - val fileCodeBuilder = GoFileCodeBuilder(sourceFile.packageName, alwaysRequiredImports + imports) + + val fileCodeBuilder = GoFileCodeBuilder( + function.sourceFile.sourcePackage, alwaysRequiredImports + structTypes.map { + GoImport(it.sourcePackage, aliases[it.sourcePackage] ?: "") + } + ) val workerTestFunctionCode = generateWorkerTestFunctionCode(function, eachExecutionTimeoutsMillisConfig, port) - val modifiedFunction = function.modifiedFunctionForCollectingTraces fileCodeBuilder.addTopLevelElements( GoCodeTemplates.getTopLevelHelperStructsAndFunctionsForWorker( - structTypes, sourceFile.packageName - ) + modifiedFunction + workerTestFunctionCode + structTypes, function.sourceFile.sourcePackage, aliases + ) + workerTestFunctionCode ) return fileCodeBuilder.buildCodeString() } + private fun generateFileWithModifiedFunctionGoCode(function: GoUtFunction): String { + val fileCodeBuilder = GoFileCodeBuilder(function.sourcePackage, function.requiredImports.toSet()) + fileCodeBuilder.addTopLevelElements(listOf(GoCodeTemplates.traces) + function.modifiedFunctionForCollectingTraces) + return fileCodeBuilder.buildCodeString() + } + private fun generateWorkerTestFunctionCode( function: GoUtFunction, eachExecutionTimeoutsMillisConfig: EachExecutionTimeoutsMillisConfig, port: Int ): String { diff --git a/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt b/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt index 068ee06499..cd4365b6df 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt @@ -4,6 +4,7 @@ import com.beust.klaxon.TypeAdapter import com.beust.klaxon.TypeFor import org.utbot.go.api.* import org.utbot.go.api.util.* +import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.framework.api.go.GoTypeId import org.utbot.go.framework.api.go.GoUtFieldModel import org.utbot.go.framework.api.go.GoUtModel @@ -119,7 +120,7 @@ private object RawValuesCodes { } fun convertRawExecutionResultToExecutionResult( - packageName: String, + destinationPackage: GoPackage, rawExecutionResult: RawExecutionResult, functionResultTypes: List, timeoutMillis: Long @@ -159,7 +160,7 @@ fun convertRawExecutionResultToExecutionResult( if (resultType.implementsError && rawResultValue != null) { executedWithNonNilErrorString = true } - createGoUtModelFromRawValue(rawResultValue, resultType, packageName) + createGoUtModelFromRawValue(rawResultValue, resultType, destinationPackage) } return if (executedWithNonNilErrorString) { GoUtExecutionWithNonNilError(resultValues, rawExecutionResult.trace) @@ -169,7 +170,7 @@ fun convertRawExecutionResultToExecutionResult( } private fun createGoUtModelFromRawValue( - rawValue: RawValue?, typeId: GoTypeId, packageName: String + rawValue: RawValue?, typeId: GoTypeId, destinationPackage: GoPackage ): GoUtModel = when (typeId) { // Only for error interface is GoInterfaceTypeId -> if (rawValue == null) { @@ -178,9 +179,9 @@ private fun createGoUtModelFromRawValue( GoUtPrimitiveModel((rawValue as PrimitiveValue).value, goStringTypeId) } - is GoStructTypeId -> createGoUtStructModelFromRawValue(rawValue as StructValue, typeId, packageName) + is GoStructTypeId -> createGoUtStructModelFromRawValue(rawValue as StructValue, typeId, destinationPackage) - is GoArrayTypeId -> createGoUtArrayModelFromRawValue(rawValue as ArrayValue, typeId, packageName) + is GoArrayTypeId -> createGoUtArrayModelFromRawValue(rawValue as ArrayValue, typeId, destinationPackage) is GoPrimitiveTypeId -> createGoUtPrimitiveModelFromRawValue(rawValue as PrimitiveValue, typeId) @@ -237,19 +238,19 @@ private fun convertRawFloatValueToGoUtPrimitiveModel( } private fun createGoUtStructModelFromRawValue( - resultValue: StructValue, resultTypeId: GoStructTypeId, packageName: String + resultValue: StructValue, resultTypeId: GoStructTypeId, destinationPackage: GoPackage ): GoUtStructModel { val value = resultValue.value.zip(resultTypeId.fields).map { (value, fieldId) -> - GoUtFieldModel(createGoUtModelFromRawValue(value.value, fieldId.declaringType, packageName), fieldId) + GoUtFieldModel(createGoUtModelFromRawValue(value.value, fieldId.declaringType, destinationPackage), fieldId) } - return GoUtStructModel(value, resultTypeId, packageName) + return GoUtStructModel(value, resultTypeId, destinationPackage, "") } private fun createGoUtArrayModelFromRawValue( - resultValue: ArrayValue, resultTypeId: GoArrayTypeId, packageName: String + resultValue: ArrayValue, resultTypeId: GoArrayTypeId, destinationPackage: GoPackage ): GoUtArrayModel { val value = (0 until resultTypeId.length).associateWith { index -> - createGoUtModelFromRawValue(resultValue.value[index], resultTypeId.elementTypeId!!, packageName) + createGoUtModelFromRawValue(resultValue.value[index], resultTypeId.elementTypeId!!, destinationPackage) }.toMutableMap() - return GoUtArrayModel(value, resultTypeId, packageName) + return GoUtArrayModel(value, resultTypeId, destinationPackage) } \ No newline at end of file diff --git a/utbot-go/src/main/resources/go_source_code_analyzer/analysis_results.go b/utbot-go/src/main/resources/go_source_code_analyzer/analysis_results.go index 79bccdc080..3ebe331923 100644 --- a/utbot-go/src/main/resources/go_source_code_analyzer/analysis_results.go +++ b/utbot-go/src/main/resources/go_source_code_analyzer/analysis_results.go @@ -63,6 +63,7 @@ type AnalyzedFunction struct { ModifiedName string `json:"modifiedName"` Parameters []AnalyzedFunctionParameter `json:"parameters"` ResultTypes []AnalyzedType `json:"resultTypes"` + RequiredImports []Import `json:"requiredImports"` ModifiedFunctionForCollectingTraces string `json:"modifiedFunctionForCollectingTraces"` NumberOfAllStatements int `json:"numberOfAllStatements"` position token.Pos @@ -70,7 +71,7 @@ type AnalyzedFunction struct { type AnalysisResult struct { AbsoluteFilePath string `json:"absoluteFilePath"` - PackageName string `json:"packageName"` + SourcePackage Package `json:"sourcePackage"` AnalyzedFunctions []AnalyzedFunction `json:"analyzedFunctions"` NotSupportedFunctionsNames []string `json:"notSupportedFunctionsNames"` NotFoundFunctionsNames []string `json:"notFoundFunctionsNames"` diff --git a/utbot-go/src/main/resources/go_source_code_analyzer/analyzer_core.go b/utbot-go/src/main/resources/go_source_code_analyzer/analyzer_core.go index 3d650c5710..bd8600ea7e 100644 --- a/utbot-go/src/main/resources/go_source_code_analyzer/analyzer_core.go +++ b/utbot-go/src/main/resources/go_source_code_analyzer/analyzer_core.go @@ -169,7 +169,17 @@ func checkIsSupported(signature *types.Signature) bool { return true } -func collectTargetAnalyzedFunctions(fset *token.FileSet, info *types.Info, targetFunctionsNames []string) ( +func createNewFunctionName(funcName string) string { + return "__" + funcName + "__" +} + +func collectTargetAnalyzedFunctions( + fset *token.FileSet, + info *types.Info, + targetFunctionsNames []string, + allImportsInFile map[Import]bool, + sourcePackage Package, +) ( analyzedFunctions []AnalyzedFunction, notSupportedFunctionsNames []string, notFoundFunctionsNames []string, @@ -178,29 +188,34 @@ func collectTargetAnalyzedFunctions(fset *token.FileSet, info *types.Info, targe notSupportedFunctionsNames = []string{} notFoundFunctionsNames = []string{} - selectAll := len(targetFunctionsNames) == 0 foundTargetFunctionsNamesMap := map[string]bool{} for _, functionName := range targetFunctionsNames { foundTargetFunctionsNamesMap[functionName] = false } + var blankImports []Import + for i := range allImportsInFile { + if i.Alias == "_" { + blankImports = append(blankImports, i) + } + } + for ident, obj := range info.Defs { switch typedObj := obj.(type) { case *types.Func: analyzedFunction := AnalyzedFunction{ - Name: typedObj.Name(), - ModifiedName: createNewFunctionName(typedObj.Name()), - Parameters: []AnalyzedFunctionParameter{}, - ResultTypes: []AnalyzedType{}, - position: typedObj.Pos(), + Name: typedObj.Name(), + ModifiedName: createNewFunctionName(typedObj.Name()), + Parameters: []AnalyzedFunctionParameter{}, + ResultTypes: []AnalyzedType{}, + RequiredImports: []Import{}, + position: typedObj.Pos(), } - if !selectAll { - if isFound, ok := foundTargetFunctionsNamesMap[analyzedFunction.Name]; !ok || isFound { - continue - } else { - foundTargetFunctionsNamesMap[analyzedFunction.Name] = true - } + if isFound, ok := foundTargetFunctionsNamesMap[analyzedFunction.Name]; !ok || isFound { + continue + } else { + foundTargetFunctionsNamesMap[analyzedFunction.Name] = true } signature := typedObj.Type().(*types.Signature) @@ -219,7 +234,8 @@ func collectTargetAnalyzedFunctions(fset *token.FileSet, info *types.Info, targe AnalyzedFunctionParameter{ Name: parameter.Name(), Type: parameterType, - }) + }, + ) } } if results := signature.Results(); results != nil { @@ -236,11 +252,19 @@ func collectTargetAnalyzedFunctions(fset *token.FileSet, info *types.Info, targe funcDecl := ident.Obj.Decl.(*ast.FuncDecl) funcDecl.Name = ast.NewIdent(analyzedFunction.ModifiedName) - visitor := Visitor{ - counter: 0, - newFunctionName: analyzedFunction.ModifiedName, + functionModifier := FunctionModifier{lineCounter: 0} + ast.Walk(&functionModifier, funcDecl) + + importsCollector := ImportsCollector{ + info: info, + requiredImports: map[Import]bool{}, + allImportsInFile: allImportsInFile, + sourcePackage: sourcePackage, + } + ast.Walk(&importsCollector, funcDecl) + for _, i := range blankImports { + importsCollector.requiredImports[i] = true } - ast.Walk(&visitor, funcDecl) var modifiedFunction bytes.Buffer cfg := printer.Config{ @@ -251,9 +275,15 @@ func collectTargetAnalyzedFunctions(fset *token.FileSet, info *types.Info, targe err := cfg.Fprint(&modifiedFunction, fset, funcDecl) checkError(err) + for i := range importsCollector.requiredImports { + analyzedFunction.RequiredImports = append(analyzedFunction.RequiredImports, i) + } analyzedFunction.ModifiedFunctionForCollectingTraces = modifiedFunction.String() - analyzedFunction.NumberOfAllStatements = visitor.counter + analyzedFunction.NumberOfAllStatements = functionModifier.lineCounter + analyzedFunctions = append(analyzedFunctions, analyzedFunction) + + fmt.Println(analyzedFunction.RequiredImports) } } diff --git a/utbot-go/src/main/resources/go_source_code_analyzer/cover.go b/utbot-go/src/main/resources/go_source_code_analyzer/function_modifier.go similarity index 84% rename from utbot-go/src/main/resources/go_source_code_analyzer/cover.go rename to utbot-go/src/main/resources/go_source_code_analyzer/function_modifier.go index 0050e2f4af..ee7439252a 100644 --- a/utbot-go/src/main/resources/go_source_code_analyzer/cover.go +++ b/utbot-go/src/main/resources/go_source_code_analyzer/function_modifier.go @@ -6,12 +6,11 @@ import ( "strconv" ) -type Visitor struct { - counter int - newFunctionName string +type FunctionModifier struct { + lineCounter int } -func (v *Visitor) Visit(node ast.Node) ast.Visitor { +func (v *FunctionModifier) Visit(node ast.Node) ast.Visitor { switch n := node.(type) { case *ast.BlockStmt: if n == nil { @@ -103,7 +102,7 @@ func (v *Visitor) Visit(node ast.Node) ast.Visitor { return v } -func (v *Visitor) addLinesWithLoggingInTraceBeforeFirstReturnStatement(stmts []ast.Stmt) []ast.Stmt { +func (v *FunctionModifier) addLinesWithLoggingInTraceBeforeFirstReturnStatement(stmts []ast.Stmt) []ast.Stmt { if len(stmts) == 0 { return []ast.Stmt{v.newLineWithLoggingInTrace()} } @@ -120,8 +119,8 @@ func (v *Visitor) addLinesWithLoggingInTraceBeforeFirstReturnStatement(stmts []a return newList } -func (v *Visitor) newLineWithLoggingInTrace() ast.Stmt { - v.counter++ +func (v *FunctionModifier) newLineWithLoggingInTrace() ast.Stmt { + v.lineCounter++ traces := ast.NewIdent("__traces__") return &ast.AssignStmt{ @@ -130,12 +129,8 @@ func (v *Visitor) newLineWithLoggingInTrace() ast.Stmt { Rhs: []ast.Expr{ &ast.CallExpr{ Fun: ast.NewIdent("append"), - Args: []ast.Expr{traces, ast.NewIdent(strconv.Itoa(v.counter))}, + Args: []ast.Expr{traces, ast.NewIdent(strconv.Itoa(v.lineCounter))}, }, }, } } - -func createNewFunctionName(funcName string) string { - return "__" + funcName + "__" -} diff --git a/utbot-go/src/main/resources/go_source_code_analyzer/imports_collector.go b/utbot-go/src/main/resources/go_source_code_analyzer/imports_collector.go new file mode 100644 index 0000000000..dc1ec1788d --- /dev/null +++ b/utbot-go/src/main/resources/go_source_code_analyzer/imports_collector.go @@ -0,0 +1,74 @@ +package main + +import ( + "fmt" + "go/ast" + "go/types" + "log" +) + +type Package struct { + PackageName string `json:"packageName"` + PackagePath string `json:"packagePath"` +} + +type Import struct { + Package Package `json:"goPackage"` + Alias string `json:"alias"` +} + +type ImportsCollector struct { + info *types.Info + prevPkg *Package // for handling alias "." + requiredImports map[Import]bool + allImportsInFile map[Import]bool + sourcePackage Package +} + +func (i *ImportsCollector) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.Ident: + if obj := i.info.ObjectOf(n); obj != nil && obj.Pkg() != nil { + var typesPkg *types.Package + + switch o := obj.(type) { + case *types.PkgName: + typesPkg = o.Imported() + case *types.Func, *types.TypeName: + if i.prevPkg == nil { + var nextImport = Import{ + Package: Package{ + PackageName: o.Pkg().Name(), + PackagePath: o.Pkg().Path(), + }, + Alias: ".", + } + fmt.Println(obj.Name()) + if _, ok := i.allImportsInFile[nextImport]; ok { + i.requiredImports[nextImport] = true + } else if i.sourcePackage != nextImport.Package { + log.Fatal(fmt.Sprintf("not found import for %s", obj.Name())) + } + } + i.prevPkg = nil + return i + default: + return i + } + + var pkg = Package{ + PackageName: typesPkg.Name(), + PackagePath: typesPkg.Path(), + } + i.prevPkg = &pkg + + var alias = "" + if n.Name != pkg.PackageName { + alias = n.Name + } + var nextImport = Import{Package: pkg, Alias: alias} + i.requiredImports[nextImport] = true + } + } + return i +} diff --git a/utbot-go/src/main/resources/go_source_code_analyzer/main.go b/utbot-go/src/main/resources/go_source_code_analyzer/main.go index c44074294e..e40e067dbb 100644 --- a/utbot-go/src/main/resources/go_source_code_analyzer/main.go +++ b/utbot-go/src/main/resources/go_source_code_analyzer/main.go @@ -38,23 +38,63 @@ func analyzeTarget(target AnalysisTarget) (*AnalysisResult, error) { dir, _ := filepath.Split(target.AbsoluteFilePath) cfg := packages.Config{ - Mode: packages.NeedName | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedDeps | packages.NeedImports, - Dir: dir, + Mode: packages.NeedName | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedDeps | + packages.NeedImports | packages.NeedSyntax | packages.NeedFiles | packages.NeedCompiledGoFiles, + Dir: dir, } - pkgs, err := packages.Load(&cfg, "") + pkgs, err := packages.Load(&cfg, fmt.Sprintf("file=%s", target.AbsoluteFilePath)) if err != nil { return nil, err } for _, pkg := range pkgs { if pkg.Name == packageName { + if len(pkg.CompiledGoFiles) != len(pkg.Syntax) { + return nil, fmt.Errorf("parsing returned nil for some files") + } + index := 0 + for ; index < len(pkg.CompiledGoFiles); index++ { + if pkg.CompiledGoFiles[index] == target.AbsoluteFilePath { + break + } + } + if index == len(pkg.CompiledGoFiles) { + return nil, fmt.Errorf("target file not found in compiled go files") + } + + allImportsInFile := map[Import]bool{} + for _, i := range pkg.Syntax[index].Imports { + packagePath := i.Path.Value[1 : len(i.Path.Value)-1] + pkgName := pkg.Imports[packagePath].Name + p := Package{ + PackageName: pkgName, + PackagePath: packagePath, + } + + alias := "" + if i.Name.String() != "" { + alias = i.Name.String() + } + + allImportsInFile[Import{Package: p, Alias: alias}] = true + } + // collect required info about selected functions analyzedFunctions, notSupportedFunctionsNames, notFoundFunctionsNames := - collectTargetAnalyzedFunctions(pkg.Fset, pkg.TypesInfo, target.TargetFunctionsNames) + collectTargetAnalyzedFunctions( + pkg.Fset, + pkg.TypesInfo, + target.TargetFunctionsNames, + allImportsInFile, + Package{ + PackageName: pkg.Name, + PackagePath: pkg.PkgPath, + }, + ) return &AnalysisResult{ AbsoluteFilePath: target.AbsoluteFilePath, - PackageName: packageName, + SourcePackage: Package{PackageName: packageName, PackagePath: pkg.PkgPath}, AnalyzedFunctions: analyzedFunctions, NotSupportedFunctionsNames: notSupportedFunctionsNames, NotFoundFunctionsNames: notFoundFunctionsNames, From bd9de9b50fd11b522f5b104818c26143266a326a Mon Sep 17 00:00:00 2001 From: Egipti Pavel Date: Wed, 15 Feb 2023 18:46:46 +0300 Subject: [PATCH 2/4] Fix go imports --- utbot-go/go-samples/go.mod | 6 +- utbot-go/go-samples/simple/supported_types.go | 5 + .../simple/supported_types_go_ut_test.go | 6 + .../src/main/kotlin/org/utbot/go/GoEngine.kt | 134 ++++++--------- .../kotlin/org/utbot/go/api/GoTypesApi.kt | 34 ++-- .../kotlin/org/utbot/go/api/GoUtModelsApi.kt | 100 ++++------- .../org/utbot/go/api/util/GoTypesApiUtil.kt | 26 ++- .../utbot/go/api/util/GoUtModelsApiUtil.kt | 40 ++++- .../org/utbot/go/framework/api/go/GoApi.kt | 12 +- .../fuzzer/providers/GoArrayValueProvider.kt | 1 - .../fuzzer/providers/GoStructValueProvider.kt | 8 +- .../org/utbot/go/imports/GoImportsResolver.kt | 56 +++++++ .../AbstractGoUtTestsGenerationController.kt | 3 +- .../GoCodeGenerationUtil.kt | 42 ----- .../simplecodegeneration/GoFileCodeBuilder.kt | 7 +- .../GoTestCasesCodeGenerator.kt | 157 ++++++++++++------ .../GoUtModelToCodeConverter.kt | 85 +++++++++- .../org/utbot/go/worker/GoCodeTemplates.kt | 18 +- .../kotlin/org/utbot/go/worker/GoWorker.kt | 35 +--- .../go/worker/GoWorkerCodeGenerationHelper.kt | 54 +++--- .../utbot/go/worker/RawExecutionResults.kt | 22 ++- .../go_source_code_analyzer/analyzer_core.go | 2 - .../imports_collector.go | 6 +- .../resources/go_source_code_analyzer/main.go | 9 +- 24 files changed, 485 insertions(+), 383 deletions(-) create mode 100644 utbot-go/src/main/kotlin/org/utbot/go/imports/GoImportsResolver.kt delete mode 100644 utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoCodeGenerationUtil.kt diff --git a/utbot-go/go-samples/go.mod b/utbot-go/go-samples/go.mod index 96da2c797d..8f59b4476d 100644 --- a/utbot-go/go-samples/go.mod +++ b/utbot-go/go-samples/go.mod @@ -2,10 +2,12 @@ module go-samples go 1.19 -require github.com/stretchr/testify v1.8.1 +require ( + github.com/pmezard/go-difflib v1.0.0 + github.com/stretchr/testify v1.8.1 +) require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/utbot-go/go-samples/simple/supported_types.go b/utbot-go/go-samples/simple/supported_types.go index 1826169c72..db69c21e7b 100644 --- a/utbot-go/go-samples/simple/supported_types.go +++ b/utbot-go/go-samples/simple/supported_types.go @@ -3,6 +3,7 @@ package simple import ( "errors" "github.com/pmezard/go-difflib/difflib" + dif "github.com/pmezard/go-difflib/difflib" "math" ) @@ -172,3 +173,7 @@ func returnErrorOrNil(n int) error { func ExternalStruct(match difflib.Match, structure Structure) Structure { return structure } + +func ExternalStructWithAlias(match dif.Match) difflib.Match { + return match +} diff --git a/utbot-go/go-samples/simple/supported_types_go_ut_test.go b/utbot-go/go-samples/simple/supported_types_go_ut_test.go index 8612f59590..c8068b7579 100644 --- a/utbot-go/go-samples/simple/supported_types_go_ut_test.go +++ b/utbot-go/go-samples/simple/supported_types_go_ut_test.go @@ -226,3 +226,9 @@ func TestExternalStructByUtGoFuzzer(t *testing.T) { assert.Equal(t, Structure{int: -1, int8: 1, int16: -32768, int32: 2147483647, int64: 1, uint: 1, uint8: 1, uint16: 1, uint32: 1, uint64: 18446744073709551615, uintptr: 18446744073709551615, float32: 0.009224832, float64: 0.9644868606768501, complex64: complex(float32(0.009224832), float32(0.009224832)), complex128: complex(0.9644868606768501, 0.9644868606768501), byte: 1, rune: 0, string: "", bool: false}, actualVal) } + +func TestExternalStructWithAliasByUtGoFuzzer(t *testing.T) { + actualVal := ExternalStructWithAlias(difflib.Match{A: 9223372036854775807, B: -1, Size: -9223372036854775808}) + + assert.Equal(t, difflib.Match{A: 9223372036854775807, B: -1, Size: -9223372036854775808}, actualVal) +} \ No newline at end of file diff --git a/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt b/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt index 5015b73b3f..42ce5b8a65 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt @@ -8,8 +8,7 @@ import org.utbot.fuzzing.BaseFeedback import org.utbot.fuzzing.Control import org.utbot.fuzzing.utils.Trie import org.utbot.go.api.* -import org.utbot.go.framework.api.go.GoPackage -import org.utbot.go.framework.api.go.GoTypeId +import org.utbot.go.imports.GoImportsResolver import org.utbot.go.logic.EachExecutionTimeoutsMillisConfig import org.utbot.go.util.executeCommandByNewProcessOrFailWithoutWaiting import org.utbot.go.worker.GoWorker @@ -21,12 +20,11 @@ import java.net.ServerSocket import java.net.SocketException import java.net.SocketTimeoutException import java.util.concurrent.TimeUnit -import kotlin.random.Random val logger = KotlinLogging.logger {} class GoEngine( - private val methodUnderTest: GoUtFunction, + private val functionUnderTest: GoUtFunction, private val sourceFile: GoUtFile, private val goExecutableAbsolutePath: String, private val eachExecutionTimeoutsMillisConfig: EachExecutionTimeoutsMillisConfig, @@ -41,102 +39,79 @@ class GoEngine( var fileToExecute: File? = null var fileWithModifiedFunction: File? = null try { - val random = Random(0) - val aliases = mutableMapOf() - val busyAliases = - GoWorkerCodeGenerationHelper.alwaysRequiredImports.map { it.goPackage.packageName }.toMutableSet() - - fun GoTypeId.getAllStructTypes(): Set = when (this) { - is GoStructTypeId -> fields.fold(setOf(this)) { acc: Set, field -> - acc + (field.declaringType).getAllStructTypes() - } - - is GoArrayTypeId -> elementTypeId!!.getAllStructTypes() - else -> emptySet() - } - - val structTypes = - methodUnderTest.parameters.fold(emptySet()) { acc: Set, functionParameter: GoUtFunctionParameter -> - acc + functionParameter.type.getAllStructTypes() - } - - structTypes.map { it.sourcePackage }.toSet().filter { it != methodUnderTest.sourcePackage } - .forEach { goPackage -> - if (goPackage.packageName !in busyAliases) { - busyAliases += goPackage.packageName - } else { - var suffix = "" - while (goPackage.packageName + suffix in busyAliases) { - suffix = random.nextInt().toString() - } - aliases[goPackage] = goPackage.packageName + suffix - } - } - - println(busyAliases) + // creating files for worker + val types = functionUnderTest.parameters.map { it.type } + val imports = GoImportsResolver.resolveImportsBasedOnTypes( + types, + functionUnderTest.sourcePackage, + GoWorkerCodeGenerationHelper.alwaysRequiredImports + ) fileToExecute = GoWorkerCodeGenerationHelper.createFileToExecute( sourceFile, - methodUnderTest, + functionUnderTest, eachExecutionTimeoutsMillisConfig, serverSocket.localPort, - aliases + imports ) + fileWithModifiedFunction = GoWorkerCodeGenerationHelper.createFileWithModifiedFunction( + sourceFile, functionUnderTest + ) + + // starting worker process val testFunctionName = GoWorkerCodeGenerationHelper.workerTestFunctionName val command = listOf( goExecutableAbsolutePath, "test", "-run", testFunctionName ) val sourceFileDir = File(sourceFile.absoluteDirectoryPath) - - fileWithModifiedFunction = GoWorkerCodeGenerationHelper.createFileWithModifiedFunction( - methodUnderTest, sourceFileDir - ) - val processStartTime = System.currentTimeMillis() val process = executeCommandByNewProcessOrFailWithoutWaiting(command, sourceFileDir) - val workerSocket = try { - serverSocket.soTimeout = timeoutMillis.toInt() - serverSocket.accept() - } catch (e: SocketTimeoutException) { - val processHasExited = process.waitFor(timeoutMillis, TimeUnit.MILLISECONDS) - if (processHasExited) { - val processOutput = InputStreamReader(process.inputStream).readText() - throw TimeoutException("Timeout exceeded: Worker not connected. Process output: $processOutput") - } else { - process.destroy() - } - throw TimeoutException("Timeout exceeded: Worker not connected") - } - logger.debug { "Worker connected - completed in ${System.currentTimeMillis() - processStartTime} ms" } + try { - val worker = GoWorker(workerSocket, methodUnderTest) - if (methodUnderTest.parameters.isEmpty()) { - worker.sendFuzzedParametersValues(listOf(), aliases) + // connecting to worker + logger.debug { "Trying to connect to worker" } + val workerSocket = try { + serverSocket.soTimeout = timeoutMillis.toInt() + serverSocket.accept() + } catch (e: SocketTimeoutException) { + val processHasExited = process.waitFor(timeoutMillis, TimeUnit.MILLISECONDS) + if (processHasExited) { + val processOutput = InputStreamReader(process.inputStream).readText() + throw TimeoutException("Timeout exceeded: Worker not connected. Process output: $processOutput") + } else { + process.destroy() + } + throw TimeoutException("Timeout exceeded: Worker not connected") + } + val worker = GoWorker(workerSocket, functionUnderTest) + logger.debug { "Worker connected - completed in ${System.currentTimeMillis() - processStartTime} ms" } + + // fuzzing + if (functionUnderTest.parameters.isEmpty()) { + worker.sendFuzzedParametersValues(emptyList(), emptyMap()) val rawExecutionResult = worker.receiveRawExecutionResult() val executionResult = convertRawExecutionResultToExecutionResult( - methodUnderTest.sourcePackage, rawExecutionResult, - methodUnderTest.resultTypes, - eachExecutionTimeoutsMillisConfig[methodUnderTest], + functionUnderTest.resultTypes, + eachExecutionTimeoutsMillisConfig[functionUnderTest], ) - val fuzzedFunction = GoUtFuzzedFunction(methodUnderTest, listOf()) + val fuzzedFunction = GoUtFuzzedFunction(functionUnderTest, emptyList()) emit(fuzzedFunction to executionResult) } else { - runGoFuzzing(methodUnderTest) { description, values -> + val aliases = imports.filter { it.alias != null }.associate { it.goPackage to it.alias } + runGoFuzzing(functionUnderTest) { description, values -> if (timeoutExceededOrIsCanceled()) { return@runGoFuzzing BaseFeedback(result = Trie.emptyNode(), control = Control.STOP) } - val fuzzedFunction = GoUtFuzzedFunction(methodUnderTest, values) + val fuzzedFunction = GoUtFuzzedFunction(functionUnderTest, values) worker.sendFuzzedParametersValues(values, aliases) val rawExecutionResult = worker.receiveRawExecutionResult() val executionResult = convertRawExecutionResultToExecutionResult( - methodUnderTest.sourcePackage, rawExecutionResult, - methodUnderTest.resultTypes, - eachExecutionTimeoutsMillisConfig[methodUnderTest], + functionUnderTest.resultTypes, + eachExecutionTimeoutsMillisConfig[functionUnderTest], ) - println(executionResult) if (executionResult.trace.isEmpty()) { - logger.error { "Coverage is empty for [${methodUnderTest.name}] with $values}" } + logger.error { "Coverage is empty for [${functionUnderTest.name}] with $values}" } if (executionResult is GoUtPanicFailure) { logger.error { "Execution completed with panic: ${executionResult.panicValue}" } } @@ -146,8 +121,7 @@ class GoEngine( if (trieNode.count > 1) { if (++attempts >= attemptsLimit) { return@runGoFuzzing BaseFeedback( - result = Trie.emptyNode(), - control = Control.STOP + result = Trie.emptyNode(), control = Control.STOP ) } return@runGoFuzzing BaseFeedback(result = trieNode, control = Control.CONTINUE) @@ -166,9 +140,9 @@ class GoEngine( val processOutput = InputStreamReader(process.inputStream).readText() throw RuntimeException( StringBuilder() - .append("Execution of ${"function [${methodUnderTest.name}] from $sourceFile"} in child process failed with non-zero exit code = $exitCode: ") - .append("\n$processOutput") - .toString() + .append("Execution of ${"function [${functionUnderTest.name}] from $sourceFile"} in child process failed with non-zero exit code = $exitCode: ") + .appendLine() + .append(processOutput).toString() ) } } @@ -183,9 +157,9 @@ class GoEngine( val processOutput = InputStreamReader(process.inputStream).readText() throw RuntimeException( StringBuilder() - .append("Execution of ${"function [${methodUnderTest.name}] from $sourceFile"} in child process failed with non-zero exit code = $exitCode: ") - .append("\n$processOutput") - .toString() + .append("Execution of ${"function [${functionUnderTest.name}] from $sourceFile"} in child process failed with non-zero exit code = $exitCode: ") + .appendLine() + .append(processOutput).toString() ) } } diff --git a/utbot-go/src/main/kotlin/org/utbot/go/api/GoTypesApi.kt b/utbot-go/src/main/kotlin/org/utbot/go/api/GoTypesApi.kt index 0b10b22121..fc7247bcb0 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/api/GoTypesApi.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/api/GoTypesApi.kt @@ -10,7 +10,7 @@ import org.utbot.go.framework.api.go.GoTypeId class GoPrimitiveTypeId(name: String) : GoTypeId(name) { override val canonicalName: String = simpleName - override fun getRelativeName(goPackage: GoPackage, alias: String): String = simpleName + override fun getRelativeName(destinationPackage: GoPackage, aliases: Map): String = simpleName override fun equals(other: Any?): Boolean { if (this === other) return true @@ -32,17 +32,14 @@ class GoStructTypeId( val packagePath: String = sourcePackage.packagePath override val canonicalName: String = "${sourcePackage.packageName}.$name" - override fun getRelativeName(goPackage: GoPackage, alias: String): String = if (alias == "") { - if (sourcePackage == goPackage) { + override fun getRelativeName(destinationPackage: GoPackage, aliases: Map): String { + val alias = aliases[sourcePackage] + return if (sourcePackage == destinationPackage || alias == ".") { simpleName + } else if (alias == null) { + "${packageName}.${simpleName}" } else { - "$packageName.$simpleName" - } - } else { - when (alias) { - "." -> simpleName - "_" -> error("not supported") - else -> "$alias.$simpleName" + "${alias}.${simpleName}" } } @@ -66,8 +63,8 @@ class GoArrayTypeId( ) : GoTypeId(name, elementTypeId = elementTypeId) { override val canonicalName: String = "[$length]${elementTypeId.canonicalName}" - override fun getRelativeName(goPackage: GoPackage, alias: String): String = - "[$length]${elementTypeId!!.getRelativeName(goPackage, alias)}" + override fun getRelativeName(destinationPackage: GoPackage, aliases: Map): String = + "[$length]${elementTypeId!!.getRelativeName(destinationPackage, aliases)}" override fun equals(other: Any?): Boolean { if (this === other) return true @@ -92,10 +89,15 @@ class GoInterfaceTypeId( simpleName } - override fun getRelativeName(goPackage: GoPackage, alias: String): String = if (this.packageName != packageName) { - canonicalName - } else { - simpleName + override fun getRelativeName(destinationPackage: GoPackage, aliases: Map): String { + val alias = aliases[sourcePackage] + return if (sourcePackage == destinationPackage || alias == ".") { + simpleName + } else if (alias == null) { + "${packageName}.${simpleName}" + } else { + "${alias}.${simpleName}" + } } override fun equals(other: Any?): Boolean { diff --git a/utbot-go/src/main/kotlin/org/utbot/go/api/GoUtModelsApi.kt b/utbot-go/src/main/kotlin/org/utbot/go/api/GoUtModelsApi.kt index 8fac10377b..71c3691e62 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/api/GoUtModelsApi.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/api/GoUtModelsApi.kt @@ -6,7 +6,10 @@ import org.utbot.go.api.util.goDefaultValueModel import org.utbot.go.api.util.goFloat64TypeId import org.utbot.go.api.util.goStringTypeId import org.utbot.go.api.util.neverRequiresExplicitCast -import org.utbot.go.framework.api.go.* +import org.utbot.go.framework.api.go.GoPackage +import org.utbot.go.framework.api.go.GoTypeId +import org.utbot.go.framework.api.go.GoUtFieldModel +import org.utbot.go.framework.api.go.GoUtModel // NEVER and DEPENDS difference is useful in code generation of assert.Equals(...). enum class ExplicitCastMode { @@ -16,112 +19,73 @@ enum class ExplicitCastMode { open class GoUtPrimitiveModel( val value: Any, typeId: GoPrimitiveTypeId, - val explicitCastMode: ExplicitCastMode = - if (typeId.neverRequiresExplicitCast) { - ExplicitCastMode.NEVER - } else { - ExplicitCastMode.DEPENDS - }, + val explicitCastMode: ExplicitCastMode = if (typeId.neverRequiresExplicitCast) { + ExplicitCastMode.NEVER + } else { + ExplicitCastMode.DEPENDS + }, private val requiredPackages: Set = emptySet(), ) : GoUtModel(typeId) { override val typeId: GoPrimitiveTypeId get() = super.typeId as GoPrimitiveTypeId - override fun getRequiredPackages(): Set = requiredPackages + override fun getRequiredPackages(destinationPackage: GoPackage): Set = requiredPackages override fun isComparable(): Boolean = true - override fun toString(): String = when (explicitCastMode) { - ExplicitCastMode.REQUIRED -> toCastedValueGoCode() - ExplicitCastMode.DEPENDS, ExplicitCastMode.NEVER -> toValueGoCode() - } - - open fun toValueGoCode(): String = if (typeId == goStringTypeId) "\"$value\"" else "$value" - fun toCastedValueGoCode(): String = "$typeId(${toValueGoCode()})" + override fun toString(): String = if (typeId == goStringTypeId) "\"${value}\"" else "$value" } -abstract class GoUtCompositeModel( - typeId: GoTypeId, - val destinationPackage: GoPackage, -) : GoUtModel(typeId) - class GoUtStructModel( val value: List, typeId: GoStructTypeId, - destinationPackage: GoPackage, - private val alias: String, -) : GoUtCompositeModel(typeId, destinationPackage) { +) : GoUtModel(typeId) { override val typeId: GoStructTypeId get() = super.typeId as GoStructTypeId - // TODO delete this method because it seems like all fields are visible - private fun getVisibleFields(): List = - value.filter { destinationPackage == typeId.sourcePackage || it.fieldId.isExported } + fun getVisibleFields(destinationPackage: GoPackage): List = + value.filter { typeId.sourcePackage == destinationPackage || it.fieldId.isExported } - override fun getRequiredPackages(): Set = getVisibleFields() - .fold(setOf(typeId.sourcePackage)) { acc, fieldModel -> acc + fieldModel.getRequiredPackages() } + override fun getRequiredPackages(destinationPackage: GoPackage): Set = + getVisibleFields(destinationPackage).fold(setOf(typeId.sourcePackage)) { acc, fieldModel -> + acc + fieldModel.getRequiredPackages(destinationPackage) + } override fun isComparable(): Boolean = value.all { it.isComparable() } - fun toStringWithoutStructName(): String = getVisibleFields() - .joinToString(prefix = "{", postfix = "}") { "${it.fieldId.name}: ${it.model}" } - - override fun toString(): String = - "${typeId.getRelativeName(destinationPackage, alias)}${toStringWithoutStructName()}" + override fun toString(): String = value.joinToString(prefix = "$typeId{", postfix = "}") { + "${it.fieldId.name}: ${it.model}" + } } class GoUtArrayModel( val value: MutableMap, typeId: GoArrayTypeId, - destinationPackage: GoPackage, -) : GoUtCompositeModel(typeId, destinationPackage) { +) : GoUtModel(typeId) { val length: Int = typeId.length override val typeId: GoArrayTypeId get() = super.typeId as GoArrayTypeId - override fun getRequiredPackages(): Set { + override fun getRequiredPackages(destinationPackage: GoPackage): Set { val elementStructTypeId = typeId.elementTypeId as? GoStructTypeId val imports = if (elementStructTypeId != null && elementStructTypeId.sourcePackage != destinationPackage) { mutableSetOf(elementStructTypeId.sourcePackage) } else { mutableSetOf() } - value.values.map { it.getRequiredPackages() }.forEach { imports += it } + value.values.map { it.getRequiredPackages(destinationPackage) }.forEach { imports += it } return imports } override fun isComparable(): Boolean = value.values.all { it.isComparable() } - fun getElements(typeId: GoTypeId): List = (0 until length).map { - value[it] ?: typeId.goDefaultValueModel(destinationPackage) + fun getElements(): List = (0 until length).map { + value[it] ?: typeId.elementTypeId!!.goDefaultValueModel() } - fun toStringWithoutTypeName(): String = when (val typeId = typeId.elementTypeId!!) { - is GoStructTypeId -> getElements(typeId).joinToString(prefix = "{", postfix = "}") { - (it as GoUtStructModel).toStringWithoutStructName() - } - - is GoArrayTypeId -> getElements(typeId).joinToString(prefix = "{", postfix = "}") { - (it as GoUtArrayModel).toStringWithoutTypeName() - } - - else -> getElements(typeId).joinToString(prefix = "{", postfix = "}") - } - - override fun toString(): String = when (val typeId = typeId.elementTypeId!!) { - is GoStructTypeId -> getElements(typeId) - .joinToString(prefix = "[$length]${typeId.getRelativeName(destinationPackage)}{", postfix = "}") { - (it as GoUtStructModel).toStringWithoutStructName() - } - - is GoArrayTypeId -> getElements(typeId) - .joinToString(prefix = "[$length]${typeId.getRelativeName(destinationPackage)}{", postfix = "}") { - (it as GoUtArrayModel).toStringWithoutTypeName() - } - - else -> getElements(typeId) - .joinToString(prefix = "[$length]${typeId.getRelativeName(destinationPackage)}{", postfix = "}") + override fun toString(): String = getElements().joinToString(prefix = "$typeId{", postfix = "}") { + it.toString() } } @@ -141,8 +105,7 @@ class GoUtFloatNaNModel( } class GoUtFloatInfModel( - val sign: Int, - typeId: GoPrimitiveTypeId + val sign: Int, typeId: GoPrimitiveTypeId ) : GoUtPrimitiveModel( "math.Inf($sign)", typeId, @@ -161,11 +124,12 @@ class GoUtComplexModel( ) : GoUtPrimitiveModel( "complex($realValue, $imagValue)", typeId, - requiredPackages = realValue.getRequiredPackages() + imagValue.getRequiredPackages(), explicitCastMode = ExplicitCastMode.NEVER ) { + override fun getRequiredPackages(destinationPackage: GoPackage): Set = + realValue.getRequiredPackages(destinationPackage) + imagValue.getRequiredPackages(destinationPackage) + override fun isComparable(): Boolean = realValue.isComparable() && imagValue.isComparable() - override fun toValueGoCode(): String = "complex($realValue, $imagValue)" } class GoUtNilModel( diff --git a/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoTypesApiUtil.kt b/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoTypesApiUtil.kt index 173681c2ae..7427a294e9 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoTypesApiUtil.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoTypesApiUtil.kt @@ -1,7 +1,6 @@ package org.utbot.go.api.util import org.utbot.go.api.* -import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.framework.api.go.GoTypeId import org.utbot.go.framework.api.go.GoUtModel import kotlin.reflect.KClass @@ -93,7 +92,7 @@ val GoPrimitiveTypeId.correspondingKClass: KClass else -> String::class // default way to hold GoUtPrimitiveModel's value is to use String } -fun GoTypeId.goDefaultValueModel(destinationPackage: GoPackage): GoUtModel = when (this) { +fun GoTypeId.goDefaultValueModel(): GoUtModel = when (this) { is GoPrimitiveTypeId -> when (this) { goBoolTypeId -> GoUtPrimitiveModel(false, this) goRuneTypeId, goIntTypeId, goInt8TypeId, goInt16TypeId, goInt32TypeId, goInt64TypeId -> GoUtPrimitiveModel( @@ -108,8 +107,8 @@ fun GoTypeId.goDefaultValueModel(destinationPackage: GoPackage): GoUtModel = whe goFloat32TypeId, goFloat64TypeId -> GoUtPrimitiveModel(0.0, this) goComplex64TypeId, goComplex128TypeId -> GoUtComplexModel( - goFloat64TypeId.goDefaultValueModel(destinationPackage) as GoUtPrimitiveModel, - goFloat64TypeId.goDefaultValueModel(destinationPackage) as GoUtPrimitiveModel, + goFloat64TypeId.goDefaultValueModel() as GoUtPrimitiveModel, + goFloat64TypeId.goDefaultValueModel() as GoUtPrimitiveModel, this ) @@ -119,7 +118,20 @@ fun GoTypeId.goDefaultValueModel(destinationPackage: GoPackage): GoUtModel = whe else -> error("Go primitive ${this.javaClass} is not supported") } - is GoStructTypeId -> GoUtStructModel(listOf(), this, destinationPackage, "") - is GoArrayTypeId -> GoUtArrayModel(hashMapOf(), this, destinationPackage) + is GoStructTypeId -> GoUtStructModel(listOf(), this) + is GoArrayTypeId -> GoUtArrayModel(hashMapOf(), this) else -> GoUtNilModel(this) -} \ No newline at end of file +} + +fun GoTypeId.getAllStructTypes(): Set = when (this) { + is GoStructTypeId -> fields.fold(setOf(this)) { acc: Set, field -> + acc + (field.declaringType).getAllStructTypes() + } + + is GoArrayTypeId -> elementTypeId!!.getAllStructTypes() + else -> emptySet() +} + +fun List.getAllStructTypes(): Set = this.fold(emptySet()) { acc, type -> + acc + type.getAllStructTypes() +} diff --git a/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoUtModelsApiUtil.kt b/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoUtModelsApiUtil.kt index 602fb24c01..556c4edd81 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoUtModelsApiUtil.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoUtModelsApiUtil.kt @@ -1,9 +1,12 @@ package org.utbot.go.api.util -import org.utbot.go.api.GoUtComplexModel -import org.utbot.go.api.GoUtFloatInfModel -import org.utbot.go.api.GoUtFloatNaNModel +import org.utbot.go.api.* +import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.framework.api.go.GoUtModel +import org.utbot.go.worker.ArrayValue +import org.utbot.go.worker.PrimitiveValue +import org.utbot.go.worker.RawValue +import org.utbot.go.worker.StructValue fun GoUtModel.isNaNOrInf(): Boolean = this is GoUtFloatNaNModel || this is GoUtFloatInfModel @@ -13,4 +16,33 @@ fun GoUtModel.doesNotContainNaNOrInf(): Boolean { return !(asComplexModel.realValue.isNaNOrInf() || asComplexModel.imagValue.isNaNOrInf()) } -fun GoUtModel.containsNaNOrInf(): Boolean = !this.doesNotContainNaNOrInf() \ No newline at end of file +fun GoUtModel.containsNaNOrInf(): Boolean = !this.doesNotContainNaNOrInf() + +fun GoUtModel.convertToRawValue(destinationPackage: GoPackage, aliases: Map): RawValue = when (val model = this) { + is GoUtComplexModel -> PrimitiveValue( + model.typeId.getRelativeName(destinationPackage, aliases), + "${model.realValue}@${model.imagValue}" + ) + + is GoUtArrayModel -> ArrayValue( + model.typeId.getRelativeName(destinationPackage, aliases), + model.typeId.elementTypeId!!.getRelativeName(destinationPackage, aliases), + model.length, + model.getElements().map { it.convertToRawValue(destinationPackage, aliases) } + ) + + is GoUtStructModel -> StructValue( + model.typeId.getRelativeName(destinationPackage, aliases), + model.value.map { + StructValue.FieldValue( + it.fieldId.name, + it.model.convertToRawValue(destinationPackage, aliases), + it.fieldId.isExported + ) + } + ) + + is GoUtPrimitiveModel -> PrimitiveValue(model.typeId.name, model.value.toString()) + + else -> error("Converting ${model.javaClass} to RawValue is not supported") +} \ No newline at end of file diff --git a/utbot-go/src/main/kotlin/org/utbot/go/framework/api/go/GoApi.kt b/utbot-go/src/main/kotlin/org/utbot/go/framework/api/go/GoApi.kt index 86b5c3550e..c2f0ed3ce2 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/framework/api/go/GoApi.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/framework/api/go/GoApi.kt @@ -14,7 +14,7 @@ abstract class GoTypeId( val simpleName: String = name abstract val canonicalName: String - abstract fun getRelativeName(goPackage: GoPackage, alias: String = ""): String + abstract fun getRelativeName(destinationPackage: GoPackage, aliases: Map): String override fun toString(): String = canonicalName } @@ -26,7 +26,7 @@ abstract class GoTypeId( abstract class GoUtModel( open val typeId: GoTypeId, ) { - open fun getRequiredPackages(): Set = emptySet() + open fun getRequiredPackages(destinationPackage: GoPackage): Set = emptySet() abstract fun isComparable(): Boolean } @@ -37,7 +37,9 @@ class GoUtFieldModel( val model: GoUtModel, val fieldId: GoFieldId, ) : GoUtModel(fieldId.declaringType) { - override fun getRequiredPackages(): Set = model.getRequiredPackages() + override fun getRequiredPackages(destinationPackage: GoPackage): Set = + model.getRequiredPackages(destinationPackage) + override fun isComparable(): Boolean = model.isComparable() } @@ -63,10 +65,10 @@ data class GoPackage( */ data class GoImport( val goPackage: GoPackage, - val alias: String + val alias: String? = null ) { override fun toString(): String { - if (alias == "") { + if (alias == null) { return "\"${goPackage.packagePath}\"" } return "$alias \"${goPackage.packagePath}\"" diff --git a/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoArrayValueProvider.kt b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoArrayValueProvider.kt index 5c4add445c..ccc7317cde 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoArrayValueProvider.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoArrayValueProvider.kt @@ -21,7 +21,6 @@ object GoArrayValueProvider : ValueProvider GoUtArrayModel( value = hashMapOf(), typeId = arrayType, - destinationPackage = description.methodUnderTest.sourcePackage ) }, modify = Routine.ForEach(listOf(arrayType.elementTypeId!!)) { self, i, values -> diff --git a/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoStructValueProvider.kt b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoStructValueProvider.kt index 9d1805d56c..229f6e4a25 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoStructValueProvider.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoStructValueProvider.kt @@ -17,9 +17,9 @@ object GoStructValueProvider : ValueProvider sequence { type.let { it as GoStructTypeId }.also { structType -> val function = description.methodUnderTest - val packageName = function.getPackageName() + val destinationPackage = function.sourcePackage val fields = structType.fields - .filter { structType.packageName == packageName || it.isExported } + .filter { structType.sourcePackage == destinationPackage || it.isExported } yield(Seed.Recursive( construct = Routine.Create(fields.map { it.declaringType }) { values -> GoUtStructModel( @@ -27,8 +27,6 @@ object GoStructValueProvider : ValueProvider GoUtFieldModel(value, field) }, typeId = structType, - destinationPackage = function.sourcePackage, - alias = function.requiredImports.first { it.goPackage == structType.sourcePackage }.alias ) }, modify = sequence { @@ -44,8 +42,6 @@ object GoStructValueProvider : ValueProvider GoUtStructModel( value = emptyList(), typeId = structType, - destinationPackage = function.sourcePackage, - alias = function.requiredImports.first { it.goPackage == structType.sourcePackage }.alias ) } )) diff --git a/utbot-go/src/main/kotlin/org/utbot/go/imports/GoImportsResolver.kt b/utbot-go/src/main/kotlin/org/utbot/go/imports/GoImportsResolver.kt new file mode 100644 index 0000000000..9fff1c5edc --- /dev/null +++ b/utbot-go/src/main/kotlin/org/utbot/go/imports/GoImportsResolver.kt @@ -0,0 +1,56 @@ +package org.utbot.go.imports + +import org.utbot.go.api.util.getAllStructTypes +import org.utbot.go.framework.api.go.GoImport +import org.utbot.go.framework.api.go.GoPackage +import org.utbot.go.framework.api.go.GoTypeId + +object GoImportsResolver { + + fun resolveImportsBasedOnTypes( + types: List, + sourcePackage: GoPackage, + busyImports: Set = emptySet() + ): Set { + val structTypes = types.getAllStructTypes() + val result = busyImports.toMutableSet() + val busyAliases = busyImports.map { it.alias ?: it.goPackage.packageName }.toMutableSet() + structTypes.map { it.sourcePackage }.distinct().filter { it != sourcePackage }.forEach { goPackage -> + val alias = if (goPackage.packageName in busyAliases) { + var n = 1 + while (goPackage.packageName + n in busyAliases) { + n++ + } + goPackage.packageName + n + } else { + null + } + busyAliases += alias ?: goPackage.packageName + result += GoImport(goPackage, alias) + } + return result + } + + fun resolveImportsBasedOnRequiredPackages( + requiredPackages: Set, + sourcePackage: GoPackage, + busyImports: Set = emptySet() + ): Set { + val result = busyImports.toMutableSet() + val busyAliases = busyImports.map { it.alias ?: it.goPackage.packageName }.toMutableSet() + requiredPackages.distinct().filter { it != sourcePackage }.forEach { goPackage -> + val alias = if (goPackage.packageName in busyAliases) { + var n = 1 + while (goPackage.packageName + n in busyAliases) { + n++ + } + goPackage.packageName + n + } else { + null + } + busyAliases += alias ?: goPackage.packageName + result += GoImport(goPackage, alias) + } + return result + } +} \ No newline at end of file diff --git a/utbot-go/src/main/kotlin/org/utbot/go/logic/AbstractGoUtTestsGenerationController.kt b/utbot-go/src/main/kotlin/org/utbot/go/logic/AbstractGoUtTestsGenerationController.kt index 96a6350774..3bc3b866fd 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/logic/AbstractGoUtTestsGenerationController.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/logic/AbstractGoUtTestsGenerationController.kt @@ -42,8 +42,7 @@ abstract class AbstractGoUtTestsGenerationController { testCasesBySourceFiles.forEach { (sourceFile, testCases) -> if (!onTestCasesFileCodeGenerationStart(sourceFile, testCases)) return - val generatedTestsFileCode = - GoTestCasesCodeGenerator.generateTestCasesFileCode(sourceFile, testCases, mapOf()) + val generatedTestsFileCode = GoTestCasesCodeGenerator.generateTestCasesFileCode(sourceFile, testCases) if (!onTestCasesFileCodeGenerationFinished(sourceFile, generatedTestsFileCode)) return } } diff --git a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoCodeGenerationUtil.kt b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoCodeGenerationUtil.kt deleted file mode 100644 index 8042203c6c..0000000000 --- a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoCodeGenerationUtil.kt +++ /dev/null @@ -1,42 +0,0 @@ -package org.utbot.go.simplecodegeneration - -import org.utbot.go.api.ExplicitCastMode -import org.utbot.go.api.GoPrimitiveTypeId -import org.utbot.go.api.GoUtFuzzedFunction -import org.utbot.go.api.GoUtPrimitiveModel -import org.utbot.go.framework.api.go.GoUtModel - - -fun generateFuzzedFunctionCall(functionName: String, parameters: List): String { - val fuzzedParametersToString = parameters.joinToString(prefix = "(", postfix = ")") { it.toString() } - return "$functionName$fuzzedParametersToString" -} - -fun generateVariablesDeclarationTo(variablesNames: List, expression: String): String { - val variables = variablesNames.joinToString() - return "$variables := $expression" -} - -fun generateFuzzedFunctionCallSavedToVariables( - variablesNames: List, - fuzzedFunction: GoUtFuzzedFunction -): String = generateVariablesDeclarationTo( - variablesNames, - generateFuzzedFunctionCall(fuzzedFunction.function.name, fuzzedFunction.parametersValues) -) - -fun generateCastIfNeed(toTypeId: GoPrimitiveTypeId, expressionType: GoPrimitiveTypeId, expression: String): String { - return if (expressionType != toTypeId) { - "${toTypeId.name}($expression)" - } else { - expression - } -} - -fun generateCastedValueIfPossible(model: GoUtPrimitiveModel): String { - return if (model.explicitCastMode == ExplicitCastMode.NEVER) { - model.toValueGoCode() - } else { - model.toCastedValueGoCode() - } -} \ No newline at end of file diff --git a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoFileCodeBuilder.kt b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoFileCodeBuilder.kt index 9a7a00cb65..d4040c713e 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoFileCodeBuilder.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoFileCodeBuilder.kt @@ -17,9 +17,10 @@ class GoFileCodeBuilder( return "import ${imports.first()}" } - return imports.sortedBy { it.toString() }.joinToString(separator = "", prefix = "import (\n", postfix = ")") { - "\t$it\n" - } + return imports.sortedWith(compareBy { it.goPackage.packagePath }.thenBy { it.alias }) + .joinToString(separator = "", prefix = "import (\n", postfix = ")") { + "\t$it\n" + } } fun buildCodeString(): String { diff --git a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoTestCasesCodeGenerator.kt b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoTestCasesCodeGenerator.kt index 09fcb63951..59ac5d665d 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoTestCasesCodeGenerator.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoTestCasesCodeGenerator.kt @@ -8,45 +8,48 @@ import org.utbot.go.api.util.goStringTypeId import org.utbot.go.framework.api.go.GoImport import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.framework.api.go.GoUtModel +import org.utbot.go.imports.GoImportsResolver object GoTestCasesCodeGenerator { - fun generateTestCasesFileCode( - sourceFile: GoUtFile, - testCases: List, - aliases: Map - ): String { - val imports = if (testCases.isNotEmpty()) { - mutableSetOf( - GoImport(GoPackage("assert", "github.com/stretchr/testify/assert"), ""), - GoImport(GoPackage("testing", "testing"), "") - ) - } else { - mutableSetOf() - } - testCases.forEach { testCase -> - when (val executionResult = testCase.executionResult) { - is GoUtExecutionCompleted -> { - executionResult.models.forEach { - imports += it.getRequiredPackages().map { pkg -> GoImport(pkg, aliases[pkg] ?: "") } - } + private val alwaysRequiredImports = setOf( + GoImport(GoPackage("assert", "github.com/stretchr/testify/assert")), GoImport(GoPackage("testing", "testing")) + ) + + fun generateTestCasesFileCode(sourceFile: GoUtFile, testCases: List): String { + val destinationPackage = sourceFile.sourcePackage + val requiredPackages = mutableSetOf() + if (testCases.isNotEmpty()) { + testCases.forEach { testCase -> + testCase.parametersValues.forEach { + requiredPackages += it.getRequiredPackages(destinationPackage) } + when (val executionResult = testCase.executionResult) { + is GoUtExecutionCompleted -> executionResult.models.forEach { + requiredPackages += it.getRequiredPackages(destinationPackage) + } - is GoUtPanicFailure -> { - imports += executionResult.panicValue.getRequiredPackages() - .map { pkg -> GoImport(pkg, aliases[pkg] ?: "") } + is GoUtPanicFailure -> requiredPackages += executionResult.panicValue.getRequiredPackages( + destinationPackage + ) } } } - val fileBuilder = GoFileCodeBuilder(sourceFile.sourcePackage, imports) + val imports = GoImportsResolver.resolveImportsBasedOnRequiredPackages( + requiredPackages, destinationPackage, alwaysRequiredImports + ) + val fileBuilder = GoFileCodeBuilder(destinationPackage, imports) + val aliases = imports.associate { (goPackage, alias) -> goPackage to alias } + val goUtModelToCodeConverter = GoUtModelToCodeConverter(destinationPackage, aliases) fun List.generateTestFunctions( - generateTestFunctionForTestCase: (GoUtFuzzedFunctionTestCase, Int?) -> String + generateTestFunctionForTestCase: (GoUtFuzzedFunctionTestCase, Int?, GoUtModelToCodeConverter) -> String, ) { this.forEachIndexed { testIndex, testCase -> val testIndexToShow = if (this.size == 1) null else testIndex + 1 - val testFunctionCode = generateTestFunctionForTestCase(testCase, testIndexToShow) + val testFunctionCode = + generateTestFunctionForTestCase(testCase, testIndexToShow, goUtModelToCodeConverter) fileBuilder.addTopLevelElements(testFunctionCode) } } @@ -64,7 +67,7 @@ object GoTestCasesCodeGenerator { } private fun generateTestFunctionForCompletedExecutionTestCase( - testCase: GoUtFuzzedFunctionTestCase, testIndexToShow: Int? + testCase: GoUtFuzzedFunctionTestCase, testIndexToShow: Int?, goUtModelToCodeConverter: GoUtModelToCodeConverter ): String { val (fuzzedFunction, executionResult) = testCase val function = fuzzedFunction.function @@ -79,14 +82,13 @@ object GoTestCasesCodeGenerator { "func Test${function.name.replaceFirstChar(Char::titlecaseChar)}${testFunctionNamePostfix}ByUtGoFuzzer$testIndexToShowString(t *testing.T)" if (function.resultTypes.isEmpty()) { - val actualFunctionCall = - generateFuzzedFunctionCall(fuzzedFunction.function.name, fuzzedFunction.parametersValues) + val actualFunctionCall = generateFuzzedFunctionCall( + fuzzedFunction.function.name, fuzzedFunction.parametersValues, goUtModelToCodeConverter + ) val testFunctionBody = "\tassert.NotPanics(t, func() { $actualFunctionCall })\n" return "$testFunctionSignatureDeclaration {\n$testFunctionBody}" } - val testFunctionBodySb = StringBuilder() - val resultTypes = function.resultTypes val doResultTypesImplementError = resultTypes.map { it.implementsError } val actualResultVariablesNames = run { @@ -103,9 +105,11 @@ object GoTestCasesCodeGenerator { } } } - val actualFunctionCall = generateFuzzedFunctionCallSavedToVariables(actualResultVariablesNames, fuzzedFunction) - testFunctionBodySb.append("\t$actualFunctionCall\n\n") + val actualFunctionCall = generateFuzzedFunctionCallSavedToVariables( + actualResultVariablesNames, fuzzedFunction, goUtModelToCodeConverter + ) + val testFunctionBodySb = StringBuilder("\t$actualFunctionCall\n\n") val expectedModels = (executionResult as GoUtExecutionCompleted).models val (assertionName, assertionTParameter) = if (expectedModels.size > 1 || expectedModels.any { it.isComplexModelAndNeedsSeparateAssertions() }) { testFunctionBodySb.append("\tassertMultiple := assert.New(t)\n") @@ -120,7 +124,11 @@ object GoTestCasesCodeGenerator { val assertionCalls = mutableListOf() fun generateAssertionCallHelper(refinedExpectedModel: GoUtModel, actualResultCode: String) { val code = generateCompletedExecutionAssertionCall( - refinedExpectedModel, actualResultCode, doesResultTypeImplementError, assertionTParameter + refinedExpectedModel, + actualResultCode, + doesResultTypeImplementError, + assertionTParameter, + goUtModelToCodeConverter ) assertionCalls.add(code) } @@ -146,7 +154,8 @@ object GoTestCasesCodeGenerator { expectedModel: GoUtModel, actualResultCode: String, doesReturnTypeImplementError: Boolean, - assertionTParameter: String + assertionTParameter: String, + goUtModelToCodeConverter: GoUtModelToCodeConverter ): String { if (expectedModel is GoUtNilModel) { return "Nil($assertionTParameter$actualResultCode)" @@ -163,27 +172,28 @@ object GoTestCasesCodeGenerator { return "True(${assertionTParameter}math.IsInf($castedActualResultCode, ${expectedModel.sign}))" } val prefix = if (!expectedModel.isComparable()) "Not" else "" - val castedExpectedResultCode = - if (expectedModel is GoUtPrimitiveModel) { - generateCastedValueIfPossible(expectedModel) - } else { - expectedModel.toString() - } + val castedExpectedResultCode = if (expectedModel is GoUtPrimitiveModel) { + generateCastedValueIfPossible(expectedModel, goUtModelToCodeConverter) + } else { + goUtModelToCodeConverter.toGoCode(expectedModel) + } + return "${prefix}Equal($assertionTParameter$castedExpectedResultCode, $actualResultCode)" } private fun generateTestFunctionForPanicFailureTestCase( - testCase: GoUtFuzzedFunctionTestCase, testIndexToShow: Int? + testCase: GoUtFuzzedFunctionTestCase, testIndexToShow: Int?, goUtModelToCodeConverter: GoUtModelToCodeConverter ): String { val (fuzzedFunction, executionResult) = testCase val function = fuzzedFunction.function val testIndexToShowString = testIndexToShow ?: "" val testFunctionSignatureDeclaration = - "func Test${function.name.capitalize()}PanicsByUtGoFuzzer$testIndexToShowString(t *testing.T)" + "func Test${function.name.replaceFirstChar(Char::titlecaseChar)}PanicsByUtGoFuzzer$testIndexToShowString(t *testing.T)" - val actualFunctionCall = - generateFuzzedFunctionCall(fuzzedFunction.function.name, fuzzedFunction.parametersValues) + val actualFunctionCall = generateFuzzedFunctionCall( + fuzzedFunction.function.name, fuzzedFunction.parametersValues, goUtModelToCodeConverter + ) val actualFunctionCallLambda = "func() { $actualFunctionCall }" val (expectedPanicValue, isErrorMessage) = (executionResult as GoUtPanicFailure) val isPrimitiveWithOkEquals = @@ -192,9 +202,9 @@ object GoTestCasesCodeGenerator { "\tassert.PanicsWithError(t, $expectedPanicValue, $actualFunctionCallLambda)" } else if (isPrimitiveWithOkEquals || expectedPanicValue is GoUtNilModel) { val expectedPanicValueCode = if (expectedPanicValue is GoUtNilModel) { - "$expectedPanicValue" + goUtModelToCodeConverter.toGoCode(expectedPanicValue) } else { - generateCastedValueIfPossible(expectedPanicValue as GoUtPrimitiveModel) + generateCastedValueIfPossible(expectedPanicValue as GoUtPrimitiveModel, goUtModelToCodeConverter) } "\tassert.PanicsWithValue(t, $expectedPanicValueCode, $actualFunctionCallLambda)" } else { @@ -205,12 +215,61 @@ object GoTestCasesCodeGenerator { } private fun generateTestFunctionForTimeoutExceededTestCase( - testCase: GoUtFuzzedFunctionTestCase, @Suppress("UNUSED_PARAMETER") testIndexToShow: Int? + testCase: GoUtFuzzedFunctionTestCase, + @Suppress("UNUSED_PARAMETER") testIndexToShow: Int?, + goUtModelToCodeConverter: GoUtModelToCodeConverter ): String { val (fuzzedFunction, executionResult) = testCase - val actualFunctionCall = - generateFuzzedFunctionCall(fuzzedFunction.function.name, fuzzedFunction.parametersValues) + val actualFunctionCall = generateFuzzedFunctionCall( + fuzzedFunction.function.name, fuzzedFunction.parametersValues, goUtModelToCodeConverter + ) val exceededTimeoutMillis = (executionResult as GoUtTimeoutExceeded).timeoutMillis return "// $actualFunctionCall exceeded $exceededTimeoutMillis ms timeout" } + + private fun generateFuzzedFunctionCall( + functionName: String, parameters: List, goUtModelToCodeConverter: GoUtModelToCodeConverter + ): String { + val fuzzedParametersToString = parameters.joinToString(prefix = "(", postfix = ")") { + goUtModelToCodeConverter.toGoCode(it) + } + return "$functionName$fuzzedParametersToString" + } + + private fun generateVariablesDeclarationTo(variablesNames: List, expression: String): String { + val variables = variablesNames.joinToString() + return "$variables := $expression" + } + + private fun generateFuzzedFunctionCallSavedToVariables( + variablesNames: List, + fuzzedFunction: GoUtFuzzedFunction, + goUtModelToCodeConverter: GoUtModelToCodeConverter + ): String = generateVariablesDeclarationTo( + variablesNames, + generateFuzzedFunctionCall( + fuzzedFunction.function.name, fuzzedFunction.parametersValues, goUtModelToCodeConverter + ) + ) + + private fun generateCastIfNeed( + toTypeId: GoPrimitiveTypeId, expressionType: GoPrimitiveTypeId, expression: String + ): String { + return if (expressionType != toTypeId) { + "${toTypeId.name}($expression)" + } else { + expression + } + } + + fun generateCastedValueIfPossible( + model: GoUtPrimitiveModel, + goUtModelToCodeConverter: GoUtModelToCodeConverter + ): String { + return if (model.explicitCastMode == ExplicitCastMode.NEVER) { + goUtModelToCodeConverter.primitiveModelToValueGoCode(model) + } else { + goUtModelToCodeConverter.primitiveModelToCastedValueGoCode(model) + } + } } \ No newline at end of file diff --git a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoUtModelToCodeConverter.kt b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoUtModelToCodeConverter.kt index a0a4f950fd..78cc5c07ae 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoUtModelToCodeConverter.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoUtModelToCodeConverter.kt @@ -1,14 +1,85 @@ package org.utbot.go.simplecodegeneration -import org.utbot.go.api.GoUtFloatNaNModel -import org.utbot.go.api.GoUtPrimitiveModel +import org.utbot.go.api.* +import org.utbot.go.api.util.goStringTypeId +import org.utbot.go.framework.api.go.GoPackage +import org.utbot.go.framework.api.go.GoUtModel -object GoUtModelToCodeConverter { - fun goUtModelToCode(goUtModel: GoUtPrimitiveModel): String { - return "1" +class GoUtModelToCodeConverter( + private val destinationPackage: GoPackage, + private val aliases: Map +) { + + fun toGoCode(model: GoUtModel): String = when (model) { + is GoUtNilModel -> "nil" + + is GoUtPrimitiveModel -> when (model.explicitCastMode) { + ExplicitCastMode.REQUIRED -> primitiveModelToCastedValueGoCode(model) + ExplicitCastMode.DEPENDS, ExplicitCastMode.NEVER -> primitiveModelToValueGoCode(model) + } + + is GoUtStructModel -> { + val typeName = model.typeId.getRelativeName(destinationPackage, aliases) + "$typeName${structModelToGoCodeWithoutStructName(model)}" + } + + is GoUtArrayModel -> arrayModelToGoCode(model) + + else -> error("Converting a $javaClass to Go code isn't supported") } - fun goUtModelToCode(goUtModel: GoUtFloatNaNModel): String { - return "2" + fun primitiveModelToValueGoCode(model: GoUtPrimitiveModel): String = when (model) { + is GoUtComplexModel -> complexModeToValueGoCode(model) + else -> if (model.typeId == goStringTypeId) "\"${model.value}\"" else "${model.value}" } + + fun primitiveModelToCastedValueGoCode(model: GoUtPrimitiveModel): String = + "${model.typeId}(${primitiveModelToValueGoCode(model)})" + + private fun complexModeToValueGoCode(model: GoUtComplexModel) = + "complex(${toGoCode(model.realValue)}, ${toGoCode(model.imagValue)})" + + private fun structModelToGoCodeWithoutStructName(model: GoUtStructModel): String = + model.getVisibleFields(destinationPackage).joinToString(prefix = "{", postfix = "}") { + "${it.fieldId.name}: ${toGoCode(it.model)}" + } + + private fun arrayModelToGoCode(model: GoUtArrayModel): String = + when (val elementType = model.typeId.elementTypeId!!) { + is GoStructTypeId -> model.getElements().joinToString( + prefix = "[${model.length}]${ + elementType.getRelativeName(destinationPackage, aliases) + }{", + postfix = "}" + ) { + structModelToGoCodeWithoutStructName(it as GoUtStructModel) + } + + is GoArrayTypeId -> model.getElements().joinToString( + prefix = "[${model.length}]${ + elementType.getRelativeName(destinationPackage, aliases) + }{", + postfix = "}" + ) { + arrayModelToGoCodeWithoutTypeName(it as GoUtArrayModel) + } + + else -> model.getElements().joinToString( + prefix = "[${model.length}]${elementType.getRelativeName(destinationPackage, aliases)}{", + postfix = "}" + ) + } + + private fun arrayModelToGoCodeWithoutTypeName(model: GoUtArrayModel): String = + when (model.typeId.elementTypeId!!) { + is GoStructTypeId -> model.getElements().joinToString(prefix = "{", postfix = "}") { + structModelToGoCodeWithoutStructName(it as GoUtStructModel) + } + + is GoArrayTypeId -> model.getElements().joinToString(prefix = "{", postfix = "}") { + arrayModelToGoCodeWithoutTypeName(it as GoUtArrayModel) + } + + else -> model.getElements().joinToString(prefix = "{", postfix = "}") + } } diff --git a/utbot-go/src/main/kotlin/org/utbot/go/worker/GoCodeTemplates.kt b/utbot-go/src/main/kotlin/org/utbot/go/worker/GoCodeTemplates.kt index 9d2354fa8f..b5f4752b48 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/worker/GoCodeTemplates.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/worker/GoCodeTemplates.kt @@ -210,8 +210,8 @@ object GoCodeTemplates { private fun convertStringToReflectType( structTypes: Set, - sourcePackage: GoPackage, - aliases: Map + destinationPackage: GoPackage, + aliases: Map ) = """ func __convertStringToReflectType__(typeName string) (reflect.Type, error) { var result reflect.Type @@ -282,17 +282,17 @@ object GoCodeTemplates { ${ structTypes.joinToString(separator = "\n") { "case \"${ - if (it.sourcePackage == sourcePackage || aliases[it.sourcePackage] == ".") { + if (it.sourcePackage == destinationPackage || aliases[it.sourcePackage] == ".") { it.simpleName - } else if ((aliases[it.sourcePackage] ?: "") == "") { + } else if (aliases[it.sourcePackage] == null) { "${it.packageName}.${it.simpleName}" } else { "${aliases[it.sourcePackage]}.${it.simpleName}" } }\": result = reflect.TypeOf(${ - if (it.sourcePackage == sourcePackage || aliases[it.sourcePackage] == ".") { + if (it.sourcePackage == destinationPackage || aliases[it.sourcePackage] == ".") { it.simpleName - } else if ((aliases[it.sourcePackage] ?: "") == "") { + } else if (aliases[it.sourcePackage] == null) { "${it.packageName}.${it.simpleName}" } else { "${aliases[it.sourcePackage]}.${it.simpleName}" @@ -688,8 +688,8 @@ object GoCodeTemplates { fun getTopLevelHelperStructsAndFunctionsForWorker( structTypes: Set, - goPackage: GoPackage, - aliases: Map + destinationPackage: GoPackage, + aliases: Map ) = listOf( rawValueInterface, primitiveValueStruct, @@ -699,7 +699,7 @@ object GoCodeTemplates { structValueToReflectValueMethod, arrayValueStruct, arrayValueToReflectValueMethod, - convertStringToReflectType(structTypes, goPackage, aliases), + convertStringToReflectType(structTypes, destinationPackage, aliases), panicMessageStruct, rawExecutionResultStruct, checkErrorFunction, diff --git a/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorker.kt b/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorker.kt index e81571d43c..ef91beaf4c 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorker.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorker.kt @@ -2,6 +2,7 @@ package org.utbot.go.worker import com.beust.klaxon.Klaxon import org.utbot.go.api.* +import org.utbot.go.api.util.convertToRawValue import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.framework.api.go.GoUtModel import org.utbot.go.util.convertObjectToJsonString @@ -18,39 +19,9 @@ class GoWorker( private val reader: BufferedReader = BufferedReader(InputStreamReader(socket.getInputStream())) private val writer: BufferedWriter = BufferedWriter(OutputStreamWriter(socket.getOutputStream())) - private fun GoUtModel.convertToRawValue(sourcePackage: GoPackage, aliases: Map): RawValue = when (val model = this) { - is GoUtComplexModel -> PrimitiveValue( - model.typeId.name, - "${model.realValue.toValueGoCode()}@${model.imagValue.toValueGoCode()}" - ) - - is GoUtArrayModel -> ArrayValue( - model.typeId.name, - model.typeId.elementTypeId!!.canonicalName, - model.length, - model.getElements(model.typeId.elementTypeId!!).map { it.convertToRawValue(sourcePackage, aliases) } - ) - - is GoUtStructModel -> StructValue( - model.typeId.getRelativeName(sourcePackage, aliases[model.typeId.sourcePackage] ?: ""), - model.value.map { - StructValue.FieldValue( - it.fieldId.name, - it.model.convertToRawValue(sourcePackage, aliases), - it.fieldId.isExported - ) - } - ) - - is GoUtPrimitiveModel -> PrimitiveValue(model.typeId.name, model.value.toString()) - - else -> error("Converting ${model.javaClass} to RawValue is not supported") - } - - fun sendFuzzedParametersValues(parameters: List, aliases: Map) { + fun sendFuzzedParametersValues(parameters: List, aliases: Map) { val rawValues = parameters.map { it.convertToRawValue(function.sourcePackage, aliases) } val json = convertObjectToJsonString(rawValues) - println(json) writer.write(json) writer.flush() } @@ -59,6 +30,6 @@ class GoWorker( val length = reader.readLine().toInt() val buffer = CharArray(length) reader.read(buffer) - return Klaxon().parse(String(buffer)) ?: error("") + return Klaxon().parse(String(buffer)) ?: error("Error with parsing json as raw execution result") } } \ No newline at end of file diff --git a/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorkerCodeGenerationHelper.kt b/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorkerCodeGenerationHelper.kt index 9710534b65..55a602c3b1 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorkerCodeGenerationHelper.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorkerCodeGenerationHelper.kt @@ -1,9 +1,10 @@ package org.utbot.go.worker -import org.utbot.go.api.* +import org.utbot.go.api.GoUtFile +import org.utbot.go.api.GoUtFunction +import org.utbot.go.api.util.getAllStructTypes import org.utbot.go.framework.api.go.GoImport import org.utbot.go.framework.api.go.GoPackage -import org.utbot.go.framework.api.go.GoTypeId import org.utbot.go.logic.EachExecutionTimeoutsMillisConfig import org.utbot.go.simplecodegeneration.GoFileCodeBuilder import java.io.File @@ -27,30 +28,31 @@ internal object GoWorkerCodeGenerationHelper { GoPackage("testing", "testing"), GoPackage("time", "time"), GoPackage("unsafe", "unsafe") - ).map { GoImport(it, "") }.toSet() + ).map { GoImport(it) }.toSet() fun createFileToExecute( sourceFile: GoUtFile, function: GoUtFunction, eachExecutionTimeoutsMillisConfig: EachExecutionTimeoutsMillisConfig, port: Int, - aliases: Map + imports: Set ): File { val fileToExecuteName = createFileToExecuteName(sourceFile) val sourceFileDir = File(sourceFile.absoluteDirectoryPath) val fileToExecute = sourceFileDir.resolve(fileToExecuteName) val fileToExecuteGoCode = - generateWorkerTestFileGoCode(function, eachExecutionTimeoutsMillisConfig, port, aliases) + generateWorkerTestFileGoCode(function, eachExecutionTimeoutsMillisConfig, port, imports) fileToExecute.writeText(fileToExecuteGoCode) return fileToExecute } fun createFileWithModifiedFunction( - function: GoUtFunction, - sourceFileDir: File + sourceFile: GoUtFile, + function: GoUtFunction ): File { val fileWithModifiedFunctionName = createFileWithModifiedFunctionName() + val sourceFileDir = File(sourceFile.absoluteDirectoryPath) val fileWithModifiedFunction = sourceFileDir.resolve(fileWithModifiedFunctionName) val fileWithModifiedFunctionGoCode = generateFileWithModifiedFunctionGoCode(function) @@ -70,32 +72,22 @@ internal object GoWorkerCodeGenerationHelper { function: GoUtFunction, eachExecutionTimeoutsMillisConfig: EachExecutionTimeoutsMillisConfig, port: Int, - aliases: Map + imports: Set ): String { - fun GoTypeId.getAllStructTypes(): Set = when (this) { - is GoStructTypeId -> fields.fold(setOf(this)) { acc: Set, field -> - acc + (field.declaringType).getAllStructTypes() - } - - is GoArrayTypeId -> elementTypeId!!.getAllStructTypes() - else -> emptySet() - } + val destinationPackage = function.sourcePackage + val fileCodeBuilder = GoFileCodeBuilder(destinationPackage, imports) - val structTypes = - function.parameters.fold(emptySet()) { acc: Set, functionParameter: GoUtFunctionParameter -> - acc + functionParameter.type.getAllStructTypes() - } + val workerTestFunctionCode = generateWorkerTestFunctionCode(function, eachExecutionTimeoutsMillisConfig, port) - val fileCodeBuilder = GoFileCodeBuilder( - function.sourceFile.sourcePackage, alwaysRequiredImports + structTypes.map { - GoImport(it.sourcePackage, aliases[it.sourcePackage] ?: "") - } - ) + val types = function.parameters.map { it.type } + val structTypes = types.getAllStructTypes() + val aliases = imports.associate { it.goPackage to it.alias } - val workerTestFunctionCode = generateWorkerTestFunctionCode(function, eachExecutionTimeoutsMillisConfig, port) fileCodeBuilder.addTopLevelElements( GoCodeTemplates.getTopLevelHelperStructsAndFunctionsForWorker( - structTypes, function.sourceFile.sourcePackage, aliases + structTypes, + destinationPackage, + aliases ) + workerTestFunctionCode ) @@ -103,8 +95,12 @@ internal object GoWorkerCodeGenerationHelper { } private fun generateFileWithModifiedFunctionGoCode(function: GoUtFunction): String { - val fileCodeBuilder = GoFileCodeBuilder(function.sourcePackage, function.requiredImports.toSet()) - fileCodeBuilder.addTopLevelElements(listOf(GoCodeTemplates.traces) + function.modifiedFunctionForCollectingTraces) + val destinationPackage = function.sourcePackage + val imports = function.requiredImports.toSet() + val fileCodeBuilder = GoFileCodeBuilder(destinationPackage, imports) + fileCodeBuilder.addTopLevelElements( + listOf(GoCodeTemplates.traces) + function.modifiedFunctionForCollectingTraces + ) return fileCodeBuilder.buildCodeString() } diff --git a/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt b/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt index cd4365b6df..174fbe79cf 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt @@ -4,7 +4,6 @@ import com.beust.klaxon.TypeAdapter import com.beust.klaxon.TypeFor import org.utbot.go.api.* import org.utbot.go.api.util.* -import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.framework.api.go.GoTypeId import org.utbot.go.framework.api.go.GoUtFieldModel import org.utbot.go.framework.api.go.GoUtModel @@ -120,7 +119,6 @@ private object RawValuesCodes { } fun convertRawExecutionResultToExecutionResult( - destinationPackage: GoPackage, rawExecutionResult: RawExecutionResult, functionResultTypes: List, timeoutMillis: Long @@ -160,7 +158,7 @@ fun convertRawExecutionResultToExecutionResult( if (resultType.implementsError && rawResultValue != null) { executedWithNonNilErrorString = true } - createGoUtModelFromRawValue(rawResultValue, resultType, destinationPackage) + createGoUtModelFromRawValue(rawResultValue, resultType) } return if (executedWithNonNilErrorString) { GoUtExecutionWithNonNilError(resultValues, rawExecutionResult.trace) @@ -170,7 +168,7 @@ fun convertRawExecutionResultToExecutionResult( } private fun createGoUtModelFromRawValue( - rawValue: RawValue?, typeId: GoTypeId, destinationPackage: GoPackage + rawValue: RawValue?, typeId: GoTypeId ): GoUtModel = when (typeId) { // Only for error interface is GoInterfaceTypeId -> if (rawValue == null) { @@ -179,9 +177,9 @@ private fun createGoUtModelFromRawValue( GoUtPrimitiveModel((rawValue as PrimitiveValue).value, goStringTypeId) } - is GoStructTypeId -> createGoUtStructModelFromRawValue(rawValue as StructValue, typeId, destinationPackage) + is GoStructTypeId -> createGoUtStructModelFromRawValue(rawValue as StructValue, typeId) - is GoArrayTypeId -> createGoUtArrayModelFromRawValue(rawValue as ArrayValue, typeId, destinationPackage) + is GoArrayTypeId -> createGoUtArrayModelFromRawValue(rawValue as ArrayValue, typeId) is GoPrimitiveTypeId -> createGoUtPrimitiveModelFromRawValue(rawValue as PrimitiveValue, typeId) @@ -238,19 +236,19 @@ private fun convertRawFloatValueToGoUtPrimitiveModel( } private fun createGoUtStructModelFromRawValue( - resultValue: StructValue, resultTypeId: GoStructTypeId, destinationPackage: GoPackage + resultValue: StructValue, resultTypeId: GoStructTypeId ): GoUtStructModel { val value = resultValue.value.zip(resultTypeId.fields).map { (value, fieldId) -> - GoUtFieldModel(createGoUtModelFromRawValue(value.value, fieldId.declaringType, destinationPackage), fieldId) + GoUtFieldModel(createGoUtModelFromRawValue(value.value, fieldId.declaringType), fieldId) } - return GoUtStructModel(value, resultTypeId, destinationPackage, "") + return GoUtStructModel(value, resultTypeId) } private fun createGoUtArrayModelFromRawValue( - resultValue: ArrayValue, resultTypeId: GoArrayTypeId, destinationPackage: GoPackage + resultValue: ArrayValue, resultTypeId: GoArrayTypeId ): GoUtArrayModel { val value = (0 until resultTypeId.length).associateWith { index -> - createGoUtModelFromRawValue(resultValue.value[index], resultTypeId.elementTypeId!!, destinationPackage) + createGoUtModelFromRawValue(resultValue.value[index], resultTypeId.elementTypeId!!) }.toMutableMap() - return GoUtArrayModel(value, resultTypeId, destinationPackage) + return GoUtArrayModel(value, resultTypeId) } \ No newline at end of file diff --git a/utbot-go/src/main/resources/go_source_code_analyzer/analyzer_core.go b/utbot-go/src/main/resources/go_source_code_analyzer/analyzer_core.go index bd8600ea7e..117aaa35ab 100644 --- a/utbot-go/src/main/resources/go_source_code_analyzer/analyzer_core.go +++ b/utbot-go/src/main/resources/go_source_code_analyzer/analyzer_core.go @@ -282,8 +282,6 @@ func collectTargetAnalyzedFunctions( analyzedFunction.NumberOfAllStatements = functionModifier.lineCounter analyzedFunctions = append(analyzedFunctions, analyzedFunction) - - fmt.Println(analyzedFunction.RequiredImports) } } diff --git a/utbot-go/src/main/resources/go_source_code_analyzer/imports_collector.go b/utbot-go/src/main/resources/go_source_code_analyzer/imports_collector.go index dc1ec1788d..cee799b3fe 100644 --- a/utbot-go/src/main/resources/go_source_code_analyzer/imports_collector.go +++ b/utbot-go/src/main/resources/go_source_code_analyzer/imports_collector.go @@ -1,10 +1,8 @@ package main import ( - "fmt" "go/ast" "go/types" - "log" ) type Package struct { @@ -43,11 +41,9 @@ func (i *ImportsCollector) Visit(node ast.Node) ast.Visitor { }, Alias: ".", } - fmt.Println(obj.Name()) + if _, ok := i.allImportsInFile[nextImport]; ok { i.requiredImports[nextImport] = true - } else if i.sourcePackage != nextImport.Package { - log.Fatal(fmt.Sprintf("not found import for %s", obj.Name())) } } i.prevPkg = nil diff --git a/utbot-go/src/main/resources/go_source_code_analyzer/main.go b/utbot-go/src/main/resources/go_source_code_analyzer/main.go index e40e067dbb..a1dae5fd48 100644 --- a/utbot-go/src/main/resources/go_source_code_analyzer/main.go +++ b/utbot-go/src/main/resources/go_source_code_analyzer/main.go @@ -54,7 +54,12 @@ func analyzeTarget(target AnalysisTarget) (*AnalysisResult, error) { } index := 0 for ; index < len(pkg.CompiledGoFiles); index++ { - if pkg.CompiledGoFiles[index] == target.AbsoluteFilePath { + p1, err := filepath.Abs(pkg.CompiledGoFiles[index]) + checkError(err) + p2, err := filepath.Abs(target.AbsoluteFilePath) + checkError(err) + + if p1 == p2 { break } } @@ -71,7 +76,7 @@ func analyzeTarget(target AnalysisTarget) (*AnalysisResult, error) { PackagePath: packagePath, } - alias := "" + var alias string if i.Name.String() != "" { alias = i.Name.String() } From 71486fd73c77950dc101ca3cc68efbab129e7cb5 Mon Sep 17 00:00:00 2001 From: Egipti Pavel Date: Thu, 16 Feb 2023 16:15:19 +0300 Subject: [PATCH 3/4] Fix empty test file generation --- .../src/main/kotlin/org/utbot/go/GoEngine.kt | 3 +-- .../GoTestCasesCodeGenerator.kt | 23 ++++++++++--------- .../kotlin/org/utbot/go/worker/GoWorker.kt | 2 +- .../go/worker/GoWorkerCodeGenerationHelper.kt | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt b/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt index 42ce5b8a65..469879dbfe 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt @@ -17,7 +17,6 @@ import org.utbot.go.worker.convertRawExecutionResultToExecutionResult import java.io.File import java.io.InputStreamReader import java.net.ServerSocket -import java.net.SocketException import java.net.SocketTimeoutException import java.util.concurrent.TimeUnit @@ -146,7 +145,7 @@ class GoEngine( ) } } - } catch (e: SocketException) { + } catch (e: Exception) { val processHasExited = process.waitFor(timeoutMillis, TimeUnit.MILLISECONDS) if (!processHasExited) { process.destroy() diff --git a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoTestCasesCodeGenerator.kt b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoTestCasesCodeGenerator.kt index 59ac5d665d..af0eba4a79 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoTestCasesCodeGenerator.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoTestCasesCodeGenerator.kt @@ -18,21 +18,22 @@ object GoTestCasesCodeGenerator { fun generateTestCasesFileCode(sourceFile: GoUtFile, testCases: List): String { val destinationPackage = sourceFile.sourcePackage + if (testCases.isEmpty()) { + return GoFileCodeBuilder(destinationPackage, emptySet()).buildCodeString() + } val requiredPackages = mutableSetOf() - if (testCases.isNotEmpty()) { - testCases.forEach { testCase -> - testCase.parametersValues.forEach { + testCases.forEach { testCase -> + testCase.parametersValues.forEach { + requiredPackages += it.getRequiredPackages(destinationPackage) + } + when (val executionResult = testCase.executionResult) { + is GoUtExecutionCompleted -> executionResult.models.forEach { requiredPackages += it.getRequiredPackages(destinationPackage) } - when (val executionResult = testCase.executionResult) { - is GoUtExecutionCompleted -> executionResult.models.forEach { - requiredPackages += it.getRequiredPackages(destinationPackage) - } - is GoUtPanicFailure -> requiredPackages += executionResult.panicValue.getRequiredPackages( - destinationPackage - ) - } + is GoUtPanicFailure -> requiredPackages += executionResult.panicValue.getRequiredPackages( + destinationPackage + ) } } diff --git a/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorker.kt b/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorker.kt index ef91beaf4c..9070c6e61a 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorker.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorker.kt @@ -1,7 +1,7 @@ package org.utbot.go.worker import com.beust.klaxon.Klaxon -import org.utbot.go.api.* +import org.utbot.go.api.GoUtFunction import org.utbot.go.api.util.convertToRawValue import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.framework.api.go.GoUtModel diff --git a/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorkerCodeGenerationHelper.kt b/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorkerCodeGenerationHelper.kt index 55a602c3b1..2eea2fcfde 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorkerCodeGenerationHelper.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/worker/GoWorkerCodeGenerationHelper.kt @@ -132,7 +132,7 @@ internal object GoWorkerCodeGenerationHelper { function := reflect.ValueOf(${function.modifiedName}) executionResult := __executeFunction__($timeoutMillis*time.Millisecond, func() []__RawValue__ { - __traces__ = []int{} + __traces__ = make([]int, 0, 1000) return __wrapResultValuesForUtBotGoWorker__(function.Call(parameters)) }) From 477ec76539ae957096807d21546a734313a3f779 Mon Sep 17 00:00:00 2001 From: Egipti Pavel Date: Thu, 16 Feb 2023 16:48:23 +0300 Subject: [PATCH 4/4] Fix empty test file generation when all test case execution results exceed timeout --- .../utbot/go/simplecodegeneration/GoTestCasesCodeGenerator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoTestCasesCodeGenerator.kt b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoTestCasesCodeGenerator.kt index af0eba4a79..04357e628e 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoTestCasesCodeGenerator.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoTestCasesCodeGenerator.kt @@ -18,7 +18,7 @@ object GoTestCasesCodeGenerator { fun generateTestCasesFileCode(sourceFile: GoUtFile, testCases: List): String { val destinationPackage = sourceFile.sourcePackage - if (testCases.isEmpty()) { + if (testCases.isEmpty() || testCases.all { it.executionResult is GoUtTimeoutExceeded }) { return GoFileCodeBuilder(destinationPackage, emptySet()).buildCodeString() } val requiredPackages = mutableSetOf()