frostfs-api-go/util/proto/marshal_test.go
Evgenii Stratonikov 47a48969b0 [#103] proto: Test end-to-end scenario
Test the generated code, do not write yet another marshaling routine in
tests.

Before:
```
ok      git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/proto      0.003s  coverage: 55.6% of statements
```

After:
```
ok      git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/proto      0.003s  coverage: 80.0% of statements
```

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-08-09 11:18:17 +03:00

155 lines
5.6 KiB
Go

package proto_test
import (
"math"
"math/rand"
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/proto/test"
"github.com/stretchr/testify/require"
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 := &test.Primitives{}
require.Zero(t, input.StableSize())
r := input.StableMarshal(nil)
require.Empty(t, r)
})
marshalCases := []struct {
name string
input *test.Primitives
}{
{name: "bytes", input: &test.Primitives{FieldA: []byte{1, 2, 3}}},
{name: "string", input: &test.Primitives{FieldB: "123"}},
{name: "bool", input: &test.Primitives{FieldC: true}},
{name: "int32", input: &test.Primitives{FieldD: nonZero[int32]()}},
{name: "uint32", input: &test.Primitives{FieldE: nonZero[uint32]()}},
{name: "int64", input: &test.Primitives{FieldF: nonZero[int64]()}},
{name: "uint64", input: &test.Primitives{FieldG: nonZero[uint64]()}},
{name: "uint64", input: &test.Primitives{FieldI: nonZero[uint64]()}},
{name: "float64", input: &test.Primitives{FieldJ: math.Float64frombits(12345677890)}},
{name: "fixed32", input: &test.Primitives{FieldK: nonZero[uint32]()}},
{name: "enum, positive", input: &test.Primitives{FieldH: test.Primitives_POSITIVE}},
{name: "enum, negative", input: &test.Primitives{FieldH: test.Primitives_NEGATIVE}},
}
for _, tc := range marshalCases {
t.Run(tc.name, func(t *testing.T) {
r := tc.input.StableMarshal(nil)
require.Equal(t, len(r), tc.input.StableSize())
require.NotEmpty(t, r)
var actual test.Primitives
require.NoError(t, goproto.Unmarshal(r, &actual))
// Compare each field directly, because proto-generated code has private fields.
require.Equal(t, tc.input.FieldA, actual.FieldA)
require.Equal(t, tc.input.FieldB, actual.FieldB)
require.Equal(t, tc.input.FieldC, actual.FieldC)
require.Equal(t, tc.input.FieldD, actual.FieldD)
require.Equal(t, tc.input.FieldE, actual.FieldE)
require.Equal(t, tc.input.FieldF, actual.FieldF)
require.Equal(t, tc.input.FieldG, actual.FieldG)
require.Equal(t, tc.input.FieldI, actual.FieldI)
require.Equal(t, tc.input.FieldJ, actual.FieldJ)
require.Equal(t, tc.input.FieldK, actual.FieldK)
require.Equal(t, tc.input.FieldH, actual.FieldH)
})
}
}
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 *test.RepPrimitives
}{
{name: "default", input: &test.RepPrimitives{}},
{name: "bytes", input: &test.RepPrimitives{FieldA: [][]byte{}}},
{name: "string", input: &test.RepPrimitives{FieldB: []string{}}},
{name: "int32", input: &test.RepPrimitives{FieldC: []int32{}}},
{name: "uint32", input: &test.RepPrimitives{FieldD: []uint32{}}},
{name: "int64", input: &test.RepPrimitives{FieldE: []int64{}}},
{name: "uint64", input: &test.RepPrimitives{FieldF: []uint64{}}},
{name: "uint64", input: &test.RepPrimitives{FieldFu: []uint64{}}},
}
for _, tc := range marshalCases {
require.Zero(t, tc.input.StableSize())
r := tc.input.StableMarshal(nil)
require.Empty(t, r)
}
})
marshalCases := []struct {
name string
input *test.RepPrimitives
}{
{name: "bytes", input: &test.RepPrimitives{FieldA: [][]byte{{1, 2, 3}}}},
{name: "string", input: &test.RepPrimitives{FieldB: []string{"123"}}},
{name: "int32", input: &test.RepPrimitives{FieldC: randIntSlice[int32](1, true)}},
{name: "int32", input: &test.RepPrimitives{FieldC: randIntSlice[int32](2, true)}},
{name: "int32", input: &test.RepPrimitives{FieldC: randIntSlice[int32](2, false)}},
{name: "uint32", input: &test.RepPrimitives{FieldD: randIntSlice[uint32](1, true)}},
{name: "uint32", input: &test.RepPrimitives{FieldD: randIntSlice[uint32](2, true)}},
{name: "uint32", input: &test.RepPrimitives{FieldD: randIntSlice[uint32](2, false)}},
{name: "int64", input: &test.RepPrimitives{FieldE: randIntSlice[int64](1, true)}},
{name: "int64", input: &test.RepPrimitives{FieldE: randIntSlice[int64](2, true)}},
{name: "int64", input: &test.RepPrimitives{FieldE: randIntSlice[int64](2, false)}},
{name: "uint64", input: &test.RepPrimitives{FieldF: randIntSlice[uint64](1, true)}},
{name: "uint64", input: &test.RepPrimitives{FieldF: randIntSlice[uint64](2, true)}},
{name: "uint64", input: &test.RepPrimitives{FieldF: randIntSlice[uint64](2, false)}},
{name: "uint64", input: &test.RepPrimitives{FieldFu: randIntSlice[uint64](1, true)}},
{name: "uint64", input: &test.RepPrimitives{FieldFu: randIntSlice[uint64](2, true)}},
{name: "uint64", input: &test.RepPrimitives{FieldFu: randIntSlice[uint64](2, false)}},
}
for _, tc := range marshalCases {
t.Run(tc.name, func(t *testing.T) {
r := tc.input.StableMarshal(nil)
require.Equal(t, len(r), tc.input.StableSize())
require.NotEmpty(t, r)
var actual test.RepPrimitives
require.NoError(t, goproto.Unmarshal(r, &actual))
// Compare each field directly, because proto-generated code has private fields.
require.Equal(t, tc.input.FieldA, actual.FieldA)
require.Equal(t, tc.input.FieldB, actual.FieldB)
require.Equal(t, tc.input.FieldC, actual.FieldC)
require.Equal(t, tc.input.FieldD, actual.FieldD)
require.Equal(t, tc.input.FieldE, actual.FieldE)
require.Equal(t, tc.input.FieldF, actual.FieldF)
require.Equal(t, tc.input.FieldFu, actual.FieldFu)
})
}
}