• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static com.google.protobuf.TestUtil.TEST_REQUIRED_INITIALIZED;
34 import static com.google.protobuf.TestUtil.TEST_REQUIRED_UNINITIALIZED;
35 
36 import com.google.protobuf.DescriptorProtos.DescriptorProto;
37 import com.google.protobuf.DescriptorProtos.FieldDescriptorProto;
38 import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
39 import com.google.protobuf.Descriptors.Descriptor;
40 import com.google.protobuf.Descriptors.FieldDescriptor;
41 import com.google.protobuf.Descriptors.FileDescriptor;
42 import com.google.protobuf.TextFormat.Parser.SingularOverwritePolicy;
43 import com.google.protobuf.testing.proto.TestProto3Optional;
44 import com.google.protobuf.testing.proto.TestProto3Optional.NestedEnum;
45 import any_test.AnyTestProto.TestAny;
46 import map_test.MapTestProto.TestMap;
47 import protobuf_unittest.UnittestMset.TestMessageSetExtension1;
48 import protobuf_unittest.UnittestMset.TestMessageSetExtension2;
49 import protobuf_unittest.UnittestProto.OneString;
50 import protobuf_unittest.UnittestProto.TestAllExtensions;
51 import protobuf_unittest.UnittestProto.TestAllTypes;
52 import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
53 import protobuf_unittest.UnittestProto.TestEmptyMessage;
54 import protobuf_unittest.UnittestProto.TestOneof2;
55 import protobuf_unittest.UnittestProto.TestRequired;
56 import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet;
57 import java.io.StringReader;
58 import java.util.Arrays;
59 import java.util.List;
60 import java.util.logging.Logger;
61 import junit.framework.TestCase;
62 
63 /**
64  * Test case for {@link TextFormat}.
65  *
66  * <p>TODO(wenboz): ExtensionTest and rest of text_format_unittest.cc.
67  *
68  * @author wenboz@google.com (Wenbo Zhu)
69  */
70 public class TextFormatTest extends TestCase {
71 
72   // A basic string with different escapable characters for testing.
73   private static final String ESCAPE_TEST_STRING =
74       "\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\";
75 
76   // A representation of the above string with all the characters escaped.
77   private static final String ESCAPE_TEST_STRING_ESCAPED =
78       "\\\"A string with \\' characters \\n and \\r newlines "
79           + "and \\t tabs and \\001 slashes \\\\";
80 
81   private static String allFieldsSetText =
82       TestUtil.readTextFromFile("text_format_unittest_data_oneof_implemented.txt");
83   private static String allExtensionsSetText =
84       TestUtil.readTextFromFile("text_format_unittest_extensions_data.txt");
85 
86   private static String exoticText =
87       ""
88           + "repeated_int32: -1\n"
89           + "repeated_int32: -2147483648\n"
90           + "repeated_int64: -1,\n"
91           + "repeated_int64: -9223372036854775808\n"
92           + "repeated_uint32: 4294967295\n"
93           + "repeated_uint32: 2147483648\n"
94           + "repeated_uint64: 18446744073709551615\n"
95           + "repeated_uint64: 9223372036854775808\n"
96           + "repeated_double: 123.0\n"
97           + "repeated_double: 123.5\n"
98           + "repeated_double: 0.125\n"
99           + "repeated_double: .125\n"
100           + "repeated_double: -.125\n"
101           + "repeated_double: 1.23E17\n"
102           + "repeated_double: 1.23E+17\n"
103           + "repeated_double: -1.23e-17\n"
104           + "repeated_double: .23e+17\n"
105           + "repeated_double: -.23E17\n"
106           + "repeated_double: 1.235E22\n"
107           + "repeated_double: 1.235E-18\n"
108           + "repeated_double: 123.456789\n"
109           + "repeated_double: Infinity\n"
110           + "repeated_double: -Infinity\n"
111           + "repeated_double: NaN\n"
112           + "repeated_string: \"\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\""
113           + "\\341\\210\\264\"\n"
114           + "repeated_bytes: \"\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\\376\"\n";
115 
116   private static String canonicalExoticText =
117       exoticText
118           .replace(": .", ": 0.")
119           .replace(": -.", ": -0.") // short-form double
120           .replace("23e", "23E")
121           .replace("E+", "E")
122           .replace("0.23E17", "2.3E16")
123           .replace(",", "");
124 
125   private String messageSetText =
126       ""
127           + "[protobuf_unittest.TestMessageSetExtension1] {\n"
128           + "  i: 123\n"
129           + "}\n"
130           + "[protobuf_unittest.TestMessageSetExtension2] {\n"
131           + "  str: \"foo\"\n"
132           + "}\n";
133 
134   private String messageSetTextWithRepeatedExtension =
135       ""
136           + "[protobuf_unittest.TestMessageSetExtension1] {\n"
137           + "  i: 123\n"
138           + "}\n"
139           + "[protobuf_unittest.TestMessageSetExtension1] {\n"
140           + "  i: 456\n"
141           + "}\n";
142 
143   private final TextFormat.Parser parserAllowingUnknownFields =
144       TextFormat.Parser.newBuilder().setAllowUnknownFields(true).build();
145 
146   private final TextFormat.Parser parserAllowingUnknownExtensions =
147       TextFormat.Parser.newBuilder().setAllowUnknownExtensions(true).build();
148 
149   private final TextFormat.Parser parserWithOverwriteForbidden =
150       TextFormat.Parser.newBuilder()
151           .setSingularOverwritePolicy(SingularOverwritePolicy.FORBID_SINGULAR_OVERWRITES)
152           .build();
153 
154   private final TextFormat.Parser defaultParser = TextFormat.Parser.newBuilder().build();
155 
156   /** Print TestAllTypes and compare with golden file. */
testPrintMessage()157   public void testPrintMessage() throws Exception {
158     String javaText = TextFormat.printer().printToString(TestUtil.getAllSet());
159 
160     // Java likes to add a trailing ".0" to floats and doubles.  C printf
161     // (with %g format) does not.  Our golden files are used for both
162     // C++ and Java TextFormat classes, so we need to conform.
163     javaText = javaText.replace(".0\n", "\n");
164 
165     assertEquals(allFieldsSetText, javaText);
166   }
167 
168   /** Print TestAllTypes as Builder and compare with golden file. */
testPrintMessageBuilder()169   public void testPrintMessageBuilder() throws Exception {
170     String javaText = TextFormat.printer().printToString(TestUtil.getAllSetBuilder());
171 
172     // Java likes to add a trailing ".0" to floats and doubles.  C printf
173     // (with %g format) does not.  Our golden files are used for both
174     // C++ and Java TextFormat classes, so we need to conform.
175     javaText = javaText.replace(".0\n", "\n");
176 
177     assertEquals(allFieldsSetText, javaText);
178   }
179 
180   /** Print TestAllExtensions and compare with golden file. */
testPrintExtensions()181   public void testPrintExtensions() throws Exception {
182     String javaText = TextFormat.printer().printToString(TestUtil.getAllExtensionsSet());
183 
184     // Java likes to add a trailing ".0" to floats and doubles.  C printf
185     // (with %g format) does not.  Our golden files are used for both
186     // C++ and Java TextFormat classes, so we need to conform.
187     javaText = javaText.replace(".0\n", "\n");
188 
189     assertEquals(allExtensionsSetText, javaText);
190   }
191 
192   // Creates an example unknown field set.
makeUnknownFieldSet()193   private UnknownFieldSet makeUnknownFieldSet() {
194 
195     return UnknownFieldSet.newBuilder()
196         .addField(
197             5,
198             UnknownFieldSet.Field.newBuilder()
199                 .addVarint(1)
200                 .addFixed32(2)
201                 .addFixed64(3)
202                 .addLengthDelimited(ByteString.copyFromUtf8("4"))
203                 .addLengthDelimited(
204                     UnknownFieldSet.newBuilder()
205                         .addField(12, UnknownFieldSet.Field.newBuilder().addVarint(6).build())
206                         .build()
207                         .toByteString())
208                 .addGroup(
209                     UnknownFieldSet.newBuilder()
210                         .addField(10, UnknownFieldSet.Field.newBuilder().addVarint(5).build())
211                         .build())
212                 .build())
213         .addField(
214             8, UnknownFieldSet.Field.newBuilder().addVarint(1).addVarint(2).addVarint(3).build())
215         .addField(
216             15,
217             UnknownFieldSet.Field.newBuilder()
218                 .addVarint(0xABCDEF1234567890L)
219                 .addFixed32(0xABCD1234)
220                 .addFixed64(0xABCDEF1234567890L)
221                 .build())
222         .build();
223   }
224 
testPrintUnknownFields()225   public void testPrintUnknownFields() throws Exception {
226     // Test printing of unknown fields in a message.
227 
228     TestEmptyMessage message =
229         TestEmptyMessage.newBuilder().setUnknownFields(makeUnknownFieldSet()).build();
230 
231     assertEquals(
232         "5: 1\n"
233             + "5: 0x00000002\n"
234             + "5: 0x0000000000000003\n"
235             + "5: \"4\"\n"
236             + "5: {\n"
237             + "  12: 6\n"
238             + "}\n"
239             + "5 {\n"
240             + "  10: 5\n"
241             + "}\n"
242             + "8: 1\n"
243             + "8: 2\n"
244             + "8: 3\n"
245             + "15: 12379813812177893520\n"
246             + "15: 0xabcd1234\n"
247             + "15: 0xabcdef1234567890\n",
248         TextFormat.printer().printToString(message));
249   }
250 
testPrintField()251   public void testPrintField() throws Exception {
252     final FieldDescriptor dataField = OneString.getDescriptor().findFieldByName("data");
253     assertEquals(
254         "data: \"test data\"\n", TextFormat.printer().printFieldToString(dataField, "test data"));
255 
256     final FieldDescriptor optionalField =
257         TestAllTypes.getDescriptor().findFieldByName("optional_nested_message");
258     final Object value = NestedMessage.newBuilder().setBb(42).build();
259 
260     assertEquals(
261         "optional_nested_message {\n  bb: 42\n}\n",
262         TextFormat.printer().printFieldToString(optionalField, value));
263   }
264 
265   /**
266    * Helper to construct a ByteString from a String containing only 8-bit characters. The characters
267    * are converted directly to bytes, *not* encoded using UTF-8.
268    */
bytes(String str)269   private ByteString bytes(String str) {
270     return ByteString.copyFrom(str.getBytes(Internal.ISO_8859_1));
271   }
272 
273   /**
274    * Helper to construct a ByteString from a bunch of bytes. The inputs are actually ints so that I
275    * can use hex notation and not get stupid errors about precision.
276    */
bytes(int... bytesAsInts)277   private ByteString bytes(int... bytesAsInts) {
278     byte[] bytes = new byte[bytesAsInts.length];
279     for (int i = 0; i < bytesAsInts.length; i++) {
280       bytes[i] = (byte) bytesAsInts[i];
281     }
282     return ByteString.copyFrom(bytes);
283   }
284 
testPrintExotic()285   public void testPrintExotic() throws Exception {
286     Message message =
287         TestAllTypes.newBuilder()
288             // Signed vs. unsigned numbers.
289             .addRepeatedInt32(-1)
290             .addRepeatedUint32(-1)
291             .addRepeatedInt64(-1)
292             .addRepeatedUint64(-1)
293             .addRepeatedInt32(1 << 31)
294             .addRepeatedUint32(1 << 31)
295             .addRepeatedInt64(1L << 63)
296             .addRepeatedUint64(1L << 63)
297 
298             // Floats of various precisions and exponents.
299             .addRepeatedDouble(123)
300             .addRepeatedDouble(123.5)
301             .addRepeatedDouble(0.125)
302             .addRepeatedDouble(.125)
303             .addRepeatedDouble(-.125)
304             .addRepeatedDouble(123e15)
305             .addRepeatedDouble(123e15)
306             .addRepeatedDouble(-1.23e-17)
307             .addRepeatedDouble(.23e17)
308             .addRepeatedDouble(-23e15)
309             .addRepeatedDouble(123.5e20)
310             .addRepeatedDouble(123.5e-20)
311             .addRepeatedDouble(123.456789)
312             .addRepeatedDouble(Double.POSITIVE_INFINITY)
313             .addRepeatedDouble(Double.NEGATIVE_INFINITY)
314             .addRepeatedDouble(Double.NaN)
315 
316             // Strings and bytes that needing escaping.
317             .addRepeatedString("\0\001\007\b\f\n\r\t\013\\\'\"\u1234")
318             .addRepeatedBytes(bytes("\0\001\007\b\f\n\r\t\013\\\'\"\u00fe"))
319             .build();
320 
321     assertEquals(canonicalExoticText, message.toString());
322   }
323 
testRoundtripProto3Optional()324   public void testRoundtripProto3Optional() throws Exception {
325     Message message =
326         TestProto3Optional.newBuilder()
327             .setOptionalInt32(1)
328             .setOptionalInt64(2)
329             .setOptionalNestedEnum(NestedEnum.BAZ)
330             .build();
331     TestProto3Optional.Builder message2 = TestProto3Optional.newBuilder();
332     TextFormat.merge(message.toString(), message2);
333 
334     assertTrue(message2.hasOptionalInt32());
335     assertTrue(message2.hasOptionalInt64());
336     assertTrue(message2.hasOptionalNestedEnum());
337     assertEquals(1, message2.getOptionalInt32());
338     assertEquals(2, message2.getOptionalInt64());
339     assertEquals(NestedEnum.BAZ, message2.getOptionalNestedEnum());
340   }
341 
testPrintMessageSet()342   public void testPrintMessageSet() throws Exception {
343     TestMessageSet messageSet =
344         TestMessageSet.newBuilder()
345             .setExtension(
346                 TestMessageSetExtension1.messageSetExtension,
347                 TestMessageSetExtension1.newBuilder().setI(123).build())
348             .setExtension(
349                 TestMessageSetExtension2.messageSetExtension,
350                 TestMessageSetExtension2.newBuilder().setStr("foo").build())
351             .build();
352 
353     assertEquals(messageSetText, messageSet.toString());
354   }
355 
356   // =================================================================
357 
testMerge()358   public void testMerge() throws Exception {
359     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
360     TextFormat.merge(allFieldsSetText, builder);
361     TestUtil.assertAllFieldsSet(builder.build());
362   }
363 
testParse()364   public void testParse() throws Exception {
365     TestUtil.assertAllFieldsSet(TextFormat.parse(allFieldsSetText, TestAllTypes.class));
366   }
367 
testMergeInitialized()368   public void testMergeInitialized() throws Exception {
369     TestRequired.Builder builder = TestRequired.newBuilder();
370     TextFormat.merge(TEST_REQUIRED_INITIALIZED.toString(), builder);
371     assertEquals(TEST_REQUIRED_INITIALIZED.toString(), builder.buildPartial().toString());
372     assertTrue(builder.isInitialized());
373   }
374 
testParseInitialized()375   public void testParseInitialized() throws Exception {
376     TestRequired parsed =
377         TextFormat.parse(TEST_REQUIRED_INITIALIZED.toString(), TestRequired.class);
378     assertEquals(TEST_REQUIRED_INITIALIZED.toString(), parsed.toString());
379     assertTrue(parsed.isInitialized());
380   }
381 
testMergeUninitialized()382   public void testMergeUninitialized() throws Exception {
383     TestRequired.Builder builder = TestRequired.newBuilder();
384     TextFormat.merge(TEST_REQUIRED_UNINITIALIZED.toString(), builder);
385     assertEquals(TEST_REQUIRED_UNINITIALIZED.toString(), builder.buildPartial().toString());
386     assertFalse(builder.isInitialized());
387   }
388 
testParseUninitialized()389   public void testParseUninitialized() throws Exception {
390     try {
391       TextFormat.parse(TEST_REQUIRED_UNINITIALIZED.toString(), TestRequired.class);
392       fail("Expected UninitializedMessageException.");
393     } catch (UninitializedMessageException e) {
394       assertEquals("Message missing required fields: b, c", e.getMessage());
395     }
396   }
397 
testMergeReader()398   public void testMergeReader() throws Exception {
399     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
400     TextFormat.merge(new StringReader(allFieldsSetText), builder);
401     TestUtil.assertAllFieldsSet(builder.build());
402   }
403 
testMergeExtensions()404   public void testMergeExtensions() throws Exception {
405     TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
406     TextFormat.merge(allExtensionsSetText, TestUtil.getFullExtensionRegistry(), builder);
407     TestUtil.assertAllExtensionsSet(builder.build());
408   }
409 
testParseExtensions()410   public void testParseExtensions() throws Exception {
411     TestUtil.assertAllExtensionsSet(
412         TextFormat.parse(
413             allExtensionsSetText, TestUtil.getFullExtensionRegistry(), TestAllExtensions.class));
414   }
415 
testMergeAndParseCompatibility()416   public void testMergeAndParseCompatibility() throws Exception {
417     String original =
418         "repeated_float: inf\n"
419             + "repeated_float: -inf\n"
420             + "repeated_float: nan\n"
421             + "repeated_float: inff\n"
422             + "repeated_float: -inff\n"
423             + "repeated_float: nanf\n"
424             + "repeated_float: 1.0f\n"
425             + "repeated_float: infinityf\n"
426             + "repeated_float: -Infinityf\n"
427             + "repeated_double: infinity\n"
428             + "repeated_double: -infinity\n"
429             + "repeated_double: nan\n";
430     String canonical =
431         "repeated_float: Infinity\n"
432             + "repeated_float: -Infinity\n"
433             + "repeated_float: NaN\n"
434             + "repeated_float: Infinity\n"
435             + "repeated_float: -Infinity\n"
436             + "repeated_float: NaN\n"
437             + "repeated_float: 1.0\n"
438             + "repeated_float: Infinity\n"
439             + "repeated_float: -Infinity\n"
440             + "repeated_double: Infinity\n"
441             + "repeated_double: -Infinity\n"
442             + "repeated_double: NaN\n";
443 
444     // Test merge().
445     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
446     TextFormat.merge(original, builder);
447     assertEquals(canonical, builder.build().toString());
448 
449     // Test parse().
450     assertEquals(canonical, TextFormat.parse(original, TestAllTypes.class).toString());
451   }
452 
testMergeAndParseExotic()453   public void testMergeAndParseExotic() throws Exception {
454     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
455     TextFormat.merge(exoticText, builder);
456 
457     // Too lazy to check things individually.  Don't try to debug this
458     // if testPrintExotic() is failing.
459     assertEquals(canonicalExoticText, builder.build().toString());
460     assertEquals(canonicalExoticText, TextFormat.parse(exoticText, TestAllTypes.class).toString());
461   }
462 
testMergeMessageSet()463   public void testMergeMessageSet() throws Exception {
464     ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance();
465     extensionRegistry.add(TestMessageSetExtension1.messageSetExtension);
466     extensionRegistry.add(TestMessageSetExtension2.messageSetExtension);
467 
468     TestMessageSet.Builder builder = TestMessageSet.newBuilder();
469     TextFormat.merge(messageSetText, extensionRegistry, builder);
470     TestMessageSet messageSet = builder.build();
471 
472     assertTrue(messageSet.hasExtension(TestMessageSetExtension1.messageSetExtension));
473     assertEquals(123, messageSet.getExtension(TestMessageSetExtension1.messageSetExtension).getI());
474     assertTrue(messageSet.hasExtension(TestMessageSetExtension2.messageSetExtension));
475     assertEquals(
476         "foo", messageSet.getExtension(TestMessageSetExtension2.messageSetExtension).getStr());
477 
478     builder = TestMessageSet.newBuilder();
479     TextFormat.merge(messageSetTextWithRepeatedExtension, extensionRegistry, builder);
480     messageSet = builder.build();
481     assertEquals(456, messageSet.getExtension(TestMessageSetExtension1.messageSetExtension).getI());
482   }
483 
testMergeMessageSetWithOverwriteForbidden()484   public void testMergeMessageSetWithOverwriteForbidden() throws Exception {
485     ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance();
486     extensionRegistry.add(TestMessageSetExtension1.messageSetExtension);
487     extensionRegistry.add(TestMessageSetExtension2.messageSetExtension);
488 
489     TestMessageSet.Builder builder = TestMessageSet.newBuilder();
490     parserWithOverwriteForbidden.merge(messageSetText, extensionRegistry, builder);
491     TestMessageSet messageSet = builder.build();
492     assertEquals(123, messageSet.getExtension(TestMessageSetExtension1.messageSetExtension).getI());
493     assertEquals(
494         "foo", messageSet.getExtension(TestMessageSetExtension2.messageSetExtension).getStr());
495 
496     builder = TestMessageSet.newBuilder();
497     try {
498       parserWithOverwriteForbidden.merge(
499           messageSetTextWithRepeatedExtension, extensionRegistry, builder);
500       fail("expected parse exception");
501     } catch (TextFormat.ParseException e) {
502       assertEquals(
503           "4:44: Non-repeated field "
504               + "\"protobuf_unittest.TestMessageSetExtension1.message_set_extension\""
505               + " cannot be overwritten.",
506           e.getMessage());
507     }
508   }
509 
testMergeNumericEnum()510   public void testMergeNumericEnum() throws Exception {
511     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
512     TextFormat.merge("optional_nested_enum: 2", builder);
513     assertEquals(TestAllTypes.NestedEnum.BAR, builder.getOptionalNestedEnum());
514   }
515 
testMergeAngleBrackets()516   public void testMergeAngleBrackets() throws Exception {
517     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
518     TextFormat.merge("OptionalGroup: < a: 1 >", builder);
519     assertTrue(builder.hasOptionalGroup());
520     assertEquals(1, builder.getOptionalGroup().getA());
521   }
522 
testMergeComment()523   public void testMergeComment() throws Exception {
524     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
525     TextFormat.merge(
526         "# this is a comment\n"
527             + "optional_int32: 1  # another comment\n"
528             + "optional_int64: 2\n"
529             + "# EOF comment",
530         builder);
531     assertEquals(1, builder.getOptionalInt32());
532     assertEquals(2, builder.getOptionalInt64());
533   }
534 
testPrintAny_customBuiltTypeRegistry()535   public void testPrintAny_customBuiltTypeRegistry() throws Exception {
536     TestAny testAny =
537         TestAny.newBuilder()
538             .setValue(
539                 Any.newBuilder()
540                     .setTypeUrl("type.googleapis.com/" + TestAllTypes.getDescriptor().getFullName())
541                     .setValue(
542                         TestAllTypes.newBuilder().setOptionalInt32(12345).build().toByteString())
543                     .build())
544             .build();
545     String actual =
546         TextFormat.printer()
547             .usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
548             .printToString(testAny);
549     String expected =
550         "value {\n"
551             + "  [type.googleapis.com/protobuf_unittest.TestAllTypes] {\n"
552             + "    optional_int32: 12345\n"
553             + "  }\n"
554             + "}\n";
555     assertEquals(expected, actual);
556   }
557 
createDescriptorForAny(FieldDescriptorProto... fields)558   private static Descriptor createDescriptorForAny(FieldDescriptorProto... fields)
559       throws Exception {
560     FileDescriptor fileDescriptor =
561         FileDescriptor.buildFrom(
562             FileDescriptorProto.newBuilder()
563                 .setName("any.proto")
564                 .setPackage("google.protobuf")
565                 .setSyntax("proto3")
566                 .addMessageType(
567                     DescriptorProto.newBuilder()
568                         .setName("Any")
569                         .addAllField(Arrays.asList(fields)))
570                 .build(),
571             new FileDescriptor[0]);
572     return fileDescriptor.getMessageTypes().get(0);
573   }
574 
testPrintAny_anyWithDynamicMessage()575   public void testPrintAny_anyWithDynamicMessage() throws Exception {
576     Descriptor descriptor =
577         createDescriptorForAny(
578             FieldDescriptorProto.newBuilder()
579                 .setName("type_url")
580                 .setNumber(1)
581                 .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
582                 .setType(FieldDescriptorProto.Type.TYPE_STRING)
583                 .build(),
584             FieldDescriptorProto.newBuilder()
585                 .setName("value")
586                 .setNumber(2)
587                 .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
588                 .setType(FieldDescriptorProto.Type.TYPE_BYTES)
589                 .build());
590     DynamicMessage testAny =
591         DynamicMessage.newBuilder(descriptor)
592             .setField(
593                 descriptor.findFieldByNumber(1),
594                 "type.googleapis.com/" + TestAllTypes.getDescriptor().getFullName())
595             .setField(
596                 descriptor.findFieldByNumber(2),
597                 TestAllTypes.newBuilder().setOptionalInt32(12345).build().toByteString())
598             .build();
599     String actual =
600         TextFormat.printer()
601             .usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
602             .printToString(testAny);
603     String expected =
604         "[type.googleapis.com/protobuf_unittest.TestAllTypes] {\n"
605             + "  optional_int32: 12345\n"
606             + "}\n";
607     assertEquals(expected, actual);
608   }
609 
testPrintAny_anyFromWithNoValueField()610   public void testPrintAny_anyFromWithNoValueField() throws Exception {
611     Descriptor descriptor =
612         createDescriptorForAny(
613             FieldDescriptorProto.newBuilder()
614                 .setName("type_url")
615                 .setNumber(1)
616                 .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
617                 .setType(FieldDescriptorProto.Type.TYPE_STRING)
618                 .build());
619     DynamicMessage testAny =
620         DynamicMessage.newBuilder(descriptor)
621             .setField(
622                 descriptor.findFieldByNumber(1),
623                 "type.googleapis.com/" + TestAllTypes.getDescriptor().getFullName())
624             .build();
625     String actual =
626         TextFormat.printer()
627             .usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
628             .printToString(testAny);
629     String expected = "type_url: \"type.googleapis.com/protobuf_unittest.TestAllTypes\"\n";
630     assertEquals(expected, actual);
631   }
632 
testPrintAny_anyFromWithNoTypeUrlField()633   public void testPrintAny_anyFromWithNoTypeUrlField() throws Exception {
634     Descriptor descriptor =
635         createDescriptorForAny(
636             FieldDescriptorProto.newBuilder()
637                 .setName("value")
638                 .setNumber(2)
639                 .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
640                 .setType(FieldDescriptorProto.Type.TYPE_BYTES)
641                 .build());
642     DynamicMessage testAny =
643         DynamicMessage.newBuilder(descriptor)
644             .setField(
645                 descriptor.findFieldByNumber(2),
646                 TestAllTypes.newBuilder().setOptionalInt32(12345).build().toByteString())
647             .build();
648     String actual =
649         TextFormat.printer()
650             .usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
651             .printToString(testAny);
652     String expected = "value: \"\\b\\271`\"\n";
653     assertEquals(expected, actual);
654   }
655 
testPrintAny_anyWithInvalidFieldType()656   public void testPrintAny_anyWithInvalidFieldType() throws Exception {
657     Descriptor descriptor =
658         createDescriptorForAny(
659             FieldDescriptorProto.newBuilder()
660                 .setName("type_url")
661                 .setNumber(1)
662                 .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
663                 .setType(FieldDescriptorProto.Type.TYPE_STRING)
664                 .build(),
665             FieldDescriptorProto.newBuilder()
666                 .setName("value")
667                 .setNumber(2)
668                 .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
669                 .setType(FieldDescriptorProto.Type.TYPE_STRING)
670                 .build());
671     DynamicMessage testAny =
672         DynamicMessage.newBuilder(descriptor)
673             .setField(
674                 descriptor.findFieldByNumber(1),
675                 "type.googleapis.com/" + TestAllTypes.getDescriptor().getFullName())
676             .setField(descriptor.findFieldByNumber(2), "test")
677             .build();
678     String actual =
679         TextFormat.printer()
680             .usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
681             .printToString(testAny);
682     String expected =
683         "type_url: \"type.googleapis.com/protobuf_unittest.TestAllTypes\"\n" + "value: \"test\"\n";
684     assertEquals(expected, actual);
685   }
686 
687 
testMergeAny_customBuiltTypeRegistry()688   public void testMergeAny_customBuiltTypeRegistry() throws Exception {
689     TestAny.Builder builder = TestAny.newBuilder();
690     TextFormat.Parser.newBuilder()
691         .setTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
692         .build()
693         .merge(
694             "value: {\n"
695                 + "[type.googleapis.com/protobuf_unittest.TestAllTypes] {\n"
696                 + "optional_int32: 12345\n"
697                 + "optional_nested_message {\n"
698                 + "  bb: 123\n"
699                 + "}\n"
700                 + "}\n"
701                 + "}",
702             builder);
703     assertEquals(
704         TestAny.newBuilder()
705             .setValue(
706                 Any.newBuilder()
707                     .setTypeUrl("type.googleapis.com/" + TestAllTypes.getDescriptor().getFullName())
708                     .setValue(
709                         TestAllTypes.newBuilder()
710                             .setOptionalInt32(12345)
711                             .setOptionalNestedMessage(
712                                 TestAllTypes.NestedMessage.newBuilder().setBb(123))
713                             .build()
714                             .toByteString())
715                     .build())
716             .build(),
717         builder.build());
718   }
719 
720 
assertParseError(String error, String text)721   private void assertParseError(String error, String text) {
722     // Test merge().
723     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
724     try {
725       TextFormat.merge(text, TestUtil.getFullExtensionRegistry(), builder);
726       fail("Expected parse exception.");
727     } catch (TextFormat.ParseException e) {
728       assertEquals(error, e.getMessage());
729     }
730 
731     // Test parse().
732     try {
733       TextFormat.parse(text, TestUtil.getFullExtensionRegistry(), TestAllTypes.class);
734       fail("Expected parse exception.");
735     } catch (TextFormat.ParseException e) {
736       assertEquals(error, e.getMessage());
737     }
738   }
739 
assertParseErrorWithUnknownFields(String error, String text)740   private void assertParseErrorWithUnknownFields(String error, String text) {
741     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
742     try {
743       parserAllowingUnknownFields.merge(text, TestUtil.getFullExtensionRegistry(), builder);
744       fail("Expected parse exception.");
745     } catch (TextFormat.ParseException e) {
746       assertEquals(error, e.getMessage());
747     }
748   }
749 
assertParseSuccessWithUnknownFields(String text)750   private TestAllTypes assertParseSuccessWithUnknownFields(String text)
751       throws TextFormat.ParseException {
752     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
753     parserAllowingUnknownFields.merge(text, TestUtil.getFullExtensionRegistry(), builder);
754     return builder.build();
755   }
756 
assertParseErrorWithUnknownExtensions(String error, String text)757   private void assertParseErrorWithUnknownExtensions(String error, String text) {
758     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
759     try {
760       parserAllowingUnknownExtensions.merge(text, builder);
761       fail("Expected parse exception.");
762     } catch (TextFormat.ParseException e) {
763       assertEquals(error, e.getMessage());
764     }
765   }
766 
assertParseSuccessWithUnknownExtensions(String text)767   private TestAllTypes assertParseSuccessWithUnknownExtensions(String text)
768       throws TextFormat.ParseException {
769     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
770     parserAllowingUnknownExtensions.merge(text, builder);
771     return builder.build();
772   }
773 
assertParseErrorWithOverwriteForbidden(String error, String text)774   private void assertParseErrorWithOverwriteForbidden(String error, String text) {
775     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
776     try {
777       parserWithOverwriteForbidden.merge(text, TestUtil.getFullExtensionRegistry(), builder);
778       fail("Expected parse exception.");
779     } catch (TextFormat.ParseException e) {
780       assertEquals(error, e.getMessage());
781     }
782   }
783 
assertParseSuccessWithOverwriteForbidden(String text)784   private TestAllTypes assertParseSuccessWithOverwriteForbidden(String text)
785       throws TextFormat.ParseException {
786     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
787     parserWithOverwriteForbidden.merge(text, TestUtil.getFullExtensionRegistry(), builder);
788     return builder.build();
789   }
790 
testParseErrors()791   public void testParseErrors() throws Exception {
792     assertParseError("1:16: Expected \":\".", "optional_int32 123");
793     assertParseError("1:23: Expected identifier. Found '?'", "optional_nested_enum: ?");
794     assertParseError(
795         "1:18: Couldn't parse integer: Number must be positive: -1", "optional_uint32: -1");
796     assertParseError(
797         "1:17: Couldn't parse integer: Number out of range for 32-bit signed "
798             + "integer: 82301481290849012385230157",
799         "optional_int32: 82301481290849012385230157");
800     assertParseError(
801         "1:16: Expected \"true\" or \"false\". Found \"maybe\".", "optional_bool: maybe");
802     assertParseError("1:16: Expected \"true\" or \"false\". Found \"2\".", "optional_bool: 2");
803     assertParseError("1:18: Expected string.", "optional_string: 123");
804     assertParseError("1:18: String missing ending quote.", "optional_string: \"ueoauaoe");
805     assertParseError(
806         "1:18: String missing ending quote.", "optional_string: \"ueoauaoe\noptional_int32: 123");
807     assertParseError("1:18: Invalid escape sequence: '\\z'", "optional_string: \"\\z\"");
808     assertParseError(
809         "1:18: String missing ending quote.", "optional_string: \"ueoauaoe\noptional_int32: 123");
810     assertParseError(
811         "1:2: Input contains unknown fields and/or extensions:\n"
812             + "1:2:\tprotobuf_unittest.TestAllTypes.[nosuchext]",
813         "[nosuchext]: 123");
814     assertParseError(
815         "1:20: Extension \"protobuf_unittest.optional_int32_extension\" does "
816             + "not extend message type \"protobuf_unittest.TestAllTypes\".",
817         "[protobuf_unittest.optional_int32_extension]: 123");
818     assertParseError(
819         "1:1: Input contains unknown fields and/or extensions:\n"
820             + "1:1:\tprotobuf_unittest.TestAllTypes.nosuchfield",
821         "nosuchfield: 123");
822     assertParseError("1:21: Expected \">\".", "OptionalGroup < a: 1");
823     assertParseError(
824         "1:23: Enum type \"protobuf_unittest.TestAllTypes.NestedEnum\" has no "
825             + "value named \"NO_SUCH_VALUE\".",
826         "optional_nested_enum: NO_SUCH_VALUE");
827     assertParseError(
828         "1:23: Enum type \"protobuf_unittest.TestAllTypes.NestedEnum\" has no "
829             + "value with number 123.",
830         "optional_nested_enum: 123");
831 
832     // Delimiters must match.
833     assertParseError("1:22: Expected identifier. Found '}'", "OptionalGroup < a: 1 }");
834     assertParseError("1:22: Expected identifier. Found '>'", "OptionalGroup { a: 1 >");
835   }
836 
837   // =================================================================
838 
testEscape()839   public void testEscape() throws Exception {
840     // Escape sequences.
841     assertEquals(
842         "\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\\177",
843         TextFormat.escapeBytes(bytes("\0\001\007\b\f\n\r\t\013\\\'\"\177")));
844     assertEquals(
845         "\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\\177",
846         TextFormat.escapeText("\0\001\007\b\f\n\r\t\013\\\'\"\177"));
847     assertEquals(
848         bytes("\0\001\007\b\f\n\r\t\013\\\'\""),
849         TextFormat.unescapeBytes("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\""));
850     assertEquals(
851         "\0\001\007\b\f\n\r\t\013\\\'\"",
852         TextFormat.unescapeText("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\""));
853     assertEquals(ESCAPE_TEST_STRING_ESCAPED, TextFormat.escapeText(ESCAPE_TEST_STRING));
854     assertEquals(ESCAPE_TEST_STRING, TextFormat.unescapeText(ESCAPE_TEST_STRING_ESCAPED));
855 
856     // Invariant
857     assertEquals("hello", TextFormat.escapeBytes(bytes("hello")));
858     assertEquals("hello", TextFormat.escapeText("hello"));
859     assertEquals(bytes("hello"), TextFormat.unescapeBytes("hello"));
860     assertEquals("hello", TextFormat.unescapeText("hello"));
861 
862     // Unicode handling.
863     assertEquals("\\341\\210\\264", TextFormat.escapeText("\u1234"));
864     assertEquals("\\341\\210\\264", TextFormat.escapeBytes(bytes(0xe1, 0x88, 0xb4)));
865     assertEquals("\u1234", TextFormat.unescapeText("\\341\\210\\264"));
866     assertEquals(bytes(0xe1, 0x88, 0xb4), TextFormat.unescapeBytes("\\341\\210\\264"));
867     assertEquals("\u1234", TextFormat.unescapeText("\\xe1\\x88\\xb4"));
868     assertEquals(bytes(0xe1, 0x88, 0xb4), TextFormat.unescapeBytes("\\xe1\\x88\\xb4"));
869 
870     // Handling of strings with unescaped Unicode characters > 255.
871     final String zh = "\u9999\u6e2f\u4e0a\u6d77\ud84f\udf80\u8c50\u9280\u884c";
872     ByteString zhByteString = ByteString.copyFromUtf8(zh);
873     assertEquals(zhByteString, TextFormat.unescapeBytes(zh));
874 
875     // Errors.
876     try {
877       TextFormat.unescapeText("\\x");
878       fail("Should have thrown an exception.");
879     } catch (TextFormat.InvalidEscapeSequenceException e) {
880       // success
881     }
882 
883     try {
884       TextFormat.unescapeText("\\z");
885       fail("Should have thrown an exception.");
886     } catch (TextFormat.InvalidEscapeSequenceException e) {
887       // success
888     }
889 
890     try {
891       TextFormat.unescapeText("\\");
892       fail("Should have thrown an exception.");
893     } catch (TextFormat.InvalidEscapeSequenceException e) {
894       // success
895     }
896   }
897 
testParseInteger()898   public void testParseInteger() throws Exception {
899     assertEquals(0, TextFormat.parseInt32("0"));
900     assertEquals(1, TextFormat.parseInt32("1"));
901     assertEquals(-1, TextFormat.parseInt32("-1"));
902     assertEquals(12345, TextFormat.parseInt32("12345"));
903     assertEquals(-12345, TextFormat.parseInt32("-12345"));
904     assertEquals(2147483647, TextFormat.parseInt32("2147483647"));
905     assertEquals(-2147483648, TextFormat.parseInt32("-2147483648"));
906 
907     assertEquals(0, TextFormat.parseUInt32("0"));
908     assertEquals(1, TextFormat.parseUInt32("1"));
909     assertEquals(12345, TextFormat.parseUInt32("12345"));
910     assertEquals(2147483647, TextFormat.parseUInt32("2147483647"));
911     assertEquals((int) 2147483648L, TextFormat.parseUInt32("2147483648"));
912     assertEquals((int) 4294967295L, TextFormat.parseUInt32("4294967295"));
913 
914     assertEquals(0L, TextFormat.parseInt64("0"));
915     assertEquals(1L, TextFormat.parseInt64("1"));
916     assertEquals(-1L, TextFormat.parseInt64("-1"));
917     assertEquals(12345L, TextFormat.parseInt64("12345"));
918     assertEquals(-12345L, TextFormat.parseInt64("-12345"));
919     assertEquals(2147483647L, TextFormat.parseInt64("2147483647"));
920     assertEquals(-2147483648L, TextFormat.parseInt64("-2147483648"));
921     assertEquals(4294967295L, TextFormat.parseInt64("4294967295"));
922     assertEquals(4294967296L, TextFormat.parseInt64("4294967296"));
923     assertEquals(9223372036854775807L, TextFormat.parseInt64("9223372036854775807"));
924     assertEquals(-9223372036854775808L, TextFormat.parseInt64("-9223372036854775808"));
925 
926     assertEquals(0L, TextFormat.parseUInt64("0"));
927     assertEquals(1L, TextFormat.parseUInt64("1"));
928     assertEquals(12345L, TextFormat.parseUInt64("12345"));
929     assertEquals(2147483647L, TextFormat.parseUInt64("2147483647"));
930     assertEquals(4294967295L, TextFormat.parseUInt64("4294967295"));
931     assertEquals(4294967296L, TextFormat.parseUInt64("4294967296"));
932     assertEquals(9223372036854775807L, TextFormat.parseUInt64("9223372036854775807"));
933     assertEquals(-9223372036854775808L, TextFormat.parseUInt64("9223372036854775808"));
934     assertEquals(-1L, TextFormat.parseUInt64("18446744073709551615"));
935 
936     // Hex
937     assertEquals(0x1234abcd, TextFormat.parseInt32("0x1234abcd"));
938     assertEquals(-0x1234abcd, TextFormat.parseInt32("-0x1234abcd"));
939     assertEquals(-1, TextFormat.parseUInt64("0xffffffffffffffff"));
940     assertEquals(0x7fffffffffffffffL, TextFormat.parseInt64("0x7fffffffffffffff"));
941 
942     // Octal
943     assertEquals(01234567, TextFormat.parseInt32("01234567"));
944 
945     // Out-of-range
946     try {
947       TextFormat.parseInt32("2147483648");
948       fail("Should have thrown an exception.");
949     } catch (NumberFormatException e) {
950       // success
951     }
952 
953     try {
954       TextFormat.parseInt32("-2147483649");
955       fail("Should have thrown an exception.");
956     } catch (NumberFormatException e) {
957       // success
958     }
959 
960     try {
961       TextFormat.parseUInt32("4294967296");
962       fail("Should have thrown an exception.");
963     } catch (NumberFormatException e) {
964       // success
965     }
966 
967     try {
968       TextFormat.parseUInt32("-1");
969       fail("Should have thrown an exception.");
970     } catch (NumberFormatException e) {
971       // success
972     }
973 
974     try {
975       TextFormat.parseInt64("9223372036854775808");
976       fail("Should have thrown an exception.");
977     } catch (NumberFormatException e) {
978       // success
979     }
980 
981     try {
982       TextFormat.parseInt64("-9223372036854775809");
983       fail("Should have thrown an exception.");
984     } catch (NumberFormatException e) {
985       // success
986     }
987 
988     try {
989       TextFormat.parseUInt64("18446744073709551616");
990       fail("Should have thrown an exception.");
991     } catch (NumberFormatException e) {
992       // success
993     }
994 
995     try {
996       TextFormat.parseUInt64("-1");
997       fail("Should have thrown an exception.");
998     } catch (NumberFormatException e) {
999       // success
1000     }
1001 
1002     // Not a number.
1003     try {
1004       TextFormat.parseInt32("abcd");
1005       fail("Should have thrown an exception.");
1006     } catch (NumberFormatException e) {
1007       // success
1008     }
1009   }
1010 
testParseString()1011   public void testParseString() throws Exception {
1012     final String zh = "\u9999\u6e2f\u4e0a\u6d77\ud84f\udf80\u8c50\u9280\u884c";
1013     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
1014     TextFormat.merge("optional_string: \"" + zh + "\"", builder);
1015     assertEquals(zh, builder.getOptionalString());
1016   }
1017 
testParseLongString()1018   public void testParseLongString() throws Exception {
1019     String longText =
1020         "123456789012345678901234567890123456789012345678901234567890"
1021             + "123456789012345678901234567890123456789012345678901234567890"
1022             + "123456789012345678901234567890123456789012345678901234567890"
1023             + "123456789012345678901234567890123456789012345678901234567890"
1024             + "123456789012345678901234567890123456789012345678901234567890"
1025             + "123456789012345678901234567890123456789012345678901234567890"
1026             + "123456789012345678901234567890123456789012345678901234567890"
1027             + "123456789012345678901234567890123456789012345678901234567890"
1028             + "123456789012345678901234567890123456789012345678901234567890"
1029             + "123456789012345678901234567890123456789012345678901234567890"
1030             + "123456789012345678901234567890123456789012345678901234567890"
1031             + "123456789012345678901234567890123456789012345678901234567890"
1032             + "123456789012345678901234567890123456789012345678901234567890"
1033             + "123456789012345678901234567890123456789012345678901234567890"
1034             + "123456789012345678901234567890123456789012345678901234567890"
1035             + "123456789012345678901234567890123456789012345678901234567890"
1036             + "123456789012345678901234567890123456789012345678901234567890"
1037             + "123456789012345678901234567890123456789012345678901234567890"
1038             + "123456789012345678901234567890123456789012345678901234567890"
1039             + "123456789012345678901234567890123456789012345678901234567890";
1040 
1041     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
1042     TextFormat.merge("optional_string: \"" + longText + "\"", builder);
1043     assertEquals(longText, builder.getOptionalString());
1044   }
1045 
testParseBoolean()1046   public void testParseBoolean() throws Exception {
1047     String goodText =
1048         "repeated_bool: t  repeated_bool : 0\n"
1049             + "repeated_bool :f repeated_bool:1\n"
1050             + "repeated_bool: False repeated_bool: True";
1051     String goodTextCanonical =
1052         "repeated_bool: true\n"
1053             + "repeated_bool: false\n"
1054             + "repeated_bool: false\n"
1055             + "repeated_bool: true\n"
1056             + "repeated_bool: false\n"
1057             + "repeated_bool: true\n";
1058     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
1059     TextFormat.merge(goodText, builder);
1060     assertEquals(goodTextCanonical, builder.build().toString());
1061 
1062     try {
1063       TestAllTypes.Builder badBuilder = TestAllTypes.newBuilder();
1064       TextFormat.merge("optional_bool:2", badBuilder);
1065       fail("Should have thrown an exception.");
1066     } catch (TextFormat.ParseException e) {
1067       // success
1068     }
1069     try {
1070       TestAllTypes.Builder badBuilder = TestAllTypes.newBuilder();
1071       TextFormat.merge("optional_bool: foo", badBuilder);
1072       fail("Should have thrown an exception.");
1073     } catch (TextFormat.ParseException e) {
1074       // success
1075     }
1076   }
1077 
testParseAdjacentStringLiterals()1078   public void testParseAdjacentStringLiterals() throws Exception {
1079     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
1080     TextFormat.merge("optional_string: \"foo\" 'corge' \"grault\"", builder);
1081     assertEquals("foocorgegrault", builder.getOptionalString());
1082   }
1083 
testPrintFieldValue()1084   public void testPrintFieldValue() throws Exception {
1085     assertPrintFieldValue("\"Hello\"", "Hello", "repeated_string");
1086     assertPrintFieldValue("123.0", 123f, "repeated_float");
1087     assertPrintFieldValue("123.0", 123d, "repeated_double");
1088     assertPrintFieldValue("123", 123, "repeated_int32");
1089     assertPrintFieldValue("123", 123L, "repeated_int64");
1090     assertPrintFieldValue("true", true, "repeated_bool");
1091     assertPrintFieldValue("4294967295", 0xFFFFFFFF, "repeated_uint32");
1092     assertPrintFieldValue("18446744073709551615", 0xFFFFFFFFFFFFFFFFL, "repeated_uint64");
1093     assertPrintFieldValue(
1094         "\"\\001\\002\\003\"", ByteString.copyFrom(new byte[] {1, 2, 3}), "repeated_bytes");
1095   }
1096 
assertPrintFieldValue(String expect, Object value, String fieldName)1097   private void assertPrintFieldValue(String expect, Object value, String fieldName)
1098       throws Exception {
1099     StringBuilder sb = new StringBuilder();
1100     TextFormat.printer()
1101         .printFieldValue(TestAllTypes.getDescriptor().findFieldByName(fieldName), value, sb);
1102     assertEquals(expect, sb.toString());
1103   }
1104 
testShortDebugString()1105   public void testShortDebugString() {
1106     assertEquals(
1107         "optional_nested_message { bb: 42 } repeated_int32: 1 repeated_uint32: 2",
1108         TextFormat.shortDebugString(
1109             TestAllTypes.newBuilder()
1110                 .addRepeatedInt32(1)
1111                 .addRepeatedUint32(2)
1112                 .setOptionalNestedMessage(NestedMessage.newBuilder().setBb(42).build())
1113                 .build()));
1114   }
1115 
testShortDebugString_field()1116   public void testShortDebugString_field() {
1117     final FieldDescriptor dataField = OneString.getDescriptor().findFieldByName("data");
1118     assertEquals(
1119         "data: \"test data\"",
1120         TextFormat.printer().shortDebugString(dataField, "test data"));
1121 
1122     final FieldDescriptor optionalField =
1123         TestAllTypes.getDescriptor().findFieldByName("optional_nested_message");
1124     final Object value = NestedMessage.newBuilder().setBb(42).build();
1125 
1126     assertEquals(
1127         "optional_nested_message { bb: 42 }",
1128         TextFormat.printer().shortDebugString(optionalField, value));
1129   }
1130 
testShortDebugString_unknown()1131   public void testShortDebugString_unknown() {
1132     assertEquals(
1133         "5: 1 5: 0x00000002 5: 0x0000000000000003 5: \"4\" 5: { 12: 6 } 5 { 10: 5 }"
1134             + " 8: 1 8: 2 8: 3 15: 12379813812177893520 15: 0xabcd1234 15:"
1135             + " 0xabcdef1234567890",
1136         TextFormat.printer().shortDebugString(makeUnknownFieldSet()));
1137   }
1138 
testPrintToUnicodeString()1139   public void testPrintToUnicodeString() throws Exception {
1140     assertEquals(
1141         "optional_string: \"abc\u3042efg\"\n"
1142             + "optional_bytes: \"\\343\\201\\202\"\n"
1143             + "repeated_string: \"\u3093XYZ\"\n",
1144         TextFormat.printer()
1145             .escapingNonAscii(false)
1146             .printToString(
1147                 TestAllTypes.newBuilder()
1148                     .setOptionalString("abc\u3042efg")
1149                     .setOptionalBytes(bytes(0xe3, 0x81, 0x82))
1150                     .addRepeatedString("\u3093XYZ")
1151                     .build()));
1152 
1153     // Double quotes and backslashes should be escaped
1154     assertEquals(
1155         "optional_string: \"a\\\\bc\\\"ef\\\"g\"\n",
1156         TextFormat.printer()
1157             .escapingNonAscii(false)
1158             .printToString(TestAllTypes.newBuilder().setOptionalString("a\\bc\"ef\"g").build()));
1159 
1160     // Test escaping roundtrip
1161     TestAllTypes message = TestAllTypes.newBuilder().setOptionalString("a\\bc\\\"ef\"g").build();
1162     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
1163     TextFormat.merge(TextFormat.printer().escapingNonAscii(false).printToString(message), builder);
1164     assertEquals(message.getOptionalString(), builder.getOptionalString());
1165   }
1166 
testPrintToUnicodeStringWithNewlines()1167   public void testPrintToUnicodeStringWithNewlines() throws Exception {
1168     // No newlines at start and end
1169     assertEquals(
1170         "optional_string: \"test newlines\\n\\nin\\nstring\"\n",
1171         TextFormat.printer()
1172             .escapingNonAscii(false)
1173             .printToString(
1174                 TestAllTypes.newBuilder()
1175                     .setOptionalString("test newlines\n\nin\nstring")
1176                     .build()));
1177 
1178     // Newlines at start and end
1179     assertEquals(
1180         "optional_string: \"\\ntest\\nnewlines\\n\\nin\\nstring\\n\"\n",
1181         TextFormat.printer()
1182             .escapingNonAscii(false)
1183             .printToString(
1184                 TestAllTypes.newBuilder()
1185                     .setOptionalString("\ntest\nnewlines\n\nin\nstring\n")
1186                     .build()));
1187 
1188     // Strings with 0, 1 and 2 newlines.
1189     assertEquals(
1190         "optional_string: \"\"\n",
1191         TextFormat.printer()
1192             .escapingNonAscii(false)
1193             .printToString(TestAllTypes.newBuilder().setOptionalString("").build()));
1194     assertEquals(
1195         "optional_string: \"\\n\"\n",
1196         TextFormat.printer()
1197             .escapingNonAscii(false)
1198             .printToString(TestAllTypes.newBuilder().setOptionalString("\n").build()));
1199     assertEquals(
1200         "optional_string: \"\\n\\n\"\n",
1201         TextFormat.printer()
1202             .escapingNonAscii(false)
1203             .printToString(TestAllTypes.newBuilder().setOptionalString("\n\n").build()));
1204 
1205     // Test escaping roundtrip
1206     TestAllTypes message =
1207         TestAllTypes.newBuilder().setOptionalString("\ntest\nnewlines\n\nin\nstring\n").build();
1208     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
1209     TextFormat.merge(TextFormat.printer().escapingNonAscii(false).printToString(message), builder);
1210     assertEquals(message.getOptionalString(), builder.getOptionalString());
1211   }
1212 
testPrintToUnicodeString_unknown()1213   public void testPrintToUnicodeString_unknown() {
1214     assertEquals(
1215         "1: \"\\343\\201\\202\"\n",
1216         TextFormat.printer()
1217             .escapingNonAscii(false)
1218             .printToString(
1219                 UnknownFieldSet.newBuilder()
1220                     .addField(
1221                         1,
1222                         UnknownFieldSet.Field.newBuilder()
1223                             .addLengthDelimited(bytes(0xe3, 0x81, 0x82))
1224                             .build())
1225                     .build()));
1226   }
1227 
1228 
testParseUnknownExtensions()1229   public void testParseUnknownExtensions() throws Exception {
1230     TestUtil.TestLogHandler logHandler = new TestUtil.TestLogHandler();
1231     Logger logger = Logger.getLogger(TextFormat.class.getName());
1232     logger.addHandler(logHandler);
1233     // Test unknown extension can pass.
1234     assertParseSuccessWithUnknownExtensions("[unknown_extension]: 123");
1235     assertParseSuccessWithUnknownExtensions(
1236         "[unknown_extension]: 123\n" + "[unknown_ext]: inf\n" + "[unknown]: 1.234");
1237     // Test warning messages.
1238     assertEquals(
1239         "Input contains unknown fields and/or extensions:\n"
1240             + "1:2:\tprotobuf_unittest.TestAllTypes.[unknown_extension]",
1241         logHandler.getStoredLogRecords().get(0).getMessage());
1242     assertEquals(
1243         "Input contains unknown fields and/or extensions:\n"
1244             + "1:2:\tprotobuf_unittest.TestAllTypes.[unknown_extension]\n"
1245             + "2:2:\tprotobuf_unittest.TestAllTypes.[unknown_ext]\n"
1246             + "3:2:\tprotobuf_unittest.TestAllTypes.[unknown]",
1247         logHandler.getStoredLogRecords().get(1).getMessage());
1248 
1249     // Test unknown field can not pass.
1250     assertParseErrorWithUnknownExtensions(
1251         "2:1: Input contains unknown fields and/or extensions:\n"
1252             + "1:2:\tprotobuf_unittest.TestAllTypes.[unknown_extension]\n"
1253             + "2:1:\tprotobuf_unittest.TestAllTypes.unknown_field",
1254         "[unknown_extension]: 1\n" + "unknown_field: 12345");
1255     assertParseErrorWithUnknownExtensions(
1256         "3:1: Input contains unknown fields and/or extensions:\n"
1257             + "1:2:\tprotobuf_unittest.TestAllTypes.[unknown_extension1]\n"
1258             + "2:2:\tprotobuf_unittest.TestAllTypes.[unknown_extension2]\n"
1259             + "3:1:\tprotobuf_unittest.TestAllTypes.unknown_field\n"
1260             + "4:2:\tprotobuf_unittest.TestAllTypes.[unknown_extension3]",
1261         "[unknown_extension1]: 1\n"
1262             + "[unknown_extension2]: 2\n"
1263             + "unknown_field: 12345\n"
1264             + "[unknown_extension3]: 3\n");
1265     assertParseErrorWithUnknownExtensions(
1266         "1:1: Input contains unknown fields and/or extensions:\n"
1267             + "1:1:\tprotobuf_unittest.TestAllTypes.unknown_field1\n"
1268             + "2:1:\tprotobuf_unittest.TestAllTypes.unknown_field2\n"
1269             + "3:2:\tprotobuf_unittest.TestAllTypes.[unknown_extension]\n"
1270             + "4:1:\tprotobuf_unittest.TestAllTypes.unknown_field3",
1271         "unknown_field1: 1\n"
1272             + "unknown_field2: 2\n"
1273             + "[unknown_extension]: 12345\n"
1274             + "unknown_field3: 3\n");
1275   }
1276 
1277   // See additional coverage in testOneofOverwriteForbidden and testMapOverwriteForbidden.
testParseNonRepeatedFields()1278   public void testParseNonRepeatedFields() throws Exception {
1279     assertParseSuccessWithOverwriteForbidden("repeated_int32: 1\nrepeated_int32: 2\n");
1280     assertParseSuccessWithOverwriteForbidden("RepeatedGroup { a: 1 }\nRepeatedGroup { a: 2 }\n");
1281     assertParseSuccessWithOverwriteForbidden(
1282         "repeated_nested_message { bb: 1 }\nrepeated_nested_message { bb: 2 }\n");
1283 
1284     assertParseErrorWithOverwriteForbidden(
1285         "3:15: Non-repeated field "
1286             + "\"protobuf_unittest.TestAllTypes.optional_int32\" "
1287             + "cannot be overwritten.",
1288         "optional_int32: 1\noptional_bool: true\noptional_int32: 1\n");
1289     assertParseErrorWithOverwriteForbidden(
1290         "2:1: Non-repeated field "
1291             + "\"protobuf_unittest.TestAllTypes.optionalgroup\" "
1292             + "cannot be overwritten.",
1293         "OptionalGroup { a: 1 }\nOptionalGroup { }\n");
1294     assertParseErrorWithOverwriteForbidden(
1295         "2:1: Non-repeated field "
1296             + "\"protobuf_unittest.TestAllTypes.optional_nested_message\" "
1297             + "cannot be overwritten.",
1298         "optional_nested_message { }\noptional_nested_message { bb: 3 }\n");
1299     assertParseErrorWithOverwriteForbidden(
1300         "2:14: Non-repeated field "
1301             + "\"protobuf_unittest.TestAllTypes.default_int32\" "
1302             + "cannot be overwritten.",
1303         "default_int32: 41\n"
1304             + // the default value
1305             "default_int32: 41\n");
1306     assertParseErrorWithOverwriteForbidden(
1307         "2:15: Non-repeated field "
1308             + "\"protobuf_unittest.TestAllTypes.default_string\" "
1309             + "cannot be overwritten.",
1310         "default_string: \"zxcv\"\ndefault_string: \"asdf\"\n");
1311   }
1312 
testParseShortRepeatedFormOfRepeatedFields()1313   public void testParseShortRepeatedFormOfRepeatedFields() throws Exception {
1314     assertParseSuccessWithOverwriteForbidden("repeated_foreign_enum: [FOREIGN_FOO, FOREIGN_BAR]");
1315     assertParseSuccessWithOverwriteForbidden("repeated_int32: [ 1, 2 ]\n");
1316     assertParseSuccessWithOverwriteForbidden("RepeatedGroup [{ a: 1 },{ a: 2 }]\n");
1317     assertParseSuccessWithOverwriteForbidden("repeated_nested_message [{ bb: 1 }, { bb: 2 }]\n");
1318     // See also testMapShortForm.
1319   }
1320 
testParseShortRepeatedFormOfEmptyRepeatedFields()1321   public void testParseShortRepeatedFormOfEmptyRepeatedFields() throws Exception {
1322     assertParseSuccessWithOverwriteForbidden("repeated_foreign_enum: []");
1323     assertParseSuccessWithOverwriteForbidden("repeated_int32: []\n");
1324     assertParseSuccessWithOverwriteForbidden("RepeatedGroup []\n");
1325     assertParseSuccessWithOverwriteForbidden("repeated_nested_message []\n");
1326     // See also testMapShortFormEmpty.
1327   }
1328 
testParseShortRepeatedFormWithTrailingComma()1329   public void testParseShortRepeatedFormWithTrailingComma() throws Exception {
1330     assertParseErrorWithOverwriteForbidden(
1331         "1:38: Expected identifier. Found \']\'", "repeated_foreign_enum: [FOREIGN_FOO, ]\n");
1332     assertParseErrorWithOverwriteForbidden(
1333         "1:22: Couldn't parse integer: For input string: \"]\"", "repeated_int32: [ 1, ]\n");
1334     assertParseErrorWithOverwriteForbidden("1:25: Expected \"{\".", "RepeatedGroup [{ a: 1 },]\n");
1335     assertParseErrorWithOverwriteForbidden(
1336         "1:37: Expected \"{\".", "repeated_nested_message [{ bb: 1 }, ]\n");
1337     // See also testMapShortFormTrailingComma.
1338   }
1339 
testParseShortRepeatedFormOfNonRepeatedFields()1340   public void testParseShortRepeatedFormOfNonRepeatedFields() throws Exception {
1341     assertParseErrorWithOverwriteForbidden(
1342         "1:17: Couldn't parse integer: For input string: \"[\"", "optional_int32: [1]\n");
1343     assertParseErrorWithOverwriteForbidden(
1344         "1:17: Couldn't parse integer: For input string: \"[\"", "optional_int32: []\n");
1345   }
1346 
1347   // =======================================================================
1348   // test oneof
1349 
testOneofTextFormat()1350   public void testOneofTextFormat() throws Exception {
1351     TestOneof2.Builder builder = TestOneof2.newBuilder();
1352     TestUtil.setOneof(builder);
1353     TestOneof2 message = builder.build();
1354     TestOneof2.Builder dest = TestOneof2.newBuilder();
1355     TextFormat.merge(TextFormat.printer().escapingNonAscii(false).printToString(message), dest);
1356     TestUtil.assertOneofSet(dest.build());
1357   }
1358 
testOneofOverwriteForbidden()1359   public void testOneofOverwriteForbidden() throws Exception {
1360     String input = "foo_string: \"stringvalue\" foo_int: 123";
1361     TestOneof2.Builder builder = TestOneof2.newBuilder();
1362     try {
1363       parserWithOverwriteForbidden.merge(input, TestUtil.getFullExtensionRegistry(), builder);
1364       fail("Expected parse exception.");
1365     } catch (TextFormat.ParseException e) {
1366       assertEquals(
1367           "1:34: Field \"protobuf_unittest.TestOneof2.foo_int\""
1368               + " is specified along with field \"protobuf_unittest.TestOneof2.foo_string\","
1369               + " another member of oneof \"foo\".",
1370           e.getMessage());
1371     }
1372   }
1373 
testOneofOverwriteAllowed()1374   public void testOneofOverwriteAllowed() throws Exception {
1375     String input = "foo_string: \"stringvalue\" foo_int: 123";
1376     TestOneof2.Builder builder = TestOneof2.newBuilder();
1377     defaultParser.merge(input, TestUtil.getFullExtensionRegistry(), builder);
1378     // Only the last value sticks.
1379     TestOneof2 oneof = builder.build();
1380     assertFalse(oneof.hasFooString());
1381     assertTrue(oneof.hasFooInt());
1382   }
1383 
1384   // =======================================================================
1385   // test map
1386 
testMapTextFormat()1387   public void testMapTextFormat() throws Exception {
1388     TestMap message =
1389         TestMap.newBuilder()
1390             .putInt32ToStringField(10, "apple")
1391             .putInt32ToStringField(20, "banana")
1392             .putInt32ToStringField(30, "cherry")
1393             .build();
1394     String text = TextFormat.printer().escapingNonAscii(false).printToString(message);
1395     {
1396       TestMap.Builder dest = TestMap.newBuilder();
1397       TextFormat.merge(text, dest);
1398       assertEquals(message, dest.build());
1399     }
1400     {
1401       TestMap.Builder dest = TestMap.newBuilder();
1402       parserWithOverwriteForbidden.merge(text, dest);
1403       assertEquals(message, dest.build());
1404     }
1405   }
1406 
testMapDuplicateKeys()1407   public void testMapDuplicateKeys() throws Exception {
1408     String input =
1409         "int32_to_int32_field: {\n"
1410             + "  key: 1\n"
1411             + "  value: 1\n"
1412             + "}\n"
1413             + "int32_to_int32_field: {\n"
1414             + "  key: -2147483647\n"
1415             + "  value: 5\n"
1416             + "}\n"
1417             + "int32_to_int32_field: {\n"
1418             + "  key: 1\n"
1419             + "  value: -1\n"
1420             + "}\n";
1421     TestMap msg = TextFormat.parse(input, TestMap.class);
1422     int i1 = msg.getInt32ToInt32Field().get(1);
1423     TestMap msg2 = TextFormat.parse(msg.toString(), TestMap.class);
1424     int i2 = msg2.getInt32ToInt32Field().get(1);
1425     assertEquals(i1, i2);
1426   }
1427 
testMapShortForm()1428   public void testMapShortForm() throws Exception {
1429     String text =
1430         "string_to_int32_field [{ key: 'x' value: 10 }, { key: 'y' value: 20 }]\n"
1431             + "int32_to_message_field "
1432             + "[{ key: 1 value { value: 100 } }, { key: 2 value: { value: 200 } }]\n";
1433     TestMap.Builder dest = TestMap.newBuilder();
1434     parserWithOverwriteForbidden.merge(text, dest);
1435     TestMap message = dest.build();
1436     assertEquals(2, message.getStringToInt32Field().size());
1437     assertEquals(2, message.getInt32ToMessageField().size());
1438     assertEquals(10, message.getStringToInt32Field().get("x").intValue());
1439     assertEquals(200, message.getInt32ToMessageField().get(2).getValue());
1440   }
1441 
testMapShortFormEmpty()1442   public void testMapShortFormEmpty() throws Exception {
1443     String text = "string_to_int32_field []\nint32_to_message_field: []\n";
1444     TestMap.Builder dest = TestMap.newBuilder();
1445     parserWithOverwriteForbidden.merge(text, dest);
1446     TestMap message = dest.build();
1447     assertEquals(0, message.getStringToInt32Field().size());
1448     assertEquals(0, message.getInt32ToMessageField().size());
1449   }
1450 
testMapShortFormTrailingComma()1451   public void testMapShortFormTrailingComma() throws Exception {
1452     String text = "string_to_int32_field [{ key: 'x' value: 10 }, ]\n";
1453     TestMap.Builder dest = TestMap.newBuilder();
1454     try {
1455       parserWithOverwriteForbidden.merge(text, dest);
1456       fail("Expected parse exception.");
1457     } catch (TextFormat.ParseException e) {
1458       assertEquals("1:48: Expected \"{\".", e.getMessage());
1459     }
1460   }
1461 
testMapOverwrite()1462   public void testMapOverwrite() throws Exception {
1463     String text =
1464         "int32_to_int32_field { key: 1 value: 10 }\n"
1465             + "int32_to_int32_field { key: 2 value: 20 }\n"
1466             + "int32_to_int32_field { key: 1 value: 30 }\n";
1467 
1468     {
1469       // With default parser, last value set for the key holds.
1470       TestMap.Builder builder = TestMap.newBuilder();
1471       defaultParser.merge(text, builder);
1472       TestMap map = builder.build();
1473       assertEquals(2, map.getInt32ToInt32Field().size());
1474       assertEquals(30, map.getInt32ToInt32Field().get(1).intValue());
1475     }
1476 
1477     {
1478       // With overwrite forbidden, same behavior.
1479       // TODO(b/29122459): Expect parse exception here.
1480       TestMap.Builder builder = TestMap.newBuilder();
1481       defaultParser.merge(text, builder);
1482       TestMap map = builder.build();
1483       assertEquals(2, map.getInt32ToInt32Field().size());
1484       assertEquals(30, map.getInt32ToInt32Field().get(1).intValue());
1485     }
1486   }
1487 
1488   // =======================================================================
1489   // test location information
1490 
testParseInfoTreeBuilding()1491   public void testParseInfoTreeBuilding() throws Exception {
1492     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
1493 
1494     Descriptor descriptor = TestAllTypes.getDescriptor();
1495     TextFormatParseInfoTree.Builder treeBuilder = TextFormatParseInfoTree.builder();
1496     // Set to allow unknown fields
1497     TextFormat.Parser parser =
1498         TextFormat.Parser.newBuilder()
1499             .setAllowUnknownFields(true)
1500             .setParseInfoTreeBuilder(treeBuilder)
1501             .build();
1502 
1503     final String stringData =
1504         "optional_int32: 1\n"
1505             + "optional_int64: 2\n"
1506             + "  optional_double: 2.4\n"
1507             + "repeated_int32: 5\n"
1508             + "repeated_int32: 10\n"
1509             + "optional_nested_message <\n"
1510             + "  bb: 78\n"
1511             + ">\n"
1512             + "repeated_nested_message <\n"
1513             + "  bb: 79\n"
1514             + ">\n"
1515             + "repeated_nested_message <\n"
1516             + "  bb: 80\n"
1517             + ">";
1518 
1519     parser.merge(stringData, builder);
1520     TextFormatParseInfoTree tree = treeBuilder.build();
1521 
1522     // Verify that the tree has the correct positions.
1523     assertLocation(tree, descriptor, "optional_int32", 0, 0, 0);
1524     assertLocation(tree, descriptor, "optional_int64", 0, 1, 0);
1525     assertLocation(tree, descriptor, "optional_double", 0, 2, 2);
1526 
1527     assertLocation(tree, descriptor, "repeated_int32", 0, 3, 0);
1528     assertLocation(tree, descriptor, "repeated_int32", 1, 4, 0);
1529 
1530     assertLocation(tree, descriptor, "optional_nested_message", 0, 5, 0);
1531     assertLocation(tree, descriptor, "repeated_nested_message", 0, 8, 0);
1532     assertLocation(tree, descriptor, "repeated_nested_message", 1, 11, 0);
1533 
1534     // Check for fields not set. For an invalid field, the location returned should be -1, -1.
1535     assertLocation(tree, descriptor, "repeated_int64", 0, -1, -1);
1536     assertLocation(tree, descriptor, "repeated_int32", 6, -1, -1);
1537 
1538     // Verify inside the nested message.
1539     FieldDescriptor nestedField = descriptor.findFieldByName("optional_nested_message");
1540 
1541     TextFormatParseInfoTree nestedTree = tree.getNestedTrees(nestedField).get(0);
1542     assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 6, 2);
1543 
1544     // Verify inside another nested message.
1545     nestedField = descriptor.findFieldByName("repeated_nested_message");
1546     nestedTree = tree.getNestedTrees(nestedField).get(0);
1547     assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 9, 2);
1548 
1549     nestedTree = tree.getNestedTrees(nestedField).get(1);
1550     assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 12, 2);
1551 
1552     // Verify a NULL tree for an unknown nested field.
1553     try {
1554       tree.getNestedTree(nestedField, 2);
1555       fail("unknown nested field should throw");
1556     } catch (IllegalArgumentException unused) {
1557       // pass
1558     }
1559   }
1560 
assertLocation( TextFormatParseInfoTree tree, final Descriptor descriptor, final String fieldName, int index, int line, int column)1561   private void assertLocation(
1562       TextFormatParseInfoTree tree,
1563       final Descriptor descriptor,
1564       final String fieldName,
1565       int index,
1566       int line,
1567       int column) {
1568     List<TextFormatParseLocation> locs = tree.getLocations(descriptor.findFieldByName(fieldName));
1569     if (index < locs.size()) {
1570       TextFormatParseLocation location = locs.get(index);
1571       TextFormatParseLocation expected = TextFormatParseLocation.create(line, column);
1572       assertEquals(expected, location);
1573     } else if (line != -1 && column != -1) {
1574       fail(
1575           String.format(
1576               "Tree/descriptor/fieldname did not contain index %d, line %d column %d expected",
1577               index, line, column));
1578     }
1579   }
1580 
testSortMapFields()1581   public void testSortMapFields() throws Exception {
1582     TestMap message =
1583         TestMap.newBuilder()
1584             .putStringToInt32Field("cherry", 30)
1585             .putStringToInt32Field("banana", 20)
1586             .putStringToInt32Field("apple", 10)
1587             .putInt32ToStringField(30, "cherry")
1588             .putInt32ToStringField(20, "banana")
1589             .putInt32ToStringField(10, "apple")
1590             .build();
1591     String text =
1592         "int32_to_string_field {\n"
1593             + "  key: 10\n"
1594             + "  value: \"apple\"\n"
1595             + "}\n"
1596             + "int32_to_string_field {\n"
1597             + "  key: 20\n"
1598             + "  value: \"banana\"\n"
1599             + "}\n"
1600             + "int32_to_string_field {\n"
1601             + "  key: 30\n"
1602             + "  value: \"cherry\"\n"
1603             + "}\n"
1604             + "string_to_int32_field {\n"
1605             + "  key: \"apple\"\n"
1606             + "  value: 10\n"
1607             + "}\n"
1608             + "string_to_int32_field {\n"
1609             + "  key: \"banana\"\n"
1610             + "  value: 20\n"
1611             + "}\n"
1612             + "string_to_int32_field {\n"
1613             + "  key: \"cherry\"\n"
1614             + "  value: 30\n"
1615             + "}\n";
1616     assertEquals(text, TextFormat.printer().printToString(message));
1617   }
1618 }
1619