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