Home | History | Annotate | Download | only in protobuf
      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 (at) 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. */
    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. */
    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. */
    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.
    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 
    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 
    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    */
    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    */
    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 
    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 
    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 
    320   public void testParse() throws Exception {
    321     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
    322     TextFormat.merge(allFieldsSetText, builder);
    323     TestUtil.assertAllFieldsSet(builder.build());
    324   }
    325 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
   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 
   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 
   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 
   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 
   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