frostfs-api-go/util/proto/marshal_test.go
Evgenii Stratonikov 47a48969b0
All checks were successful
DCO action / DCO (pull_request) Successful in 1m27s
Tests and linters / Tests (1.19) (pull_request) Successful in 1m36s
Tests and linters / Tests (1.20) (pull_request) Successful in 1m43s
Tests and linters / Lint (pull_request) Successful in 1m51s
Tests and linters / Tests with -race (pull_request) Successful in 2m6s
[#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)
})
}
}