1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 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.Descriptors.Descriptor; 34 import com.google.protobuf.Descriptors.FieldDescriptor; 35 import com.google.protobuf.TextFormat.Parser.SingularOverwritePolicy; 36 import protobuf_unittest.UnittestMset.TestMessageSetExtension1; 37 import protobuf_unittest.UnittestMset.TestMessageSetExtension2; 38 import protobuf_unittest.UnittestProto.OneString; 39 import protobuf_unittest.UnittestProto.TestAllExtensions; 40 import protobuf_unittest.UnittestProto.TestAllTypes; 41 import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage; 42 import protobuf_unittest.UnittestProto.TestEmptyMessage; 43 import protobuf_unittest.UnittestProto.TestOneof2; 44 import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet; 45 46 import junit.framework.TestCase; 47 48 import java.io.StringReader; 49 import java.util.List; 50 51 /** 52 * Test case for {@link TextFormat}. 53 * 54 * TODO(wenboz): ExtensionTest and rest of text_format_unittest.cc. 55 * 56 * @author wenboz@google.com (Wenbo Zhu) 57 */ 58 public class TextFormatTest extends TestCase { 59 60 // A basic string with different escapable characters for testing. 61 private final static String kEscapeTestString = 62 "\"A string with ' characters \n and \r newlines and \t tabs and \001 " 63 + "slashes \\"; 64 65 // A representation of the above string with all the characters escaped. 66 private final static String kEscapeTestStringEscaped = 67 "\\\"A string with \\' characters \\n and \\r newlines " 68 + "and \\t tabs and \\001 slashes \\\\"; 69 70 private static String allFieldsSetText = TestUtil.readTextFromFile( 71 "text_format_unittest_data_oneof_implemented.txt"); 72 private static String allExtensionsSetText = TestUtil.readTextFromFile( 73 "text_format_unittest_extensions_data.txt"); 74 75 private static String exoticText = 76 "repeated_int32: -1\n" + 77 "repeated_int32: -2147483648\n" + 78 "repeated_int64: -1,\n" + 79 "repeated_int64: -9223372036854775808\n" + 80 "repeated_uint32: 4294967295\n" + 81 "repeated_uint32: 2147483648\n" + 82 "repeated_uint64: 18446744073709551615\n" + 83 "repeated_uint64: 9223372036854775808\n" + 84 "repeated_double: 123.0\n" + 85 "repeated_double: 123.5\n" + 86 "repeated_double: 0.125\n" + 87 "repeated_double: .125\n" + 88 "repeated_double: -.125\n" + 89 "repeated_double: 1.23E17\n" + 90 "repeated_double: 1.23E+17\n" + 91 "repeated_double: -1.23e-17\n" + 92 "repeated_double: .23e+17\n" + 93 "repeated_double: -.23E17\n" + 94 "repeated_double: 1.235E22\n" + 95 "repeated_double: 1.235E-18\n" + 96 "repeated_double: 123.456789\n" + 97 "repeated_double: Infinity\n" + 98 "repeated_double: -Infinity\n" + 99 "repeated_double: NaN\n" + 100 "repeated_string: \"\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"" + 101 "\\341\\210\\264\"\n" + 102 "repeated_bytes: \"\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\\376\"\n"; 103 104 private static String canonicalExoticText = 105 exoticText.replace(": .", ": 0.").replace(": -.", ": -0.") // short-form double 106 .replace("23e", "23E").replace("E+", "E").replace("0.23E17", "2.3E16").replace(",", ""); 107 108 private String messageSetText = 109 "[protobuf_unittest.TestMessageSetExtension1] {\n" + 110 " i: 123\n" + 111 "}\n" + 112 "[protobuf_unittest.TestMessageSetExtension2] {\n" + 113 " str: \"foo\"\n" + 114 "}\n"; 115 116 private String messageSetTextWithRepeatedExtension = 117 "[protobuf_unittest.TestMessageSetExtension1] {\n" + 118 " i: 123\n" + 119 "}\n" + 120 "[protobuf_unittest.TestMessageSetExtension1] {\n" + 121 " i: 456\n" + 122 "}\n"; 123 124 125 private final TextFormat.Parser parserWithOverwriteForbidden = 126 TextFormat.Parser.newBuilder() 127 .setSingularOverwritePolicy( 128 SingularOverwritePolicy.FORBID_SINGULAR_OVERWRITES) 129 .build(); 130 131 private final TextFormat.Parser defaultParser = 132 TextFormat.Parser.newBuilder().build(); 133 134 /** Print TestAllTypes and compare with golden file. */ testPrintMessage()135 public void testPrintMessage() throws Exception { 136 String javaText = TextFormat.printToString(TestUtil.getAllSet()); 137 138 // Java likes to add a trailing ".0" to floats and doubles. C printf 139 // (with %g format) does not. Our golden files are used for both 140 // C++ and Java TextFormat classes, so we need to conform. 141 javaText = javaText.replace(".0\n", "\n"); 142 143 assertEquals(allFieldsSetText, javaText); 144 } 145 146 /** Print TestAllTypes as Builder and compare with golden file. */ testPrintMessageBuilder()147 public void testPrintMessageBuilder() throws Exception { 148 String javaText = TextFormat.printToString(TestUtil.getAllSetBuilder()); 149 150 // Java likes to add a trailing ".0" to floats and doubles. C printf 151 // (with %g format) does not. Our golden files are used for both 152 // C++ and Java TextFormat classes, so we need to conform. 153 javaText = javaText.replace(".0\n", "\n"); 154 155 assertEquals(allFieldsSetText, javaText); 156 } 157 158 /** Print TestAllExtensions and compare with golden file. */ testPrintExtensions()159 public void testPrintExtensions() throws Exception { 160 String javaText = TextFormat.printToString(TestUtil.getAllExtensionsSet()); 161 162 // Java likes to add a trailing ".0" to floats and doubles. C printf 163 // (with %g format) does not. Our golden files are used for both 164 // C++ and Java TextFormat classes, so we need to conform. 165 javaText = javaText.replace(".0\n", "\n"); 166 167 assertEquals(allExtensionsSetText, javaText); 168 } 169 170 // Creates an example unknown field set. makeUnknownFieldSet()171 private UnknownFieldSet makeUnknownFieldSet() { 172 return UnknownFieldSet.newBuilder() 173 .addField(5, 174 UnknownFieldSet.Field.newBuilder() 175 .addVarint(1) 176 .addFixed32(2) 177 .addFixed64(3) 178 .addLengthDelimited(ByteString.copyFromUtf8("4")) 179 .addGroup( 180 UnknownFieldSet.newBuilder() 181 .addField(10, 182 UnknownFieldSet.Field.newBuilder() 183 .addVarint(5) 184 .build()) 185 .build()) 186 .build()) 187 .addField(8, 188 UnknownFieldSet.Field.newBuilder() 189 .addVarint(1) 190 .addVarint(2) 191 .addVarint(3) 192 .build()) 193 .addField(15, 194 UnknownFieldSet.Field.newBuilder() 195 .addVarint(0xABCDEF1234567890L) 196 .addFixed32(0xABCD1234) 197 .addFixed64(0xABCDEF1234567890L) 198 .build()) 199 .build(); 200 } 201 testPrintUnknownFields()202 public void testPrintUnknownFields() throws Exception { 203 // Test printing of unknown fields in a message. 204 205 TestEmptyMessage message = 206 TestEmptyMessage.newBuilder() 207 .setUnknownFields(makeUnknownFieldSet()) 208 .build(); 209 210 assertEquals( 211 "5: 1\n" + 212 "5: 0x00000002\n" + 213 "5: 0x0000000000000003\n" + 214 "5: \"4\"\n" + 215 "5 {\n" + 216 " 10: 5\n" + 217 "}\n" + 218 "8: 1\n" + 219 "8: 2\n" + 220 "8: 3\n" + 221 "15: 12379813812177893520\n" + 222 "15: 0xabcd1234\n" + 223 "15: 0xabcdef1234567890\n", 224 TextFormat.printToString(message)); 225 } 226 testPrintField()227 public void testPrintField() throws Exception { 228 final FieldDescriptor dataField = 229 OneString.getDescriptor().findFieldByName("data"); 230 assertEquals( 231 "data: \"test data\"\n", 232 TextFormat.printFieldToString(dataField, "test data")); 233 234 final FieldDescriptor optionalField = 235 TestAllTypes.getDescriptor().findFieldByName("optional_nested_message"); 236 final Object value = NestedMessage.newBuilder().setBb(42).build(); 237 238 assertEquals( 239 "optional_nested_message {\n bb: 42\n}\n", 240 TextFormat.printFieldToString(optionalField, value)); 241 } 242 243 /** 244 * Helper to construct a ByteString from a String containing only 8-bit 245 * characters. The characters are converted directly to bytes, *not* 246 * encoded using UTF-8. 247 */ bytes(String str)248 private ByteString bytes(String str) { 249 return ByteString.copyFrom(str.getBytes(Internal.ISO_8859_1)); 250 } 251 252 /** 253 * Helper to construct a ByteString from a bunch of bytes. The inputs are 254 * actually ints so that I can use hex notation and not get stupid errors 255 * about precision. 256 */ bytes(int... bytesAsInts)257 private ByteString bytes(int... bytesAsInts) { 258 byte[] bytes = new byte[bytesAsInts.length]; 259 for (int i = 0; i < bytesAsInts.length; i++) { 260 bytes[i] = (byte) bytesAsInts[i]; 261 } 262 return ByteString.copyFrom(bytes); 263 } 264 testPrintExotic()265 public void testPrintExotic() throws Exception { 266 Message message = TestAllTypes.newBuilder() 267 // Signed vs. unsigned numbers. 268 .addRepeatedInt32 (-1) 269 .addRepeatedUint32(-1) 270 .addRepeatedInt64 (-1) 271 .addRepeatedUint64(-1) 272 273 .addRepeatedInt32 (1 << 31) 274 .addRepeatedUint32(1 << 31) 275 .addRepeatedInt64 (1L << 63) 276 .addRepeatedUint64(1L << 63) 277 278 // Floats of various precisions and exponents. 279 .addRepeatedDouble(123) 280 .addRepeatedDouble(123.5) 281 .addRepeatedDouble(0.125) 282 .addRepeatedDouble(.125) 283 .addRepeatedDouble(-.125) 284 .addRepeatedDouble(123e15) 285 .addRepeatedDouble(123e15) 286 .addRepeatedDouble(-1.23e-17) 287 .addRepeatedDouble(.23e17) 288 .addRepeatedDouble(-23e15) 289 .addRepeatedDouble(123.5e20) 290 .addRepeatedDouble(123.5e-20) 291 .addRepeatedDouble(123.456789) 292 .addRepeatedDouble(Double.POSITIVE_INFINITY) 293 .addRepeatedDouble(Double.NEGATIVE_INFINITY) 294 .addRepeatedDouble(Double.NaN) 295 296 // Strings and bytes that needing escaping. 297 .addRepeatedString("\0\001\007\b\f\n\r\t\013\\\'\"\u1234") 298 .addRepeatedBytes(bytes("\0\001\007\b\f\n\r\t\013\\\'\"\u00fe")) 299 .build(); 300 301 assertEquals(canonicalExoticText, message.toString()); 302 } 303 testPrintMessageSet()304 public void testPrintMessageSet() throws Exception { 305 TestMessageSet messageSet = 306 TestMessageSet.newBuilder() 307 .setExtension( 308 TestMessageSetExtension1.messageSetExtension, 309 TestMessageSetExtension1.newBuilder().setI(123).build()) 310 .setExtension( 311 TestMessageSetExtension2.messageSetExtension, 312 TestMessageSetExtension2.newBuilder().setStr("foo").build()) 313 .build(); 314 315 assertEquals(messageSetText, messageSet.toString()); 316 } 317 318 // ================================================================= 319 testParse()320 public void testParse() throws Exception { 321 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 322 TextFormat.merge(allFieldsSetText, builder); 323 TestUtil.assertAllFieldsSet(builder.build()); 324 } 325 testParseReader()326 public void testParseReader() throws Exception { 327 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 328 TextFormat.merge(new StringReader(allFieldsSetText), builder); 329 TestUtil.assertAllFieldsSet(builder.build()); 330 } 331 testParseExtensions()332 public void testParseExtensions() throws Exception { 333 TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); 334 TextFormat.merge(allExtensionsSetText, 335 TestUtil.getExtensionRegistry(), 336 builder); 337 TestUtil.assertAllExtensionsSet(builder.build()); 338 } 339 testParseCompatibility()340 public void testParseCompatibility() throws Exception { 341 String original = "repeated_float: inf\n" + 342 "repeated_float: -inf\n" + 343 "repeated_float: nan\n" + 344 "repeated_float: inff\n" + 345 "repeated_float: -inff\n" + 346 "repeated_float: nanf\n" + 347 "repeated_float: 1.0f\n" + 348 "repeated_float: infinityf\n" + 349 "repeated_float: -Infinityf\n" + 350 "repeated_double: infinity\n" + 351 "repeated_double: -infinity\n" + 352 "repeated_double: nan\n"; 353 String canonical = "repeated_float: Infinity\n" + 354 "repeated_float: -Infinity\n" + 355 "repeated_float: NaN\n" + 356 "repeated_float: Infinity\n" + 357 "repeated_float: -Infinity\n" + 358 "repeated_float: NaN\n" + 359 "repeated_float: 1.0\n" + 360 "repeated_float: Infinity\n" + 361 "repeated_float: -Infinity\n" + 362 "repeated_double: Infinity\n" + 363 "repeated_double: -Infinity\n" + 364 "repeated_double: NaN\n"; 365 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 366 TextFormat.merge(original, builder); 367 assertEquals(canonical, builder.build().toString()); 368 } 369 testParseExotic()370 public void testParseExotic() throws Exception { 371 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 372 TextFormat.merge(exoticText, builder); 373 374 // Too lazy to check things individually. Don't try to debug this 375 // if testPrintExotic() is failing. 376 assertEquals(canonicalExoticText, builder.build().toString()); 377 } 378 testParseMessageSet()379 public void testParseMessageSet() throws Exception { 380 ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance(); 381 extensionRegistry.add(TestMessageSetExtension1.messageSetExtension); 382 extensionRegistry.add(TestMessageSetExtension2.messageSetExtension); 383 384 TestMessageSet.Builder builder = TestMessageSet.newBuilder(); 385 TextFormat.merge(messageSetText, extensionRegistry, builder); 386 TestMessageSet messageSet = builder.build(); 387 388 assertTrue(messageSet.hasExtension( 389 TestMessageSetExtension1.messageSetExtension)); 390 assertEquals(123, messageSet.getExtension( 391 TestMessageSetExtension1.messageSetExtension).getI()); 392 assertTrue(messageSet.hasExtension( 393 TestMessageSetExtension2.messageSetExtension)); 394 assertEquals("foo", messageSet.getExtension( 395 TestMessageSetExtension2.messageSetExtension).getStr()); 396 397 builder = TestMessageSet.newBuilder(); 398 TextFormat.merge(messageSetTextWithRepeatedExtension, extensionRegistry, 399 builder); 400 messageSet = builder.build(); 401 assertEquals(456, messageSet.getExtension( 402 TestMessageSetExtension1.messageSetExtension).getI()); 403 } 404 testParseMessageSetWithOverwriteForbidden()405 public void testParseMessageSetWithOverwriteForbidden() throws Exception { 406 ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance(); 407 extensionRegistry.add(TestMessageSetExtension1.messageSetExtension); 408 extensionRegistry.add(TestMessageSetExtension2.messageSetExtension); 409 410 TestMessageSet.Builder builder = TestMessageSet.newBuilder(); 411 parserWithOverwriteForbidden.merge( 412 messageSetText, extensionRegistry, builder); 413 TestMessageSet messageSet = builder.build(); 414 assertEquals(123, messageSet.getExtension( 415 TestMessageSetExtension1.messageSetExtension).getI()); 416 assertEquals("foo", messageSet.getExtension( 417 TestMessageSetExtension2.messageSetExtension).getStr()); 418 419 builder = TestMessageSet.newBuilder(); 420 try { 421 parserWithOverwriteForbidden.merge( 422 messageSetTextWithRepeatedExtension, extensionRegistry, builder); 423 fail("expected parse exception"); 424 } catch (TextFormat.ParseException e) { 425 assertEquals("6:1: Non-repeated field " 426 + "\"protobuf_unittest.TestMessageSetExtension1.message_set_extension\"" 427 + " cannot be overwritten.", 428 e.getMessage()); 429 } 430 } 431 testParseNumericEnum()432 public void testParseNumericEnum() throws Exception { 433 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 434 TextFormat.merge("optional_nested_enum: 2", builder); 435 assertEquals(TestAllTypes.NestedEnum.BAR, builder.getOptionalNestedEnum()); 436 } 437 testParseAngleBrackets()438 public void testParseAngleBrackets() throws Exception { 439 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 440 TextFormat.merge("OptionalGroup: < a: 1 >", builder); 441 assertTrue(builder.hasOptionalGroup()); 442 assertEquals(1, builder.getOptionalGroup().getA()); 443 } 444 testParseComment()445 public void testParseComment() throws Exception { 446 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 447 TextFormat.merge( 448 "# this is a comment\n" + 449 "optional_int32: 1 # another comment\n" + 450 "optional_int64: 2\n" + 451 "# EOF comment", builder); 452 assertEquals(1, builder.getOptionalInt32()); 453 assertEquals(2, builder.getOptionalInt64()); 454 } 455 assertParseError(String error, String text)456 private void assertParseError(String error, String text) { 457 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 458 try { 459 TextFormat.merge(text, TestUtil.getExtensionRegistry(), builder); 460 fail("Expected parse exception."); 461 } catch (TextFormat.ParseException e) { 462 assertEquals(error, e.getMessage()); 463 } 464 } 465 466 assertParseErrorWithOverwriteForbidden(String error, String text)467 private void assertParseErrorWithOverwriteForbidden(String error, 468 String text) { 469 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 470 try { 471 parserWithOverwriteForbidden.merge( 472 text, TestUtil.getExtensionRegistry(), builder); 473 fail("Expected parse exception."); 474 } catch (TextFormat.ParseException e) { 475 assertEquals(error, e.getMessage()); 476 } 477 } 478 assertParseSuccessWithOverwriteForbidden( String text)479 private TestAllTypes assertParseSuccessWithOverwriteForbidden( 480 String text) throws TextFormat.ParseException { 481 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 482 parserWithOverwriteForbidden.merge( 483 text, TestUtil.getExtensionRegistry(), builder); 484 return builder.build(); 485 } 486 testParseErrors()487 public void testParseErrors() throws Exception { 488 assertParseError( 489 "1:16: Expected \":\".", 490 "optional_int32 123"); 491 assertParseError( 492 "1:23: Expected identifier. Found '?'", 493 "optional_nested_enum: ?"); 494 assertParseError( 495 "1:18: Couldn't parse integer: Number must be positive: -1", 496 "optional_uint32: -1"); 497 assertParseError( 498 "1:17: Couldn't parse integer: Number out of range for 32-bit signed " + 499 "integer: 82301481290849012385230157", 500 "optional_int32: 82301481290849012385230157"); 501 assertParseError( 502 "1:16: Expected \"true\" or \"false\".", 503 "optional_bool: maybe"); 504 assertParseError( 505 "1:16: Expected \"true\" or \"false\".", 506 "optional_bool: 2"); 507 assertParseError( 508 "1:18: Expected string.", 509 "optional_string: 123"); 510 assertParseError( 511 "1:18: String missing ending quote.", 512 "optional_string: \"ueoauaoe"); 513 assertParseError( 514 "1:18: String missing ending quote.", 515 "optional_string: \"ueoauaoe\n" + 516 "optional_int32: 123"); 517 assertParseError( 518 "1:18: Invalid escape sequence: '\\z'", 519 "optional_string: \"\\z\""); 520 assertParseError( 521 "1:18: String missing ending quote.", 522 "optional_string: \"ueoauaoe\n" + 523 "optional_int32: 123"); 524 assertParseError( 525 "1:2: Input contains unknown fields and/or extensions:\n" + 526 "1:2:\tprotobuf_unittest.TestAllTypes.[nosuchext]", 527 "[nosuchext]: 123"); 528 assertParseError( 529 "1:20: Extension \"protobuf_unittest.optional_int32_extension\" does " + 530 "not extend message type \"protobuf_unittest.TestAllTypes\".", 531 "[protobuf_unittest.optional_int32_extension]: 123"); 532 assertParseError( 533 "1:1: Input contains unknown fields and/or extensions:\n" + 534 "1:1:\tprotobuf_unittest.TestAllTypes.nosuchfield", 535 "nosuchfield: 123"); 536 assertParseError( 537 "1:21: Expected \">\".", 538 "OptionalGroup < a: 1"); 539 assertParseError( 540 "1:23: Enum type \"protobuf_unittest.TestAllTypes.NestedEnum\" has no " + 541 "value named \"NO_SUCH_VALUE\".", 542 "optional_nested_enum: NO_SUCH_VALUE"); 543 assertParseError( 544 "1:23: Enum type \"protobuf_unittest.TestAllTypes.NestedEnum\" has no " + 545 "value with number 123.", 546 "optional_nested_enum: 123"); 547 548 // Delimiters must match. 549 assertParseError( 550 "1:22: Expected identifier. Found '}'", 551 "OptionalGroup < a: 1 }"); 552 assertParseError( 553 "1:22: Expected identifier. Found '>'", 554 "OptionalGroup { a: 1 >"); 555 } 556 557 // ================================================================= 558 testEscape()559 public void testEscape() throws Exception { 560 // Escape sequences. 561 assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\\177", 562 TextFormat.escapeBytes(bytes("\0\001\007\b\f\n\r\t\013\\\'\"\177"))); 563 assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\\177", 564 TextFormat.escapeText("\0\001\007\b\f\n\r\t\013\\\'\"\177")); 565 assertEquals(bytes("\0\001\007\b\f\n\r\t\013\\\'\""), 566 TextFormat.unescapeBytes("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"")); 567 assertEquals("\0\001\007\b\f\n\r\t\013\\\'\"", 568 TextFormat.unescapeText("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"")); 569 assertEquals(kEscapeTestStringEscaped, 570 TextFormat.escapeText(kEscapeTestString)); 571 assertEquals(kEscapeTestString, 572 TextFormat.unescapeText(kEscapeTestStringEscaped)); 573 574 // Invariant 575 assertEquals("hello", 576 TextFormat.escapeBytes(bytes("hello"))); 577 assertEquals("hello", 578 TextFormat.escapeText("hello")); 579 assertEquals(bytes("hello"), 580 TextFormat.unescapeBytes("hello")); 581 assertEquals("hello", 582 TextFormat.unescapeText("hello")); 583 584 // Unicode handling. 585 assertEquals("\\341\\210\\264", TextFormat.escapeText("\u1234")); 586 assertEquals("\\341\\210\\264", 587 TextFormat.escapeBytes(bytes(0xe1, 0x88, 0xb4))); 588 assertEquals("\u1234", TextFormat.unescapeText("\\341\\210\\264")); 589 assertEquals(bytes(0xe1, 0x88, 0xb4), 590 TextFormat.unescapeBytes("\\341\\210\\264")); 591 assertEquals("\u1234", TextFormat.unescapeText("\\xe1\\x88\\xb4")); 592 assertEquals(bytes(0xe1, 0x88, 0xb4), 593 TextFormat.unescapeBytes("\\xe1\\x88\\xb4")); 594 595 // Handling of strings with unescaped Unicode characters > 255. 596 final String zh = "\u9999\u6e2f\u4e0a\u6d77\ud84f\udf80\u8c50\u9280\u884c"; 597 ByteString zhByteString = ByteString.copyFromUtf8(zh); 598 assertEquals(zhByteString, TextFormat.unescapeBytes(zh)); 599 600 // Errors. 601 try { 602 TextFormat.unescapeText("\\x"); 603 fail("Should have thrown an exception."); 604 } catch (TextFormat.InvalidEscapeSequenceException e) { 605 // success 606 } 607 608 try { 609 TextFormat.unescapeText("\\z"); 610 fail("Should have thrown an exception."); 611 } catch (TextFormat.InvalidEscapeSequenceException e) { 612 // success 613 } 614 615 try { 616 TextFormat.unescapeText("\\"); 617 fail("Should have thrown an exception."); 618 } catch (TextFormat.InvalidEscapeSequenceException e) { 619 // success 620 } 621 } 622 testParseInteger()623 public void testParseInteger() throws Exception { 624 assertEquals( 0, TextFormat.parseInt32( "0")); 625 assertEquals( 1, TextFormat.parseInt32( "1")); 626 assertEquals( -1, TextFormat.parseInt32( "-1")); 627 assertEquals( 12345, TextFormat.parseInt32( "12345")); 628 assertEquals( -12345, TextFormat.parseInt32( "-12345")); 629 assertEquals( 2147483647, TextFormat.parseInt32( "2147483647")); 630 assertEquals(-2147483648, TextFormat.parseInt32("-2147483648")); 631 632 assertEquals( 0, TextFormat.parseUInt32( "0")); 633 assertEquals( 1, TextFormat.parseUInt32( "1")); 634 assertEquals( 12345, TextFormat.parseUInt32( "12345")); 635 assertEquals( 2147483647, TextFormat.parseUInt32("2147483647")); 636 assertEquals((int) 2147483648L, TextFormat.parseUInt32("2147483648")); 637 assertEquals((int) 4294967295L, TextFormat.parseUInt32("4294967295")); 638 639 assertEquals( 0L, TextFormat.parseInt64( "0")); 640 assertEquals( 1L, TextFormat.parseInt64( "1")); 641 assertEquals( -1L, TextFormat.parseInt64( "-1")); 642 assertEquals( 12345L, TextFormat.parseInt64( "12345")); 643 assertEquals( -12345L, TextFormat.parseInt64( "-12345")); 644 assertEquals( 2147483647L, TextFormat.parseInt64( "2147483647")); 645 assertEquals(-2147483648L, TextFormat.parseInt64("-2147483648")); 646 assertEquals( 4294967295L, TextFormat.parseInt64( "4294967295")); 647 assertEquals( 4294967296L, TextFormat.parseInt64( "4294967296")); 648 assertEquals(9223372036854775807L, 649 TextFormat.parseInt64("9223372036854775807")); 650 assertEquals(-9223372036854775808L, 651 TextFormat.parseInt64("-9223372036854775808")); 652 653 assertEquals( 0L, TextFormat.parseUInt64( "0")); 654 assertEquals( 1L, TextFormat.parseUInt64( "1")); 655 assertEquals( 12345L, TextFormat.parseUInt64( "12345")); 656 assertEquals( 2147483647L, TextFormat.parseUInt64( "2147483647")); 657 assertEquals( 4294967295L, TextFormat.parseUInt64( "4294967295")); 658 assertEquals( 4294967296L, TextFormat.parseUInt64( "4294967296")); 659 assertEquals(9223372036854775807L, 660 TextFormat.parseUInt64("9223372036854775807")); 661 assertEquals(-9223372036854775808L, 662 TextFormat.parseUInt64("9223372036854775808")); 663 assertEquals(-1L, TextFormat.parseUInt64("18446744073709551615")); 664 665 // Hex 666 assertEquals(0x1234abcd, TextFormat.parseInt32("0x1234abcd")); 667 assertEquals(-0x1234abcd, TextFormat.parseInt32("-0x1234abcd")); 668 assertEquals(-1, TextFormat.parseUInt64("0xffffffffffffffff")); 669 assertEquals(0x7fffffffffffffffL, 670 TextFormat.parseInt64("0x7fffffffffffffff")); 671 672 // Octal 673 assertEquals(01234567, TextFormat.parseInt32("01234567")); 674 675 // Out-of-range 676 try { 677 TextFormat.parseInt32("2147483648"); 678 fail("Should have thrown an exception."); 679 } catch (NumberFormatException e) { 680 // success 681 } 682 683 try { 684 TextFormat.parseInt32("-2147483649"); 685 fail("Should have thrown an exception."); 686 } catch (NumberFormatException e) { 687 // success 688 } 689 690 try { 691 TextFormat.parseUInt32("4294967296"); 692 fail("Should have thrown an exception."); 693 } catch (NumberFormatException e) { 694 // success 695 } 696 697 try { 698 TextFormat.parseUInt32("-1"); 699 fail("Should have thrown an exception."); 700 } catch (NumberFormatException e) { 701 // success 702 } 703 704 try { 705 TextFormat.parseInt64("9223372036854775808"); 706 fail("Should have thrown an exception."); 707 } catch (NumberFormatException e) { 708 // success 709 } 710 711 try { 712 TextFormat.parseInt64("-9223372036854775809"); 713 fail("Should have thrown an exception."); 714 } catch (NumberFormatException e) { 715 // success 716 } 717 718 try { 719 TextFormat.parseUInt64("18446744073709551616"); 720 fail("Should have thrown an exception."); 721 } catch (NumberFormatException e) { 722 // success 723 } 724 725 try { 726 TextFormat.parseUInt64("-1"); 727 fail("Should have thrown an exception."); 728 } catch (NumberFormatException e) { 729 // success 730 } 731 732 // Not a number. 733 try { 734 TextFormat.parseInt32("abcd"); 735 fail("Should have thrown an exception."); 736 } catch (NumberFormatException e) { 737 // success 738 } 739 } 740 testParseString()741 public void testParseString() throws Exception { 742 final String zh = "\u9999\u6e2f\u4e0a\u6d77\ud84f\udf80\u8c50\u9280\u884c"; 743 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 744 TextFormat.merge("optional_string: \"" + zh + "\"", builder); 745 assertEquals(zh, builder.getOptionalString()); 746 } 747 testParseLongString()748 public void testParseLongString() throws Exception { 749 String longText = 750 "123456789012345678901234567890123456789012345678901234567890" + 751 "123456789012345678901234567890123456789012345678901234567890" + 752 "123456789012345678901234567890123456789012345678901234567890" + 753 "123456789012345678901234567890123456789012345678901234567890" + 754 "123456789012345678901234567890123456789012345678901234567890" + 755 "123456789012345678901234567890123456789012345678901234567890" + 756 "123456789012345678901234567890123456789012345678901234567890" + 757 "123456789012345678901234567890123456789012345678901234567890" + 758 "123456789012345678901234567890123456789012345678901234567890" + 759 "123456789012345678901234567890123456789012345678901234567890" + 760 "123456789012345678901234567890123456789012345678901234567890" + 761 "123456789012345678901234567890123456789012345678901234567890" + 762 "123456789012345678901234567890123456789012345678901234567890" + 763 "123456789012345678901234567890123456789012345678901234567890" + 764 "123456789012345678901234567890123456789012345678901234567890" + 765 "123456789012345678901234567890123456789012345678901234567890" + 766 "123456789012345678901234567890123456789012345678901234567890" + 767 "123456789012345678901234567890123456789012345678901234567890" + 768 "123456789012345678901234567890123456789012345678901234567890" + 769 "123456789012345678901234567890123456789012345678901234567890"; 770 771 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 772 TextFormat.merge("optional_string: \"" + longText + "\"", builder); 773 assertEquals(longText, builder.getOptionalString()); 774 } 775 testParseBoolean()776 public void testParseBoolean() throws Exception { 777 String goodText = 778 "repeated_bool: t repeated_bool : 0\n" + 779 "repeated_bool :f repeated_bool:1\n" + 780 "repeated_bool: False repeated_bool: True"; 781 String goodTextCanonical = 782 "repeated_bool: true\n" + 783 "repeated_bool: false\n" + 784 "repeated_bool: false\n" + 785 "repeated_bool: true\n" + 786 "repeated_bool: false\n" + 787 "repeated_bool: true\n"; 788 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 789 TextFormat.merge(goodText, builder); 790 assertEquals(goodTextCanonical, builder.build().toString()); 791 792 try { 793 TestAllTypes.Builder badBuilder = TestAllTypes.newBuilder(); 794 TextFormat.merge("optional_bool:2", badBuilder); 795 fail("Should have thrown an exception."); 796 } catch (TextFormat.ParseException e) { 797 // success 798 } 799 try { 800 TestAllTypes.Builder badBuilder = TestAllTypes.newBuilder(); 801 TextFormat.merge("optional_bool: foo", badBuilder); 802 fail("Should have thrown an exception."); 803 } catch (TextFormat.ParseException e) { 804 // success 805 } 806 } 807 testParseAdjacentStringLiterals()808 public void testParseAdjacentStringLiterals() throws Exception { 809 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 810 TextFormat.merge("optional_string: \"foo\" 'corge' \"grault\"", builder); 811 assertEquals("foocorgegrault", builder.getOptionalString()); 812 } 813 testPrintFieldValue()814 public void testPrintFieldValue() throws Exception { 815 assertPrintFieldValue("\"Hello\"", "Hello", "repeated_string"); 816 assertPrintFieldValue("123.0", 123f, "repeated_float"); 817 assertPrintFieldValue("123.0", 123d, "repeated_double"); 818 assertPrintFieldValue("123", 123, "repeated_int32"); 819 assertPrintFieldValue("123", 123L, "repeated_int64"); 820 assertPrintFieldValue("true", true, "repeated_bool"); 821 assertPrintFieldValue("4294967295", 0xFFFFFFFF, "repeated_uint32"); 822 assertPrintFieldValue("18446744073709551615", 0xFFFFFFFFFFFFFFFFL, 823 "repeated_uint64"); 824 assertPrintFieldValue("\"\\001\\002\\003\"", 825 ByteString.copyFrom(new byte[] {1, 2, 3}), "repeated_bytes"); 826 } 827 assertPrintFieldValue(String expect, Object value, String fieldName)828 private void assertPrintFieldValue(String expect, Object value, 829 String fieldName) throws Exception { 830 StringBuilder sb = new StringBuilder(); 831 TextFormat.printFieldValue( 832 TestAllTypes.getDescriptor().findFieldByName(fieldName), 833 value, sb); 834 assertEquals(expect, sb.toString()); 835 } 836 testShortDebugString()837 public void testShortDebugString() { 838 assertEquals("optional_nested_message { bb: 42 } repeated_int32: 1" 839 + " repeated_uint32: 2", 840 TextFormat.shortDebugString(TestAllTypes.newBuilder() 841 .addRepeatedInt32(1) 842 .addRepeatedUint32(2) 843 .setOptionalNestedMessage( 844 NestedMessage.newBuilder().setBb(42).build()) 845 .build())); 846 } 847 testShortDebugString_field()848 public void testShortDebugString_field() { 849 final FieldDescriptor dataField = 850 OneString.getDescriptor().findFieldByName("data"); 851 assertEquals( 852 "data: \"test data\"", 853 TextFormat.shortDebugString(dataField, "test data")); 854 855 final FieldDescriptor optionalField = 856 TestAllTypes.getDescriptor().findFieldByName("optional_nested_message"); 857 final Object value = NestedMessage.newBuilder().setBb(42).build(); 858 859 assertEquals( 860 "optional_nested_message { bb: 42 }", 861 TextFormat.shortDebugString(optionalField, value)); 862 } 863 testShortDebugString_unknown()864 public void testShortDebugString_unknown() { 865 assertEquals("5: 1 5: 0x00000002 5: 0x0000000000000003 5: \"4\" 5 { 10: 5 }" 866 + " 8: 1 8: 2 8: 3 15: 12379813812177893520 15: 0xabcd1234 15:" 867 + " 0xabcdef1234567890", 868 TextFormat.shortDebugString(makeUnknownFieldSet())); 869 } 870 testPrintToUnicodeString()871 public void testPrintToUnicodeString() throws Exception { 872 assertEquals( 873 "optional_string: \"abc\u3042efg\"\n" + 874 "optional_bytes: \"\\343\\201\\202\"\n" + 875 "repeated_string: \"\u3093XYZ\"\n", 876 TextFormat.printToUnicodeString(TestAllTypes.newBuilder() 877 .setOptionalString("abc\u3042efg") 878 .setOptionalBytes(bytes(0xe3, 0x81, 0x82)) 879 .addRepeatedString("\u3093XYZ") 880 .build())); 881 882 // Double quotes and backslashes should be escaped 883 assertEquals( 884 "optional_string: \"a\\\\bc\\\"ef\\\"g\"\n", 885 TextFormat.printToUnicodeString(TestAllTypes.newBuilder() 886 .setOptionalString("a\\bc\"ef\"g") 887 .build())); 888 889 // Test escaping roundtrip 890 TestAllTypes message = TestAllTypes.newBuilder() 891 .setOptionalString("a\\bc\\\"ef\"g") 892 .build(); 893 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 894 TextFormat.merge(TextFormat.printToUnicodeString(message), builder); 895 assertEquals(message.getOptionalString(), builder.getOptionalString()); 896 } 897 testPrintToUnicodeStringWithNewlines()898 public void testPrintToUnicodeStringWithNewlines() throws Exception { 899 // No newlines at start and end 900 assertEquals("optional_string: \"test newlines\\n\\nin\\nstring\"\n", 901 TextFormat.printToUnicodeString(TestAllTypes.newBuilder() 902 .setOptionalString("test newlines\n\nin\nstring") 903 .build())); 904 905 // Newlines at start and end 906 assertEquals("optional_string: \"\\ntest\\nnewlines\\n\\nin\\nstring\\n\"\n", 907 TextFormat.printToUnicodeString(TestAllTypes.newBuilder() 908 .setOptionalString("\ntest\nnewlines\n\nin\nstring\n") 909 .build())); 910 911 // Strings with 0, 1 and 2 newlines. 912 assertEquals("optional_string: \"\"\n", 913 TextFormat.printToUnicodeString(TestAllTypes.newBuilder() 914 .setOptionalString("") 915 .build())); 916 assertEquals("optional_string: \"\\n\"\n", 917 TextFormat.printToUnicodeString(TestAllTypes.newBuilder() 918 .setOptionalString("\n") 919 .build())); 920 assertEquals("optional_string: \"\\n\\n\"\n", 921 TextFormat.printToUnicodeString(TestAllTypes.newBuilder() 922 .setOptionalString("\n\n") 923 .build())); 924 925 // Test escaping roundtrip 926 TestAllTypes message = TestAllTypes.newBuilder() 927 .setOptionalString("\ntest\nnewlines\n\nin\nstring\n") 928 .build(); 929 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 930 TextFormat.merge(TextFormat.printToUnicodeString(message), builder); 931 assertEquals(message.getOptionalString(), builder.getOptionalString()); 932 } 933 testPrintToUnicodeString_unknown()934 public void testPrintToUnicodeString_unknown() { 935 assertEquals( 936 "1: \"\\343\\201\\202\"\n", 937 TextFormat.printToUnicodeString(UnknownFieldSet.newBuilder() 938 .addField(1, 939 UnknownFieldSet.Field.newBuilder() 940 .addLengthDelimited(bytes(0xe3, 0x81, 0x82)).build()) 941 .build())); 942 } 943 944 testParseNonRepeatedFields()945 public void testParseNonRepeatedFields() throws Exception { 946 assertParseSuccessWithOverwriteForbidden( 947 "repeated_int32: 1\n" + 948 "repeated_int32: 2\n"); 949 assertParseSuccessWithOverwriteForbidden( 950 "RepeatedGroup { a: 1 }\n" + 951 "RepeatedGroup { a: 2 }\n"); 952 assertParseSuccessWithOverwriteForbidden( 953 "repeated_nested_message { bb: 1 }\n" + 954 "repeated_nested_message { bb: 2 }\n"); 955 assertParseErrorWithOverwriteForbidden( 956 "3:17: Non-repeated field " + 957 "\"protobuf_unittest.TestAllTypes.optional_int32\" " + 958 "cannot be overwritten.", 959 "optional_int32: 1\n" + 960 "optional_bool: true\n" + 961 "optional_int32: 1\n"); 962 assertParseErrorWithOverwriteForbidden( 963 "2:17: Non-repeated field " + 964 "\"protobuf_unittest.TestAllTypes.optionalgroup\" " + 965 "cannot be overwritten.", 966 "OptionalGroup { a: 1 }\n" + 967 "OptionalGroup { }\n"); 968 assertParseErrorWithOverwriteForbidden( 969 "2:33: Non-repeated field " + 970 "\"protobuf_unittest.TestAllTypes.optional_nested_message\" " + 971 "cannot be overwritten.", 972 "optional_nested_message { }\n" + 973 "optional_nested_message { bb: 3 }\n"); 974 assertParseErrorWithOverwriteForbidden( 975 "2:16: Non-repeated field " + 976 "\"protobuf_unittest.TestAllTypes.default_int32\" " + 977 "cannot be overwritten.", 978 "default_int32: 41\n" + // the default value 979 "default_int32: 41\n"); 980 assertParseErrorWithOverwriteForbidden( 981 "2:17: Non-repeated field " + 982 "\"protobuf_unittest.TestAllTypes.default_string\" " + 983 "cannot be overwritten.", 984 "default_string: \"zxcv\"\n" + 985 "default_string: \"asdf\"\n"); 986 } 987 testParseShortRepeatedFormOfRepeatedFields()988 public void testParseShortRepeatedFormOfRepeatedFields() throws Exception { 989 assertParseSuccessWithOverwriteForbidden("repeated_foreign_enum: [FOREIGN_FOO, FOREIGN_BAR]"); 990 assertParseSuccessWithOverwriteForbidden("repeated_int32: [ 1, 2 ]\n"); 991 assertParseSuccessWithOverwriteForbidden("RepeatedGroup [{ a: 1 },{ a: 2 }]\n"); 992 assertParseSuccessWithOverwriteForbidden("repeated_nested_message [{ bb: 1 }, { bb: 2 }]\n"); 993 } 994 testParseShortRepeatedFormOfNonRepeatedFields()995 public void testParseShortRepeatedFormOfNonRepeatedFields() throws Exception { 996 assertParseErrorWithOverwriteForbidden( 997 "1:17: Couldn't parse integer: For input string: \"[\"", 998 "optional_int32: [1]\n"); 999 } 1000 1001 // ======================================================================= 1002 // test oneof 1003 testOneofTextFormat()1004 public void testOneofTextFormat() throws Exception { 1005 TestOneof2.Builder builder = TestOneof2.newBuilder(); 1006 TestUtil.setOneof(builder); 1007 TestOneof2 message = builder.build(); 1008 TestOneof2.Builder dest = TestOneof2.newBuilder(); 1009 TextFormat.merge(TextFormat.printToUnicodeString(message), dest); 1010 TestUtil.assertOneofSet(dest.build()); 1011 } 1012 testOneofOverwriteForbidden()1013 public void testOneofOverwriteForbidden() throws Exception { 1014 String input = "foo_string: \"stringvalue\" foo_int: 123"; 1015 TestOneof2.Builder builder = TestOneof2.newBuilder(); 1016 try { 1017 parserWithOverwriteForbidden.merge( 1018 input, TestUtil.getExtensionRegistry(), builder); 1019 fail("Expected parse exception."); 1020 } catch (TextFormat.ParseException e) { 1021 assertEquals("1:36: Field \"protobuf_unittest.TestOneof2.foo_int\"" 1022 + " is specified along with field \"protobuf_unittest.TestOneof2.foo_string\"," 1023 + " another member of oneof \"foo\".", e.getMessage()); 1024 } 1025 } 1026 testOneofOverwriteAllowed()1027 public void testOneofOverwriteAllowed() throws Exception { 1028 String input = "foo_string: \"stringvalue\" foo_int: 123"; 1029 TestOneof2.Builder builder = TestOneof2.newBuilder(); 1030 defaultParser.merge(input, TestUtil.getExtensionRegistry(), builder); 1031 // Only the last value sticks. 1032 TestOneof2 oneof = builder.build(); 1033 assertFalse(oneof.hasFooString()); 1034 assertTrue(oneof.hasFooInt()); 1035 } 1036 1037 // ======================================================================= 1038 // test location information 1039 testParseInfoTreeBuilding()1040 public void testParseInfoTreeBuilding() throws Exception { 1041 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 1042 1043 Descriptor descriptor = TestAllTypes.getDescriptor(); 1044 TextFormatParseInfoTree.Builder treeBuilder = TextFormatParseInfoTree.builder(); 1045 // Set to allow unknown fields 1046 TextFormat.Parser parser = 1047 TextFormat.Parser.newBuilder() 1048 .setParseInfoTreeBuilder(treeBuilder) 1049 .build(); 1050 1051 final String stringData = 1052 "optional_int32: 1\n" 1053 + "optional_int64: 2\n" 1054 + " optional_double: 2.4\n" 1055 + "repeated_int32: 5\n" 1056 + "repeated_int32: 10\n" 1057 + "optional_nested_message <\n" 1058 + " bb: 78\n" 1059 + ">\n" 1060 + "repeated_nested_message <\n" 1061 + " bb: 79\n" 1062 + ">\n" 1063 + "repeated_nested_message <\n" 1064 + " bb: 80\n" 1065 + ">"; 1066 1067 parser.merge(stringData, builder); 1068 TextFormatParseInfoTree tree = treeBuilder.build(); 1069 1070 // Verify that the tree has the correct positions. 1071 assertLocation(tree, descriptor, "optional_int32", 0, 0, 0); 1072 assertLocation(tree, descriptor, "optional_int64", 0, 1, 0); 1073 assertLocation(tree, descriptor, "optional_double", 0, 2, 2); 1074 1075 assertLocation(tree, descriptor, "repeated_int32", 0, 3, 0); 1076 assertLocation(tree, descriptor, "repeated_int32", 1, 4, 0); 1077 1078 assertLocation(tree, descriptor, "optional_nested_message", 0, 5, 0); 1079 assertLocation(tree, descriptor, "repeated_nested_message", 0, 8, 0); 1080 assertLocation(tree, descriptor, "repeated_nested_message", 1, 11, 0); 1081 1082 // Check for fields not set. For an invalid field, the location returned should be -1, -1. 1083 assertLocation(tree, descriptor, "repeated_int64", 0, -1, -1); 1084 assertLocation(tree, descriptor, "repeated_int32", 6, -1, -1); 1085 1086 // Verify inside the nested message. 1087 FieldDescriptor nestedField = descriptor.findFieldByName("optional_nested_message"); 1088 1089 TextFormatParseInfoTree nestedTree = tree.getNestedTrees(nestedField).get(0); 1090 assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 6, 2); 1091 1092 // Verify inside another nested message. 1093 nestedField = descriptor.findFieldByName("repeated_nested_message"); 1094 nestedTree = tree.getNestedTrees(nestedField).get(0); 1095 assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 9, 2); 1096 1097 nestedTree = tree.getNestedTrees(nestedField).get(1); 1098 assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 12, 2); 1099 1100 // Verify a NULL tree for an unknown nested field. 1101 try { 1102 tree.getNestedTree(nestedField, 2); 1103 fail("unknown nested field should throw"); 1104 } catch (IllegalArgumentException unused) { 1105 // pass 1106 } 1107 } 1108 assertLocation( TextFormatParseInfoTree tree, final Descriptor descriptor, final String fieldName, int index, int line, int column)1109 private void assertLocation( 1110 TextFormatParseInfoTree tree, 1111 final Descriptor descriptor, 1112 final String fieldName, 1113 int index, 1114 int line, 1115 int column) { 1116 List<TextFormatParseLocation> locs = tree.getLocations(descriptor.findFieldByName(fieldName)); 1117 if (index < locs.size()) { 1118 TextFormatParseLocation location = locs.get(index); 1119 TextFormatParseLocation expected = TextFormatParseLocation.create(line, column); 1120 assertEquals(expected, location); 1121 } else if (line != -1 && column != -1) { 1122 fail( 1123 String.format( 1124 "Tree/descriptor/fieldname did not contain index %d, line %d column %d expected", 1125 index, 1126 line, 1127 column)); 1128 } 1129 } 1130 } 1131