• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #region Copyright notice and license
2 // Protocol Buffers - Google's data interchange format
3 // Copyright 2008 Google Inc.  All rights reserved.
4 //
5 // Use of this source code is governed by a BSD-style
6 // license that can be found in the LICENSE file or at
7 // https://developers.google.com/open-source/licenses/bsd
8 #endregion
9 
10 using Google.Protobuf.TestProtos;
11 using LegacyFeaturesUnittest;
12 using NUnit.Framework;
13 using ProtobufUnittest;
14 using System;
15 using System.Collections.Generic;
16 using System.Linq;
17 using UnitTest.Issues.TestProtos;
18 using static Google.Protobuf.Reflection.FeatureSet.Types;
19 using proto2 = Google.Protobuf.TestProtos.Proto2;
20 
21 namespace Google.Protobuf.Reflection
22 {
23     /// <summary>
24     /// Tests for descriptors. (Not in its own namespace or broken up into individual classes as the
25     /// size doesn't warrant it. On the other hand, this makes me feel a bit dirty...)
26     /// </summary>
27     public class DescriptorsTest
28     {
29         [Test]
FileDescriptor_GeneratedCode()30         public void FileDescriptor_GeneratedCode()
31         {
32             TestFileDescriptor(
33                 UnittestProto3Reflection.Descriptor,
34                 UnittestImportProto3Reflection.Descriptor,
35                 UnittestImportPublicProto3Reflection.Descriptor);
36         }
37 
38         [Test]
FileDescriptor_BuildFromByteStrings()39         public void FileDescriptor_BuildFromByteStrings()
40         {
41             // The descriptors have to be supplied in an order such that all the
42             // dependencies come before the descriptors depending on them.
43             var descriptorData = new List<ByteString>
44             {
45                 UnittestImportPublicProto3Reflection.Descriptor.SerializedData,
46                 UnittestImportProto3Reflection.Descriptor.SerializedData,
47                 UnittestProto3Reflection.Descriptor.SerializedData
48             };
49             var converted = FileDescriptor.BuildFromByteStrings(descriptorData);
50             Assert.AreEqual(3, converted.Count);
51             TestFileDescriptor(converted[2], converted[1], converted[0]);
52         }
53 
54         [Test]
FileDescriptor_BuildFromByteStrings_WithExtensionRegistry()55         public void FileDescriptor_BuildFromByteStrings_WithExtensionRegistry()
56         {
57             var extension = UnittestCustomOptionsProto3Extensions.MessageOpt1;
58 
59             var byteStrings = new[]
60             {
61                 DescriptorReflection.Descriptor.Proto.ToByteString(),
62                 UnittestCustomOptionsProto3Reflection.Descriptor.Proto.ToByteString()
63             };
64             var registry = new ExtensionRegistry { extension };
65 
66             var descriptor = FileDescriptor.BuildFromByteStrings(byteStrings, registry).Last();
67             var message = descriptor.MessageTypes.Single(t => t.Name == nameof(TestMessageWithCustomOptions));
68             var extensionValue = message.GetOptions().GetExtension(extension);
69             Assert.AreEqual(-56, extensionValue);
70         }
71 
TestFileDescriptor(FileDescriptor file, FileDescriptor importedFile, FileDescriptor importedPublicFile)72         private void TestFileDescriptor(FileDescriptor file, FileDescriptor importedFile, FileDescriptor importedPublicFile)
73         {
74             Assert.AreEqual("csharp/protos/unittest_proto3.proto", file.Name);
75             Assert.AreEqual("protobuf_unittest3", file.Package);
76 
77             Assert.AreEqual("UnittestProto", file.Proto.Options.JavaOuterClassname);
78             Assert.AreEqual("csharp/protos/unittest_proto3.proto", file.Proto.Name);
79 
80             // unittest_proto3.proto doesn't have any public imports, but unittest_import_proto3.proto does.
81             Assert.AreEqual(0, file.PublicDependencies.Count);
82             Assert.AreEqual(1, importedFile.PublicDependencies.Count);
83             Assert.AreEqual(importedPublicFile, importedFile.PublicDependencies[0]);
84 
85             Assert.AreEqual(1, file.Dependencies.Count);
86             Assert.AreEqual(importedFile, file.Dependencies[0]);
87 
88             Assert.Null(file.FindTypeByName<MessageDescriptor>("NoSuchType"));
89             Assert.Null(file.FindTypeByName<MessageDescriptor>("protobuf_unittest3.TestAllTypes"));
90             for (int i = 0; i < file.MessageTypes.Count; i++)
91             {
92                 Assert.AreEqual(i, file.MessageTypes[i].Index);
93             }
94 
95             Assert.AreEqual(file.EnumTypes[0], file.FindTypeByName<EnumDescriptor>("ForeignEnum"));
96             Assert.Null(file.FindTypeByName<EnumDescriptor>("NoSuchType"));
97             Assert.Null(file.FindTypeByName<EnumDescriptor>("protobuf_unittest3.ForeignEnum"));
98             Assert.AreEqual(1, importedFile.EnumTypes.Count);
99             Assert.AreEqual("ImportEnum", importedFile.EnumTypes[0].Name);
100             for (int i = 0; i < file.EnumTypes.Count; i++)
101             {
102                 Assert.AreEqual(i, file.EnumTypes[i].Index);
103             }
104 
105             Assert.AreEqual(10, file.SerializedData[0]);
106             TestDescriptorToProto(file.ToProto, file.Proto);
107         }
108 
109         [Test]
FileDescriptor_BuildFromByteStrings_MissingDependency()110         public void FileDescriptor_BuildFromByteStrings_MissingDependency()
111         {
112             var descriptorData = new List<ByteString>
113             {
114                 UnittestImportProto3Reflection.Descriptor.SerializedData,
115                 UnittestProto3Reflection.Descriptor.SerializedData,
116             };
117             // This will fail, because we're missing UnittestImportPublicProto3Reflection
118             Assert.Throws<ArgumentException>(() => FileDescriptor.BuildFromByteStrings(descriptorData));
119         }
120 
121         [Test]
FileDescriptor_BuildFromByteStrings_DuplicateNames()122         public void FileDescriptor_BuildFromByteStrings_DuplicateNames()
123         {
124             var descriptorData = new List<ByteString>
125             {
126                 UnittestImportPublicProto3Reflection.Descriptor.SerializedData,
127                 UnittestImportPublicProto3Reflection.Descriptor.SerializedData,
128             };
129             // This will fail due to the same name being used twice
130             Assert.Throws<ArgumentException>(() => FileDescriptor.BuildFromByteStrings(descriptorData));
131         }
132 
133         [Test]
FileDescriptor_BuildFromByteStrings_IncorrectOrder()134         public void FileDescriptor_BuildFromByteStrings_IncorrectOrder()
135         {
136             var descriptorData = new List<ByteString>
137             {
138                 UnittestProto3Reflection.Descriptor.SerializedData,
139                 UnittestImportPublicProto3Reflection.Descriptor.SerializedData,
140                 UnittestImportProto3Reflection.Descriptor.SerializedData
141             };
142             // This will fail, because the dependencies should come first
143             Assert.Throws<ArgumentException>(() => FileDescriptor.BuildFromByteStrings(descriptorData));
144 
145         }
146 
147         [Test]
MessageDescriptorFromGeneratedCodeFileDescriptor()148         public void MessageDescriptorFromGeneratedCodeFileDescriptor()
149         {
150             var file = UnittestProto3Reflection.Descriptor;
151 
152             MessageDescriptor messageType = TestAllTypes.Descriptor;
153             Assert.AreSame(typeof(TestAllTypes), messageType.ClrType);
154             Assert.AreSame(TestAllTypes.Parser, messageType.Parser);
155             Assert.AreEqual(messageType, file.MessageTypes[0]);
156             Assert.AreEqual(messageType, file.FindTypeByName<MessageDescriptor>("TestAllTypes"));
157         }
158 
159         [Test]
MessageDescriptor()160         public void MessageDescriptor()
161         {
162             MessageDescriptor messageType = TestAllTypes.Descriptor;
163             MessageDescriptor nestedType = TestAllTypes.Types.NestedMessage.Descriptor;
164 
165             Assert.AreEqual("TestAllTypes", messageType.Name);
166             Assert.AreEqual("protobuf_unittest3.TestAllTypes", messageType.FullName);
167             Assert.AreEqual(UnittestProto3Reflection.Descriptor, messageType.File);
168             Assert.IsNull(messageType.ContainingType);
169             Assert.IsNull(messageType.Proto.Options);
170 
171             Assert.AreEqual("TestAllTypes", messageType.Name);
172 
173             Assert.AreEqual("NestedMessage", nestedType.Name);
174             Assert.AreEqual("protobuf_unittest3.TestAllTypes.NestedMessage", nestedType.FullName);
175             Assert.AreEqual(UnittestProto3Reflection.Descriptor, nestedType.File);
176             Assert.AreEqual(messageType, nestedType.ContainingType);
177 
178             FieldDescriptor field = messageType.Fields.InDeclarationOrder()[0];
179             Assert.AreEqual("single_int32", field.Name);
180             Assert.AreEqual(field, messageType.FindDescriptor<FieldDescriptor>("single_int32"));
181             Assert.Null(messageType.FindDescriptor<FieldDescriptor>("no_such_field"));
182             Assert.AreEqual(field, messageType.FindFieldByNumber(1));
183             Assert.Null(messageType.FindFieldByNumber(571283));
184             var fieldsInDeclarationOrder = messageType.Fields.InDeclarationOrder();
185             for (int i = 0; i < fieldsInDeclarationOrder.Count; i++)
186             {
187                 Assert.AreEqual(i, fieldsInDeclarationOrder[i].Index);
188             }
189 
190             Assert.AreEqual(nestedType, messageType.NestedTypes[0]);
191             Assert.AreEqual(nestedType, messageType.FindDescriptor<MessageDescriptor>("NestedMessage"));
192             Assert.Null(messageType.FindDescriptor<MessageDescriptor>("NoSuchType"));
193             for (int i = 0; i < messageType.NestedTypes.Count; i++)
194             {
195                 Assert.AreEqual(i, messageType.NestedTypes[i].Index);
196             }
197 
198             Assert.AreEqual(messageType.EnumTypes[0], messageType.FindDescriptor<EnumDescriptor>("NestedEnum"));
199             Assert.Null(messageType.FindDescriptor<EnumDescriptor>("NoSuchType"));
200             for (int i = 0; i < messageType.EnumTypes.Count; i++)
201             {
202                 Assert.AreEqual(i, messageType.EnumTypes[i].Index);
203             }
204             TestDescriptorToProto(messageType.ToProto, messageType.Proto);
205         }
206 
207         [Test]
MessageDescriptor_IsMapEntry()208         public void MessageDescriptor_IsMapEntry()
209         {
210             var testMapMessage = TestMap.Descriptor;
211             Assert.False(testMapMessage.IsMapEntry);
212             Assert.True(testMapMessage.Fields[1].MessageType.IsMapEntry);
213         }
214 
215         [Test]
FieldDescriptor_GeneratedCode()216         public void FieldDescriptor_GeneratedCode()
217         {
218             TestFieldDescriptor(UnittestProto3Reflection.Descriptor, TestAllTypes.Descriptor, ForeignMessage.Descriptor, ImportMessage.Descriptor);
219         }
220 
221         [Test]
FieldDescriptor_BuildFromByteStrings()222         public void FieldDescriptor_BuildFromByteStrings()
223         {
224             // The descriptors have to be supplied in an order such that all the
225             // dependencies come before the descriptors depending on them.
226             var descriptorData = new List<ByteString>
227             {
228                 UnittestImportPublicProto3Reflection.Descriptor.SerializedData,
229                 UnittestImportProto3Reflection.Descriptor.SerializedData,
230                 UnittestProto3Reflection.Descriptor.SerializedData
231             };
232             var converted = FileDescriptor.BuildFromByteStrings(descriptorData);
233             TestFieldDescriptor(
234                 converted[2],
235                 converted[2].FindTypeByName<MessageDescriptor>("TestAllTypes"),
236                 converted[2].FindTypeByName<MessageDescriptor>("ForeignMessage"),
237                 converted[1].FindTypeByName<MessageDescriptor>("ImportMessage"));
238         }
239 
TestFieldDescriptor( FileDescriptor unitTestProto3Descriptor, MessageDescriptor testAllTypesDescriptor, MessageDescriptor foreignMessageDescriptor, MessageDescriptor importMessageDescriptor)240         public void TestFieldDescriptor(
241             FileDescriptor unitTestProto3Descriptor,
242             MessageDescriptor testAllTypesDescriptor,
243             MessageDescriptor foreignMessageDescriptor,
244             MessageDescriptor importMessageDescriptor)
245         {
246             FieldDescriptor primitiveField = testAllTypesDescriptor.FindDescriptor<FieldDescriptor>("single_int32");
247             FieldDescriptor enumField = testAllTypesDescriptor.FindDescriptor<FieldDescriptor>("single_nested_enum");
248             FieldDescriptor foreignMessageField = testAllTypesDescriptor.FindDescriptor<FieldDescriptor>("single_foreign_message");
249             FieldDescriptor importMessageField = testAllTypesDescriptor.FindDescriptor<FieldDescriptor>("single_import_message");
250             FieldDescriptor fieldInOneof = testAllTypesDescriptor.FindDescriptor<FieldDescriptor>("oneof_string");
251 
252             Assert.AreEqual("single_int32", primitiveField.Name);
253             Assert.AreEqual("protobuf_unittest3.TestAllTypes.single_int32",
254                             primitiveField.FullName);
255             Assert.AreEqual(1, primitiveField.FieldNumber);
256             Assert.AreEqual(testAllTypesDescriptor, primitiveField.ContainingType);
257             Assert.AreEqual(unitTestProto3Descriptor, primitiveField.File);
258             Assert.AreEqual(FieldType.Int32, primitiveField.FieldType);
259             Assert.IsNull(primitiveField.Proto.Options);
260 
261             Assert.AreEqual("single_nested_enum", enumField.Name);
262             Assert.AreEqual(FieldType.Enum, enumField.FieldType);
263             Assert.AreEqual(testAllTypesDescriptor.EnumTypes[0], enumField.EnumType);
264 
265             Assert.AreEqual("single_foreign_message", foreignMessageField.Name);
266             Assert.AreEqual(FieldType.Message, foreignMessageField.FieldType);
267             Assert.AreEqual(foreignMessageDescriptor, foreignMessageField.MessageType);
268 
269             Assert.AreEqual("single_import_message", importMessageField.Name);
270             Assert.AreEqual(FieldType.Message, importMessageField.FieldType);
271             Assert.AreEqual(importMessageDescriptor, importMessageField.MessageType);
272 
273             // For a field in a regular onoef, ContainingOneof and RealContainingOneof should be the same.
274             Assert.AreEqual("oneof_field", fieldInOneof.ContainingOneof.Name);
275             Assert.AreSame(fieldInOneof.ContainingOneof, fieldInOneof.RealContainingOneof);
276 
277             TestDescriptorToProto(primitiveField.ToProto, primitiveField.Proto);
278             TestDescriptorToProto(enumField.ToProto, enumField.Proto);
279             TestDescriptorToProto(foreignMessageField.ToProto, foreignMessageField.Proto);
280             TestDescriptorToProto(fieldInOneof.ToProto, fieldInOneof.Proto);
281         }
282 
283         [Test]
FieldDescriptorLabel()284         public void FieldDescriptorLabel()
285         {
286             FieldDescriptor singleField =
287                 TestAllTypes.Descriptor.FindDescriptor<FieldDescriptor>("single_int32");
288             FieldDescriptor repeatedField =
289                 TestAllTypes.Descriptor.FindDescriptor<FieldDescriptor>("repeated_int32");
290 
291             Assert.IsFalse(singleField.IsRepeated);
292             Assert.IsTrue(repeatedField.IsRepeated);
293         }
294 
295         [Test]
EnumDescriptor()296         public void EnumDescriptor()
297         {
298             // Note: this test is a bit different to the Java version because there's no static way of getting to the descriptor
299             EnumDescriptor enumType = UnittestProto3Reflection.Descriptor.FindTypeByName<EnumDescriptor>("ForeignEnum");
300             EnumDescriptor nestedType = TestAllTypes.Descriptor.FindDescriptor<EnumDescriptor>("NestedEnum");
301 
302             Assert.AreEqual("ForeignEnum", enumType.Name);
303             Assert.AreEqual("protobuf_unittest3.ForeignEnum", enumType.FullName);
304             Assert.AreEqual(UnittestProto3Reflection.Descriptor, enumType.File);
305             Assert.Null(enumType.ContainingType);
306             Assert.Null(enumType.Proto.Options);
307 
308             Assert.AreEqual("NestedEnum", nestedType.Name);
309             Assert.AreEqual("protobuf_unittest3.TestAllTypes.NestedEnum",
310                             nestedType.FullName);
311             Assert.AreEqual(UnittestProto3Reflection.Descriptor, nestedType.File);
312             Assert.AreEqual(TestAllTypes.Descriptor, nestedType.ContainingType);
313 
314             EnumValueDescriptor value = enumType.FindValueByName("FOREIGN_FOO");
315             Assert.AreEqual(value, enumType.Values[1]);
316             Assert.AreEqual("FOREIGN_FOO", value.Name);
317             Assert.AreEqual(4, value.Number);
318             Assert.AreEqual((int) ForeignEnum.ForeignFoo, value.Number);
319             Assert.AreEqual(value, enumType.FindValueByNumber(4));
320             Assert.Null(enumType.FindValueByName("NO_SUCH_VALUE"));
321             for (int i = 0; i < enumType.Values.Count; i++)
322             {
323                 Assert.AreEqual(i, enumType.Values[i].Index);
324             }
325             TestDescriptorToProto(enumType.ToProto, enumType.Proto);
326             TestDescriptorToProto(nestedType.ToProto, nestedType.Proto);
327         }
328 
329         [Test]
OneofDescriptor()330         public void OneofDescriptor()
331         {
332             OneofDescriptor descriptor = TestAllTypes.Descriptor.FindDescriptor<OneofDescriptor>("oneof_field");
333             Assert.IsFalse(descriptor.IsSynthetic);
334             Assert.AreEqual("oneof_field", descriptor.Name);
335             Assert.AreEqual("protobuf_unittest3.TestAllTypes.oneof_field", descriptor.FullName);
336 
337             var expectedFields = new[] {
338                 TestAllTypes.OneofBytesFieldNumber,
339                 TestAllTypes.OneofNestedMessageFieldNumber,
340                 TestAllTypes.OneofStringFieldNumber,
341                 TestAllTypes.OneofUint32FieldNumber }
342                 .Select(fieldNumber => TestAllTypes.Descriptor.FindFieldByNumber(fieldNumber))
343                 .ToList();
344             foreach (var field in expectedFields)
345             {
346                 Assert.AreSame(descriptor, field.ContainingOneof);
347             }
348 
349             CollectionAssert.AreEquivalent(expectedFields, descriptor.Fields);
350             TestDescriptorToProto(descriptor.ToProto, descriptor.Proto);
351         }
352 
353         [Test]
MapEntryMessageDescriptor()354         public void MapEntryMessageDescriptor()
355         {
356             var descriptor = MapWellKnownTypes.Descriptor.NestedTypes[0];
357             Assert.IsNull(descriptor.Parser);
358             Assert.IsNull(descriptor.ClrType);
359             Assert.IsNull(descriptor.Fields[1].Accessor);
360             TestDescriptorToProto(descriptor.ToProto, descriptor.Proto);
361         }
362 
363         // From TestFieldOrdering:
364         // string my_string = 11;
365         // int64 my_int = 1;
366         // float my_float = 101;
367         // NestedMessage single_nested_message = 200;
368         [Test]
FieldListOrderings()369         public void FieldListOrderings()
370         {
371             var fields = TestFieldOrderings.Descriptor.Fields;
372             Assert.AreEqual(new[] { 11, 1, 101, 200 }, fields.InDeclarationOrder().Select(x => x.FieldNumber));
373             Assert.AreEqual(new[] { 1, 11, 101, 200 }, fields.InFieldNumberOrder().Select(x => x.FieldNumber));
374         }
375 
376 
377         [Test]
DescriptorProtoFileDescriptor()378         public void DescriptorProtoFileDescriptor()
379         {
380             var descriptor = Google.Protobuf.Reflection.FileDescriptor.DescriptorProtoFileDescriptor;
381             Assert.AreEqual("google/protobuf/descriptor.proto", descriptor.Name);
382             TestDescriptorToProto(descriptor.ToProto, descriptor.Proto);
383         }
384 
385         [Test]
DescriptorImportingExtensionsFromOldCodeGen()386         public void DescriptorImportingExtensionsFromOldCodeGen()
387         {
388             if (MethodOptions.Descriptor.FullName != "google.protobuf.MethodOptions")
389             {
390                 Assert.Ignore("Embedded descriptor for OldExtensions expects google.protobuf reflection package.");
391             }
392 
393             // The extension collection includes a null extension. There's not a lot we can do about that
394             // in itself, as the old generator didn't provide us the extension information.
395             var extensions = TestProtos.OldGenerator.OldExtensions2Reflection.Descriptor.Extensions;
396             Assert.AreEqual(1, extensions.UnorderedExtensions.Count);
397             // Note: this assertion is present so that it will fail if OldExtensions2 is regenerated
398             // with a new generator.
399             Assert.Null(extensions.UnorderedExtensions[0].Extension);
400 
401             // ... but we can make sure we at least don't cause a failure when retrieving descriptors.
402             // In particular, old_extensions1.proto imports old_extensions2.proto, and this used to cause
403             // an execution-time failure.
404             var importingDescriptor = TestProtos.OldGenerator.OldExtensions1Reflection.Descriptor;
405             Assert.NotNull(importingDescriptor);
406         }
407 
408         [Test]
Proto3OptionalDescriptors()409         public void Proto3OptionalDescriptors()
410         {
411             var descriptor = TestProto3Optional.Descriptor;
412             var field = descriptor.Fields[TestProto3Optional.OptionalInt32FieldNumber];
413             Assert.NotNull(field.ContainingOneof);
414             Assert.IsTrue(field.ContainingOneof.IsSynthetic);
415             Assert.Null(field.RealContainingOneof);
416         }
417 
418 
419         [Test]
SyntheticOneofReflection()420         public void SyntheticOneofReflection()
421         {
422             // Expect every oneof in TestProto3Optional to be synthetic
423             var proto3OptionalDescriptor = TestProto3Optional.Descriptor;
424             Assert.AreEqual(0, proto3OptionalDescriptor.RealOneofCount);
425             foreach (var oneof in proto3OptionalDescriptor.Oneofs)
426             {
427                 Assert.True(oneof.IsSynthetic);
428             }
429 
430             // Expect no oneof in the original proto3 unit test file to be synthetic.
431             // (This excludes oneofs with "lazy" in the name, due to internal differences.)
432             foreach (var descriptor in ProtobufTestMessages.Proto3.TestMessagesProto3Reflection.Descriptor.MessageTypes)
433             {
434                 var nonLazyOneofs = descriptor.Oneofs.Where(d => !d.Name.Contains("lazy")).ToList();
435                 Assert.AreEqual(nonLazyOneofs.Count, descriptor.RealOneofCount);
436                 foreach (var oneof in nonLazyOneofs)
437                 {
438                     Assert.False(oneof.IsSynthetic);
439                 }
440             }
441 
442             // Expect no oneof in the original proto2 unit test file to be synthetic.
443             foreach (var descriptor in ProtobufTestMessages.Proto2.TestMessagesProto2Reflection.Descriptor.MessageTypes)
444             {
445                 Assert.AreEqual(descriptor.Oneofs.Count, descriptor.RealOneofCount);
446                 foreach (var oneof in descriptor.Oneofs)
447                 {
448                     Assert.False(oneof.IsSynthetic);
449                 }
450             }
451         }
452 
453         [Test]
OptionRetention()454         public void OptionRetention()
455         {
456           var proto = UnittestRetentionReflection.Descriptor.Proto;
457           Assert.AreEqual(1, proto.Options.GetExtension(
458               UnittestRetentionExtensions.PlainOption));
459           Assert.AreEqual(2, proto.Options.GetExtension(
460               UnittestRetentionExtensions.RuntimeRetentionOption));
461           // This option has a value of 3 in the .proto file, but we expect it
462           // to be zeroed out in the generated descriptor since it has source
463           // retention.
464           Assert.AreEqual(0, proto.Options.GetExtension(
465               UnittestRetentionExtensions.SourceRetentionOption));
466         }
467 
468         [Test]
GetOptionsStripsFeatures()469         public void GetOptionsStripsFeatures()
470         {
471             var messageDescriptor = TestEditionsMessage.Descriptor;
472             var fieldDescriptor = messageDescriptor.FindFieldByName("required_field");
473             // Note: ideally we'd test GetOptions() for other descriptor types as well, but that requires
474             // non-fields with features applied.
475             Assert.Null(fieldDescriptor.GetOptions().Features);
476         }
477 
478         [Test]
LegacyRequiredTransform()479         public void LegacyRequiredTransform()
480         {
481             var messageDescriptor = TestEditionsMessage.Descriptor;
482             var fieldDescriptor = messageDescriptor.FindFieldByName("required_field");
483             Assert.True(fieldDescriptor.IsRequired);
484         }
485 
486         [Test]
LegacyGroupTransform()487         public void LegacyGroupTransform()
488         {
489             var messageDescriptor = TestEditionsMessage.Descriptor;
490             var fieldDescriptor = messageDescriptor.FindFieldByName("delimited_field");
491             Assert.AreEqual(FieldType.Group, fieldDescriptor.FieldType);
492         }
493 
494         [Test]
LegacyInferRequired()495         public void LegacyInferRequired()
496         {
497             var messageDescriptor = proto2::TestRequired.Descriptor;
498             var fieldDescriptor = messageDescriptor.FindFieldByName("a");
499             Assert.AreEqual(FieldPresence.LegacyRequired, fieldDescriptor.Features.FieldPresence);
500         }
501 
502         [Test]
LegacyInferGroup()503         public void LegacyInferGroup()
504         {
505             var messageDescriptor = proto2::TestAllTypes.Descriptor;
506             var fieldDescriptor = messageDescriptor.FindFieldByName("optionalgroup");
507             Assert.AreEqual(MessageEncoding.Delimited, fieldDescriptor.Features.MessageEncoding);
508         }
509 
510         [Test]
LegacyInferProto2Packed()511         public void LegacyInferProto2Packed()
512         {
513             var messageDescriptor = proto2::TestPackedTypes.Descriptor;
514             var fieldDescriptor = messageDescriptor.FindFieldByName("packed_int32");
515             Assert.AreEqual(RepeatedFieldEncoding.Packed, fieldDescriptor.Features.RepeatedFieldEncoding);
516         }
517 
518         [Test]
LegacyInferProto3Expanded()519         public void LegacyInferProto3Expanded()
520         {
521             var messageDescriptor = TestUnpackedTypes.Descriptor;
522             var fieldDescriptor = messageDescriptor.FindFieldByName("unpacked_int32");
523             Assert.NotNull(fieldDescriptor);
524             Assert.AreEqual(RepeatedFieldEncoding.Expanded, fieldDescriptor.Features.RepeatedFieldEncoding);
525         }
526 
TestDescriptorToProto(Func<IMessage> toProtoFunction, IMessage expectedProto)527         private static void TestDescriptorToProto(Func<IMessage> toProtoFunction, IMessage expectedProto)
528         {
529             var clone1 = toProtoFunction();
530             var clone2 = toProtoFunction();
531             Assert.AreNotSame(clone1, clone2);
532             Assert.AreNotSame(clone1, expectedProto);
533             Assert.AreNotSame(clone2, expectedProto);
534 
535             Assert.AreEqual(clone1, clone2);
536             Assert.AreEqual(clone1, expectedProto);
537         }
538     }
539 }
540