1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // http://code.google.com/p/protobuf/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 package com.google.protobuf; 32 33 import com.google.protobuf.DescriptorProtos.DescriptorProto; 34 import com.google.protobuf.DescriptorProtos.FieldDescriptorProto; 35 import com.google.protobuf.DescriptorProtos.FileDescriptorProto; 36 import com.google.protobuf.Descriptors.DescriptorValidationException; 37 import com.google.protobuf.Descriptors.FileDescriptor; 38 import com.google.protobuf.Descriptors.Descriptor; 39 import com.google.protobuf.Descriptors.FieldDescriptor; 40 import com.google.protobuf.Descriptors.EnumDescriptor; 41 import com.google.protobuf.Descriptors.EnumValueDescriptor; 42 import com.google.protobuf.Descriptors.ServiceDescriptor; 43 import com.google.protobuf.Descriptors.MethodDescriptor; 44 45 import com.google.protobuf.test.UnittestImport; 46 import com.google.protobuf.test.UnittestImport.ImportEnum; 47 import com.google.protobuf.test.UnittestImport.ImportMessage; 48 import protobuf_unittest.UnittestProto; 49 import protobuf_unittest.UnittestProto.ForeignEnum; 50 import protobuf_unittest.UnittestProto.ForeignMessage; 51 import protobuf_unittest.UnittestProto.TestAllTypes; 52 import protobuf_unittest.UnittestProto.TestAllExtensions; 53 import protobuf_unittest.UnittestProto.TestExtremeDefaultValues; 54 import protobuf_unittest.UnittestProto.TestRequired; 55 import protobuf_unittest.UnittestProto.TestService; 56 import protobuf_unittest.UnittestCustomOptions; 57 58 59 import junit.framework.TestCase; 60 61 import java.util.Arrays; 62 import java.util.Collections; 63 64 /** 65 * Unit test for {@link Descriptors}. 66 * 67 * @author kenton (at) google.com Kenton Varda 68 */ 69 public class DescriptorsTest extends TestCase { 70 71 // Regression test for bug where referencing a FieldDescriptor.Type value 72 // before a FieldDescriptorProto.Type value would yield a 73 // ExceptionInInitializerError. 74 private static final Object STATIC_INIT_TEST = FieldDescriptor.Type.BOOL; 75 76 public void testFieldTypeEnumMapping() throws Exception { 77 assertEquals(FieldDescriptor.Type.values().length, 78 FieldDescriptorProto.Type.values().length); 79 for (FieldDescriptor.Type type : FieldDescriptor.Type.values()) { 80 FieldDescriptorProto.Type protoType = type.toProto(); 81 assertEquals("TYPE_" + type.name(), protoType.name()); 82 assertEquals(type, FieldDescriptor.Type.valueOf(protoType)); 83 } 84 } 85 86 public void testFileDescriptor() throws Exception { 87 FileDescriptor file = UnittestProto.getDescriptor(); 88 89 assertEquals("google/protobuf/unittest.proto", file.getName()); 90 assertEquals("protobuf_unittest", file.getPackage()); 91 92 assertEquals("UnittestProto", file.getOptions().getJavaOuterClassname()); 93 assertEquals("google/protobuf/unittest.proto", 94 file.toProto().getName()); 95 96 assertEquals(Arrays.asList(UnittestImport.getDescriptor()), 97 file.getDependencies()); 98 99 Descriptor messageType = TestAllTypes.getDescriptor(); 100 assertEquals(messageType, file.getMessageTypes().get(0)); 101 assertEquals(messageType, file.findMessageTypeByName("TestAllTypes")); 102 assertNull(file.findMessageTypeByName("NoSuchType")); 103 assertNull(file.findMessageTypeByName("protobuf_unittest.TestAllTypes")); 104 for (int i = 0; i < file.getMessageTypes().size(); i++) { 105 assertEquals(i, file.getMessageTypes().get(i).getIndex()); 106 } 107 108 EnumDescriptor enumType = ForeignEnum.getDescriptor(); 109 assertEquals(enumType, file.getEnumTypes().get(0)); 110 assertEquals(enumType, file.findEnumTypeByName("ForeignEnum")); 111 assertNull(file.findEnumTypeByName("NoSuchType")); 112 assertNull(file.findEnumTypeByName("protobuf_unittest.ForeignEnum")); 113 assertEquals(Arrays.asList(ImportEnum.getDescriptor()), 114 UnittestImport.getDescriptor().getEnumTypes()); 115 for (int i = 0; i < file.getEnumTypes().size(); i++) { 116 assertEquals(i, file.getEnumTypes().get(i).getIndex()); 117 } 118 119 ServiceDescriptor service = TestService.getDescriptor(); 120 assertEquals(service, file.getServices().get(0)); 121 assertEquals(service, file.findServiceByName("TestService")); 122 assertNull(file.findServiceByName("NoSuchType")); 123 assertNull(file.findServiceByName("protobuf_unittest.TestService")); 124 assertEquals(Collections.emptyList(), 125 UnittestImport.getDescriptor().getServices()); 126 for (int i = 0; i < file.getServices().size(); i++) { 127 assertEquals(i, file.getServices().get(i).getIndex()); 128 } 129 130 FieldDescriptor extension = 131 UnittestProto.optionalInt32Extension.getDescriptor(); 132 assertEquals(extension, file.getExtensions().get(0)); 133 assertEquals(extension, 134 file.findExtensionByName("optional_int32_extension")); 135 assertNull(file.findExtensionByName("no_such_ext")); 136 assertNull(file.findExtensionByName( 137 "protobuf_unittest.optional_int32_extension")); 138 assertEquals(Collections.emptyList(), 139 UnittestImport.getDescriptor().getExtensions()); 140 for (int i = 0; i < file.getExtensions().size(); i++) { 141 assertEquals(i, file.getExtensions().get(i).getIndex()); 142 } 143 } 144 145 public void testDescriptor() throws Exception { 146 Descriptor messageType = TestAllTypes.getDescriptor(); 147 Descriptor nestedType = TestAllTypes.NestedMessage.getDescriptor(); 148 149 assertEquals("TestAllTypes", messageType.getName()); 150 assertEquals("protobuf_unittest.TestAllTypes", messageType.getFullName()); 151 assertEquals(UnittestProto.getDescriptor(), messageType.getFile()); 152 assertNull(messageType.getContainingType()); 153 assertEquals(DescriptorProtos.MessageOptions.getDefaultInstance(), 154 messageType.getOptions()); 155 assertEquals("TestAllTypes", messageType.toProto().getName()); 156 157 assertEquals("NestedMessage", nestedType.getName()); 158 assertEquals("protobuf_unittest.TestAllTypes.NestedMessage", 159 nestedType.getFullName()); 160 assertEquals(UnittestProto.getDescriptor(), nestedType.getFile()); 161 assertEquals(messageType, nestedType.getContainingType()); 162 163 FieldDescriptor field = messageType.getFields().get(0); 164 assertEquals("optional_int32", field.getName()); 165 assertEquals(field, messageType.findFieldByName("optional_int32")); 166 assertNull(messageType.findFieldByName("no_such_field")); 167 assertEquals(field, messageType.findFieldByNumber(1)); 168 assertNull(messageType.findFieldByNumber(571283)); 169 for (int i = 0; i < messageType.getFields().size(); i++) { 170 assertEquals(i, messageType.getFields().get(i).getIndex()); 171 } 172 173 assertEquals(nestedType, messageType.getNestedTypes().get(0)); 174 assertEquals(nestedType, messageType.findNestedTypeByName("NestedMessage")); 175 assertNull(messageType.findNestedTypeByName("NoSuchType")); 176 for (int i = 0; i < messageType.getNestedTypes().size(); i++) { 177 assertEquals(i, messageType.getNestedTypes().get(i).getIndex()); 178 } 179 180 EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor(); 181 assertEquals(enumType, messageType.getEnumTypes().get(0)); 182 assertEquals(enumType, messageType.findEnumTypeByName("NestedEnum")); 183 assertNull(messageType.findEnumTypeByName("NoSuchType")); 184 for (int i = 0; i < messageType.getEnumTypes().size(); i++) { 185 assertEquals(i, messageType.getEnumTypes().get(i).getIndex()); 186 } 187 } 188 189 public void testFieldDescriptor() throws Exception { 190 Descriptor messageType = TestAllTypes.getDescriptor(); 191 FieldDescriptor primitiveField = 192 messageType.findFieldByName("optional_int32"); 193 FieldDescriptor enumField = 194 messageType.findFieldByName("optional_nested_enum"); 195 FieldDescriptor messageField = 196 messageType.findFieldByName("optional_foreign_message"); 197 FieldDescriptor cordField = 198 messageType.findFieldByName("optional_cord"); 199 FieldDescriptor extension = 200 UnittestProto.optionalInt32Extension.getDescriptor(); 201 FieldDescriptor nestedExtension = TestRequired.single.getDescriptor(); 202 203 assertEquals("optional_int32", primitiveField.getName()); 204 assertEquals("protobuf_unittest.TestAllTypes.optional_int32", 205 primitiveField.getFullName()); 206 assertEquals(1, primitiveField.getNumber()); 207 assertEquals(messageType, primitiveField.getContainingType()); 208 assertEquals(UnittestProto.getDescriptor(), primitiveField.getFile()); 209 assertEquals(FieldDescriptor.Type.INT32, primitiveField.getType()); 210 assertEquals(FieldDescriptor.JavaType.INT, primitiveField.getJavaType()); 211 assertEquals(DescriptorProtos.FieldOptions.getDefaultInstance(), 212 primitiveField.getOptions()); 213 assertFalse(primitiveField.isExtension()); 214 assertEquals("optional_int32", primitiveField.toProto().getName()); 215 216 assertEquals("optional_nested_enum", enumField.getName()); 217 assertEquals(FieldDescriptor.Type.ENUM, enumField.getType()); 218 assertEquals(FieldDescriptor.JavaType.ENUM, enumField.getJavaType()); 219 assertEquals(TestAllTypes.NestedEnum.getDescriptor(), 220 enumField.getEnumType()); 221 222 assertEquals("optional_foreign_message", messageField.getName()); 223 assertEquals(FieldDescriptor.Type.MESSAGE, messageField.getType()); 224 assertEquals(FieldDescriptor.JavaType.MESSAGE, messageField.getJavaType()); 225 assertEquals(ForeignMessage.getDescriptor(), messageField.getMessageType()); 226 227 assertEquals("optional_cord", cordField.getName()); 228 assertEquals(FieldDescriptor.Type.STRING, cordField.getType()); 229 assertEquals(FieldDescriptor.JavaType.STRING, cordField.getJavaType()); 230 assertEquals(DescriptorProtos.FieldOptions.CType.CORD, 231 cordField.getOptions().getCtype()); 232 233 assertEquals("optional_int32_extension", extension.getName()); 234 assertEquals("protobuf_unittest.optional_int32_extension", 235 extension.getFullName()); 236 assertEquals(1, extension.getNumber()); 237 assertEquals(TestAllExtensions.getDescriptor(), 238 extension.getContainingType()); 239 assertEquals(UnittestProto.getDescriptor(), extension.getFile()); 240 assertEquals(FieldDescriptor.Type.INT32, extension.getType()); 241 assertEquals(FieldDescriptor.JavaType.INT, extension.getJavaType()); 242 assertEquals(DescriptorProtos.FieldOptions.getDefaultInstance(), 243 extension.getOptions()); 244 assertTrue(extension.isExtension()); 245 assertEquals(null, extension.getExtensionScope()); 246 assertEquals("optional_int32_extension", extension.toProto().getName()); 247 248 assertEquals("single", nestedExtension.getName()); 249 assertEquals("protobuf_unittest.TestRequired.single", 250 nestedExtension.getFullName()); 251 assertEquals(TestRequired.getDescriptor(), 252 nestedExtension.getExtensionScope()); 253 } 254 255 public void testFieldDescriptorLabel() throws Exception { 256 FieldDescriptor requiredField = 257 TestRequired.getDescriptor().findFieldByName("a"); 258 FieldDescriptor optionalField = 259 TestAllTypes.getDescriptor().findFieldByName("optional_int32"); 260 FieldDescriptor repeatedField = 261 TestAllTypes.getDescriptor().findFieldByName("repeated_int32"); 262 263 assertTrue(requiredField.isRequired()); 264 assertFalse(requiredField.isRepeated()); 265 assertFalse(optionalField.isRequired()); 266 assertFalse(optionalField.isRepeated()); 267 assertFalse(repeatedField.isRequired()); 268 assertTrue(repeatedField.isRepeated()); 269 } 270 271 public void testFieldDescriptorDefault() throws Exception { 272 Descriptor d = TestAllTypes.getDescriptor(); 273 assertFalse(d.findFieldByName("optional_int32").hasDefaultValue()); 274 assertEquals(0, d.findFieldByName("optional_int32").getDefaultValue()); 275 assertTrue(d.findFieldByName("default_int32").hasDefaultValue()); 276 assertEquals(41, d.findFieldByName("default_int32").getDefaultValue()); 277 278 d = TestExtremeDefaultValues.getDescriptor(); 279 assertEquals( 280 ByteString.copyFrom( 281 "\0\001\007\b\f\n\r\t\013\\\'\"\u00fe".getBytes("ISO-8859-1")), 282 d.findFieldByName("escaped_bytes").getDefaultValue()); 283 assertEquals(-1, d.findFieldByName("large_uint32").getDefaultValue()); 284 assertEquals(-1L, d.findFieldByName("large_uint64").getDefaultValue()); 285 } 286 287 public void testEnumDescriptor() throws Exception { 288 EnumDescriptor enumType = ForeignEnum.getDescriptor(); 289 EnumDescriptor nestedType = TestAllTypes.NestedEnum.getDescriptor(); 290 291 assertEquals("ForeignEnum", enumType.getName()); 292 assertEquals("protobuf_unittest.ForeignEnum", enumType.getFullName()); 293 assertEquals(UnittestProto.getDescriptor(), enumType.getFile()); 294 assertNull(enumType.getContainingType()); 295 assertEquals(DescriptorProtos.EnumOptions.getDefaultInstance(), 296 enumType.getOptions()); 297 298 assertEquals("NestedEnum", nestedType.getName()); 299 assertEquals("protobuf_unittest.TestAllTypes.NestedEnum", 300 nestedType.getFullName()); 301 assertEquals(UnittestProto.getDescriptor(), nestedType.getFile()); 302 assertEquals(TestAllTypes.getDescriptor(), nestedType.getContainingType()); 303 304 EnumValueDescriptor value = ForeignEnum.FOREIGN_FOO.getValueDescriptor(); 305 assertEquals(value, enumType.getValues().get(0)); 306 assertEquals("FOREIGN_FOO", value.getName()); 307 assertEquals(4, value.getNumber()); 308 assertEquals(value, enumType.findValueByName("FOREIGN_FOO")); 309 assertEquals(value, enumType.findValueByNumber(4)); 310 assertNull(enumType.findValueByName("NO_SUCH_VALUE")); 311 for (int i = 0; i < enumType.getValues().size(); i++) { 312 assertEquals(i, enumType.getValues().get(i).getIndex()); 313 } 314 } 315 316 public void testServiceDescriptor() throws Exception { 317 ServiceDescriptor service = TestService.getDescriptor(); 318 319 assertEquals("TestService", service.getName()); 320 assertEquals("protobuf_unittest.TestService", service.getFullName()); 321 assertEquals(UnittestProto.getDescriptor(), service.getFile()); 322 323 assertEquals(2, service.getMethods().size()); 324 325 MethodDescriptor fooMethod = service.getMethods().get(0); 326 assertEquals("Foo", fooMethod.getName()); 327 assertEquals(UnittestProto.FooRequest.getDescriptor(), 328 fooMethod.getInputType()); 329 assertEquals(UnittestProto.FooResponse.getDescriptor(), 330 fooMethod.getOutputType()); 331 assertEquals(fooMethod, service.findMethodByName("Foo")); 332 333 MethodDescriptor barMethod = service.getMethods().get(1); 334 assertEquals("Bar", barMethod.getName()); 335 assertEquals(UnittestProto.BarRequest.getDescriptor(), 336 barMethod.getInputType()); 337 assertEquals(UnittestProto.BarResponse.getDescriptor(), 338 barMethod.getOutputType()); 339 assertEquals(barMethod, service.findMethodByName("Bar")); 340 341 assertNull(service.findMethodByName("NoSuchMethod")); 342 343 for (int i = 0; i < service.getMethods().size(); i++) { 344 assertEquals(i, service.getMethods().get(i).getIndex()); 345 } 346 } 347 348 349 public void testCustomOptions() throws Exception { 350 Descriptor descriptor = 351 UnittestCustomOptions.TestMessageWithCustomOptions.getDescriptor(); 352 353 assertTrue( 354 descriptor.getOptions().hasExtension(UnittestCustomOptions.messageOpt1)); 355 assertEquals(Integer.valueOf(-56), 356 descriptor.getOptions().getExtension(UnittestCustomOptions.messageOpt1)); 357 358 FieldDescriptor field = descriptor.findFieldByName("field1"); 359 assertNotNull(field); 360 361 assertTrue( 362 field.getOptions().hasExtension(UnittestCustomOptions.fieldOpt1)); 363 assertEquals(Long.valueOf(8765432109L), 364 field.getOptions().getExtension(UnittestCustomOptions.fieldOpt1)); 365 366 EnumDescriptor enumType = 367 UnittestCustomOptions.TestMessageWithCustomOptions.AnEnum.getDescriptor(); 368 369 assertTrue( 370 enumType.getOptions().hasExtension(UnittestCustomOptions.enumOpt1)); 371 assertEquals(Integer.valueOf(-789), 372 enumType.getOptions().getExtension(UnittestCustomOptions.enumOpt1)); 373 374 ServiceDescriptor service = 375 UnittestCustomOptions.TestServiceWithCustomOptions.getDescriptor(); 376 377 assertTrue( 378 service.getOptions().hasExtension(UnittestCustomOptions.serviceOpt1)); 379 assertEquals(Long.valueOf(-9876543210L), 380 service.getOptions().getExtension(UnittestCustomOptions.serviceOpt1)); 381 382 MethodDescriptor method = service.findMethodByName("Foo"); 383 assertNotNull(method); 384 385 assertTrue( 386 method.getOptions().hasExtension(UnittestCustomOptions.methodOpt1)); 387 assertEquals(UnittestCustomOptions.MethodOpt1.METHODOPT1_VAL2, 388 method.getOptions().getExtension(UnittestCustomOptions.methodOpt1)); 389 } 390 391 /** 392 * Test that the FieldDescriptor.Type enum is the same as the 393 * WireFormat.FieldType enum. 394 */ 395 public void testFieldTypeTablesMatch() throws Exception { 396 FieldDescriptor.Type[] values1 = FieldDescriptor.Type.values(); 397 WireFormat.FieldType[] values2 = WireFormat.FieldType.values(); 398 399 assertEquals(values1.length, values2.length); 400 401 for (int i = 0; i < values1.length; i++) { 402 assertEquals(values1[i].toString(), values2[i].toString()); 403 } 404 } 405 406 /** 407 * Test that the FieldDescriptor.JavaType enum is the same as the 408 * WireFormat.JavaType enum. 409 */ 410 public void testJavaTypeTablesMatch() throws Exception { 411 FieldDescriptor.JavaType[] values1 = FieldDescriptor.JavaType.values(); 412 WireFormat.JavaType[] values2 = WireFormat.JavaType.values(); 413 414 assertEquals(values1.length, values2.length); 415 416 for (int i = 0; i < values1.length; i++) { 417 assertEquals(values1[i].toString(), values2[i].toString()); 418 } 419 } 420 421 public void testEnormousDescriptor() throws Exception { 422 // The descriptor for this file is larger than 64k, yet it did not cause 423 // a compiler error due to an over-long string literal. 424 assertTrue( 425 UnittestEnormousDescriptor.getDescriptor() 426 .toProto().getSerializedSize() > 65536); 427 } 428 429 /** 430 * Tests that the DescriptorValidationException works as intended. 431 */ 432 public void testDescriptorValidatorException() throws Exception { 433 FileDescriptorProto fileDescriptorProto = FileDescriptorProto.newBuilder() 434 .setName("foo.proto") 435 .addMessageType(DescriptorProto.newBuilder() 436 .setName("Foo") 437 .addField(FieldDescriptorProto.newBuilder() 438 .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) 439 .setType(FieldDescriptorProto.Type.TYPE_INT32) 440 .setName("foo") 441 .setNumber(1) 442 .setDefaultValue("invalid") 443 .build()) 444 .build()) 445 .build(); 446 try { 447 Descriptors.FileDescriptor.buildFrom(fileDescriptorProto, 448 new FileDescriptor[0]); 449 fail("DescriptorValidationException expected"); 450 } catch (DescriptorValidationException e) { 451 // Expected; check that the error message contains some useful hints 452 assertTrue(e.getMessage().indexOf("foo") != -1); 453 assertTrue(e.getMessage().indexOf("Foo") != -1); 454 assertTrue(e.getMessage().indexOf("invalid") != -1); 455 assertTrue(e.getCause() instanceof NumberFormatException); 456 assertTrue(e.getCause().getMessage().indexOf("invalid") != -1); 457 } 458 } 459 } 460