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: Extension \"nosuchext\" not found in the ExtensionRegistry.", 526 "[nosuchext]: 123"); 527 assertParseError( 528 "1:20: Extension \"protobuf_unittest.optional_int32_extension\" does " + 529 "not extend message type \"protobuf_unittest.TestAllTypes\".", 530 "[protobuf_unittest.optional_int32_extension]: 123"); 531 assertParseError( 532 "1:1: Message type \"protobuf_unittest.TestAllTypes\" has no field " + 533 "named \"nosuchfield\".", 534 "nosuchfield: 123"); 535 assertParseError( 536 "1:21: Expected \">\".", 537 "OptionalGroup < a: 1"); 538 assertParseError( 539 "1:23: Enum type \"protobuf_unittest.TestAllTypes.NestedEnum\" has no " + 540 "value named \"NO_SUCH_VALUE\".", 541 "optional_nested_enum: NO_SUCH_VALUE"); 542 assertParseError( 543 "1:23: Enum type \"protobuf_unittest.TestAllTypes.NestedEnum\" has no " + 544 "value with number 123.", 545 "optional_nested_enum: 123"); 546 547 // Delimiters must match. 548 assertParseError( 549 "1:22: Expected identifier. Found '}'", 550 "OptionalGroup < a: 1 }"); 551 assertParseError( 552 "1:22: Expected identifier. Found '>'", 553 "OptionalGroup { a: 1 >"); 554 } 555 556 // ================================================================= 557 testEscape()558 public void testEscape() throws Exception { 559 // Escape sequences. 560 assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\\177", 561 TextFormat.escapeBytes(bytes("\0\001\007\b\f\n\r\t\013\\\'\"\177"))); 562 assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\\177", 563 TextFormat.escapeText("\0\001\007\b\f\n\r\t\013\\\'\"\177")); 564 assertEquals(bytes("\0\001\007\b\f\n\r\t\013\\\'\""), 565 TextFormat.unescapeBytes("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"")); 566 assertEquals("\0\001\007\b\f\n\r\t\013\\\'\"", 567 TextFormat.unescapeText("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"")); 568 assertEquals(kEscapeTestStringEscaped, 569 TextFormat.escapeText(kEscapeTestString)); 570 assertEquals(kEscapeTestString, 571 TextFormat.unescapeText(kEscapeTestStringEscaped)); 572 573 // Invariant 574 assertEquals("hello", 575 TextFormat.escapeBytes(bytes("hello"))); 576 assertEquals("hello", 577 TextFormat.escapeText("hello")); 578 assertEquals(bytes("hello"), 579 TextFormat.unescapeBytes("hello")); 580 assertEquals("hello", 581 TextFormat.unescapeText("hello")); 582 583 // Unicode handling. 584 assertEquals("\\341\\210\\264", TextFormat.escapeText("\u1234")); 585 assertEquals("\\341\\210\\264", 586 TextFormat.escapeBytes(bytes(0xe1, 0x88, 0xb4))); 587 assertEquals("\u1234", TextFormat.unescapeText("\\341\\210\\264")); 588 assertEquals(bytes(0xe1, 0x88, 0xb4), 589 TextFormat.unescapeBytes("\\341\\210\\264")); 590 assertEquals("\u1234", TextFormat.unescapeText("\\xe1\\x88\\xb4")); 591 assertEquals(bytes(0xe1, 0x88, 0xb4), 592 TextFormat.unescapeBytes("\\xe1\\x88\\xb4")); 593 594 // Handling of strings with unescaped Unicode characters > 255. 595 final String zh = "\u9999\u6e2f\u4e0a\u6d77\ud84f\udf80\u8c50\u9280\u884c"; 596 ByteString zhByteString = ByteString.copyFromUtf8(zh); 597 assertEquals(zhByteString, TextFormat.unescapeBytes(zh)); 598 599 // Errors. 600 try { 601 TextFormat.unescapeText("\\x"); 602 fail("Should have thrown an exception."); 603 } catch (TextFormat.InvalidEscapeSequenceException e) { 604 // success 605 } 606 607 try { 608 TextFormat.unescapeText("\\z"); 609 fail("Should have thrown an exception."); 610 } catch (TextFormat.InvalidEscapeSequenceException e) { 611 // success 612 } 613 614 try { 615 TextFormat.unescapeText("\\"); 616 fail("Should have thrown an exception."); 617 } catch (TextFormat.InvalidEscapeSequenceException e) { 618 // success 619 } 620 } 621 testParseInteger()622 public void testParseInteger() throws Exception { 623 assertEquals( 0, TextFormat.parseInt32( "0")); 624 assertEquals( 1, TextFormat.parseInt32( "1")); 625 assertEquals( -1, TextFormat.parseInt32( "-1")); 626 assertEquals( 12345, TextFormat.parseInt32( "12345")); 627 assertEquals( -12345, TextFormat.parseInt32( "-12345")); 628 assertEquals( 2147483647, TextFormat.parseInt32( "2147483647")); 629 assertEquals(-2147483648, TextFormat.parseInt32("-2147483648")); 630 631 assertEquals( 0, TextFormat.parseUInt32( "0")); 632 assertEquals( 1, TextFormat.parseUInt32( "1")); 633 assertEquals( 12345, TextFormat.parseUInt32( "12345")); 634 assertEquals( 2147483647, TextFormat.parseUInt32("2147483647")); 635 assertEquals((int) 2147483648L, TextFormat.parseUInt32("2147483648")); 636 assertEquals((int) 4294967295L, TextFormat.parseUInt32("4294967295")); 637 638 assertEquals( 0L, TextFormat.parseInt64( "0")); 639 assertEquals( 1L, TextFormat.parseInt64( "1")); 640 assertEquals( -1L, TextFormat.parseInt64( "-1")); 641 assertEquals( 12345L, TextFormat.parseInt64( "12345")); 642 assertEquals( -12345L, TextFormat.parseInt64( "-12345")); 643 assertEquals( 2147483647L, TextFormat.parseInt64( "2147483647")); 644 assertEquals(-2147483648L, TextFormat.parseInt64("-2147483648")); 645 assertEquals( 4294967295L, TextFormat.parseInt64( "4294967295")); 646 assertEquals( 4294967296L, TextFormat.parseInt64( "4294967296")); 647 assertEquals(9223372036854775807L, 648 TextFormat.parseInt64("9223372036854775807")); 649 assertEquals(-9223372036854775808L, 650 TextFormat.parseInt64("-9223372036854775808")); 651 652 assertEquals( 0L, TextFormat.parseUInt64( "0")); 653 assertEquals( 1L, TextFormat.parseUInt64( "1")); 654 assertEquals( 12345L, TextFormat.parseUInt64( "12345")); 655 assertEquals( 2147483647L, TextFormat.parseUInt64( "2147483647")); 656 assertEquals( 4294967295L, TextFormat.parseUInt64( "4294967295")); 657 assertEquals( 4294967296L, TextFormat.parseUInt64( "4294967296")); 658 assertEquals(9223372036854775807L, 659 TextFormat.parseUInt64("9223372036854775807")); 660 assertEquals(-9223372036854775808L, 661 TextFormat.parseUInt64("9223372036854775808")); 662 assertEquals(-1L, TextFormat.parseUInt64("18446744073709551615")); 663 664 // Hex 665 assertEquals(0x1234abcd, TextFormat.parseInt32("0x1234abcd")); 666 assertEquals(-0x1234abcd, TextFormat.parseInt32("-0x1234abcd")); 667 assertEquals(-1, TextFormat.parseUInt64("0xffffffffffffffff")); 668 assertEquals(0x7fffffffffffffffL, 669 TextFormat.parseInt64("0x7fffffffffffffff")); 670 671 // Octal 672 assertEquals(01234567, TextFormat.parseInt32("01234567")); 673 674 // Out-of-range 675 try { 676 TextFormat.parseInt32("2147483648"); 677 fail("Should have thrown an exception."); 678 } catch (NumberFormatException e) { 679 // success 680 } 681 682 try { 683 TextFormat.parseInt32("-2147483649"); 684 fail("Should have thrown an exception."); 685 } catch (NumberFormatException e) { 686 // success 687 } 688 689 try { 690 TextFormat.parseUInt32("4294967296"); 691 fail("Should have thrown an exception."); 692 } catch (NumberFormatException e) { 693 // success 694 } 695 696 try { 697 TextFormat.parseUInt32("-1"); 698 fail("Should have thrown an exception."); 699 } catch (NumberFormatException e) { 700 // success 701 } 702 703 try { 704 TextFormat.parseInt64("9223372036854775808"); 705 fail("Should have thrown an exception."); 706 } catch (NumberFormatException e) { 707 // success 708 } 709 710 try { 711 TextFormat.parseInt64("-9223372036854775809"); 712 fail("Should have thrown an exception."); 713 } catch (NumberFormatException e) { 714 // success 715 } 716 717 try { 718 TextFormat.parseUInt64("18446744073709551616"); 719 fail("Should have thrown an exception."); 720 } catch (NumberFormatException e) { 721 // success 722 } 723 724 try { 725 TextFormat.parseUInt64("-1"); 726 fail("Should have thrown an exception."); 727 } catch (NumberFormatException e) { 728 // success 729 } 730 731 // Not a number. 732 try { 733 TextFormat.parseInt32("abcd"); 734 fail("Should have thrown an exception."); 735 } catch (NumberFormatException e) { 736 // success 737 } 738 } 739 testParseString()740 public void testParseString() throws Exception { 741 final String zh = "\u9999\u6e2f\u4e0a\u6d77\ud84f\udf80\u8c50\u9280\u884c"; 742 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 743 TextFormat.merge("optional_string: \"" + zh + "\"", builder); 744 assertEquals(zh, builder.getOptionalString()); 745 } 746 testParseLongString()747 public void testParseLongString() throws Exception { 748 String longText = 749 "123456789012345678901234567890123456789012345678901234567890" + 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 770 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 771 TextFormat.merge("optional_string: \"" + longText + "\"", builder); 772 assertEquals(longText, builder.getOptionalString()); 773 } 774 testParseBoolean()775 public void testParseBoolean() throws Exception { 776 String goodText = 777 "repeated_bool: t repeated_bool : 0\n" + 778 "repeated_bool :f repeated_bool:1\n" + 779 "repeated_bool: False repeated_bool: True"; 780 String goodTextCanonical = 781 "repeated_bool: true\n" + 782 "repeated_bool: false\n" + 783 "repeated_bool: false\n" + 784 "repeated_bool: true\n" + 785 "repeated_bool: false\n" + 786 "repeated_bool: true\n"; 787 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 788 TextFormat.merge(goodText, builder); 789 assertEquals(goodTextCanonical, builder.build().toString()); 790 791 try { 792 TestAllTypes.Builder badBuilder = TestAllTypes.newBuilder(); 793 TextFormat.merge("optional_bool:2", badBuilder); 794 fail("Should have thrown an exception."); 795 } catch (TextFormat.ParseException e) { 796 // success 797 } 798 try { 799 TestAllTypes.Builder badBuilder = TestAllTypes.newBuilder(); 800 TextFormat.merge("optional_bool: foo", badBuilder); 801 fail("Should have thrown an exception."); 802 } catch (TextFormat.ParseException e) { 803 // success 804 } 805 } 806 testParseAdjacentStringLiterals()807 public void testParseAdjacentStringLiterals() throws Exception { 808 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 809 TextFormat.merge("optional_string: \"foo\" 'corge' \"grault\"", builder); 810 assertEquals("foocorgegrault", builder.getOptionalString()); 811 } 812 testPrintFieldValue()813 public void testPrintFieldValue() throws Exception { 814 assertPrintFieldValue("\"Hello\"", "Hello", "repeated_string"); 815 assertPrintFieldValue("123.0", 123f, "repeated_float"); 816 assertPrintFieldValue("123.0", 123d, "repeated_double"); 817 assertPrintFieldValue("123", 123, "repeated_int32"); 818 assertPrintFieldValue("123", 123L, "repeated_int64"); 819 assertPrintFieldValue("true", true, "repeated_bool"); 820 assertPrintFieldValue("4294967295", 0xFFFFFFFF, "repeated_uint32"); 821 assertPrintFieldValue("18446744073709551615", 0xFFFFFFFFFFFFFFFFL, 822 "repeated_uint64"); 823 assertPrintFieldValue("\"\\001\\002\\003\"", 824 ByteString.copyFrom(new byte[] {1, 2, 3}), "repeated_bytes"); 825 } 826 assertPrintFieldValue(String expect, Object value, String fieldName)827 private void assertPrintFieldValue(String expect, Object value, 828 String fieldName) throws Exception { 829 StringBuilder sb = new StringBuilder(); 830 TextFormat.printFieldValue( 831 TestAllTypes.getDescriptor().findFieldByName(fieldName), 832 value, sb); 833 assertEquals(expect, sb.toString()); 834 } 835 testShortDebugString()836 public void testShortDebugString() { 837 assertEquals("optional_nested_message { bb: 42 } repeated_int32: 1" 838 + " repeated_uint32: 2", 839 TextFormat.shortDebugString(TestAllTypes.newBuilder() 840 .addRepeatedInt32(1) 841 .addRepeatedUint32(2) 842 .setOptionalNestedMessage( 843 NestedMessage.newBuilder().setBb(42).build()) 844 .build())); 845 } 846 testShortDebugString_field()847 public void testShortDebugString_field() { 848 final FieldDescriptor dataField = 849 OneString.getDescriptor().findFieldByName("data"); 850 assertEquals( 851 "data: \"test data\"", 852 TextFormat.shortDebugString(dataField, "test data")); 853 854 final FieldDescriptor optionalField = 855 TestAllTypes.getDescriptor().findFieldByName("optional_nested_message"); 856 final Object value = NestedMessage.newBuilder().setBb(42).build(); 857 858 assertEquals( 859 "optional_nested_message { bb: 42 }", 860 TextFormat.shortDebugString(optionalField, value)); 861 } 862 testShortDebugString_unknown()863 public void testShortDebugString_unknown() { 864 assertEquals("5: 1 5: 0x00000002 5: 0x0000000000000003 5: \"4\" 5 { 10: 5 }" 865 + " 8: 1 8: 2 8: 3 15: 12379813812177893520 15: 0xabcd1234 15:" 866 + " 0xabcdef1234567890", 867 TextFormat.shortDebugString(makeUnknownFieldSet())); 868 } 869 testPrintToUnicodeString()870 public void testPrintToUnicodeString() throws Exception { 871 assertEquals( 872 "optional_string: \"abc\u3042efg\"\n" + 873 "optional_bytes: \"\\343\\201\\202\"\n" + 874 "repeated_string: \"\u3093XYZ\"\n", 875 TextFormat.printToUnicodeString(TestAllTypes.newBuilder() 876 .setOptionalString("abc\u3042efg") 877 .setOptionalBytes(bytes(0xe3, 0x81, 0x82)) 878 .addRepeatedString("\u3093XYZ") 879 .build())); 880 881 // Double quotes and backslashes should be escaped 882 assertEquals( 883 "optional_string: \"a\\\\bc\\\"ef\\\"g\"\n", 884 TextFormat.printToUnicodeString(TestAllTypes.newBuilder() 885 .setOptionalString("a\\bc\"ef\"g") 886 .build())); 887 888 // Test escaping roundtrip 889 TestAllTypes message = TestAllTypes.newBuilder() 890 .setOptionalString("a\\bc\\\"ef\"g") 891 .build(); 892 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 893 TextFormat.merge(TextFormat.printToUnicodeString(message), builder); 894 assertEquals(message.getOptionalString(), builder.getOptionalString()); 895 } 896 testPrintToUnicodeStringWithNewlines()897 public void testPrintToUnicodeStringWithNewlines() throws Exception { 898 // No newlines at start and end 899 assertEquals("optional_string: \"test newlines\\n\\nin\\nstring\"\n", 900 TextFormat.printToUnicodeString(TestAllTypes.newBuilder() 901 .setOptionalString("test newlines\n\nin\nstring") 902 .build())); 903 904 // Newlines at start and end 905 assertEquals("optional_string: \"\\ntest\\nnewlines\\n\\nin\\nstring\\n\"\n", 906 TextFormat.printToUnicodeString(TestAllTypes.newBuilder() 907 .setOptionalString("\ntest\nnewlines\n\nin\nstring\n") 908 .build())); 909 910 // Strings with 0, 1 and 2 newlines. 911 assertEquals("optional_string: \"\"\n", 912 TextFormat.printToUnicodeString(TestAllTypes.newBuilder() 913 .setOptionalString("") 914 .build())); 915 assertEquals("optional_string: \"\\n\"\n", 916 TextFormat.printToUnicodeString(TestAllTypes.newBuilder() 917 .setOptionalString("\n") 918 .build())); 919 assertEquals("optional_string: \"\\n\\n\"\n", 920 TextFormat.printToUnicodeString(TestAllTypes.newBuilder() 921 .setOptionalString("\n\n") 922 .build())); 923 924 // Test escaping roundtrip 925 TestAllTypes message = TestAllTypes.newBuilder() 926 .setOptionalString("\ntest\nnewlines\n\nin\nstring\n") 927 .build(); 928 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 929 TextFormat.merge(TextFormat.printToUnicodeString(message), builder); 930 assertEquals(message.getOptionalString(), builder.getOptionalString()); 931 } 932 testPrintToUnicodeString_unknown()933 public void testPrintToUnicodeString_unknown() { 934 assertEquals( 935 "1: \"\\343\\201\\202\"\n", 936 TextFormat.printToUnicodeString(UnknownFieldSet.newBuilder() 937 .addField(1, 938 UnknownFieldSet.Field.newBuilder() 939 .addLengthDelimited(bytes(0xe3, 0x81, 0x82)).build()) 940 .build())); 941 } 942 943 testParseNonRepeatedFields()944 public void testParseNonRepeatedFields() throws Exception { 945 assertParseSuccessWithOverwriteForbidden( 946 "repeated_int32: 1\n" + 947 "repeated_int32: 2\n"); 948 assertParseSuccessWithOverwriteForbidden( 949 "RepeatedGroup { a: 1 }\n" + 950 "RepeatedGroup { a: 2 }\n"); 951 assertParseSuccessWithOverwriteForbidden( 952 "repeated_nested_message { bb: 1 }\n" + 953 "repeated_nested_message { bb: 2 }\n"); 954 assertParseErrorWithOverwriteForbidden( 955 "3:17: Non-repeated field " + 956 "\"protobuf_unittest.TestAllTypes.optional_int32\" " + 957 "cannot be overwritten.", 958 "optional_int32: 1\n" + 959 "optional_bool: true\n" + 960 "optional_int32: 1\n"); 961 assertParseErrorWithOverwriteForbidden( 962 "2:17: Non-repeated field " + 963 "\"protobuf_unittest.TestAllTypes.optionalgroup\" " + 964 "cannot be overwritten.", 965 "OptionalGroup { a: 1 }\n" + 966 "OptionalGroup { }\n"); 967 assertParseErrorWithOverwriteForbidden( 968 "2:33: Non-repeated field " + 969 "\"protobuf_unittest.TestAllTypes.optional_nested_message\" " + 970 "cannot be overwritten.", 971 "optional_nested_message { }\n" + 972 "optional_nested_message { bb: 3 }\n"); 973 assertParseErrorWithOverwriteForbidden( 974 "2:16: Non-repeated field " + 975 "\"protobuf_unittest.TestAllTypes.default_int32\" " + 976 "cannot be overwritten.", 977 "default_int32: 41\n" + // the default value 978 "default_int32: 41\n"); 979 assertParseErrorWithOverwriteForbidden( 980 "2:17: Non-repeated field " + 981 "\"protobuf_unittest.TestAllTypes.default_string\" " + 982 "cannot be overwritten.", 983 "default_string: \"zxcv\"\n" + 984 "default_string: \"asdf\"\n"); 985 } 986 testParseShortRepeatedFormOfRepeatedFields()987 public void testParseShortRepeatedFormOfRepeatedFields() throws Exception { 988 assertParseSuccessWithOverwriteForbidden("repeated_foreign_enum: [FOREIGN_FOO, FOREIGN_BAR]"); 989 assertParseSuccessWithOverwriteForbidden("repeated_int32: [ 1, 2 ]\n"); 990 assertParseSuccessWithOverwriteForbidden("RepeatedGroup [{ a: 1 },{ a: 2 }]\n"); 991 assertParseSuccessWithOverwriteForbidden("repeated_nested_message [{ bb: 1 }, { bb: 2 }]\n"); 992 } 993 testParseShortRepeatedFormOfNonRepeatedFields()994 public void testParseShortRepeatedFormOfNonRepeatedFields() throws Exception { 995 assertParseErrorWithOverwriteForbidden( 996 "1:17: Couldn't parse integer: For input string: \"[\"", 997 "optional_int32: [1]\n"); 998 } 999 1000 // ======================================================================= 1001 // test oneof 1002 testOneofTextFormat()1003 public void testOneofTextFormat() throws Exception { 1004 TestOneof2.Builder builder = TestOneof2.newBuilder(); 1005 TestUtil.setOneof(builder); 1006 TestOneof2 message = builder.build(); 1007 TestOneof2.Builder dest = TestOneof2.newBuilder(); 1008 TextFormat.merge(TextFormat.printToUnicodeString(message), dest); 1009 TestUtil.assertOneofSet(dest.build()); 1010 } 1011 testOneofOverwriteForbidden()1012 public void testOneofOverwriteForbidden() throws Exception { 1013 String input = "foo_string: \"stringvalue\" foo_int: 123"; 1014 TestOneof2.Builder builder = TestOneof2.newBuilder(); 1015 try { 1016 parserWithOverwriteForbidden.merge( 1017 input, TestUtil.getExtensionRegistry(), builder); 1018 fail("Expected parse exception."); 1019 } catch (TextFormat.ParseException e) { 1020 assertEquals("1:36: Field \"protobuf_unittest.TestOneof2.foo_int\"" 1021 + " is specified along with field \"protobuf_unittest.TestOneof2.foo_string\"," 1022 + " another member of oneof \"foo\".", e.getMessage()); 1023 } 1024 } 1025 testOneofOverwriteAllowed()1026 public void testOneofOverwriteAllowed() throws Exception { 1027 String input = "foo_string: \"stringvalue\" foo_int: 123"; 1028 TestOneof2.Builder builder = TestOneof2.newBuilder(); 1029 defaultParser.merge(input, TestUtil.getExtensionRegistry(), builder); 1030 // Only the last value sticks. 1031 TestOneof2 oneof = builder.build(); 1032 assertFalse(oneof.hasFooString()); 1033 assertTrue(oneof.hasFooInt()); 1034 } 1035 1036 // ======================================================================= 1037 // test location information 1038 testParseInfoTreeBuilding()1039 public void testParseInfoTreeBuilding() throws Exception { 1040 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 1041 1042 Descriptor descriptor = TestAllTypes.getDescriptor(); 1043 TextFormatParseInfoTree.Builder treeBuilder = TextFormatParseInfoTree.builder(); 1044 // Set to allow unknown fields 1045 TextFormat.Parser parser = 1046 TextFormat.Parser.newBuilder() 1047 .setParseInfoTreeBuilder(treeBuilder) 1048 .build(); 1049 1050 final String stringData = 1051 "optional_int32: 1\n" 1052 + "optional_int64: 2\n" 1053 + " optional_double: 2.4\n" 1054 + "repeated_int32: 5\n" 1055 + "repeated_int32: 10\n" 1056 + "optional_nested_message <\n" 1057 + " bb: 78\n" 1058 + ">\n" 1059 + "repeated_nested_message <\n" 1060 + " bb: 79\n" 1061 + ">\n" 1062 + "repeated_nested_message <\n" 1063 + " bb: 80\n" 1064 + ">"; 1065 1066 parser.merge(stringData, builder); 1067 TextFormatParseInfoTree tree = treeBuilder.build(); 1068 1069 // Verify that the tree has the correct positions. 1070 assertLocation(tree, descriptor, "optional_int32", 0, 0, 0); 1071 assertLocation(tree, descriptor, "optional_int64", 0, 1, 0); 1072 assertLocation(tree, descriptor, "optional_double", 0, 2, 2); 1073 1074 assertLocation(tree, descriptor, "repeated_int32", 0, 3, 0); 1075 assertLocation(tree, descriptor, "repeated_int32", 1, 4, 0); 1076 1077 assertLocation(tree, descriptor, "optional_nested_message", 0, 5, 0); 1078 assertLocation(tree, descriptor, "repeated_nested_message", 0, 8, 0); 1079 assertLocation(tree, descriptor, "repeated_nested_message", 1, 11, 0); 1080 1081 // Check for fields not set. For an invalid field, the location returned should be -1, -1. 1082 assertLocation(tree, descriptor, "repeated_int64", 0, -1, -1); 1083 assertLocation(tree, descriptor, "repeated_int32", 6, -1, -1); 1084 1085 // Verify inside the nested message. 1086 FieldDescriptor nestedField = descriptor.findFieldByName("optional_nested_message"); 1087 1088 TextFormatParseInfoTree nestedTree = tree.getNestedTrees(nestedField).get(0); 1089 assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 6, 2); 1090 1091 // Verify inside another nested message. 1092 nestedField = descriptor.findFieldByName("repeated_nested_message"); 1093 nestedTree = tree.getNestedTrees(nestedField).get(0); 1094 assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 9, 2); 1095 1096 nestedTree = tree.getNestedTrees(nestedField).get(1); 1097 assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 12, 2); 1098 1099 // Verify a NULL tree for an unknown nested field. 1100 try { 1101 tree.getNestedTree(nestedField, 2); 1102 fail("unknown nested field should throw"); 1103 } catch (IllegalArgumentException unused) { 1104 // pass 1105 } 1106 } 1107 assertLocation( TextFormatParseInfoTree tree, final Descriptor descriptor, final String fieldName, int index, int line, int column)1108 private void assertLocation( 1109 TextFormatParseInfoTree tree, 1110 final Descriptor descriptor, 1111 final String fieldName, 1112 int index, 1113 int line, 1114 int column) { 1115 List<TextFormatParseLocation> locs = tree.getLocations(descriptor.findFieldByName(fieldName)); 1116 if (index < locs.size()) { 1117 TextFormatParseLocation location = locs.get(index); 1118 TextFormatParseLocation expected = TextFormatParseLocation.create(line, column); 1119 assertEquals(expected, location); 1120 } else if (line != -1 && column != -1) { 1121 fail( 1122 String.format( 1123 "Tree/descriptor/fieldname did not contain index %d, line %d column %d expected", 1124 index, 1125 line, 1126 column)); 1127 } 1128 } 1129 } 1130