1// Copyright 2018 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 impl_test 6 7import ( 8 "fmt" 9 "reflect" 10 "sync" 11 "testing" 12 13 "github.com/google/go-cmp/cmp" 14 "github.com/google/go-cmp/cmp/cmpopts" 15 16 "google.golang.org/protobuf/encoding/prototext" 17 pimpl "google.golang.org/protobuf/internal/impl" 18 "google.golang.org/protobuf/internal/pragma" 19 "google.golang.org/protobuf/proto" 20 "google.golang.org/protobuf/reflect/protodesc" 21 "google.golang.org/protobuf/reflect/protoreflect" 22 "google.golang.org/protobuf/reflect/protoregistry" 23 "google.golang.org/protobuf/runtime/protoiface" 24 25 proto2_20180125 "google.golang.org/protobuf/internal/testprotos/legacy/proto2_20180125_92554152" 26 "google.golang.org/protobuf/types/descriptorpb" 27) 28 29type LegacyTestMessage struct { 30 XXX_unrecognized []byte 31 XXX_InternalExtensions map[int32]pimpl.ExtensionField 32} 33 34func (*LegacyTestMessage) Reset() {} 35func (*LegacyTestMessage) String() string { return "" } 36func (*LegacyTestMessage) ProtoMessage() {} 37func (*LegacyTestMessage) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { 38 return []protoiface.ExtensionRangeV1{{Start: 10, End: 20}, {Start: 40, End: 80}, {Start: 10000, End: 20000}} 39} 40func (*LegacyTestMessage) Descriptor() ([]byte, []int) { return legacyFD, []int{0} } 41 42var legacyFD = func() []byte { 43 b, _ := proto.Marshal(protodesc.ToFileDescriptorProto(mustMakeFileDesc(` 44 name: "legacy.proto" 45 syntax: "proto2" 46 message_type: [{ 47 name: "LegacyTestMessage" 48 extension_range: [{start:10 end:20}, {start:40 end:80}, {start:10000 end:20000}] 49 }] 50 `, nil))) 51 return pimpl.Export{}.CompressGZIP(b) 52}() 53 54func init() { 55 mt := pimpl.Export{}.MessageTypeOf((*LegacyTestMessage)(nil)) 56 protoregistry.GlobalFiles.RegisterFile(mt.Descriptor().ParentFile()) 57 protoregistry.GlobalTypes.RegisterMessage(mt) 58} 59 60func mustMakeExtensionType(fileDesc, extDesc string, t reflect.Type, r protodesc.Resolver) protoreflect.ExtensionType { 61 s := fmt.Sprintf(`name:"test.proto" syntax:"proto2" %s extension:[{%s}]`, fileDesc, extDesc) 62 xd := mustMakeFileDesc(s, r).Extensions().Get(0) 63 xi := &pimpl.ExtensionInfo{} 64 pimpl.InitExtensionInfo(xi, xd, t) 65 return xi 66} 67 68func mustMakeFileDesc(s string, r protodesc.Resolver) protoreflect.FileDescriptor { 69 pb := new(descriptorpb.FileDescriptorProto) 70 if err := prototext.Unmarshal([]byte(s), pb); err != nil { 71 panic(err) 72 } 73 fd, err := protodesc.NewFile(pb, r) 74 if err != nil { 75 panic(err) 76 } 77 return fd 78} 79 80var ( 81 testParentDesc = pimpl.Export{}.MessageDescriptorOf((*LegacyTestMessage)(nil)) 82 testEnumV1Desc = pimpl.Export{}.EnumDescriptorOf(proto2_20180125.Message_ChildEnum(0)) 83 testMessageV1Desc = pimpl.Export{}.MessageDescriptorOf((*proto2_20180125.Message_ChildMessage)(nil)) 84 testMessageV2Desc = enumMessagesType.Desc 85 86 depReg = newFileRegistry( 87 testParentDesc.ParentFile(), 88 testEnumV1Desc.ParentFile(), 89 testMessageV1Desc.ParentFile(), 90 enumProto2Desc.ParentFile(), 91 testMessageV2Desc.ParentFile(), 92 ) 93 extensionTypes = []protoreflect.ExtensionType{ 94 mustMakeExtensionType( 95 `package:"fizz.buzz" dependency:"legacy.proto"`, 96 `name:"optional_bool" number:10000 label:LABEL_OPTIONAL type:TYPE_BOOL default_value:"true" extendee:".LegacyTestMessage"`, 97 reflect.TypeOf(false), depReg, 98 ), 99 mustMakeExtensionType( 100 `package:"fizz.buzz" dependency:"legacy.proto"`, 101 `name:"optional_int32" number:10001 label:LABEL_OPTIONAL type:TYPE_INT32 default_value:"-12345" extendee:".LegacyTestMessage"`, 102 reflect.TypeOf(int32(0)), depReg, 103 ), 104 mustMakeExtensionType( 105 `package:"fizz.buzz" dependency:"legacy.proto"`, 106 `name:"optional_uint32" number:10002 label:LABEL_OPTIONAL type:TYPE_UINT32 default_value:"3200" extendee:".LegacyTestMessage"`, 107 reflect.TypeOf(uint32(0)), depReg, 108 ), 109 mustMakeExtensionType( 110 `package:"fizz.buzz" dependency:"legacy.proto"`, 111 `name:"optional_float" number:10003 label:LABEL_OPTIONAL type:TYPE_FLOAT default_value:"3.14159" extendee:".LegacyTestMessage"`, 112 reflect.TypeOf(float32(0)), depReg, 113 ), 114 mustMakeExtensionType( 115 `package:"fizz.buzz" dependency:"legacy.proto"`, 116 `name:"optional_string" number:10004 label:LABEL_OPTIONAL type:TYPE_STRING default_value:"hello, \"world!\"\n" extendee:".LegacyTestMessage"`, 117 reflect.TypeOf(""), depReg, 118 ), 119 mustMakeExtensionType( 120 `package:"fizz.buzz" dependency:"legacy.proto"`, 121 `name:"optional_bytes" number:10005 label:LABEL_OPTIONAL type:TYPE_BYTES default_value:"dead\\336\\255\\276\\357beef" extendee:".LegacyTestMessage"`, 122 reflect.TypeOf(([]byte)(nil)), depReg, 123 ), 124 mustMakeExtensionType( 125 `package:"fizz.buzz" dependency:["legacy.proto", "proto2_20180125_92554152/test.proto"]`, 126 `name:"optional_enum_v1" number:10006 label:LABEL_OPTIONAL type:TYPE_ENUM type_name:".google.golang.org.proto2_20180125.Message.ChildEnum" default_value:"ALPHA" extendee:".LegacyTestMessage"`, 127 reflect.TypeOf(proto2_20180125.Message_ChildEnum(0)), depReg, 128 ), 129 mustMakeExtensionType( 130 `package:"fizz.buzz" dependency:["legacy.proto", "proto2_20180125_92554152/test.proto"]`, 131 `name:"optional_message_v1" number:10007 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:".google.golang.org.proto2_20180125.Message.ChildMessage" extendee:".LegacyTestMessage"`, 132 reflect.TypeOf((*proto2_20180125.Message_ChildMessage)(nil)), depReg, 133 ), 134 mustMakeExtensionType( 135 `package:"fizz.buzz" dependency:["legacy.proto", "enum2.proto"]`, 136 `name:"optional_enum_v2" number:10008 label:LABEL_OPTIONAL type:TYPE_ENUM type_name:".EnumProto2" default_value:"DEAD" extendee:".LegacyTestMessage"`, 137 reflect.TypeOf(EnumProto2(0)), depReg, 138 ), 139 mustMakeExtensionType( 140 `package:"fizz.buzz" dependency:["legacy.proto", "enum-messages.proto"]`, 141 `name:"optional_message_v2" number:10009 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:".EnumMessages" extendee:".LegacyTestMessage"`, 142 reflect.TypeOf((*EnumMessages)(nil)), depReg, 143 ), 144 mustMakeExtensionType( 145 `package:"fizz.buzz" dependency:"legacy.proto"`, 146 `name:"repeated_bool" number:10010 label:LABEL_REPEATED type:TYPE_BOOL extendee:".LegacyTestMessage"`, 147 reflect.TypeOf([]bool(nil)), depReg, 148 ), 149 mustMakeExtensionType( 150 `package:"fizz.buzz" dependency:"legacy.proto"`, 151 `name:"repeated_int32" number:10011 label:LABEL_REPEATED type:TYPE_INT32 extendee:".LegacyTestMessage"`, 152 reflect.TypeOf([]int32(nil)), depReg, 153 ), 154 mustMakeExtensionType( 155 `package:"fizz.buzz" dependency:"legacy.proto"`, 156 `name:"repeated_uint32" number:10012 label:LABEL_REPEATED type:TYPE_UINT32 extendee:".LegacyTestMessage"`, 157 reflect.TypeOf([]uint32(nil)), depReg, 158 ), 159 mustMakeExtensionType( 160 `package:"fizz.buzz" dependency:"legacy.proto"`, 161 `name:"repeated_float" number:10013 label:LABEL_REPEATED type:TYPE_FLOAT extendee:".LegacyTestMessage"`, 162 reflect.TypeOf([]float32(nil)), depReg, 163 ), 164 mustMakeExtensionType( 165 `package:"fizz.buzz" dependency:"legacy.proto"`, 166 `name:"repeated_string" number:10014 label:LABEL_REPEATED type:TYPE_STRING extendee:".LegacyTestMessage"`, 167 reflect.TypeOf([]string(nil)), depReg, 168 ), 169 mustMakeExtensionType( 170 `package:"fizz.buzz" dependency:"legacy.proto"`, 171 `name:"repeated_bytes" number:10015 label:LABEL_REPEATED type:TYPE_BYTES extendee:".LegacyTestMessage"`, 172 reflect.TypeOf([][]byte(nil)), depReg, 173 ), 174 mustMakeExtensionType( 175 `package:"fizz.buzz" dependency:["legacy.proto", "proto2_20180125_92554152/test.proto"]`, 176 `name:"repeated_enum_v1" number:10016 label:LABEL_REPEATED type:TYPE_ENUM type_name:".google.golang.org.proto2_20180125.Message.ChildEnum" extendee:".LegacyTestMessage"`, 177 reflect.TypeOf([]proto2_20180125.Message_ChildEnum(nil)), depReg, 178 ), 179 mustMakeExtensionType( 180 `package:"fizz.buzz" dependency:["legacy.proto", "proto2_20180125_92554152/test.proto"]`, 181 `name:"repeated_message_v1" number:10017 label:LABEL_REPEATED type:TYPE_MESSAGE type_name:".google.golang.org.proto2_20180125.Message.ChildMessage" extendee:".LegacyTestMessage"`, 182 reflect.TypeOf([]*proto2_20180125.Message_ChildMessage(nil)), depReg, 183 ), 184 mustMakeExtensionType( 185 `package:"fizz.buzz" dependency:["legacy.proto", "enum2.proto"]`, 186 `name:"repeated_enum_v2" number:10018 label:LABEL_REPEATED type:TYPE_ENUM type_name:".EnumProto2" extendee:".LegacyTestMessage"`, 187 reflect.TypeOf([]EnumProto2(nil)), depReg, 188 ), 189 mustMakeExtensionType( 190 `package:"fizz.buzz" dependency:["legacy.proto", "enum-messages.proto"]`, 191 `name:"repeated_message_v2" number:10019 label:LABEL_REPEATED type:TYPE_MESSAGE type_name:".EnumMessages" extendee:".LegacyTestMessage"`, 192 reflect.TypeOf([]*EnumMessages(nil)), depReg, 193 ), 194 } 195 196 extensionDescs = []*pimpl.ExtensionInfo{{ 197 ExtendedType: (*LegacyTestMessage)(nil), 198 ExtensionType: (*bool)(nil), 199 Field: 10000, 200 Name: "fizz.buzz.optional_bool", 201 Tag: "varint,10000,opt,name=optional_bool,def=1", 202 Filename: "test.proto", 203 }, { 204 ExtendedType: (*LegacyTestMessage)(nil), 205 ExtensionType: (*int32)(nil), 206 Field: 10001, 207 Name: "fizz.buzz.optional_int32", 208 Tag: "varint,10001,opt,name=optional_int32,def=-12345", 209 Filename: "test.proto", 210 }, { 211 ExtendedType: (*LegacyTestMessage)(nil), 212 ExtensionType: (*uint32)(nil), 213 Field: 10002, 214 Name: "fizz.buzz.optional_uint32", 215 Tag: "varint,10002,opt,name=optional_uint32,def=3200", 216 Filename: "test.proto", 217 }, { 218 ExtendedType: (*LegacyTestMessage)(nil), 219 ExtensionType: (*float32)(nil), 220 Field: 10003, 221 Name: "fizz.buzz.optional_float", 222 Tag: "fixed32,10003,opt,name=optional_float,def=3.14159", 223 Filename: "test.proto", 224 }, { 225 ExtendedType: (*LegacyTestMessage)(nil), 226 ExtensionType: (*string)(nil), 227 Field: 10004, 228 Name: "fizz.buzz.optional_string", 229 Tag: "bytes,10004,opt,name=optional_string,def=hello, \"world!\"\n", 230 Filename: "test.proto", 231 }, { 232 ExtendedType: (*LegacyTestMessage)(nil), 233 ExtensionType: ([]byte)(nil), 234 Field: 10005, 235 Name: "fizz.buzz.optional_bytes", 236 Tag: "bytes,10005,opt,name=optional_bytes,def=dead\\336\\255\\276\\357beef", 237 Filename: "test.proto", 238 }, { 239 ExtendedType: (*LegacyTestMessage)(nil), 240 ExtensionType: (*proto2_20180125.Message_ChildEnum)(nil), 241 Field: 10006, 242 Name: "fizz.buzz.optional_enum_v1", 243 Tag: "varint,10006,opt,name=optional_enum_v1,enum=google.golang.org.proto2_20180125.Message_ChildEnum,def=0", 244 Filename: "test.proto", 245 }, { 246 ExtendedType: (*LegacyTestMessage)(nil), 247 ExtensionType: (*proto2_20180125.Message_ChildMessage)(nil), 248 Field: 10007, 249 Name: "fizz.buzz.optional_message_v1", 250 Tag: "bytes,10007,opt,name=optional_message_v1", 251 Filename: "test.proto", 252 }, { 253 ExtendedType: (*LegacyTestMessage)(nil), 254 ExtensionType: (*EnumProto2)(nil), 255 Field: 10008, 256 Name: "fizz.buzz.optional_enum_v2", 257 Tag: "varint,10008,opt,name=optional_enum_v2,enum=EnumProto2,def=57005", 258 Filename: "test.proto", 259 }, { 260 ExtendedType: (*LegacyTestMessage)(nil), 261 ExtensionType: (*EnumMessages)(nil), 262 Field: 10009, 263 Name: "fizz.buzz.optional_message_v2", 264 Tag: "bytes,10009,opt,name=optional_message_v2", 265 Filename: "test.proto", 266 }, { 267 ExtendedType: (*LegacyTestMessage)(nil), 268 ExtensionType: ([]bool)(nil), 269 Field: 10010, 270 Name: "fizz.buzz.repeated_bool", 271 Tag: "varint,10010,rep,name=repeated_bool", 272 Filename: "test.proto", 273 }, { 274 ExtendedType: (*LegacyTestMessage)(nil), 275 ExtensionType: ([]int32)(nil), 276 Field: 10011, 277 Name: "fizz.buzz.repeated_int32", 278 Tag: "varint,10011,rep,name=repeated_int32", 279 Filename: "test.proto", 280 }, { 281 ExtendedType: (*LegacyTestMessage)(nil), 282 ExtensionType: ([]uint32)(nil), 283 Field: 10012, 284 Name: "fizz.buzz.repeated_uint32", 285 Tag: "varint,10012,rep,name=repeated_uint32", 286 Filename: "test.proto", 287 }, { 288 ExtendedType: (*LegacyTestMessage)(nil), 289 ExtensionType: ([]float32)(nil), 290 Field: 10013, 291 Name: "fizz.buzz.repeated_float", 292 Tag: "fixed32,10013,rep,name=repeated_float", 293 Filename: "test.proto", 294 }, { 295 ExtendedType: (*LegacyTestMessage)(nil), 296 ExtensionType: ([]string)(nil), 297 Field: 10014, 298 Name: "fizz.buzz.repeated_string", 299 Tag: "bytes,10014,rep,name=repeated_string", 300 Filename: "test.proto", 301 }, { 302 ExtendedType: (*LegacyTestMessage)(nil), 303 ExtensionType: ([][]byte)(nil), 304 Field: 10015, 305 Name: "fizz.buzz.repeated_bytes", 306 Tag: "bytes,10015,rep,name=repeated_bytes", 307 Filename: "test.proto", 308 }, { 309 ExtendedType: (*LegacyTestMessage)(nil), 310 ExtensionType: ([]proto2_20180125.Message_ChildEnum)(nil), 311 Field: 10016, 312 Name: "fizz.buzz.repeated_enum_v1", 313 Tag: "varint,10016,rep,name=repeated_enum_v1,enum=google.golang.org.proto2_20180125.Message_ChildEnum", 314 Filename: "test.proto", 315 }, { 316 ExtendedType: (*LegacyTestMessage)(nil), 317 ExtensionType: ([]*proto2_20180125.Message_ChildMessage)(nil), 318 Field: 10017, 319 Name: "fizz.buzz.repeated_message_v1", 320 Tag: "bytes,10017,rep,name=repeated_message_v1", 321 Filename: "test.proto", 322 }, { 323 ExtendedType: (*LegacyTestMessage)(nil), 324 ExtensionType: ([]EnumProto2)(nil), 325 Field: 10018, 326 Name: "fizz.buzz.repeated_enum_v2", 327 Tag: "varint,10018,rep,name=repeated_enum_v2,enum=EnumProto2", 328 Filename: "test.proto", 329 }, { 330 ExtendedType: (*LegacyTestMessage)(nil), 331 ExtensionType: ([]*EnumMessages)(nil), 332 Field: 10019, 333 Name: "fizz.buzz.repeated_message_v2", 334 Tag: "bytes,10019,rep,name=repeated_message_v2", 335 Filename: "test.proto", 336 }} 337) 338 339func TestLegacyExtensions(t *testing.T) { 340 opts := cmp.Options{cmp.Comparer(func(x, y *proto2_20180125.Message_ChildMessage) bool { 341 return x == y // pointer compare messages for object identity 342 })} 343 344 m := pimpl.Export{}.MessageOf(new(LegacyTestMessage)) 345 346 // Check that getting the zero value returns the default value for scalars, 347 // nil for singular messages, and an empty list for repeated fields. 348 defaultValues := map[int]interface{}{ 349 0: bool(true), 350 1: int32(-12345), 351 2: uint32(3200), 352 3: float32(3.14159), 353 4: string("hello, \"world!\"\n"), 354 5: []byte("dead\xde\xad\xbe\xefbeef"), 355 6: proto2_20180125.Message_ALPHA, 356 7: nil, 357 8: EnumProto2(0xdead), 358 9: nil, 359 } 360 for i, xt := range extensionTypes { 361 var got interface{} 362 xd := xt.TypeDescriptor() 363 if !(xd.IsList() || xd.IsMap() || xd.Message() != nil) { 364 got = xt.InterfaceOf(m.Get(xd)) 365 } 366 want := defaultValues[i] 367 if diff := cmp.Diff(want, got, opts); diff != "" { 368 t.Errorf("Message.Get(%d) mismatch (-want +got):\n%v", xd.Number(), diff) 369 } 370 } 371 372 // All fields should be unpopulated. 373 for _, xt := range extensionTypes { 374 xd := xt.TypeDescriptor() 375 if m.Has(xd) { 376 t.Errorf("Message.Has(%d) = true, want false", xd.Number()) 377 } 378 } 379 380 // Set some values and append to values to the lists. 381 m1a := &proto2_20180125.Message_ChildMessage{F1: proto.String("m1a")} 382 m1b := &proto2_20180125.Message_ChildMessage{F1: proto.String("m2b")} 383 m2a := &EnumMessages{EnumP2: EnumProto2(0x1b).Enum()} 384 m2b := &EnumMessages{EnumP2: EnumProto2(0x2b).Enum()} 385 setValues := map[int]interface{}{ 386 0: bool(false), 387 1: int32(-54321), 388 2: uint32(6400), 389 3: float32(2.71828), 390 4: string("goodbye, \"world!\"\n"), 391 5: []byte("live\xde\xad\xbe\xefchicken"), 392 6: proto2_20180125.Message_CHARLIE, 393 7: m1a, 394 8: EnumProto2(0xbeef), 395 9: m2a, 396 10: []bool{true}, 397 11: []int32{-1000}, 398 12: []uint32{1280}, 399 13: []float32{1.6180}, 400 14: []string{"zero"}, 401 15: [][]byte{[]byte("zero")}, 402 16: []proto2_20180125.Message_ChildEnum{proto2_20180125.Message_BRAVO}, 403 17: []*proto2_20180125.Message_ChildMessage{m1b}, 404 18: []EnumProto2{0xdead}, 405 19: []*EnumMessages{m2b}, 406 } 407 for i, xt := range extensionTypes { 408 m.Set(xt.TypeDescriptor(), xt.ValueOf(setValues[i])) 409 } 410 for i, xt := range extensionTypes[len(extensionTypes)/2:] { 411 v := extensionTypes[i].ValueOf(setValues[i]) 412 m.Get(xt.TypeDescriptor()).List().Append(v) 413 } 414 415 // Get the values and check for equality. 416 getValues := map[int]interface{}{ 417 0: bool(false), 418 1: int32(-54321), 419 2: uint32(6400), 420 3: float32(2.71828), 421 4: string("goodbye, \"world!\"\n"), 422 5: []byte("live\xde\xad\xbe\xefchicken"), 423 6: proto2_20180125.Message_ChildEnum(proto2_20180125.Message_CHARLIE), 424 7: m1a, 425 8: EnumProto2(0xbeef), 426 9: m2a, 427 10: []bool{true, false}, 428 11: []int32{-1000, -54321}, 429 12: []uint32{1280, 6400}, 430 13: []float32{1.6180, 2.71828}, 431 14: []string{"zero", "goodbye, \"world!\"\n"}, 432 15: [][]byte{[]byte("zero"), []byte("live\xde\xad\xbe\xefchicken")}, 433 16: []proto2_20180125.Message_ChildEnum{proto2_20180125.Message_BRAVO, proto2_20180125.Message_CHARLIE}, 434 17: []*proto2_20180125.Message_ChildMessage{m1b, m1a}, 435 18: []EnumProto2{0xdead, 0xbeef}, 436 19: []*EnumMessages{m2b, m2a}, 437 } 438 for i, xt := range extensionTypes { 439 xd := xt.TypeDescriptor() 440 got := xt.InterfaceOf(m.Get(xd)) 441 want := getValues[i] 442 if diff := cmp.Diff(want, got, opts); diff != "" { 443 t.Errorf("Message.Get(%d) mismatch (-want +got):\n%v", xd.Number(), diff) 444 } 445 } 446 447 // Clear all singular fields and truncate all repeated fields. 448 for _, xt := range extensionTypes[:len(extensionTypes)/2] { 449 m.Clear(xt.TypeDescriptor()) 450 } 451 for _, xt := range extensionTypes[len(extensionTypes)/2:] { 452 m.Get(xt.TypeDescriptor()).List().Truncate(0) 453 } 454 455 // Clear all repeated fields. 456 for _, xt := range extensionTypes[len(extensionTypes)/2:] { 457 m.Clear(xt.TypeDescriptor()) 458 } 459} 460 461func TestLegacyExtensionConvert(t *testing.T) { 462 for i := range extensionTypes { 463 i := i 464 t.Run("", func(t *testing.T) { 465 t.Parallel() 466 467 wantType := extensionTypes[i] 468 wantDesc := extensionDescs[i] 469 gotType := (protoreflect.ExtensionType)(wantDesc) 470 gotDesc := wantType.(*pimpl.ExtensionInfo) 471 472 // Concurrently call accessors to trigger possible races. 473 for _, xt := range []protoreflect.ExtensionType{wantType, wantDesc} { 474 xt := xt 475 go func() { xt.New() }() 476 go func() { xt.Zero() }() 477 go func() { xt.TypeDescriptor() }() 478 } 479 480 // TODO: We need a test package to compare descriptors. 481 type list interface { 482 Len() int 483 pragma.DoNotImplement 484 } 485 opts := cmp.Options{ 486 cmp.Comparer(func(x, y reflect.Type) bool { 487 return x == y 488 }), 489 cmp.Transformer("", func(x list) []interface{} { 490 out := make([]interface{}, x.Len()) 491 v := reflect.ValueOf(x) 492 for i := 0; i < x.Len(); i++ { 493 m := v.MethodByName("Get") 494 out[i] = m.Call([]reflect.Value{reflect.ValueOf(i)})[0].Interface() 495 } 496 return out 497 }), 498 cmp.Transformer("", func(x protoreflect.Descriptor) map[string]interface{} { 499 out := make(map[string]interface{}) 500 v := reflect.ValueOf(x) 501 for i := 0; i < v.NumMethod(); i++ { 502 name := v.Type().Method(i).Name 503 if m := v.Method(i); m.Type().NumIn() == 0 && m.Type().NumOut() == 1 { 504 switch name { 505 case "ParentFile", "Parent": 506 // Ignore parents to avoid recursive cycle. 507 case "Options": 508 // Ignore descriptor options since protos are not cmperable. 509 case "ContainingOneof", "ContainingMessage", "Enum", "Message": 510 // Avoid descending into a dependency to avoid a cycle. 511 // Just record the full name if available. 512 // 513 // TODO: Cycle support in cmp would be useful here. 514 v := m.Call(nil)[0] 515 if !v.IsNil() { 516 out[name] = v.Interface().(protoreflect.Descriptor).FullName() 517 } 518 case "Type": 519 // Ignore ExtensionTypeDescriptor.Type method to avoid cycle. 520 default: 521 out[name] = m.Call(nil)[0].Interface() 522 } 523 } 524 } 525 return out 526 }), 527 cmp.Transformer("", func(xt protoreflect.ExtensionType) map[string]interface{} { 528 return map[string]interface{}{ 529 "Descriptor": xt.TypeDescriptor(), 530 } 531 }), 532 cmp.Transformer("", func(v protoreflect.Value) interface{} { 533 return v.Interface() 534 }), 535 } 536 if diff := cmp.Diff(&wantType, &gotType, opts); diff != "" { 537 t.Errorf("ExtensionType mismatch (-want, +got):\n%v", diff) 538 } 539 540 opts = cmp.Options{ 541 cmpopts.IgnoreFields(pimpl.ExtensionInfo{}, "ExtensionType"), 542 cmpopts.IgnoreUnexported(pimpl.ExtensionInfo{}), 543 } 544 if diff := cmp.Diff(wantDesc, gotDesc, opts); diff != "" { 545 t.Errorf("ExtensionDesc mismatch (-want, +got):\n%v", diff) 546 } 547 }) 548 } 549} 550 551type ( 552 MessageA struct { 553 A1 *MessageA `protobuf:"bytes,1,req,name=a1"` 554 A2 *MessageB `protobuf:"bytes,2,req,name=a2"` 555 A3 Enum `protobuf:"varint,3,opt,name=a3,enum=legacy.Enum"` 556 } 557 MessageB struct { 558 B1 *MessageA `protobuf:"bytes,1,req,name=b1"` 559 B2 *MessageB `protobuf:"bytes,2,req,name=b2"` 560 B3 Enum `protobuf:"varint,3,opt,name=b3,enum=legacy.Enum"` 561 } 562 Enum int32 563) 564 565func (*MessageA) Reset() { panic("not implemented") } 566func (*MessageA) String() string { panic("not implemented") } 567func (*MessageA) ProtoMessage() { panic("not implemented") } 568func (*MessageA) Descriptor() ([]byte, []int) { return concurrentFD, []int{0} } 569 570func (*MessageB) Reset() { panic("not implemented") } 571func (*MessageB) String() string { panic("not implemented") } 572func (*MessageB) ProtoMessage() { panic("not implemented") } 573func (*MessageB) Descriptor() ([]byte, []int) { return concurrentFD, []int{1} } 574 575func (Enum) EnumDescriptor() ([]byte, []int) { return concurrentFD, []int{0} } 576 577var concurrentFD = func() []byte { 578 b, _ := proto.Marshal(protodesc.ToFileDescriptorProto(mustMakeFileDesc(` 579 name: "concurrent.proto" 580 syntax: "proto2" 581 package: "legacy" 582 message_type: [{ 583 name: "MessageA" 584 field: [ 585 {name:"a1" number:1 label:LABEL_REQUIRED type:TYPE_MESSAGE type_name:".legacy.MessageA"}, 586 {name:"a2" number:2 label:LABEL_REQUIRED type:TYPE_MESSAGE type_name:".legacy.MessageB"}, 587 {name:"a3" number:3 label:LABEL_OPTIONAL type:TYPE_ENUM type_name:".legacy.Enum"} 588 ] 589 }, { 590 name: "MessageB" 591 field: [ 592 {name:"a1" number:1 label:LABEL_REQUIRED type:TYPE_MESSAGE type_name:".legacy.MessageA"}, 593 {name:"a2" number:2 label:LABEL_REQUIRED type:TYPE_MESSAGE type_name:".legacy.MessageB"}, 594 {name:"a3" number:3 label:LABEL_OPTIONAL type:TYPE_ENUM type_name:".legacy.Enum"} 595 ] 596 }] 597 enum_type: [{ 598 name: "Enum" 599 value: [{name:"FOO" number:500}] 600 }] 601 `, nil))) 602 return pimpl.Export{}.CompressGZIP(b) 603}() 604 605// TestLegacyConcurrentInit tests that concurrent wrapping of multiple legacy types 606// results in the exact same descriptor being created. 607func TestLegacyConcurrentInit(t *testing.T) { 608 const numParallel = 5 609 var messageATypes [numParallel]protoreflect.MessageType 610 var messageBTypes [numParallel]protoreflect.MessageType 611 var enumDescs [numParallel]protoreflect.EnumDescriptor 612 613 // Concurrently load message and enum types. 614 var wg sync.WaitGroup 615 for i := 0; i < numParallel; i++ { 616 i := i 617 wg.Add(3) 618 go func() { 619 defer wg.Done() 620 messageATypes[i] = pimpl.Export{}.MessageTypeOf((*MessageA)(nil)) 621 }() 622 go func() { 623 defer wg.Done() 624 messageBTypes[i] = pimpl.Export{}.MessageTypeOf((*MessageB)(nil)) 625 }() 626 go func() { 627 defer wg.Done() 628 enumDescs[i] = pimpl.Export{}.EnumDescriptorOf(Enum(0)) 629 }() 630 } 631 wg.Wait() 632 633 var ( 634 wantMTA = messageATypes[0] 635 wantMDA = messageATypes[0].Descriptor().Fields().ByNumber(1).Message() 636 wantMTB = messageBTypes[0] 637 wantMDB = messageBTypes[0].Descriptor().Fields().ByNumber(2).Message() 638 wantED = messageATypes[0].Descriptor().Fields().ByNumber(3).Enum() 639 ) 640 641 for _, gotMT := range messageATypes[1:] { 642 if gotMT != wantMTA { 643 t.Error("MessageType(MessageA) mismatch") 644 } 645 if gotMDA := gotMT.Descriptor().Fields().ByNumber(1).Message(); gotMDA != wantMDA { 646 t.Error("MessageDescriptor(MessageA) mismatch") 647 } 648 if gotMDB := gotMT.Descriptor().Fields().ByNumber(2).Message(); gotMDB != wantMDB { 649 t.Error("MessageDescriptor(MessageB) mismatch") 650 } 651 if gotED := gotMT.Descriptor().Fields().ByNumber(3).Enum(); gotED != wantED { 652 t.Error("EnumDescriptor(Enum) mismatch") 653 } 654 } 655 for _, gotMT := range messageBTypes[1:] { 656 if gotMT != wantMTB { 657 t.Error("MessageType(MessageB) mismatch") 658 } 659 if gotMDA := gotMT.Descriptor().Fields().ByNumber(1).Message(); gotMDA != wantMDA { 660 t.Error("MessageDescriptor(MessageA) mismatch") 661 } 662 if gotMDB := gotMT.Descriptor().Fields().ByNumber(2).Message(); gotMDB != wantMDB { 663 t.Error("MessageDescriptor(MessageB) mismatch") 664 } 665 if gotED := gotMT.Descriptor().Fields().ByNumber(3).Enum(); gotED != wantED { 666 t.Error("EnumDescriptor(Enum) mismatch") 667 } 668 } 669 for _, gotED := range enumDescs[1:] { 670 if gotED != wantED { 671 t.Error("EnumType(Enum) mismatch") 672 } 673 } 674} 675 676type LegacyTestMessageName1 struct{} 677 678func (*LegacyTestMessageName1) Reset() { panic("not implemented") } 679func (*LegacyTestMessageName1) String() string { panic("not implemented") } 680func (*LegacyTestMessageName1) ProtoMessage() { panic("not implemented") } 681 682type LegacyTestMessageName2 struct{} 683 684func (*LegacyTestMessageName2) Reset() { panic("not implemented") } 685func (*LegacyTestMessageName2) String() string { panic("not implemented") } 686func (*LegacyTestMessageName2) ProtoMessage() { panic("not implemented") } 687func (*LegacyTestMessageName2) XXX_MessageName() string { 688 return "google.golang.org.LegacyTestMessageName2" 689} 690 691func TestLegacyMessageName(t *testing.T) { 692 tests := []struct { 693 in protoiface.MessageV1 694 suggestName protoreflect.FullName 695 wantName protoreflect.FullName 696 }{ 697 {new(LegacyTestMessageName1), "google.golang.org.LegacyTestMessageName1", "google.golang.org.LegacyTestMessageName1"}, 698 {new(LegacyTestMessageName2), "", "google.golang.org.LegacyTestMessageName2"}, 699 } 700 701 for _, tt := range tests { 702 mt := pimpl.Export{}.LegacyMessageTypeOf(tt.in, tt.suggestName) 703 if got := mt.Descriptor().FullName(); got != tt.wantName { 704 t.Errorf("type: %T, name mismatch: got %v, want %v", tt.in, got, tt.wantName) 705 } 706 } 707} 708