1 #region Copyright notice and license 2 // Protocol Buffers - Google's data interchange format 3 // Copyright 2008 Google Inc. All rights reserved. 4 // https://developers.google.com/protocol-buffers/ 5 // 6 // Redistribution and use in source and binary forms, with or without 7 // modification, are permitted provided that the following conditions are 8 // met: 9 // 10 // * Redistributions of source code must retain the above copyright 11 // notice, this list of conditions and the following disclaimer. 12 // * Redistributions in binary form must reproduce the above 13 // copyright notice, this list of conditions and the following disclaimer 14 // in the documentation and/or other materials provided with the 15 // distribution. 16 // * Neither the name of Google Inc. nor the names of its 17 // contributors may be used to endorse or promote products derived from 18 // this software without specific prior written permission. 19 // 20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 #endregion 32 33 using Google.Protobuf.TestProtos; 34 using NUnit.Framework; 35 using ProtobufUnittest; 36 using System; 37 using System.Collections.Generic; 38 using System.IO; 39 using System.Linq; 40 using UnitTest.Issues.TestProtos; 41 42 namespace Google.Protobuf.Reflection 43 { 44 /// <summary> 45 /// Tests for descriptors. (Not in its own namespace or broken up into individual classes as the 46 /// size doesn't warrant it. On the other hand, this makes me feel a bit dirty...) 47 /// </summary> 48 public class DescriptorsTest 49 { 50 [Test] FileDescriptor_GeneratedCode()51 public void FileDescriptor_GeneratedCode() 52 { 53 TestFileDescriptor( 54 UnittestProto3Reflection.Descriptor, 55 UnittestImportProto3Reflection.Descriptor, 56 UnittestImportPublicProto3Reflection.Descriptor); 57 } 58 59 [Test] FileDescriptor_BuildFromByteStrings()60 public void FileDescriptor_BuildFromByteStrings() 61 { 62 // The descriptors have to be supplied in an order such that all the 63 // dependencies come before the descriptors depending on them. 64 var descriptorData = new List<ByteString> 65 { 66 UnittestImportPublicProto3Reflection.Descriptor.SerializedData, 67 UnittestImportProto3Reflection.Descriptor.SerializedData, 68 UnittestProto3Reflection.Descriptor.SerializedData 69 }; 70 var converted = FileDescriptor.BuildFromByteStrings(descriptorData); 71 Assert.AreEqual(3, converted.Count); 72 TestFileDescriptor(converted[2], converted[1], converted[0]); 73 } 74 75 [Test] FileDescriptor_BuildFromByteStrings_WithExtensionRegistry()76 public void FileDescriptor_BuildFromByteStrings_WithExtensionRegistry() 77 { 78 var extension = UnittestCustomOptionsProto3Extensions.MessageOpt1; 79 80 var byteStrings = new[] 81 { 82 DescriptorReflection.Descriptor.Proto.ToByteString(), 83 UnittestCustomOptionsProto3Reflection.Descriptor.Proto.ToByteString() 84 }; 85 var registry = new ExtensionRegistry { extension }; 86 87 var descriptor = FileDescriptor.BuildFromByteStrings(byteStrings, registry).Last(); 88 var message = descriptor.MessageTypes.Single(t => t.Name == nameof(TestMessageWithCustomOptions)); 89 var extensionValue = message.GetOptions().GetExtension(extension); 90 Assert.AreEqual(-56, extensionValue); 91 } 92 TestFileDescriptor(FileDescriptor file, FileDescriptor importedFile, FileDescriptor importedPublicFile)93 private void TestFileDescriptor(FileDescriptor file, FileDescriptor importedFile, FileDescriptor importedPublicFile) 94 { 95 Assert.AreEqual("unittest_proto3.proto", file.Name); 96 Assert.AreEqual("protobuf_unittest3", file.Package); 97 98 Assert.AreEqual("UnittestProto", file.Proto.Options.JavaOuterClassname); 99 Assert.AreEqual("unittest_proto3.proto", file.Proto.Name); 100 101 // unittest_proto3.proto doesn't have any public imports, but unittest_import_proto3.proto does. 102 Assert.AreEqual(0, file.PublicDependencies.Count); 103 Assert.AreEqual(1, importedFile.PublicDependencies.Count); 104 Assert.AreEqual(importedPublicFile, importedFile.PublicDependencies[0]); 105 106 Assert.AreEqual(1, file.Dependencies.Count); 107 Assert.AreEqual(importedFile, file.Dependencies[0]); 108 109 Assert.Null(file.FindTypeByName<MessageDescriptor>("NoSuchType")); 110 Assert.Null(file.FindTypeByName<MessageDescriptor>("protobuf_unittest3.TestAllTypes")); 111 for (int i = 0; i < file.MessageTypes.Count; i++) 112 { 113 Assert.AreEqual(i, file.MessageTypes[i].Index); 114 } 115 116 Assert.AreEqual(file.EnumTypes[0], file.FindTypeByName<EnumDescriptor>("ForeignEnum")); 117 Assert.Null(file.FindTypeByName<EnumDescriptor>("NoSuchType")); 118 Assert.Null(file.FindTypeByName<EnumDescriptor>("protobuf_unittest3.ForeignEnum")); 119 Assert.AreEqual(1, importedFile.EnumTypes.Count); 120 Assert.AreEqual("ImportEnum", importedFile.EnumTypes[0].Name); 121 for (int i = 0; i < file.EnumTypes.Count; i++) 122 { 123 Assert.AreEqual(i, file.EnumTypes[i].Index); 124 } 125 126 Assert.AreEqual(10, file.SerializedData[0]); 127 TestDescriptorToProto(file.ToProto, file.Proto); 128 } 129 130 [Test] FileDescriptor_NonRootPath()131 public void FileDescriptor_NonRootPath() 132 { 133 // unittest_proto3.proto used to be in google/protobuf. Now it's in the C#-specific location, 134 // let's test something that's still in a directory. 135 FileDescriptor file = UnittestWellKnownTypesReflection.Descriptor; 136 Assert.AreEqual("google/protobuf/unittest_well_known_types.proto", file.Name); 137 Assert.AreEqual("protobuf_unittest", file.Package); 138 } 139 140 [Test] FileDescriptor_BuildFromByteStrings_MissingDependency()141 public void FileDescriptor_BuildFromByteStrings_MissingDependency() 142 { 143 var descriptorData = new List<ByteString> 144 { 145 UnittestImportProto3Reflection.Descriptor.SerializedData, 146 UnittestProto3Reflection.Descriptor.SerializedData, 147 }; 148 // This will fail, because we're missing UnittestImportPublicProto3Reflection 149 Assert.Throws<ArgumentException>(() => FileDescriptor.BuildFromByteStrings(descriptorData)); 150 } 151 152 [Test] FileDescriptor_BuildFromByteStrings_DuplicateNames()153 public void FileDescriptor_BuildFromByteStrings_DuplicateNames() 154 { 155 var descriptorData = new List<ByteString> 156 { 157 UnittestImportPublicProto3Reflection.Descriptor.SerializedData, 158 UnittestImportPublicProto3Reflection.Descriptor.SerializedData, 159 }; 160 // This will fail due to the same name being used twice 161 Assert.Throws<ArgumentException>(() => FileDescriptor.BuildFromByteStrings(descriptorData)); 162 } 163 164 [Test] FileDescriptor_BuildFromByteStrings_IncorrectOrder()165 public void FileDescriptor_BuildFromByteStrings_IncorrectOrder() 166 { 167 var descriptorData = new List<ByteString> 168 { 169 UnittestProto3Reflection.Descriptor.SerializedData, 170 UnittestImportPublicProto3Reflection.Descriptor.SerializedData, 171 UnittestImportProto3Reflection.Descriptor.SerializedData 172 }; 173 // This will fail, because the dependencies should come first 174 Assert.Throws<ArgumentException>(() => FileDescriptor.BuildFromByteStrings(descriptorData)); 175 176 } 177 178 [Test] MessageDescriptorFromGeneratedCodeFileDescriptor()179 public void MessageDescriptorFromGeneratedCodeFileDescriptor() 180 { 181 var file = UnittestProto3Reflection.Descriptor; 182 183 MessageDescriptor messageType = TestAllTypes.Descriptor; 184 Assert.AreSame(typeof(TestAllTypes), messageType.ClrType); 185 Assert.AreSame(TestAllTypes.Parser, messageType.Parser); 186 Assert.AreEqual(messageType, file.MessageTypes[0]); 187 Assert.AreEqual(messageType, file.FindTypeByName<MessageDescriptor>("TestAllTypes")); 188 } 189 190 [Test] MessageDescriptor()191 public void MessageDescriptor() 192 { 193 MessageDescriptor messageType = TestAllTypes.Descriptor; 194 MessageDescriptor nestedType = TestAllTypes.Types.NestedMessage.Descriptor; 195 196 Assert.AreEqual("TestAllTypes", messageType.Name); 197 Assert.AreEqual("protobuf_unittest3.TestAllTypes", messageType.FullName); 198 Assert.AreEqual(UnittestProto3Reflection.Descriptor, messageType.File); 199 Assert.IsNull(messageType.ContainingType); 200 Assert.IsNull(messageType.Proto.Options); 201 202 Assert.AreEqual("TestAllTypes", messageType.Name); 203 204 Assert.AreEqual("NestedMessage", nestedType.Name); 205 Assert.AreEqual("protobuf_unittest3.TestAllTypes.NestedMessage", nestedType.FullName); 206 Assert.AreEqual(UnittestProto3Reflection.Descriptor, nestedType.File); 207 Assert.AreEqual(messageType, nestedType.ContainingType); 208 209 FieldDescriptor field = messageType.Fields.InDeclarationOrder()[0]; 210 Assert.AreEqual("single_int32", field.Name); 211 Assert.AreEqual(field, messageType.FindDescriptor<FieldDescriptor>("single_int32")); 212 Assert.Null(messageType.FindDescriptor<FieldDescriptor>("no_such_field")); 213 Assert.AreEqual(field, messageType.FindFieldByNumber(1)); 214 Assert.Null(messageType.FindFieldByNumber(571283)); 215 var fieldsInDeclarationOrder = messageType.Fields.InDeclarationOrder(); 216 for (int i = 0; i < fieldsInDeclarationOrder.Count; i++) 217 { 218 Assert.AreEqual(i, fieldsInDeclarationOrder[i].Index); 219 } 220 221 Assert.AreEqual(nestedType, messageType.NestedTypes[0]); 222 Assert.AreEqual(nestedType, messageType.FindDescriptor<MessageDescriptor>("NestedMessage")); 223 Assert.Null(messageType.FindDescriptor<MessageDescriptor>("NoSuchType")); 224 for (int i = 0; i < messageType.NestedTypes.Count; i++) 225 { 226 Assert.AreEqual(i, messageType.NestedTypes[i].Index); 227 } 228 229 Assert.AreEqual(messageType.EnumTypes[0], messageType.FindDescriptor<EnumDescriptor>("NestedEnum")); 230 Assert.Null(messageType.FindDescriptor<EnumDescriptor>("NoSuchType")); 231 for (int i = 0; i < messageType.EnumTypes.Count; i++) 232 { 233 Assert.AreEqual(i, messageType.EnumTypes[i].Index); 234 } 235 TestDescriptorToProto(messageType.ToProto, messageType.Proto); 236 } 237 238 [Test] FieldDescriptor_GeneratedCode()239 public void FieldDescriptor_GeneratedCode() 240 { 241 TestFieldDescriptor(UnittestProto3Reflection.Descriptor, TestAllTypes.Descriptor, ForeignMessage.Descriptor, ImportMessage.Descriptor); 242 } 243 244 [Test] FieldDescriptor_BuildFromByteStrings()245 public void FieldDescriptor_BuildFromByteStrings() 246 { 247 // The descriptors have to be supplied in an order such that all the 248 // dependencies come before the descriptors depending on them. 249 var descriptorData = new List<ByteString> 250 { 251 UnittestImportPublicProto3Reflection.Descriptor.SerializedData, 252 UnittestImportProto3Reflection.Descriptor.SerializedData, 253 UnittestProto3Reflection.Descriptor.SerializedData 254 }; 255 var converted = FileDescriptor.BuildFromByteStrings(descriptorData); 256 TestFieldDescriptor( 257 converted[2], 258 converted[2].FindTypeByName<MessageDescriptor>("TestAllTypes"), 259 converted[2].FindTypeByName<MessageDescriptor>("ForeignMessage"), 260 converted[1].FindTypeByName<MessageDescriptor>("ImportMessage")); 261 } 262 TestFieldDescriptor( FileDescriptor unitTestProto3Descriptor, MessageDescriptor testAllTypesDescriptor, MessageDescriptor foreignMessageDescriptor, MessageDescriptor importMessageDescriptor)263 public void TestFieldDescriptor( 264 FileDescriptor unitTestProto3Descriptor, 265 MessageDescriptor testAllTypesDescriptor, 266 MessageDescriptor foreignMessageDescriptor, 267 MessageDescriptor importMessageDescriptor) 268 { 269 FieldDescriptor primitiveField = testAllTypesDescriptor.FindDescriptor<FieldDescriptor>("single_int32"); 270 FieldDescriptor enumField = testAllTypesDescriptor.FindDescriptor<FieldDescriptor>("single_nested_enum"); 271 FieldDescriptor foreignMessageField = testAllTypesDescriptor.FindDescriptor<FieldDescriptor>("single_foreign_message"); 272 FieldDescriptor importMessageField = testAllTypesDescriptor.FindDescriptor<FieldDescriptor>("single_import_message"); 273 FieldDescriptor fieldInOneof = testAllTypesDescriptor.FindDescriptor<FieldDescriptor>("oneof_string"); 274 275 Assert.AreEqual("single_int32", primitiveField.Name); 276 Assert.AreEqual("protobuf_unittest3.TestAllTypes.single_int32", 277 primitiveField.FullName); 278 Assert.AreEqual(1, primitiveField.FieldNumber); 279 Assert.AreEqual(testAllTypesDescriptor, primitiveField.ContainingType); 280 Assert.AreEqual(unitTestProto3Descriptor, primitiveField.File); 281 Assert.AreEqual(FieldType.Int32, primitiveField.FieldType); 282 Assert.IsNull(primitiveField.Proto.Options); 283 284 Assert.AreEqual("single_nested_enum", enumField.Name); 285 Assert.AreEqual(FieldType.Enum, enumField.FieldType); 286 Assert.AreEqual(testAllTypesDescriptor.EnumTypes[0], enumField.EnumType); 287 288 Assert.AreEqual("single_foreign_message", foreignMessageField.Name); 289 Assert.AreEqual(FieldType.Message, foreignMessageField.FieldType); 290 Assert.AreEqual(foreignMessageDescriptor, foreignMessageField.MessageType); 291 292 Assert.AreEqual("single_import_message", importMessageField.Name); 293 Assert.AreEqual(FieldType.Message, importMessageField.FieldType); 294 Assert.AreEqual(importMessageDescriptor, importMessageField.MessageType); 295 296 // For a field in a regular onoef, ContainingOneof and RealContainingOneof should be the same. 297 Assert.AreEqual("oneof_field", fieldInOneof.ContainingOneof.Name); 298 Assert.AreSame(fieldInOneof.ContainingOneof, fieldInOneof.RealContainingOneof); 299 300 TestDescriptorToProto(primitiveField.ToProto, primitiveField.Proto); 301 TestDescriptorToProto(enumField.ToProto, enumField.Proto); 302 TestDescriptorToProto(foreignMessageField.ToProto, foreignMessageField.Proto); 303 TestDescriptorToProto(fieldInOneof.ToProto, fieldInOneof.Proto); 304 } 305 306 [Test] FieldDescriptorLabel()307 public void FieldDescriptorLabel() 308 { 309 FieldDescriptor singleField = 310 TestAllTypes.Descriptor.FindDescriptor<FieldDescriptor>("single_int32"); 311 FieldDescriptor repeatedField = 312 TestAllTypes.Descriptor.FindDescriptor<FieldDescriptor>("repeated_int32"); 313 314 Assert.IsFalse(singleField.IsRepeated); 315 Assert.IsTrue(repeatedField.IsRepeated); 316 } 317 318 [Test] EnumDescriptor()319 public void EnumDescriptor() 320 { 321 // Note: this test is a bit different to the Java version because there's no static way of getting to the descriptor 322 EnumDescriptor enumType = UnittestProto3Reflection.Descriptor.FindTypeByName<EnumDescriptor>("ForeignEnum"); 323 EnumDescriptor nestedType = TestAllTypes.Descriptor.FindDescriptor<EnumDescriptor>("NestedEnum"); 324 325 Assert.AreEqual("ForeignEnum", enumType.Name); 326 Assert.AreEqual("protobuf_unittest3.ForeignEnum", enumType.FullName); 327 Assert.AreEqual(UnittestProto3Reflection.Descriptor, enumType.File); 328 Assert.Null(enumType.ContainingType); 329 Assert.Null(enumType.Proto.Options); 330 331 Assert.AreEqual("NestedEnum", nestedType.Name); 332 Assert.AreEqual("protobuf_unittest3.TestAllTypes.NestedEnum", 333 nestedType.FullName); 334 Assert.AreEqual(UnittestProto3Reflection.Descriptor, nestedType.File); 335 Assert.AreEqual(TestAllTypes.Descriptor, nestedType.ContainingType); 336 337 EnumValueDescriptor value = enumType.FindValueByName("FOREIGN_FOO"); 338 Assert.AreEqual(value, enumType.Values[1]); 339 Assert.AreEqual("FOREIGN_FOO", value.Name); 340 Assert.AreEqual(4, value.Number); 341 Assert.AreEqual((int) ForeignEnum.ForeignFoo, value.Number); 342 Assert.AreEqual(value, enumType.FindValueByNumber(4)); 343 Assert.Null(enumType.FindValueByName("NO_SUCH_VALUE")); 344 for (int i = 0; i < enumType.Values.Count; i++) 345 { 346 Assert.AreEqual(i, enumType.Values[i].Index); 347 } 348 TestDescriptorToProto(enumType.ToProto, enumType.Proto); 349 TestDescriptorToProto(nestedType.ToProto, nestedType.Proto); 350 } 351 352 [Test] OneofDescriptor()353 public void OneofDescriptor() 354 { 355 OneofDescriptor descriptor = TestAllTypes.Descriptor.FindDescriptor<OneofDescriptor>("oneof_field"); 356 Assert.IsFalse(descriptor.IsSynthetic); 357 Assert.AreEqual("oneof_field", descriptor.Name); 358 Assert.AreEqual("protobuf_unittest3.TestAllTypes.oneof_field", descriptor.FullName); 359 360 var expectedFields = new[] { 361 TestAllTypes.OneofBytesFieldNumber, 362 TestAllTypes.OneofNestedMessageFieldNumber, 363 TestAllTypes.OneofStringFieldNumber, 364 TestAllTypes.OneofUint32FieldNumber } 365 .Select(fieldNumber => TestAllTypes.Descriptor.FindFieldByNumber(fieldNumber)) 366 .ToList(); 367 foreach (var field in expectedFields) 368 { 369 Assert.AreSame(descriptor, field.ContainingOneof); 370 } 371 372 CollectionAssert.AreEquivalent(expectedFields, descriptor.Fields); 373 TestDescriptorToProto(descriptor.ToProto, descriptor.Proto); 374 } 375 376 [Test] MapEntryMessageDescriptor()377 public void MapEntryMessageDescriptor() 378 { 379 var descriptor = MapWellKnownTypes.Descriptor.NestedTypes[0]; 380 Assert.IsNull(descriptor.Parser); 381 Assert.IsNull(descriptor.ClrType); 382 Assert.IsNull(descriptor.Fields[1].Accessor); 383 TestDescriptorToProto(descriptor.ToProto, descriptor.Proto); 384 } 385 386 // From TestFieldOrdering: 387 // string my_string = 11; 388 // int64 my_int = 1; 389 // float my_float = 101; 390 // NestedMessage single_nested_message = 200; 391 [Test] FieldListOrderings()392 public void FieldListOrderings() 393 { 394 var fields = TestFieldOrderings.Descriptor.Fields; 395 Assert.AreEqual(new[] { 11, 1, 101, 200 }, fields.InDeclarationOrder().Select(x => x.FieldNumber)); 396 Assert.AreEqual(new[] { 1, 11, 101, 200 }, fields.InFieldNumberOrder().Select(x => x.FieldNumber)); 397 } 398 399 400 [Test] DescriptorProtoFileDescriptor()401 public void DescriptorProtoFileDescriptor() 402 { 403 var descriptor = Google.Protobuf.Reflection.FileDescriptor.DescriptorProtoFileDescriptor; 404 Assert.AreEqual("google/protobuf/descriptor.proto", descriptor.Name); 405 TestDescriptorToProto(descriptor.ToProto, descriptor.Proto); 406 } 407 408 [Test] DescriptorImportingExtensionsFromOldCodeGen()409 public void DescriptorImportingExtensionsFromOldCodeGen() 410 { 411 // The extension collection includes a null extension. There's not a lot we can do about that 412 // in itself, as the old generator didn't provide us the extension information. 413 var extensions = TestProtos.OldGenerator.OldExtensions2Reflection.Descriptor.Extensions; 414 Assert.AreEqual(1, extensions.UnorderedExtensions.Count); 415 // Note: this assertion is present so that it will fail if OldExtensions2 is regenerated 416 // with a new generator. 417 Assert.Null(extensions.UnorderedExtensions[0].Extension); 418 419 // ... but we can make sure we at least don't cause a failure when retrieving descriptors. 420 // In particular, old_extensions1.proto imports old_extensions2.proto, and this used to cause 421 // an execution-time failure. 422 var importingDescriptor = TestProtos.OldGenerator.OldExtensions1Reflection.Descriptor; 423 Assert.NotNull(importingDescriptor); 424 } 425 426 [Test] Proto3OptionalDescriptors()427 public void Proto3OptionalDescriptors() 428 { 429 var descriptor = TestProto3Optional.Descriptor; 430 var field = descriptor.Fields[TestProto3Optional.OptionalInt32FieldNumber]; 431 Assert.NotNull(field.ContainingOneof); 432 Assert.IsTrue(field.ContainingOneof.IsSynthetic); 433 Assert.Null(field.RealContainingOneof); 434 } 435 436 437 [Test] SyntheticOneofReflection()438 public void SyntheticOneofReflection() 439 { 440 // Expect every oneof in TestProto3Optional to be synthetic 441 var proto3OptionalDescriptor = TestProto3Optional.Descriptor; 442 Assert.AreEqual(0, proto3OptionalDescriptor.RealOneofCount); 443 foreach (var oneof in proto3OptionalDescriptor.Oneofs) 444 { 445 Assert.True(oneof.IsSynthetic); 446 } 447 448 // Expect no oneof in the original proto3 unit test file to be synthetic. 449 foreach (var descriptor in ProtobufTestMessages.Proto3.TestMessagesProto3Reflection.Descriptor.MessageTypes) 450 { 451 Assert.AreEqual(descriptor.Oneofs.Count, descriptor.RealOneofCount); 452 foreach (var oneof in descriptor.Oneofs) 453 { 454 Assert.False(oneof.IsSynthetic); 455 } 456 } 457 458 // Expect no oneof in the original proto2 unit test file to be synthetic. 459 foreach (var descriptor in ProtobufTestMessages.Proto2.TestMessagesProto2Reflection.Descriptor.MessageTypes) 460 { 461 Assert.AreEqual(descriptor.Oneofs.Count, descriptor.RealOneofCount); 462 foreach (var oneof in descriptor.Oneofs) 463 { 464 Assert.False(oneof.IsSynthetic); 465 } 466 } 467 } 468 TestDescriptorToProto(Func<IMessage> toProtoFunction, IMessage expectedProto)469 private static void TestDescriptorToProto(Func<IMessage> toProtoFunction, IMessage expectedProto) 470 { 471 var clone1 = toProtoFunction(); 472 var clone2 = toProtoFunction(); 473 Assert.AreNotSame(clone1, clone2); 474 Assert.AreNotSame(clone1, expectedProto); 475 Assert.AreNotSame(clone2, expectedProto); 476 477 Assert.AreEqual(clone1, clone2); 478 Assert.AreEqual(clone1, expectedProto); 479 } 480 } 481 } 482