From 0924faf61de9123433d466f06dd7390a8ed36b1c Mon Sep 17 00:00:00 2001 From: Egipti Pavel Date: Mon, 6 Mar 2023 21:23:49 +0300 Subject: [PATCH 1/4] fix GoFileCodeBuilder --- .../org/utbot/go/simplecodegeneration/GoFileCodeBuilder.kt | 3 +++ 1 file changed, 3 insertions(+) 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 d4040c713e..466a084c73 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 @@ -24,6 +24,9 @@ class GoFileCodeBuilder( } fun buildCodeString(): String { + if (importLines.isEmpty()) { + return "$packageLine\n\n${topLevelElements.joinToString(separator = "\n\n")}" + } return "$packageLine\n\n$importLines\n\n${topLevelElements.joinToString(separator = "\n\n")}" } From 1d82e9ceb39c10b166039c3cf61d0d3032f4472f Mon Sep 17 00:00:00 2001 From: Egipti Pavel Date: Tue, 7 Mar 2023 15:31:33 +0300 Subject: [PATCH 2/4] Refactor Go code --- .../org/utbot/go/worker/GoCodeTemplates.kt | 4 +- .../go/worker/GoWorkerCodeGenerationHelper.kt | 4 +- .../go_source_code_analyzer/analyzer_core.go | 208 +++++++++--------- .../resources/go_source_code_analyzer/main.go | 26 ++- 4 files changed, 123 insertions(+), 119 deletions(-) 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 4b3d6cb810..c8dc42d8b5 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 @@ -603,9 +603,7 @@ object GoCodeTemplates { private val convertParsedJsonToRawValueFunction = """ //goland:noinspection GoPreferNilSlice - func __convertParsedJsonToRawValue__(p map[string]interface{}) (__RawValue__, error) { - rawValue := p - + func __convertParsedJsonToRawValue__(rawValue map[string]interface{}) (__RawValue__, error) { typeName, ok := rawValue["type"] if !ok { return nil, fmt.Errorf("every rawValue must contain field 'type'") 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 b36835654a..b46b650f15 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 @@ -114,12 +114,12 @@ internal object GoWorkerCodeGenerationHelper { con, err := net.Dial("tcp", ":$port") __checkErrorAndExit__(err) - defer func(con net.Conn) { + defer func() { err := con.Close() if err != nil { __checkErrorAndExit__(err) } - }(con) + }() jsonDecoder := json.NewDecoder(con) for { 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 28a2c3aeb6..5c6ff40ac7 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 @@ -5,53 +5,31 @@ import ( "errors" "fmt" "go/ast" - "go/importer" - "go/parser" "go/printer" "go/token" "go/types" "sort" + "sync" ) var errorInterface = func() *types.Interface { - // TODO not sure if it's best way - src := `package src - -import "errors" - -var x = errors.New("") -` - fset := token.NewFileSet() - fileAst, astErr := parser.ParseFile(fset, "", src, 0) - checkError(astErr) - typesConfig := types.Config{Importer: importer.Default()} - info := &types.Info{ - Defs: make(map[*ast.Ident]types.Object), - Uses: make(map[*ast.Ident]types.Object), - Types: make(map[ast.Expr]types.TypeAndValue), - } - _, typesCheckErr := typesConfig.Check("", fset, []*ast.File{fileAst}, info) - checkError(typesCheckErr) - for _, obj := range info.Defs { - if obj != nil { - return obj.Type().Underlying().(*types.Interface) - } - } - return nil + variable := types.NewVar(token.NoPos, nil, "", types.Typ[types.String]) + results := types.NewTuple(variable) + signature := types.NewSignatureType(nil, nil, nil, nil, results, false) + method := types.NewFunc(token.NoPos, nil, "Error", signature) + return types.NewInterfaceType([]*types.Func{method}, nil) }() -func implementsError(typ *types.Named) bool { +func implementsError(typ types.Type) bool { return types.Implements(typ, errorInterface) } //goland:noinspection GoPreferNilSlice func toAnalyzedType(typ types.Type) (AnalyzedType, error) { var result AnalyzedType - underlyingType := typ.Underlying() - switch underlyingType.(type) { + switch underlyingType := typ.Underlying().(type) { case *types.Basic: - basicType := underlyingType.(*types.Basic) - name := basicType.Name() + name := underlyingType.Name() result = AnalyzedPrimitiveType{Name: name} case *types.Struct: namedType := typ.(*types.Named) @@ -59,10 +37,9 @@ func toAnalyzedType(typ types.Type) (AnalyzedType, error) { pkg := namedType.Obj().Pkg() isError := implementsError(namedType) - structType := underlyingType.(*types.Struct) fields := []AnalyzedField{} - for i := 0; i < structType.NumFields(); i++ { - field := structType.Field(i) + for i := 0; i < underlyingType.NumFields(); i++ { + field := underlyingType.Field(i) fieldType, err := toAnalyzedType(field.Type()) checkError(err) @@ -151,7 +128,7 @@ func checkTypeIsSupported(typ types.Type, isResultType bool) bool { return checkTypeIsSupported(sliceType.Elem(), isResultType) } if interfaceType, ok := underlyingType.(*types.Interface); ok && isResultType { - return interfaceType == errorInterface + return implementsError(interfaceType) } return false } @@ -216,95 +193,112 @@ func collectTargetAnalyzedFunctions( } } + var wg sync.WaitGroup + var mutex sync.Mutex + 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{}, - RequiredImports: []Import{}, - position: typedObj.Pos(), - } + wg.Add(1) + go func(ident *ast.Ident, typeObj *types.Func) { + defer wg.Done() + + analyzedFunction := AnalyzedFunction{ + Name: typedObj.Name(), + ModifiedName: createNewFunctionName(typedObj.Name()), + Parameters: []AnalyzedFunctionParameter{}, + ResultTypes: []AnalyzedType{}, + RequiredImports: []Import{}, + position: typedObj.Pos(), + } - if isFound, ok := foundTargetFunctionsNamesMap[analyzedFunction.Name]; !ok || isFound { - continue - } else { - foundTargetFunctionsNamesMap[analyzedFunction.Name] = true - } + mutex.Lock() + if isFound, ok := foundTargetFunctionsNamesMap[analyzedFunction.Name]; !ok || isFound { + mutex.Unlock() + return + } else { + foundTargetFunctionsNamesMap[analyzedFunction.Name] = true + mutex.Unlock() + } - signature := typedObj.Type().(*types.Signature) - if !checkIsSupported(signature) { - notSupportedFunctionsNames = append(notSupportedFunctionsNames, analyzedFunction.Name) - continue - } - if parameters := signature.Params(); parameters != nil { - for i := 0; i < parameters.Len(); i++ { - parameter := parameters.At(i) - - parameterType, err := toAnalyzedType(parameter.Type()) - checkError(err) - - analyzedFunction.Parameters = append(analyzedFunction.Parameters, - AnalyzedFunctionParameter{ - Name: parameter.Name(), - Type: parameterType, - }, - ) + signature := typedObj.Type().(*types.Signature) + if !checkIsSupported(signature) { + mutex.Lock() + notSupportedFunctionsNames = append(notSupportedFunctionsNames, analyzedFunction.Name) + mutex.Unlock() + return } - } - if results := signature.Results(); results != nil { - for i := 0; i < results.Len(); i++ { - result := results.At(i) + if parameters := signature.Params(); parameters != nil { + for i := 0; i < parameters.Len(); i++ { + parameter := parameters.At(i) + + parameterType, err := toAnalyzedType(parameter.Type()) + checkError(err) + + analyzedFunction.Parameters = append(analyzedFunction.Parameters, + AnalyzedFunctionParameter{ + Name: parameter.Name(), + Type: parameterType, + }, + ) + } + } + if results := signature.Results(); results != nil { + for i := 0; i < results.Len(); i++ { + result := results.At(i) - resultType, err := toAnalyzedType(result.Type()) - checkError(err) + resultType, err := toAnalyzedType(result.Type()) + checkError(err) - analyzedFunction.ResultTypes = append(analyzedFunction.ResultTypes, resultType) + analyzedFunction.ResultTypes = append(analyzedFunction.ResultTypes, resultType) + } } - } - funcDecl := ident.Obj.Decl.(*ast.FuncDecl) - funcDecl.Name = ast.NewIdent(analyzedFunction.ModifiedName) + 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 + constantExtractor := ConstantExtractor{info: info, constants: map[string][]string{}} + ast.Walk(&constantExtractor, funcDecl) + analyzedFunction.Constants = constantExtractor.constants - functionModifier := FunctionModifier{lineCounter: 0} - ast.Walk(&functionModifier, funcDecl) + 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 - } + importsCollector := ImportsCollector{ + info: info, + requiredImports: map[Import]bool{}, + allImportsInFile: allImportsInFile, + sourcePackage: sourcePackage, + } + ast.Walk(&importsCollector, funcDecl) + for _, i := range blankImports { + importsCollector.requiredImports[i] = true + } - var modifiedFunction bytes.Buffer - cfg := printer.Config{ - Mode: printer.TabIndent, - Tabwidth: 4, - Indent: 0, - } - err := cfg.Fprint(&modifiedFunction, fset, funcDecl) - checkError(err) + var modifiedFunction bytes.Buffer + cfg := printer.Config{ + Mode: printer.TabIndent, + Tabwidth: 4, + Indent: 0, + } + 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 = functionModifier.lineCounter + for i := range importsCollector.requiredImports { + analyzedFunction.RequiredImports = append(analyzedFunction.RequiredImports, i) + } + analyzedFunction.ModifiedFunctionForCollectingTraces = modifiedFunction.String() + analyzedFunction.NumberOfAllStatements = functionModifier.lineCounter - analyzedFunctions = append(analyzedFunctions, analyzedFunction) + mutex.Lock() + analyzedFunctions = append(analyzedFunctions, analyzedFunction) + mutex.Unlock() + }(ident, typedObj) } } + wg.Wait() + for functionName, isFound := range foundTargetFunctionsNamesMap { if !isFound { notFoundFunctionsNames = append(notFoundFunctionsNames, functionName) @@ -313,8 +307,8 @@ func collectTargetAnalyzedFunctions( sort.Slice(analyzedFunctions, func(i, j int) bool { return analyzedFunctions[i].position < analyzedFunctions[j].position }) - sort.Sort(sort.StringSlice(notSupportedFunctionsNames)) - sort.Sort(sort.StringSlice(notFoundFunctionsNames)) + sort.Strings(notSupportedFunctionsNames) + sort.Strings(notFoundFunctionsNames) return analyzedFunctions, notSupportedFunctionsNames, notFoundFunctionsNames } 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 a1dae5fd48..58e51573f8 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 @@ -10,6 +10,7 @@ import ( "os" "path/filepath" "strconv" + "sync" "golang.org/x/tools/go/packages" ) @@ -124,15 +125,26 @@ func main() { checkError(fromJsonErr) // parse each requested Go source file - analysisResults := AnalysisResults{ - IntSize: strconv.IntSize, - Results: []AnalysisResult{}, + var wg sync.WaitGroup + + results := make([]AnalysisResult, len(analysisTargets.Targets)) + for i, target := range analysisTargets.Targets { + wg.Add(1) + go func(i int, target AnalysisTarget) { + defer wg.Done() + + result, err := analyzeTarget(target) + checkError(err) + + results[i] = *result + }(i, target) } - for _, target := range analysisTargets.Targets { - result, err := analyzeTarget(target) - checkError(err) + + wg.Wait() - analysisResults.Results = append(analysisResults.Results, *result) + analysisResults := AnalysisResults{ + IntSize: strconv.IntSize, + Results: results, } // serialize and write results From fcdaa68cf8f00d13197749dd82dd195be7ce5f20 Mon Sep 17 00:00:00 2001 From: Egipti Pavel Date: Wed, 8 Mar 2023 15:45:01 +0300 Subject: [PATCH 3/4] Fix infinity loop --- .../go/gocodeanalyzer/AnalysisResults.kt | 2 +- .../go/gocodeanalyzer/GoSourceCodeAnalyzer.kt | 7 +- .../AbstractGoUtTestsGenerationController.kt | 3 +- .../utbot/go/logic/GoTestCasesGenerator.kt | 2 + .../GoTestCasesCodeGenerator.kt | 35 ++++----- .../org/utbot/go/worker/GoCodeTemplates.kt | 28 +++---- .../go/worker/GoWorkerCodeGenerationHelper.kt | 19 +++-- .../analysis_results.go | 5 +- .../go_source_code_analyzer/analyzer_core.go | 10 +-- .../function_modifier.go | 73 +++++++++++++++++-- .../resources/go_source_code_analyzer/main.go | 9 ++- 11 files changed, 133 insertions(+), 60 deletions(-) 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 14a461f742..d3e60a9b48 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 @@ -115,4 +115,4 @@ internal data class AnalysisResult( val notFoundFunctionsNames: List ) -internal data class AnalysisResults(val intSize: Int, val results: List) \ No newline at end of file +internal data class AnalysisResults(val results: List, val intSize: Int, val maxTraceLength: Int) \ No newline at end of file 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 8d77d513a4..3319cb7a43 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 @@ -31,7 +31,7 @@ object GoSourceCodeAnalyzer { fun analyzeGoSourceFilesForFunctions( targetFunctionsNamesBySourceFiles: Map>, goExecutableAbsolutePath: String - ): Pair, Int> { + ): Triple, Int, Int> { val analysisTargets = AnalysisTargets( targetFunctionsNamesBySourceFiles.map { (absoluteFilePath, targetFunctionsNames) -> AnalysisTarget(absoluteFilePath, targetFunctionsNames) @@ -67,7 +67,8 @@ object GoSourceCodeAnalyzer { ) val analysisResults = parseFromJsonOrFail(analysisResultsFile) val intSize = analysisResults.intSize - return analysisResults.results.map { analysisResult -> + val maxTraceLength = analysisResults.maxTraceLength + return Triple(analysisResults.results.map { analysisResult -> GoUtFile(analysisResult.absoluteFilePath, analysisResult.sourcePackage) to analysisResult }.associateBy({ (sourceFile, _) -> sourceFile }) { (sourceFile, analysisResult) -> val functions = analysisResult.analyzedFunctions.map { analyzedFunction -> @@ -106,7 +107,7 @@ object GoSourceCodeAnalyzer { analysisResult.notSupportedFunctionsNames, analysisResult.notFoundFunctionsNames ) - } to intSize + }, intSize, maxTraceLength) } 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 709c550e73..663f9b2978 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, intSize) = GoSourceCodeAnalyzer.analyzeGoSourceFilesForFunctions( + val (analysisResults, intSize, maxTraceLength) = GoSourceCodeAnalyzer.analyzeGoSourceFilesForFunctions( selectedFunctionsNamesBySourceFiles, testsGenerationConfig.goExecutableAbsolutePath ) @@ -32,6 +32,7 @@ abstract class AbstractGoUtTestsGenerationController { sourceFile, functions, intSize, + maxTraceLength, testsGenerationConfig.goExecutableAbsolutePath, testsGenerationConfig.eachFunctionExecutionTimeoutMillis ) { 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 aaf49f25a6..161bf838e7 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 @@ -28,6 +28,7 @@ object GoTestCasesGenerator { sourceFile: GoUtFile, functions: List, intSize: Int, + maxTraceLength: Int, goExecutableAbsolutePath: String, eachExecutionTimeoutMillis: Long, connectionTimeoutMillis: Long = 10000, @@ -52,6 +53,7 @@ object GoTestCasesGenerator { functions, eachExecutionTimeoutMillis, serverSocket.localPort, + maxTraceLength, imports ) fileWithModifiedFunctions = GoWorkerCodeGenerationHelper.createFileWithModifiedFunctions( 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 04357e628e..31d0aef4c6 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,28 +18,29 @@ object GoTestCasesCodeGenerator { fun generateTestCasesFileCode(sourceFile: GoUtFile, testCases: List): String { val destinationPackage = sourceFile.sourcePackage - if (testCases.isEmpty() || testCases.all { it.executionResult is GoUtTimeoutExceeded }) { - return GoFileCodeBuilder(destinationPackage, emptySet()).buildCodeString() - } - val requiredPackages = mutableSetOf() - testCases.forEach { testCase -> - testCase.parametersValues.forEach { - requiredPackages += it.getRequiredPackages(destinationPackage) - } - when (val executionResult = testCase.executionResult) { - is GoUtExecutionCompleted -> executionResult.models.forEach { + val imports = if (testCases.isEmpty() || testCases.all { it.executionResult is GoUtTimeoutExceeded }) { + emptySet() + } else { + val requiredPackages = mutableSetOf() + 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 -> requiredPackages += executionResult.panicValue.getRequiredPackages( - destinationPackage - ) + is GoUtPanicFailure -> requiredPackages += executionResult.panicValue.getRequiredPackages( + destinationPackage + ) + } } - } - val imports = GoImportsResolver.resolveImportsBasedOnRequiredPackages( - requiredPackages, destinationPackage, alwaysRequiredImports - ) + GoImportsResolver.resolveImportsBasedOnRequiredPackages( + requiredPackages, destinationPackage, alwaysRequiredImports + ) + } val fileBuilder = GoFileCodeBuilder(destinationPackage, imports) val aliases = imports.associate { (goPackage, alias) -> goPackage to alias } val goUtModelToCodeConverter = GoUtModelToCodeConverter(destinationPackage, aliases) 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 c8dc42d8b5..01cf8760f7 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 @@ -5,10 +5,6 @@ import org.utbot.go.framework.api.go.GoPackage object GoCodeTemplates { - val traces = """ - var __traces__ []int - """.trimIndent() - private val testInputStruct = """ type __TestInput__ struct { FunctionName string `json:"functionName"` @@ -354,7 +350,7 @@ object GoCodeTemplates { TimeoutExceeded bool `json:"timeoutExceeded"` RawResultValues []__RawValue__ `json:"rawResultValues"` PanicMessage *__RawPanicMessage__ `json:"panicMessage"` - Trace []int `json:"trace"` + Trace []uint16 `json:"trace"` } """.trimIndent() @@ -491,20 +487,24 @@ object GoCodeTemplates { } """.trimIndent() - private val executeFunctionFunction = """ + private fun executeFunctionFunction(maxTraceLength: Int) = """ func __executeFunction__( - timeoutMillis time.Duration, wrappedFunction func() []__RawValue__, + timeout time.Duration, arguments []reflect.Value, wrappedFunction func([]reflect.Value) []__RawValue__, ) __RawExecutionResult__ { - ctxWithTimeout, cancel := context.WithTimeout(context.Background(), timeoutMillis) + ctxWithTimeout, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() + trace := make([]uint16, 0, $maxTraceLength) + done := make(chan __RawExecutionResult__, 1) go func() { executionResult := __RawExecutionResult__{ TimeoutExceeded: false, RawResultValues: []__RawValue__{}, PanicMessage: nil, + Trace: []uint16{}, } + panicked := true defer func() { panicMessage := recover() @@ -526,11 +526,12 @@ object GoCodeTemplates { ImplementsError: implementsError, } } - executionResult.Trace = __traces__ + executionResult.Trace = trace done <- executionResult }() - resultValues := wrappedFunction() + argumentsWithTrace := append(arguments, reflect.ValueOf(&trace)) + resultValues := wrappedFunction(argumentsWithTrace) executionResult.RawResultValues = resultValues panicked = false }() @@ -543,7 +544,7 @@ object GoCodeTemplates { TimeoutExceeded: true, RawResultValues: []__RawValue__{}, PanicMessage: nil, - Trace: __traces__, + Trace: trace, } } } @@ -767,7 +768,8 @@ object GoCodeTemplates { fun getTopLevelHelperStructsAndFunctionsForWorker( structTypes: Set, destinationPackage: GoPackage, - aliases: Map + aliases: Map, + maxTraceLength: Int, ) = listOf( testInputStruct, rawValueInterface, @@ -786,7 +788,7 @@ object GoCodeTemplates { checkErrorFunction, convertFloat64ValueToStringFunction, convertReflectValueToRawValueFunction, - executeFunctionFunction, + executeFunctionFunction(maxTraceLength), wrapResultValuesForWorkerFunction, convertRawValuesToReflectValuesFunction, parseJsonToFunctionNameAndRawValuesFunction, 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 b46b650f15..1d4e7b6806 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 @@ -34,6 +34,7 @@ internal object GoWorkerCodeGenerationHelper { functions: List, eachExecutionTimeoutMillis: Long, port: Int, + maxTraceLength: Int, imports: Set ): File { val fileToExecuteName = createFileToExecuteName(sourceFile) @@ -41,7 +42,14 @@ internal object GoWorkerCodeGenerationHelper { val fileToExecute = sourceFileDir.resolve(fileToExecuteName) val fileToExecuteGoCode = - generateWorkerTestFileGoCode(sourceFile, functions, eachExecutionTimeoutMillis, port, imports) + generateWorkerTestFileGoCode( + sourceFile, + functions, + eachExecutionTimeoutMillis, + port, + maxTraceLength, + imports + ) fileToExecute.writeText(fileToExecuteGoCode) return fileToExecute } @@ -72,6 +80,7 @@ internal object GoWorkerCodeGenerationHelper { functions: List, eachExecutionTimeoutMillis: Long, port: Int, + maxTraceLength: Int, imports: Set ): String { val destinationPackage = sourceFile.sourcePackage @@ -87,7 +96,8 @@ internal object GoWorkerCodeGenerationHelper { GoCodeTemplates.getTopLevelHelperStructsAndFunctionsForWorker( structTypes, destinationPackage, - aliases + aliases, + maxTraceLength, ) + workerTestFunctionCode ) @@ -101,7 +111,7 @@ internal object GoWorkerCodeGenerationHelper { } val fileCodeBuilder = GoFileCodeBuilder(destinationPackage, imports) fileCodeBuilder.addTopLevelElements( - listOf(GoCodeTemplates.traces) + functions.map { it.modifiedFunctionForCollectingTraces } + functions.map { it.modifiedFunctionForCollectingTraces } ) return fileCodeBuilder.buildCodeString() } @@ -142,8 +152,7 @@ internal object GoWorkerCodeGenerationHelper { panic(fmt.Sprintf("no function with that name: %s", funcName)) } - executionResult := __executeFunction__($eachExecutionTimeoutMillis*time.Millisecond, func() []__RawValue__ { - __traces__ = make([]int, 0, 100) + executionResult := __executeFunction__($eachExecutionTimeoutMillis*time.Millisecond, arguments, func(arguments []reflect.Value) []__RawValue__ { return __wrapResultValuesForUtBotGoWorker__(function.Call(arguments)) }) 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 8e54f34d8e..f0410d0add 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 @@ -88,6 +88,7 @@ type AnalysisResult struct { } type AnalysisResults struct { - IntSize int `json:"intSize"` - Results []AnalysisResult `json:"results"` + Results []AnalysisResult `json:"results"` + IntSize int `json:"intSize"` + MaxTraceLength int `json:"maxTraceLength"` } 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 5c6ff40ac7..d1c99101e1 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 @@ -162,10 +162,6 @@ func checkIsSupported(signature *types.Signature) bool { return true } -func createNewFunctionName(funcName string) string { - return "__" + funcName + "__" -} - func collectTargetAnalyzedFunctions( fset *token.FileSet, info *types.Info, @@ -205,7 +201,6 @@ func collectTargetAnalyzedFunctions( analyzedFunction := AnalyzedFunction{ Name: typedObj.Name(), - ModifiedName: createNewFunctionName(typedObj.Name()), Parameters: []AnalyzedFunctionParameter{}, ResultTypes: []AnalyzedType{}, RequiredImports: []Import{}, @@ -255,13 +250,13 @@ 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} + functionModifier := FunctionModifier{maxTraceLen: MaxTraceLength} + functionModifier.ModifyFunctionDeclaration(funcDecl) ast.Walk(&functionModifier, funcDecl) importsCollector := ImportsCollector{ @@ -284,6 +279,7 @@ func collectTargetAnalyzedFunctions( err := cfg.Fprint(&modifiedFunction, fset, funcDecl) checkError(err) + analyzedFunction.ModifiedName = funcDecl.Name.String() for i := range importsCollector.requiredImports { analyzedFunction.RequiredImports = append(analyzedFunction.RequiredImports, i) } diff --git a/utbot-go/src/main/resources/go_source_code_analyzer/function_modifier.go b/utbot-go/src/main/resources/go_source_code_analyzer/function_modifier.go index 16f18bcf30..bc9234f87e 100644 --- a/utbot-go/src/main/resources/go_source_code_analyzer/function_modifier.go +++ b/utbot-go/src/main/resources/go_source_code_analyzer/function_modifier.go @@ -8,6 +8,38 @@ import ( type FunctionModifier struct { lineCounter int + maxTraceLen int +} + +func createNewFunctionName(funcDecl *ast.FuncDecl) *ast.Ident { + funcName := funcDecl.Name.String() + return ast.NewIdent("__" + funcName + "__") +} + +func (v *FunctionModifier) ModifyFunctionDeclaration(funcDecl *ast.FuncDecl) { + funcDecl.Name = createNewFunctionName(funcDecl) + + traceName := ast.NewIdent("__trace__") + obj := ast.Object{ + Kind: ast.Var, + Name: "__trace__", + Decl: &traceName, + } + traceName.Obj = &obj + + traceParam := ast.Field{ + Names: []*ast.Ident{ + traceName, + }, + Type: &ast.StarExpr{ + X: &ast.ArrayType{ + Elt: ast.NewIdent("uint16"), + }, + }, + } + + params := &funcDecl.Type.Params.List + *params = append(*params, &traceParam) } func (v *FunctionModifier) Visit(node ast.Node) ast.Visitor { @@ -124,14 +156,39 @@ func (v *FunctionModifier) addLinesWithLoggingInTraceBeforeFirstReturnStatement( func (v *FunctionModifier) newLineWithLoggingInTrace() ast.Stmt { v.lineCounter++ - traces := ast.NewIdent("__traces__") - return &ast.AssignStmt{ - Lhs: []ast.Expr{traces}, - Tok: token.ASSIGN, - Rhs: []ast.Expr{ - &ast.CallExpr{ - Fun: ast.NewIdent("append"), - Args: []ast.Expr{traces, ast.NewIdent(strconv.Itoa(v.lineCounter))}, + trace := ast.StarExpr{ + X: ast.NewIdent("__trace__"), + } + return &ast.IfStmt{ + Cond: &ast.BinaryExpr{ + X: &ast.CallExpr{ + Fun: ast.NewIdent("len"), + Args: []ast.Expr{&trace}, + }, + Op: token.LSS, + Y: &ast.BasicLit{ + Kind: token.INT, + Value: strconv.Itoa(v.maxTraceLen), + }, + }, + Body: &ast.BlockStmt{ + List: []ast.Stmt{ + &ast.AssignStmt{ + Lhs: []ast.Expr{&trace}, + Tok: token.ASSIGN, + Rhs: []ast.Expr{ + &ast.CallExpr{ + Fun: ast.NewIdent("append"), + Args: []ast.Expr{ + &trace, + &ast.BasicLit{ + Kind: token.INT, + Value: strconv.Itoa(v.lineCounter), + }, + }, + }, + }, + }, }, }, } 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 58e51573f8..da5b1221c9 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 @@ -15,6 +15,8 @@ import ( "golang.org/x/tools/go/packages" ) +const MaxTraceLength = 1024 + func checkError(err error) { if err != nil { log.Fatal(err.Error()) @@ -139,12 +141,13 @@ func main() { results[i] = *result }(i, target) } - + wg.Wait() analysisResults := AnalysisResults{ - IntSize: strconv.IntSize, - Results: results, + Results: results, + IntSize: strconv.IntSize, + MaxTraceLength: MaxTraceLength, } // serialize and write results From 8103875e94d709c459c528220ab7e2057a03f312 Mon Sep 17 00:00:00 2001 From: Egipti Pavel Date: Thu, 9 Mar 2023 12:00:46 +0300 Subject: [PATCH 4/4] Fix --- .../utbot/go/gocodeanalyzer/GoSourceCodeAnalyzer.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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 3319cb7a43..4ae8a5b0bd 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 @@ -23,6 +23,12 @@ object GoSourceCodeAnalyzer { val notFoundFunctionsNames: List ) + data class GoSourceCodeAnalyzerResult( + val analysisResults: Map, + val intSize: Int, + val maxTraceLength: Int, + ) + /** * Takes map from absolute paths of Go source files to names of their selected functions. * @@ -31,7 +37,7 @@ object GoSourceCodeAnalyzer { fun analyzeGoSourceFilesForFunctions( targetFunctionsNamesBySourceFiles: Map>, goExecutableAbsolutePath: String - ): Triple, Int, Int> { + ): GoSourceCodeAnalyzerResult { val analysisTargets = AnalysisTargets( targetFunctionsNamesBySourceFiles.map { (absoluteFilePath, targetFunctionsNames) -> AnalysisTarget(absoluteFilePath, targetFunctionsNames) @@ -68,7 +74,7 @@ object GoSourceCodeAnalyzer { val analysisResults = parseFromJsonOrFail(analysisResultsFile) val intSize = analysisResults.intSize val maxTraceLength = analysisResults.maxTraceLength - return Triple(analysisResults.results.map { analysisResult -> + return GoSourceCodeAnalyzerResult(analysisResults.results.map { analysisResult -> GoUtFile(analysisResult.absoluteFilePath, analysisResult.sourcePackage) to analysisResult }.associateBy({ (sourceFile, _) -> sourceFile }) { (sourceFile, analysisResult) -> val functions = analysisResult.analyzedFunctions.map { analyzedFunction ->