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 System; 36 using System.Collections.Generic; 37 using System.Linq; 38 39 namespace Google.Protobuf.Reflection 40 { 41 /// <summary> 42 /// Tests for descriptors. (Not in its own namespace or broken up into individual classes as the 43 /// size doesn't warrant it. On the other hand, this makes me feel a bit dirty...) 44 /// </summary> 45 public class DescriptorsTest 46 { 47 [Test] FileDescriptor_GeneratedCode()48 public void FileDescriptor_GeneratedCode() 49 { 50 TestFileDescriptor( 51 UnittestProto3Reflection.Descriptor, 52 UnittestImportProto3Reflection.Descriptor, 53 UnittestImportPublicProto3Reflection.Descriptor); 54 } 55 56 [Test] FileDescriptor_BuildFromByteStrings()57 public void FileDescriptor_BuildFromByteStrings() 58 { 59 // The descriptors have to be supplied in an order such that all the 60 // dependencies come before the descriptors depending on them. 61 var descriptorData = new List<ByteString> 62 { 63 UnittestImportPublicProto3Reflection.Descriptor.SerializedData, 64 UnittestImportProto3Reflection.Descriptor.SerializedData, 65 UnittestProto3Reflection.Descriptor.SerializedData 66 }; 67 var converted = FileDescriptor.BuildFromByteStrings(descriptorData); 68 Assert.AreEqual(3, converted.Count); 69 TestFileDescriptor(converted[2], converted[1], converted[0]); 70 } 71 TestFileDescriptor(FileDescriptor file, FileDescriptor importedFile, FileDescriptor importedPublicFile)72 private void TestFileDescriptor(FileDescriptor file, FileDescriptor importedFile, FileDescriptor importedPublicFile) 73 { 74 Assert.AreEqual("unittest_proto3.proto", file.Name); 75 Assert.AreEqual("protobuf_unittest3", file.Package); 76 77 Assert.AreEqual("UnittestProto", file.Proto.Options.JavaOuterClassname); 78 Assert.AreEqual("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 } 107 108 [Test] FileDescriptor_NonRootPath()109 public void FileDescriptor_NonRootPath() 110 { 111 // unittest_proto3.proto used to be in google/protobuf. Now it's in the C#-specific location, 112 // let's test something that's still in a directory. 113 FileDescriptor file = UnittestWellKnownTypesReflection.Descriptor; 114 Assert.AreEqual("google/protobuf/unittest_well_known_types.proto", file.Name); 115 Assert.AreEqual("protobuf_unittest", file.Package); 116 } 117 118 [Test] FileDescriptor_BuildFromByteStrings_MissingDependency()119 public void FileDescriptor_BuildFromByteStrings_MissingDependency() 120 { 121 var descriptorData = new List<ByteString> 122 { 123 UnittestImportProto3Reflection.Descriptor.SerializedData, 124 UnittestProto3Reflection.Descriptor.SerializedData, 125 }; 126 // This will fail, because we're missing UnittestImportPublicProto3Reflection 127 Assert.Throws<ArgumentException>(() => FileDescriptor.BuildFromByteStrings(descriptorData)); 128 } 129 130 [Test] FileDescriptor_BuildFromByteStrings_DuplicateNames()131 public void FileDescriptor_BuildFromByteStrings_DuplicateNames() 132 { 133 var descriptorData = new List<ByteString> 134 { 135 UnittestImportPublicProto3Reflection.Descriptor.SerializedData, 136 UnittestImportPublicProto3Reflection.Descriptor.SerializedData, 137 }; 138 // This will fail due to the same name being used twice 139 Assert.Throws<ArgumentException>(() => FileDescriptor.BuildFromByteStrings(descriptorData)); 140 } 141 142 [Test] FileDescriptor_BuildFromByteStrings_IncorrectOrder()143 public void FileDescriptor_BuildFromByteStrings_IncorrectOrder() 144 { 145 var descriptorData = new List<ByteString> 146 { 147 UnittestProto3Reflection.Descriptor.SerializedData, 148 UnittestImportPublicProto3Reflection.Descriptor.SerializedData, 149 UnittestImportProto3Reflection.Descriptor.SerializedData 150 }; 151 // This will fail, because the dependencies should come first 152 Assert.Throws<ArgumentException>(() => FileDescriptor.BuildFromByteStrings(descriptorData)); 153 154 } 155 156 [Test] MessageDescriptorFromGeneratedCodeFileDescriptor()157 public void MessageDescriptorFromGeneratedCodeFileDescriptor() 158 { 159 var file = UnittestProto3Reflection.Descriptor; 160 161 MessageDescriptor messageType = TestAllTypes.Descriptor; 162 Assert.AreSame(typeof(TestAllTypes), messageType.ClrType); 163 Assert.AreSame(TestAllTypes.Parser, messageType.Parser); 164 Assert.AreEqual(messageType, file.MessageTypes[0]); 165 Assert.AreEqual(messageType, file.FindTypeByName<MessageDescriptor>("TestAllTypes")); 166 } 167 168 [Test] MessageDescriptor()169 public void MessageDescriptor() 170 { 171 MessageDescriptor messageType = TestAllTypes.Descriptor; 172 MessageDescriptor nestedType = TestAllTypes.Types.NestedMessage.Descriptor; 173 174 Assert.AreEqual("TestAllTypes", messageType.Name); 175 Assert.AreEqual("protobuf_unittest3.TestAllTypes", messageType.FullName); 176 Assert.AreEqual(UnittestProto3Reflection.Descriptor, messageType.File); 177 Assert.IsNull(messageType.ContainingType); 178 Assert.IsNull(messageType.Proto.Options); 179 180 Assert.AreEqual("TestAllTypes", messageType.Name); 181 182 Assert.AreEqual("NestedMessage", nestedType.Name); 183 Assert.AreEqual("protobuf_unittest3.TestAllTypes.NestedMessage", nestedType.FullName); 184 Assert.AreEqual(UnittestProto3Reflection.Descriptor, nestedType.File); 185 Assert.AreEqual(messageType, nestedType.ContainingType); 186 187 FieldDescriptor field = messageType.Fields.InDeclarationOrder()[0]; 188 Assert.AreEqual("single_int32", field.Name); 189 Assert.AreEqual(field, messageType.FindDescriptor<FieldDescriptor>("single_int32")); 190 Assert.Null(messageType.FindDescriptor<FieldDescriptor>("no_such_field")); 191 Assert.AreEqual(field, messageType.FindFieldByNumber(1)); 192 Assert.Null(messageType.FindFieldByNumber(571283)); 193 var fieldsInDeclarationOrder = messageType.Fields.InDeclarationOrder(); 194 for (int i = 0; i < fieldsInDeclarationOrder.Count; i++) 195 { 196 Assert.AreEqual(i, fieldsInDeclarationOrder[i].Index); 197 } 198 199 Assert.AreEqual(nestedType, messageType.NestedTypes[0]); 200 Assert.AreEqual(nestedType, messageType.FindDescriptor<MessageDescriptor>("NestedMessage")); 201 Assert.Null(messageType.FindDescriptor<MessageDescriptor>("NoSuchType")); 202 for (int i = 0; i < messageType.NestedTypes.Count; i++) 203 { 204 Assert.AreEqual(i, messageType.NestedTypes[i].Index); 205 } 206 207 Assert.AreEqual(messageType.EnumTypes[0], messageType.FindDescriptor<EnumDescriptor>("NestedEnum")); 208 Assert.Null(messageType.FindDescriptor<EnumDescriptor>("NoSuchType")); 209 for (int i = 0; i < messageType.EnumTypes.Count; i++) 210 { 211 Assert.AreEqual(i, messageType.EnumTypes[i].Index); 212 } 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 251 Assert.AreEqual("single_int32", primitiveField.Name); 252 Assert.AreEqual("protobuf_unittest3.TestAllTypes.single_int32", 253 primitiveField.FullName); 254 Assert.AreEqual(1, primitiveField.FieldNumber); 255 Assert.AreEqual(testAllTypesDescriptor, primitiveField.ContainingType); 256 Assert.AreEqual(unitTestProto3Descriptor, primitiveField.File); 257 Assert.AreEqual(FieldType.Int32, primitiveField.FieldType); 258 Assert.IsNull(primitiveField.Proto.Options); 259 260 Assert.AreEqual("single_nested_enum", enumField.Name); 261 Assert.AreEqual(FieldType.Enum, enumField.FieldType); 262 Assert.AreEqual(testAllTypesDescriptor.EnumTypes[0], enumField.EnumType); 263 264 Assert.AreEqual("single_foreign_message", foreignMessageField.Name); 265 Assert.AreEqual(FieldType.Message, foreignMessageField.FieldType); 266 Assert.AreEqual(foreignMessageDescriptor, foreignMessageField.MessageType); 267 268 Assert.AreEqual("single_import_message", importMessageField.Name); 269 Assert.AreEqual(FieldType.Message, importMessageField.FieldType); 270 Assert.AreEqual(importMessageDescriptor, importMessageField.MessageType); 271 } 272 273 [Test] FieldDescriptorLabel()274 public void FieldDescriptorLabel() 275 { 276 FieldDescriptor singleField = 277 TestAllTypes.Descriptor.FindDescriptor<FieldDescriptor>("single_int32"); 278 FieldDescriptor repeatedField = 279 TestAllTypes.Descriptor.FindDescriptor<FieldDescriptor>("repeated_int32"); 280 281 Assert.IsFalse(singleField.IsRepeated); 282 Assert.IsTrue(repeatedField.IsRepeated); 283 } 284 285 [Test] EnumDescriptor()286 public void EnumDescriptor() 287 { 288 // Note: this test is a bit different to the Java version because there's no static way of getting to the descriptor 289 EnumDescriptor enumType = UnittestProto3Reflection.Descriptor.FindTypeByName<EnumDescriptor>("ForeignEnum"); 290 EnumDescriptor nestedType = TestAllTypes.Descriptor.FindDescriptor<EnumDescriptor>("NestedEnum"); 291 292 Assert.AreEqual("ForeignEnum", enumType.Name); 293 Assert.AreEqual("protobuf_unittest3.ForeignEnum", enumType.FullName); 294 Assert.AreEqual(UnittestProto3Reflection.Descriptor, enumType.File); 295 Assert.Null(enumType.ContainingType); 296 Assert.Null(enumType.Proto.Options); 297 298 Assert.AreEqual("NestedEnum", nestedType.Name); 299 Assert.AreEqual("protobuf_unittest3.TestAllTypes.NestedEnum", 300 nestedType.FullName); 301 Assert.AreEqual(UnittestProto3Reflection.Descriptor, nestedType.File); 302 Assert.AreEqual(TestAllTypes.Descriptor, nestedType.ContainingType); 303 304 EnumValueDescriptor value = enumType.FindValueByName("FOREIGN_FOO"); 305 Assert.AreEqual(value, enumType.Values[1]); 306 Assert.AreEqual("FOREIGN_FOO", value.Name); 307 Assert.AreEqual(4, value.Number); 308 Assert.AreEqual((int) ForeignEnum.ForeignFoo, value.Number); 309 Assert.AreEqual(value, enumType.FindValueByNumber(4)); 310 Assert.Null(enumType.FindValueByName("NO_SUCH_VALUE")); 311 for (int i = 0; i < enumType.Values.Count; i++) 312 { 313 Assert.AreEqual(i, enumType.Values[i].Index); 314 } 315 } 316 317 [Test] OneofDescriptor()318 public void OneofDescriptor() 319 { 320 OneofDescriptor descriptor = TestAllTypes.Descriptor.FindDescriptor<OneofDescriptor>("oneof_field"); 321 Assert.AreEqual("oneof_field", descriptor.Name); 322 Assert.AreEqual("protobuf_unittest3.TestAllTypes.oneof_field", descriptor.FullName); 323 324 var expectedFields = new[] { 325 TestAllTypes.OneofBytesFieldNumber, 326 TestAllTypes.OneofNestedMessageFieldNumber, 327 TestAllTypes.OneofStringFieldNumber, 328 TestAllTypes.OneofUint32FieldNumber } 329 .Select(fieldNumber => TestAllTypes.Descriptor.FindFieldByNumber(fieldNumber)) 330 .ToList(); 331 foreach (var field in expectedFields) 332 { 333 Assert.AreSame(descriptor, field.ContainingOneof); 334 } 335 336 CollectionAssert.AreEquivalent(expectedFields, descriptor.Fields); 337 } 338 339 [Test] MapEntryMessageDescriptor()340 public void MapEntryMessageDescriptor() 341 { 342 var descriptor = MapWellKnownTypes.Descriptor.NestedTypes[0]; 343 Assert.IsNull(descriptor.Parser); 344 Assert.IsNull(descriptor.ClrType); 345 Assert.IsNull(descriptor.Fields[1].Accessor); 346 } 347 348 // From TestFieldOrdering: 349 // string my_string = 11; 350 // int64 my_int = 1; 351 // float my_float = 101; 352 // NestedMessage single_nested_message = 200; 353 [Test] FieldListOrderings()354 public void FieldListOrderings() 355 { 356 var fields = TestFieldOrderings.Descriptor.Fields; 357 Assert.AreEqual(new[] { 11, 1, 101, 200 }, fields.InDeclarationOrder().Select(x => x.FieldNumber)); 358 Assert.AreEqual(new[] { 1, 11, 101, 200 }, fields.InFieldNumberOrder().Select(x => x.FieldNumber)); 359 } 360 361 362 [Test] DescriptorProtoFileDescriptor()363 public void DescriptorProtoFileDescriptor() 364 { 365 var descriptor = Google.Protobuf.Reflection.FileDescriptor.DescriptorProtoFileDescriptor; 366 Assert.AreEqual("google/protobuf/descriptor.proto", descriptor.Name); 367 } 368 } 369 } 370