From 5597fc618666e84e629da5b0170cd34f037a3fa7 Mon Sep 17 00:00:00 2001 From: Egipti Pavel Date: Mon, 20 Feb 2023 15:47:23 +0300 Subject: [PATCH 1/3] Do constants collecting --- .../go/gocodeanalyzer/AnalysisResults.kt | 33 ++++++------- .../go/gocodeanalyzer/GoSourceCodeAnalyzer.kt | 3 +- .../analysis_results.go | 1 + .../go_source_code_analyzer/analyzer_core.go | 4 ++ .../constant_extractor.go | 46 +++++++++++++++++++ 5 files changed, 70 insertions(+), 17 deletions(-) create mode 100644 utbot-go/src/main/resources/go_source_code_analyzer/constant_extractor.go 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 1f0eb8a246..035632228b 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 @@ -13,22 +13,6 @@ import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.framework.api.go.GoTypeId import kotlin.reflect.KClass -data class AnalyzedInterfaceType( - override val name: String, - val implementsError: Boolean, - val packageName: String, - val packagePath: String -) : AnalyzedType(name) { - override fun toGoTypeId(): GoTypeId = - GoInterfaceTypeId( - name = simpleName, - implementsError = implementsError, - sourcePackage = GoPackage(packageName, packagePath) - ) - - private val simpleName: String = name.replaceFirst("interface ", "") -} - data class AnalyzedPrimitiveType( override val name: String ) : AnalyzedType(name) { @@ -68,6 +52,22 @@ data class AnalyzedArrayType( ) } +data class AnalyzedInterfaceType( + override val name: String, + val implementsError: Boolean, + val packageName: String, + val packagePath: String +) : AnalyzedType(name) { + override fun toGoTypeId(): GoTypeId = + GoInterfaceTypeId( + name = simpleName, + implementsError = implementsError, + sourcePackage = GoPackage(packageName, packagePath) + ) + + private val simpleName: String = name.replaceFirst("interface ", "") +} + @TypeFor(field = "name", adapter = AnalyzedTypeAdapter::class) abstract class AnalyzedType(open val name: String) { abstract fun toGoTypeId(): GoTypeId @@ -95,6 +95,7 @@ internal data class AnalyzedFunction( val parameters: List, val resultTypes: List, val requiredImports: List, + val constants: Map, val modifiedFunctionForCollectingTraces: String, val numberOfAllStatements: Int ) 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 836a40bb5b..ec7520ac1f 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 @@ -122,7 +122,8 @@ object GoSourceCodeAnalyzer { "analysis_targets.go", "analysis_results.go", "function_modifier.go", - "imports_collector.go" + "imports_collector.go", + "constant_extractor.go" ) } 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 3ebe331923..2b433240ae 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 @@ -64,6 +64,7 @@ type AnalyzedFunction struct { Parameters []AnalyzedFunctionParameter `json:"parameters"` ResultTypes []AnalyzedType `json:"resultTypes"` RequiredImports []Import `json:"requiredImports"` + Constants map[string][]string `json:"constants"` ModifiedFunctionForCollectingTraces string `json:"modifiedFunctionForCollectingTraces"` NumberOfAllStatements int `json:"numberOfAllStatements"` position token.Pos 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 117aaa35ab..96d67e32cc 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 @@ -252,6 +252,10 @@ func collectTargetAnalyzedFunctions( funcDecl := ident.Obj.Decl.(*ast.FuncDecl) funcDecl.Name = ast.NewIdent(analyzedFunction.ModifiedName) + constantExtractor := ConstantExtractor{info: info, constants: map[string][]string{}} + ast.Walk(&constantExtractor, funcDecl) + analyzedFunction.Constants = constantExtractor.constants + functionModifier := FunctionModifier{lineCounter: 0} ast.Walk(&functionModifier, funcDecl) diff --git a/utbot-go/src/main/resources/go_source_code_analyzer/constant_extractor.go b/utbot-go/src/main/resources/go_source_code_analyzer/constant_extractor.go new file mode 100644 index 0000000000..aeedc9f59f --- /dev/null +++ b/utbot-go/src/main/resources/go_source_code_analyzer/constant_extractor.go @@ -0,0 +1,46 @@ +package main + +import ( + "go/ast" + "go/types" +) + +type ConstantExtractor struct { + info *types.Info + constants map[string][]string +} + +func (e *ConstantExtractor) Visit(node ast.Node) ast.Visitor { + if _, ok := node.(*ast.BasicLit); !ok { + return e + } + + expr, ok := node.(ast.Expr) + if !ok { + return e + } + + typeAndValue, ok := e.info.Types[expr] + if !ok { + return e + } + + var t = typeAndValue.Type + basicType, ok := t.(*types.Basic) + if !ok { + return e + } + + switch basicType.Kind() { + case types.Int, types.Int8, types.Int16, types.Int32, types.Int64: + e.constants[basicType.Name()] = append(e.constants[basicType.Name()], typeAndValue.Value.String()) + case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr: + e.constants[basicType.Name()] = append(e.constants[basicType.Name()], typeAndValue.Value.String()) + case types.Float32, types.Float64: + e.constants[basicType.Name()] = append(e.constants[basicType.Name()], typeAndValue.Value.String()) + case types.String: + e.constants[basicType.Name()] = append(e.constants[basicType.Name()], typeAndValue.Value.String()) + } + + return e +} From 0f28805c630e61b6b912bc3a7bb4f31b6d39573d Mon Sep 17 00:00:00 2001 From: Egipti Pavel Date: Tue, 21 Feb 2023 16:11:17 +0300 Subject: [PATCH 2/3] Add GoConstantValueProvider to use constants to generate test cases (only for ints now) --- .../src/main/kotlin/org/utbot/go/GoEngine.kt | 5 +- .../main/kotlin/org/utbot/go/GoLanguage.kt | 9 ++- .../org/utbot/go/api/GoUtFunctionApi.kt | 1 + .../org/utbot/go/api/util/GoTypesApiUtil.kt | 80 +++++++++++++------ .../providers/GoConstantValueProvider.kt | 71 ++++++++++++++++ .../providers/GoPrimitivesValueProvider.kt | 7 +- .../go/gocodeanalyzer/AnalysisResults.kt | 2 +- .../go/gocodeanalyzer/GoSourceCodeAnalyzer.kt | 23 +++++- .../AbstractGoUtTestsGenerationController.kt | 3 +- .../utbot/go/logic/GoTestCasesGenerator.kt | 2 + .../utbot/go/worker/RawExecutionResults.kt | 39 ++++----- 11 files changed, 179 insertions(+), 63 deletions(-) create mode 100644 utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoConstantValueProvider.kt 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 469879dbfe..89728e7844 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt @@ -25,6 +25,7 @@ val logger = KotlinLogging.logger {} class GoEngine( private val functionUnderTest: GoUtFunction, private val sourceFile: GoUtFile, + private val intSize: Int, private val goExecutableAbsolutePath: String, private val eachExecutionTimeoutsMillisConfig: EachExecutionTimeoutsMillisConfig, private val timeoutExceededOrIsCanceled: () -> Boolean, @@ -91,13 +92,14 @@ class GoEngine( val executionResult = convertRawExecutionResultToExecutionResult( rawExecutionResult, functionUnderTest.resultTypes, + intSize, eachExecutionTimeoutsMillisConfig[functionUnderTest], ) val fuzzedFunction = GoUtFuzzedFunction(functionUnderTest, emptyList()) emit(fuzzedFunction to executionResult) } else { val aliases = imports.filter { it.alias != null }.associate { it.goPackage to it.alias } - runGoFuzzing(functionUnderTest) { description, values -> + runGoFuzzing(functionUnderTest, intSize) { description, values -> if (timeoutExceededOrIsCanceled()) { return@runGoFuzzing BaseFeedback(result = Trie.emptyNode(), control = Control.STOP) } @@ -107,6 +109,7 @@ class GoEngine( val executionResult = convertRawExecutionResultToExecutionResult( rawExecutionResult, functionUnderTest.resultTypes, + intSize, eachExecutionTimeoutsMillisConfig[functionUnderTest], ) if (executionResult.trace.isEmpty()) { diff --git a/utbot-go/src/main/kotlin/org/utbot/go/GoLanguage.kt b/utbot-go/src/main/kotlin/org/utbot/go/GoLanguage.kt index 55b4378389..cce09834af 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/GoLanguage.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/GoLanguage.kt @@ -6,12 +6,13 @@ import org.utbot.go.api.GoUtFunction import org.utbot.go.framework.api.go.GoTypeId import org.utbot.go.framework.api.go.GoUtModel import org.utbot.go.fuzzer.providers.GoArrayValueProvider +import org.utbot.go.fuzzer.providers.GoConstantValueProvider import org.utbot.go.fuzzer.providers.GoPrimitivesValueProvider import org.utbot.go.fuzzer.providers.GoStructValueProvider fun goDefaultValueProviders() = listOf( - GoPrimitivesValueProvider, GoArrayValueProvider, GoStructValueProvider + GoPrimitivesValueProvider, GoArrayValueProvider, GoStructValueProvider, GoConstantValueProvider ) class GoInstruction( @@ -20,13 +21,15 @@ class GoInstruction( class GoDescription( val methodUnderTest: GoUtFunction, - val tracer: Trie + val tracer: Trie, + val intSize: Int ) : Description(methodUnderTest.parameters.map { it.type }.toList()) suspend fun runGoFuzzing( methodUnderTest: GoUtFunction, + intSize: Int, providers: List> = goDefaultValueProviders(), exec: suspend (description: GoDescription, values: List) -> BaseFeedback, GoTypeId, GoUtModel> ) { - BaseFuzzing(providers, exec).fuzz(GoDescription(methodUnderTest, Trie(GoInstruction::lineNumber))) + BaseFuzzing(providers, exec).fuzz(GoDescription(methodUnderTest, Trie(GoInstruction::lineNumber), intSize)) } \ No newline at end of file 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 9b211338da..94fa4c20e6 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 @@ -21,6 +21,7 @@ data class GoUtFunction( val parameters: List, val resultTypes: List, val requiredImports: List, + val constants: Map>, val modifiedFunctionForCollectingTraces: String, val numberOfAllStatements: Int, val sourceFile: GoUtFile 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 7427a294e9..e93e3d2450 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 @@ -14,20 +14,20 @@ val goComplex64TypeId = GoPrimitiveTypeId("complex64") val goFloat32TypeId = GoPrimitiveTypeId("float32") val goFloat64TypeId = GoPrimitiveTypeId("float64") -val goIntTypeId = GoPrimitiveTypeId("int") +val goInt8TypeId = GoPrimitiveTypeId("int8") val goInt16TypeId = GoPrimitiveTypeId("int16") val goInt32TypeId = GoPrimitiveTypeId("int32") +val goIntTypeId = GoPrimitiveTypeId("int") val goInt64TypeId = GoPrimitiveTypeId("int64") -val goInt8TypeId = GoPrimitiveTypeId("int8") val goRuneTypeId = GoPrimitiveTypeId("rune") // = int32 val goStringTypeId = GoPrimitiveTypeId("string") -val goUintTypeId = GoPrimitiveTypeId("uint") +val goUint8TypeId = GoPrimitiveTypeId("uint8") val goUint16TypeId = GoPrimitiveTypeId("uint16") val goUint32TypeId = GoPrimitiveTypeId("uint32") +val goUintTypeId = GoPrimitiveTypeId("uint") val goUint64TypeId = GoPrimitiveTypeId("uint64") -val goUint8TypeId = GoPrimitiveTypeId("uint8") val goUintPtrTypeId = GoPrimitiveTypeId("uintptr") val goPrimitives = setOf( @@ -52,6 +52,25 @@ val goPrimitives = setOf( goUintPtrTypeId, ) +val goConstantTypes = setOf( + goByteTypeId, + goFloat32TypeId, + goFloat64TypeId, + goIntTypeId, + goInt8TypeId, + goInt16TypeId, + goInt32TypeId, + goInt64TypeId, + goRuneTypeId, + goStringTypeId, + goUintTypeId, + goUint8TypeId, + goUint16TypeId, + goUint32TypeId, + goUint64TypeId, + goUintPtrTypeId, +) + val GoTypeId.isPrimitiveGoType: Boolean get() = this in goPrimitives @@ -69,27 +88,40 @@ val GoPrimitiveTypeId.neverRequiresExplicitCast: Boolean /** * This method is useful for converting the string representation of a Go value to its more accurate representation. - * For example, to build more proper GoUtPrimitiveModel-s with GoFuzzedFunctionsExecutor. - * Note, that for now such conversion is not required and is done for convenience only. - * - * About corresponding types: int and uint / uintptr types sizes in Go are platform dependent, - * but are supposed to fit in Long and ULong respectively. */ -val GoPrimitiveTypeId.correspondingKClass: KClass - get() = when (this) { - goByteTypeId, goUint8TypeId -> UByte::class - goBoolTypeId -> Boolean::class - goFloat32TypeId -> Float::class - goFloat64TypeId -> Double::class - goInt16TypeId -> Short::class - goInt32TypeId, goRuneTypeId -> Int::class - goIntTypeId, goInt64TypeId -> Long::class - goInt8TypeId -> Byte::class - goStringTypeId -> String::class - goUint32TypeId -> UInt::class - goUint16TypeId -> UShort::class - goUintTypeId, goUint64TypeId, goUintPtrTypeId -> ULong::class - else -> String::class // default way to hold GoUtPrimitiveModel's value is to use String +private fun GoPrimitiveTypeId.correspondingKClass(intSize: Int): KClass = when (this) { + goBoolTypeId -> Boolean::class + goFloat32TypeId -> Float::class + goFloat64TypeId -> Double::class + goInt8TypeId -> Byte::class + goInt16TypeId -> Short::class + goInt32TypeId, goRuneTypeId -> Int::class + goIntTypeId -> if (intSize == 32) Int::class else Long::class + goInt64TypeId -> Long::class + goStringTypeId -> String::class + goUint8TypeId, goByteTypeId -> UByte::class + goUint16TypeId -> UShort::class + goUint32TypeId -> UInt::class + goUintTypeId -> if (intSize == 32) UInt::class else ULong::class + goUint64TypeId -> ULong::class + goUintPtrTypeId -> if (intSize == 32) UInt::class else ULong::class + else -> String::class // default way to hold GoUtPrimitiveModel's value is to use String +} + +fun rawValueOfGoPrimitiveTypeToValue(typeId: GoPrimitiveTypeId, rawValue: String, intSize: Int): Any = + when (typeId.correspondingKClass(intSize)) { + UByte::class -> rawValue.toUByte() + Boolean::class -> rawValue.toBoolean() + Float::class -> rawValue.toFloat() + Double::class -> rawValue.toDouble() + Int::class -> rawValue.toInt() + Short::class -> rawValue.toShort() + Long::class -> rawValue.toLong() + Byte::class -> rawValue.toByte() + UInt::class -> rawValue.toUInt() + UShort::class -> rawValue.toUShort() + ULong::class -> rawValue.toULong() + else -> rawValue } fun GoTypeId.goDefaultValueModel(): GoUtModel = when (this) { diff --git a/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoConstantValueProvider.kt b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoConstantValueProvider.kt new file mode 100644 index 0000000000..45e8027013 --- /dev/null +++ b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoConstantValueProvider.kt @@ -0,0 +1,71 @@ +package org.utbot.go.fuzzer.providers + +import org.utbot.fuzzing.Seed +import org.utbot.fuzzing.ValueProvider +import org.utbot.fuzzing.seeds.BitVectorValue +import org.utbot.go.GoDescription +import org.utbot.go.api.GoPrimitiveTypeId +import org.utbot.go.api.GoUtPrimitiveModel +import org.utbot.go.api.util.* +import org.utbot.go.framework.api.go.GoTypeId +import org.utbot.go.framework.api.go.GoUtModel + +object GoConstantValueProvider : ValueProvider { + override fun accept(type: GoTypeId): Boolean = type in goConstantTypes + + override fun generate(description: GoDescription, type: GoTypeId): Sequence> = sequence { + type.let { it as GoPrimitiveTypeId }.also { primitiveType -> + val constants = description.methodUnderTest.constants + val intSize = description.intSize + val primitives: List> = when (type) { + goRuneTypeId, goIntTypeId, goInt8TypeId, goInt16TypeId, goInt32TypeId, goInt64TypeId -> + (constants[type] ?: emptyList()).map { + when (type) { + goInt8TypeId -> Seed.Known(BitVectorValue.fromValue(it)) { obj: BitVectorValue -> + GoUtPrimitiveModel( + obj.toByte(), + primitiveType + ) + } + + goInt16TypeId -> Seed.Known(BitVectorValue.fromValue(it)) { obj: BitVectorValue -> + GoUtPrimitiveModel( + obj.toShort(), + primitiveType + ) + } + + goInt32TypeId, goRuneTypeId -> Seed.Known(BitVectorValue.fromValue(it)) { obj: BitVectorValue -> + GoUtPrimitiveModel( + obj.toInt(), + primitiveType + ) + } + + goIntTypeId -> Seed.Known(BitVectorValue.fromValue(it)) { obj: BitVectorValue -> + GoUtPrimitiveModel( + if (intSize == 32) obj.toInt() else obj.toLong(), + primitiveType + ) + } + + goInt64TypeId -> Seed.Known(BitVectorValue.fromValue(it)) { obj: BitVectorValue -> + GoUtPrimitiveModel( + obj.toLong(), + primitiveType + ) + } + + else -> return@sequence + } + } + + else -> emptyList() + } + + primitives.forEach { seed -> + yield(seed) + } + } + } +} \ No newline at end of file diff --git a/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoPrimitivesValueProvider.kt b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoPrimitivesValueProvider.kt index 8999f7524e..154052b2d6 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoPrimitivesValueProvider.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoPrimitivesValueProvider.kt @@ -12,16 +12,15 @@ import org.utbot.go.api.util.* import org.utbot.go.framework.api.go.GoTypeId import org.utbot.go.framework.api.go.GoUtModel import java.util.* -import kotlin.properties.Delegates object GoPrimitivesValueProvider : ValueProvider { - var intSize by Delegates.notNull() private val random = Random(0) override fun accept(type: GoTypeId): Boolean = type in goPrimitives override fun generate(description: GoDescription, type: GoTypeId): Sequence> = sequence { + val intSize = description.intSize type.let { it as GoPrimitiveTypeId }.also { primitiveType -> val primitives: List> = when (primitiveType) { goBoolTypeId -> listOf( @@ -148,8 +147,8 @@ object GoPrimitivesValueProvider : ValueProvider emptyList() } - primitives.forEach { fuzzedValue -> - yield(fuzzedValue) + primitives.forEach { seed -> + yield(seed) } } } 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 035632228b..d7080f8ca4 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 @@ -95,7 +95,7 @@ internal data class AnalyzedFunction( val parameters: List, val resultTypes: List, val requiredImports: List, - val constants: Map, + val constants: Map>, val modifiedFunctionForCollectingTraces: String, val numberOfAllStatements: Int ) 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 ec7520ac1f..8f7ce9b92f 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 @@ -2,10 +2,13 @@ package org.utbot.go.gocodeanalyzer import org.utbot.common.FileUtil.extractDirectoryFromArchive import org.utbot.common.scanForResourcesContaining +import org.utbot.go.api.GoPrimitiveTypeId import org.utbot.go.api.GoUtFile import org.utbot.go.api.GoUtFunction import org.utbot.go.api.GoUtFunctionParameter -import org.utbot.go.fuzzer.providers.GoPrimitivesValueProvider +import org.utbot.go.api.util.goConstantTypes +import org.utbot.go.api.util.rawValueOfGoPrimitiveTypeToValue +import org.utbot.go.framework.api.go.GoTypeId import org.utbot.go.util.executeCommandByNewProcessOrFail import org.utbot.go.util.parseFromJsonOrFail import org.utbot.go.util.writeJsonToFileOrFail @@ -28,7 +31,7 @@ object GoSourceCodeAnalyzer { fun analyzeGoSourceFilesForFunctions( targetFunctionsNamesBySourceFiles: Map>, goExecutableAbsolutePath: String - ): Map { + ): Pair, Int> { val analysisTargets = AnalysisTargets( targetFunctionsNamesBySourceFiles.map { (absoluteFilePath, targetFunctionsNames) -> AnalysisTarget(absoluteFilePath, targetFunctionsNames) @@ -63,7 +66,7 @@ object GoSourceCodeAnalyzer { environment ) val analysisResults = parseFromJsonOrFail(analysisResultsFile) - GoPrimitivesValueProvider.intSize = analysisResults.intSize + val intSize = analysisResults.intSize return analysisResults.results.map { analysisResult -> GoUtFile(analysisResult.absoluteFilePath, analysisResult.sourcePackage) to analysisResult }.associateBy({ (sourceFile, _) -> sourceFile }) { (sourceFile, analysisResult) -> @@ -75,12 +78,24 @@ object GoSourceCodeAnalyzer { ) } val resultTypes = analyzedFunction.resultTypes.map { analyzedType -> analyzedType.toGoTypeId() } + val constants = mutableMapOf>() + analyzedFunction.constants.map { (type, rawValues) -> + val typeId = GoPrimitiveTypeId(type) + if (typeId !in goConstantTypes) { + error("Constants extraction: $type is a unsupported constant type") + } + val values = rawValues.map { rawValue -> + rawValueOfGoPrimitiveTypeToValue(typeId, rawValue, intSize) + } + constants.compute(typeId) { _, v -> if (v == null) values else v + values } + } GoUtFunction( analyzedFunction.name, analyzedFunction.modifiedName, parameters, resultTypes, analyzedFunction.requiredImports, + constants, analyzedFunction.modifiedFunctionForCollectingTraces, analyzedFunction.numberOfAllStatements, sourceFile @@ -91,7 +106,7 @@ object GoSourceCodeAnalyzer { analysisResult.notSupportedFunctionsNames, analysisResult.notFoundFunctionsNames ) - } + } to intSize } finally { // TODO correctly? analysisTargetsFile.delete() 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..899ebf802d 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 @@ -14,7 +14,7 @@ abstract class AbstractGoUtTestsGenerationController { isCanceled: () -> Boolean = { false } ) { if (!onSourceCodeAnalysisStart(selectedFunctionsNamesBySourceFiles)) return - val analysisResults = GoSourceCodeAnalyzer.analyzeGoSourceFilesForFunctions( + val (analysisResults, intSize) = GoSourceCodeAnalyzer.analyzeGoSourceFilesForFunctions( selectedFunctionsNamesBySourceFiles, testsGenerationConfig.goExecutableAbsolutePath ) @@ -31,6 +31,7 @@ abstract class AbstractGoUtTestsGenerationController { GoTestCasesGenerator.generateTestCasesForGoSourceFileFunctions( sourceFile, functions, + intSize, testsGenerationConfig.goExecutableAbsolutePath, testsGenerationConfig.eachExecutionTimeoutsMillisConfig ) { index -> isCanceled() || System.currentTimeMillis() - (startTimeMillis + (index + 1) * functionTimeoutStepMillis) > 0 } diff --git a/utbot-go/src/main/kotlin/org/utbot/go/logic/GoTestCasesGenerator.kt b/utbot-go/src/main/kotlin/org/utbot/go/logic/GoTestCasesGenerator.kt index 50350c635b..88ec34dc7c 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/logic/GoTestCasesGenerator.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/logic/GoTestCasesGenerator.kt @@ -16,6 +16,7 @@ object GoTestCasesGenerator { fun generateTestCasesForGoSourceFileFunctions( sourceFile: GoUtFile, functions: List, + intSize: Int, goExecutableAbsolutePath: String, eachExecutionTimeoutsMillisConfig: EachExecutionTimeoutsMillisConfig, timeoutExceededOrIsCanceled: (index: Int) -> Boolean @@ -26,6 +27,7 @@ object GoTestCasesGenerator { val engine = GoEngine( function, sourceFile, + intSize, goExecutableAbsolutePath, eachExecutionTimeoutsMillisConfig, { timeoutExceededOrIsCanceled(index) } 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 174fbe79cf..e105388ee8 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 @@ -121,6 +121,7 @@ private object RawValuesCodes { fun convertRawExecutionResultToExecutionResult( rawExecutionResult: RawExecutionResult, functionResultTypes: List, + intSize: Int, timeoutMillis: Long ): GoUtExecutionResult { if (rawExecutionResult.timeoutExceeded) { @@ -131,7 +132,8 @@ fun convertRawExecutionResultToExecutionResult( val panicValue = if (goPrimitives.map { it.simpleName }.contains(rawResultValue.type)) { createGoUtPrimitiveModelFromRawValue( rawResultValue as PrimitiveValue, - GoPrimitiveTypeId(rawResultValue.type) + GoPrimitiveTypeId(rawResultValue.type), + intSize ) } else { error("Only primitive panic value is currently supported") @@ -158,7 +160,7 @@ fun convertRawExecutionResultToExecutionResult( if (resultType.implementsError && rawResultValue != null) { executedWithNonNilErrorString = true } - createGoUtModelFromRawValue(rawResultValue, resultType) + createGoUtModelFromRawValue(rawResultValue, resultType, intSize) } return if (executedWithNonNilErrorString) { GoUtExecutionWithNonNilError(resultValues, rawExecutionResult.trace) @@ -168,7 +170,7 @@ fun convertRawExecutionResultToExecutionResult( } private fun createGoUtModelFromRawValue( - rawValue: RawValue?, typeId: GoTypeId + rawValue: RawValue?, typeId: GoTypeId, intSize: Int ): GoUtModel = when (typeId) { // Only for error interface is GoInterfaceTypeId -> if (rawValue == null) { @@ -177,17 +179,17 @@ private fun createGoUtModelFromRawValue( GoUtPrimitiveModel((rawValue as PrimitiveValue).value, goStringTypeId) } - is GoStructTypeId -> createGoUtStructModelFromRawValue(rawValue as StructValue, typeId) + is GoStructTypeId -> createGoUtStructModelFromRawValue(rawValue as StructValue, typeId, intSize) - is GoArrayTypeId -> createGoUtArrayModelFromRawValue(rawValue as ArrayValue, typeId) + is GoArrayTypeId -> createGoUtArrayModelFromRawValue(rawValue as ArrayValue, typeId, intSize) - is GoPrimitiveTypeId -> createGoUtPrimitiveModelFromRawValue(rawValue as PrimitiveValue, typeId) + is GoPrimitiveTypeId -> createGoUtPrimitiveModelFromRawValue(rawValue as PrimitiveValue, typeId, intSize) else -> error("Creating a model from raw value of [${typeId.javaClass}] type is not supported") } private fun createGoUtPrimitiveModelFromRawValue( - resultValue: PrimitiveValue, typeId: GoPrimitiveTypeId + resultValue: PrimitiveValue, typeId: GoPrimitiveTypeId, intSize: Int ): GoUtPrimitiveModel { val rawValue = resultValue.value if (typeId == goFloat64TypeId || typeId == goFloat32TypeId) { @@ -200,20 +202,7 @@ private fun createGoUtPrimitiveModelFromRawValue( } return GoUtComplexModel(realPartModel, imagPartModel, typeId) } - val value = when (typeId.correspondingKClass) { - UByte::class -> rawValue.toUByte() - Boolean::class -> rawValue.toBoolean() - Float::class -> rawValue.toFloat() - Double::class -> rawValue.toDouble() - Int::class -> rawValue.toInt() - Short::class -> rawValue.toShort() - Long::class -> rawValue.toLong() - Byte::class -> rawValue.toByte() - UInt::class -> rawValue.toUInt() - UShort::class -> rawValue.toUShort() - ULong::class -> rawValue.toULong() - else -> rawValue - } + val value = rawValueOfGoPrimitiveTypeToValue(typeId, rawValue, intSize) return GoUtPrimitiveModel(value, typeId) } @@ -236,19 +225,19 @@ private fun convertRawFloatValueToGoUtPrimitiveModel( } private fun createGoUtStructModelFromRawValue( - resultValue: StructValue, resultTypeId: GoStructTypeId + resultValue: StructValue, resultTypeId: GoStructTypeId, intSize: Int ): GoUtStructModel { val value = resultValue.value.zip(resultTypeId.fields).map { (value, fieldId) -> - GoUtFieldModel(createGoUtModelFromRawValue(value.value, fieldId.declaringType), fieldId) + GoUtFieldModel(createGoUtModelFromRawValue(value.value, fieldId.declaringType, intSize), fieldId) } return GoUtStructModel(value, resultTypeId) } private fun createGoUtArrayModelFromRawValue( - resultValue: ArrayValue, resultTypeId: GoArrayTypeId + resultValue: ArrayValue, resultTypeId: GoArrayTypeId, intSize: Int ): GoUtArrayModel { val value = (0 until resultTypeId.length).associateWith { index -> - createGoUtModelFromRawValue(resultValue.value[index], resultTypeId.elementTypeId!!) + createGoUtModelFromRawValue(resultValue.value[index], resultTypeId.elementTypeId!!, intSize) }.toMutableMap() return GoUtArrayModel(value, resultTypeId) } \ No newline at end of file From 6d0cce83660f6eab8cc96719a1dea3dc847fb490 Mon Sep 17 00:00:00 2001 From: Egipti Pavel Date: Wed, 22 Feb 2023 17:43:25 +0300 Subject: [PATCH 3/3] Use constants in fuzzing for all primitive types except complex numbers and bool. Fix float constants --- .../org/utbot/go/api/util/GoTypesApiUtil.kt | 2 +- .../providers/GoConstantValueProvider.kt | 94 +++++++++++++++++-- .../go/gocodeanalyzer/GoSourceCodeAnalyzer.kt | 4 +- .../constant_extractor.go | 14 ++- 4 files changed, 100 insertions(+), 14 deletions(-) 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 e93e3d2450..82c00102cc 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 @@ -52,7 +52,7 @@ val goPrimitives = setOf( goUintPtrTypeId, ) -val goConstantTypes = setOf( +val goSupportedConstantTypes = setOf( goByteTypeId, goFloat32TypeId, goFloat64TypeId, diff --git a/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoConstantValueProvider.kt b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoConstantValueProvider.kt index 45e8027013..f968b801e0 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoConstantValueProvider.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoConstantValueProvider.kt @@ -3,6 +3,8 @@ package org.utbot.go.fuzzer.providers import org.utbot.fuzzing.Seed import org.utbot.fuzzing.ValueProvider import org.utbot.fuzzing.seeds.BitVectorValue +import org.utbot.fuzzing.seeds.IEEE754Value +import org.utbot.fuzzing.seeds.StringValue import org.utbot.go.GoDescription import org.utbot.go.api.GoPrimitiveTypeId import org.utbot.go.api.GoUtPrimitiveModel @@ -11,24 +13,24 @@ import org.utbot.go.framework.api.go.GoTypeId import org.utbot.go.framework.api.go.GoUtModel object GoConstantValueProvider : ValueProvider { - override fun accept(type: GoTypeId): Boolean = type in goConstantTypes + override fun accept(type: GoTypeId): Boolean = type in goSupportedConstantTypes override fun generate(description: GoDescription, type: GoTypeId): Sequence> = sequence { type.let { it as GoPrimitiveTypeId }.also { primitiveType -> val constants = description.methodUnderTest.constants val intSize = description.intSize - val primitives: List> = when (type) { - goRuneTypeId, goIntTypeId, goInt8TypeId, goInt16TypeId, goInt32TypeId, goInt64TypeId -> - (constants[type] ?: emptyList()).map { - when (type) { - goInt8TypeId -> Seed.Known(BitVectorValue.fromValue(it)) { obj: BitVectorValue -> + val primitives: List> = (constants[primitiveType] ?: emptyList()).mapNotNull { + when (primitiveType) { + goRuneTypeId, goIntTypeId, goInt8TypeId, goInt16TypeId, goInt32TypeId, goInt64TypeId -> + when (primitiveType) { + goInt8TypeId -> Seed.Known(BitVectorValue.fromValue(it)) { obj: BitVectorValue -> GoUtPrimitiveModel( obj.toByte(), primitiveType ) } - goInt16TypeId -> Seed.Known(BitVectorValue.fromValue(it)) { obj: BitVectorValue -> + goInt16TypeId -> Seed.Known(BitVectorValue.fromValue(it)) { obj: BitVectorValue -> GoUtPrimitiveModel( obj.toShort(), primitiveType @@ -58,9 +60,85 @@ object GoConstantValueProvider : ValueProvider return@sequence } + + goByteTypeId, goUintTypeId, goUintPtrTypeId, goUint8TypeId, goUint16TypeId, goUint32TypeId, goUint64TypeId -> + when (primitiveType) { + goByteTypeId, goUint8TypeId -> { + val uint8AsLong = (it as UByte).toLong() + Seed.Known(BitVectorValue.fromValue(uint8AsLong)) { obj: BitVectorValue -> + GoUtPrimitiveModel( + obj.toUByte(), + primitiveType + ) + } + } + + goUint16TypeId -> { + val uint16AsLong = (it as UShort).toLong() + Seed.Known(BitVectorValue.fromValue(uint16AsLong)) { obj: BitVectorValue -> + GoUtPrimitiveModel( + obj.toUShort(), + primitiveType + ) + } + } + + goUint32TypeId -> { + val uint32AsLong = (it as UInt).toLong() + Seed.Known(BitVectorValue.fromValue(uint32AsLong)) { obj: BitVectorValue -> + GoUtPrimitiveModel( + obj.toUInt(), + primitiveType + ) + } + } + + goUintTypeId, goUintPtrTypeId -> { + val uintAsLong = if (intSize == 32) (it as UInt).toLong() else (it as ULong).toLong() + Seed.Known(BitVectorValue.fromValue(uintAsLong)) { obj: BitVectorValue -> + GoUtPrimitiveModel( + if (intSize == 32) obj.toUInt() else obj.toULong(), + primitiveType + ) + } + } + + goUint64TypeId -> { + val uint64AsLong = (it as ULong).toLong() + Seed.Known(BitVectorValue.fromValue(uint64AsLong)) { obj: BitVectorValue -> + GoUtPrimitiveModel( + obj.toULong(), + primitiveType + ) + } + } + + else -> return@sequence + } + + goFloat32TypeId -> Seed.Known(IEEE754Value.fromValue(it)) { obj: IEEE754Value -> + GoUtPrimitiveModel( + obj.toFloat(), + primitiveType + ) + } + + goFloat64TypeId -> Seed.Known(IEEE754Value.fromValue(it)) { obj: IEEE754Value -> + GoUtPrimitiveModel( + obj.toDouble(), + primitiveType + ) + } + + goStringTypeId -> Seed.Known(StringValue(it as String)) { obj: StringValue -> + GoUtPrimitiveModel( + obj.value, + primitiveType + ) } - else -> emptyList() + else -> null + } } primitives.forEach { seed -> 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 8f7ce9b92f..8d77d513a4 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 @@ -6,7 +6,7 @@ import org.utbot.go.api.GoPrimitiveTypeId import org.utbot.go.api.GoUtFile import org.utbot.go.api.GoUtFunction import org.utbot.go.api.GoUtFunctionParameter -import org.utbot.go.api.util.goConstantTypes +import org.utbot.go.api.util.goSupportedConstantTypes import org.utbot.go.api.util.rawValueOfGoPrimitiveTypeToValue import org.utbot.go.framework.api.go.GoTypeId import org.utbot.go.util.executeCommandByNewProcessOrFail @@ -81,7 +81,7 @@ object GoSourceCodeAnalyzer { val constants = mutableMapOf>() analyzedFunction.constants.map { (type, rawValues) -> val typeId = GoPrimitiveTypeId(type) - if (typeId !in goConstantTypes) { + if (typeId !in goSupportedConstantTypes) { error("Constants extraction: $type is a unsupported constant type") } val values = rawValues.map { rawValue -> diff --git a/utbot-go/src/main/resources/go_source_code_analyzer/constant_extractor.go b/utbot-go/src/main/resources/go_source_code_analyzer/constant_extractor.go index aeedc9f59f..b8f12389d5 100644 --- a/utbot-go/src/main/resources/go_source_code_analyzer/constant_extractor.go +++ b/utbot-go/src/main/resources/go_source_code_analyzer/constant_extractor.go @@ -1,7 +1,9 @@ package main import ( + "fmt" "go/ast" + "go/constant" "go/types" ) @@ -36,10 +38,16 @@ func (e *ConstantExtractor) Visit(node ast.Node) ast.Visitor { e.constants[basicType.Name()] = append(e.constants[basicType.Name()], typeAndValue.Value.String()) case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr: e.constants[basicType.Name()] = append(e.constants[basicType.Name()], typeAndValue.Value.String()) - case types.Float32, types.Float64: - e.constants[basicType.Name()] = append(e.constants[basicType.Name()], typeAndValue.Value.String()) + case types.Float32: + if f32, ok := constant.Float32Val(typeAndValue.Value); ok { + e.constants[basicType.Name()] = append(e.constants[basicType.Name()], fmt.Sprintf("%v", f32)) + } + case types.Float64: + if f64, ok := constant.Float64Val(typeAndValue.Value); ok { + e.constants[basicType.Name()] = append(e.constants[basicType.Name()], fmt.Sprintf("%v", f64)) + } case types.String: - e.constants[basicType.Name()] = append(e.constants[basicType.Name()], typeAndValue.Value.String()) + e.constants[basicType.Name()] = append(e.constants[basicType.Name()], constant.StringVal(typeAndValue.Value)) } return e