From 144d5e1c198e40d7ad734659a6752194930c65aa Mon Sep 17 00:00:00 2001 From: Egipti Pavel Date: Wed, 5 Apr 2023 12:27:18 +0300 Subject: [PATCH 1/6] Fix raw values --- .../utbot/go/worker/RawExecutionResults.kt | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt b/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt index 438f469545..5716ef4e07 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt @@ -30,7 +30,7 @@ data class PrimitiveValue( data class NamedValue( override val type: String, - val value: RawValue, + val value: RawValue?, ) : RawValue(type) { override fun checkIsEqualTypes(type: GoTypeId): Boolean = error("Not supported") } @@ -41,7 +41,7 @@ data class StructValue( ) : RawValue(type) { data class FieldValue( val name: String, - val value: RawValue, + val value: RawValue?, val isExported: Boolean ) @@ -59,7 +59,7 @@ data class StructValue( if (fieldValue.name != fieldId.name) { return false } - if (!fieldValue.value.checkIsEqualTypes(fieldId.declaringType)) { + if (fieldValue.value != null && !fieldValue.value.checkIsEqualTypes(fieldId.declaringType)) { return false } if (fieldValue.isExported != fieldId.isExported) { @@ -74,7 +74,7 @@ data class ArrayValue( override val type: String, val elementType: String, val length: Int, - val value: List + val value: List ) : RawValue(type) { override fun checkIsEqualTypes(type: GoTypeId): Boolean { if (type is GoNamedTypeId) { @@ -86,7 +86,7 @@ data class ArrayValue( if (length != type.length || elementType != type.elementTypeId!!.canonicalName) { return false } - return value.all { it.checkIsEqualTypes(type.elementTypeId) } + return value.all { it?.checkIsEqualTypes(type.elementTypeId) ?: true } } } @@ -94,7 +94,7 @@ data class SliceValue( override val type: String, val elementType: String, val length: Int, - val value: List + val value: List ) : RawValue(type) { override fun checkIsEqualTypes(type: GoTypeId): Boolean { if (type is GoNamedTypeId) { @@ -106,7 +106,7 @@ data class SliceValue( if (elementType != type.elementTypeId!!.canonicalName) { return false } - return value.all { it.checkIsEqualTypes(type.elementTypeId) } + return value.all { it?.checkIsEqualTypes(type.elementTypeId) ?: true } } } @@ -202,23 +202,23 @@ fun convertRawExecutionResultToExecutionResult( private fun createGoUtModelFromRawValue( rawValue: RawValue?, typeId: GoTypeId, intSize: Int -): GoUtModel = when (typeId) { - is GoNamedTypeId -> GoUtNamedModel(createGoUtModelFromRawValue(rawValue, typeId.underlyingTypeId, intSize), typeId) +): GoUtModel = when { + typeId is GoNamedTypeId -> GoUtNamedModel( + value = createGoUtModelFromRawValue(rawValue, typeId.underlyingTypeId, intSize), + typeId = typeId + ) + rawValue == null -> GoUtNilModel(typeId) // Only for error interface - is GoInterfaceTypeId -> if (rawValue == null) { - GoUtNilModel(typeId) - } else { - GoUtPrimitiveModel((rawValue as PrimitiveValue).value, goStringTypeId) - } + typeId is GoInterfaceTypeId -> GoUtPrimitiveModel((rawValue as PrimitiveValue).value, goStringTypeId) - is GoStructTypeId -> createGoUtStructModelFromRawValue(rawValue as StructValue, typeId, intSize) + typeId is GoStructTypeId -> createGoUtStructModelFromRawValue(rawValue as StructValue, typeId, intSize) - is GoArrayTypeId -> createGoUtArrayModelFromRawValue(rawValue as ArrayValue, typeId, intSize) + typeId is GoArrayTypeId -> createGoUtArrayModelFromRawValue(rawValue as ArrayValue, typeId, intSize) - is GoSliceTypeId -> createGoUtSliceModelFromRawValue(rawValue as SliceValue, typeId, intSize) + typeId is GoSliceTypeId -> createGoUtSliceModelFromRawValue(rawValue as SliceValue, typeId, intSize) - is GoPrimitiveTypeId -> createGoUtPrimitiveModelFromRawValue(rawValue as PrimitiveValue, typeId, intSize) + typeId is GoPrimitiveTypeId -> createGoUtPrimitiveModelFromRawValue(rawValue as PrimitiveValue, typeId, intSize) else -> error("Creating a model from raw value of [${typeId.javaClass}] type is not supported") } From 56ab9c67b8ff97a425939918d70235c9c2e36617 Mon Sep 17 00:00:00 2001 From: Egipti Pavel Date: Wed, 5 Apr 2023 14:41:17 +0300 Subject: [PATCH 2/6] Remove function __typeConversionToNamedType__ --- .../org/utbot/go/worker/GoCodeTemplates.kt | 31 ++----------------- 1 file changed, 2 insertions(+), 29 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 ea8bc46219..62caf78171 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 @@ -1,7 +1,6 @@ package org.utbot.go.worker import org.utbot.go.api.GoNamedTypeId -import org.utbot.go.api.GoStructTypeId import org.utbot.go.api.util.goDefaultValueModel import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.simplecodegeneration.GoUtModelToCodeConverter @@ -273,35 +272,10 @@ object GoCodeTemplates { value, err := v.Value.__toReflectValue__() __checkErrorAndExit__(err) - val, err := __typeConversionToNamedType__(v.Type, value.Interface()) + typ, err := __convertStringToReflectType__(v.Type) __checkErrorAndExit__(err) - return reflect.ValueOf(val), nil - } - """.trimIndent() - - private fun typeConversionToNamedType( - namedTypes: Set, - destinationPackage: GoPackage, - aliases: Map - ) = """ - func __typeConversionToNamedType__(typ string, value any) (any, error) { - switch typ { - ${ - namedTypes.joinToString(separator = "\n") { - val relativeName = it.getRelativeName(destinationPackage, aliases) - "case \"${relativeName}\": return ${relativeName}(value.(${ - if (it.underlyingTypeId is GoStructTypeId) { - relativeName - } else { - it.underlyingTypeId.getRelativeName(destinationPackage, aliases) - } - })), nil" - } - } - default: - return nil, fmt.Errorf("unknown named type: %s", typ) - } + return value.Convert(typ), nil } """.trimIndent() @@ -867,7 +841,6 @@ object GoCodeTemplates { nilValueToReflectValueMethod, namedValueStruct, namedValueToReflectValueMethod, - typeConversionToNamedType(namedTypes, destinationPackage, aliases), convertStringToReflectType(namedTypes, destinationPackage, aliases), panicMessageStruct, rawExecutionResultStruct, From 3ff4c2678e11fed5fdb88ed53c3402167548983f Mon Sep 17 00:00:00 2001 From: Egipti Pavel Date: Fri, 7 Apr 2023 12:17:07 +0300 Subject: [PATCH 3/6] Refactor serialization and deserialization of raw values --- .../org/utbot/go/worker/GoCodeTemplates.kt | 416 +++++++++++------- .../go/worker/GoWorkerCodeGenerationHelper.kt | 56 ++- .../utbot/go/worker/RawExecutionResults.kt | 161 ++----- 3 files changed, 351 insertions(+), 282 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 62caf78171..cfb4a90c29 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 @@ -7,6 +7,15 @@ import org.utbot.go.simplecodegeneration.GoUtModelToCodeConverter object GoCodeTemplates { + private val errorMessages = """ + var ( + ErrParsingValue = "failed to parse %s value: %s" + ErrStringToReflectTypeFailure = "failed to convert '%s' to reflect.Type: %s" + ErrRawValueToReflectValueFailure = "failed to convert RawValue to reflect.Value: %s" + ErrReflectValueToRawValueFailure = "failed to convert reflect.Value to RawValue: %s" + ) + """.trimIndent() + private val testInputStruct = """ type __TestInput__ struct { FunctionName string `json:"functionName"` @@ -34,113 +43,153 @@ object GoCodeTemplates { switch v.Type { case "bool": value, err := strconv.ParseBool(v.Value) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(value), nil case "int": value, err := strconv.Atoi(v.Value) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(value), nil case "int8": value, err := strconv.ParseInt(v.Value, 10, 8) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(int8(value)), nil case "int16": value, err := strconv.ParseInt(v.Value, 10, 16) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(int16(value)), nil case "int32": value, err := strconv.ParseInt(v.Value, 10, 32) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(int32(value)), nil case "rune": value, err := strconv.ParseInt(v.Value, 10, 32) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(rune(value)), nil case "int64": value, err := strconv.ParseInt(v.Value, 10, 64) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(value), nil case "byte": value, err := strconv.ParseUint(v.Value, 10, 8) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(byte(value)), nil case "uint": value, err := strconv.ParseUint(v.Value, 10, strconv.IntSize) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(uint(value)), nil case "uint8": value, err := strconv.ParseUint(v.Value, 10, 8) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(uint8(value)), nil case "uint16": value, err := strconv.ParseUint(v.Value, 10, 16) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(uint16(value)), nil case "uint32": value, err := strconv.ParseUint(v.Value, 10, 32) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(uint32(value)), nil case "uint64": value, err := strconv.ParseUint(v.Value, 10, 64) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(value), nil case "float32": value, err := strconv.ParseFloat(v.Value, 32) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(float32(value)), nil case "float64": value, err := strconv.ParseFloat(v.Value, 64) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(value), nil case "complex64": - splittedValue := strings.Split(v.Value, complexPartsDelimiter) - if len(splittedValue) != 2 { - return reflect.Value{}, fmt.Errorf("not correct complex64 value") + splitValue := strings.Split(v.Value, complexPartsDelimiter) + if len(splitValue) != 2 { + return reflect.Value{}, fmt.Errorf("not correct complex64 value: %s", v.Value) + } + realPart, err := strconv.ParseFloat(splitValue[0], 32) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) } - realPart, err := strconv.ParseFloat(splittedValue[0], 32) - __checkErrorAndExit__(err) - imaginaryPart, err := strconv.ParseFloat(splittedValue[1], 32) - __checkErrorAndExit__(err) + imaginaryPart, err := strconv.ParseFloat(splitValue[1], 32) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(complex(float32(realPart), float32(imaginaryPart))), nil case "complex128": - splittedValue := strings.Split(v.Value, complexPartsDelimiter) - if len(splittedValue) != 2 { - return reflect.Value{}, fmt.Errorf("not correct complex128 value") + splitValue := strings.Split(v.Value, complexPartsDelimiter) + if len(splitValue) != 2 { + return reflect.Value{}, fmt.Errorf("not correct complex128 value: %s", v.Value) } - realPart, err := strconv.ParseFloat(splittedValue[0], 64) - __checkErrorAndExit__(err) + realPart, err := strconv.ParseFloat(splitValue[0], 64) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } - imaginaryPart, err := strconv.ParseFloat(splittedValue[1], 64) - __checkErrorAndExit__(err) + imaginaryPart, err := strconv.ParseFloat(splitValue[1], 64) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(complex(realPart, imaginaryPart)), nil case "string": return reflect.ValueOf(v.Value), nil case "uintptr": value, err := strconv.ParseUint(v.Value, 10, strconv.IntSize) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrParsingValue, v.Type, err) + } return reflect.ValueOf(uintptr(value)), nil } - return reflect.Value{}, fmt.Errorf("primitive type '%s' is not supported", v.Type) + return reflect.Value{}, fmt.Errorf("unsupported primitive type: '%s'", v.Type) } """.trimIndent() @@ -162,7 +211,9 @@ object GoCodeTemplates { private val structValueToReflectValueMethod = """ func (v __StructValue__) __toReflectValue__() (reflect.Value, error) { structType, err := __convertStringToReflectType__(v.Type) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrStringToReflectTypeFailure, v.Type, err) + } structPtr := reflect.New(structType) @@ -170,8 +221,10 @@ object GoCodeTemplates { field := structPtr.Elem().FieldByName(f.Name) reflectValue, err := f.Value.__toReflectValue__() - __checkErrorAndExit__(err) - + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrRawValueToReflectValueFailure, err) + } + if field.Type().Kind() == reflect.Uintptr { reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().SetUint(reflectValue.Uint()) } else { @@ -195,7 +248,9 @@ object GoCodeTemplates { private val arrayValueToReflectValueMethod = """ func (v __ArrayValue__) __toReflectValue__() (reflect.Value, error) { elementType, err := __convertStringToReflectType__(v.ElementType) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrStringToReflectTypeFailure, v.ElementType, err) + } arrayType := reflect.ArrayOf(v.Length, elementType) arrayPtr := reflect.New(arrayType) @@ -204,7 +259,9 @@ object GoCodeTemplates { element := arrayPtr.Elem().Index(i) reflectValue, err := v.Value[i].__toReflectValue__() - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrRawValueToReflectValueFailure, err) + } element.Set(reflectValue) } @@ -225,7 +282,9 @@ object GoCodeTemplates { private val sliceValueToReflectValueMethod = """ func (v __SliceValue__) __toReflectValue__() (reflect.Value, error) { elementType, err := __convertStringToReflectType__(v.ElementType) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrStringToReflectTypeFailure, v.ElementType, err) + } sliceType := reflect.SliceOf(elementType) slice := reflect.MakeSlice(sliceType, v.Length, v.Length) @@ -236,7 +295,9 @@ object GoCodeTemplates { element := slicePtr.Elem().Index(i) reflectValue, err := v.Value[i].__toReflectValue__() - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrRawValueToReflectValueFailure, err) + } element.Set(reflectValue) } @@ -254,7 +315,9 @@ object GoCodeTemplates { private val nilValueToReflectValueMethod = """ func (v __NilValue__) __toReflectValue__() (reflect.Value, error) { typ, err := __convertStringToReflectType__(v.Type) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrStringToReflectTypeFailure, v.Type, err) + } return reflect.Zero(typ), nil } @@ -270,10 +333,14 @@ object GoCodeTemplates { private val namedValueToReflectValueMethod = """ func (v __NamedValue__) __toReflectValue__() (reflect.Value, error) { value, err := v.Value.__toReflectValue__() - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrRawValueToReflectValueFailure, err) + } typ, err := __convertStringToReflectType__(v.Type) - __checkErrorAndExit__(err) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrStringToReflectTypeFailure, v.Type, err) + } return value.Convert(typ), nil } @@ -300,7 +367,7 @@ object GoCodeTemplates { res, err := __convertStringToReflectType__(typeName[index+1:]) if err != nil { - return nil, err + return nil, fmt.Errorf("not correct type name '%s'", typeName) } result = reflect.SliceOf(res) @@ -318,7 +385,7 @@ object GoCodeTemplates { res, err := __convertStringToReflectType__(typeName[index+1:]) if err != nil { - return nil, err + return nil, fmt.Errorf(ErrStringToReflectTypeFailure, typeName[index+1:], err) } result = reflect.ArrayOf(length, res) @@ -393,11 +460,17 @@ object GoCodeTemplates { } """.trimIndent() - private val checkErrorFunction = """ - func __checkErrorAndExit__(err error) { + private val convertReflectValueOfDefinedTypeToRawValueFunction = """ + func __convertReflectValueOfDefinedTypeToRawValue__(v reflect.Value) (__RawValue__, error) { + value, err := __convertReflectValueOfPredeclaredOrNotDefinedTypeToRawValue__(v) if err != nil { - log.Fatal(err) + return nil, fmt.Errorf(ErrReflectValueToRawValueFailure, err) } + + return __NamedValue__{ + Type: v.Type().Name(), + Value: value, + }, nil } """.trimIndent() @@ -419,55 +492,56 @@ object GoCodeTemplates { } """.trimIndent() - private val convertReflectValueToRawValueFunction = """ - //goland:noinspection GoPreferNilSlice - func __convertReflectValueToRawValue__(valueOfRes reflect.Value) (__RawValue__, error) { + private val convertReflectValueOfPredeclaredOrNotDefinedTypeToRawValueFunction = """ + func __convertReflectValueOfPredeclaredOrNotDefinedTypeToRawValue__(v reflect.Value) (__RawValue__, error) { const outputComplexPartsDelimiter = "@" - switch valueOfRes.Kind() { + switch v.Kind() { case reflect.Bool: return __PrimitiveValue__{ - Type: valueOfRes.Kind().String(), - Value: fmt.Sprintf("%#v", valueOfRes.Bool()), + Type: v.Kind().String(), + Value: fmt.Sprintf("%#v", v.Bool()), }, nil case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return __PrimitiveValue__{ - Type: valueOfRes.Kind().String(), - Value: fmt.Sprintf("%#v", valueOfRes.Int()), + Type: v.Kind().String(), + Value: fmt.Sprintf("%#v", v.Int()), }, nil case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return __PrimitiveValue__{ - Type: valueOfRes.Kind().String(), - Value: fmt.Sprintf("%v", valueOfRes.Uint()), + Type: v.Kind().String(), + Value: fmt.Sprintf("%v", v.Uint()), }, nil case reflect.Float32, reflect.Float64: return __PrimitiveValue__{ - Type: valueOfRes.Kind().String(), - Value: __convertFloat64ValueToString__(valueOfRes.Float()), + Type: v.Kind().String(), + Value: __convertFloat64ValueToString__(v.Float()), }, nil case reflect.Complex64, reflect.Complex128: - value := valueOfRes.Complex() + value := v.Complex() realPartString := __convertFloat64ValueToString__(real(value)) imagPartString := __convertFloat64ValueToString__(imag(value)) return __PrimitiveValue__{ - Type: valueOfRes.Kind().String(), + Type: v.Kind().String(), Value: fmt.Sprintf("%v%v%v", realPartString, outputComplexPartsDelimiter, imagPartString), }, nil case reflect.String: return __PrimitiveValue__{ Type: reflect.String.String(), - Value: fmt.Sprintf("%v", valueOfRes.String()), + Value: fmt.Sprintf("%v", v.String()), }, nil case reflect.Struct: - fields := reflect.VisibleFields(valueOfRes.Type()) - resultValues := make([]__FieldValue__, 0, valueOfRes.NumField()) + fields := reflect.VisibleFields(v.Type()) + resultValues := make([]__FieldValue__, 0, v.NumField()) for _, field := range fields { if len(field.Index) != 1 { continue } - res, err := __convertReflectValueToRawValue__(valueOfRes.FieldByName(field.Name)) - __checkErrorAndExit__(err) + res, err := __convertReflectValueToRawValue__(v.FieldByName(field.Name)) + if err != nil { + return nil, fmt.Errorf(ErrReflectValueToRawValueFailure, err) + } resultValues = append(resultValues, __FieldValue__{ Name: field.Name, @@ -476,16 +550,18 @@ object GoCodeTemplates { }) } return __StructValue__{ - Type: valueOfRes.Type().String(), + Type: "struct{}", Value: resultValues, }, nil case reflect.Array: - elem := valueOfRes.Type().Elem() + elem := v.Type().Elem() elementType := elem.String() - arrayElementValues := []__RawValue__{} - for i := 0; i < valueOfRes.Len(); i++ { - arrayElementValue, err := __convertReflectValueToRawValue__(valueOfRes.Index(i)) - __checkErrorAndExit__(err) + arrayElementValues := make([]__RawValue__, 0, v.Len()) + for i := 0; i < v.Len(); i++ { + arrayElementValue, err := __convertReflectValueToRawValue__(v.Index(i)) + if err != nil { + return nil, fmt.Errorf(ErrReflectValueToRawValueFailure, err) + } arrayElementValues = append(arrayElementValues, arrayElementValue) } @@ -497,42 +573,61 @@ object GoCodeTemplates { Value: arrayElementValues, }, nil case reflect.Slice: - if valueOfRes.IsNil() { - return nil, nil + if v.IsNil() { + return __NilValue__{Type: "nil"}, nil } - elem := valueOfRes.Type().Elem() + elem := v.Type().Elem() elementType := elem.String() - sliceElementValues := []__RawValue__{} - for i := 0; i < valueOfRes.Len(); i++ { - sliceElementValue, err := __convertReflectValueToRawValue__(valueOfRes.Index(i)) - __checkErrorAndExit__(err) + typeName := fmt.Sprintf("[]%s", elementType) + sliceElementValues := make([]__RawValue__, v.Len()) + for i := 0; i < v.Len(); i++ { + sliceElementValue, err := __convertReflectValueToRawValue__(v.Index(i)) + if err != nil { + return nil, fmt.Errorf(ErrReflectValueToRawValueFailure, err) + } sliceElementValues = append(sliceElementValues, sliceElementValue) } length := len(sliceElementValues) return __SliceValue__{ - Type: fmt.Sprintf("[]%s", elementType), + Type: typeName, ElementType: elementType, Length: length, Value: sliceElementValues, }, nil case reflect.Interface: - if valueOfRes.Interface() == nil { - return nil, nil + if v.Interface() == nil { + return __NilValue__{Type: "nil"}, nil } - if e, ok := valueOfRes.Interface().(error); ok { - return __convertReflectValueToRawValue__(reflect.ValueOf(e.Error())) + if e, ok := v.Interface().(error); ok { + value, err := __convertReflectValueToRawValue__(reflect.ValueOf(e.Error())) + if err != nil { + return nil, fmt.Errorf(ErrReflectValueToRawValueFailure, err) + } + return __NamedValue__{ + Type: "error", + Value: value, + }, nil } - return nil, errors.New("unsupported result type: " + valueOfRes.Type().String()) + return nil, fmt.Errorf("unsupported result type: %s", v.Type().String()) default: - return nil, errors.New("unsupported result type: " + valueOfRes.Type().String()) + return nil, fmt.Errorf("unsupported result type: %s", v.Type().String()) + } + } + """.trimIndent() + + private val convertReflectValueToRawValueFunction = """ + func __convertReflectValueToRawValue__(v reflect.Value) (__RawValue__, error) { + if v.Type().PkgPath() != "" { + return __convertReflectValueOfDefinedTypeToRawValue__(v) } + return __convertReflectValueOfPredeclaredOrNotDefinedTypeToRawValue__(v) } """.trimIndent() private fun executeFunctionFunction(maxTraceLength: Int) = """ func __executeFunction__( - timeout time.Duration, arguments []reflect.Value, wrappedFunction func([]reflect.Value) []__RawValue__, + function reflect.Value, arguments []reflect.Value, timeout time.Duration, ) __RawExecutionResult__ { ctxWithTimeout, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() @@ -562,7 +657,10 @@ object GoCodeTemplates { } else { resultValue, err = __convertReflectValueToRawValue__(reflect.ValueOf(panicMessage)) } - __checkErrorAndExit__(err) + if err != nil { + _, _ = fmt.Fprint(os.Stderr, ErrReflectValueToRawValueFailure, err) + os.Exit(1) + } executionResult.PanicMessage = &__RawPanicMessage__{ RawResultValue: resultValue, @@ -574,7 +672,11 @@ object GoCodeTemplates { }() argumentsWithTrace := append(arguments, reflect.ValueOf(&trace)) - resultValues := wrappedFunction(argumentsWithTrace) + resultValues, err := __wrapResultValues__(function.Call(argumentsWithTrace)) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "Failed to wrap result values: %s", err) + os.Exit(1) + } executionResult.RawResultValues = resultValues panicked = false }() @@ -594,63 +696,67 @@ object GoCodeTemplates { """.trimIndent() private val wrapResultValuesForWorkerFunction = """ - //goland:noinspection GoPreferNilSlice - func __wrapResultValuesForUtBotGoWorker__(values []reflect.Value) []__RawValue__ { - rawValues := []__RawValue__{} + func __wrapResultValues__(values []reflect.Value) ([]__RawValue__, error) { + rawValues := make([]__RawValue__, 0, len(values)) for _, value := range values { resultValue, err := __convertReflectValueToRawValue__(value) - __checkErrorAndExit__(err) + if err != nil { + return nil, fmt.Errorf(ErrReflectValueToRawValueFailure, err) + } rawValues = append(rawValues, resultValue) } - return rawValues + return rawValues, nil } """.trimIndent() private val convertRawValuesToReflectValuesFunction = """ - //goland:noinspection GoPreferNilSlice - func __convertRawValuesToReflectValues__(values []__RawValue__) []reflect.Value { - parameters := []reflect.Value{} + func __convertRawValuesToReflectValues__(values []__RawValue__) ([]reflect.Value, error) { + parameters := make([]reflect.Value, 0, len(values)) for _, value := range values { reflectValue, err := value.__toReflectValue__() - __checkErrorAndExit__(err) + if err != nil { + return nil, fmt.Errorf("failed to convert RawValue %s to reflect.Value: %s", value, err) + } parameters = append(parameters, reflectValue) } - return parameters + return parameters, nil } """.trimIndent() private val parseJsonToFunctionNameAndRawValuesFunction = """ - //goland:noinspection GoPreferNilSlice - func __parseJsonToFunctionNameAndRawValues__(decoder *json.Decoder) (string, []__RawValue__, error) { + func __parseTestInput__(decoder *json.Decoder) (funcName string, rawValues []__RawValue__, err error) { var testInput __TestInput__ - err := decoder.Decode(&testInput) - if err == io.EOF { - return "", nil, err + err = decoder.Decode(&testInput) + if err != nil { + return } - __checkErrorAndExit__(err) - result := make([]__RawValue__, 0) + funcName = testInput.FunctionName + rawValues = make([]__RawValue__, 0, 10) for _, arg := range testInput.Arguments { - rawValue, err := __convertParsedJsonToRawValue__(arg, "") - __checkErrorAndExit__(err) + var rawValue __RawValue__ + + rawValue, err = __parseRawValue__(arg, "") + if err != nil { + return "", nil, fmt.Errorf("failed to parse argument %s of function %s: %s", arg, funcName, err) + } - result = append(result, rawValue) + rawValues = append(rawValues, rawValue) } - return testInput.FunctionName, result, nil + return } """.trimIndent() private val convertParsedJsonToRawValueFunction = """ - //goland:noinspection GoPreferNilSlice - func __convertParsedJsonToRawValue__(rawValue map[string]interface{}, name string) (__RawValue__, error) { + func __parseRawValue__(rawValue map[string]interface{}, name string) (__RawValue__, error) { typeName, ok := rawValue["type"] if !ok { - return nil, fmt.Errorf("every rawValue must contain field 'type'") + return nil, fmt.Errorf("every RawValue must contain field 'type'") } typeNameStr, ok := typeName.(string) if !ok { @@ -670,13 +776,15 @@ object GoCodeTemplates { value, ok := v.([]interface{}) if !ok { - return nil, fmt.Errorf("structValue field 'value' must be array") + return nil, fmt.Errorf("StructValue field 'value' must be array") } - values := []__FieldValue__{} + values := make([]__FieldValue__, 0, len(value)) for _, v := range value { - nextValue, err := __convertParsedJsonToFieldValue__(v.(map[string]interface{})) - __checkErrorAndExit__(err) + nextValue, err := __parseFieldValue__(v.(map[string]interface{})) + if err != nil { + return nil, fmt.Errorf("failed to parse field %s of struct: %s", v, err) + } values = append(values, nextValue) } @@ -690,30 +798,32 @@ object GoCodeTemplates { case strings.HasPrefix(typeNameStr, "[]"): elementType, ok := rawValue["elementType"] if !ok { - return nil, fmt.Errorf("sliceValue must contain field 'elementType'") + return nil, fmt.Errorf("SliceValue must contain field 'elementType'") } elementTypeStr, ok := elementType.(string) if !ok { - return nil, fmt.Errorf("sliceValue field 'elementType' must be string") + return nil, fmt.Errorf("SliceValue field 'elementType' must be string") } if _, ok := rawValue["length"]; !ok { - return nil, fmt.Errorf("sliceValue must contain field 'length'") + return nil, fmt.Errorf("SliceValue must contain field 'length'") } length, ok := rawValue["length"].(float64) if !ok { - return nil, fmt.Errorf("sliceValue field 'length' must be float64") + return nil, fmt.Errorf("SliceValue field 'length' must be float64") } value, ok := v.([]interface{}) if !ok || len(value) != int(length) { - return nil, fmt.Errorf("sliceValue field 'value' must be array of length %d", int(length)) + return nil, fmt.Errorf("SliceValue field 'value' must be array of length %d", int(length)) } - values := []__RawValue__{} - for _, v := range value { - nextValue, err := __convertParsedJsonToRawValue__(v.(map[string]interface{}), "") - __checkErrorAndExit__(err) + values := make([]__RawValue__, 0, len(value)) + for i, v := range value { + nextValue, err := __parseRawValue__(v.(map[string]interface{}), "") + if err != nil { + return nil, fmt.Errorf("failed to parse %d slice element: %s", i, err) + } values = append(values, nextValue) } @@ -727,30 +837,32 @@ object GoCodeTemplates { case strings.HasPrefix(typeNameStr, "["): elementType, ok := rawValue["elementType"] if !ok { - return nil, fmt.Errorf("arrayValue must contain field 'elementType") + return nil, fmt.Errorf("ArrayValue must contain field 'elementType") } elementTypeStr, ok := elementType.(string) if !ok { - return nil, fmt.Errorf("arrayValue field 'elementType' must be string") + return nil, fmt.Errorf("ArrayValue field 'elementType' must be string") } if _, ok := rawValue["length"]; !ok { - return nil, fmt.Errorf("arrayValue must contain field 'length'") + return nil, fmt.Errorf("ArrayValue must contain field 'length'") } length, ok := rawValue["length"].(float64) if !ok { - return nil, fmt.Errorf("arrayValue field 'length' must be float64") + return nil, fmt.Errorf("ArrayValue field 'length' must be float64") } value, ok := v.([]interface{}) if !ok || len(value) != int(length) { - return nil, fmt.Errorf("arrayValue field 'value' must be array of length %d", int(length)) + return nil, fmt.Errorf("ArrayValue field 'value' must be array of length %d", int(length)) } - values := []__RawValue__{} - for _, v := range value { - nextValue, err := __convertParsedJsonToRawValue__(v.(map[string]interface{}), "") - __checkErrorAndExit__(err) + values := make([]__RawValue__, 0, len(value)) + for i, v := range value { + nextValue, err := __parseRawValue__(v.(map[string]interface{}), "") + if err != nil { + return nil, fmt.Errorf("failed to parse %d array element: %s", i, err) + } values = append(values, nextValue) } @@ -766,7 +878,7 @@ object GoCodeTemplates { case "bool", "rune", "int", "int8", "int16", "int32", "int64", "byte", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64", "complex64", "complex128", "string", "uintptr": value, ok := v.(string) if !ok { - return nil, fmt.Errorf("primitiveValue field 'value' must be string") + return nil, fmt.Errorf("PrimitiveValue field 'value' must be string") } return __PrimitiveValue__{ @@ -774,8 +886,10 @@ object GoCodeTemplates { Value: value, }, nil default: // named type - value, err := __convertParsedJsonToRawValue__(v.(map[string]interface{}), typeNameStr) - __checkErrorAndExit__(err) + value, err := __parseRawValue__(v.(map[string]interface{}), typeNameStr) + if err != nil { + return nil, fmt.Errorf("failed to parse of NamedValue with type %s: %s", typeNameStr, err) + } return __NamedValue__{ Type: typeNameStr, @@ -787,30 +901,32 @@ object GoCodeTemplates { """.trimIndent() private val convertParsedJsonToFieldValueFunction = """ - func __convertParsedJsonToFieldValue__(p map[string]interface{}) (__FieldValue__, error) { + func __parseFieldValue__(p map[string]interface{}) (__FieldValue__, error) { name, ok := p["name"] if !ok { - return __FieldValue__{}, fmt.Errorf("fieldValue must contain field 'name'") + return __FieldValue__{}, fmt.Errorf("FieldValue must contain field 'name'") } nameStr, ok := name.(string) if !ok { - return __FieldValue__{}, fmt.Errorf("fieldValue 'name' must be string") + return __FieldValue__{}, fmt.Errorf("FieldValue 'name' must be string") + } + + if _, ok := p["value"]; !ok { + return __FieldValue__{}, fmt.Errorf("FieldValue must contain field 'value'") + } + value, err := __parseRawValue__(p["value"].(map[string]interface{}), "") + if err != nil { + return __FieldValue__{}, err } isExported, ok := p["isExported"] if !ok { - return __FieldValue__{}, fmt.Errorf("fieldValue must contain field 'isExported'") + return __FieldValue__{}, fmt.Errorf("FieldValue must contain field 'isExported'") } isExportedBool, ok := isExported.(bool) if !ok { - return __FieldValue__{}, fmt.Errorf("fieldValue 'isExported' must be bool") - } - - if _, ok := p["value"]; !ok { - return __FieldValue__{}, fmt.Errorf("fieldValue must contain field 'value'") + return __FieldValue__{}, fmt.Errorf("FieldValue 'isExported' must be bool") } - value, err := __convertParsedJsonToRawValue__(p["value"].(map[string]interface{}), "") - __checkErrorAndExit__(err) return __FieldValue__{ Name: nameStr, @@ -826,6 +942,7 @@ object GoCodeTemplates { aliases: Map, maxTraceLength: Int, ) = listOf( + errorMessages, testInputStruct, rawValueInterface, primitiveValueStruct, @@ -844,8 +961,9 @@ object GoCodeTemplates { convertStringToReflectType(namedTypes, destinationPackage, aliases), panicMessageStruct, rawExecutionResultStruct, - checkErrorFunction, + convertReflectValueOfDefinedTypeToRawValueFunction, convertFloat64ValueToStringFunction, + convertReflectValueOfPredeclaredOrNotDefinedTypeToRawValueFunction, convertReflectValueToRawValueFunction, executeFunctionFunction(maxTraceLength), wrapResultValuesForWorkerFunction, 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 6000899735..3f1256ac49 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 @@ -14,11 +14,10 @@ internal object GoWorkerCodeGenerationHelper { val alwaysRequiredImports = setOf( GoPackage("io", "io"), + GoPackage("os", "os"), GoPackage("context", "context"), GoPackage("json", "encoding/json"), - GoPackage("errors", "errors"), GoPackage("fmt", "fmt"), - GoPackage("log", "log"), GoPackage("math", "math"), GoPackage("net", "net"), GoPackage("reflect", "reflect"), @@ -122,24 +121,40 @@ internal object GoWorkerCodeGenerationHelper { return """ func $workerTestFunctionName(t *testing.T) { con, err := net.Dial("tcp", ":$port") - __checkErrorAndExit__(err) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "Connection to server failed: %s", err) + os.Exit(1) + } defer func() { - err := con.Close() + err = con.Close() if err != nil { - __checkErrorAndExit__(err) + _, _ = fmt.Fprintf(os.Stderr, "Closing connection failed: %s", err) + os.Exit(1) } }() jsonDecoder := json.NewDecoder(con) for { - funcName, rawValues, err := __parseJsonToFunctionNameAndRawValues__(jsonDecoder) + var ( + funcName string + rawValues []__RawValue__ + ) + funcName, rawValues, err = __parseTestInput__(jsonDecoder) if err == io.EOF { break } - __checkErrorAndExit__(err) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "Failed to parse test input: %s", err) + os.Exit(1) + } - arguments := __convertRawValuesToReflectValues__(rawValues) + var arguments []reflect.Value + arguments, err = __convertRawValuesToReflectValues__(rawValues) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "Failed to convert slice of RawValue to slice of reflect.Value: %s", err) + os.Exit(1) + } var function reflect.Value switch funcName { @@ -149,21 +164,30 @@ internal object GoWorkerCodeGenerationHelper { } } default: - panic(fmt.Sprintf("no function with that name: %s", funcName)) + _, _ = fmt.Fprintf(os.Stderr, "Function %s not found", funcName) + os.Exit(1) } - executionResult := __executeFunction__($eachExecutionTimeoutMillis*time.Millisecond, arguments, func(arguments []reflect.Value) []__RawValue__ { - return __wrapResultValuesForUtBotGoWorker__(function.Call(arguments)) - }) + executionResult := __executeFunction__(function, arguments, $eachExecutionTimeoutMillis*time.Millisecond) - jsonBytes, toJsonErr := json.MarshalIndent(executionResult, "", " ") - __checkErrorAndExit__(toJsonErr) + var jsonBytes []byte + jsonBytes, err = json.Marshal(executionResult) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "Failed to serialize execution result to json: %s", err) + os.Exit(1) + } _, err = con.Write([]byte(strconv.Itoa(len(jsonBytes)) + "\n")) - __checkErrorAndExit__(err) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "Failed to send length of execution result: %s", err) + os.Exit(1) + } _, err = con.Write(jsonBytes) - __checkErrorAndExit__(err) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "Failed to send execution result: %s", err) + os.Exit(1) + } } } """.trimIndent() diff --git a/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt b/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt index 5716ef4e07..0fe65442ce 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt @@ -12,28 +12,12 @@ import kotlin.reflect.KClass data class PrimitiveValue( override val type: String, val value: String, -) : RawValue(type) { - override fun checkIsEqualTypes(type: GoTypeId): Boolean { - if (type is GoNamedTypeId) { - return checkIsEqualTypes(type.underlyingTypeId) - } - if (!type.isPrimitiveGoType && type !is GoInterfaceTypeId) { - return false - } - // for error support - if (this.type == "string" && type is GoInterfaceTypeId) { - return true - } - return GoPrimitiveTypeId(this.type) == type - } -} +) : RawValue(type) data class NamedValue( override val type: String, - val value: RawValue?, -) : RawValue(type) { - override fun checkIsEqualTypes(type: GoTypeId): Boolean = error("Not supported") -} + val value: RawValue, +) : RawValue(type) data class StructValue( override val type: String, @@ -41,102 +25,50 @@ data class StructValue( ) : RawValue(type) { data class FieldValue( val name: String, - val value: RawValue?, + val value: RawValue, val isExported: Boolean ) - - override fun checkIsEqualTypes(type: GoTypeId): Boolean { - if (type is GoNamedTypeId) { - return this.type == type.canonicalName && checkIsEqualTypes(type.underlyingTypeId) - } - if (type !is GoStructTypeId) { - return false - } - if (value.size != type.fields.size) { - return false - } - value.zip(type.fields).forEach { (fieldValue, fieldId) -> - if (fieldValue.name != fieldId.name) { - return false - } - if (fieldValue.value != null && !fieldValue.value.checkIsEqualTypes(fieldId.declaringType)) { - return false - } - if (fieldValue.isExported != fieldId.isExported) { - return false - } - } - return true - } } data class ArrayValue( override val type: String, val elementType: String, val length: Int, - val value: List -) : RawValue(type) { - override fun checkIsEqualTypes(type: GoTypeId): Boolean { - if (type is GoNamedTypeId) { - return checkIsEqualTypes(type.underlyingTypeId) - } - if (type !is GoArrayTypeId) { - return false - } - if (length != type.length || elementType != type.elementTypeId!!.canonicalName) { - return false - } - return value.all { it?.checkIsEqualTypes(type.elementTypeId) ?: true } - } -} + val value: List +) : RawValue(type) data class SliceValue( override val type: String, val elementType: String, val length: Int, - val value: List -) : RawValue(type) { - override fun checkIsEqualTypes(type: GoTypeId): Boolean { - if (type is GoNamedTypeId) { - return checkIsEqualTypes(type.underlyingTypeId) - } - if (type !is GoSliceTypeId) { - return false - } - if (elementType != type.elementTypeId!!.canonicalName) { - return false - } - return value.all { it?.checkIsEqualTypes(type.elementTypeId) ?: true } - } -} + val value: List +) : RawValue(type) -data class NilValue( - override val type: String -) : RawValue(type) { - override fun checkIsEqualTypes(type: GoTypeId): Boolean = error("Not supported") -} +data class NilValue(override val type: String) : RawValue(type) + +data class InterfaceValue(override val type: String) : RawValue(type) @TypeFor(field = "type", adapter = RawResultValueAdapter::class) -abstract class RawValue(open val type: String) { - abstract fun checkIsEqualTypes(type: GoTypeId): Boolean -} +abstract class RawValue(open val type: String) class RawResultValueAdapter : TypeAdapter { override fun classFor(type: Any): KClass { val typeName = type as String return when { + typeName == "nil" -> NilValue::class + typeName == "interface{}" -> InterfaceValue::class + typeName == "struct{}" -> StructValue::class typeName.startsWith("map[") -> error("Map result type not supported") typeName.startsWith("[]") -> SliceValue::class typeName.startsWith("[") -> ArrayValue::class goPrimitives.map { it.name }.contains(typeName) -> PrimitiveValue::class - else -> StructValue::class + else -> NamedValue::class } } } data class RawPanicMessage( - val rawResultValue: RawValue, - val implementsError: Boolean + val rawResultValue: RawValue, val implementsError: Boolean ) data class RawExecutionResult( @@ -156,10 +88,7 @@ private object RawValuesCodes { class GoWorkerFailedException(s: String) : Exception(s) fun convertRawExecutionResultToExecutionResult( - rawExecutionResult: RawExecutionResult, - functionResultTypes: List, - intSize: Int, - timeoutMillis: Long + rawExecutionResult: RawExecutionResult, functionResultTypes: List, intSize: Int, timeoutMillis: Long ): GoUtExecutionResult { if (rawExecutionResult.timeoutExceeded) { return GoUtTimeoutExceeded(timeoutMillis, rawExecutionResult.trace) @@ -168,9 +97,7 @@ fun convertRawExecutionResultToExecutionResult( val (rawResultValue, implementsError) = rawExecutionResult.panicMessage val panicValue = if (goPrimitives.map { it.simpleName }.contains(rawResultValue.type)) { createGoUtPrimitiveModelFromRawValue( - rawResultValue as PrimitiveValue, - GoPrimitiveTypeId(rawResultValue.type), - intSize + rawResultValue as PrimitiveValue, GoPrimitiveTypeId(rawResultValue.type), intSize ) } else { error("Only primitive panic value is currently supported") @@ -180,19 +107,13 @@ fun convertRawExecutionResultToExecutionResult( if (rawExecutionResult.rawResultValues.size != functionResultTypes.size) { error("Function completed execution must have as many result raw values as result types.") } - rawExecutionResult.rawResultValues.zip(functionResultTypes).forEach { (rawResultValue, resultType) -> - if (rawResultValue != null && !rawResultValue.checkIsEqualTypes(resultType)) { - error("Result of function execution must have same type as function result") - } - } var executedWithNonNilErrorString = false - val resultValues = - rawExecutionResult.rawResultValues.zip(functionResultTypes).map { (rawResultValue, resultType) -> - if (resultType.implementsError && rawResultValue != null) { - executedWithNonNilErrorString = true - } - createGoUtModelFromRawValue(rawResultValue, resultType, intSize) + val resultValues = rawExecutionResult.rawResultValues.zip(functionResultTypes).map { (rawResultValue, resultType) -> + if (resultType.implementsError && rawResultValue != null) { + executedWithNonNilErrorString = true } + createGoUtModelFromRawValue(rawResultValue, resultType, intSize) + } return if (executedWithNonNilErrorString) { GoUtExecutionWithNonNilError(resultValues, rawExecutionResult.trace) } else { @@ -202,25 +123,24 @@ fun convertRawExecutionResultToExecutionResult( private fun createGoUtModelFromRawValue( rawValue: RawValue?, typeId: GoTypeId, intSize: Int -): GoUtModel = when { - typeId is GoNamedTypeId -> GoUtNamedModel( - value = createGoUtModelFromRawValue(rawValue, typeId.underlyingTypeId, intSize), - typeId = typeId - ) - - rawValue == null -> GoUtNilModel(typeId) - // Only for error interface - typeId is GoInterfaceTypeId -> GoUtPrimitiveModel((rawValue as PrimitiveValue).value, goStringTypeId) +): GoUtModel = if (rawValue is NilValue) { + GoUtNilModel(typeId) +} else { + when (typeId) { + is GoNamedTypeId -> createGoUtNamedModelFromRawValue(rawValue as NamedValue, typeId, intSize) + // Only for error interface + is GoInterfaceTypeId -> GoUtPrimitiveModel((rawValue as PrimitiveValue).value, goStringTypeId) - typeId is GoStructTypeId -> createGoUtStructModelFromRawValue(rawValue as StructValue, typeId, intSize) + is GoStructTypeId -> createGoUtStructModelFromRawValue(rawValue as StructValue, typeId, intSize) - typeId is GoArrayTypeId -> createGoUtArrayModelFromRawValue(rawValue as ArrayValue, typeId, intSize) + is GoArrayTypeId -> createGoUtArrayModelFromRawValue(rawValue as ArrayValue, typeId, intSize) - typeId is GoSliceTypeId -> createGoUtSliceModelFromRawValue(rawValue as SliceValue, typeId, intSize) + is GoSliceTypeId -> createGoUtSliceModelFromRawValue(rawValue as SliceValue, typeId, intSize) - typeId is GoPrimitiveTypeId -> createGoUtPrimitiveModelFromRawValue(rawValue as PrimitiveValue, typeId, intSize) + is GoPrimitiveTypeId -> createGoUtPrimitiveModelFromRawValue(rawValue as PrimitiveValue, typeId, intSize) - else -> error("Creating a model from raw value of [${typeId.javaClass}] type is not supported") + else -> error("Creating a model from raw value of [${typeId.javaClass}] type is not supported") + } } private fun createGoUtPrimitiveModelFromRawValue( @@ -284,4 +204,11 @@ private fun createGoUtSliceModelFromRawValue( createGoUtModelFromRawValue(resultValue.value[index], resultTypeId.elementTypeId!!, intSize) }.toMutableMap() return GoUtSliceModel(value, resultTypeId, resultValue.length) +} + +private fun createGoUtNamedModelFromRawValue( + resultValue: NamedValue, resultTypeId: GoNamedTypeId, intSize: Int +): GoUtNamedModel { + val value = createGoUtModelFromRawValue(resultValue.value, resultTypeId.underlyingTypeId, intSize) + return GoUtNamedModel(value, resultTypeId) } \ No newline at end of file From e4bb1ad5ebdc962d8041f97a46faafa3067911ae Mon Sep 17 00:00:00 2001 From: Egipti Pavel Date: Fri, 7 Apr 2023 13:13:19 +0300 Subject: [PATCH 4/6] Fix lenght of slice --- .../src/main/kotlin/org/utbot/go/worker/GoCodeTemplates.kt | 2 +- .../main/kotlin/org/utbot/go/worker/RawExecutionResults.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 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 cfb4a90c29..bb5ee41557 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 @@ -579,7 +579,7 @@ object GoCodeTemplates { elem := v.Type().Elem() elementType := elem.String() typeName := fmt.Sprintf("[]%s", elementType) - sliceElementValues := make([]__RawValue__, v.Len()) + sliceElementValues := make([]__RawValue__, 0, v.Len()) for i := 0; i < v.Len(); i++ { sliceElementValue, err := __convertReflectValueToRawValue__(v.Index(i)) if err != nil { diff --git a/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt b/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt index 0fe65442ce..4f390a0e75 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt @@ -73,7 +73,7 @@ data class RawPanicMessage( data class RawExecutionResult( val timeoutExceeded: Boolean, - val rawResultValues: List, + val rawResultValues: List, val panicMessage: RawPanicMessage?, val trace: List ) @@ -109,7 +109,7 @@ fun convertRawExecutionResultToExecutionResult( } var executedWithNonNilErrorString = false val resultValues = rawExecutionResult.rawResultValues.zip(functionResultTypes).map { (rawResultValue, resultType) -> - if (resultType.implementsError && rawResultValue != null) { + if (resultType.implementsError) { executedWithNonNilErrorString = true } createGoUtModelFromRawValue(rawResultValue, resultType, intSize) @@ -122,7 +122,7 @@ fun convertRawExecutionResultToExecutionResult( } private fun createGoUtModelFromRawValue( - rawValue: RawValue?, typeId: GoTypeId, intSize: Int + rawValue: RawValue, typeId: GoTypeId, intSize: Int ): GoUtModel = if (rawValue is NilValue) { GoUtNilModel(typeId) } else { From 813047d5eb15d4a5b2f748c30d20b5a0b078ffd5 Mon Sep 17 00:00:00 2001 From: Egipti Pavel Date: Fri, 7 Apr 2023 14:57:22 +0300 Subject: [PATCH 5/6] Add logging of number of function executions --- .../src/main/kotlin/org/utbot/go/GoEngine.kt | 34 +++++------------ .../utbot/go/logic/GoTestCasesGenerator.kt | 37 +++++++++---------- 2 files changed, 27 insertions(+), 44 deletions(-) diff --git a/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt b/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt index 38ba2d34dc..c5869fd4c4 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/GoEngine.kt @@ -6,13 +6,11 @@ import mu.KotlinLogging import org.utbot.fuzzing.BaseFeedback import org.utbot.fuzzing.Control import org.utbot.fuzzing.utils.Trie -import org.utbot.go.api.GoUtExecutionResult import org.utbot.go.api.GoUtFunction import org.utbot.go.api.GoUtFuzzedFunction -import org.utbot.go.api.GoUtPanicFailure import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.worker.GoWorker -import org.utbot.go.worker.convertRawExecutionResultToExecutionResult +import org.utbot.go.worker.RawExecutionResult val logger = KotlinLogging.logger {} @@ -21,24 +19,18 @@ class GoEngine( private val functionUnderTest: GoUtFunction, private val aliases: Map, private val intSize: Int, - private val eachExecutionTimeoutMillis: Long, private val timeoutExceededOrIsCanceled: () -> Boolean, ) { + var numberOfFunctionExecutions: Int = 0 - fun fuzzing(): Flow> = flow { + fun fuzzing(): Flow> = flow { var attempts = 0 val attemptsLimit = Int.MAX_VALUE if (functionUnderTest.parameters.isEmpty()) { worker.sendFuzzedParametersValues(functionUnderTest, emptyList(), emptyMap()) val rawExecutionResult = worker.receiveRawExecutionResult() - val executionResult = convertRawExecutionResultToExecutionResult( - rawExecutionResult, - functionUnderTest.resultTypes, - intSize, - eachExecutionTimeoutMillis, - ) val fuzzedFunction = GoUtFuzzedFunction(functionUnderTest, emptyList()) - emit(fuzzedFunction to executionResult) + emit(fuzzedFunction to rawExecutionResult) } else { val notCoveredLines = (1..functionUnderTest.numberOfAllStatements).toMutableSet() runGoFuzzing(functionUnderTest, intSize) { description, values -> @@ -48,28 +40,20 @@ class GoEngine( val fuzzedFunction = GoUtFuzzedFunction(functionUnderTest, values) worker.sendFuzzedParametersValues(functionUnderTest, values, aliases) val rawExecutionResult = worker.receiveRawExecutionResult() - val executionResult = convertRawExecutionResultToExecutionResult( - rawExecutionResult, - functionUnderTest.resultTypes, - intSize, - eachExecutionTimeoutMillis, - ) - if (executionResult.trace.isEmpty()) { + numberOfFunctionExecutions++ + if (rawExecutionResult.trace.isEmpty()) { logger.error { "Coverage is empty for [${functionUnderTest.name}] with $values}" } - if (executionResult is GoUtPanicFailure) { - logger.error { "Execution completed with panic: ${executionResult.panicValue}" } - } return@runGoFuzzing BaseFeedback(result = Trie.emptyNode(), control = Control.PASS) } - val trieNode = description.tracer.add(executionResult.trace.map { GoInstruction(it) }) + val trieNode = description.tracer.add(rawExecutionResult.trace.map { GoInstruction(it) }) if (trieNode.count > 1) { if (++attempts >= attemptsLimit) { return@runGoFuzzing BaseFeedback(result = Trie.emptyNode(), control = Control.STOP) } return@runGoFuzzing BaseFeedback(result = trieNode, control = Control.CONTINUE) } - if (notCoveredLines.removeAll(executionResult.trace.toSet())) { - emit(fuzzedFunction to executionResult) + if (notCoveredLines.removeAll(rawExecutionResult.trace.toSet())) { + emit(fuzzedFunction to rawExecutionResult) } BaseFeedback(result = trieNode, control = Control.CONTINUE) } 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 8a3e5f6530..d9125b3327 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 @@ -5,14 +5,10 @@ import kotlinx.coroutines.runBlocking import mu.KotlinLogging import org.utbot.framework.plugin.api.TimeoutException import org.utbot.go.GoEngine -import org.utbot.go.api.GoUtFile -import org.utbot.go.api.GoUtFunction -import org.utbot.go.api.GoUtFuzzedFunctionTestCase +import org.utbot.go.api.* import org.utbot.go.imports.GoImportsResolver import org.utbot.go.util.executeCommandByNewProcessOrFailWithoutWaiting -import org.utbot.go.worker.GoWorker -import org.utbot.go.worker.GoWorkerCodeGenerationHelper -import org.utbot.go.worker.GoWorkerFailedException +import org.utbot.go.worker.* import java.io.File import java.io.InputStreamReader import java.net.ServerSocket @@ -37,7 +33,7 @@ object GoTestCasesGenerator { timeoutExceededOrIsCanceled: (index: Int) -> Boolean = { false }, ): List = runBlocking { ServerSocket(0).use { serverSocket -> - val allTestCases = mutableListOf() + val allRawExecutionResults = mutableListOf>() var fileToExecute: File? = null var fileWithModifiedFunctions: File? = null try { @@ -89,24 +85,19 @@ object GoTestCasesGenerator { logger.debug { "Worker connected - completed in ${System.currentTimeMillis() - processStartTime} ms" } functions.forEachIndexed { index, function -> if (timeoutExceededOrIsCanceled(index)) return@forEachIndexed - val testCases = mutableListOf() - val engine = GoEngine( - worker, - function, - aliases, - intSize, - eachExecutionTimeoutMillis - ) { timeoutExceededOrIsCanceled(index) } + val rawExecutionResults = mutableListOf>() + val engine = GoEngine(worker, function, aliases, intSize) { timeoutExceededOrIsCanceled(index) } logger.info { "Fuzzing for function [${function.name}] - started" } val totalFuzzingTime = measureTimeMillis { engine.fuzzing().catch { logger.error { "Error in flow: ${it.message}" } }.collect { (fuzzedFunction, executionResult) -> - testCases.add(GoUtFuzzedFunctionTestCase(fuzzedFunction, executionResult)) + rawExecutionResults.add(fuzzedFunction to executionResult) } } - logger.info { "Fuzzing for function [${function.name}] - completed in $totalFuzzingTime ms. Generated ${testCases.size} test cases" } - allTestCases += testCases + logger.debug { "Number of function executions - ${engine.numberOfFunctionExecutions}" } + logger.info { "Fuzzing for function [${function.name}] - completed in $totalFuzzingTime ms. Generated ${rawExecutionResults.size} test cases" } + allRawExecutionResults += rawExecutionResults } workerSocket.close() val processHasExited = process.waitFor(endOfWorkerExecutionTimeout, TimeUnit.MILLISECONDS) @@ -162,7 +153,15 @@ object GoTestCasesGenerator { fileToExecute?.delete() fileWithModifiedFunctions?.delete() } - return@runBlocking allTestCases + return@runBlocking allRawExecutionResults.map { (fuzzedFunction, rawExecutionResult) -> + val executionResult: GoUtExecutionResult = convertRawExecutionResultToExecutionResult( + rawExecutionResult, + fuzzedFunction.function.resultTypes, + intSize, + eachExecutionTimeoutMillis + ) + GoUtFuzzedFunctionTestCase(fuzzedFunction, executionResult) + } } } } From 4ab3898ffd7c7ccd24191840abbb6a1eee248c39 Mon Sep 17 00:00:00 2001 From: Egipti Pavel Date: Sun, 9 Apr 2023 14:05:55 +0300 Subject: [PATCH 6/6] Add maps support --- .../go-samples/simple/samples_go_ut_test.go | 22 +- utbot-go/go-samples/simple/supported_types.go | 26 ++ .../simple/supported_types_go_ut_test.go | 88 +++-- .../main/kotlin/org/utbot/go/GoLanguage.kt | 1 + .../kotlin/org/utbot/go/api/GoTypesApi.kt | 21 ++ .../kotlin/org/utbot/go/api/GoUtModelsApi.kt | 56 ++- .../org/utbot/go/api/util/GoTypesApiUtil.kt | 20 +- .../utbot/go/api/util/GoUtModelsApiUtil.kt | 11 + .../go/fuzzer/providers/GoMapValueProvider.kt | 39 ++ .../go/fuzzer/providers/GoNilValueProvider.kt | 3 +- .../go/gocodeanalyzer/AnalysisResults.kt | 14 +- .../GoUtModelToCodeConverter.kt | 28 +- .../org/utbot/go/worker/GoCodeTemplates.kt | 346 +++++++++++++----- .../utbot/go/worker/RawExecutionResults.kt | 27 +- .../analysis_results.go | 10 + .../go_source_code_analyzer/analyzer_core.go | 53 +-- 16 files changed, 568 insertions(+), 197 deletions(-) create mode 100644 utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoMapValueProvider.kt diff --git a/utbot-go/go-samples/simple/samples_go_ut_test.go b/utbot-go/go-samples/simple/samples_go_ut_test.go index 46b38d9b2d..db4ca92ba2 100644 --- a/utbot-go/go-samples/simple/samples_go_ut_test.go +++ b/utbot-go/go-samples/simple/samples_go_ut_test.go @@ -92,23 +92,23 @@ func TestIsIdentityByUtGoFuzzer3(t *testing.T) { } func TestBinaryWithNonNilErrorByUtGoFuzzer1(t *testing.T) { - actualVal, actualErr := Binary([]int{1}, 2, 0, -1) + actualVal, actualErr := Binary(nil, 2, 0, -1) assertMultiple := assert.New(t) assertMultiple.Equal(-1, actualVal) assertMultiple.ErrorContains(actualErr, "target not found in array") } -func TestBinaryByUtGoFuzzer2(t *testing.T) { - actualVal, actualErr := Binary([]int{9223372036854775807, -1, -1, -1, -1}, 9223372036854775807, 0, 1) +func TestBinaryWithNonNilErrorByUtGoFuzzer2(t *testing.T) { + actualVal, actualErr := Binary([]int{1, 1, 0}, 2, 1, 1) assertMultiple := assert.New(t) - assertMultiple.Equal(0, actualVal) - assertMultiple.Nil(actualErr) + assertMultiple.Equal(-1, actualVal) + assertMultiple.ErrorContains(actualErr, "target not found in array") } func TestBinaryWithNonNilErrorByUtGoFuzzer3(t *testing.T) { - actualVal, actualErr := Binary([]int{1, 17592186044417, 257, 1125899906842625}, -9223372036854775808, 1, 2) + actualVal, actualErr := Binary([]int{1, 1, 0}, -9214364837600034814, 1, 1) assertMultiple := assert.New(t) assertMultiple.Equal(-1, actualVal) @@ -116,15 +116,15 @@ func TestBinaryWithNonNilErrorByUtGoFuzzer3(t *testing.T) { } func TestBinaryWithNonNilErrorByUtGoFuzzer4(t *testing.T) { - actualVal, actualErr := Binary([]int{-1, -1, -1, -1, 9223372036854775807}, 9223372036854775807, 0, 1) + actualVal, actualErr := Binary([]int{9223372036854775807, 9223372036854775807, 1, 9223372036854775807, -9223372036854775808}, 9223372036854775807, -1, 1) assertMultiple := assert.New(t) - assertMultiple.Equal(-1, actualVal) - assertMultiple.ErrorContains(actualErr, "target not found in array") + assertMultiple.Equal(0, actualVal) + assertMultiple.Nil(actualErr) } func TestBinaryPanicsByUtGoFuzzer(t *testing.T) { - assert.PanicsWithError(t, "runtime error: index out of range [-4611686018427387905]", func() { Binary([]int{1}, 2, -9223372036854775808, -1) }) + assert.PanicsWithError(t, "runtime error: index out of range [-4611686018427387905]", func() { Binary(nil, 2, -9223372036854775808, -1) }) } func TestStringSearchByUtGoFuzzer1(t *testing.T) { @@ -155,4 +155,4 @@ func TestStringSearchByUtGoFuzzer5(t *testing.T) { actualVal := StringSearch("ABC") assert.Equal(t, true, actualVal) -} \ No newline at end of file +} diff --git a/utbot-go/go-samples/simple/supported_types.go b/utbot-go/go-samples/simple/supported_types.go index 5c9dc8909d..3b9e3f3a50 100644 --- a/utbot-go/go-samples/simple/supported_types.go +++ b/utbot-go/go-samples/simple/supported_types.go @@ -262,3 +262,29 @@ type S struct { func StructWithFieldsOfNamedTypes(s S) S { return s } + +func Map(table map[string]int) map[string]int { + return table +} + +func MapOfStructures(table map[Structure]Structure) map[Structure]Structure { + return table +} + +func MapOfSliceOfInt(table map[string][]int) map[string][]int { + return table +} + +func MapOfNamedType(table map[int]Type) map[int]Type { + return table +} + +func MapOfNamedSlice(table map[uint]NS) map[uint]NS { + return table +} + +type NM map[string]NA + +func NamedMap(n NM) NM { + return n +} diff --git a/utbot-go/go-samples/simple/supported_types_go_ut_test.go b/utbot-go/go-samples/simple/supported_types_go_ut_test.go index 239c75f358..8fced124e0 100644 --- a/utbot-go/go-samples/simple/supported_types_go_ut_test.go +++ b/utbot-go/go-samples/simple/supported_types_go_ut_test.go @@ -108,27 +108,27 @@ func TestUintPtrByUtGoFuzzer(t *testing.T) { } func TestFloat32ByUtGoFuzzer(t *testing.T) { - actualVal := Float32(0.24053639) + actualVal := Float32(0.59754527) - assert.Equal(t, float32(0.24053639), actualVal) + assert.Equal(t, float32(0.59754527), actualVal) } func TestFloat64ByUtGoFuzzer(t *testing.T) { - actualVal := Float64(0.6063452159973596) + actualVal := Float64(0.7815346320453048) - assert.Equal(t, 0.6063452159973596, actualVal) + assert.Equal(t, 0.7815346320453048, actualVal) } func TestComplex64ByUtGoFuzzer(t *testing.T) { - actualVal := Complex64(complex(0.30905056, 0.30905056)) + actualVal := Complex64(complex(0.25277615, 0.25277615)) - assert.Equal(t, complex(float32(0.30905056), float32(0.30905056)), actualVal) + assert.Equal(t, complex(float32(0.25277615), float32(0.25277615)), actualVal) } func TestComplex128ByUtGoFuzzer(t *testing.T) { - actualVal := Complex128(complex(0.5504370051176339, 0.5504370051176339)) + actualVal := Complex128(complex(0.3851891847407185, 0.3851891847407185)) - assert.Equal(t, complex(0.5504370051176339, 0.5504370051176339), actualVal) + assert.Equal(t, complex(0.3851891847407185, 0.3851891847407185), actualVal) } func TestByteByUtGoFuzzer(t *testing.T) { @@ -210,12 +210,12 @@ func TestArrayOfArrayOfStructsByUtGoFuzzer(t *testing.T) { } func TestArrayOfSliceOfUintByUtGoFuzzer(t *testing.T) { - actualVal := ArrayOfSliceOfUint([5][]uint{{}, {}, {}, {}, {}}) + actualVal := ArrayOfSliceOfUint([5][]uint{nil, nil, nil, nil, nil}) - assert.Equal(t, [5][]uint{{}, {}, {}, {}, {}}, actualVal) + assert.Equal(t, [5][]uint{nil, nil, nil, nil, nil}, actualVal) } -func TestReturnErrorOrNilByUtGoFuzzer1(t *testing.T) { +func TestReturnErrorOrNilWithNonNilErrorByUtGoFuzzer1(t *testing.T) { actualErr := returnErrorOrNil(0) assert.Nil(t, actualErr) @@ -240,21 +240,21 @@ func TestExternalStructWithAliasByUtGoFuzzer(t *testing.T) { } func TestSliceOfIntByUtGoFuzzer(t *testing.T) { - actualVal := SliceOfInt([]int{-1}) + actualVal := SliceOfInt(nil) - assert.Equal(t, []int{-1}, actualVal) + assert.Nil(t, actualVal) } func TestSliceOfUintPtrByUtGoFuzzer(t *testing.T) { - actualVal := SliceOfUintPtr([]uintptr{0}) + actualVal := SliceOfUintPtr(nil) - assert.Equal(t, []uintptr{0}, actualVal) + assert.Nil(t, actualVal) } func TestSliceOfStringByUtGoFuzzer(t *testing.T) { - actualVal := SliceOfString([]string{"hello"}) + actualVal := SliceOfString(nil) - assert.Equal(t, []string{"hello"}, actualVal) + assert.Nil(t, actualVal) } func TestSliceOfStructsByUtGoFuzzer(t *testing.T) { @@ -270,15 +270,15 @@ func TestSliceOfStructsWithNanByUtGoFuzzer(t *testing.T) { } func TestSliceOfSliceOfByteByUtGoFuzzer(t *testing.T) { - actualVal := SliceOfSliceOfByte([][]byte{{}}) + actualVal := SliceOfSliceOfByte([][]byte{nil}) - assert.Equal(t, [][]byte{{}}, actualVal) + assert.Equal(t, [][]byte{nil}, actualVal) } func TestSliceOfSliceOfStructsByUtGoFuzzer(t *testing.T) { - actualVal := SliceOfSliceOfStructs([][]Structure{{}}) + actualVal := SliceOfSliceOfStructs([][]Structure{nil}) - assert.Equal(t, [][]Structure{{}}, actualVal) + assert.Equal(t, [][]Structure{nil}, actualVal) } func TestSliceOfArrayOfIntByUtGoFuzzer(t *testing.T) { @@ -294,7 +294,7 @@ func TestExportedStructWithEmbeddedUnexportedStructByUtGoFuzzer(t *testing.T) { } func TestNamedTypeByUtGoFuzzer(t *testing.T) { - actualVal := NamedType(Type(0)) + actualVal := NamedType(0) assert.Equal(t, Type(0), actualVal) } @@ -324,13 +324,49 @@ func TestNamedArrayByUtGoFuzzer(t *testing.T) { } func TestNamedSliceByUtGoFuzzer(t *testing.T) { - actualVal := NamedSlice(NS{-1, 9223372036854775807, 9223372036854775807, 1, 9223372036854775807}) + actualVal := NamedSlice(NS(nil)) - assert.Equal(t, NS{-1, 9223372036854775807, 9223372036854775807, 1, 9223372036854775807}, actualVal) + assert.Nil(t, actualVal) } func TestStructWithFieldsOfNamedTypesByUtGoFuzzer(t *testing.T) { - actualVal := StructWithFieldsOfNamedTypes(S{T: T{{0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}}, NS: NS{9223372036854775807, 1, 0, -9223372036854775808, 9223372036854775807}}) + actualVal := StructWithFieldsOfNamedTypes(S{T: T{{0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}}, NS: NS(nil)}) - assert.Equal(t, S{T: T{{0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}}, NS: NS{9223372036854775807, 1, 0, -9223372036854775808, 9223372036854775807}}, actualVal) + assert.Equal(t, S{T: T{{0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}}, NS: NS(nil)}, actualVal) +} + +func TestMapByUtGoFuzzer(t *testing.T) { + actualVal := Map(nil) + + assert.Nil(t, actualVal) +} + +func TestMapOfStructuresByUtGoFuzzer(t *testing.T) { + actualVal := MapOfStructures(map[Structure]Structure{Structure{}: {}}) + + assert.Equal(t, map[Structure]Structure{Structure{}: {}}, actualVal) +} + +func TestMapOfSliceOfIntByUtGoFuzzer(t *testing.T) { + actualVal := MapOfSliceOfInt(map[string][]int{"hello": {}}) + + assert.Equal(t, map[string][]int{"hello": {}}, actualVal) +} + +func TestMapOfNamedTypeByUtGoFuzzer(t *testing.T) { + actualVal := MapOfNamedType(map[int]Type{-1: 255}) + + assert.Equal(t, map[int]Type{-1: 255}, actualVal) +} + +func TestMapOfNamedSliceByUtGoFuzzer(t *testing.T) { + actualVal := MapOfNamedSlice(nil) + + assert.Nil(t, actualVal) +} + +func TestNamedMapByUtGoFuzzer(t *testing.T) { + actualVal := NamedMap(NM(nil)) + + assert.Nil(t, actualVal) } diff --git a/utbot-go/src/main/kotlin/org/utbot/go/GoLanguage.kt b/utbot-go/src/main/kotlin/org/utbot/go/GoLanguage.kt index becfd20495..4754ad4204 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/GoLanguage.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/GoLanguage.kt @@ -12,6 +12,7 @@ fun goDefaultValueProviders() = listOf( GoPrimitivesValueProvider, GoArrayValueProvider, GoSliceValueProvider, + GoMapValueProvider, GoStructValueProvider, GoConstantValueProvider, GoNamedValueProvider, diff --git a/utbot-go/src/main/kotlin/org/utbot/go/api/GoTypesApi.kt b/utbot-go/src/main/kotlin/org/utbot/go/api/GoTypesApi.kt index 7d12ae9e0f..dad64d15c9 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/api/GoTypesApi.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/api/GoTypesApi.kt @@ -82,6 +82,27 @@ class GoSliceTypeId( override fun hashCode(): Int = elementTypeId.hashCode() } +class GoMapTypeId( + name: String, val keyTypeId: GoTypeId, elementTypeId: GoTypeId, +) : GoTypeId(name, elementTypeId = elementTypeId) { + override val canonicalName: String = "map[${keyTypeId.canonicalName}]${elementTypeId.canonicalName}" + + override fun getRelativeName(destinationPackage: GoPackage, aliases: Map): String { + val keyType = keyTypeId.getRelativeName(destinationPackage, aliases) + val elementType = elementTypeId!!.getRelativeName(destinationPackage, aliases) + return "map[$keyType]$elementType" + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is GoMapTypeId) return false + + return keyTypeId == other.keyTypeId && elementTypeId == other.elementTypeId + } + + override fun hashCode(): Int = 31 * keyTypeId.hashCode() + elementTypeId.hashCode() +} + class GoInterfaceTypeId(name: String) : GoTypeId(name) { override val canonicalName: String = name diff --git a/utbot-go/src/main/kotlin/org/utbot/go/api/GoUtModelsApi.kt b/utbot-go/src/main/kotlin/org/utbot/go/api/GoUtModelsApi.kt index 7199cf327b..292e5d2f28 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/api/GoUtModelsApi.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/api/GoUtModelsApi.kt @@ -2,10 +2,7 @@ package org.utbot.go.api -import org.utbot.go.api.util.goDefaultValueModel -import org.utbot.go.api.util.goFloat64TypeId -import org.utbot.go.api.util.goStringTypeId -import org.utbot.go.api.util.neverRequiresExplicitCast +import org.utbot.go.api.util.* import org.utbot.go.framework.api.go.GoPackage import org.utbot.go.framework.api.go.GoTypeId import org.utbot.go.framework.api.go.GoUtFieldModel @@ -69,15 +66,7 @@ class GoUtArrayModel( override fun getRequiredPackages(destinationPackage: GoPackage): Set { val elementNamedTypeId = typeId.elementTypeId as? GoNamedTypeId - val imports = - if (elementNamedTypeId != null && - !elementNamedTypeId.sourcePackage.isBuiltin && - elementNamedTypeId.sourcePackage != destinationPackage - ) { - setOf(elementNamedTypeId.sourcePackage) - } else { - emptySet() - } + val imports = elementNamedTypeId?.getRequiredPackages(destinationPackage) ?: emptySet() return value.values.fold(imports) { acc, model -> acc + model.getRequiredPackages(destinationPackage) } @@ -104,15 +93,7 @@ class GoUtSliceModel( override fun getRequiredPackages(destinationPackage: GoPackage): Set { val elementNamedTypeId = typeId.elementTypeId as? GoNamedTypeId - val imports = - if (elementNamedTypeId != null && - !elementNamedTypeId.sourcePackage.isBuiltin && - elementNamedTypeId.sourcePackage != destinationPackage - ) { - setOf(elementNamedTypeId.sourcePackage) - } else { - emptySet() - } + val imports = elementNamedTypeId?.getRequiredPackages(destinationPackage) ?: emptySet() return value.values.fold(imports) { acc, model -> acc + model.getRequiredPackages(destinationPackage) } @@ -129,6 +110,27 @@ class GoUtSliceModel( } } +class GoUtMapModel( + val value: MutableMap, + typeId: GoMapTypeId, +) : GoUtModel(typeId) { + override val typeId: GoMapTypeId + get() = super.typeId as GoMapTypeId + + override fun getRequiredPackages(destinationPackage: GoPackage): Set { + val keyNamedTypeId = typeId.keyTypeId as? GoNamedTypeId + var imports = keyNamedTypeId?.getRequiredPackages(destinationPackage) ?: emptySet() + val elementNamedTypeId = typeId.elementTypeId as? GoNamedTypeId + imports = imports + (elementNamedTypeId?.getRequiredPackages(destinationPackage) ?: emptySet()) + return value.values.fold(imports) { acc, model -> + acc + model.getRequiredPackages(destinationPackage) + } + } + + override fun isComparable(): Boolean = + value.keys.all { it.isComparable() } && value.values.all { it.isComparable() } +} + class GoUtFloatNaNModel( typeId: GoPrimitiveTypeId ) : GoUtPrimitiveModel( @@ -186,14 +188,8 @@ class GoUtNamedModel( override val typeId: GoNamedTypeId get() = super.typeId as GoNamedTypeId - override fun getRequiredPackages(destinationPackage: GoPackage): Set { - val import = if (!typeId.sourcePackage.isBuiltin && typeId.sourcePackage != destinationPackage) { - setOf(typeId.sourcePackage) - } else { - emptySet() - } - return import + value.getRequiredPackages(destinationPackage) - } + override fun getRequiredPackages(destinationPackage: GoPackage): Set = + typeId.getRequiredPackages(destinationPackage) + value.getRequiredPackages(destinationPackage) override fun isComparable(): Boolean = value.isComparable() } \ No newline at end of file diff --git a/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoTypesApiUtil.kt b/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoTypesApiUtil.kt index 7578456c94..524cc80730 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoTypesApiUtil.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoTypesApiUtil.kt @@ -154,22 +154,25 @@ fun GoTypeId.goDefaultValueModel(): GoUtModel = when (this) { is GoStructTypeId -> GoUtStructModel(listOf(), this) is GoArrayTypeId -> GoUtArrayModel(hashMapOf(), this) is GoSliceTypeId -> GoUtNilModel(this) + is GoMapTypeId -> GoUtNilModel(this) is GoNamedTypeId -> GoUtNamedModel(this.underlyingTypeId.goDefaultValueModel(), this) else -> error("Generating Go default value model for ${this.javaClass} is not supported") } fun GoTypeId.getAllVisibleNamedTypes(goPackage: GoPackage): Set = when (this) { - is GoStructTypeId -> fields.fold(emptySet()) { acc: Set, field -> - acc + (field.declaringType).getAllVisibleNamedTypes(goPackage) - } - - is GoArrayTypeId, is GoSliceTypeId -> elementTypeId!!.getAllVisibleNamedTypes(goPackage) is GoNamedTypeId -> if (this.sourcePackage == goPackage || this.exported()) { setOf(this) + underlyingTypeId.getAllVisibleNamedTypes(goPackage) } else { emptySet() } + is GoStructTypeId -> fields.fold(emptySet()) { acc: Set, field -> + acc + (field.declaringType).getAllVisibleNamedTypes(goPackage) + } + + is GoArrayTypeId, is GoSliceTypeId -> elementTypeId!!.getAllVisibleNamedTypes(goPackage) + is GoMapTypeId -> keyTypeId.getAllVisibleNamedTypes(goPackage) + elementTypeId!!.getAllVisibleNamedTypes(goPackage) + else -> emptySet() } @@ -177,3 +180,10 @@ fun List.getAllVisibleNamedTypes(goPackage: GoPackage): Set acc + type.getAllVisibleNamedTypes(goPackage) } + +fun GoNamedTypeId.getRequiredPackages(destinationPackage: GoPackage): Set = + if (!this.sourcePackage.isBuiltin && this.sourcePackage != destinationPackage) { + setOf(this.sourcePackage) + } else { + emptySet() + } \ No newline at end of file diff --git a/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoUtModelsApiUtil.kt b/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoUtModelsApiUtil.kt index 633b482b6b..1866c832a4 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoUtModelsApiUtil.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/api/util/GoUtModelsApiUtil.kt @@ -41,6 +41,17 @@ fun GoUtModel.convertToRawValue(destinationPackage: GoPackage, aliases: Map MapValue( + model.typeId.getRelativeName(destinationPackage, aliases), + model.typeId.keyTypeId.getRelativeName(destinationPackage, aliases), + model.typeId.elementTypeId!!.getRelativeName(destinationPackage, aliases), + model.value.entries.map { + val key = it.key.convertToRawValue(destinationPackage, aliases) + val value = it.value.convertToRawValue(destinationPackage, aliases) + MapValue.KeyValue(key, value) + } + ) + is GoUtStructModel -> StructValue( model.typeId.getRelativeName(destinationPackage, aliases), model.value.map { diff --git a/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoMapValueProvider.kt b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoMapValueProvider.kt new file mode 100644 index 0000000000..bcf2ab1af2 --- /dev/null +++ b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoMapValueProvider.kt @@ -0,0 +1,39 @@ +package org.utbot.go.fuzzer.providers + +import org.utbot.fuzzing.Routine +import org.utbot.fuzzing.Seed +import org.utbot.fuzzing.ValueProvider +import org.utbot.go.GoDescription +import org.utbot.go.api.GoMapTypeId +import org.utbot.go.api.GoUtMapModel +import org.utbot.go.framework.api.go.GoTypeId +import org.utbot.go.framework.api.go.GoUtModel + +object GoMapValueProvider : ValueProvider { + override fun accept(type: GoTypeId): Boolean = type is GoMapTypeId + + override fun generate(description: GoDescription, type: GoTypeId): Sequence> = + sequence { + type.let { it as GoMapTypeId }.also { mapType -> + yield( + Seed.Collection( + construct = Routine.Collection { + GoUtMapModel( + value = mutableMapOf(), + typeId = mapType + ) + }, + modify = Routine.ForEach( + listOf( + mapType.keyTypeId, + mapType.elementTypeId!! + ) + ) { self, _, values -> + val model = self as GoUtMapModel + model.value[values[0]] = values[1] + } + ) + ) + } + } +} \ No newline at end of file diff --git a/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoNilValueProvider.kt b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoNilValueProvider.kt index cb1d65ee19..db1b6813f5 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoNilValueProvider.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/fuzzer/providers/GoNilValueProvider.kt @@ -3,13 +3,14 @@ package org.utbot.go.fuzzer.providers import org.utbot.fuzzing.Seed import org.utbot.fuzzing.ValueProvider import org.utbot.go.GoDescription +import org.utbot.go.api.GoMapTypeId import org.utbot.go.api.GoSliceTypeId import org.utbot.go.api.GoUtNilModel import org.utbot.go.framework.api.go.GoTypeId import org.utbot.go.framework.api.go.GoUtModel object GoNilValueProvider : ValueProvider { - override fun accept(type: GoTypeId): Boolean = type is GoSliceTypeId + override fun accept(type: GoTypeId): Boolean = type is GoSliceTypeId || type is GoMapTypeId override fun generate(description: GoDescription, type: GoTypeId): Sequence> = sequenceOf(Seed.Simple(GoUtNilModel(type))) 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 078969214b..00ce2fe4c2 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 @@ -54,6 +54,18 @@ data class AnalyzedSliceType( ) } +data class AnalyzedMapType( + override val name: String, + val keyType: AnalyzedType, + val elementType: AnalyzedType, +) : AnalyzedType(name) { + override fun toGoTypeId(): GoTypeId = GoMapTypeId( + name = name, + keyTypeId = keyType.toGoTypeId(), + elementTypeId = elementType.toGoTypeId(), + ) +} + data class AnalyzedInterfaceType( override val name: String, ) : AnalyzedType(name) { @@ -85,7 +97,7 @@ class AnalyzedTypeAdapter : TypeAdapter { return when { typeName == "interface{}" -> AnalyzedInterfaceType::class typeName == "struct{}" -> AnalyzedStructType::class - typeName.startsWith("map[") -> error("Map type not yet supported") + typeName.startsWith("map[") -> AnalyzedMapType::class typeName.startsWith("[]") -> AnalyzedSliceType::class typeName.startsWith("[") -> AnalyzedArrayType::class goPrimitives.map { it.name }.contains(typeName) -> AnalyzedPrimitiveType::class diff --git a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoUtModelToCodeConverter.kt b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoUtModelToCodeConverter.kt index dc9be0521f..e72da4c4d0 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoUtModelToCodeConverter.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/simplecodegeneration/GoUtModelToCodeConverter.kt @@ -22,6 +22,8 @@ class GoUtModelToCodeConverter( is GoUtSliceModel -> sliceModelToGoCode(model) + is GoUtMapModel -> mapModelToGoCode(model) + is GoUtNamedModel -> if (!withTypeConversion && model.value is GoUtPrimitiveModel) { toGoCodeWithoutTypeName(model.value) } else { @@ -45,6 +47,8 @@ class GoUtModelToCodeConverter( is GoUtSliceModel -> sliceModelToGoCodeWithoutTypeName(model) + is GoUtMapModel -> mapModelToGoCodeWithoutTypeName(model) + is GoUtNamedModel -> toGoCodeWithoutTypeName(model.value) else -> error("Converting a ${model.javaClass} to Go code isn't supported") @@ -67,11 +71,8 @@ class GoUtModelToCodeConverter( } private fun arrayModelToGoCode(model: GoUtArrayModel): String { - val elementType = model.typeId.elementTypeId!! - val elementTypeName = elementType.getRelativeName(destinationPackage, aliases) - return model.getElements().joinToString(prefix = "[${model.length}]$elementTypeName{", postfix = "}") { - toGoCodeWithoutTypeName(it) - } + val typeName = model.typeId.getRelativeName(destinationPackage, aliases) + return typeName + arrayModelToGoCodeWithoutTypeName(model) } private fun arrayModelToGoCodeWithoutTypeName(model: GoUtArrayModel): String = @@ -80,11 +81,8 @@ class GoUtModelToCodeConverter( } private fun sliceModelToGoCode(model: GoUtSliceModel): String { - val elementType = model.typeId.elementTypeId!! - val elementTypeName = elementType.getRelativeName(destinationPackage, aliases) - return model.getElements().joinToString(prefix = "[]$elementTypeName{", postfix = "}") { - toGoCodeWithoutTypeName(it) - } + val typeName = model.typeId.getRelativeName(destinationPackage, aliases) + return typeName + sliceModelToGoCodeWithoutTypeName(model) } private fun sliceModelToGoCodeWithoutTypeName(model: GoUtSliceModel): String = @@ -92,6 +90,16 @@ class GoUtModelToCodeConverter( toGoCodeWithoutTypeName(it) } + private fun mapModelToGoCode(model: GoUtMapModel): String { + val typeName = model.typeId.getRelativeName(destinationPackage, aliases) + return typeName + mapModelToGoCodeWithoutTypeName(model) + } + + private fun mapModelToGoCodeWithoutTypeName(model: GoUtMapModel): String = + model.value.entries.joinToString(prefix = "{", postfix = "}") { + "${toGoCode(it.key)}: ${toGoCodeWithoutTypeName(it.value)}" + } + private fun namedModelToGoCode(model: GoUtNamedModel): String { val typeName = model.typeId.getRelativeName(destinationPackage, aliases) return if (model.value is GoUtPrimitiveModel || model.value is GoUtNilModel) { 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 bb5ee41557..ea54e9b0d3 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 @@ -10,6 +10,7 @@ object GoCodeTemplates { private val errorMessages = """ var ( ErrParsingValue = "failed to parse %s value: %s" + ErrInvalidTypeName = "invalid type name: %s" ErrStringToReflectTypeFailure = "failed to convert '%s' to reflect.Type: %s" ErrRawValueToReflectValueFailure = "failed to convert RawValue to reflect.Value: %s" ErrReflectValueToRawValueFailure = "failed to convert reflect.Value to RawValue: %s" @@ -306,6 +307,53 @@ object GoCodeTemplates { } """.trimIndent() + private val keyValueStruct = """ + type __KeyValue__ struct { + Key __RawValue__ `json:"key"` + Value __RawValue__ `json:"value"` + } + """.trimIndent() + + private val mapValueStruct = """ + type __MapValue__ struct { + Type string `json:"type"` + KeyType string `json:"keyType"` + ElementType string `json:"elementType"` + Value []__KeyValue__ `json:"value"` + } + """.trimIndent() + + private val mapValueToReflectValueMethod = """ + func (v __MapValue__) __toReflectValue__() (reflect.Value, error) { + keyType, err := __convertStringToReflectType__(v.KeyType) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrStringToReflectTypeFailure, v.KeyType, err) + } + + elementType, err := __convertStringToReflectType__(v.ElementType) + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrStringToReflectTypeFailure, v.ElementType, err) + } + + mapType := reflect.MapOf(keyType, elementType) + m := reflect.MakeMap(mapType) + for _, keyValue := range v.Value { + keyReflectValue, err := keyValue.Key.__toReflectValue__() + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrRawValueToReflectValueFailure, err) + } + + valueReflectValue, err := keyValue.Value.__toReflectValue__() + if err != nil { + return reflect.Value{}, fmt.Errorf(ErrRawValueToReflectValueFailure, err) + } + + m.SetMapIndex(keyReflectValue, valueReflectValue) + } + return m, nil + } + """.trimIndent() + private val nilValueStruct = """ type __NilValue__ struct { Type string `json:"type"` @@ -353,95 +401,112 @@ object GoCodeTemplates { ): String { val converter = GoUtModelToCodeConverter(destinationPackage, aliases) return """ - func __convertStringToReflectType__(typeName string) (reflect.Type, error) { - var result reflect.Type - - switch { - case strings.HasPrefix(typeName, "map["): - return nil, fmt.Errorf("map type not supported") - case strings.HasPrefix(typeName, "[]"): - index := strings.IndexRune(typeName, ']') - if index == -1 { - return nil, fmt.Errorf("not correct type name '%s'", typeName) - } - - res, err := __convertStringToReflectType__(typeName[index+1:]) - if err != nil { - return nil, fmt.Errorf("not correct type name '%s'", typeName) - } - - result = reflect.SliceOf(res) - case strings.HasPrefix(typeName, "["): - index := strings.IndexRune(typeName, ']') - if index == -1 { - return nil, fmt.Errorf("not correct type name '%s'", typeName) - } - - lengthStr := typeName[1:index] - length, err := strconv.Atoi(lengthStr) - if err != nil { - return nil, err - } - - res, err := __convertStringToReflectType__(typeName[index+1:]) - if err != nil { - return nil, fmt.Errorf(ErrStringToReflectTypeFailure, typeName[index+1:], err) - } - - result = reflect.ArrayOf(length, res) - default: - switch typeName { - case "bool": - result = reflect.TypeOf(true) - case "int": - result = reflect.TypeOf(0) - case "int8": - result = reflect.TypeOf(int8(0)) - case "int16": - result = reflect.TypeOf(int16(0)) - case "int32": - result = reflect.TypeOf(int32(0)) - case "rune": - result = reflect.TypeOf(rune(0)) - case "int64": - result = reflect.TypeOf(int64(0)) - case "byte": - result = reflect.TypeOf(byte(0)) - case "uint": - result = reflect.TypeOf(uint(0)) - case "uint8": - result = reflect.TypeOf(uint8(0)) - case "uint16": - result = reflect.TypeOf(uint16(0)) - case "uint32": - result = reflect.TypeOf(uint32(0)) - case "uint64": - result = reflect.TypeOf(uint64(0)) - case "float32": - result = reflect.TypeOf(float32(0)) - case "float64": - result = reflect.TypeOf(float64(0)) - case "complex64": - result = reflect.TypeOf(complex(float32(0), float32(0))) - case "complex128": - result = reflect.TypeOf(complex(float64(0), float64(0))) - case "string": - result = reflect.TypeOf("") - case "uintptr": - result = reflect.TypeOf(uintptr(0)) + func __convertStringToReflectType__(typeName string) (reflect.Type, error) { + var result reflect.Type + + switch { + case strings.HasPrefix(typeName, "map["): + index := strings.IndexRune(typeName, ']') + if index == -1 { + return nil, fmt.Errorf(ErrInvalidTypeName, typeName) + } + + keyTypeStr := typeName[4:index] + keyType, err := __convertStringToReflectType__(keyTypeStr) + if err != nil { + return nil, fmt.Errorf(ErrStringToReflectTypeFailure, keyTypeStr, err) + } + + elementTypeStr := typeName[index+1:] + elementType, err := __convertStringToReflectType__(elementTypeStr) + if err != nil { + return nil, fmt.Errorf(ErrStringToReflectTypeFailure, elementTypeStr, err) + } + + result = reflect.MapOf(keyType, elementType) + case strings.HasPrefix(typeName, "[]"): + index := strings.IndexRune(typeName, ']') + if index == -1 { + return nil, fmt.Errorf(ErrInvalidTypeName, typeName) + } + + res, err := __convertStringToReflectType__(typeName[index+1:]) + if err != nil { + return nil, fmt.Errorf(ErrInvalidTypeName, typeName) + } + + result = reflect.SliceOf(res) + case strings.HasPrefix(typeName, "["): + index := strings.IndexRune(typeName, ']') + if index == -1 { + return nil, fmt.Errorf(ErrInvalidTypeName, typeName) + } + + lengthStr := typeName[1:index] + length, err := strconv.Atoi(lengthStr) + if err != nil { + return nil, err + } + + res, err := __convertStringToReflectType__(typeName[index+1:]) + if err != nil { + return nil, fmt.Errorf(ErrStringToReflectTypeFailure, typeName[index+1:], err) + } + + result = reflect.ArrayOf(length, res) + default: + switch typeName { + case "bool": + result = reflect.TypeOf(true) + case "int": + result = reflect.TypeOf(0) + case "int8": + result = reflect.TypeOf(int8(0)) + case "int16": + result = reflect.TypeOf(int16(0)) + case "int32": + result = reflect.TypeOf(int32(0)) + case "rune": + result = reflect.TypeOf(rune(0)) + case "int64": + result = reflect.TypeOf(int64(0)) + case "byte": + result = reflect.TypeOf(byte(0)) + case "uint": + result = reflect.TypeOf(uint(0)) + case "uint8": + result = reflect.TypeOf(uint8(0)) + case "uint16": + result = reflect.TypeOf(uint16(0)) + case "uint32": + result = reflect.TypeOf(uint32(0)) + case "uint64": + result = reflect.TypeOf(uint64(0)) + case "float32": + result = reflect.TypeOf(float32(0)) + case "float64": + result = reflect.TypeOf(float64(0)) + case "complex64": + result = reflect.TypeOf(complex(float32(0), float32(0))) + case "complex128": + result = reflect.TypeOf(complex(float64(0), float64(0))) + case "string": + result = reflect.TypeOf("") + case "uintptr": + result = reflect.TypeOf(uintptr(0)) ${ namedTypes.joinToString(separator = "\n") { val relativeName = it.getRelativeName(destinationPackage, aliases) "case \"${relativeName}\": result = reflect.TypeOf(${converter.toGoCode(it.goDefaultValueModel())})" } } - default: - return nil, fmt.Errorf("type '%s' is not supported", typeName) - } - } - return result, nil - } - """.trimIndent() + default: + return nil, fmt.Errorf("unsupported type: %s", typeName) + } + } + return result, nil + } + """.trimIndent() } private val panicMessageStruct = """ @@ -595,12 +660,44 @@ object GoCodeTemplates { Length: length, Value: sliceElementValues, }, nil + case reflect.Map: + if v.IsNil() { + return __NilValue__{Type: "nil"}, nil + } + key := v.Type().Key() + keyType := key.String() + elem := v.Type().Elem() + elementType := elem.String() + typeName := fmt.Sprintf("map[%s]%s", keyType, elementType) + mapValues := make([]__KeyValue__, 0, v.Len()) + for iter := v.MapRange(); iter.Next(); { + key, err := __convertReflectValueToRawValue__(iter.Key()) + if err != nil { + return nil, fmt.Errorf(ErrReflectValueToRawValueFailure, err) + } + + value, err := __convertReflectValueToRawValue__(iter.Value()) + if err != nil { + return nil, fmt.Errorf(ErrReflectValueToRawValueFailure, err) + } + + mapValues = append(mapValues, __KeyValue__{ + Key: key, + Value: value, + }) + } + return __MapValue__{ + Type: typeName, + KeyType: keyType, + ElementType: elementType, + Value: mapValues, + }, nil case reflect.Interface: if v.Interface() == nil { return __NilValue__{Type: "nil"}, nil } if e, ok := v.Interface().(error); ok { - value, err := __convertReflectValueToRawValue__(reflect.ValueOf(e.Error())) + value, err := __convertReflectValueOfPredeclaredOrNotDefinedTypeToRawValue__(reflect.ValueOf(e.Error())) if err != nil { return nil, fmt.Errorf(ErrReflectValueToRawValueFailure, err) } @@ -727,7 +824,7 @@ object GoCodeTemplates { } """.trimIndent() - private val parseJsonToFunctionNameAndRawValuesFunction = """ + private val parseTestInputFunction = """ func __parseTestInput__(decoder *json.Decoder) (funcName string, rawValues []__RawValue__, err error) { var testInput __TestInput__ err = decoder.Decode(&testInput) @@ -752,7 +849,7 @@ object GoCodeTemplates { } """.trimIndent() - private val convertParsedJsonToRawValueFunction = """ + private val parseRawValueFunction = """ func __parseRawValue__(rawValue map[string]interface{}, name string) (__RawValue__, error) { typeName, ok := rawValue["type"] if !ok { @@ -794,7 +891,45 @@ object GoCodeTemplates { Value: values, }, nil case strings.HasPrefix(typeNameStr, "map["): - return nil, fmt.Errorf("map type is not supported") + keyType, ok := rawValue["keyType"] + if !ok { + return nil, fmt.Errorf("MapValue must contain field 'keyType'") + } + keyTypeStr, ok := keyType.(string) + if !ok { + return nil, fmt.Errorf("MapValue field 'keyType' must be string") + } + + elementType, ok := rawValue["elementType"] + if !ok { + return nil, fmt.Errorf("MapValue must contain field 'elementType'") + } + elementTypeStr, ok := elementType.(string) + if !ok { + return nil, fmt.Errorf("MapValue field 'elementType' must be string") + } + + value, ok := v.([]interface{}) + if !ok { + return nil, fmt.Errorf("MapValue field 'value' must be array") + } + + values := make([]__KeyValue__, 0, len(value)) + for _, v := range value { + nextValue, err := __parseKeyValue__(v.(map[string]interface{})) + if err != nil { + return nil, fmt.Errorf("failed to parse KeyValue %s of map: %s", v, err) + } + + values = append(values, nextValue) + } + + return __MapValue__{ + Type: typeNameStr, + KeyType: keyTypeStr, + ElementType: elementTypeStr, + Value: values, + }, nil case strings.HasPrefix(typeNameStr, "[]"): elementType, ok := rawValue["elementType"] if !ok { @@ -900,7 +1035,7 @@ object GoCodeTemplates { } """.trimIndent() - private val convertParsedJsonToFieldValueFunction = """ + private val parseFieldValueFunction = """ func __parseFieldValue__(p map[string]interface{}) (__FieldValue__, error) { name, ok := p["name"] if !ok { @@ -936,6 +1071,31 @@ object GoCodeTemplates { } """.trimIndent() + private val parseKeyValueFunction = """ + func __parseKeyValue__(p map[string]interface{}) (__KeyValue__, error) { + if _, ok := p["key"]; !ok { + return __KeyValue__{}, fmt.Errorf("KeyValue must contain field 'key'") + } + key, err := __parseRawValue__(p["key"].(map[string]interface{}), "") + if err != nil { + return __KeyValue__{}, err + } + + if _, ok := p["value"]; !ok { + return __KeyValue__{}, fmt.Errorf("KeyValue must contain field 'value'") + } + value, err := __parseRawValue__(p["value"].(map[string]interface{}), "") + if err != nil { + return __KeyValue__{}, err + } + + return __KeyValue__{ + Key: key, + Value: value, + }, nil + } + """.trimIndent() + fun getTopLevelHelperStructsAndFunctionsForWorker( namedTypes: Set, destinationPackage: GoPackage, @@ -950,6 +1110,9 @@ object GoCodeTemplates { fieldValueStruct, structValueStruct, structValueToReflectValueMethod, + keyValueStruct, + mapValueStruct, + mapValueToReflectValueMethod, arrayValueStruct, arrayValueToReflectValueMethod, sliceValueStruct, @@ -968,8 +1131,9 @@ object GoCodeTemplates { executeFunctionFunction(maxTraceLength), wrapResultValuesForWorkerFunction, convertRawValuesToReflectValuesFunction, - parseJsonToFunctionNameAndRawValuesFunction, - convertParsedJsonToRawValueFunction, - convertParsedJsonToFieldValueFunction + parseTestInputFunction, + parseRawValueFunction, + parseFieldValueFunction, + parseKeyValueFunction ) } \ No newline at end of file diff --git a/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt b/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt index 4f390a0e75..c2ef62da35 100644 --- a/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt +++ b/utbot-go/src/main/kotlin/org/utbot/go/worker/RawExecutionResults.kt @@ -44,6 +44,18 @@ data class SliceValue( val value: List ) : RawValue(type) +data class MapValue( + override val type: String, + val keyType: String, + val elementType: String, + val value: List +) : RawValue(type) { + data class KeyValue( + val key: RawValue, + val value: RawValue + ) +} + data class NilValue(override val type: String) : RawValue(type) data class InterfaceValue(override val type: String) : RawValue(type) @@ -58,7 +70,7 @@ class RawResultValueAdapter : TypeAdapter { typeName == "nil" -> NilValue::class typeName == "interface{}" -> InterfaceValue::class typeName == "struct{}" -> StructValue::class - typeName.startsWith("map[") -> error("Map result type not supported") + typeName.startsWith("map[") -> MapValue::class typeName.startsWith("[]") -> SliceValue::class typeName.startsWith("[") -> ArrayValue::class goPrimitives.map { it.name }.contains(typeName) -> PrimitiveValue::class @@ -137,6 +149,8 @@ private fun createGoUtModelFromRawValue( is GoSliceTypeId -> createGoUtSliceModelFromRawValue(rawValue as SliceValue, typeId, intSize) + is GoMapTypeId -> createGoUtMapModelFromRawValue(rawValue as MapValue, typeId, intSize) + is GoPrimitiveTypeId -> createGoUtPrimitiveModelFromRawValue(rawValue as PrimitiveValue, typeId, intSize) else -> error("Creating a model from raw value of [${typeId.javaClass}] type is not supported") @@ -206,6 +220,17 @@ private fun createGoUtSliceModelFromRawValue( return GoUtSliceModel(value, resultTypeId, resultValue.length) } +private fun createGoUtMapModelFromRawValue( + resultValue: MapValue, resultTypeId: GoMapTypeId, intSize: Int +): GoUtMapModel { + val value = resultValue.value.associate { + val key = createGoUtModelFromRawValue(it.key, resultTypeId.keyTypeId, intSize) + val value = createGoUtModelFromRawValue(it.value, resultTypeId.elementTypeId!!, intSize) + key to value + }.toMutableMap() + return GoUtMapModel(value, resultTypeId) +} + private fun createGoUtNamedModelFromRawValue( resultValue: NamedValue, resultTypeId: GoNamedTypeId, intSize: Int ): GoUtNamedModel { 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 91bec689a5..028dc80e4c 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 @@ -77,6 +77,16 @@ type AnalyzedFunctionParameter struct { Type AnalyzedType `json:"type"` } +type AnalyzedMapType struct { + Name string `json:"name"` + KeyType AnalyzedType `json:"keyType"` + ElementType AnalyzedType `json:"elementType"` +} + +func (t AnalyzedMapType) GetName() string { + return t.Name +} + type AnalyzedFunction struct { Name string `json:"name"` ModifiedName string `json:"modifiedName"` 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 9a2e424298..153cd28a8c 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 @@ -26,8 +26,6 @@ func implementsError(typ types.Type) bool { //goland:noinspection GoPreferNilSlice func toAnalyzedType(typ types.Type) (AnalyzedType, error) { - var result AnalyzedType - switch t := typ.(type) { case *types.Named: name := t.Obj().Name() @@ -44,7 +42,7 @@ func toAnalyzedType(typ types.Type) (AnalyzedType, error) { underlyingType, err := toAnalyzedType(t.Underlying()) checkError(err) - result = AnalyzedNamedType{ + return AnalyzedNamedType{ Name: name, SourcePackage: GoPackage{ PackageName: packageName, @@ -52,10 +50,10 @@ func toAnalyzedType(typ types.Type) (AnalyzedType, error) { }, ImplementsError: isError, UnderlyingType: underlyingType, - } + }, nil case *types.Basic: name := t.Name() - result = AnalyzedPrimitiveType{Name: name} + return AnalyzedPrimitiveType{Name: name}, nil case *types.Struct: name := "struct{}" @@ -69,50 +67,60 @@ func toAnalyzedType(typ types.Type) (AnalyzedType, error) { fields = append(fields, AnalyzedField{field.Name(), fieldType, field.Exported()}) } - result = AnalyzedStructType{ + return AnalyzedStructType{ Name: name, Fields: fields, - } + }, nil case *types.Array: - arrayType := typ.(*types.Array) - - arrayElemType, err := toAnalyzedType(arrayType.Elem()) + arrayElemType, err := toAnalyzedType(t.Elem()) checkError(err) elemTypeName := arrayElemType.GetName() - length := arrayType.Len() + length := t.Len() name := fmt.Sprintf("[%d]%s", length, elemTypeName) - result = AnalyzedArrayType{ + return AnalyzedArrayType{ Name: name, ElementType: arrayElemType, Length: length, - } + }, nil case *types.Slice: - sliceType := typ.(*types.Slice) - - sliceElemType, err := toAnalyzedType(sliceType.Elem()) + sliceElemType, err := toAnalyzedType(t.Elem()) checkError(err) elemTypeName := sliceElemType.GetName() name := fmt.Sprintf("[]%s", elemTypeName) - result = AnalyzedSliceType{ + return AnalyzedSliceType{ Name: name, ElementType: sliceElemType, - } + }, nil + case *types.Map: + keyType, err := toAnalyzedType(t.Key()) + checkError(err) + + elemType, err := toAnalyzedType(t.Elem()) + checkError(err) + + name := fmt.Sprintf("map[%s]%s", keyType.GetName(), elemType.GetName()) + + return AnalyzedMapType{ + Name: name, + KeyType: keyType, + ElementType: elemType, + }, nil case *types.Interface: name := "interface{}" isError := implementsError(t) if !isError { return nil, errors.New("currently only error interface is supported") } - result = AnalyzedInterfaceType{ + return AnalyzedInterfaceType{ Name: name, - } + }, nil } - return result, nil + return nil, fmt.Errorf("unsupported type: %s", typ) } // for now supports only basic and error result types @@ -135,6 +143,9 @@ func checkTypeIsSupported(typ types.Type, isResultType bool) bool { if sliceType, ok := underlyingType.(*types.Slice); ok { return checkTypeIsSupported(sliceType.Elem(), isResultType) } + if mapType, ok := underlyingType.(*types.Map); ok { + return checkTypeIsSupported(mapType.Key(), isResultType) && checkTypeIsSupported(mapType.Elem(), isResultType) + } if interfaceType, ok := underlyingType.(*types.Interface); ok && isResultType { return implementsError(interfaceType) }