frostfs-api-go/util/proto/marshal_test.go
Evgenii Stratonikov dce25705f8
Some checks failed
DCO action / DCO (pull_request) Failing after 1m1s
Tests and linters / Tests (1.22) (pull_request) Successful in 1m20s
Tests and linters / Tests (1.23) (pull_request) Successful in 1m18s
Tests and linters / Tests with -race (pull_request) Successful in 1m28s
Tests and linters / Lint (pull_request) Successful in 2m29s
proto/test: Add oneof test
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-08-26 14:33:30 +03:00

202 lines
7.4 KiB
Go

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