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