diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 3cb74e63243..80b989eb82e 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -13,6 +13,8 @@ Deprecations SDK Features --- +* `service/dynamodb/dynamodbattribute`: New Encoder and Decoder Behavior for Empty Collections + * The `Encoder` and `Decoder` types have been enhanced to support the marshaling of empty structures, maps, and slices to and from their respective DynamoDB AttributeValues. SDK Enhancements --- diff --git a/aws/convert_types.go b/aws/convert_types.go index ff5d58e0683..4e076c1837a 100644 --- a/aws/convert_types.go +++ b/aws/convert_types.go @@ -179,6 +179,242 @@ func IntValueMap(src map[string]*int) map[string]int { return dst } +// Uint returns a pointer to the uint value passed in. +func Uint(v uint) *uint { + return &v +} + +// UintValue returns the value of the uint pointer passed in or +// 0 if the pointer is nil. +func UintValue(v *uint) uint { + if v != nil { + return *v + } + return 0 +} + +// UintSlice converts a slice of uint values uinto a slice of +// uint pointers +func UintSlice(src []uint) []*uint { + dst := make([]*uint, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// UintValueSlice converts a slice of uint pointers uinto a slice of +// uint values +func UintValueSlice(src []*uint) []uint { + dst := make([]uint, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// UintMap converts a string map of uint values uinto a string +// map of uint pointers +func UintMap(src map[string]uint) map[string]*uint { + dst := make(map[string]*uint) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// UintValueMap converts a string map of uint pointers uinto a string +// map of uint values +func UintValueMap(src map[string]*uint) map[string]uint { + dst := make(map[string]uint) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Int8 returns a pointer to the int8 value passed in. +func Int8(v int8) *int8 { + return &v +} + +// Int8Value returns the value of the int8 pointer passed in or +// 0 if the pointer is nil. +func Int8Value(v *int8) int8 { + if v != nil { + return *v + } + return 0 +} + +// Int8Slice converts a slice of int8 values into a slice of +// int8 pointers +func Int8Slice(src []int8) []*int8 { + dst := make([]*int8, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Int8ValueSlice converts a slice of int8 pointers into a slice of +// int8 values +func Int8ValueSlice(src []*int8) []int8 { + dst := make([]int8, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Int8Map converts a string map of int8 values into a string +// map of int8 pointers +func Int8Map(src map[string]int8) map[string]*int8 { + dst := make(map[string]*int8) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Int8ValueMap converts a string map of int8 pointers into a string +// map of int8 values +func Int8ValueMap(src map[string]*int8) map[string]int8 { + dst := make(map[string]int8) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Int16 returns a pointer to the int16 value passed in. +func Int16(v int16) *int16 { + return &v +} + +// Int16Value returns the value of the int16 pointer passed in or +// 0 if the pointer is nil. +func Int16Value(v *int16) int16 { + if v != nil { + return *v + } + return 0 +} + +// Int16Slice converts a slice of int16 values into a slice of +// int16 pointers +func Int16Slice(src []int16) []*int16 { + dst := make([]*int16, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Int16ValueSlice converts a slice of int16 pointers into a slice of +// int16 values +func Int16ValueSlice(src []*int16) []int16 { + dst := make([]int16, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Int16Map converts a string map of int16 values into a string +// map of int16 pointers +func Int16Map(src map[string]int16) map[string]*int16 { + dst := make(map[string]*int16) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Int16ValueMap converts a string map of int16 pointers into a string +// map of int16 values +func Int16ValueMap(src map[string]*int16) map[string]int16 { + dst := make(map[string]int16) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Int32 returns a pointer to the int32 value passed in. +func Int32(v int32) *int32 { + return &v +} + +// Int32Value returns the value of the int32 pointer passed in or +// 0 if the pointer is nil. +func Int32Value(v *int32) int32 { + if v != nil { + return *v + } + return 0 +} + +// Int32Slice converts a slice of int32 values into a slice of +// int32 pointers +func Int32Slice(src []int32) []*int32 { + dst := make([]*int32, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Int32ValueSlice converts a slice of int32 pointers into a slice of +// int32 values +func Int32ValueSlice(src []*int32) []int32 { + dst := make([]int32, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Int32Map converts a string map of int32 values into a string +// map of int32 pointers +func Int32Map(src map[string]int32) map[string]*int32 { + dst := make(map[string]*int32) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Int32ValueMap converts a string map of int32 pointers into a string +// map of int32 values +func Int32ValueMap(src map[string]*int32) map[string]int32 { + dst := make(map[string]int32) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + // Int64 returns a pointer to the int64 value passed in. func Int64(v int64) *int64 { return &v @@ -238,6 +474,301 @@ func Int64ValueMap(src map[string]*int64) map[string]int64 { return dst } +// Uint8 returns a pointer to the uint8 value passed in. +func Uint8(v uint8) *uint8 { + return &v +} + +// Uint8Value returns the value of the uint8 pointer passed in or +// 0 if the pointer is nil. +func Uint8Value(v *uint8) uint8 { + if v != nil { + return *v + } + return 0 +} + +// Uint8Slice converts a slice of uint8 values into a slice of +// uint8 pointers +func Uint8Slice(src []uint8) []*uint8 { + dst := make([]*uint8, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Uint8ValueSlice converts a slice of uint8 pointers into a slice of +// uint8 values +func Uint8ValueSlice(src []*uint8) []uint8 { + dst := make([]uint8, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Uint8Map converts a string map of uint8 values into a string +// map of uint8 pointers +func Uint8Map(src map[string]uint8) map[string]*uint8 { + dst := make(map[string]*uint8) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Uint8ValueMap converts a string map of uint8 pointers into a string +// map of uint8 values +func Uint8ValueMap(src map[string]*uint8) map[string]uint8 { + dst := make(map[string]uint8) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Uint16 returns a pointer to the uint16 value passed in. +func Uint16(v uint16) *uint16 { + return &v +} + +// Uint16Value returns the value of the uint16 pointer passed in or +// 0 if the pointer is nil. +func Uint16Value(v *uint16) uint16 { + if v != nil { + return *v + } + return 0 +} + +// Uint16Slice converts a slice of uint16 values into a slice of +// uint16 pointers +func Uint16Slice(src []uint16) []*uint16 { + dst := make([]*uint16, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Uint16ValueSlice converts a slice of uint16 pointers into a slice of +// uint16 values +func Uint16ValueSlice(src []*uint16) []uint16 { + dst := make([]uint16, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Uint16Map converts a string map of uint16 values into a string +// map of uint16 pointers +func Uint16Map(src map[string]uint16) map[string]*uint16 { + dst := make(map[string]*uint16) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Uint16ValueMap converts a string map of uint16 pointers into a string +// map of uint16 values +func Uint16ValueMap(src map[string]*uint16) map[string]uint16 { + dst := make(map[string]uint16) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Uint32 returns a pointer to the uint32 value passed in. +func Uint32(v uint32) *uint32 { + return &v +} + +// Uint32Value returns the value of the uint32 pointer passed in or +// 0 if the pointer is nil. +func Uint32Value(v *uint32) uint32 { + if v != nil { + return *v + } + return 0 +} + +// Uint32Slice converts a slice of uint32 values into a slice of +// uint32 pointers +func Uint32Slice(src []uint32) []*uint32 { + dst := make([]*uint32, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Uint32ValueSlice converts a slice of uint32 pointers into a slice of +// uint32 values +func Uint32ValueSlice(src []*uint32) []uint32 { + dst := make([]uint32, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Uint32Map converts a string map of uint32 values into a string +// map of uint32 pointers +func Uint32Map(src map[string]uint32) map[string]*uint32 { + dst := make(map[string]*uint32) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Uint32ValueMap converts a string map of uint32 pointers into a string +// map of uint32 values +func Uint32ValueMap(src map[string]*uint32) map[string]uint32 { + dst := make(map[string]uint32) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Uint64 returns a pointer to the uint64 value passed in. +func Uint64(v uint64) *uint64 { + return &v +} + +// Uint64Value returns the value of the uint64 pointer passed in or +// 0 if the pointer is nil. +func Uint64Value(v *uint64) uint64 { + if v != nil { + return *v + } + return 0 +} + +// Uint64Slice converts a slice of uint64 values into a slice of +// uint64 pointers +func Uint64Slice(src []uint64) []*uint64 { + dst := make([]*uint64, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Uint64ValueSlice converts a slice of uint64 pointers into a slice of +// uint64 values +func Uint64ValueSlice(src []*uint64) []uint64 { + dst := make([]uint64, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Uint64Map converts a string map of uint64 values into a string +// map of uint64 pointers +func Uint64Map(src map[string]uint64) map[string]*uint64 { + dst := make(map[string]*uint64) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Uint64ValueMap converts a string map of uint64 pointers into a string +// map of uint64 values +func Uint64ValueMap(src map[string]*uint64) map[string]uint64 { + dst := make(map[string]uint64) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Float32 returns a pointer to the float32 value passed in. +func Float32(v float32) *float32 { + return &v +} + +// Float32Value returns the value of the float32 pointer passed in or +// 0 if the pointer is nil. +func Float32Value(v *float32) float32 { + if v != nil { + return *v + } + return 0 +} + +// Float32Slice converts a slice of float32 values into a slice of +// float32 pointers +func Float32Slice(src []float32) []*float32 { + dst := make([]*float32, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Float32ValueSlice converts a slice of float32 pointers into a slice of +// float32 values +func Float32ValueSlice(src []*float32) []float32 { + dst := make([]float32, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Float32Map converts a string map of float32 values into a string +// map of float32 pointers +func Float32Map(src map[string]float32) map[string]*float32 { + dst := make(map[string]*float32) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Float32ValueMap converts a string map of float32 pointers into a string +// map of float32 values +func Float32ValueMap(src map[string]*float32) map[string]float32 { + dst := make(map[string]float32) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + // Float64 returns a pointer to the float64 value passed in. func Float64(v float64) *float64 { return &v diff --git a/aws/convert_types_test.go b/aws/convert_types_test.go index 1a9461e1bab..1c83947bf94 100644 --- a/aws/convert_types_test.go +++ b/aws/convert_types_test.go @@ -207,6 +207,105 @@ func TestBoolMap(t *testing.T) { } } +var testCasesUintSlice = [][]uint{ + {1, 2, 3, 4}, +} + +func TestUintSlice(t *testing.T) { + for idx, in := range testCasesUintSlice { + if in == nil { + continue + } + out := UintSlice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := UintValueSlice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + +var testCasesUintValueSlice = [][]*uint{} + +func TestUintValueSlice(t *testing.T) { + for idx, in := range testCasesUintValueSlice { + if in == nil { + continue + } + out := UintValueSlice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if in[i] == nil { + if out[i] != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := *(in[i]), out[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + + out2 := UintSlice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out2 { + if in[i] == nil { + if *(out2[i]) != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := in[i], out2[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + } +} + +var testCasesUintMap = []map[string]uint{ + {"a": 3, "b": 2, "c": 1}, +} + +func TestUintMap(t *testing.T) { + for idx, in := range testCasesUintMap { + if in == nil { + continue + } + out := UintMap(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := UintValueMap(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + var testCasesIntSlice = [][]int{ {1, 2, 3, 4}, } @@ -306,16 +405,16 @@ func TestIntMap(t *testing.T) { } } -var testCasesInt64Slice = [][]int64{ +var testCasesInt8Slice = [][]int8{ {1, 2, 3, 4}, } -func TestInt64Slice(t *testing.T) { - for idx, in := range testCasesInt64Slice { +func TestInt8Slice(t *testing.T) { + for idx, in := range testCasesInt8Slice { if in == nil { continue } - out := Int64Slice(in) + out := Int8Slice(in) if e, a := len(out), len(in); e != a { t.Errorf("Unexpected len at idx %d", idx) } @@ -325,7 +424,7 @@ func TestInt64Slice(t *testing.T) { } } - out2 := Int64ValueSlice(out) + out2 := Int8ValueSlice(out) if e, a := len(out2), len(in); e != a { t.Errorf("Unexpected len at idx %d", idx) } @@ -335,14 +434,14 @@ func TestInt64Slice(t *testing.T) { } } -var testCasesInt64ValueSlice = [][]*int64{} +var testCasesInt8ValueSlice = [][]*int8{} -func TestInt64ValueSlice(t *testing.T) { - for idx, in := range testCasesInt64ValueSlice { +func TestInt8ValueSlice(t *testing.T) { + for idx, in := range testCasesInt8ValueSlice { if in == nil { continue } - out := Int64ValueSlice(in) + out := Int8ValueSlice(in) if e, a := len(out), len(in); e != a { t.Errorf("Unexpected len at idx %d", idx) } @@ -358,7 +457,7 @@ func TestInt64ValueSlice(t *testing.T) { } } - out2 := Int64Slice(out) + out2 := Int8Slice(out) if e, a := len(out2), len(in); e != a { t.Errorf("Unexpected len at idx %d", idx) } @@ -376,16 +475,16 @@ func TestInt64ValueSlice(t *testing.T) { } } -var testCasesInt64Map = []map[string]int64{ +var testCasesInt8Map = []map[string]int8{ {"a": 3, "b": 2, "c": 1}, } -func TestInt64Map(t *testing.T) { - for idx, in := range testCasesInt64Map { +func TestInt8Map(t *testing.T) { + for idx, in := range testCasesInt8Map { if in == nil { continue } - out := Int64Map(in) + out := Int8Map(in) if e, a := len(out), len(in); e != a { t.Errorf("Unexpected len at idx %d", idx) } @@ -395,7 +494,799 @@ func TestInt64Map(t *testing.T) { } } - out2 := Int64ValueMap(out) + out2 := Int8ValueMap(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + +var testCasesInt16Slice = [][]int16{ + {1, 2, 3, 4}, +} + +func TestInt16Slice(t *testing.T) { + for idx, in := range testCasesInt16Slice { + if in == nil { + continue + } + out := Int16Slice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := Int16ValueSlice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + +var testCasesInt16ValueSlice = [][]*int16{} + +func TestInt16ValueSlice(t *testing.T) { + for idx, in := range testCasesInt16ValueSlice { + if in == nil { + continue + } + out := Int16ValueSlice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if in[i] == nil { + if out[i] != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := *(in[i]), out[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + + out2 := Int16Slice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out2 { + if in[i] == nil { + if *(out2[i]) != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := in[i], out2[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + } +} + +var testCasesInt16Map = []map[string]int16{ + {"a": 3, "b": 2, "c": 1}, +} + +func TestInt16Map(t *testing.T) { + for idx, in := range testCasesInt16Map { + if in == nil { + continue + } + out := Int16Map(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := Int16ValueMap(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + +var testCasesInt32Slice = [][]int32{ + {1, 2, 3, 4}, +} + +func TestInt32Slice(t *testing.T) { + for idx, in := range testCasesInt32Slice { + if in == nil { + continue + } + out := Int32Slice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := Int32ValueSlice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + +var testCasesInt32ValueSlice = [][]*int32{} + +func TestInt32ValueSlice(t *testing.T) { + for idx, in := range testCasesInt32ValueSlice { + if in == nil { + continue + } + out := Int32ValueSlice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if in[i] == nil { + if out[i] != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := *(in[i]), out[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + + out2 := Int32Slice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out2 { + if in[i] == nil { + if *(out2[i]) != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := in[i], out2[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + } +} + +var testCasesInt32Map = []map[string]int32{ + {"a": 3, "b": 2, "c": 1}, +} + +func TestInt32Map(t *testing.T) { + for idx, in := range testCasesInt32Map { + if in == nil { + continue + } + out := Int32Map(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := Int32ValueMap(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + +var testCasesInt64Slice = [][]int64{ + {1, 2, 3, 4}, +} + +func TestInt64Slice(t *testing.T) { + for idx, in := range testCasesInt64Slice { + if in == nil { + continue + } + out := Int64Slice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := Int64ValueSlice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + +var testCasesInt64ValueSlice = [][]*int64{} + +func TestInt64ValueSlice(t *testing.T) { + for idx, in := range testCasesInt64ValueSlice { + if in == nil { + continue + } + out := Int64ValueSlice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if in[i] == nil { + if out[i] != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := *(in[i]), out[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + + out2 := Int64Slice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out2 { + if in[i] == nil { + if *(out2[i]) != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := in[i], out2[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + } +} + +var testCasesInt64Map = []map[string]int64{ + {"a": 3, "b": 2, "c": 1}, +} + +func TestInt64Map(t *testing.T) { + for idx, in := range testCasesInt64Map { + if in == nil { + continue + } + out := Int64Map(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := Int64ValueMap(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + +var testCasesUint8Slice = [][]uint8{ + {1, 2, 3, 4}, +} + +func TestUint8Slice(t *testing.T) { + for idx, in := range testCasesUint8Slice { + if in == nil { + continue + } + out := Uint8Slice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := Uint8ValueSlice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + +var testCasesUint8ValueSlice = [][]*uint8{} + +func TestUint8ValueSlice(t *testing.T) { + for idx, in := range testCasesUint8ValueSlice { + if in == nil { + continue + } + out := Uint8ValueSlice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if in[i] == nil { + if out[i] != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := *(in[i]), out[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + + out2 := Uint8Slice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out2 { + if in[i] == nil { + if *(out2[i]) != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := in[i], out2[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + } +} + +var testCasesUint8Map = []map[string]uint8{ + {"a": 3, "b": 2, "c": 1}, +} + +func TestUint8Map(t *testing.T) { + for idx, in := range testCasesUint8Map { + if in == nil { + continue + } + out := Uint8Map(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := Uint8ValueMap(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + +var testCasesUint16Slice = [][]uint16{ + {1, 2, 3, 4}, +} + +func TestUint16Slice(t *testing.T) { + for idx, in := range testCasesUint16Slice { + if in == nil { + continue + } + out := Uint16Slice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := Uint16ValueSlice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + +var testCasesUint16ValueSlice = [][]*uint16{} + +func TestUint16ValueSlice(t *testing.T) { + for idx, in := range testCasesUint16ValueSlice { + if in == nil { + continue + } + out := Uint16ValueSlice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if in[i] == nil { + if out[i] != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := *(in[i]), out[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + + out2 := Uint16Slice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out2 { + if in[i] == nil { + if *(out2[i]) != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := in[i], out2[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + } +} + +var testCasesUint16Map = []map[string]uint16{ + {"a": 3, "b": 2, "c": 1}, +} + +func TestUint16Map(t *testing.T) { + for idx, in := range testCasesUint16Map { + if in == nil { + continue + } + out := Uint16Map(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := Uint16ValueMap(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + +var testCasesUint32Slice = [][]uint32{ + {1, 2, 3, 4}, +} + +func TestUint32Slice(t *testing.T) { + for idx, in := range testCasesUint32Slice { + if in == nil { + continue + } + out := Uint32Slice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := Uint32ValueSlice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + +var testCasesUint32ValueSlice = [][]*uint32{} + +func TestUint32ValueSlice(t *testing.T) { + for idx, in := range testCasesUint32ValueSlice { + if in == nil { + continue + } + out := Uint32ValueSlice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if in[i] == nil { + if out[i] != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := *(in[i]), out[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + + out2 := Uint32Slice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out2 { + if in[i] == nil { + if *(out2[i]) != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := in[i], out2[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + } +} + +var testCasesUint32Map = []map[string]uint32{ + {"a": 3, "b": 2, "c": 1}, +} + +func TestUint32Map(t *testing.T) { + for idx, in := range testCasesUint32Map { + if in == nil { + continue + } + out := Uint32Map(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := Uint32ValueMap(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + +var testCasesUint64Slice = [][]uint64{ + {1, 2, 3, 4}, +} + +func TestUint64Slice(t *testing.T) { + for idx, in := range testCasesUint64Slice { + if in == nil { + continue + } + out := Uint64Slice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := Uint64ValueSlice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + +var testCasesUint64ValueSlice = [][]*uint64{} + +func TestUint64ValueSlice(t *testing.T) { + for idx, in := range testCasesUint64ValueSlice { + if in == nil { + continue + } + out := Uint64ValueSlice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if in[i] == nil { + if out[i] != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := *(in[i]), out[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + + out2 := Uint64Slice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out2 { + if in[i] == nil { + if *(out2[i]) != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := in[i], out2[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + } +} + +var testCasesUint64Map = []map[string]uint64{ + {"a": 3, "b": 2, "c": 1}, +} + +func TestUint64Map(t *testing.T) { + for idx, in := range testCasesUint64Map { + if in == nil { + continue + } + out := Uint64Map(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := Uint64ValueMap(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + +var testCasesFloat32Slice = [][]float32{ + {1, 2, 3, 4}, +} + +func TestFloat32Slice(t *testing.T) { + for idx, in := range testCasesFloat32Slice { + if in == nil { + continue + } + out := Float32Slice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := Float32ValueSlice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + if e, a := in, out2; !reflect.DeepEqual(e, a) { + t.Errorf("Unexpected value at idx %d", idx) + } + } +} + +var testCasesFloat32ValueSlice = [][]*float32{} + +func TestFloat32ValueSlice(t *testing.T) { + for idx, in := range testCasesFloat32ValueSlice { + if in == nil { + continue + } + out := Float32ValueSlice(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if in[i] == nil { + if out[i] != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := *(in[i]), out[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + + out2 := Float32Slice(out) + if e, a := len(out2), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out2 { + if in[i] == nil { + if *(out2[i]) != 0 { + t.Errorf("Unexpected value at idx %d", idx) + } + } else { + if e, a := in[i], out2[i]; e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + } + } +} + +var testCasesFloat32Map = []map[string]float32{ + {"a": 3, "b": 2, "c": 1}, +} + +func TestFloat32Map(t *testing.T) { + for idx, in := range testCasesFloat32Map { + if in == nil { + continue + } + out := Float32Map(in) + if e, a := len(out), len(in); e != a { + t.Errorf("Unexpected len at idx %d", idx) + } + for i := range out { + if e, a := in[i], *(out[i]); e != a { + t.Errorf("Unexpected value at idx %d", idx) + } + } + + out2 := Float32ValueMap(out) if e, a := len(out2), len(in); e != a { t.Errorf("Unexpected len at idx %d", idx) } @@ -562,7 +1453,7 @@ func TestTimeValueSlice(t *testing.T) { } for i := range out2 { if in[i] == nil { - if !(*(out2[i])).IsZero() { + if !(out2[i]).IsZero() { t.Errorf("Unexpected value at idx %d", idx) } } else { diff --git a/service/dynamodb/dynamodbattribute/decode.go b/service/dynamodb/dynamodbattribute/decode.go index 4985c9a5d0c..49e32659dbb 100644 --- a/service/dynamodb/dynamodbattribute/decode.go +++ b/service/dynamodb/dynamodbattribute/decode.go @@ -171,23 +171,23 @@ func (d *Decoder) decode(av *dynamodb.AttributeValue, v reflect.Value, fieldTag } switch { - case len(av.B) != 0: + case av.B != nil: return d.decodeBinary(av.B, v) case av.BOOL != nil: return d.decodeBool(av.BOOL, v) - case len(av.BS) != 0: + case av.BS != nil: return d.decodeBinarySet(av.BS, v) - case len(av.L) != 0: + case av.L != nil: return d.decodeList(av.L, v) - case len(av.M) != 0: + case av.M != nil: return d.decodeMap(av.M, v) case av.N != nil: return d.decodeNumber(av.N, v, fieldTag) - case len(av.NS) != 0: + case av.NS != nil: return d.decodeNumberSet(av.NS, v) - case av.S != nil: + case av.S != nil: // DynamoDB does not allow for empty strings, so we do not consider the length or EnableEmptyCollections flag here return d.decodeString(av.S, v, fieldTag) - case len(av.SS) != 0: + case av.SS != nil: return d.decodeStringSet(av.SS, v) } @@ -202,7 +202,7 @@ func (d *Decoder) decodeBinary(b []byte, v reflect.Value) error { return nil } - if v.Kind() != reflect.Slice { + if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { return &UnmarshalTypeError{Value: "binary", Type: v.Type()} } @@ -220,7 +220,7 @@ func (d *Decoder) decodeBinary(b []byte, v reflect.Value) error { switch v.Type().Elem().Kind() { case reflect.Uint8: // Fallback to reflection copy for type aliased of []byte type - if v.IsNil() || v.Cap() < len(b) { + if v.Kind() != reflect.Array && (v.IsNil() || v.Cap() < len(b)) { v.Set(reflect.MakeSlice(v.Type(), len(b), len(b))) } else if v.Len() != len(b) { v.SetLen(len(b)) diff --git a/service/dynamodb/dynamodbattribute/decode_test.go b/service/dynamodb/dynamodbattribute/decode_test.go index d7f96d1cd90..6309b29f782 100644 --- a/service/dynamodb/dynamodbattribute/decode_test.go +++ b/service/dynamodb/dynamodbattribute/decode_test.go @@ -19,8 +19,10 @@ func TestUnmarshalErrorTypes(t *testing.T) { func TestUnmarshalShared(t *testing.T) { for i, c := range sharedTestCases { - err := Unmarshal(c.in, c.actual) - assertConvertTest(t, i, c.actual, c.expected, err, c.err) + t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { + err := Unmarshal(c.in, c.actual) + assertConvertTest(t, c.actual, c.expected, err, c.err) + }) } } @@ -173,8 +175,10 @@ func TestUnmarshal(t *testing.T) { } for i, c := range cases { - err := Unmarshal(c.in, c.actual) - assertConvertTest(t, i, c.actual, c.expected, err, c.err) + t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { + err := Unmarshal(c.in, c.actual) + assertConvertTest(t, c.actual, c.expected, err, c.err) + }) } } @@ -184,7 +188,7 @@ func TestInterfaceInput(t *testing.T) { err := Unmarshal(&dynamodb.AttributeValue{L: []dynamodb.AttributeValue{ {S: aws.String("abc")}, {S: aws.String("123")}, }}, &v) - assertConvertTest(t, 0, v, expected, err, nil) + assertConvertTest(t, v, expected, err, nil) } func TestUnmarshalError(t *testing.T) { @@ -202,15 +206,19 @@ func TestUnmarshalError(t *testing.T) { } for i, c := range cases { - err := Unmarshal(c.in, c.actual) - assertConvertTest(t, i, c.actual, c.expected, err, c.err) + t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { + err := Unmarshal(c.in, c.actual) + assertConvertTest(t, c.actual, c.expected, err, c.err) + }) } } func TestUnmarshalListShared(t *testing.T) { for i, c := range sharedListTestCases { - err := UnmarshalList(c.in, c.actual) - assertConvertTest(t, i, c.actual, c.expected, err, c.err) + t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { + err := UnmarshalList(c.in, c.actual) + assertConvertTest(t, c.actual, c.expected, err, c.err) + }) } } @@ -229,15 +237,19 @@ func TestUnmarshalListError(t *testing.T) { } for i, c := range cases { - err := UnmarshalList(c.in, c.actual) - assertConvertTest(t, i, c.actual, c.expected, err, c.err) + t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { + err := UnmarshalList(c.in, c.actual) + assertConvertTest(t, c.actual, c.expected, err, c.err) + }) } } func TestUnmarshalMapShared(t *testing.T) { for i, c := range sharedMapTestCases { - err := UnmarshalMap(c.in, c.actual) - assertConvertTest(t, i, c.actual, c.expected, err, c.err) + t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { + err := UnmarshalMap(c.in, c.actual) + assertConvertTest(t, c.actual, c.expected, err, c.err) + }) } } @@ -264,8 +276,10 @@ func TestUnmarshalMapError(t *testing.T) { } for i, c := range cases { - err := UnmarshalMap(c.in, c.actual) - assertConvertTest(t, i, c.actual, c.expected, err, c.err) + t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { + err := UnmarshalMap(c.in, c.actual) + assertConvertTest(t, c.actual, c.expected, err, c.err) + }) } } @@ -317,8 +331,10 @@ func TestUnmarshalListOfMaps(t *testing.T) { } for i, c := range cases { - err := UnmarshalListOfMaps(c.in, c.actual) - assertConvertTest(t, i, c.actual, c.expected, err, c.err) + t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { + err := UnmarshalListOfMaps(c.in, c.actual) + assertConvertTest(t, c.actual, c.expected, err, c.err) + }) } } diff --git a/service/dynamodb/dynamodbattribute/empty_collections_test.go b/service/dynamodb/dynamodbattribute/empty_collections_test.go new file mode 100644 index 00000000000..eadc7376410 --- /dev/null +++ b/service/dynamodb/dynamodbattribute/empty_collections_test.go @@ -0,0 +1,632 @@ +package dynamodbattribute + +import ( + "reflect" + "testing" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/dynamodb" +) + +type testEmptyCollectionsNumericalScalars struct { + String string + + Uint8 uint8 + Uint16 uint16 + Uint32 uint32 + Uint64 uint64 + + Int8 int8 + Int16 int16 + Int32 int32 + Int64 int64 + + Float32 float32 + Float64 float64 +} + +type testEmptyCollectionsOmittedNumericalScalars struct { + String string `dynamodbav:",omitempty"` + + Uint8 uint8 `dynamodbav:",omitempty"` + Uint16 uint16 `dynamodbav:",omitempty"` + Uint32 uint32 `dynamodbav:",omitempty"` + Uint64 uint64 `dynamodbav:",omitempty"` + + Int8 int8 `dynamodbav:",omitempty"` + Int16 int16 `dynamodbav:",omitempty"` + Int32 int32 `dynamodbav:",omitempty"` + Int64 int64 `dynamodbav:",omitempty"` + + Float32 float32 `dynamodbav:",omitempty"` + Float64 float64 `dynamodbav:",omitempty"` +} + +type testEmptyCollectionsPtrScalars struct { + PtrString *string + + PtrUint8 *uint8 + PtrUint16 *uint16 + PtrUint32 *uint32 + PtrUint64 *uint64 + + PtrInt8 *int8 + PtrInt16 *int16 + PtrInt32 *int32 + PtrInt64 *int64 + + PtrFloat32 *float32 + PtrFloat64 *float64 +} + +type testEmptyCollectionsOmittedPtrNumericalScalars struct { + PtrUint8 *uint8 `dynamodbav:",omitempty"` + PtrUint16 *uint16 `dynamodbav:",omitempty"` + PtrUint32 *uint32 `dynamodbav:",omitempty"` + PtrUint64 *uint64 `dynamodbav:",omitempty"` + + PtrInt8 *int8 `dynamodbav:",omitempty"` + PtrInt16 *int16 `dynamodbav:",omitempty"` + PtrInt32 *int32 `dynamodbav:",omitempty"` + PtrInt64 *int64 `dynamodbav:",omitempty"` + + PtrFloat32 *float32 `dynamodbav:",omitempty"` + PtrFloat64 *float64 `dynamodbav:",omitempty"` +} + +type testEmptyCollectionTypes struct { + Map map[string]string + Slice []string + ByteSlice []byte + ByteArray [4]byte + ZeroArray [0]byte + BinarySet [][]byte `dynamodbav:",binaryset"` + NumberSet []int `dynamodbav:",numberset"` + StringSet []string `dynamodbav:",stringset"` +} + +type testEmptyCollectionTypesOmitted struct { + Map map[string]string `dynamodbav:",omitempty"` + Slice []string `dynamodbav:",omitempty"` + ByteSlice []byte `dynamodbav:",omitempty"` + ByteArray [4]byte `dynamodbav:",omitempty"` + ZeroArray [0]byte `dynamodbav:",omitempty"` + BinarySet [][]byte `dynamodbav:",binaryset,omitempty"` + NumberSet []int `dynamodbav:",numberset,omitempty"` + StringSet []string `dynamodbav:",stringset,omitempty"` +} + +type testEmptyCollectionStruct struct { + Int int +} + +type testEmptyCollectionStructOmitted struct { + Slice []string `dynamodbav:",omitempty"` +} + +var sharedEmptyCollectionsTestCases = map[string]struct { + in *dynamodb.AttributeValue + actual, expected interface{} + err error +}{ + "scalars with zero value": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "String": {NULL: aws.Bool(true)}, + "Uint8": {N: aws.String("0")}, + "Uint16": {N: aws.String("0")}, + "Uint32": {N: aws.String("0")}, + "Uint64": {N: aws.String("0")}, + "Int8": {N: aws.String("0")}, + "Int16": {N: aws.String("0")}, + "Int32": {N: aws.String("0")}, + "Int64": {N: aws.String("0")}, + "Float32": {N: aws.String("0")}, + "Float64": {N: aws.String("0")}, + }, + }, + actual: &testEmptyCollectionsNumericalScalars{}, + expected: testEmptyCollectionsNumericalScalars{}, + }, + "scalars with non-zero values": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "String": {S: aws.String("test string")}, + "Uint8": {N: aws.String("1")}, + "Uint16": {N: aws.String("2")}, + "Uint32": {N: aws.String("3")}, + "Uint64": {N: aws.String("4")}, + "Int8": {N: aws.String("-5")}, + "Int16": {N: aws.String("-6")}, + "Int32": {N: aws.String("-7")}, + "Int64": {N: aws.String("-8")}, + "Float32": {N: aws.String("9.9")}, + "Float64": {N: aws.String("10.1")}, + }, + }, + actual: &testEmptyCollectionsNumericalScalars{}, + expected: testEmptyCollectionsNumericalScalars{ + String: "test string", + Uint8: 1, + Uint16: 2, + Uint32: 3, + Uint64: 4, + Int8: -5, + Int16: -6, + Int32: -7, + Int64: -8, + Float32: 9.9, + Float64: 10.1, + }, + }, + "omittable scalars with zero value": { + in: &dynamodb.AttributeValue{M: map[string]dynamodb.AttributeValue{}}, + actual: &testEmptyCollectionsOmittedNumericalScalars{}, + expected: testEmptyCollectionsOmittedNumericalScalars{}, + }, + "omittable scalars with non-zero value": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "String": {S: aws.String("test string")}, + "Uint8": {N: aws.String("1")}, + "Uint16": {N: aws.String("2")}, + "Uint32": {N: aws.String("3")}, + "Uint64": {N: aws.String("4")}, + "Int8": {N: aws.String("-5")}, + "Int16": {N: aws.String("-6")}, + "Int32": {N: aws.String("-7")}, + "Int64": {N: aws.String("-8")}, + "Float32": {N: aws.String("9.9")}, + "Float64": {N: aws.String("10.1")}, + }, + }, + actual: &testEmptyCollectionsOmittedNumericalScalars{}, + expected: testEmptyCollectionsOmittedNumericalScalars{ + String: "test string", + Uint8: 1, + Uint16: 2, + Uint32: 3, + Uint64: 4, + Int8: -5, + Int16: -6, + Int32: -7, + Int64: -8, + Float32: 9.9, + Float64: 10.1, + }, + }, + "nil pointer scalars": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "PtrString": {NULL: aws.Bool(true)}, + "PtrUint8": {NULL: aws.Bool(true)}, + "PtrUint16": {NULL: aws.Bool(true)}, + "PtrUint32": {NULL: aws.Bool(true)}, + "PtrUint64": {NULL: aws.Bool(true)}, + "PtrInt8": {NULL: aws.Bool(true)}, + "PtrInt16": {NULL: aws.Bool(true)}, + "PtrInt32": {NULL: aws.Bool(true)}, + "PtrInt64": {NULL: aws.Bool(true)}, + "PtrFloat32": {NULL: aws.Bool(true)}, + "PtrFloat64": {NULL: aws.Bool(true)}, + }, + }, + actual: &testEmptyCollectionsPtrScalars{}, + expected: testEmptyCollectionsPtrScalars{}, + }, + "non-nil pointer to scalars with zero value": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "PtrString": {NULL: aws.Bool(true)}, + "PtrUint8": {N: aws.String("0")}, + "PtrUint16": {N: aws.String("0")}, + "PtrUint32": {N: aws.String("0")}, + "PtrUint64": {N: aws.String("0")}, + "PtrInt8": {N: aws.String("0")}, + "PtrInt16": {N: aws.String("0")}, + "PtrInt32": {N: aws.String("0")}, + "PtrInt64": {N: aws.String("0")}, + "PtrFloat32": {N: aws.String("0")}, + "PtrFloat64": {N: aws.String("0")}, + }, + }, + actual: &testEmptyCollectionsPtrScalars{}, + expected: testEmptyCollectionsPtrScalars{ + PtrUint8: aws.Uint8(0), + PtrUint16: aws.Uint16(0), + PtrUint32: aws.Uint32(0), + PtrUint64: aws.Uint64(0), + PtrInt8: aws.Int8(0), + PtrInt16: aws.Int16(0), + PtrInt32: aws.Int32(0), + PtrInt64: aws.Int64(0), + PtrFloat32: aws.Float32(0), + PtrFloat64: aws.Float64(0), + }, + }, + "pointer scalars non-nil non-zero": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "PtrString": {S: aws.String("test string")}, + "PtrUint8": {N: aws.String("1")}, + "PtrUint16": {N: aws.String("2")}, + "PtrUint32": {N: aws.String("3")}, + "PtrUint64": {N: aws.String("4")}, + "PtrInt8": {N: aws.String("-5")}, + "PtrInt16": {N: aws.String("-6")}, + "PtrInt32": {N: aws.String("-7")}, + "PtrInt64": {N: aws.String("-8")}, + "PtrFloat32": {N: aws.String("9.9")}, + "PtrFloat64": {N: aws.String("10.1")}, + }, + }, + actual: &testEmptyCollectionsPtrScalars{}, + expected: testEmptyCollectionsPtrScalars{ + PtrString: aws.String("test string"), + PtrUint8: aws.Uint8(1), + PtrUint16: aws.Uint16(2), + PtrUint32: aws.Uint32(3), + PtrUint64: aws.Uint64(4), + PtrInt8: aws.Int8(-5), + PtrInt16: aws.Int16(-6), + PtrInt32: aws.Int32(-7), + PtrInt64: aws.Int64(-8), + PtrFloat32: aws.Float32(9.9), + PtrFloat64: aws.Float64(10.1), + }, + }, + "omittable nil pointer scalars": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{}, + }, + actual: &testEmptyCollectionsOmittedPtrNumericalScalars{}, + expected: testEmptyCollectionsOmittedPtrNumericalScalars{}, + }, + "omittable non-nil pointer to scalars with zero value": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "PtrUint8": {N: aws.String("0")}, + "PtrUint16": {N: aws.String("0")}, + "PtrUint32": {N: aws.String("0")}, + "PtrUint64": {N: aws.String("0")}, + "PtrInt8": {N: aws.String("0")}, + "PtrInt16": {N: aws.String("0")}, + "PtrInt32": {N: aws.String("0")}, + "PtrInt64": {N: aws.String("0")}, + "PtrFloat32": {N: aws.String("0")}, + "PtrFloat64": {N: aws.String("0")}, + }, + }, + actual: &testEmptyCollectionsOmittedPtrNumericalScalars{}, + expected: testEmptyCollectionsOmittedPtrNumericalScalars{ + PtrUint8: aws.Uint8(0), + PtrUint16: aws.Uint16(0), + PtrUint32: aws.Uint32(0), + PtrUint64: aws.Uint64(0), + PtrInt8: aws.Int8(0), + PtrInt16: aws.Int16(0), + PtrInt32: aws.Int32(0), + PtrInt64: aws.Int64(0), + PtrFloat32: aws.Float32(0), + PtrFloat64: aws.Float64(0), + }, + }, + "omittable non-nil pointer to non-zero scalar": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "PtrUint8": {N: aws.String("1")}, + "PtrUint16": {N: aws.String("2")}, + "PtrUint32": {N: aws.String("3")}, + "PtrUint64": {N: aws.String("4")}, + "PtrInt8": {N: aws.String("-5")}, + "PtrInt16": {N: aws.String("-6")}, + "PtrInt32": {N: aws.String("-7")}, + "PtrInt64": {N: aws.String("-8")}, + "PtrFloat32": {N: aws.String("9.9")}, + "PtrFloat64": {N: aws.String("10.1")}, + }, + }, + actual: &testEmptyCollectionsOmittedPtrNumericalScalars{}, + expected: testEmptyCollectionsOmittedPtrNumericalScalars{ + PtrUint8: aws.Uint8(1), + PtrUint16: aws.Uint16(2), + PtrUint32: aws.Uint32(3), + PtrUint64: aws.Uint64(4), + PtrInt8: aws.Int8(-5), + PtrInt16: aws.Int16(-6), + PtrInt32: aws.Int32(-7), + PtrInt64: aws.Int64(-8), + PtrFloat32: aws.Float32(9.9), + PtrFloat64: aws.Float64(10.1), + }, + }, + "maps slices nil values": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "Map": {NULL: aws.Bool(true)}, + "Slice": {NULL: aws.Bool(true)}, + "ByteSlice": {NULL: aws.Bool(true)}, + "ByteArray": {B: make([]byte, 4)}, + "ZeroArray": {B: make([]byte, 0)}, + "BinarySet": {NULL: aws.Bool(true)}, + "NumberSet": {NULL: aws.Bool(true)}, + "StringSet": {NULL: aws.Bool(true)}, + }, + }, + actual: &testEmptyCollectionTypes{}, + expected: testEmptyCollectionTypes{}, + }, + "maps slices zero values": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "Map": {M: map[string]dynamodb.AttributeValue{}}, + "Slice": {L: []dynamodb.AttributeValue{}}, + "ByteSlice": {B: []byte{}}, + "ByteArray": {B: make([]byte, 4)}, + "ZeroArray": {B: make([]byte, 0)}, + "BinarySet": {BS: [][]byte{}}, + "NumberSet": {NS: []string{}}, + "StringSet": {SS: []string{}}, + }, + }, + actual: &testEmptyCollectionTypes{}, + expected: testEmptyCollectionTypes{ + Map: map[string]string{}, + Slice: []string{}, + ByteSlice: []byte{}, + ByteArray: [4]byte{}, + ZeroArray: [0]byte{}, + BinarySet: [][]byte{}, + NumberSet: []int{}, + StringSet: []string{}, + }, + }, + "maps slices non-zero values": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "Map": { + M: map[string]dynamodb.AttributeValue{ + "key": {S: aws.String("value")}, + }, + }, + "Slice": {L: []dynamodb.AttributeValue{{S: aws.String("test")}, {S: aws.String("slice")}}}, + "ByteSlice": {B: []byte{0, 1}}, + "ByteArray": {B: []byte{0, 1, 2, 3}}, + "ZeroArray": {B: make([]byte, 0)}, + "BinarySet": {BS: [][]byte{{0, 1}, {2, 3}}}, + "NumberSet": {NS: []string{"0", "1"}}, + "StringSet": {SS: []string{"test", "slice"}}, + }, + }, + actual: &testEmptyCollectionTypes{}, + expected: testEmptyCollectionTypes{ + Map: map[string]string{"key": "value"}, + Slice: []string{"test", "slice"}, + ByteSlice: []byte{0, 1}, + ByteArray: [4]byte{0, 1, 2, 3}, + ZeroArray: [0]byte{}, + BinarySet: [][]byte{{0, 1}, {2, 3}}, + NumberSet: []int{0, 1}, + StringSet: []string{"test", "slice"}, + }, + }, + "omittable maps slices nil values": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "ByteArray": {B: make([]byte, 4)}, + }, + }, + actual: &testEmptyCollectionTypesOmitted{}, + expected: testEmptyCollectionTypesOmitted{}, + }, + "omittable maps slices zero values": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "Map": {M: map[string]dynamodb.AttributeValue{}}, + "Slice": {L: []dynamodb.AttributeValue{}}, + "ByteSlice": {B: []byte{}}, + "ByteArray": {B: make([]byte, 4)}, + "BinarySet": {BS: [][]byte{}}, + "NumberSet": {NS: []string{}}, + "StringSet": {SS: []string{}}, + }, + }, + actual: &testEmptyCollectionTypesOmitted{}, + expected: testEmptyCollectionTypesOmitted{ + Map: map[string]string{}, + Slice: []string{}, + ByteSlice: []byte{}, + ByteArray: [4]byte{}, + BinarySet: [][]byte{}, + NumberSet: []int{}, + StringSet: []string{}, + }, + }, + "omittable maps slices non-zero values": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "Map": { + M: map[string]dynamodb.AttributeValue{ + "key": {S: aws.String("value")}, + }, + }, + "Slice": {L: []dynamodb.AttributeValue{{S: aws.String("test")}, {S: aws.String("slice")}}}, + "ByteSlice": {B: []byte{0, 1}}, + "ByteArray": {B: []byte{0, 1, 2, 3}}, + "BinarySet": {BS: [][]byte{{0, 1}, {2, 3}}}, + "NumberSet": {NS: []string{"0", "1"}}, + "StringSet": {SS: []string{"test", "slice"}}, + }, + }, + actual: &testEmptyCollectionTypesOmitted{}, + expected: testEmptyCollectionTypesOmitted{ + Map: map[string]string{"key": "value"}, + Slice: []string{"test", "slice"}, + ByteSlice: []byte{0, 1}, + ByteArray: [4]byte{0, 1, 2, 3}, + ZeroArray: [0]byte{}, + BinarySet: [][]byte{{0, 1}, {2, 3}}, + NumberSet: []int{0, 1}, + StringSet: []string{"test", "slice"}, + }, + }, + "structs with members zero": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "Struct": { + M: map[string]dynamodb.AttributeValue{ + "Int": {N: aws.String("0")}, + }, + }, + "PtrStruct": {NULL: aws.Bool(true)}, + }, + }, + actual: &struct { + Struct testEmptyCollectionStruct + PtrStruct *testEmptyCollectionStruct + }{}, + expected: struct { + Struct testEmptyCollectionStruct + PtrStruct *testEmptyCollectionStruct + }{}, + }, + "structs with members non-zero value": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "Struct": { + M: map[string]dynamodb.AttributeValue{ + "Int": {N: aws.String("1")}, + }, + }, + "PtrStruct": { + M: map[string]dynamodb.AttributeValue{ + "Int": {N: aws.String("1")}, + }, + }, + }, + }, + actual: &struct { + Struct testEmptyCollectionStruct + PtrStruct *testEmptyCollectionStruct + }{}, + expected: struct { + Struct testEmptyCollectionStruct + PtrStruct *testEmptyCollectionStruct + }{ + Struct: testEmptyCollectionStruct{Int: 1}, + PtrStruct: &testEmptyCollectionStruct{Int: 1}, + }, + }, + "struct with omittable members zero value": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "Struct": {M: map[string]dynamodb.AttributeValue{}}, + "PtrStruct": {NULL: aws.Bool(true)}, + }, + }, + actual: &struct { + Struct testEmptyCollectionStructOmitted + PtrStruct *testEmptyCollectionStructOmitted + }{}, + expected: struct { + Struct testEmptyCollectionStructOmitted + PtrStruct *testEmptyCollectionStructOmitted + }{}, + }, + "omittable struct with omittable members zero value": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "Struct": {M: map[string]dynamodb.AttributeValue{}}, + }, + }, + actual: &struct { + Struct testEmptyCollectionStructOmitted `dynamodbav:",omitempty"` + PtrStruct *testEmptyCollectionStructOmitted `dynamodbav:",omitempty"` + }{}, + expected: struct { + Struct testEmptyCollectionStructOmitted `dynamodbav:",omitempty"` + PtrStruct *testEmptyCollectionStructOmitted `dynamodbav:",omitempty"` + }{}, + }, + "omittable struct with omittable members non-zero value": { + in: &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "Struct": { + M: map[string]dynamodb.AttributeValue{ + "Slice": {L: []dynamodb.AttributeValue{{S: aws.String("test")}}}, + }, + }, + "InitPtrStruct": { + M: map[string]dynamodb.AttributeValue{ + "Slice": {L: []dynamodb.AttributeValue{{S: aws.String("test")}}}, + }, + }, + }, + }, + actual: &struct { + Struct testEmptyCollectionStructOmitted `dynamodbav:",omitempty"` + InitPtrStruct *testEmptyCollectionStructOmitted `dynamodbav:",omitempty"` + }{}, + expected: struct { + Struct testEmptyCollectionStructOmitted `dynamodbav:",omitempty"` + InitPtrStruct *testEmptyCollectionStructOmitted `dynamodbav:",omitempty"` + }{ + Struct: testEmptyCollectionStructOmitted{Slice: []string{"test"}}, + InitPtrStruct: &testEmptyCollectionStructOmitted{Slice: []string{"test"}}, + }, + }, +} + +func TestMarshalEmptyCollections(t *testing.T) { + for name, c := range sharedEmptyCollectionsTestCases { + t.Run(name, func(t *testing.T) { + av, err := Marshal(c.expected) + assertConvertTest(t, av, c.in, err, c.err) + }) + } +} + +func TestEmptyCollectionsSpecialCases(t *testing.T) { + // ptr string non nil with empty value + + type SpecialCases struct { + PtrString *string + OmittedPtrString *string `dynamodbav:",omitempty"` + } + + expectedEncode := &dynamodb.AttributeValue{ + M: map[string]dynamodb.AttributeValue{ + "PtrString": {NULL: aws.Bool(true)}, + }, + } + expectedDecode := SpecialCases{} + + actualEncode, err := Marshal(&SpecialCases{ + PtrString: aws.String(""), + OmittedPtrString: aws.String(""), + }) + if err != nil { + t.Fatalf("expected no err got %v", err) + } + if e, a := expectedEncode, actualEncode; !reflect.DeepEqual(e, a) { + t.Errorf("expected %v, got %v", e, a) + } + + var actualDecode SpecialCases + err = Unmarshal(&dynamodb.AttributeValue{}, &actualDecode) + if err != nil { + t.Fatalf("expected no err got %v", err) + } + if e, a := expectedDecode, actualDecode; !reflect.DeepEqual(e, a) { + t.Errorf("expected %v, got %v", e, a) + } +} + +func TestUnmarshalEmptyCollections(t *testing.T) { + for name, c := range sharedEmptyCollectionsTestCases { + t.Run(name, func(t *testing.T) { + err := Unmarshal(c.in, c.actual) + assertConvertTest(t, c.actual, c.expected, err, c.err) + }) + } +} diff --git a/service/dynamodb/dynamodbattribute/encode.go b/service/dynamodb/dynamodbattribute/encode.go index e3f0ec7928d..c1462fe3a6a 100644 --- a/service/dynamodb/dynamodbattribute/encode.go +++ b/service/dynamodb/dynamodbattribute/encode.go @@ -330,9 +330,6 @@ func (e *Encoder) encodeStruct(av *dynamodb.AttributeValue, v reflect.Value, fie av.M[f.Name] = elem } - if len(av.M) == 0 { - encodeNull(av) - } return nil } @@ -357,7 +354,8 @@ func (e *Encoder) encodeMap(av *dynamodb.AttributeValue, v reflect.Value, fieldT av.M[keyName] = elem } - if len(av.M) == 0 { + + if v.IsNil() { encodeNull(av) } @@ -365,13 +363,23 @@ func (e *Encoder) encodeMap(av *dynamodb.AttributeValue, v reflect.Value, fieldT } func (e *Encoder) encodeSlice(av *dynamodb.AttributeValue, v reflect.Value, fieldTag tag) error { + if v.Kind() == reflect.Array && v.Len() == 0 && fieldTag.OmitEmpty { + encodeNull(av) + return nil + } + switch v.Type().Elem().Kind() { case reflect.Uint8: - b := v.Bytes() - if len(b) == 0 { + if v.Kind() == reflect.Slice && v.IsNil() { encodeNull(av) return nil } + + slice := reflect.MakeSlice(byteSliceType, v.Len(), v.Len()) + reflect.Copy(slice, v) + + b := slice.Bytes() + av.B = append([]byte{}, b...) default: var elemFn func(dynamodb.AttributeValue) error @@ -411,9 +419,9 @@ func (e *Encoder) encodeSlice(av *dynamodb.AttributeValue, v reflect.Value, fiel } } - if n, err := e.encodeList(v, fieldTag, elemFn); err != nil { + if _, err := e.encodeList(v, fieldTag, elemFn); err != nil { return err - } else if n == 0 { + } else if v.Kind() == reflect.Slice && v.IsNil() { encodeNull(av) } } @@ -486,8 +494,10 @@ func (e *Encoder) encodeNumber(av *dynamodb.AttributeValue, v reflect.Value) err out = encodeInt(v.Int()) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: out = encodeUint(v.Uint()) - case reflect.Float32, reflect.Float64: - out = encodeFloat(v.Float()) + case reflect.Float32: + out = encodeFloat(v.Float(), 32) + case reflect.Float64: + out = encodeFloat(v.Float(), 64) default: return &unsupportedMarshalTypeError{Type: v.Type()} } @@ -523,8 +533,8 @@ func encodeInt(i int64) string { func encodeUint(u uint64) string { return strconv.FormatUint(u, 10) } -func encodeFloat(f float64) string { - return strconv.FormatFloat(f, 'f', -1, 64) +func encodeFloat(f float64, bitSize int) string { + return strconv.FormatFloat(f, 'f', -1, bitSize) } func encodeNull(av *dynamodb.AttributeValue) { t := true @@ -544,7 +554,11 @@ func valueElem(v reflect.Value) reflect.Value { func emptyValue(v reflect.Value) bool { switch v.Kind() { - case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + case reflect.Array: + return v.Len() == 0 + case reflect.Map, reflect.Slice: + return v.IsNil() + case reflect.String: return v.Len() == 0 case reflect.Bool: return !v.Bool() diff --git a/service/dynamodb/dynamodbattribute/encode_test.go b/service/dynamodb/dynamodbattribute/encode_test.go index ed4ef82ab64..255b67e188f 100644 --- a/service/dynamodb/dynamodbattribute/encode_test.go +++ b/service/dynamodb/dynamodbattribute/encode_test.go @@ -18,22 +18,28 @@ func TestMarshalErrorTypes(t *testing.T) { func TestMarshalShared(t *testing.T) { for i, c := range sharedTestCases { - av, err := Marshal(c.expected) - assertConvertTest(t, i, av, c.in, err, c.err) + t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { + av, err := Marshal(c.expected) + assertConvertTest(t, av, c.in, err, c.err) + }) } } func TestMarshalListShared(t *testing.T) { for i, c := range sharedListTestCases { - av, err := MarshalList(c.expected) - assertConvertTest(t, i, av, c.in, err, c.err) + t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { + av, err := MarshalList(c.expected) + assertConvertTest(t, av, c.in, err, c.err) + }) } } func TestMarshalMapShared(t *testing.T) { for i, c := range sharedMapTestCases { - av, err := MarshalMap(c.expected) - assertConvertTest(t, i, av, c.in, err, c.err) + t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { + av, err := MarshalMap(c.expected) + assertConvertTest(t, av, c.in, err, c.err) + }) } } diff --git a/service/dynamodb/dynamodbattribute/shared_test.go b/service/dynamodb/dynamodbattribute/shared_test.go index 8e283e5686f..bca695793a9 100644 --- a/service/dynamodb/dynamodbattribute/shared_test.go +++ b/service/dynamodb/dynamodbattribute/shared_test.go @@ -371,21 +371,20 @@ var sharedMapTestCases = []struct { }, } -func assertConvertTest(t *testing.T, i int, actual, expected interface{}, err, expectedErr error) { - i++ +func assertConvertTest(t *testing.T, actual, expected interface{}, err, expectedErr error) { if expectedErr != nil { if err != nil { if e, a := expectedErr, err; !reflect.DeepEqual(e, a) { - t.Errorf("case %d expect %v, got %v", i, e, a) + t.Errorf("expect %v, got %v", e, a) } } else { - t.Fatalf("case %d, expected error, %v", i, expectedErr) + t.Fatalf("expected error, %v", expectedErr) } } else if err != nil { - t.Fatalf("case %d, expect no error, got %v", i, err) + t.Fatalf("expect no error, got %v", err) } else { if e, a := ptrToValue(expected), ptrToValue(actual); !reflect.DeepEqual(e, a) { - t.Errorf("case %d, expect %v, got %v", i, e, a) + t.Errorf("expect %v, got %v", e, a) } } }