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