Skip to content

Go. Fix tests generation for functions with infinity loop #1911

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,4 @@ internal data class AnalysisResult(
val notFoundFunctionsNames: List<String>
)

internal data class AnalysisResults(val intSize: Int, val results: List<AnalysisResult>)
internal data class AnalysisResults(val results: List<AnalysisResult>, val intSize: Int, val maxTraceLength: Int)
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ object GoSourceCodeAnalyzer {
val notFoundFunctionsNames: List<String>
)

data class GoSourceCodeAnalyzerResult(
val analysisResults: Map<GoUtFile, GoSourceFileAnalysisResult>,
val intSize: Int,
val maxTraceLength: Int,
)

/**
* Takes map from absolute paths of Go source files to names of their selected functions.
*
Expand All @@ -31,7 +37,7 @@ object GoSourceCodeAnalyzer {
fun analyzeGoSourceFilesForFunctions(
targetFunctionsNamesBySourceFiles: Map<String, List<String>>,
goExecutableAbsolutePath: String
): Pair<Map<GoUtFile, GoSourceFileAnalysisResult>, Int> {
): GoSourceCodeAnalyzerResult {
val analysisTargets = AnalysisTargets(
targetFunctionsNamesBySourceFiles.map { (absoluteFilePath, targetFunctionsNames) ->
AnalysisTarget(absoluteFilePath, targetFunctionsNames)
Expand Down Expand Up @@ -67,7 +73,8 @@ object GoSourceCodeAnalyzer {
)
val analysisResults = parseFromJsonOrFail<AnalysisResults>(analysisResultsFile)
val intSize = analysisResults.intSize
return analysisResults.results.map { analysisResult ->
val maxTraceLength = analysisResults.maxTraceLength
return GoSourceCodeAnalyzerResult(analysisResults.results.map { analysisResult ->
GoUtFile(analysisResult.absoluteFilePath, analysisResult.sourcePackage) to analysisResult
}.associateBy({ (sourceFile, _) -> sourceFile }) { (sourceFile, analysisResult) ->
val functions = analysisResult.analyzedFunctions.map { analyzedFunction ->
Expand Down Expand Up @@ -106,7 +113,7 @@ object GoSourceCodeAnalyzer {
analysisResult.notSupportedFunctionsNames,
analysisResult.notFoundFunctionsNames
)
} to intSize
}, intSize, maxTraceLength)
} finally {
// TODO correctly?
analysisTargetsFile.delete()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand All @@ -32,6 +32,7 @@ abstract class AbstractGoUtTestsGenerationController {
sourceFile,
functions,
intSize,
maxTraceLength,
testsGenerationConfig.goExecutableAbsolutePath,
testsGenerationConfig.eachFunctionExecutionTimeoutMillis
) { index -> isCanceled() || System.currentTimeMillis() - (startTimeMillis + (index + 1) * functionTimeoutStepMillis) > 0 }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ object GoTestCasesGenerator {
sourceFile: GoUtFile,
functions: List<GoUtFunction>,
intSize: Int,
maxTraceLength: Int,
goExecutableAbsolutePath: String,
eachExecutionTimeoutMillis: Long,
connectionTimeoutMillis: Long = 10000,
Expand All @@ -52,6 +53,7 @@ object GoTestCasesGenerator {
functions,
eachExecutionTimeoutMillis,
serverSocket.localPort,
maxTraceLength,
imports
)
fileWithModifiedFunctions = GoWorkerCodeGenerationHelper.createFileWithModifiedFunctions(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")}"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,29 @@ object GoTestCasesCodeGenerator {

fun generateTestCasesFileCode(sourceFile: GoUtFile, testCases: List<GoUtFuzzedFunctionTestCase>): String {
val destinationPackage = sourceFile.sourcePackage
if (testCases.isEmpty() || testCases.all { it.executionResult is GoUtTimeoutExceeded }) {
return GoFileCodeBuilder(destinationPackage, emptySet()).buildCodeString()
}
val requiredPackages = mutableSetOf<GoPackage>()
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<GoPackage>()
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)
Expand Down
32 changes: 16 additions & 16 deletions utbot-go/src/main/kotlin/org/utbot/go/worker/GoCodeTemplates.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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()
Expand All @@ -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
}()
Expand All @@ -543,7 +544,7 @@ object GoCodeTemplates {
TimeoutExceeded: true,
RawResultValues: []__RawValue__{},
PanicMessage: nil,
Trace: __traces__,
Trace: trace,
}
}
}
Expand Down Expand Up @@ -603,9 +604,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'")
Expand Down Expand Up @@ -769,7 +768,8 @@ object GoCodeTemplates {
fun getTopLevelHelperStructsAndFunctionsForWorker(
structTypes: Set<GoStructTypeId>,
destinationPackage: GoPackage,
aliases: Map<GoPackage, String?>
aliases: Map<GoPackage, String?>,
maxTraceLength: Int,
) = listOf(
testInputStruct,
rawValueInterface,
Expand All @@ -788,7 +788,7 @@ object GoCodeTemplates {
checkErrorFunction,
convertFloat64ValueToStringFunction,
convertReflectValueToRawValueFunction,
executeFunctionFunction,
executeFunctionFunction(maxTraceLength),
wrapResultValuesForWorkerFunction,
convertRawValuesToReflectValuesFunction,
parseJsonToFunctionNameAndRawValuesFunction,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,22 @@ internal object GoWorkerCodeGenerationHelper {
functions: List<GoUtFunction>,
eachExecutionTimeoutMillis: Long,
port: Int,
maxTraceLength: Int,
imports: Set<GoImport>
): File {
val fileToExecuteName = createFileToExecuteName(sourceFile)
val sourceFileDir = File(sourceFile.absoluteDirectoryPath)
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
}
Expand Down Expand Up @@ -72,6 +80,7 @@ internal object GoWorkerCodeGenerationHelper {
functions: List<GoUtFunction>,
eachExecutionTimeoutMillis: Long,
port: Int,
maxTraceLength: Int,
imports: Set<GoImport>
): String {
val destinationPackage = sourceFile.sourcePackage
Expand All @@ -87,7 +96,8 @@ internal object GoWorkerCodeGenerationHelper {
GoCodeTemplates.getTopLevelHelperStructsAndFunctionsForWorker(
structTypes,
destinationPackage,
aliases
aliases,
maxTraceLength,
) + workerTestFunctionCode
)

Expand All @@ -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()
}
Expand All @@ -114,12 +124,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 {
Expand All @@ -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))
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}
Loading