• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package order
6
7import (
8	"math/rand"
9	"sort"
10	"testing"
11
12	"github.com/google/go-cmp/cmp"
13	"google.golang.org/protobuf/reflect/protoreflect"
14)
15
16type fieldDesc struct {
17	index      int
18	name       protoreflect.FullName
19	number     protoreflect.FieldNumber
20	extension  bool
21	oneofIndex int // non-zero means within oneof; negative means synthetic
22	protoreflect.FieldDescriptor
23}
24
25func (d fieldDesc) Index() int                       { return d.index }
26func (d fieldDesc) Name() protoreflect.Name          { return d.name.Name() }
27func (d fieldDesc) FullName() protoreflect.FullName  { return d.name }
28func (d fieldDesc) Number() protoreflect.FieldNumber { return d.number }
29func (d fieldDesc) IsExtension() bool                { return d.extension }
30func (d fieldDesc) ContainingOneof() protoreflect.OneofDescriptor {
31	switch {
32	case d.oneofIndex < 0:
33		return oneofDesc{index: -d.oneofIndex, synthetic: true}
34	case d.oneofIndex > 0:
35		return oneofDesc{index: +d.oneofIndex, synthetic: false}
36	default:
37		return nil
38	}
39}
40
41type oneofDesc struct {
42	index     int
43	synthetic bool
44	protoreflect.OneofDescriptor
45}
46
47func (d oneofDesc) Index() int        { return d.index }
48func (d oneofDesc) IsSynthetic() bool { return d.synthetic }
49
50func TestFieldOrder(t *testing.T) {
51	tests := []struct {
52		label  string
53		order  FieldOrder
54		fields []fieldDesc
55	}{{
56		label: "LegacyFieldOrder",
57		order: LegacyFieldOrder,
58		fields: []fieldDesc{
59			// Extension fields sorted first by field number.
60			{number: 2, extension: true},
61			{number: 4, extension: true},
62			{number: 100, extension: true},
63			{number: 120, extension: true},
64
65			// Non-extension fields that are not within a oneof
66			// sorted next by field number.
67			{number: 1},
68			{number: 5, oneofIndex: -9}, // synthetic oneof
69			{number: 10},
70			{number: 11, oneofIndex: -10}, // synthetic oneof
71			{number: 12},
72
73			// Non-synthetic oneofs sorted last by index.
74			{number: 13, oneofIndex: 4},
75			{number: 3, oneofIndex: 5},
76			{number: 9, oneofIndex: 5},
77			{number: 7, oneofIndex: 8},
78		},
79	}, {
80		label: "NumberFieldOrder",
81		order: NumberFieldOrder,
82		fields: []fieldDesc{
83			{number: 1, index: 5, name: "c"},
84			{number: 2, index: 2, name: "b"},
85			{number: 3, index: 3, name: "d"},
86			{number: 5, index: 1, name: "a"},
87			{number: 7, index: 7, name: "e"},
88		},
89	}, {
90		label: "IndexNameFieldOrder",
91		order: IndexNameFieldOrder,
92		fields: []fieldDesc{
93			// Non-extension fields sorted first by index.
94			{index: 0, number: 5, name: "c"},
95			{index: 2, number: 2, name: "a"},
96			{index: 4, number: 4, name: "b"},
97			{index: 7, number: 6, name: "d"},
98
99			// Extension fields sorted last by full name.
100			{index: 3, number: 1, name: "d.a", extension: true},
101			{index: 5, number: 3, name: "e", extension: true},
102			{index: 1, number: 7, name: "g", extension: true},
103		},
104	}}
105
106	for _, tt := range tests {
107		t.Run(tt.label, func(t *testing.T) {
108			want := tt.fields
109			got := append([]fieldDesc(nil), want...)
110			for i, j := range rand.Perm(len(got)) {
111				got[i], got[j] = got[j], got[i]
112			}
113			sort.Slice(got, func(i, j int) bool {
114				return tt.order(got[i], got[j])
115			})
116			if diff := cmp.Diff(want, got,
117				cmp.Comparer(func(x, y fieldDesc) bool { return x == y }),
118			); diff != "" {
119				t.Errorf("order mismatch (-want +got):\n%s", diff)
120			}
121		})
122	}
123}
124
125func TestKeyOrder(t *testing.T) {
126	tests := []struct {
127		label string
128		order KeyOrder
129		keys  []interface{}
130	}{{
131		label: "GenericKeyOrder",
132		order: GenericKeyOrder,
133		keys:  []interface{}{false, true},
134	}, {
135		label: "GenericKeyOrder",
136		order: GenericKeyOrder,
137		keys:  []interface{}{int32(-100), int32(-99), int32(-10), int32(-9), int32(-1), int32(0), int32(+1), int32(+9), int32(+10), int32(+99), int32(+100)},
138	}, {
139		label: "GenericKeyOrder",
140		order: GenericKeyOrder,
141		keys:  []interface{}{int64(-100), int64(-99), int64(-10), int64(-9), int64(-1), int64(0), int64(+1), int64(+9), int64(+10), int64(+99), int64(+100)},
142	}, {
143		label: "GenericKeyOrder",
144		order: GenericKeyOrder,
145		keys:  []interface{}{uint32(0), uint32(1), uint32(9), uint32(10), uint32(99), uint32(100)},
146	}, {
147		label: "GenericKeyOrder",
148		order: GenericKeyOrder,
149		keys:  []interface{}{uint64(0), uint64(1), uint64(9), uint64(10), uint64(99), uint64(100)},
150	}, {
151		label: "GenericKeyOrder",
152		order: GenericKeyOrder,
153		keys:  []interface{}{"", "a", "aa", "ab", "ba", "bb", "\u0080", "\u0080\u0081", "\u0082\u0080"},
154	}}
155
156	for _, tt := range tests {
157		t.Run(tt.label, func(t *testing.T) {
158			var got, want []protoreflect.MapKey
159			for _, v := range tt.keys {
160				want = append(want, protoreflect.ValueOf(v).MapKey())
161			}
162			got = append(got, want...)
163			for i, j := range rand.Perm(len(got)) {
164				got[i], got[j] = got[j], got[i]
165			}
166			sort.Slice(got, func(i, j int) bool {
167				return tt.order(got[i], got[j])
168			})
169			if diff := cmp.Diff(want, got, cmp.Transformer("", protoreflect.MapKey.Interface)); diff != "" {
170				t.Errorf("order mismatch (-want +got):\n%s", diff)
171			}
172		})
173	}
174}
175