// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package order import ( "math/rand" "sort" "testing" "github.com/google/go-cmp/cmp" "google.golang.org/protobuf/reflect/protoreflect" ) type fieldDesc struct { index int name protoreflect.FullName number protoreflect.FieldNumber extension bool oneofIndex int // non-zero means within oneof; negative means synthetic protoreflect.FieldDescriptor } func (d fieldDesc) Index() int { return d.index } func (d fieldDesc) Name() protoreflect.Name { return d.name.Name() } func (d fieldDesc) FullName() protoreflect.FullName { return d.name } func (d fieldDesc) Number() protoreflect.FieldNumber { return d.number } func (d fieldDesc) IsExtension() bool { return d.extension } func (d fieldDesc) ContainingOneof() protoreflect.OneofDescriptor { switch { case d.oneofIndex < 0: return oneofDesc{index: -d.oneofIndex, synthetic: true} case d.oneofIndex > 0: return oneofDesc{index: +d.oneofIndex, synthetic: false} default: return nil } } type oneofDesc struct { index int synthetic bool protoreflect.OneofDescriptor } func (d oneofDesc) Index() int { return d.index } func (d oneofDesc) IsSynthetic() bool { return d.synthetic } func TestFieldOrder(t *testing.T) { tests := []struct { label string order FieldOrder fields []fieldDesc }{{ label: "LegacyFieldOrder", order: LegacyFieldOrder, fields: []fieldDesc{ // Extension fields sorted first by field number. {number: 2, extension: true}, {number: 4, extension: true}, {number: 100, extension: true}, {number: 120, extension: true}, // Non-extension fields that are not within a oneof // sorted next by field number. {number: 1}, {number: 5, oneofIndex: -10}, // synthetic oneof {number: 10}, {number: 11, oneofIndex: -9}, // synthetic oneof {number: 12}, // Non-synthetic oneofs sorted last by index. {number: 13, oneofIndex: 4}, {number: 3, oneofIndex: 5}, {number: 9, oneofIndex: 5}, {number: 7, oneofIndex: 8}, }, }, { label: "NumberFieldOrder", order: NumberFieldOrder, fields: []fieldDesc{ {number: 1, index: 5, name: "c"}, {number: 2, index: 2, name: "b"}, {number: 3, index: 3, name: "d"}, {number: 5, index: 1, name: "a"}, {number: 7, index: 7, name: "e"}, }, }, { label: "IndexNameFieldOrder", order: IndexNameFieldOrder, fields: []fieldDesc{ // Non-extension fields sorted first by index. {index: 0, number: 5, name: "c"}, {index: 2, number: 2, name: "a"}, {index: 4, number: 4, name: "b"}, {index: 7, number: 6, name: "d"}, // Extension fields sorted last by full name. {index: 3, number: 1, name: "d.a", extension: true}, {index: 5, number: 3, name: "e", extension: true}, {index: 1, number: 7, name: "g", extension: true}, }, }} for _, tt := range tests { t.Run(tt.label, func(t *testing.T) { want := tt.fields got := append([]fieldDesc(nil), want...) for i, j := range rand.Perm(len(got)) { got[i], got[j] = got[j], got[i] } sort.Slice(got, func(i, j int) bool { return tt.order(got[i], got[j]) }) if diff := cmp.Diff(want, got, cmp.Comparer(func(x, y fieldDesc) bool { return x == y }), ); diff != "" { t.Errorf("order mismatch (-want +got):\n%s", diff) } }) } } func TestKeyOrder(t *testing.T) { tests := []struct { label string order KeyOrder keys []any }{{ label: "GenericKeyOrder", order: GenericKeyOrder, keys: []any{false, true}, }, { label: "GenericKeyOrder", order: GenericKeyOrder, keys: []any{int32(-100), int32(-99), int32(-10), int32(-9), int32(-1), int32(0), int32(+1), int32(+9), int32(+10), int32(+99), int32(+100)}, }, { label: "GenericKeyOrder", order: GenericKeyOrder, keys: []any{int64(-100), int64(-99), int64(-10), int64(-9), int64(-1), int64(0), int64(+1), int64(+9), int64(+10), int64(+99), int64(+100)}, }, { label: "GenericKeyOrder", order: GenericKeyOrder, keys: []any{uint32(0), uint32(1), uint32(9), uint32(10), uint32(99), uint32(100)}, }, { label: "GenericKeyOrder", order: GenericKeyOrder, keys: []any{uint64(0), uint64(1), uint64(9), uint64(10), uint64(99), uint64(100)}, }, { label: "GenericKeyOrder", order: GenericKeyOrder, keys: []any{"", "a", "aa", "ab", "ba", "bb", "\u0080", "\u0080\u0081", "\u0082\u0080"}, }} for _, tt := range tests { t.Run(tt.label, func(t *testing.T) { var got, want []protoreflect.MapKey for _, v := range tt.keys { want = append(want, protoreflect.ValueOf(v).MapKey()) } got = append(got, want...) for i, j := range rand.Perm(len(got)) { got[i], got[j] = got[j], got[i] } sort.Slice(got, func(i, j int) bool { return tt.order(got[i], got[j]) }) if diff := cmp.Diff(want, got, cmp.Transformer("", protoreflect.MapKey.Interface)); diff != "" { t.Errorf("order mismatch (-want +got):\n%s", diff) } }) } }