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