1// Copyright 2019 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 proto_test 6 7import ( 8 "fmt" 9 "reflect" 10 "sync" 11 "testing" 12 13 "github.com/google/go-cmp/cmp" 14 15 "google.golang.org/protobuf/proto" 16 "google.golang.org/protobuf/reflect/protoreflect" 17 pref "google.golang.org/protobuf/reflect/protoreflect" 18 "google.golang.org/protobuf/runtime/protoimpl" 19 "google.golang.org/protobuf/testing/protocmp" 20 21 legacy1pb "google.golang.org/protobuf/internal/testprotos/legacy/proto2_20160225_2fc053c5" 22 testpb "google.golang.org/protobuf/internal/testprotos/test" 23 test3pb "google.golang.org/protobuf/internal/testprotos/test3" 24 descpb "google.golang.org/protobuf/types/descriptorpb" 25) 26 27func TestExtensionFuncs(t *testing.T) { 28 for _, test := range []struct { 29 message proto.Message 30 ext pref.ExtensionType 31 wantDefault interface{} 32 value interface{} 33 }{ 34 { 35 message: &testpb.TestAllExtensions{}, 36 ext: testpb.E_OptionalInt32, 37 wantDefault: int32(0), 38 value: int32(1), 39 }, 40 { 41 message: &testpb.TestAllExtensions{}, 42 ext: testpb.E_RepeatedString, 43 wantDefault: ([]string)(nil), 44 value: []string{"a", "b", "c"}, 45 }, 46 { 47 message: protoimpl.X.MessageOf(&legacy1pb.Message{}).Interface(), 48 ext: legacy1pb.E_Message_ExtensionOptionalBool, 49 wantDefault: false, 50 value: true, 51 }, 52 } { 53 desc := fmt.Sprintf("Extension %v, value %v", test.ext.TypeDescriptor().FullName(), test.value) 54 if proto.HasExtension(test.message, test.ext) { 55 t.Errorf("%v:\nbefore setting extension HasExtension(...) = true, want false", desc) 56 } 57 got := proto.GetExtension(test.message, test.ext) 58 if d := cmp.Diff(test.wantDefault, got); d != "" { 59 t.Errorf("%v:\nbefore setting extension GetExtension(...) returns unexpected value (-want,+got):\n%v", desc, d) 60 } 61 proto.SetExtension(test.message, test.ext, test.value) 62 if !proto.HasExtension(test.message, test.ext) { 63 t.Errorf("%v:\nafter setting extension HasExtension(...) = false, want true", desc) 64 } 65 got = proto.GetExtension(test.message, test.ext) 66 if d := cmp.Diff(test.value, got); d != "" { 67 t.Errorf("%v:\nafter setting extension GetExtension(...) returns unexpected value (-want,+got):\n%v", desc, d) 68 } 69 proto.ClearExtension(test.message, test.ext) 70 if proto.HasExtension(test.message, test.ext) { 71 t.Errorf("%v:\nafter clearing extension HasExtension(...) = true, want false", desc) 72 } 73 } 74} 75 76func TestIsValid(t *testing.T) { 77 tests := []struct { 78 xt protoreflect.ExtensionType 79 vi interface{} 80 want bool 81 }{ 82 {testpb.E_OptionalBool, nil, false}, 83 {testpb.E_OptionalBool, bool(true), true}, 84 {testpb.E_OptionalBool, new(bool), false}, 85 {testpb.E_OptionalInt32, nil, false}, 86 {testpb.E_OptionalInt32, int32(0), true}, 87 {testpb.E_OptionalInt32, new(int32), false}, 88 {testpb.E_OptionalInt64, nil, false}, 89 {testpb.E_OptionalInt64, int64(0), true}, 90 {testpb.E_OptionalInt64, new(int64), false}, 91 {testpb.E_OptionalUint32, nil, false}, 92 {testpb.E_OptionalUint32, uint32(0), true}, 93 {testpb.E_OptionalUint32, new(uint32), false}, 94 {testpb.E_OptionalUint64, nil, false}, 95 {testpb.E_OptionalUint64, uint64(0), true}, 96 {testpb.E_OptionalUint64, new(uint64), false}, 97 {testpb.E_OptionalFloat, nil, false}, 98 {testpb.E_OptionalFloat, float32(0), true}, 99 {testpb.E_OptionalFloat, new(float32), false}, 100 {testpb.E_OptionalDouble, nil, false}, 101 {testpb.E_OptionalDouble, float64(0), true}, 102 {testpb.E_OptionalDouble, new(float32), false}, 103 {testpb.E_OptionalString, nil, false}, 104 {testpb.E_OptionalString, string(""), true}, 105 {testpb.E_OptionalString, new(string), false}, 106 {testpb.E_OptionalNestedEnum, nil, false}, 107 {testpb.E_OptionalNestedEnum, testpb.TestAllTypes_BAZ, true}, 108 {testpb.E_OptionalNestedEnum, testpb.TestAllTypes_BAZ.Enum(), false}, 109 {testpb.E_OptionalNestedMessage, nil, false}, 110 {testpb.E_OptionalNestedMessage, (*testpb.TestAllExtensions_NestedMessage)(nil), true}, 111 {testpb.E_OptionalNestedMessage, new(testpb.TestAllExtensions_NestedMessage), true}, 112 {testpb.E_OptionalNestedMessage, new(testpb.TestAllExtensions), false}, 113 {testpb.E_RepeatedBool, nil, false}, 114 {testpb.E_RepeatedBool, []bool(nil), true}, 115 {testpb.E_RepeatedBool, []bool{}, true}, 116 {testpb.E_RepeatedBool, []bool{false}, true}, 117 {testpb.E_RepeatedBool, []*bool{}, false}, 118 {testpb.E_RepeatedInt32, nil, false}, 119 {testpb.E_RepeatedInt32, []int32(nil), true}, 120 {testpb.E_RepeatedInt32, []int32{}, true}, 121 {testpb.E_RepeatedInt32, []int32{0}, true}, 122 {testpb.E_RepeatedInt32, []*int32{}, false}, 123 {testpb.E_RepeatedInt64, nil, false}, 124 {testpb.E_RepeatedInt64, []int64(nil), true}, 125 {testpb.E_RepeatedInt64, []int64{}, true}, 126 {testpb.E_RepeatedInt64, []int64{0}, true}, 127 {testpb.E_RepeatedInt64, []*int64{}, false}, 128 {testpb.E_RepeatedUint32, nil, false}, 129 {testpb.E_RepeatedUint32, []uint32(nil), true}, 130 {testpb.E_RepeatedUint32, []uint32{}, true}, 131 {testpb.E_RepeatedUint32, []uint32{0}, true}, 132 {testpb.E_RepeatedUint32, []*uint32{}, false}, 133 {testpb.E_RepeatedUint64, nil, false}, 134 {testpb.E_RepeatedUint64, []uint64(nil), true}, 135 {testpb.E_RepeatedUint64, []uint64{}, true}, 136 {testpb.E_RepeatedUint64, []uint64{0}, true}, 137 {testpb.E_RepeatedUint64, []*uint64{}, false}, 138 {testpb.E_RepeatedFloat, nil, false}, 139 {testpb.E_RepeatedFloat, []float32(nil), true}, 140 {testpb.E_RepeatedFloat, []float32{}, true}, 141 {testpb.E_RepeatedFloat, []float32{0}, true}, 142 {testpb.E_RepeatedFloat, []*float32{}, false}, 143 {testpb.E_RepeatedDouble, nil, false}, 144 {testpb.E_RepeatedDouble, []float64(nil), true}, 145 {testpb.E_RepeatedDouble, []float64{}, true}, 146 {testpb.E_RepeatedDouble, []float64{0}, true}, 147 {testpb.E_RepeatedDouble, []*float64{}, false}, 148 {testpb.E_RepeatedString, nil, false}, 149 {testpb.E_RepeatedString, []string(nil), true}, 150 {testpb.E_RepeatedString, []string{}, true}, 151 {testpb.E_RepeatedString, []string{""}, true}, 152 {testpb.E_RepeatedString, []*string{}, false}, 153 {testpb.E_RepeatedNestedEnum, nil, false}, 154 {testpb.E_RepeatedNestedEnum, []testpb.TestAllTypes_NestedEnum(nil), true}, 155 {testpb.E_RepeatedNestedEnum, []testpb.TestAllTypes_NestedEnum{}, true}, 156 {testpb.E_RepeatedNestedEnum, []testpb.TestAllTypes_NestedEnum{0}, true}, 157 {testpb.E_RepeatedNestedEnum, []*testpb.TestAllTypes_NestedEnum{}, false}, 158 {testpb.E_RepeatedNestedMessage, nil, false}, 159 {testpb.E_RepeatedNestedMessage, []*testpb.TestAllExtensions_NestedMessage(nil), true}, 160 {testpb.E_RepeatedNestedMessage, []*testpb.TestAllExtensions_NestedMessage{}, true}, 161 {testpb.E_RepeatedNestedMessage, []*testpb.TestAllExtensions_NestedMessage{{}}, true}, 162 {testpb.E_RepeatedNestedMessage, []*testpb.TestAllExtensions{}, false}, 163 } 164 165 for _, tt := range tests { 166 // Check the results of IsValidInterface. 167 got := tt.xt.IsValidInterface(tt.vi) 168 if got != tt.want { 169 t.Errorf("%v.IsValidInterface() = %v, want %v", tt.xt.TypeDescriptor().FullName(), got, tt.want) 170 } 171 if !got { 172 continue 173 } 174 175 // Set the extension value and verify the results of Has. 176 wantHas := true 177 pv := tt.xt.ValueOf(tt.vi) 178 switch v := pv.Interface().(type) { 179 case protoreflect.List: 180 wantHas = v.Len() > 0 181 case protoreflect.Message: 182 wantHas = v.IsValid() 183 } 184 m := &testpb.TestAllExtensions{} 185 proto.SetExtension(m, tt.xt, tt.vi) 186 gotHas := proto.HasExtension(m, tt.xt) 187 if gotHas != wantHas { 188 t.Errorf("HasExtension(%q) = %v, want %v", tt.xt.TypeDescriptor().FullName(), gotHas, wantHas) 189 } 190 191 // Check consistency of IsValidInterface and IsValidValue. 192 got = tt.xt.IsValidValue(pv) 193 if got != tt.want { 194 t.Errorf("%v.IsValidValue() = %v, want %v", tt.xt.TypeDescriptor().FullName(), got, tt.want) 195 } 196 if !got { 197 continue 198 } 199 200 // Use of reflect.DeepEqual is intentional. 201 // We really do want to ensure that the memory layout is identical. 202 vi := tt.xt.InterfaceOf(pv) 203 if !reflect.DeepEqual(vi, tt.vi) { 204 t.Errorf("InterfaceOf(ValueOf(...)) round-trip mismatch: got %v, want %v", vi, tt.vi) 205 } 206 } 207} 208 209func TestExtensionRanger(t *testing.T) { 210 tests := []struct { 211 msg proto.Message 212 want map[pref.ExtensionType]interface{} 213 }{{ 214 msg: &testpb.TestAllExtensions{}, 215 want: map[pref.ExtensionType]interface{}{ 216 testpb.E_OptionalInt32: int32(5), 217 testpb.E_OptionalString: string("hello"), 218 testpb.E_OptionalNestedMessage: &testpb.TestAllExtensions_NestedMessage{}, 219 testpb.E_OptionalNestedEnum: testpb.TestAllTypes_BAZ, 220 testpb.E_RepeatedFloat: []float32{+32.32, -32.32}, 221 testpb.E_RepeatedNestedMessage: []*testpb.TestAllExtensions_NestedMessage{{}}, 222 testpb.E_RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAZ}, 223 }, 224 }, { 225 msg: &descpb.MessageOptions{}, 226 want: map[pref.ExtensionType]interface{}{ 227 test3pb.E_OptionalInt32: int32(5), 228 test3pb.E_OptionalString: string("hello"), 229 test3pb.E_OptionalForeignMessage: &test3pb.ForeignMessage{}, 230 test3pb.E_OptionalForeignEnum: test3pb.ForeignEnum_FOREIGN_BAR, 231 232 test3pb.E_OptionalOptionalInt32: int32(5), 233 test3pb.E_OptionalOptionalString: string("hello"), 234 test3pb.E_OptionalOptionalForeignMessage: &test3pb.ForeignMessage{}, 235 test3pb.E_OptionalOptionalForeignEnum: test3pb.ForeignEnum_FOREIGN_BAR, 236 }, 237 }} 238 239 for _, tt := range tests { 240 for xt, v := range tt.want { 241 proto.SetExtension(tt.msg, xt, v) 242 } 243 244 got := make(map[pref.ExtensionType]interface{}) 245 proto.RangeExtensions(tt.msg, func(xt pref.ExtensionType, v interface{}) bool { 246 got[xt] = v 247 return true 248 }) 249 250 if diff := cmp.Diff(tt.want, got, protocmp.Transform()); diff != "" { 251 t.Errorf("proto.RangeExtensions mismatch (-want +got):\n%s", diff) 252 } 253 } 254} 255 256func TestExtensionGetRace(t *testing.T) { 257 // Concurrently fetch an extension value while marshaling the message containing it. 258 // Create the message with proto.Unmarshal to give lazy extension decoding (if present) 259 // a chance to occur. 260 want := int32(42) 261 m1 := &testpb.TestAllExtensions{} 262 proto.SetExtension(m1, testpb.E_OptionalNestedMessage, &testpb.TestAllExtensions_NestedMessage{A: proto.Int32(want)}) 263 b, err := proto.Marshal(m1) 264 if err != nil { 265 t.Fatal(err) 266 } 267 m := &testpb.TestAllExtensions{} 268 if err := proto.Unmarshal(b, m); err != nil { 269 t.Fatal(err) 270 } 271 var wg sync.WaitGroup 272 for i := 0; i < 3; i++ { 273 wg.Add(1) 274 go func() { 275 defer wg.Done() 276 if _, err := proto.Marshal(m); err != nil { 277 t.Error(err) 278 } 279 }() 280 wg.Add(1) 281 go func() { 282 defer wg.Done() 283 got := proto.GetExtension(m, testpb.E_OptionalNestedMessage).(*testpb.TestAllExtensions_NestedMessage).GetA() 284 if got != want { 285 t.Errorf("GetExtension(optional_nested_message).a = %v, want %v", got, want) 286 } 287 }() 288 } 289 wg.Wait() 290} 291