Skip to content

Commit 4413a7e

Browse files
authored
Fix Go imports (#1801)
* pre-alfa of fix imports. Collecting of imports is done * Fix go imports * Fix empty test file generation * Fix empty test file generation when all test case execution results exceed timeout
1 parent 4a769ad commit 4413a7e

28 files changed

+804
-398
lines changed

utbot-go/go-samples/go.mod

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ module go-samples
22

33
go 1.19
44

5-
require github.com/stretchr/testify v1.8.1
5+
require (
6+
github.com/pmezard/go-difflib v1.0.0
7+
github.com/stretchr/testify v1.8.1
8+
)
69

710
require (
811
github.com/davecgh/go-spew v1.1.1 // indirect
9-
github.com/pmezard/go-difflib v1.0.0 // indirect
1012
gopkg.in/yaml.v3 v3.0.1 // indirect
1113
)

utbot-go/go-samples/simple/supported_types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package simple
33
import (
44
"errors"
55
"github.com/pmezard/go-difflib/difflib"
6+
dif "github.com/pmezard/go-difflib/difflib"
67
"math"
78
)
89

@@ -172,3 +173,7 @@ func returnErrorOrNil(n int) error {
172173
func ExternalStruct(match difflib.Match, structure Structure) Structure {
173174
return structure
174175
}
176+
177+
func ExternalStructWithAlias(match dif.Match) difflib.Match {
178+
return match
179+
}

utbot-go/go-samples/simple/supported_types_go_ut_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,9 @@ func TestExternalStructByUtGoFuzzer(t *testing.T) {
226226

227227
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)
228228
}
229+
230+
func TestExternalStructWithAliasByUtGoFuzzer(t *testing.T) {
231+
actualVal := ExternalStructWithAlias(difflib.Match{A: 9223372036854775807, B: -1, Size: -9223372036854775808})
232+
233+
assert.Equal(t, difflib.Match{A: 9223372036854775807, B: -1, Size: -9223372036854775808}, actualVal)
234+
}

utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt

Lines changed: 59 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import org.utbot.fuzzing.BaseFeedback
88
import org.utbot.fuzzing.Control
99
import org.utbot.fuzzing.utils.Trie
1010
import org.utbot.go.api.*
11+
import org.utbot.go.imports.GoImportsResolver
1112
import org.utbot.go.logic.EachExecutionTimeoutsMillisConfig
1213
import org.utbot.go.util.executeCommandByNewProcessOrFailWithoutWaiting
1314
import org.utbot.go.worker.GoWorker
@@ -16,14 +17,13 @@ import org.utbot.go.worker.convertRawExecutionResultToExecutionResult
1617
import java.io.File
1718
import java.io.InputStreamReader
1819
import java.net.ServerSocket
19-
import java.net.SocketException
2020
import java.net.SocketTimeoutException
2121
import java.util.concurrent.TimeUnit
2222

2323
val logger = KotlinLogging.logger {}
2424

2525
class GoEngine(
26-
private val methodUnderTest: GoUtFunction,
26+
private val functionUnderTest: GoUtFunction,
2727
private val sourceFile: GoUtFile,
2828
private val goExecutableAbsolutePath: String,
2929
private val eachExecutionTimeoutsMillisConfig: EachExecutionTimeoutsMillisConfig,
@@ -36,63 +36,81 @@ class GoEngine(
3636
val attemptsLimit = Int.MAX_VALUE
3737
ServerSocket(0).use { serverSocket ->
3838
var fileToExecute: File? = null
39+
var fileWithModifiedFunction: File? = null
3940
try {
41+
// creating files for worker
42+
val types = functionUnderTest.parameters.map { it.type }
43+
val imports = GoImportsResolver.resolveImportsBasedOnTypes(
44+
types,
45+
functionUnderTest.sourcePackage,
46+
GoWorkerCodeGenerationHelper.alwaysRequiredImports
47+
)
4048
fileToExecute = GoWorkerCodeGenerationHelper.createFileToExecute(
4149
sourceFile,
42-
methodUnderTest,
50+
functionUnderTest,
4351
eachExecutionTimeoutsMillisConfig,
44-
serverSocket.localPort
52+
serverSocket.localPort,
53+
imports
54+
)
55+
fileWithModifiedFunction = GoWorkerCodeGenerationHelper.createFileWithModifiedFunction(
56+
sourceFile, functionUnderTest
4557
)
58+
59+
// starting worker process
4660
val testFunctionName = GoWorkerCodeGenerationHelper.workerTestFunctionName
4761
val command = listOf(
4862
goExecutableAbsolutePath, "test", "-run", testFunctionName
4963
)
5064
val sourceFileDir = File(sourceFile.absoluteDirectoryPath)
5165
val processStartTime = System.currentTimeMillis()
5266
val process = executeCommandByNewProcessOrFailWithoutWaiting(command, sourceFileDir)
53-
val workerSocket = try {
54-
serverSocket.soTimeout = timeoutMillis.toInt()
55-
serverSocket.accept()
56-
} catch (e: SocketTimeoutException) {
57-
val processHasExited = process.waitFor(timeoutMillis, TimeUnit.MILLISECONDS)
58-
if (processHasExited) {
59-
val processOutput = InputStreamReader(process.inputStream).readText()
60-
throw TimeoutException("Timeout exceeded: Worker not connected. Process output: $processOutput")
61-
} else {
62-
process.destroy()
63-
}
64-
throw TimeoutException("Timeout exceeded: Worker not connected")
65-
}
66-
logger.debug { "Worker connected - completed in ${System.currentTimeMillis() - processStartTime} ms" }
67+
6768
try {
68-
val worker = GoWorker(workerSocket)
69-
if (methodUnderTest.parameters.isEmpty()) {
70-
worker.sendFuzzedParametersValues(listOf())
69+
// connecting to worker
70+
logger.debug { "Trying to connect to worker" }
71+
val workerSocket = try {
72+
serverSocket.soTimeout = timeoutMillis.toInt()
73+
serverSocket.accept()
74+
} catch (e: SocketTimeoutException) {
75+
val processHasExited = process.waitFor(timeoutMillis, TimeUnit.MILLISECONDS)
76+
if (processHasExited) {
77+
val processOutput = InputStreamReader(process.inputStream).readText()
78+
throw TimeoutException("Timeout exceeded: Worker not connected. Process output: $processOutput")
79+
} else {
80+
process.destroy()
81+
}
82+
throw TimeoutException("Timeout exceeded: Worker not connected")
83+
}
84+
val worker = GoWorker(workerSocket, functionUnderTest)
85+
logger.debug { "Worker connected - completed in ${System.currentTimeMillis() - processStartTime} ms" }
86+
87+
// fuzzing
88+
if (functionUnderTest.parameters.isEmpty()) {
89+
worker.sendFuzzedParametersValues(emptyList(), emptyMap())
7190
val rawExecutionResult = worker.receiveRawExecutionResult()
7291
val executionResult = convertRawExecutionResultToExecutionResult(
73-
methodUnderTest.getPackageName(),
7492
rawExecutionResult,
75-
methodUnderTest.resultTypes,
76-
eachExecutionTimeoutsMillisConfig[methodUnderTest],
93+
functionUnderTest.resultTypes,
94+
eachExecutionTimeoutsMillisConfig[functionUnderTest],
7795
)
78-
val fuzzedFunction = GoUtFuzzedFunction(methodUnderTest, listOf())
96+
val fuzzedFunction = GoUtFuzzedFunction(functionUnderTest, emptyList())
7997
emit(fuzzedFunction to executionResult)
8098
} else {
81-
runGoFuzzing(methodUnderTest) { description, values ->
99+
val aliases = imports.filter { it.alias != null }.associate { it.goPackage to it.alias }
100+
runGoFuzzing(functionUnderTest) { description, values ->
82101
if (timeoutExceededOrIsCanceled()) {
83102
return@runGoFuzzing BaseFeedback(result = Trie.emptyNode(), control = Control.STOP)
84103
}
85-
val fuzzedFunction = GoUtFuzzedFunction(methodUnderTest, values)
86-
worker.sendFuzzedParametersValues(values)
104+
val fuzzedFunction = GoUtFuzzedFunction(functionUnderTest, values)
105+
worker.sendFuzzedParametersValues(values, aliases)
87106
val rawExecutionResult = worker.receiveRawExecutionResult()
88107
val executionResult = convertRawExecutionResultToExecutionResult(
89-
methodUnderTest.getPackageName(),
90108
rawExecutionResult,
91-
methodUnderTest.resultTypes,
92-
eachExecutionTimeoutsMillisConfig[methodUnderTest],
109+
functionUnderTest.resultTypes,
110+
eachExecutionTimeoutsMillisConfig[functionUnderTest],
93111
)
94112
if (executionResult.trace.isEmpty()) {
95-
logger.error { "Coverage is empty for [${methodUnderTest.name}] with $values}" }
113+
logger.error { "Coverage is empty for [${functionUnderTest.name}] with $values}" }
96114
if (executionResult is GoUtPanicFailure) {
97115
logger.error { "Execution completed with panic: ${executionResult.panicValue}" }
98116
}
@@ -102,8 +120,7 @@ class GoEngine(
102120
if (trieNode.count > 1) {
103121
if (++attempts >= attemptsLimit) {
104122
return@runGoFuzzing BaseFeedback(
105-
result = Trie.emptyNode(),
106-
control = Control.STOP
123+
result = Trie.emptyNode(), control = Control.STOP
107124
)
108125
}
109126
return@runGoFuzzing BaseFeedback(result = trieNode, control = Control.CONTINUE)
@@ -122,13 +139,13 @@ class GoEngine(
122139
val processOutput = InputStreamReader(process.inputStream).readText()
123140
throw RuntimeException(
124141
StringBuilder()
125-
.append("Execution of ${"function [${methodUnderTest.name}] from $sourceFile"} in child process failed with non-zero exit code = $exitCode: ")
126-
.append("\n$processOutput")
127-
.toString()
142+
.append("Execution of ${"function [${functionUnderTest.name}] from $sourceFile"} in child process failed with non-zero exit code = $exitCode: ")
143+
.appendLine()
144+
.append(processOutput).toString()
128145
)
129146
}
130147
}
131-
} catch (e: SocketException) {
148+
} catch (e: Exception) {
132149
val processHasExited = process.waitFor(timeoutMillis, TimeUnit.MILLISECONDS)
133150
if (!processHasExited) {
134151
process.destroy()
@@ -139,14 +156,15 @@ class GoEngine(
139156
val processOutput = InputStreamReader(process.inputStream).readText()
140157
throw RuntimeException(
141158
StringBuilder()
142-
.append("Execution of ${"function [${methodUnderTest.name}] from $sourceFile"} in child process failed with non-zero exit code = $exitCode: ")
143-
.append("\n$processOutput")
144-
.toString()
159+
.append("Execution of ${"function [${functionUnderTest.name}] from $sourceFile"} in child process failed with non-zero exit code = $exitCode: ")
160+
.appendLine()
161+
.append(processOutput).toString()
145162
)
146163
}
147164
}
148165
} finally {
149166
fileToExecute?.delete()
167+
fileWithModifiedFunction?.delete()
150168
}
151169
}
152170
}

utbot-go/src/main/kotlin/org/utbot/go/api/GoTypesApi.kt

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
package org.utbot.go.api
22

33
import org.utbot.go.framework.api.go.GoFieldId
4+
import org.utbot.go.framework.api.go.GoPackage
45
import org.utbot.go.framework.api.go.GoTypeId
56

67
/**
78
* Represents real Go primitive type.
89
*/
910
class GoPrimitiveTypeId(name: String) : GoTypeId(name) {
10-
override val packageName: String = ""
1111
override val canonicalName: String = simpleName
1212

13-
override fun getRelativeName(packageName: String): String = simpleName
13+
override fun getRelativeName(destinationPackage: GoPackage, aliases: Map<GoPackage, String?>): String = simpleName
1414

1515
override fun equals(other: Any?): Boolean {
1616
if (this === other) return true
@@ -25,16 +25,22 @@ class GoPrimitiveTypeId(name: String) : GoTypeId(name) {
2525
class GoStructTypeId(
2626
name: String,
2727
implementsError: Boolean,
28-
override val packageName: String,
29-
val packagePath: String,
28+
override val sourcePackage: GoPackage,
3029
val fields: List<GoFieldId>,
3130
) : GoTypeId(name, implementsError = implementsError) {
32-
override val canonicalName: String = "$packageName.$name"
33-
34-
override fun getRelativeName(packageName: String): String = if (this.packageName != packageName) {
35-
canonicalName
36-
} else {
37-
simpleName
31+
val packageName: String = sourcePackage.packageName
32+
val packagePath: String = sourcePackage.packagePath
33+
override val canonicalName: String = "${sourcePackage.packageName}.$name"
34+
35+
override fun getRelativeName(destinationPackage: GoPackage, aliases: Map<GoPackage, String?>): String {
36+
val alias = aliases[sourcePackage]
37+
return if (sourcePackage == destinationPackage || alias == ".") {
38+
simpleName
39+
} else if (alias == null) {
40+
"${packageName}.${simpleName}"
41+
} else {
42+
"${alias}.${simpleName}"
43+
}
3844
}
3945

4046
override fun equals(other: Any?): Boolean {
@@ -53,14 +59,12 @@ class GoStructTypeId(
5359
}
5460

5561
class GoArrayTypeId(
56-
name: String,
57-
elementTypeId: GoTypeId,
58-
val length: Int
62+
name: String, elementTypeId: GoTypeId, val length: Int
5963
) : GoTypeId(name, elementTypeId = elementTypeId) {
6064
override val canonicalName: String = "[$length]${elementTypeId.canonicalName}"
6165

62-
override fun getRelativeName(packageName: String): String =
63-
"[$length]${elementTypeId!!.getRelativeName(packageName)}"
66+
override fun getRelativeName(destinationPackage: GoPackage, aliases: Map<GoPackage, String?>): String =
67+
"[$length]${elementTypeId!!.getRelativeName(destinationPackage, aliases)}"
6468

6569
override fun equals(other: Any?): Boolean {
6670
if (this === other) return true
@@ -75,19 +79,25 @@ class GoArrayTypeId(
7579
class GoInterfaceTypeId(
7680
name: String,
7781
implementsError: Boolean,
78-
override val packageName: String,
79-
val packagePath: String,
82+
override val sourcePackage: GoPackage,
8083
) : GoTypeId(name, implementsError = implementsError) {
84+
val packageName: String = sourcePackage.packageName
85+
val packagePath: String = sourcePackage.packagePath
8186
override val canonicalName: String = if (packageName != "") {
8287
"$packageName.$name"
8388
} else {
8489
simpleName
8590
}
8691

87-
override fun getRelativeName(packageName: String): String = if (this.packageName != packageName) {
88-
canonicalName
89-
} else {
90-
simpleName
92+
override fun getRelativeName(destinationPackage: GoPackage, aliases: Map<GoPackage, String?>): String {
93+
val alias = aliases[sourcePackage]
94+
return if (sourcePackage == destinationPackage || alias == ".") {
95+
simpleName
96+
} else if (alias == null) {
97+
"${packageName}.${simpleName}"
98+
} else {
99+
"${alias}.${simpleName}"
100+
}
91101
}
92102

93103
override fun equals(other: Any?): Boolean {

utbot-go/src/main/kotlin/org/utbot/go/api/GoUtFunctionApi.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package org.utbot.go.api
22

3-
import org.utbot.fuzzer.FuzzedConcreteValue
3+
import org.utbot.go.framework.api.go.GoImport
4+
import org.utbot.go.framework.api.go.GoPackage
45
import org.utbot.go.framework.api.go.GoTypeId
56
import org.utbot.go.framework.api.go.GoUtModel
67
import java.io.File
78
import java.nio.file.Paths
89

9-
data class GoUtFile(val absolutePath: String, val packageName: String) {
10+
data class GoUtFile(val absolutePath: String, val sourcePackage: GoPackage) {
1011
val fileName: String get() = File(absolutePath).name
1112
val fileNameWithoutExtension: String get() = File(absolutePath).nameWithoutExtension
1213
val absoluteDirectoryPath: String get() = Paths.get(absolutePath).parent.toString()
@@ -19,11 +20,14 @@ data class GoUtFunction(
1920
val modifiedName: String,
2021
val parameters: List<GoUtFunctionParameter>,
2122
val resultTypes: List<GoTypeId>,
23+
val requiredImports: List<GoImport>,
2224
val modifiedFunctionForCollectingTraces: String,
2325
val numberOfAllStatements: Int,
2426
val sourceFile: GoUtFile
2527
) {
26-
fun getPackageName(): String = sourceFile.packageName
28+
val sourcePackage: GoPackage = sourceFile.sourcePackage
29+
30+
fun getPackageName(): String = sourceFile.sourcePackage.packageName
2731
}
2832

2933
data class GoUtFuzzedFunction(val function: GoUtFunction, val parametersValues: List<GoUtModel>)

0 commit comments

Comments
 (0)