frostfs-api-go/util/proto/marshal_test.go

203 lines
7.4 KiB
Go
Raw Normal View History

package proto_test
import (
"math"
"math/rand"
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/proto/test"
generated "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/proto/test/custom"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/encoding/protojson"
goproto "google.golang.org/protobuf/proto"
)
type protoInt interface {
~int32 | ~uint32 | ~int64 | ~uint64
}
func nonZero[T protoInt]() T {
var r T
for r == 0 {
r = T(rand.Uint64())
}
return r
}
func TestStableMarshalSingle(t *testing.T) {
t.Run("empty", func(t *testing.T) {
input := &generated.Primitives{}
require.Zero(t, input.StableSize())
r := input.MarshalProtobuf(nil)
require.Empty(t, r)
})
marshalCases := []struct {
name string
input *generated.Primitives
}{
{name: "bytes", input: &generated.Primitives{FieldA: []byte{1, 2, 3}}},
{name: "string", input: &generated.Primitives{FieldB: "123"}},
{name: "bool", input: &generated.Primitives{FieldC: true}},
{name: "int32", input: &generated.Primitives{FieldD: -10}},
{name: "uint32", input: &generated.Primitives{FieldE: nonZero[uint32]()}},
{name: "int64", input: &generated.Primitives{FieldF: nonZero[int64]()}},
{name: "uint64", input: &generated.Primitives{FieldG: nonZero[uint64]()}},
{name: "uint64", input: &generated.Primitives{FieldI: nonZero[uint64]()}},
{name: "float64", input: &generated.Primitives{FieldJ: math.Float64frombits(12345677890)}},
{name: "fixed32", input: &generated.Primitives{FieldK: nonZero[uint32]()}},
{name: "enum, positive", input: &generated.Primitives{FieldH: generated.Primitives_POSITIVE}},
{name: "enum, negative", input: &generated.Primitives{FieldH: generated.Primitives_NEGATIVE}},
{name: "oneof, first", input: &generated.Primitives{FieldM: &generated.Primitives_FieldMa{FieldMa: []byte{4, 2}}}},
{name: "oneof, second", input: &generated.Primitives{FieldM: &generated.Primitives_FieldMe{FieldMe: nonZero[uint32]()}}},
}
for _, tc := range marshalCases {
t.Run(tc.name, func(t *testing.T) {
t.Run("proto", func(t *testing.T) {
r := tc.input.MarshalProtobuf(nil)
require.Equal(t, len(r), tc.input.StableSize())
require.NotEmpty(t, r)
var actual test.Primitives
require.NoError(t, goproto.Unmarshal(r, &actual))
var actualFrostfs generated.Primitives
require.NoError(t, actualFrostfs.UnmarshalProtobuf(r))
require.Equal(t, tc.input, &actualFrostfs)
primitivesEqual(t, tc.input, &actual)
})
t.Run("json", func(t *testing.T) {
r, err := tc.input.MarshalJSON()
require.NoError(t, err)
require.NotEmpty(t, r)
var actual test.Primitives
require.NoError(t, protojson.Unmarshal(r, &actual))
var actualFrostfs generated.Primitives
require.NoError(t, actualFrostfs.UnmarshalJSON(r))
require.Equal(t, tc.input, &actualFrostfs)
primitivesEqual(t, tc.input, &actual)
})
})
}
}
func primitivesEqual(t *testing.T, a *generated.Primitives, b *test.Primitives) {
// Compare each field directly, because proto-generated code has private fields.
require.Equal(t, a.FieldA, b.FieldA)
require.Equal(t, a.FieldB, b.FieldB)
require.Equal(t, a.FieldC, b.FieldC)
require.Equal(t, a.FieldD, b.FieldD)
require.Equal(t, a.FieldE, b.FieldE)
require.Equal(t, a.FieldF, b.FieldF)
require.Equal(t, a.FieldG, b.FieldG)
require.Equal(t, a.FieldI, b.FieldI)
require.Equal(t, a.FieldJ, b.FieldJ)
require.Equal(t, a.FieldK, b.FieldK)
require.EqualValues(t, a.FieldH, b.FieldH)
require.Equal(t, a.GetFieldMa(), b.GetFieldMa())
require.Equal(t, a.GetFieldMe(), b.GetFieldMe())
require.Equal(t, a.GetFieldAux().GetInnerField(), b.GetFieldAux().GetInnerField())
}
func repPrimitivesEqual(t *testing.T, a *generated.RepPrimitives, b *test.RepPrimitives) {
// Compare each field directly, because proto-generated code has private fields.
require.Equal(t, a.FieldA, b.FieldA)
require.Equal(t, a.FieldB, b.FieldB)
require.Equal(t, a.FieldC, b.FieldC)
require.Equal(t, a.FieldD, b.FieldD)
require.Equal(t, a.FieldE, b.FieldE)
require.Equal(t, a.FieldF, b.FieldF)
require.Equal(t, a.FieldFu, b.FieldFu)
}
func randIntSlice[T protoInt](n int, includeZero bool) []T {
r := make([]T, n)
if n == 0 {
return r
}
for i := range r {
r[i] = T(rand.Uint64())
}
if includeZero {
r[0] = 0
}
return r
}
func TestStableMarshalRep(t *testing.T) {
t.Run("empty", func(t *testing.T) {
marshalCases := []struct {
name string
input *generated.RepPrimitives
}{
{name: "default", input: &generated.RepPrimitives{}},
{name: "bytes", input: &generated.RepPrimitives{FieldA: [][]byte{}}},
{name: "string", input: &generated.RepPrimitives{FieldB: []string{}}},
{name: "int32", input: &generated.RepPrimitives{FieldC: []int32{}}},
{name: "uint32", input: &generated.RepPrimitives{FieldD: []uint32{}}},
{name: "int64", input: &generated.RepPrimitives{FieldE: []int64{}}},
{name: "uint64", input: &generated.RepPrimitives{FieldF: []uint64{}}},
{name: "uint64", input: &generated.RepPrimitives{FieldFu: []uint64{}}},
}
for _, tc := range marshalCases {
t.Run(tc.name, func(t *testing.T) {
require.Zero(t, tc.input.StableSize())
r := tc.input.MarshalProtobuf(nil)
require.Empty(t, r)
})
}
})
marshalCases := []struct {
name string
input *generated.RepPrimitives
}{
{name: "bytes", input: &generated.RepPrimitives{FieldA: [][]byte{{1, 2, 3}}}},
{name: "string", input: &generated.RepPrimitives{FieldB: []string{"123"}}},
{name: "int32", input: &generated.RepPrimitives{FieldC: randIntSlice[int32](1, true)}},
{name: "int32", input: &generated.RepPrimitives{FieldC: randIntSlice[int32](2, true)}},
{name: "int32", input: &generated.RepPrimitives{FieldC: randIntSlice[int32](2, false)}},
{name: "uint32", input: &generated.RepPrimitives{FieldD: randIntSlice[uint32](1, true)}},
{name: "uint32", input: &generated.RepPrimitives{FieldD: randIntSlice[uint32](2, true)}},
{name: "uint32", input: &generated.RepPrimitives{FieldD: randIntSlice[uint32](2, false)}},
{name: "int64", input: &generated.RepPrimitives{FieldE: randIntSlice[int64](1, true)}},
{name: "int64", input: &generated.RepPrimitives{FieldE: randIntSlice[int64](2, true)}},
{name: "int64", input: &generated.RepPrimitives{FieldE: randIntSlice[int64](2, false)}},
{name: "uint64", input: &generated.RepPrimitives{FieldF: randIntSlice[uint64](1, true)}},
{name: "uint64", input: &generated.RepPrimitives{FieldF: randIntSlice[uint64](2, true)}},
{name: "uint64", input: &generated.RepPrimitives{FieldF: randIntSlice[uint64](2, false)}},
{name: "uint64", input: &generated.RepPrimitives{FieldFu: randIntSlice[uint64](1, true)}},
{name: "uint64", input: &generated.RepPrimitives{FieldFu: randIntSlice[uint64](2, true)}},
{name: "uint64", input: &generated.RepPrimitives{FieldFu: randIntSlice[uint64](2, false)}},
}
for _, tc := range marshalCases {
t.Run(tc.name, func(t *testing.T) {
t.Run("proto", func(t *testing.T) {
r := tc.input.MarshalProtobuf(nil)
require.Equal(t, len(r), tc.input.StableSize())
require.NotEmpty(t, r)
var actual test.RepPrimitives
require.NoError(t, goproto.Unmarshal(r, &actual))
repPrimitivesEqual(t, tc.input, &actual)
})
t.Run("json", func(t *testing.T) {
r, err := tc.input.MarshalJSON()
require.NoError(t, err)
require.NotEmpty(t, r)
var actual test.RepPrimitives
require.NoError(t, protojson.Unmarshal(r, &actual))
repPrimitivesEqual(t, tc.input, &actual)
})
})
}
}