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