Skip to content

UnitTestBot Go. Constants extraction #1852

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 3 commits into from
Feb 27, 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
5 changes: 4 additions & 1 deletion utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
}
Expand All @@ -107,6 +109,7 @@ class GoEngine(
val executionResult = convertRawExecutionResultToExecutionResult(
rawExecutionResult,
functionUnderTest.resultTypes,
intSize,
eachExecutionTimeoutsMillisConfig[functionUnderTest],
)
if (executionResult.trace.isEmpty()) {
Expand Down
9 changes: 6 additions & 3 deletions utbot-go/src/main/kotlin/org/utbot/go/GoLanguage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -20,13 +21,15 @@ class GoInstruction(

class GoDescription(
val methodUnderTest: GoUtFunction,
val tracer: Trie<GoInstruction, *>
val tracer: Trie<GoInstruction, *>,
val intSize: Int
) : Description<GoTypeId>(methodUnderTest.parameters.map { it.type }.toList())

suspend fun runGoFuzzing(
methodUnderTest: GoUtFunction,
intSize: Int,
providers: List<ValueProvider<GoTypeId, GoUtModel, GoDescription>> = goDefaultValueProviders(),
exec: suspend (description: GoDescription, values: List<GoUtModel>) -> BaseFeedback<Trie.Node<GoInstruction>, GoTypeId, GoUtModel>
) {
BaseFuzzing(providers, exec).fuzz(GoDescription(methodUnderTest, Trie(GoInstruction::lineNumber)))
BaseFuzzing(providers, exec).fuzz(GoDescription(methodUnderTest, Trie(GoInstruction::lineNumber), intSize))
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ data class GoUtFunction(
val parameters: List<GoUtFunctionParameter>,
val resultTypes: List<GoTypeId>,
val requiredImports: List<GoImport>,
val constants: Map<GoTypeId, List<Any>>,
val modifiedFunctionForCollectingTraces: String,
val numberOfAllStatements: Int,
val sourceFile: GoUtFile
Expand Down
80 changes: 56 additions & 24 deletions utbot-go/src/main/kotlin/org/utbot/go/api/util/GoTypesApiUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -52,6 +52,25 @@ val goPrimitives = setOf(
goUintPtrTypeId,
)

val goSupportedConstantTypes = 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

Expand All @@ -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<out Any>
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<out Any> = 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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
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
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<GoTypeId, GoUtModel, GoDescription> {
override fun accept(type: GoTypeId): Boolean = type in goSupportedConstantTypes

override fun generate(description: GoDescription, type: GoTypeId): Sequence<Seed<GoTypeId, GoUtModel>> = sequence {
type.let { it as GoPrimitiveTypeId }.also { primitiveType ->
val constants = description.methodUnderTest.constants
val intSize = description.intSize
val primitives: List<Seed<GoTypeId, GoUtModel>> = (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 ->
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
}

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 -> null
}
}

primitives.forEach { seed ->
yield(seed)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<GoTypeId, GoUtModel, GoDescription> {
var intSize by Delegates.notNull<Int>()
private val random = Random(0)

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

override fun generate(description: GoDescription, type: GoTypeId): Sequence<Seed<GoTypeId, GoUtModel>> =
sequence {
val intSize = description.intSize
type.let { it as GoPrimitiveTypeId }.also { primitiveType ->
val primitives: List<Seed<GoTypeId, GoUtModel>> = when (primitiveType) {
goBoolTypeId -> listOf(
Expand Down Expand Up @@ -148,8 +147,8 @@ object GoPrimitivesValueProvider : ValueProvider<GoTypeId, GoUtModel, GoDescript
else -> emptyList()
}

primitives.forEach { fuzzedValue ->
yield(fuzzedValue)
primitives.forEach { seed ->
yield(seed)
}
}
}
Expand Down
Loading