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