Skip to content

Commit 88808fc

Browse files
authored
UnitTestBot Go. Constants extraction (#1852)
* Do constants collecting * Add GoConstantValueProvider to use constants to generate test cases (only for ints now) * Use constants in fuzzing for all primitive types except complex numbers and bool. Fix float constants
1 parent a647725 commit 88808fc

File tree

14 files changed

+334
-79
lines changed

14 files changed

+334
-79
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ val logger = KotlinLogging.logger {}
2525
class GoEngine(
2626
private val functionUnderTest: GoUtFunction,
2727
private val sourceFile: GoUtFile,
28+
private val intSize: Int,
2829
private val goExecutableAbsolutePath: String,
2930
private val eachExecutionTimeoutsMillisConfig: EachExecutionTimeoutsMillisConfig,
3031
private val timeoutExceededOrIsCanceled: () -> Boolean,
@@ -91,13 +92,14 @@ class GoEngine(
9192
val executionResult = convertRawExecutionResultToExecutionResult(
9293
rawExecutionResult,
9394
functionUnderTest.resultTypes,
95+
intSize,
9496
eachExecutionTimeoutsMillisConfig[functionUnderTest],
9597
)
9698
val fuzzedFunction = GoUtFuzzedFunction(functionUnderTest, emptyList())
9799
emit(fuzzedFunction to executionResult)
98100
} else {
99101
val aliases = imports.filter { it.alias != null }.associate { it.goPackage to it.alias }
100-
runGoFuzzing(functionUnderTest) { description, values ->
102+
runGoFuzzing(functionUnderTest, intSize) { description, values ->
101103
if (timeoutExceededOrIsCanceled()) {
102104
return@runGoFuzzing BaseFeedback(result = Trie.emptyNode(), control = Control.STOP)
103105
}
@@ -107,6 +109,7 @@ class GoEngine(
107109
val executionResult = convertRawExecutionResultToExecutionResult(
108110
rawExecutionResult,
109111
functionUnderTest.resultTypes,
112+
intSize,
110113
eachExecutionTimeoutsMillisConfig[functionUnderTest],
111114
)
112115
if (executionResult.trace.isEmpty()) {

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ import org.utbot.go.api.GoUtFunction
66
import org.utbot.go.framework.api.go.GoTypeId
77
import org.utbot.go.framework.api.go.GoUtModel
88
import org.utbot.go.fuzzer.providers.GoArrayValueProvider
9+
import org.utbot.go.fuzzer.providers.GoConstantValueProvider
910
import org.utbot.go.fuzzer.providers.GoPrimitivesValueProvider
1011
import org.utbot.go.fuzzer.providers.GoStructValueProvider
1112

1213

1314
fun goDefaultValueProviders() = listOf(
14-
GoPrimitivesValueProvider, GoArrayValueProvider, GoStructValueProvider
15+
GoPrimitivesValueProvider, GoArrayValueProvider, GoStructValueProvider, GoConstantValueProvider
1516
)
1617

1718
class GoInstruction(
@@ -20,13 +21,15 @@ class GoInstruction(
2021

2122
class GoDescription(
2223
val methodUnderTest: GoUtFunction,
23-
val tracer: Trie<GoInstruction, *>
24+
val tracer: Trie<GoInstruction, *>,
25+
val intSize: Int
2426
) : Description<GoTypeId>(methodUnderTest.parameters.map { it.type }.toList())
2527

2628
suspend fun runGoFuzzing(
2729
methodUnderTest: GoUtFunction,
30+
intSize: Int,
2831
providers: List<ValueProvider<GoTypeId, GoUtModel, GoDescription>> = goDefaultValueProviders(),
2932
exec: suspend (description: GoDescription, values: List<GoUtModel>) -> BaseFeedback<Trie.Node<GoInstruction>, GoTypeId, GoUtModel>
3033
) {
31-
BaseFuzzing(providers, exec).fuzz(GoDescription(methodUnderTest, Trie(GoInstruction::lineNumber)))
34+
BaseFuzzing(providers, exec).fuzz(GoDescription(methodUnderTest, Trie(GoInstruction::lineNumber), intSize))
3235
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ data class GoUtFunction(
2121
val parameters: List<GoUtFunctionParameter>,
2222
val resultTypes: List<GoTypeId>,
2323
val requiredImports: List<GoImport>,
24+
val constants: Map<GoTypeId, List<Any>>,
2425
val modifiedFunctionForCollectingTraces: String,
2526
val numberOfAllStatements: Int,
2627
val sourceFile: GoUtFile

utbot-go/src/main/kotlin/org/utbot/go/api/util/GoTypesApiUtil.kt

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,20 @@ val goComplex64TypeId = GoPrimitiveTypeId("complex64")
1414
val goFloat32TypeId = GoPrimitiveTypeId("float32")
1515
val goFloat64TypeId = GoPrimitiveTypeId("float64")
1616

17-
val goIntTypeId = GoPrimitiveTypeId("int")
17+
val goInt8TypeId = GoPrimitiveTypeId("int8")
1818
val goInt16TypeId = GoPrimitiveTypeId("int16")
1919
val goInt32TypeId = GoPrimitiveTypeId("int32")
20+
val goIntTypeId = GoPrimitiveTypeId("int")
2021
val goInt64TypeId = GoPrimitiveTypeId("int64")
21-
val goInt8TypeId = GoPrimitiveTypeId("int8")
2222

2323
val goRuneTypeId = GoPrimitiveTypeId("rune") // = int32
2424
val goStringTypeId = GoPrimitiveTypeId("string")
2525

26-
val goUintTypeId = GoPrimitiveTypeId("uint")
26+
val goUint8TypeId = GoPrimitiveTypeId("uint8")
2727
val goUint16TypeId = GoPrimitiveTypeId("uint16")
2828
val goUint32TypeId = GoPrimitiveTypeId("uint32")
29+
val goUintTypeId = GoPrimitiveTypeId("uint")
2930
val goUint64TypeId = GoPrimitiveTypeId("uint64")
30-
val goUint8TypeId = GoPrimitiveTypeId("uint8")
3131
val goUintPtrTypeId = GoPrimitiveTypeId("uintptr")
3232

3333
val goPrimitives = setOf(
@@ -52,6 +52,25 @@ val goPrimitives = setOf(
5252
goUintPtrTypeId,
5353
)
5454

55+
val goSupportedConstantTypes = setOf(
56+
goByteTypeId,
57+
goFloat32TypeId,
58+
goFloat64TypeId,
59+
goIntTypeId,
60+
goInt8TypeId,
61+
goInt16TypeId,
62+
goInt32TypeId,
63+
goInt64TypeId,
64+
goRuneTypeId,
65+
goStringTypeId,
66+
goUintTypeId,
67+
goUint8TypeId,
68+
goUint16TypeId,
69+
goUint32TypeId,
70+
goUint64TypeId,
71+
goUintPtrTypeId,
72+
)
73+
5574
val GoTypeId.isPrimitiveGoType: Boolean
5675
get() = this in goPrimitives
5776

@@ -69,27 +88,40 @@ val GoPrimitiveTypeId.neverRequiresExplicitCast: Boolean
6988

7089
/**
7190
* This method is useful for converting the string representation of a Go value to its more accurate representation.
72-
* For example, to build more proper GoUtPrimitiveModel-s with GoFuzzedFunctionsExecutor.
73-
* Note, that for now such conversion is not required and is done for convenience only.
74-
*
75-
* About corresponding types: int and uint / uintptr types sizes in Go are platform dependent,
76-
* but are supposed to fit in Long and ULong respectively.
7791
*/
78-
val GoPrimitiveTypeId.correspondingKClass: KClass<out Any>
79-
get() = when (this) {
80-
goByteTypeId, goUint8TypeId -> UByte::class
81-
goBoolTypeId -> Boolean::class
82-
goFloat32TypeId -> Float::class
83-
goFloat64TypeId -> Double::class
84-
goInt16TypeId -> Short::class
85-
goInt32TypeId, goRuneTypeId -> Int::class
86-
goIntTypeId, goInt64TypeId -> Long::class
87-
goInt8TypeId -> Byte::class
88-
goStringTypeId -> String::class
89-
goUint32TypeId -> UInt::class
90-
goUint16TypeId -> UShort::class
91-
goUintTypeId, goUint64TypeId, goUintPtrTypeId -> ULong::class
92-
else -> String::class // default way to hold GoUtPrimitiveModel's value is to use String
92+
private fun GoPrimitiveTypeId.correspondingKClass(intSize: Int): KClass<out Any> = when (this) {
93+
goBoolTypeId -> Boolean::class
94+
goFloat32TypeId -> Float::class
95+
goFloat64TypeId -> Double::class
96+
goInt8TypeId -> Byte::class
97+
goInt16TypeId -> Short::class
98+
goInt32TypeId, goRuneTypeId -> Int::class
99+
goIntTypeId -> if (intSize == 32) Int::class else Long::class
100+
goInt64TypeId -> Long::class
101+
goStringTypeId -> String::class
102+
goUint8TypeId, goByteTypeId -> UByte::class
103+
goUint16TypeId -> UShort::class
104+
goUint32TypeId -> UInt::class
105+
goUintTypeId -> if (intSize == 32) UInt::class else ULong::class
106+
goUint64TypeId -> ULong::class
107+
goUintPtrTypeId -> if (intSize == 32) UInt::class else ULong::class
108+
else -> String::class // default way to hold GoUtPrimitiveModel's value is to use String
109+
}
110+
111+
fun rawValueOfGoPrimitiveTypeToValue(typeId: GoPrimitiveTypeId, rawValue: String, intSize: Int): Any =
112+
when (typeId.correspondingKClass(intSize)) {
113+
UByte::class -> rawValue.toUByte()
114+
Boolean::class -> rawValue.toBoolean()
115+
Float::class -> rawValue.toFloat()
116+
Double::class -> rawValue.toDouble()
117+
Int::class -> rawValue.toInt()
118+
Short::class -> rawValue.toShort()
119+
Long::class -> rawValue.toLong()
120+
Byte::class -> rawValue.toByte()
121+
UInt::class -> rawValue.toUInt()
122+
UShort::class -> rawValue.toUShort()
123+
ULong::class -> rawValue.toULong()
124+
else -> rawValue
93125
}
94126

95127
fun GoTypeId.goDefaultValueModel(): GoUtModel = when (this) {
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package org.utbot.go.fuzzer.providers
2+
3+
import org.utbot.fuzzing.Seed
4+
import org.utbot.fuzzing.ValueProvider
5+
import org.utbot.fuzzing.seeds.BitVectorValue
6+
import org.utbot.fuzzing.seeds.IEEE754Value
7+
import org.utbot.fuzzing.seeds.StringValue
8+
import org.utbot.go.GoDescription
9+
import org.utbot.go.api.GoPrimitiveTypeId
10+
import org.utbot.go.api.GoUtPrimitiveModel
11+
import org.utbot.go.api.util.*
12+
import org.utbot.go.framework.api.go.GoTypeId
13+
import org.utbot.go.framework.api.go.GoUtModel
14+
15+
object GoConstantValueProvider : ValueProvider<GoTypeId, GoUtModel, GoDescription> {
16+
override fun accept(type: GoTypeId): Boolean = type in goSupportedConstantTypes
17+
18+
override fun generate(description: GoDescription, type: GoTypeId): Sequence<Seed<GoTypeId, GoUtModel>> = sequence {
19+
type.let { it as GoPrimitiveTypeId }.also { primitiveType ->
20+
val constants = description.methodUnderTest.constants
21+
val intSize = description.intSize
22+
val primitives: List<Seed<GoTypeId, GoUtModel>> = (constants[primitiveType] ?: emptyList()).mapNotNull {
23+
when (primitiveType) {
24+
goRuneTypeId, goIntTypeId, goInt8TypeId, goInt16TypeId, goInt32TypeId, goInt64TypeId ->
25+
when (primitiveType) {
26+
goInt8TypeId -> Seed.Known(BitVectorValue.fromValue(it)) { obj: BitVectorValue ->
27+
GoUtPrimitiveModel(
28+
obj.toByte(),
29+
primitiveType
30+
)
31+
}
32+
33+
goInt16TypeId -> Seed.Known(BitVectorValue.fromValue(it)) { obj: BitVectorValue ->
34+
GoUtPrimitiveModel(
35+
obj.toShort(),
36+
primitiveType
37+
)
38+
}
39+
40+
goInt32TypeId, goRuneTypeId -> Seed.Known(BitVectorValue.fromValue(it)) { obj: BitVectorValue ->
41+
GoUtPrimitiveModel(
42+
obj.toInt(),
43+
primitiveType
44+
)
45+
}
46+
47+
goIntTypeId -> Seed.Known(BitVectorValue.fromValue(it)) { obj: BitVectorValue ->
48+
GoUtPrimitiveModel(
49+
if (intSize == 32) obj.toInt() else obj.toLong(),
50+
primitiveType
51+
)
52+
}
53+
54+
goInt64TypeId -> Seed.Known(BitVectorValue.fromValue(it)) { obj: BitVectorValue ->
55+
GoUtPrimitiveModel(
56+
obj.toLong(),
57+
primitiveType
58+
)
59+
}
60+
61+
else -> return@sequence
62+
}
63+
64+
goByteTypeId, goUintTypeId, goUintPtrTypeId, goUint8TypeId, goUint16TypeId, goUint32TypeId, goUint64TypeId ->
65+
when (primitiveType) {
66+
goByteTypeId, goUint8TypeId -> {
67+
val uint8AsLong = (it as UByte).toLong()
68+
Seed.Known(BitVectorValue.fromValue(uint8AsLong)) { obj: BitVectorValue ->
69+
GoUtPrimitiveModel(
70+
obj.toUByte(),
71+
primitiveType
72+
)
73+
}
74+
}
75+
76+
goUint16TypeId -> {
77+
val uint16AsLong = (it as UShort).toLong()
78+
Seed.Known(BitVectorValue.fromValue(uint16AsLong)) { obj: BitVectorValue ->
79+
GoUtPrimitiveModel(
80+
obj.toUShort(),
81+
primitiveType
82+
)
83+
}
84+
}
85+
86+
goUint32TypeId -> {
87+
val uint32AsLong = (it as UInt).toLong()
88+
Seed.Known(BitVectorValue.fromValue(uint32AsLong)) { obj: BitVectorValue ->
89+
GoUtPrimitiveModel(
90+
obj.toUInt(),
91+
primitiveType
92+
)
93+
}
94+
}
95+
96+
goUintTypeId, goUintPtrTypeId -> {
97+
val uintAsLong = if (intSize == 32) (it as UInt).toLong() else (it as ULong).toLong()
98+
Seed.Known(BitVectorValue.fromValue(uintAsLong)) { obj: BitVectorValue ->
99+
GoUtPrimitiveModel(
100+
if (intSize == 32) obj.toUInt() else obj.toULong(),
101+
primitiveType
102+
)
103+
}
104+
}
105+
106+
goUint64TypeId -> {
107+
val uint64AsLong = (it as ULong).toLong()
108+
Seed.Known(BitVectorValue.fromValue(uint64AsLong)) { obj: BitVectorValue ->
109+
GoUtPrimitiveModel(
110+
obj.toULong(),
111+
primitiveType
112+
)
113+
}
114+
}
115+
116+
else -> return@sequence
117+
}
118+
119+
goFloat32TypeId -> Seed.Known(IEEE754Value.fromValue(it)) { obj: IEEE754Value ->
120+
GoUtPrimitiveModel(
121+
obj.toFloat(),
122+
primitiveType
123+
)
124+
}
125+
126+
goFloat64TypeId -> Seed.Known(IEEE754Value.fromValue(it)) { obj: IEEE754Value ->
127+
GoUtPrimitiveModel(
128+
obj.toDouble(),
129+
primitiveType
130+
)
131+
}
132+
133+
goStringTypeId -> Seed.Known(StringValue(it as String)) { obj: StringValue ->
134+
GoUtPrimitiveModel(
135+
obj.value,
136+
primitiveType
137+
)
138+
}
139+
140+
else -> null
141+
}
142+
}
143+
144+
primitives.forEach { seed ->
145+
yield(seed)
146+
}
147+
}
148+
}
149+
}

utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoPrimitivesValueProvider.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,15 @@ import org.utbot.go.api.util.*
1212
import org.utbot.go.framework.api.go.GoTypeId
1313
import org.utbot.go.framework.api.go.GoUtModel
1414
import java.util.*
15-
import kotlin.properties.Delegates
1615

1716
object GoPrimitivesValueProvider : ValueProvider<GoTypeId, GoUtModel, GoDescription> {
18-
var intSize by Delegates.notNull<Int>()
1917
private val random = Random(0)
2018

2119
override fun accept(type: GoTypeId): Boolean = type in goPrimitives
2220

2321
override fun generate(description: GoDescription, type: GoTypeId): Sequence<Seed<GoTypeId, GoUtModel>> =
2422
sequence {
23+
val intSize = description.intSize
2524
type.let { it as GoPrimitiveTypeId }.also { primitiveType ->
2625
val primitives: List<Seed<GoTypeId, GoUtModel>> = when (primitiveType) {
2726
goBoolTypeId -> listOf(
@@ -148,8 +147,8 @@ object GoPrimitivesValueProvider : ValueProvider<GoTypeId, GoUtModel, GoDescript
148147
else -> emptyList()
149148
}
150149

151-
primitives.forEach { fuzzedValue ->
152-
yield(fuzzedValue)
150+
primitives.forEach { seed ->
151+
yield(seed)
153152
}
154153
}
155154
}

0 commit comments

Comments
 (0)