• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#! /usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Protocol Buffers - Google's data interchange format
5# Copyright 2008 Google Inc.  All rights reserved.
6# https://developers.google.com/protocol-buffers/
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions are
10# met:
11#
12#     * Redistributions of source code must retain the above copyright
13# notice, this list of conditions and the following disclaimer.
14#     * Redistributions in binary form must reproduce the above
15# copyright notice, this list of conditions and the following disclaimer
16# in the documentation and/or other materials provided with the
17# distribution.
18#     * Neither the name of Google Inc. nor the names of its
19# contributors may be used to endorse or promote products derived from
20# this software without specific prior written permission.
21#
22# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34"""Test for google.protobuf.text_format."""
35
36import io
37import math
38import re
39import string
40import textwrap
41
42import six
43
44# pylint: disable=g-import-not-at-top
45try:
46  import unittest2 as unittest  # PY26
47except ImportError:
48  import unittest
49
50from google.protobuf import any_pb2
51from google.protobuf import any_test_pb2
52from google.protobuf import map_unittest_pb2
53from google.protobuf import unittest_custom_options_pb2
54from google.protobuf import unittest_mset_pb2
55from google.protobuf import unittest_pb2
56from google.protobuf import unittest_proto3_arena_pb2
57from google.protobuf import descriptor_pb2
58from google.protobuf.internal import any_test_pb2 as test_extend_any
59from google.protobuf.internal import message_set_extensions_pb2
60from google.protobuf.internal import test_proto3_optional_pb2
61from google.protobuf.internal import test_util
62from google.protobuf import descriptor_pool
63from google.protobuf import text_format
64from google.protobuf.internal import _parameterized
65# pylint: enable=g-import-not-at-top
66
67
68# Low-level nuts-n-bolts tests.
69class SimpleTextFormatTests(unittest.TestCase):
70
71  # The members of _QUOTES are formatted into a regexp template that
72  # expects single characters.  Therefore it's an error (in addition to being
73  # non-sensical in the first place) to try to specify a "quote mark" that is
74  # more than one character.
75  def testQuoteMarksAreSingleChars(self):
76    for quote in text_format._QUOTES:
77      self.assertEqual(1, len(quote))
78
79
80# Base class with some common functionality.
81class TextFormatBase(unittest.TestCase):
82
83  def ReadGolden(self, golden_filename):
84    with test_util.GoldenFile(golden_filename) as f:
85      return (f.readlines() if str is bytes else  # PY3
86              [golden_line.decode('utf-8') for golden_line in f])
87
88  def CompareToGoldenFile(self, text, golden_filename):
89    golden_lines = self.ReadGolden(golden_filename)
90    self.assertMultiLineEqual(text, ''.join(golden_lines))
91
92  def CompareToGoldenText(self, text, golden_text):
93    self.assertEqual(text, golden_text)
94
95  def RemoveRedundantZeros(self, text):
96    # Some platforms print 1e+5 as 1e+005.  This is fine, but we need to remove
97    # these zeros in order to match the golden file.
98    text = text.replace('e+0','e+').replace('e+0','e+') \
99               .replace('e-0','e-').replace('e-0','e-')
100    # Floating point fields are printed with .0 suffix even if they are
101    # actually integer numbers.
102    text = re.compile(r'\.0$', re.MULTILINE).sub('', text)
103    return text
104
105
106@_parameterized.parameters(unittest_pb2, unittest_proto3_arena_pb2)
107class TextFormatMessageToStringTests(TextFormatBase):
108
109  def testPrintExotic(self, message_module):
110    message = message_module.TestAllTypes()
111    message.repeated_int64.append(-9223372036854775808)
112    message.repeated_uint64.append(18446744073709551615)
113    message.repeated_double.append(123.456)
114    message.repeated_double.append(1.23e22)
115    message.repeated_double.append(1.23e-18)
116    message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
117    message.repeated_string.append(u'\u00fc\ua71f')
118    self.CompareToGoldenText(
119        self.RemoveRedundantZeros(text_format.MessageToString(message)),
120        'repeated_int64: -9223372036854775808\n'
121        'repeated_uint64: 18446744073709551615\n'
122        'repeated_double: 123.456\n'
123        'repeated_double: 1.23e+22\n'
124        'repeated_double: 1.23e-18\n'
125        'repeated_string:'
126        ' "\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
127        'repeated_string: "\\303\\274\\352\\234\\237"\n')
128
129  def testPrintFloatPrecision(self, message_module):
130    message = message_module.TestAllTypes()
131
132    message.repeated_float.append(0.0)
133    message.repeated_float.append(0.8)
134    message.repeated_float.append(1.0)
135    message.repeated_float.append(1.2)
136    message.repeated_float.append(1.23)
137    message.repeated_float.append(1.234)
138    message.repeated_float.append(1.2345)
139    message.repeated_float.append(1.23456)
140    message.repeated_float.append(1.2e10)
141    message.repeated_float.append(1.23e10)
142    message.repeated_float.append(1.234e10)
143    message.repeated_float.append(1.2345e10)
144    message.repeated_float.append(1.23456e10)
145    message.repeated_double.append(0.0)
146    message.repeated_double.append(0.8)
147    message.repeated_double.append(1.0)
148    message.repeated_double.append(1.2)
149    message.repeated_double.append(1.23)
150    message.repeated_double.append(1.234)
151    message.repeated_double.append(1.2345)
152    message.repeated_double.append(1.23456)
153    message.repeated_double.append(1.234567)
154    message.repeated_double.append(1.2345678)
155    message.repeated_double.append(1.23456789)
156    message.repeated_double.append(1.234567898)
157    message.repeated_double.append(1.2345678987)
158    message.repeated_double.append(1.23456789876)
159    message.repeated_double.append(1.234567898765)
160    message.repeated_double.append(1.2345678987654)
161    message.repeated_double.append(1.23456789876543)
162    message.repeated_double.append(1.2e100)
163    message.repeated_double.append(1.23e100)
164    message.repeated_double.append(1.234e100)
165    message.repeated_double.append(1.2345e100)
166    message.repeated_double.append(1.23456e100)
167    message.repeated_double.append(1.234567e100)
168    message.repeated_double.append(1.2345678e100)
169    message.repeated_double.append(1.23456789e100)
170    message.repeated_double.append(1.234567898e100)
171    message.repeated_double.append(1.2345678987e100)
172    message.repeated_double.append(1.23456789876e100)
173    message.repeated_double.append(1.234567898765e100)
174    message.repeated_double.append(1.2345678987654e100)
175    message.repeated_double.append(1.23456789876543e100)
176    # pylint: disable=g-long-ternary
177    self.CompareToGoldenText(
178        self.RemoveRedundantZeros(text_format.MessageToString(message)),
179        'repeated_float: 0\n'
180        'repeated_float: 0.8\n'
181        'repeated_float: 1\n'
182        'repeated_float: 1.2\n'
183        'repeated_float: 1.23\n'
184        'repeated_float: 1.234\n'
185        'repeated_float: 1.2345\n'
186        'repeated_float: 1.23456\n'
187        # Note that these don't use scientific notation.
188        'repeated_float: 12000000000\n'
189        'repeated_float: 12300000000\n'
190        'repeated_float: 12340000000\n'
191        'repeated_float: 12345000000\n'
192        'repeated_float: 12345600000\n'
193        'repeated_double: 0\n'
194        'repeated_double: 0.8\n'
195        'repeated_double: 1\n'
196        'repeated_double: 1.2\n'
197        'repeated_double: 1.23\n'
198        'repeated_double: 1.234\n'
199        'repeated_double: 1.2345\n'
200        'repeated_double: 1.23456\n'
201        'repeated_double: 1.234567\n'
202        'repeated_double: 1.2345678\n'
203        'repeated_double: 1.23456789\n'
204        'repeated_double: 1.234567898\n'
205        'repeated_double: 1.2345678987\n'
206        'repeated_double: 1.23456789876\n' +
207        ('repeated_double: 1.23456789876\n'
208         'repeated_double: 1.23456789877\n'
209         'repeated_double: 1.23456789877\n'
210         if six.PY2 else
211         'repeated_double: 1.234567898765\n'
212         'repeated_double: 1.2345678987654\n'
213         'repeated_double: 1.23456789876543\n') +
214        'repeated_double: 1.2e+100\n'
215        'repeated_double: 1.23e+100\n'
216        'repeated_double: 1.234e+100\n'
217        'repeated_double: 1.2345e+100\n'
218        'repeated_double: 1.23456e+100\n'
219        'repeated_double: 1.234567e+100\n'
220        'repeated_double: 1.2345678e+100\n'
221        'repeated_double: 1.23456789e+100\n'
222        'repeated_double: 1.234567898e+100\n'
223        'repeated_double: 1.2345678987e+100\n'
224        'repeated_double: 1.23456789876e+100\n' +
225        ('repeated_double: 1.23456789877e+100\n'
226         'repeated_double: 1.23456789877e+100\n'
227         'repeated_double: 1.23456789877e+100\n'
228         if six.PY2 else
229         'repeated_double: 1.234567898765e+100\n'
230         'repeated_double: 1.2345678987654e+100\n'
231         'repeated_double: 1.23456789876543e+100\n'))
232
233  def testPrintExoticUnicodeSubclass(self, message_module):
234
235    class UnicodeSub(six.text_type):
236      pass
237
238    message = message_module.TestAllTypes()
239    message.repeated_string.append(UnicodeSub(u'\u00fc\ua71f'))
240    self.CompareToGoldenText(
241        text_format.MessageToString(message),
242        'repeated_string: "\\303\\274\\352\\234\\237"\n')
243
244  def testPrintNestedMessageAsOneLine(self, message_module):
245    message = message_module.TestAllTypes()
246    msg = message.repeated_nested_message.add()
247    msg.bb = 42
248    self.CompareToGoldenText(
249        text_format.MessageToString(message, as_one_line=True),
250        'repeated_nested_message { bb: 42 }')
251
252  def testPrintRepeatedFieldsAsOneLine(self, message_module):
253    message = message_module.TestAllTypes()
254    message.repeated_int32.append(1)
255    message.repeated_int32.append(1)
256    message.repeated_int32.append(3)
257    message.repeated_string.append('Google')
258    message.repeated_string.append('Zurich')
259    self.CompareToGoldenText(
260        text_format.MessageToString(message, as_one_line=True),
261        'repeated_int32: 1 repeated_int32: 1 repeated_int32: 3 '
262        'repeated_string: "Google" repeated_string: "Zurich"')
263
264  def VerifyPrintShortFormatRepeatedFields(self, message_module, as_one_line):
265    message = message_module.TestAllTypes()
266    message.repeated_int32.append(1)
267    message.repeated_string.append('Google')
268    message.repeated_string.append('Hello,World')
269    message.repeated_foreign_enum.append(unittest_pb2.FOREIGN_FOO)
270    message.repeated_foreign_enum.append(unittest_pb2.FOREIGN_BAR)
271    message.repeated_foreign_enum.append(unittest_pb2.FOREIGN_BAZ)
272    message.optional_nested_message.bb = 3
273    for i in (21, 32):
274      msg = message.repeated_nested_message.add()
275      msg.bb = i
276    expected_ascii = (
277        'optional_nested_message {\n  bb: 3\n}\n'
278        'repeated_int32: [1]\n'
279        'repeated_string: "Google"\n'
280        'repeated_string: "Hello,World"\n'
281        'repeated_nested_message {\n  bb: 21\n}\n'
282        'repeated_nested_message {\n  bb: 32\n}\n'
283        'repeated_foreign_enum: [FOREIGN_FOO, FOREIGN_BAR, FOREIGN_BAZ]\n')
284    if as_one_line:
285      expected_ascii = expected_ascii.replace('\n', ' ')
286      expected_ascii = re.sub(r'\s+', ' ', expected_ascii)
287      expected_ascii = re.sub(r'\s$', '', expected_ascii)
288
289    actual_ascii = text_format.MessageToString(
290        message, use_short_repeated_primitives=True,
291        as_one_line=as_one_line)
292    self.CompareToGoldenText(actual_ascii, expected_ascii)
293    parsed_message = message_module.TestAllTypes()
294    text_format.Parse(actual_ascii, parsed_message)
295    self.assertEqual(parsed_message, message)
296
297  def testPrintShortFormatRepeatedFields(self, message_module):
298    self.VerifyPrintShortFormatRepeatedFields(message_module, False)
299    self.VerifyPrintShortFormatRepeatedFields(message_module, True)
300
301  def testPrintNestedNewLineInStringAsOneLine(self, message_module):
302    message = message_module.TestAllTypes()
303    message.optional_string = 'a\nnew\nline'
304    self.CompareToGoldenText(
305        text_format.MessageToString(message, as_one_line=True),
306        'optional_string: "a\\nnew\\nline"')
307
308  def testPrintExoticAsOneLine(self, message_module):
309    message = message_module.TestAllTypes()
310    message.repeated_int64.append(-9223372036854775808)
311    message.repeated_uint64.append(18446744073709551615)
312    message.repeated_double.append(123.456)
313    message.repeated_double.append(1.23e22)
314    message.repeated_double.append(1.23e-18)
315    message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
316    message.repeated_string.append(u'\u00fc\ua71f')
317    self.CompareToGoldenText(
318        self.RemoveRedundantZeros(text_format.MessageToString(
319            message, as_one_line=True)),
320        'repeated_int64: -9223372036854775808'
321        ' repeated_uint64: 18446744073709551615'
322        ' repeated_double: 123.456'
323        ' repeated_double: 1.23e+22'
324        ' repeated_double: 1.23e-18'
325        ' repeated_string: '
326        '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""'
327        ' repeated_string: "\\303\\274\\352\\234\\237"')
328
329  def testRoundTripExoticAsOneLine(self, message_module):
330    message = message_module.TestAllTypes()
331    message.repeated_int64.append(-9223372036854775808)
332    message.repeated_uint64.append(18446744073709551615)
333    message.repeated_double.append(123.456)
334    message.repeated_double.append(1.23e22)
335    message.repeated_double.append(1.23e-18)
336    message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
337    message.repeated_string.append(u'\u00fc\ua71f')
338
339    # Test as_utf8 = False.
340    wire_text = text_format.MessageToString(message,
341                                            as_one_line=True,
342                                            as_utf8=False)
343    parsed_message = message_module.TestAllTypes()
344    r = text_format.Parse(wire_text, parsed_message)
345    self.assertIs(r, parsed_message)
346    self.assertEqual(message, parsed_message)
347
348    # Test as_utf8 = True.
349    wire_text = text_format.MessageToString(message,
350                                            as_one_line=True,
351                                            as_utf8=True)
352    parsed_message = message_module.TestAllTypes()
353    r = text_format.Parse(wire_text, parsed_message)
354    self.assertIs(r, parsed_message)
355    self.assertEqual(message, parsed_message,
356                     '\n%s != %s' % (message, parsed_message))
357
358  def testPrintRawUtf8String(self, message_module):
359    message = message_module.TestAllTypes()
360    message.repeated_string.append(u'\u00fc\t\ua71f')
361    text = text_format.MessageToString(message, as_utf8=True)
362    golden_unicode = u'repeated_string: "\u00fc\\t\ua71f"\n'
363    golden_text = golden_unicode if six.PY3 else golden_unicode.encode('utf-8')
364    # MessageToString always returns a native str.
365    self.CompareToGoldenText(text, golden_text)
366    parsed_message = message_module.TestAllTypes()
367    text_format.Parse(text, parsed_message)
368    self.assertEqual(
369        message, parsed_message, '\n%s != %s  (%s != %s)' %
370        (message, parsed_message, message.repeated_string[0],
371         parsed_message.repeated_string[0]))
372
373  def testPrintFloatFormat(self, message_module):
374    # Check that float_format argument is passed to sub-message formatting.
375    message = message_module.NestedTestAllTypes()
376    message.payload.optional_float = 1.25
377    # Check rounding at 15 significant digits
378    message.payload.optional_double = -.000003456789012345678
379    # Check no decimal point.
380    message.payload.repeated_float.append(-5642)
381    # Check no trailing zeros.
382    message.payload.repeated_double.append(.000078900)
383    formatted_fields = ['optional_float: 1.25',
384                        'optional_double: -3.45678901234568e-6',
385                        'repeated_float: -5642', 'repeated_double: 7.89e-5']
386    text_message = text_format.MessageToString(message, float_format='.15g')
387    self.CompareToGoldenText(
388        self.RemoveRedundantZeros(text_message),
389        'payload {{\n  {0}\n  {1}\n  {2}\n  {3}\n}}\n'.format(
390            *formatted_fields))
391    # as_one_line=True is a separate code branch where float_format is passed.
392    text_message = text_format.MessageToString(message,
393                                               as_one_line=True,
394                                               float_format='.15g')
395    self.CompareToGoldenText(
396        self.RemoveRedundantZeros(text_message),
397        'payload {{ {0} {1} {2} {3} }}'.format(*formatted_fields))
398
399    # 32-bit 1.2 is noisy when extended to 64-bit:
400    #  >>> struct.unpack('f', struct.pack('f', 1.2))[0]
401    #  1.2000000476837158
402    message.payload.optional_float = 1.2
403    formatted_fields = ['optional_float: 1.2',
404                        'optional_double: -3.45678901234568e-6',
405                        'repeated_float: -5642', 'repeated_double: 7.89e-5']
406    text_message = text_format.MessageToString(message, float_format='.7g',
407                                               double_format='.15g')
408    self.CompareToGoldenText(
409        self.RemoveRedundantZeros(text_message),
410        'payload {{\n  {0}\n  {1}\n  {2}\n  {3}\n}}\n'.format(
411            *formatted_fields))
412
413    # Test only set float_format affect both float and double fields.
414    formatted_fields = ['optional_float: 1.2',
415                        'optional_double: -3.456789e-6',
416                        'repeated_float: -5642', 'repeated_double: 7.89e-5']
417    text_message = text_format.MessageToString(message, float_format='.7g')
418    self.CompareToGoldenText(
419        self.RemoveRedundantZeros(text_message),
420        'payload {{\n  {0}\n  {1}\n  {2}\n  {3}\n}}\n'.format(
421            *formatted_fields))
422
423    # Test default float_format will automatic print shortest float.
424    message.payload.optional_float = 1.2345678912
425    message.payload.optional_double = 1.2345678912
426    formatted_fields = ['optional_float: 1.2345679',
427                        'optional_double: 1.2345678912',
428                        'repeated_float: -5642', 'repeated_double: 7.89e-5']
429    text_message = text_format.MessageToString(message)
430    self.CompareToGoldenText(
431        self.RemoveRedundantZeros(text_message),
432        'payload {{\n  {0}\n  {1}\n  {2}\n  {3}\n}}\n'.format(
433            *formatted_fields))
434
435    message.Clear()
436    message.payload.optional_float = 1.1000000000011
437    self.assertEqual(text_format.MessageToString(message),
438                     'payload {\n  optional_float: 1.1\n}\n')
439    message.payload.optional_float = 1.00000075e-36
440    self.assertEqual(text_format.MessageToString(message),
441                     'payload {\n  optional_float: 1.00000075e-36\n}\n')
442    message.payload.optional_float = 12345678912345e+11
443    self.assertEqual(text_format.MessageToString(message),
444                     'payload {\n  optional_float: 1.234568e+24\n}\n')
445
446  def testMessageToString(self, message_module):
447    message = message_module.ForeignMessage()
448    message.c = 123
449    self.assertEqual('c: 123\n', str(message))
450
451  def testMessageToStringUnicode(self, message_module):
452    golden_unicode = u'Á short desçription and a ��.'
453    golden_bytes = golden_unicode.encode('utf-8')
454    message = message_module.TestAllTypes()
455    message.optional_string = golden_unicode
456    message.optional_bytes = golden_bytes
457    text = text_format.MessageToString(message, as_utf8=True)
458    golden_message = textwrap.dedent(
459        'optional_string: "Á short desçription and a ��."\n'
460        'optional_bytes: '
461        r'"\303\201 short des\303\247ription and a \360\237\215\214."'
462        '\n')
463    self.CompareToGoldenText(text, golden_message)
464
465  def testMessageToStringASCII(self, message_module):
466    golden_unicode = u'Á short desçription and a ��.'
467    golden_bytes = golden_unicode.encode('utf-8')
468    message = message_module.TestAllTypes()
469    message.optional_string = golden_unicode
470    message.optional_bytes = golden_bytes
471    text = text_format.MessageToString(message, as_utf8=False)  # ASCII
472    golden_message = (
473        'optional_string: '
474        r'"\303\201 short des\303\247ription and a \360\237\215\214."'
475        '\n'
476        'optional_bytes: '
477        r'"\303\201 short des\303\247ription and a \360\237\215\214."'
478        '\n')
479    self.CompareToGoldenText(text, golden_message)
480
481  def testPrintField(self, message_module):
482    message = message_module.TestAllTypes()
483    field = message.DESCRIPTOR.fields_by_name['optional_float']
484    value = message.optional_float
485    out = text_format.TextWriter(False)
486    text_format.PrintField(field, value, out)
487    self.assertEqual('optional_float: 0.0\n', out.getvalue())
488    out.close()
489    # Test Printer
490    out = text_format.TextWriter(False)
491    printer = text_format._Printer(out)
492    printer.PrintField(field, value)
493    self.assertEqual('optional_float: 0.0\n', out.getvalue())
494    out.close()
495
496  def testPrintFieldValue(self, message_module):
497    message = message_module.TestAllTypes()
498    field = message.DESCRIPTOR.fields_by_name['optional_float']
499    value = message.optional_float
500    out = text_format.TextWriter(False)
501    text_format.PrintFieldValue(field, value, out)
502    self.assertEqual('0.0', out.getvalue())
503    out.close()
504    # Test Printer
505    out = text_format.TextWriter(False)
506    printer = text_format._Printer(out)
507    printer.PrintFieldValue(field, value)
508    self.assertEqual('0.0', out.getvalue())
509    out.close()
510
511  def testCustomOptions(self, message_module):
512    message_descriptor = (unittest_custom_options_pb2.
513                          TestMessageWithCustomOptions.DESCRIPTOR)
514    message_proto = descriptor_pb2.DescriptorProto()
515    message_descriptor.CopyToProto(message_proto)
516    expected_text = (
517        'name: "TestMessageWithCustomOptions"\n'
518        'field {\n'
519        '  name: "field1"\n'
520        '  number: 1\n'
521        '  label: LABEL_OPTIONAL\n'
522        '  type: TYPE_STRING\n'
523        '  options {\n'
524        '    ctype: CORD\n'
525        '    [protobuf_unittest.field_opt1]: 8765432109\n'
526        '  }\n'
527        '}\n'
528        'field {\n'
529        '  name: "oneof_field"\n'
530        '  number: 2\n'
531        '  label: LABEL_OPTIONAL\n'
532        '  type: TYPE_INT32\n'
533        '  oneof_index: 0\n'
534        '}\n'
535        'enum_type {\n'
536        '  name: "AnEnum"\n'
537        '  value {\n'
538        '    name: "ANENUM_VAL1"\n'
539        '    number: 1\n'
540        '  }\n'
541        '  value {\n'
542        '    name: "ANENUM_VAL2"\n'
543        '    number: 2\n'
544        '    options {\n'
545        '      [protobuf_unittest.enum_value_opt1]: 123\n'
546        '    }\n'
547        '  }\n'
548        '  options {\n'
549        '    [protobuf_unittest.enum_opt1]: -789\n'
550        '  }\n'
551        '}\n'
552        'options {\n'
553        '  message_set_wire_format: false\n'
554        '  [protobuf_unittest.message_opt1]: -56\n'
555        '}\n'
556        'oneof_decl {\n'
557        '  name: "AnOneof"\n'
558        '  options {\n'
559        '    [protobuf_unittest.oneof_opt1]: -99\n'
560        '  }\n'
561        '}\n')
562    self.assertEqual(expected_text,
563                     text_format.MessageToString(message_proto))
564    parsed_proto = descriptor_pb2.DescriptorProto()
565    text_format.Parse(expected_text, parsed_proto)
566    self.assertEqual(message_proto, parsed_proto)
567
568  def testPrintUnknownFieldsEmbeddedMessageInBytes(self, message_module):
569    inner_msg = message_module.TestAllTypes()
570    inner_msg.optional_int32 = 101
571    inner_msg.optional_double = 102.0
572    inner_msg.optional_string = u'hello'
573    inner_msg.optional_bytes = b'103'
574    inner_msg.optional_nested_message.bb = 105
575    inner_data = inner_msg.SerializeToString()
576    outer_message = message_module.TestAllTypes()
577    outer_message.optional_int32 = 101
578    outer_message.optional_bytes = inner_data
579    all_data = outer_message.SerializeToString()
580    empty_message = message_module.TestEmptyMessage()
581    empty_message.ParseFromString(all_data)
582
583    self.assertEqual('  1: 101\n'
584                     '  15 {\n'
585                     '    1: 101\n'
586                     '    12: 4636878028842991616\n'
587                     '    14: "hello"\n'
588                     '    15: "103"\n'
589                     '    18 {\n'
590                     '      1: 105\n'
591                     '    }\n'
592                     '  }\n',
593                     text_format.MessageToString(empty_message,
594                                                 indent=2,
595                                                 print_unknown_fields=True))
596    self.assertEqual('1: 101 '
597                     '15 { '
598                     '1: 101 '
599                     '12: 4636878028842991616 '
600                     '14: "hello" '
601                     '15: "103" '
602                     '18 { 1: 105 } '
603                     '}',
604                     text_format.MessageToString(empty_message,
605                                                 print_unknown_fields=True,
606                                                 as_one_line=True))
607
608
609@_parameterized.parameters(unittest_pb2, unittest_proto3_arena_pb2)
610class TextFormatMessageToTextBytesTests(TextFormatBase):
611
612  def testMessageToBytes(self, message_module):
613    message = message_module.ForeignMessage()
614    message.c = 123
615    self.assertEqual(b'c: 123\n', text_format.MessageToBytes(message))
616
617  def testRawUtf8RoundTrip(self, message_module):
618    message = message_module.TestAllTypes()
619    message.repeated_string.append(u'\u00fc\t\ua71f')
620    utf8_text = text_format.MessageToBytes(message, as_utf8=True)
621    golden_bytes = b'repeated_string: "\xc3\xbc\\t\xea\x9c\x9f"\n'
622    self.CompareToGoldenText(utf8_text, golden_bytes)
623    parsed_message = message_module.TestAllTypes()
624    text_format.Parse(utf8_text, parsed_message)
625    self.assertEqual(
626        message, parsed_message, '\n%s != %s  (%s != %s)' %
627        (message, parsed_message, message.repeated_string[0],
628         parsed_message.repeated_string[0]))
629
630  def testEscapedUtf8ASCIIRoundTrip(self, message_module):
631    message = message_module.TestAllTypes()
632    message.repeated_string.append(u'\u00fc\t\ua71f')
633    ascii_text = text_format.MessageToBytes(message)  # as_utf8=False default
634    golden_bytes = b'repeated_string: "\\303\\274\\t\\352\\234\\237"\n'
635    self.CompareToGoldenText(ascii_text, golden_bytes)
636    parsed_message = message_module.TestAllTypes()
637    text_format.Parse(ascii_text, parsed_message)
638    self.assertEqual(
639        message, parsed_message, '\n%s != %s  (%s != %s)' %
640        (message, parsed_message, message.repeated_string[0],
641         parsed_message.repeated_string[0]))
642
643
644@_parameterized.parameters(unittest_pb2, unittest_proto3_arena_pb2)
645class TextFormatParserTests(TextFormatBase):
646
647  def testParseAllFields(self, message_module):
648    message = message_module.TestAllTypes()
649    test_util.SetAllFields(message)
650    ascii_text = text_format.MessageToString(message)
651
652    parsed_message = message_module.TestAllTypes()
653    text_format.Parse(ascii_text, parsed_message)
654    self.assertEqual(message, parsed_message)
655    if message_module is unittest_pb2:
656      test_util.ExpectAllFieldsSet(self, message)
657
658  def testParseAndMergeUtf8(self, message_module):
659    message = message_module.TestAllTypes()
660    test_util.SetAllFields(message)
661    ascii_text = text_format.MessageToString(message)
662    ascii_text = ascii_text.encode('utf-8')
663
664    parsed_message = message_module.TestAllTypes()
665    text_format.Parse(ascii_text, parsed_message)
666    self.assertEqual(message, parsed_message)
667    if message_module is unittest_pb2:
668      test_util.ExpectAllFieldsSet(self, message)
669
670    parsed_message.Clear()
671    text_format.Merge(ascii_text, parsed_message)
672    self.assertEqual(message, parsed_message)
673    if message_module is unittest_pb2:
674      test_util.ExpectAllFieldsSet(self, message)
675
676    msg2 = message_module.TestAllTypes()
677    text = (u'optional_string: "café"')
678    text_format.Merge(text, msg2)
679    self.assertEqual(msg2.optional_string, u'café')
680    msg2.Clear()
681    self.assertEqual(msg2.optional_string, u'')
682    text_format.Parse(text, msg2)
683    self.assertEqual(msg2.optional_string, u'café')
684
685  def testParseDoubleToFloat(self, message_module):
686    message = message_module.TestAllTypes()
687    text = ('repeated_float: 3.4028235e+39\n'
688            'repeated_float: 1.4028235e-39\n')
689    text_format.Parse(text, message)
690    self.assertEqual(message.repeated_float[0], float('inf'))
691    self.assertAlmostEqual(message.repeated_float[1], 1.4028235e-39)
692
693  def testParseExotic(self, message_module):
694    message = message_module.TestAllTypes()
695    text = ('repeated_int64: -9223372036854775808\n'
696            'repeated_uint64: 18446744073709551615\n'
697            'repeated_double: 123.456\n'
698            'repeated_double: 1.23e+22\n'
699            'repeated_double: 1.23e-18\n'
700            'repeated_string: \n'
701            '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
702            'repeated_string: "foo" \'corge\' "grault"\n'
703            'repeated_string: "\\303\\274\\352\\234\\237"\n'
704            'repeated_string: "\\xc3\\xbc"\n'
705            'repeated_string: "\xc3\xbc"\n')
706    text_format.Parse(text, message)
707
708    self.assertEqual(-9223372036854775808, message.repeated_int64[0])
709    self.assertEqual(18446744073709551615, message.repeated_uint64[0])
710    self.assertEqual(123.456, message.repeated_double[0])
711    self.assertEqual(1.23e22, message.repeated_double[1])
712    self.assertEqual(1.23e-18, message.repeated_double[2])
713    self.assertEqual('\000\001\a\b\f\n\r\t\v\\\'"', message.repeated_string[0])
714    self.assertEqual('foocorgegrault', message.repeated_string[1])
715    self.assertEqual(u'\u00fc\ua71f', message.repeated_string[2])
716    self.assertEqual(u'\u00fc', message.repeated_string[3])
717
718  def testParseTrailingCommas(self, message_module):
719    message = message_module.TestAllTypes()
720    text = ('repeated_int64: 100;\n'
721            'repeated_int64: 200;\n'
722            'repeated_int64: 300,\n'
723            'repeated_string: "one",\n'
724            'repeated_string: "two";\n')
725    text_format.Parse(text, message)
726
727    self.assertEqual(100, message.repeated_int64[0])
728    self.assertEqual(200, message.repeated_int64[1])
729    self.assertEqual(300, message.repeated_int64[2])
730    self.assertEqual(u'one', message.repeated_string[0])
731    self.assertEqual(u'two', message.repeated_string[1])
732
733  def testParseRepeatedScalarShortFormat(self, message_module):
734    message = message_module.TestAllTypes()
735    text = ('repeated_int64: [100, 200];\n'
736            'repeated_int64: []\n'
737            'repeated_int64: 300,\n'
738            'repeated_string: ["one", "two"];\n')
739    text_format.Parse(text, message)
740
741    self.assertEqual(100, message.repeated_int64[0])
742    self.assertEqual(200, message.repeated_int64[1])
743    self.assertEqual(300, message.repeated_int64[2])
744    self.assertEqual(u'one', message.repeated_string[0])
745    self.assertEqual(u'two', message.repeated_string[1])
746
747  def testParseRepeatedMessageShortFormat(self, message_module):
748    message = message_module.TestAllTypes()
749    text = ('repeated_nested_message: [{bb: 100}, {bb: 200}],\n'
750            'repeated_nested_message: {bb: 300}\n'
751            'repeated_nested_message [{bb: 400}];\n')
752    text_format.Parse(text, message)
753
754    self.assertEqual(100, message.repeated_nested_message[0].bb)
755    self.assertEqual(200, message.repeated_nested_message[1].bb)
756    self.assertEqual(300, message.repeated_nested_message[2].bb)
757    self.assertEqual(400, message.repeated_nested_message[3].bb)
758
759  def testParseEmptyText(self, message_module):
760    message = message_module.TestAllTypes()
761    text = ''
762    text_format.Parse(text, message)
763    self.assertEqual(message_module.TestAllTypes(), message)
764
765  def testParseInvalidUtf8(self, message_module):
766    message = message_module.TestAllTypes()
767    text = 'repeated_string: "\\xc3\\xc3"'
768    with self.assertRaises(text_format.ParseError) as e:
769      text_format.Parse(text, message)
770    self.assertEqual(e.exception.GetLine(), 1)
771    self.assertEqual(e.exception.GetColumn(), 28)
772
773  def testParseSingleWord(self, message_module):
774    message = message_module.TestAllTypes()
775    text = 'foo'
776    six.assertRaisesRegex(self, text_format.ParseError, (
777        r'1:1 : Message type "\w+.TestAllTypes" has no field named '
778        r'"foo".'), text_format.Parse, text, message)
779
780  def testParseUnknownField(self, message_module):
781    message = message_module.TestAllTypes()
782    text = 'unknown_field: 8\n'
783    six.assertRaisesRegex(self, text_format.ParseError, (
784        r'1:1 : Message type "\w+.TestAllTypes" has no field named '
785        r'"unknown_field".'), text_format.Parse, text, message)
786    text = ('optional_int32: 123\n'
787            'unknown_field: 8\n'
788            'optional_nested_message { bb: 45 }')
789    text_format.Parse(text, message, allow_unknown_field=True)
790    self.assertEqual(message.optional_nested_message.bb, 45)
791    self.assertEqual(message.optional_int32, 123)
792
793  def testParseBadEnumValue(self, message_module):
794    message = message_module.TestAllTypes()
795    text = 'optional_nested_enum: BARR'
796    six.assertRaisesRegex(self, text_format.ParseError,
797                          (r'1:23 : \'optional_nested_enum: BARR\': '
798                           r'Enum type "\w+.TestAllTypes.NestedEnum" '
799                           r'has no value named BARR.'), text_format.Parse,
800                          text, message)
801
802  def testParseBadIntValue(self, message_module):
803    message = message_module.TestAllTypes()
804    text = 'optional_int32: bork'
805    six.assertRaisesRegex(self, text_format.ParseError,
806                          ('1:17 : \'optional_int32: bork\': '
807                           'Couldn\'t parse integer: bork'),
808                          text_format.Parse, text, message)
809
810  def testParseStringFieldUnescape(self, message_module):
811    message = message_module.TestAllTypes()
812    text = r'''repeated_string: "\xf\x62"
813               repeated_string: "\\xf\\x62"
814               repeated_string: "\\\xf\\\x62"
815               repeated_string: "\\\\xf\\\\x62"
816               repeated_string: "\\\\\xf\\\\\x62"
817               repeated_string: "\x5cx20"'''
818
819    text_format.Parse(text, message)
820
821    SLASH = '\\'
822    self.assertEqual('\x0fb', message.repeated_string[0])
823    self.assertEqual(SLASH + 'xf' + SLASH + 'x62', message.repeated_string[1])
824    self.assertEqual(SLASH + '\x0f' + SLASH + 'b', message.repeated_string[2])
825    self.assertEqual(SLASH + SLASH + 'xf' + SLASH + SLASH + 'x62',
826                     message.repeated_string[3])
827    self.assertEqual(SLASH + SLASH + '\x0f' + SLASH + SLASH + 'b',
828                     message.repeated_string[4])
829    self.assertEqual(SLASH + 'x20', message.repeated_string[5])
830
831  def testParseOneof(self, message_module):
832    m = message_module.TestAllTypes()
833    m.oneof_uint32 = 11
834    m2 = message_module.TestAllTypes()
835    text_format.Parse(text_format.MessageToString(m), m2)
836    self.assertEqual('oneof_uint32', m2.WhichOneof('oneof_field'))
837
838  def testParseMultipleOneof(self, message_module):
839    m_string = '\n'.join(['oneof_uint32: 11', 'oneof_string: "foo"'])
840    m2 = message_module.TestAllTypes()
841    with six.assertRaisesRegex(self, text_format.ParseError,
842                               ' is specified along with field '):
843      text_format.Parse(m_string, m2)
844
845  # This example contains non-ASCII codepoint unicode data as literals
846  # which should come through as utf-8 for bytes, and as the unicode
847  # itself for string fields.  It also demonstrates escaped binary data.
848  # The ur"" string prefix is unfortunately missing from Python 3
849  # so we resort to double escaping our \s so that they come through.
850  _UNICODE_SAMPLE = u"""
851      optional_bytes: 'Á short desçription'
852      optional_string: 'Á short desçription'
853      repeated_bytes: '\\303\\201 short des\\303\\247ription'
854      repeated_bytes: '\\x12\\x34\\x56\\x78\\x90\\xab\\xcd\\xef'
855      repeated_string: '\\xd0\\x9f\\xd1\\x80\\xd0\\xb8\\xd0\\xb2\\xd0\\xb5\\xd1\\x82'
856      """
857  _BYTES_SAMPLE = _UNICODE_SAMPLE.encode('utf-8')
858  _GOLDEN_UNICODE = u'Á short desçription'
859  _GOLDEN_BYTES = _GOLDEN_UNICODE.encode('utf-8')
860  _GOLDEN_BYTES_1 = b'\x12\x34\x56\x78\x90\xab\xcd\xef'
861  _GOLDEN_STR_0 = u'Привет'
862
863  def testParseUnicode(self, message_module):
864    m = message_module.TestAllTypes()
865    text_format.Parse(self._UNICODE_SAMPLE, m)
866    self.assertEqual(m.optional_bytes, self._GOLDEN_BYTES)
867    self.assertEqual(m.optional_string, self._GOLDEN_UNICODE)
868    self.assertEqual(m.repeated_bytes[0], self._GOLDEN_BYTES)
869    # repeated_bytes[1] contained simple \ escaped non-UTF-8 raw binary data.
870    self.assertEqual(m.repeated_bytes[1], self._GOLDEN_BYTES_1)
871    # repeated_string[0] contained \ escaped data representing the UTF-8
872    # representation of _GOLDEN_STR_0 - it needs to decode as such.
873    self.assertEqual(m.repeated_string[0], self._GOLDEN_STR_0)
874
875  def testParseBytes(self, message_module):
876    m = message_module.TestAllTypes()
877    text_format.Parse(self._BYTES_SAMPLE, m)
878    self.assertEqual(m.optional_bytes, self._GOLDEN_BYTES)
879    self.assertEqual(m.optional_string, self._GOLDEN_UNICODE)
880    self.assertEqual(m.repeated_bytes[0], self._GOLDEN_BYTES)
881    # repeated_bytes[1] contained simple \ escaped non-UTF-8 raw binary data.
882    self.assertEqual(m.repeated_bytes[1], self._GOLDEN_BYTES_1)
883    # repeated_string[0] contained \ escaped data representing the UTF-8
884    # representation of _GOLDEN_STR_0 - it needs to decode as such.
885    self.assertEqual(m.repeated_string[0], self._GOLDEN_STR_0)
886
887  def testFromBytesFile(self, message_module):
888    m = message_module.TestAllTypes()
889    f = io.BytesIO(self._BYTES_SAMPLE)
890    text_format.ParseLines(f, m)
891    self.assertEqual(m.optional_bytes, self._GOLDEN_BYTES)
892    self.assertEqual(m.optional_string, self._GOLDEN_UNICODE)
893    self.assertEqual(m.repeated_bytes[0], self._GOLDEN_BYTES)
894
895  def testFromUnicodeFile(self, message_module):
896    m = message_module.TestAllTypes()
897    f = io.StringIO(self._UNICODE_SAMPLE)
898    text_format.ParseLines(f, m)
899    self.assertEqual(m.optional_bytes, self._GOLDEN_BYTES)
900    self.assertEqual(m.optional_string, self._GOLDEN_UNICODE)
901    self.assertEqual(m.repeated_bytes[0], self._GOLDEN_BYTES)
902
903  def testFromBytesLines(self, message_module):
904    m = message_module.TestAllTypes()
905    text_format.ParseLines(self._BYTES_SAMPLE.split(b'\n'), m)
906    self.assertEqual(m.optional_bytes, self._GOLDEN_BYTES)
907    self.assertEqual(m.optional_string, self._GOLDEN_UNICODE)
908    self.assertEqual(m.repeated_bytes[0], self._GOLDEN_BYTES)
909
910  def testFromUnicodeLines(self, message_module):
911    m = message_module.TestAllTypes()
912    text_format.ParseLines(self._UNICODE_SAMPLE.split(u'\n'), m)
913    self.assertEqual(m.optional_bytes, self._GOLDEN_BYTES)
914    self.assertEqual(m.optional_string, self._GOLDEN_UNICODE)
915    self.assertEqual(m.repeated_bytes[0], self._GOLDEN_BYTES)
916
917  def testParseDuplicateMessages(self, message_module):
918    message = message_module.TestAllTypes()
919    text = ('optional_nested_message { bb: 1 } '
920            'optional_nested_message { bb: 2 }')
921    six.assertRaisesRegex(self, text_format.ParseError, (
922        r'1:59 : Message type "\w+.TestAllTypes" '
923        r'should not have multiple "optional_nested_message" fields.'),
924                          text_format.Parse, text,
925                          message)
926
927  def testParseDuplicateScalars(self, message_module):
928    message = message_module.TestAllTypes()
929    text = ('optional_int32: 42 ' 'optional_int32: 67')
930    six.assertRaisesRegex(self, text_format.ParseError, (
931        r'1:36 : Message type "\w+.TestAllTypes" should not '
932        r'have multiple "optional_int32" fields.'), text_format.Parse, text,
933                          message)
934
935  def testParseExistingScalarInMessage(self, message_module):
936    message = message_module.TestAllTypes(optional_int32=42)
937    text = 'optional_int32: 67'
938    six.assertRaisesRegex(self, text_format.ParseError,
939                          (r'Message type "\w+.TestAllTypes" should not '
940                           r'have multiple "optional_int32" fields.'),
941                          text_format.Parse, text, message)
942
943
944@_parameterized.parameters(unittest_pb2, unittest_proto3_arena_pb2)
945class TextFormatMergeTests(TextFormatBase):
946
947  def testMergeDuplicateScalarsInText(self, message_module):
948    message = message_module.TestAllTypes()
949    text = ('optional_int32: 42 ' 'optional_int32: 67')
950    r = text_format.Merge(text, message)
951    self.assertIs(r, message)
952    self.assertEqual(67, message.optional_int32)
953
954  def testMergeDuplicateNestedMessageScalars(self, message_module):
955    message = message_module.TestAllTypes()
956    text = ('optional_nested_message { bb: 1 } '
957            'optional_nested_message { bb: 2 }')
958    r = text_format.Merge(text, message)
959    self.assertTrue(r is message)
960    self.assertEqual(2, message.optional_nested_message.bb)
961
962  def testReplaceScalarInMessage(self, message_module):
963    message = message_module.TestAllTypes(optional_int32=42)
964    text = 'optional_int32: 67'
965    r = text_format.Merge(text, message)
966    self.assertIs(r, message)
967    self.assertEqual(67, message.optional_int32)
968
969  def testReplaceMessageInMessage(self, message_module):
970    message = message_module.TestAllTypes(
971        optional_int32=42, optional_nested_message=dict())
972    self.assertTrue(message.HasField('optional_nested_message'))
973    text = 'optional_nested_message{ bb: 3 }'
974    r = text_format.Merge(text, message)
975    self.assertIs(r, message)
976    self.assertEqual(3, message.optional_nested_message.bb)
977
978  def testMergeMultipleOneof(self, message_module):
979    m_string = '\n'.join(['oneof_uint32: 11', 'oneof_string: "foo"'])
980    m2 = message_module.TestAllTypes()
981    text_format.Merge(m_string, m2)
982    self.assertEqual('oneof_string', m2.WhichOneof('oneof_field'))
983
984
985# These are tests that aren't fundamentally specific to proto2, but are at
986# the moment because of differences between the proto2 and proto3 test schemas.
987# Ideally the schemas would be made more similar so these tests could pass.
988class OnlyWorksWithProto2RightNowTests(TextFormatBase):
989
990  def testPrintAllFieldsPointy(self):
991    message = unittest_pb2.TestAllTypes()
992    test_util.SetAllFields(message)
993    self.CompareToGoldenFile(
994        self.RemoveRedundantZeros(text_format.MessageToString(
995            message, pointy_brackets=True)),
996        'text_format_unittest_data_pointy_oneof.txt')
997
998  def testParseGolden(self):
999    golden_text = '\n'.join(self.ReadGolden(
1000        'text_format_unittest_data_oneof_implemented.txt'))
1001    parsed_message = unittest_pb2.TestAllTypes()
1002    r = text_format.Parse(golden_text, parsed_message)
1003    self.assertIs(r, parsed_message)
1004
1005    message = unittest_pb2.TestAllTypes()
1006    test_util.SetAllFields(message)
1007    self.assertEqual(message, parsed_message)
1008
1009  def testPrintAllFields(self):
1010    message = unittest_pb2.TestAllTypes()
1011    test_util.SetAllFields(message)
1012    self.CompareToGoldenFile(
1013        self.RemoveRedundantZeros(text_format.MessageToString(message)),
1014        'text_format_unittest_data_oneof_implemented.txt')
1015
1016  def testPrintUnknownFields(self):
1017    message = unittest_pb2.TestAllTypes()
1018    message.optional_int32 = 101
1019    message.optional_double = 102.0
1020    message.optional_string = u'hello'
1021    message.optional_bytes = b'103'
1022    message.optionalgroup.a = 104
1023    message.optional_nested_message.bb = 105
1024    all_data = message.SerializeToString()
1025    empty_message = unittest_pb2.TestEmptyMessage()
1026    empty_message.ParseFromString(all_data)
1027    self.assertEqual('  1: 101\n'
1028                     '  12: 4636878028842991616\n'
1029                     '  14: "hello"\n'
1030                     '  15: "103"\n'
1031                     '  16 {\n'
1032                     '    17: 104\n'
1033                     '  }\n'
1034                     '  18 {\n'
1035                     '    1: 105\n'
1036                     '  }\n',
1037                     text_format.MessageToString(empty_message,
1038                                                 indent=2,
1039                                                 print_unknown_fields=True))
1040    self.assertEqual('1: 101 '
1041                     '12: 4636878028842991616 '
1042                     '14: "hello" '
1043                     '15: "103" '
1044                     '16 { 17: 104 } '
1045                     '18 { 1: 105 }',
1046                     text_format.MessageToString(empty_message,
1047                                                 print_unknown_fields=True,
1048                                                 as_one_line=True))
1049
1050  def testPrintInIndexOrder(self):
1051    message = unittest_pb2.TestFieldOrderings()
1052    # Fields are listed in index order instead of field number.
1053    message.my_string = 'str'
1054    message.my_int = 101
1055    message.my_float = 111
1056    message.optional_nested_message.oo = 0
1057    message.optional_nested_message.bb = 1
1058    message.Extensions[unittest_pb2.my_extension_string] = 'ext_str0'
1059    # Extensions are listed based on the order of extension number.
1060    # Extension number 12.
1061    message.Extensions[unittest_pb2.TestExtensionOrderings2.
1062                       test_ext_orderings2].my_string = 'ext_str2'
1063    # Extension number 13.
1064    message.Extensions[unittest_pb2.TestExtensionOrderings1.
1065                       test_ext_orderings1].my_string = 'ext_str1'
1066    # Extension number 14.
1067    message.Extensions[
1068        unittest_pb2.TestExtensionOrderings2.TestExtensionOrderings3.
1069        test_ext_orderings3].my_string = 'ext_str3'
1070
1071    # Print in index order.
1072    self.CompareToGoldenText(
1073        self.RemoveRedundantZeros(
1074            text_format.MessageToString(message, use_index_order=True)),
1075        'my_string: "str"\n'
1076        'my_int: 101\n'
1077        'my_float: 111\n'
1078        'optional_nested_message {\n'
1079        '  oo: 0\n'
1080        '  bb: 1\n'
1081        '}\n'
1082        '[protobuf_unittest.TestExtensionOrderings2.test_ext_orderings2] {\n'
1083        '  my_string: "ext_str2"\n'
1084        '}\n'
1085        '[protobuf_unittest.TestExtensionOrderings1.test_ext_orderings1] {\n'
1086        '  my_string: "ext_str1"\n'
1087        '}\n'
1088        '[protobuf_unittest.TestExtensionOrderings2.TestExtensionOrderings3'
1089        '.test_ext_orderings3] {\n'
1090        '  my_string: "ext_str3"\n'
1091        '}\n'
1092        '[protobuf_unittest.my_extension_string]: "ext_str0"\n')
1093    # By default, print in field number order.
1094    self.CompareToGoldenText(
1095        self.RemoveRedundantZeros(text_format.MessageToString(message)),
1096        'my_int: 101\n'
1097        'my_string: "str"\n'
1098        '[protobuf_unittest.TestExtensionOrderings2.test_ext_orderings2] {\n'
1099        '  my_string: "ext_str2"\n'
1100        '}\n'
1101        '[protobuf_unittest.TestExtensionOrderings1.test_ext_orderings1] {\n'
1102        '  my_string: "ext_str1"\n'
1103        '}\n'
1104        '[protobuf_unittest.TestExtensionOrderings2.TestExtensionOrderings3'
1105        '.test_ext_orderings3] {\n'
1106        '  my_string: "ext_str3"\n'
1107        '}\n'
1108        '[protobuf_unittest.my_extension_string]: "ext_str0"\n'
1109        'my_float: 111\n'
1110        'optional_nested_message {\n'
1111        '  bb: 1\n'
1112        '  oo: 0\n'
1113        '}\n')
1114
1115  def testMergeLinesGolden(self):
1116    opened = self.ReadGolden('text_format_unittest_data_oneof_implemented.txt')
1117    parsed_message = unittest_pb2.TestAllTypes()
1118    r = text_format.MergeLines(opened, parsed_message)
1119    self.assertIs(r, parsed_message)
1120
1121    message = unittest_pb2.TestAllTypes()
1122    test_util.SetAllFields(message)
1123    self.assertEqual(message, parsed_message)
1124
1125  def testParseLinesGolden(self):
1126    opened = self.ReadGolden('text_format_unittest_data_oneof_implemented.txt')
1127    parsed_message = unittest_pb2.TestAllTypes()
1128    r = text_format.ParseLines(opened, parsed_message)
1129    self.assertIs(r, parsed_message)
1130
1131    message = unittest_pb2.TestAllTypes()
1132    test_util.SetAllFields(message)
1133    self.assertEqual(message, parsed_message)
1134
1135  def testPrintMap(self):
1136    message = map_unittest_pb2.TestMap()
1137
1138    message.map_int32_int32[-123] = -456
1139    message.map_int64_int64[-2**33] = -2**34
1140    message.map_uint32_uint32[123] = 456
1141    message.map_uint64_uint64[2**33] = 2**34
1142    message.map_string_string['abc'] = '123'
1143    message.map_int32_foreign_message[111].c = 5
1144
1145    # Maps are serialized to text format using their underlying repeated
1146    # representation.
1147    self.CompareToGoldenText(
1148        text_format.MessageToString(message), 'map_int32_int32 {\n'
1149        '  key: -123\n'
1150        '  value: -456\n'
1151        '}\n'
1152        'map_int64_int64 {\n'
1153        '  key: -8589934592\n'
1154        '  value: -17179869184\n'
1155        '}\n'
1156        'map_uint32_uint32 {\n'
1157        '  key: 123\n'
1158        '  value: 456\n'
1159        '}\n'
1160        'map_uint64_uint64 {\n'
1161        '  key: 8589934592\n'
1162        '  value: 17179869184\n'
1163        '}\n'
1164        'map_string_string {\n'
1165        '  key: "abc"\n'
1166        '  value: "123"\n'
1167        '}\n'
1168        'map_int32_foreign_message {\n'
1169        '  key: 111\n'
1170        '  value {\n'
1171        '    c: 5\n'
1172        '  }\n'
1173        '}\n')
1174
1175  # In cpp implementation, __str__ calls the cpp implementation of text format.
1176  def testPrintMapUsingCppImplementation(self):
1177    message = map_unittest_pb2.TestMap()
1178    inner_msg = message.map_int32_foreign_message[111]
1179    inner_msg.c = 1
1180    self.assertEqual(
1181        str(message),
1182        'map_int32_foreign_message {\n'
1183        '  key: 111\n'
1184        '  value {\n'
1185        '    c: 1\n'
1186        '  }\n'
1187        '}\n')
1188    inner_msg.c = 2
1189    self.assertEqual(
1190        str(message),
1191        'map_int32_foreign_message {\n'
1192        '  key: 111\n'
1193        '  value {\n'
1194        '    c: 2\n'
1195        '  }\n'
1196        '}\n')
1197
1198  def testMapOrderEnforcement(self):
1199    message = map_unittest_pb2.TestMap()
1200    for letter in string.ascii_uppercase[13:26]:
1201      message.map_string_string[letter] = 'dummy'
1202    for letter in reversed(string.ascii_uppercase[0:13]):
1203      message.map_string_string[letter] = 'dummy'
1204    golden = ''.join(('map_string_string {\n  key: "%c"\n  value: "dummy"\n}\n'
1205                      % (letter,) for letter in string.ascii_uppercase))
1206    self.CompareToGoldenText(text_format.MessageToString(message), golden)
1207
1208  # TODO(teboring): In c/137553523, not serializing default value for map entry
1209  # message has been fixed. This test needs to be disabled in order to submit
1210  # that cl. Add this back when c/137553523 has been submitted.
1211  # def testMapOrderSemantics(self):
1212  #   golden_lines = self.ReadGolden('map_test_data.txt')
1213
1214  #   message = map_unittest_pb2.TestMap()
1215  #   text_format.ParseLines(golden_lines, message)
1216  #   candidate = text_format.MessageToString(message)
1217  #   # The Python implementation emits "1.0" for the double value that the C++
1218  #   # implementation emits as "1".
1219  #   candidate = candidate.replace('1.0', '1', 2)
1220  #   candidate = candidate.replace('0.0', '0', 2)
1221  #   self.assertMultiLineEqual(candidate, ''.join(golden_lines))
1222
1223
1224# Tests of proto2-only features (MessageSet, extensions, etc.).
1225class Proto2Tests(TextFormatBase):
1226
1227  def testPrintMessageSet(self):
1228    message = unittest_mset_pb2.TestMessageSetContainer()
1229    ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
1230    ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
1231    message.message_set.Extensions[ext1].i = 23
1232    message.message_set.Extensions[ext2].str = 'foo'
1233    self.CompareToGoldenText(
1234        text_format.MessageToString(message), 'message_set {\n'
1235        '  [protobuf_unittest.TestMessageSetExtension1] {\n'
1236        '    i: 23\n'
1237        '  }\n'
1238        '  [protobuf_unittest.TestMessageSetExtension2] {\n'
1239        '    str: \"foo\"\n'
1240        '  }\n'
1241        '}\n')
1242
1243    message = message_set_extensions_pb2.TestMessageSet()
1244    ext = message_set_extensions_pb2.message_set_extension3
1245    message.Extensions[ext].text = 'bar'
1246    self.CompareToGoldenText(
1247        text_format.MessageToString(message),
1248        '[google.protobuf.internal.TestMessageSetExtension3] {\n'
1249        '  text: \"bar\"\n'
1250        '}\n')
1251
1252  def testPrintMessageSetByFieldNumber(self):
1253    out = text_format.TextWriter(False)
1254    message = unittest_mset_pb2.TestMessageSetContainer()
1255    ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
1256    ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
1257    message.message_set.Extensions[ext1].i = 23
1258    message.message_set.Extensions[ext2].str = 'foo'
1259    text_format.PrintMessage(message, out, use_field_number=True)
1260    self.CompareToGoldenText(out.getvalue(), '1 {\n'
1261                             '  1545008 {\n'
1262                             '    15: 23\n'
1263                             '  }\n'
1264                             '  1547769 {\n'
1265                             '    25: \"foo\"\n'
1266                             '  }\n'
1267                             '}\n')
1268    out.close()
1269
1270  def testPrintMessageSetAsOneLine(self):
1271    message = unittest_mset_pb2.TestMessageSetContainer()
1272    ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
1273    ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
1274    message.message_set.Extensions[ext1].i = 23
1275    message.message_set.Extensions[ext2].str = 'foo'
1276    self.CompareToGoldenText(
1277        text_format.MessageToString(message, as_one_line=True),
1278        'message_set {'
1279        ' [protobuf_unittest.TestMessageSetExtension1] {'
1280        ' i: 23'
1281        ' }'
1282        ' [protobuf_unittest.TestMessageSetExtension2] {'
1283        ' str: \"foo\"'
1284        ' }'
1285        ' }')
1286
1287  def testParseMessageSet(self):
1288    message = unittest_pb2.TestAllTypes()
1289    text = ('repeated_uint64: 1\n' 'repeated_uint64: 2\n')
1290    text_format.Parse(text, message)
1291    self.assertEqual(1, message.repeated_uint64[0])
1292    self.assertEqual(2, message.repeated_uint64[1])
1293
1294    message = unittest_mset_pb2.TestMessageSetContainer()
1295    text = ('message_set {\n'
1296            '  [protobuf_unittest.TestMessageSetExtension1] {\n'
1297            '    i: 23\n'
1298            '  }\n'
1299            '  [protobuf_unittest.TestMessageSetExtension2] {\n'
1300            '    str: \"foo\"\n'
1301            '  }\n'
1302            '}\n')
1303    text_format.Parse(text, message)
1304    ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
1305    ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
1306    self.assertEqual(23, message.message_set.Extensions[ext1].i)
1307    self.assertEqual('foo', message.message_set.Extensions[ext2].str)
1308
1309  def testExtensionInsideAnyMessage(self):
1310    message = test_extend_any.TestAny()
1311    text = ('value {\n'
1312            '  [type.googleapis.com/google.protobuf.internal.TestAny] {\n'
1313            '    [google.protobuf.internal.TestAnyExtension1.extension1] {\n'
1314            '      i: 10\n'
1315            '    }\n'
1316            '  }\n'
1317            '}\n')
1318    text_format.Merge(text, message, descriptor_pool=descriptor_pool.Default())
1319    self.CompareToGoldenText(
1320        text_format.MessageToString(
1321            message, descriptor_pool=descriptor_pool.Default()),
1322        text)
1323
1324  def testParseMessageByFieldNumber(self):
1325    message = unittest_pb2.TestAllTypes()
1326    text = ('34: 1\n' 'repeated_uint64: 2\n')
1327    text_format.Parse(text, message, allow_field_number=True)
1328    self.assertEqual(1, message.repeated_uint64[0])
1329    self.assertEqual(2, message.repeated_uint64[1])
1330
1331    message = unittest_mset_pb2.TestMessageSetContainer()
1332    text = ('1 {\n'
1333            '  1545008 {\n'
1334            '    15: 23\n'
1335            '  }\n'
1336            '  1547769 {\n'
1337            '    25: \"foo\"\n'
1338            '  }\n'
1339            '}\n')
1340    text_format.Parse(text, message, allow_field_number=True)
1341    ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
1342    ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
1343    self.assertEqual(23, message.message_set.Extensions[ext1].i)
1344    self.assertEqual('foo', message.message_set.Extensions[ext2].str)
1345
1346    # Can't parse field number without set allow_field_number=True.
1347    message = unittest_pb2.TestAllTypes()
1348    text = '34:1\n'
1349    six.assertRaisesRegex(self, text_format.ParseError, (
1350        r'1:1 : Message type "\w+.TestAllTypes" has no field named '
1351        r'"34".'), text_format.Parse, text, message)
1352
1353    # Can't parse if field number is not found.
1354    text = '1234:1\n'
1355    six.assertRaisesRegex(
1356        self,
1357        text_format.ParseError,
1358        (r'1:1 : Message type "\w+.TestAllTypes" has no field named '
1359         r'"1234".'),
1360        text_format.Parse,
1361        text,
1362        message,
1363        allow_field_number=True)
1364
1365  def testPrintAllExtensions(self):
1366    message = unittest_pb2.TestAllExtensions()
1367    test_util.SetAllExtensions(message)
1368    self.CompareToGoldenFile(
1369        self.RemoveRedundantZeros(text_format.MessageToString(message)),
1370        'text_format_unittest_extensions_data.txt')
1371
1372  def testPrintAllExtensionsPointy(self):
1373    message = unittest_pb2.TestAllExtensions()
1374    test_util.SetAllExtensions(message)
1375    self.CompareToGoldenFile(
1376        self.RemoveRedundantZeros(text_format.MessageToString(
1377            message, pointy_brackets=True)),
1378        'text_format_unittest_extensions_data_pointy.txt')
1379
1380  def testParseGoldenExtensions(self):
1381    golden_text = '\n'.join(self.ReadGolden(
1382        'text_format_unittest_extensions_data.txt'))
1383    parsed_message = unittest_pb2.TestAllExtensions()
1384    text_format.Parse(golden_text, parsed_message)
1385
1386    message = unittest_pb2.TestAllExtensions()
1387    test_util.SetAllExtensions(message)
1388    self.assertEqual(message, parsed_message)
1389
1390  def testParseAllExtensions(self):
1391    message = unittest_pb2.TestAllExtensions()
1392    test_util.SetAllExtensions(message)
1393    ascii_text = text_format.MessageToString(message)
1394
1395    parsed_message = unittest_pb2.TestAllExtensions()
1396    text_format.Parse(ascii_text, parsed_message)
1397    self.assertEqual(message, parsed_message)
1398
1399  def testParseAllowedUnknownExtension(self):
1400    # Skip over unknown extension correctly.
1401    message = unittest_mset_pb2.TestMessageSetContainer()
1402    text = ('message_set {\n'
1403            '  [unknown_extension] {\n'
1404            '    i: 23\n'
1405            '    bin: "\xe0"'
1406            '    [nested_unknown_ext]: {\n'
1407            '      i: 23\n'
1408            '      x: x\n'
1409            '      test: "test_string"\n'
1410            '      floaty_float: -0.315\n'
1411            '      num: -inf\n'
1412            '      multiline_str: "abc"\n'
1413            '          "def"\n'
1414            '          "xyz."\n'
1415            '      [nested_unknown_ext.ext]: <\n'
1416            '        i: 23\n'
1417            '        i: 24\n'
1418            '        pointfloat: .3\n'
1419            '        test: "test_string"\n'
1420            '        floaty_float: -0.315\n'
1421            '        num: -inf\n'
1422            '        long_string: "test" "test2" \n'
1423            '      >\n'
1424            '    }\n'
1425            '  }\n'
1426            '  [unknown_extension]: 5\n'
1427            '  [unknown_extension_with_number_field] {\n'
1428            '    1: "some_field"\n'
1429            '    2: -0.451\n'
1430            '  }\n'
1431            '}\n')
1432    text_format.Parse(text, message, allow_unknown_extension=True)
1433    golden = 'message_set {\n}\n'
1434    self.CompareToGoldenText(text_format.MessageToString(message), golden)
1435
1436    # Catch parse errors in unknown extension.
1437    message = unittest_mset_pb2.TestMessageSetContainer()
1438    malformed = ('message_set {\n'
1439                 '  [unknown_extension] {\n'
1440                 '    i:\n'  # Missing value.
1441                 '  }\n'
1442                 '}\n')
1443    six.assertRaisesRegex(self,
1444                          text_format.ParseError,
1445                          'Invalid field value: }',
1446                          text_format.Parse,
1447                          malformed,
1448                          message,
1449                          allow_unknown_extension=True)
1450
1451    message = unittest_mset_pb2.TestMessageSetContainer()
1452    malformed = ('message_set {\n'
1453                 '  [unknown_extension] {\n'
1454                 '    str: "malformed string\n'  # Missing closing quote.
1455                 '  }\n'
1456                 '}\n')
1457    six.assertRaisesRegex(self,
1458                          text_format.ParseError,
1459                          'Invalid field value: "',
1460                          text_format.Parse,
1461                          malformed,
1462                          message,
1463                          allow_unknown_extension=True)
1464
1465    message = unittest_mset_pb2.TestMessageSetContainer()
1466    malformed = ('message_set {\n'
1467                 '  [unknown_extension] {\n'
1468                 '    str: "malformed\n multiline\n string\n'
1469                 '  }\n'
1470                 '}\n')
1471    six.assertRaisesRegex(self,
1472                          text_format.ParseError,
1473                          'Invalid field value: "',
1474                          text_format.Parse,
1475                          malformed,
1476                          message,
1477                          allow_unknown_extension=True)
1478
1479    message = unittest_mset_pb2.TestMessageSetContainer()
1480    malformed = ('message_set {\n'
1481                 '  [malformed_extension] <\n'
1482                 '    i: -5\n'
1483                 '  \n'  # Missing '>' here.
1484                 '}\n')
1485    six.assertRaisesRegex(self,
1486                          text_format.ParseError,
1487                          '5:1 : \'}\': Expected ">".',
1488                          text_format.Parse,
1489                          malformed,
1490                          message,
1491                          allow_unknown_extension=True)
1492
1493    # Don't allow unknown fields with allow_unknown_extension=True.
1494    message = unittest_mset_pb2.TestMessageSetContainer()
1495    malformed = ('message_set {\n'
1496                 '  unknown_field: true\n'
1497                 '}\n')
1498    six.assertRaisesRegex(self,
1499                          text_format.ParseError,
1500                          ('2:3 : Message type '
1501                           '"proto2_wireformat_unittest.TestMessageSet" has no'
1502                           ' field named "unknown_field".'),
1503                          text_format.Parse,
1504                          malformed,
1505                          message,
1506                          allow_unknown_extension=True)
1507
1508    # Parse known extension correctly.
1509    message = unittest_mset_pb2.TestMessageSetContainer()
1510    text = ('message_set {\n'
1511            '  [protobuf_unittest.TestMessageSetExtension1] {\n'
1512            '    i: 23\n'
1513            '  }\n'
1514            '  [protobuf_unittest.TestMessageSetExtension2] {\n'
1515            '    str: \"foo\"\n'
1516            '  }\n'
1517            '}\n')
1518    text_format.Parse(text, message, allow_unknown_extension=True)
1519    ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
1520    ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
1521    self.assertEqual(23, message.message_set.Extensions[ext1].i)
1522    self.assertEqual('foo', message.message_set.Extensions[ext2].str)
1523
1524  def testParseBadIdentifier(self):
1525    message = unittest_pb2.TestAllTypes()
1526    text = ('optional_nested_message { "bb": 1 }')
1527    with self.assertRaises(text_format.ParseError) as e:
1528      text_format.Parse(text, message)
1529    self.assertEqual(str(e.exception),
1530                     '1:27 : \'optional_nested_message { "bb": 1 }\': '
1531                     'Expected identifier or number, got "bb".')
1532
1533  def testParseBadExtension(self):
1534    message = unittest_pb2.TestAllExtensions()
1535    text = '[unknown_extension]: 8\n'
1536    six.assertRaisesRegex(self, text_format.ParseError,
1537                          '1:2 : Extension "unknown_extension" not registered.',
1538                          text_format.Parse, text, message)
1539    message = unittest_pb2.TestAllTypes()
1540    six.assertRaisesRegex(self, text_format.ParseError, (
1541        '1:2 : Message type "protobuf_unittest.TestAllTypes" does not have '
1542        'extensions.'), text_format.Parse, text, message)
1543
1544  def testParseNumericUnknownEnum(self):
1545    message = unittest_pb2.TestAllTypes()
1546    text = 'optional_nested_enum: 100'
1547    six.assertRaisesRegex(self, text_format.ParseError,
1548                          (r'1:23 : \'optional_nested_enum: 100\': '
1549                           r'Enum type "\w+.TestAllTypes.NestedEnum" '
1550                           r'has no value with number 100.'), text_format.Parse,
1551                          text, message)
1552
1553  def testMergeDuplicateExtensionScalars(self):
1554    message = unittest_pb2.TestAllExtensions()
1555    text = ('[protobuf_unittest.optional_int32_extension]: 42 '
1556            '[protobuf_unittest.optional_int32_extension]: 67')
1557    text_format.Merge(text, message)
1558    self.assertEqual(67,
1559                     message.Extensions[unittest_pb2.optional_int32_extension])
1560
1561  def testParseDuplicateExtensionScalars(self):
1562    message = unittest_pb2.TestAllExtensions()
1563    text = ('[protobuf_unittest.optional_int32_extension]: 42 '
1564            '[protobuf_unittest.optional_int32_extension]: 67')
1565    six.assertRaisesRegex(self, text_format.ParseError, (
1566        '1:96 : Message type "protobuf_unittest.TestAllExtensions" '
1567        'should not have multiple '
1568        '"protobuf_unittest.optional_int32_extension" extensions.'),
1569                          text_format.Parse, text, message)
1570
1571  def testParseDuplicateExtensionMessages(self):
1572    message = unittest_pb2.TestAllExtensions()
1573    text = ('[protobuf_unittest.optional_nested_message_extension]: {} '
1574            '[protobuf_unittest.optional_nested_message_extension]: {}')
1575    six.assertRaisesRegex(self, text_format.ParseError, (
1576        '1:114 : Message type "protobuf_unittest.TestAllExtensions" '
1577        'should not have multiple '
1578        '"protobuf_unittest.optional_nested_message_extension" extensions.'),
1579                          text_format.Parse, text, message)
1580
1581  def testParseGroupNotClosed(self):
1582    message = unittest_pb2.TestAllTypes()
1583    text = 'RepeatedGroup: <'
1584    six.assertRaisesRegex(self, text_format.ParseError, '1:16 : Expected ">".',
1585                          text_format.Parse, text, message)
1586    text = 'RepeatedGroup: {'
1587    six.assertRaisesRegex(self, text_format.ParseError, '1:16 : Expected "}".',
1588                          text_format.Parse, text, message)
1589
1590  def testParseEmptyGroup(self):
1591    message = unittest_pb2.TestAllTypes()
1592    text = 'OptionalGroup: {}'
1593    text_format.Parse(text, message)
1594    self.assertTrue(message.HasField('optionalgroup'))
1595
1596    message.Clear()
1597
1598    message = unittest_pb2.TestAllTypes()
1599    text = 'OptionalGroup: <>'
1600    text_format.Parse(text, message)
1601    self.assertTrue(message.HasField('optionalgroup'))
1602
1603  # Maps aren't really proto2-only, but our test schema only has maps for
1604  # proto2.
1605  def testParseMap(self):
1606    text = ('map_int32_int32 {\n'
1607            '  key: -123\n'
1608            '  value: -456\n'
1609            '}\n'
1610            'map_int64_int64 {\n'
1611            '  key: -8589934592\n'
1612            '  value: -17179869184\n'
1613            '}\n'
1614            'map_uint32_uint32 {\n'
1615            '  key: 123\n'
1616            '  value: 456\n'
1617            '}\n'
1618            'map_uint64_uint64 {\n'
1619            '  key: 8589934592\n'
1620            '  value: 17179869184\n'
1621            '}\n'
1622            'map_string_string {\n'
1623            '  key: "abc"\n'
1624            '  value: "123"\n'
1625            '}\n'
1626            'map_int32_foreign_message {\n'
1627            '  key: 111\n'
1628            '  value {\n'
1629            '    c: 5\n'
1630            '  }\n'
1631            '}\n')
1632    message = map_unittest_pb2.TestMap()
1633    text_format.Parse(text, message)
1634
1635    self.assertEqual(-456, message.map_int32_int32[-123])
1636    self.assertEqual(-2**34, message.map_int64_int64[-2**33])
1637    self.assertEqual(456, message.map_uint32_uint32[123])
1638    self.assertEqual(2**34, message.map_uint64_uint64[2**33])
1639    self.assertEqual('123', message.map_string_string['abc'])
1640    self.assertEqual(5, message.map_int32_foreign_message[111].c)
1641
1642
1643class Proto3Tests(unittest.TestCase):
1644
1645  def testPrintMessageExpandAny(self):
1646    packed_message = unittest_pb2.OneString()
1647    packed_message.data = 'string'
1648    message = any_test_pb2.TestAny()
1649    message.any_value.Pack(packed_message)
1650    self.assertEqual(
1651        text_format.MessageToString(message,
1652                                    descriptor_pool=descriptor_pool.Default()),
1653        'any_value {\n'
1654        '  [type.googleapis.com/protobuf_unittest.OneString] {\n'
1655        '    data: "string"\n'
1656        '  }\n'
1657        '}\n')
1658
1659  def testTopAnyMessage(self):
1660    packed_msg = unittest_pb2.OneString()
1661    msg = any_pb2.Any()
1662    msg.Pack(packed_msg)
1663    text = text_format.MessageToString(msg)
1664    other_msg = text_format.Parse(text, any_pb2.Any())
1665    self.assertEqual(msg, other_msg)
1666
1667  def testPrintMessageExpandAnyRepeated(self):
1668    packed_message = unittest_pb2.OneString()
1669    message = any_test_pb2.TestAny()
1670    packed_message.data = 'string0'
1671    message.repeated_any_value.add().Pack(packed_message)
1672    packed_message.data = 'string1'
1673    message.repeated_any_value.add().Pack(packed_message)
1674    self.assertEqual(
1675        text_format.MessageToString(message),
1676        'repeated_any_value {\n'
1677        '  [type.googleapis.com/protobuf_unittest.OneString] {\n'
1678        '    data: "string0"\n'
1679        '  }\n'
1680        '}\n'
1681        'repeated_any_value {\n'
1682        '  [type.googleapis.com/protobuf_unittest.OneString] {\n'
1683        '    data: "string1"\n'
1684        '  }\n'
1685        '}\n')
1686
1687  def testPrintMessageExpandAnyDescriptorPoolMissingType(self):
1688    packed_message = unittest_pb2.OneString()
1689    packed_message.data = 'string'
1690    message = any_test_pb2.TestAny()
1691    message.any_value.Pack(packed_message)
1692    empty_pool = descriptor_pool.DescriptorPool()
1693    self.assertEqual(
1694        text_format.MessageToString(message, descriptor_pool=empty_pool),
1695        'any_value {\n'
1696        '  type_url: "type.googleapis.com/protobuf_unittest.OneString"\n'
1697        '  value: "\\n\\006string"\n'
1698        '}\n')
1699
1700  def testPrintMessageExpandAnyPointyBrackets(self):
1701    packed_message = unittest_pb2.OneString()
1702    packed_message.data = 'string'
1703    message = any_test_pb2.TestAny()
1704    message.any_value.Pack(packed_message)
1705    self.assertEqual(
1706        text_format.MessageToString(message,
1707                                    pointy_brackets=True),
1708        'any_value <\n'
1709        '  [type.googleapis.com/protobuf_unittest.OneString] <\n'
1710        '    data: "string"\n'
1711        '  >\n'
1712        '>\n')
1713
1714  def testPrintMessageExpandAnyAsOneLine(self):
1715    packed_message = unittest_pb2.OneString()
1716    packed_message.data = 'string'
1717    message = any_test_pb2.TestAny()
1718    message.any_value.Pack(packed_message)
1719    self.assertEqual(
1720        text_format.MessageToString(message,
1721                                    as_one_line=True),
1722        'any_value {'
1723        ' [type.googleapis.com/protobuf_unittest.OneString]'
1724        ' { data: "string" } '
1725        '}')
1726
1727  def testPrintMessageExpandAnyAsOneLinePointyBrackets(self):
1728    packed_message = unittest_pb2.OneString()
1729    packed_message.data = 'string'
1730    message = any_test_pb2.TestAny()
1731    message.any_value.Pack(packed_message)
1732    self.assertEqual(
1733        text_format.MessageToString(message,
1734                                    as_one_line=True,
1735                                    pointy_brackets=True,
1736                                    descriptor_pool=descriptor_pool.Default()),
1737        'any_value <'
1738        ' [type.googleapis.com/protobuf_unittest.OneString]'
1739        ' < data: "string" > '
1740        '>')
1741
1742  def testPrintAndParseMessageInvalidAny(self):
1743    packed_message = unittest_pb2.OneString()
1744    packed_message.data = 'string'
1745    message = any_test_pb2.TestAny()
1746    message.any_value.Pack(packed_message)
1747    # Only include string after last '/' in type_url.
1748    message.any_value.type_url = message.any_value.TypeName()
1749    text = text_format.MessageToString(message)
1750    self.assertEqual(
1751        text, 'any_value {\n'
1752        '  type_url: "protobuf_unittest.OneString"\n'
1753        '  value: "\\n\\006string"\n'
1754        '}\n')
1755
1756    parsed_message = any_test_pb2.TestAny()
1757    text_format.Parse(text, parsed_message)
1758    self.assertEqual(message, parsed_message)
1759
1760  def testUnknownEnums(self):
1761    message = unittest_proto3_arena_pb2.TestAllTypes()
1762    message2 = unittest_proto3_arena_pb2.TestAllTypes()
1763    message.optional_nested_enum = 999
1764    text_string = text_format.MessageToString(message)
1765    text_format.Parse(text_string, message2)
1766    self.assertEqual(999, message2.optional_nested_enum)
1767
1768  def testMergeExpandedAny(self):
1769    message = any_test_pb2.TestAny()
1770    text = ('any_value {\n'
1771            '  [type.googleapis.com/protobuf_unittest.OneString] {\n'
1772            '    data: "string"\n'
1773            '  }\n'
1774            '}\n')
1775    text_format.Merge(text, message)
1776    packed_message = unittest_pb2.OneString()
1777    message.any_value.Unpack(packed_message)
1778    self.assertEqual('string', packed_message.data)
1779    message.Clear()
1780    text_format.Parse(text, message)
1781    packed_message = unittest_pb2.OneString()
1782    message.any_value.Unpack(packed_message)
1783    self.assertEqual('string', packed_message.data)
1784
1785  def testMergeExpandedAnyRepeated(self):
1786    message = any_test_pb2.TestAny()
1787    text = ('repeated_any_value {\n'
1788            '  [type.googleapis.com/protobuf_unittest.OneString] {\n'
1789            '    data: "string0"\n'
1790            '  }\n'
1791            '}\n'
1792            'repeated_any_value {\n'
1793            '  [type.googleapis.com/protobuf_unittest.OneString] {\n'
1794            '    data: "string1"\n'
1795            '  }\n'
1796            '}\n')
1797    text_format.Merge(text, message)
1798    packed_message = unittest_pb2.OneString()
1799    message.repeated_any_value[0].Unpack(packed_message)
1800    self.assertEqual('string0', packed_message.data)
1801    message.repeated_any_value[1].Unpack(packed_message)
1802    self.assertEqual('string1', packed_message.data)
1803
1804  def testMergeExpandedAnyPointyBrackets(self):
1805    message = any_test_pb2.TestAny()
1806    text = ('any_value {\n'
1807            '  [type.googleapis.com/protobuf_unittest.OneString] <\n'
1808            '    data: "string"\n'
1809            '  >\n'
1810            '}\n')
1811    text_format.Merge(text, message)
1812    packed_message = unittest_pb2.OneString()
1813    message.any_value.Unpack(packed_message)
1814    self.assertEqual('string', packed_message.data)
1815
1816  def testMergeAlternativeUrl(self):
1817    message = any_test_pb2.TestAny()
1818    text = ('any_value {\n'
1819            '  [type.otherapi.com/protobuf_unittest.OneString] {\n'
1820            '    data: "string"\n'
1821            '  }\n'
1822            '}\n')
1823    text_format.Merge(text, message)
1824    packed_message = unittest_pb2.OneString()
1825    self.assertEqual('type.otherapi.com/protobuf_unittest.OneString',
1826                     message.any_value.type_url)
1827
1828  def testMergeExpandedAnyDescriptorPoolMissingType(self):
1829    message = any_test_pb2.TestAny()
1830    text = ('any_value {\n'
1831            '  [type.googleapis.com/protobuf_unittest.OneString] {\n'
1832            '    data: "string"\n'
1833            '  }\n'
1834            '}\n')
1835    with self.assertRaises(text_format.ParseError) as e:
1836      empty_pool = descriptor_pool.DescriptorPool()
1837      text_format.Merge(text, message, descriptor_pool=empty_pool)
1838    self.assertEqual(
1839        str(e.exception),
1840        'Type protobuf_unittest.OneString not found in descriptor pool')
1841
1842  def testMergeUnexpandedAny(self):
1843    text = ('any_value {\n'
1844            '  type_url: "type.googleapis.com/protobuf_unittest.OneString"\n'
1845            '  value: "\\n\\006string"\n'
1846            '}\n')
1847    message = any_test_pb2.TestAny()
1848    text_format.Merge(text, message)
1849    packed_message = unittest_pb2.OneString()
1850    message.any_value.Unpack(packed_message)
1851    self.assertEqual('string', packed_message.data)
1852
1853  def testMergeMissingAnyEndToken(self):
1854    message = any_test_pb2.TestAny()
1855    text = ('any_value {\n'
1856            '  [type.googleapis.com/protobuf_unittest.OneString] {\n'
1857            '    data: "string"\n')
1858    with self.assertRaises(text_format.ParseError) as e:
1859      text_format.Merge(text, message)
1860    self.assertEqual(str(e.exception), '3:11 : Expected "}".')
1861
1862  def testProto3Optional(self):
1863    msg = test_proto3_optional_pb2.TestProto3Optional()
1864    self.assertEqual(text_format.MessageToString(msg), '')
1865    msg.optional_int32 = 0
1866    msg.optional_float = 0.0
1867    msg.optional_string = ''
1868    msg.optional_nested_message.bb = 0
1869    text = ('optional_int32: 0\n'
1870            'optional_float: 0.0\n'
1871            'optional_string: ""\n'
1872            'optional_nested_message {\n'
1873            '  bb: 0\n'
1874            '}\n')
1875    self.assertEqual(text_format.MessageToString(msg), text)
1876    msg2 = test_proto3_optional_pb2.TestProto3Optional()
1877    text_format.Parse(text, msg2)
1878    self.assertEqual(text_format.MessageToString(msg2), text)
1879
1880
1881class TokenizerTest(unittest.TestCase):
1882
1883  def testSimpleTokenCases(self):
1884    text = ('identifier1:"string1"\n     \n\n'
1885            'identifier2 : \n \n123  \n  identifier3 :\'string\'\n'
1886            'identifiER_4 : 1.1e+2 ID5:-0.23 ID6:\'aaaa\\\'bbbb\'\n'
1887            'ID7 : "aa\\"bb"\n\n\n\n ID8: {A:inf B:-inf C:true D:false}\n'
1888            'ID9: 22 ID10: -111111111111111111 ID11: -22\n'
1889            'ID12: 2222222222222222222 ID13: 1.23456f ID14: 1.2e+2f '
1890            'false_bool:  0 true_BOOL:t \n true_bool1:  1 false_BOOL1:f '
1891            'False_bool: False True_bool: True X:iNf Y:-inF Z:nAN')
1892    tokenizer = text_format.Tokenizer(text.splitlines())
1893    methods = [(tokenizer.ConsumeIdentifier, 'identifier1'), ':',
1894               (tokenizer.ConsumeString, 'string1'),
1895               (tokenizer.ConsumeIdentifier, 'identifier2'), ':',
1896               (tokenizer.ConsumeInteger, 123),
1897               (tokenizer.ConsumeIdentifier, 'identifier3'), ':',
1898               (tokenizer.ConsumeString, 'string'),
1899               (tokenizer.ConsumeIdentifier, 'identifiER_4'), ':',
1900               (tokenizer.ConsumeFloat, 1.1e+2),
1901               (tokenizer.ConsumeIdentifier, 'ID5'), ':',
1902               (tokenizer.ConsumeFloat, -0.23),
1903               (tokenizer.ConsumeIdentifier, 'ID6'), ':',
1904               (tokenizer.ConsumeString, 'aaaa\'bbbb'),
1905               (tokenizer.ConsumeIdentifier, 'ID7'), ':',
1906               (tokenizer.ConsumeString, 'aa\"bb'),
1907               (tokenizer.ConsumeIdentifier, 'ID8'), ':', '{',
1908               (tokenizer.ConsumeIdentifier, 'A'), ':',
1909               (tokenizer.ConsumeFloat, float('inf')),
1910               (tokenizer.ConsumeIdentifier, 'B'), ':',
1911               (tokenizer.ConsumeFloat, -float('inf')),
1912               (tokenizer.ConsumeIdentifier, 'C'), ':',
1913               (tokenizer.ConsumeBool, True),
1914               (tokenizer.ConsumeIdentifier, 'D'), ':',
1915               (tokenizer.ConsumeBool, False), '}',
1916               (tokenizer.ConsumeIdentifier, 'ID9'), ':',
1917               (tokenizer.ConsumeInteger, 22),
1918               (tokenizer.ConsumeIdentifier, 'ID10'), ':',
1919               (tokenizer.ConsumeInteger, -111111111111111111),
1920               (tokenizer.ConsumeIdentifier, 'ID11'), ':',
1921               (tokenizer.ConsumeInteger, -22),
1922               (tokenizer.ConsumeIdentifier, 'ID12'), ':',
1923               (tokenizer.ConsumeInteger, 2222222222222222222),
1924               (tokenizer.ConsumeIdentifier, 'ID13'), ':',
1925               (tokenizer.ConsumeFloat, 1.23456),
1926               (tokenizer.ConsumeIdentifier, 'ID14'), ':',
1927               (tokenizer.ConsumeFloat, 1.2e+2),
1928               (tokenizer.ConsumeIdentifier, 'false_bool'), ':',
1929               (tokenizer.ConsumeBool, False),
1930               (tokenizer.ConsumeIdentifier, 'true_BOOL'), ':',
1931               (tokenizer.ConsumeBool, True),
1932               (tokenizer.ConsumeIdentifier, 'true_bool1'), ':',
1933               (tokenizer.ConsumeBool, True),
1934               (tokenizer.ConsumeIdentifier, 'false_BOOL1'), ':',
1935               (tokenizer.ConsumeBool, False),
1936               (tokenizer.ConsumeIdentifier, 'False_bool'), ':',
1937               (tokenizer.ConsumeBool, False),
1938               (tokenizer.ConsumeIdentifier, 'True_bool'), ':',
1939               (tokenizer.ConsumeBool, True),
1940               (tokenizer.ConsumeIdentifier, 'X'), ':',
1941               (tokenizer.ConsumeFloat, float('inf')),
1942               (tokenizer.ConsumeIdentifier, 'Y'), ':',
1943               (tokenizer.ConsumeFloat, float('-inf')),
1944               (tokenizer.ConsumeIdentifier, 'Z'), ':',
1945               (tokenizer.ConsumeFloat, float('nan'))]
1946
1947    i = 0
1948    while not tokenizer.AtEnd():
1949      m = methods[i]
1950      if isinstance(m, str):
1951        token = tokenizer.token
1952        self.assertEqual(token, m)
1953        tokenizer.NextToken()
1954      elif isinstance(m[1], float) and math.isnan(m[1]):
1955        self.assertTrue(math.isnan(m[0]()))
1956      else:
1957        self.assertEqual(m[1], m[0]())
1958      i += 1
1959
1960  def testConsumeAbstractIntegers(self):
1961    # This test only tests the failures in the integer parsing methods as well
1962    # as the '0' special cases.
1963    int64_max = (1 << 63) - 1
1964    uint32_max = (1 << 32) - 1
1965    text = '-1 %d %d' % (uint32_max + 1, int64_max + 1)
1966    tokenizer = text_format.Tokenizer(text.splitlines())
1967    self.assertEqual(-1, tokenizer.ConsumeInteger())
1968
1969    self.assertEqual(uint32_max + 1, tokenizer.ConsumeInteger())
1970
1971    self.assertEqual(int64_max + 1, tokenizer.ConsumeInteger())
1972    self.assertTrue(tokenizer.AtEnd())
1973
1974    text = '-0 0 0 1.2'
1975    tokenizer = text_format.Tokenizer(text.splitlines())
1976    self.assertEqual(0, tokenizer.ConsumeInteger())
1977    self.assertEqual(0, tokenizer.ConsumeInteger())
1978    self.assertEqual(True, tokenizer.TryConsumeInteger())
1979    self.assertEqual(False, tokenizer.TryConsumeInteger())
1980    with self.assertRaises(text_format.ParseError):
1981      tokenizer.ConsumeInteger()
1982    self.assertEqual(1.2, tokenizer.ConsumeFloat())
1983    self.assertTrue(tokenizer.AtEnd())
1984
1985  def testConsumeIntegers(self):
1986    # This test only tests the failures in the integer parsing methods as well
1987    # as the '0' special cases.
1988    int64_max = (1 << 63) - 1
1989    uint32_max = (1 << 32) - 1
1990    text = '-1 %d %d' % (uint32_max + 1, int64_max + 1)
1991    tokenizer = text_format.Tokenizer(text.splitlines())
1992    self.assertRaises(text_format.ParseError,
1993                      text_format._ConsumeUint32, tokenizer)
1994    self.assertRaises(text_format.ParseError,
1995                      text_format._ConsumeUint64, tokenizer)
1996    self.assertEqual(-1, text_format._ConsumeInt32(tokenizer))
1997
1998    self.assertRaises(text_format.ParseError,
1999                      text_format._ConsumeUint32, tokenizer)
2000    self.assertRaises(text_format.ParseError,
2001                      text_format._ConsumeInt32, tokenizer)
2002    self.assertEqual(uint32_max + 1, text_format._ConsumeInt64(tokenizer))
2003
2004    self.assertRaises(text_format.ParseError,
2005                      text_format._ConsumeInt64, tokenizer)
2006    self.assertEqual(int64_max + 1, text_format._ConsumeUint64(tokenizer))
2007    self.assertTrue(tokenizer.AtEnd())
2008
2009    text = '-0 -0 0 0'
2010    tokenizer = text_format.Tokenizer(text.splitlines())
2011    self.assertEqual(0, text_format._ConsumeUint32(tokenizer))
2012    self.assertEqual(0, text_format._ConsumeUint64(tokenizer))
2013    self.assertEqual(0, text_format._ConsumeUint32(tokenizer))
2014    self.assertEqual(0, text_format._ConsumeUint64(tokenizer))
2015    self.assertTrue(tokenizer.AtEnd())
2016
2017  def testConsumeOctalIntegers(self):
2018    """Test support for C style octal integers."""
2019    text = '00 -00 04 0755 -010 007 -0033 08 -09 01'
2020    tokenizer = text_format.Tokenizer(text.splitlines())
2021    self.assertEqual(0, tokenizer.ConsumeInteger())
2022    self.assertEqual(0, tokenizer.ConsumeInteger())
2023    self.assertEqual(4, tokenizer.ConsumeInteger())
2024    self.assertEqual(0o755, tokenizer.ConsumeInteger())
2025    self.assertEqual(-0o10, tokenizer.ConsumeInteger())
2026    self.assertEqual(7, tokenizer.ConsumeInteger())
2027    self.assertEqual(-0o033, tokenizer.ConsumeInteger())
2028    with self.assertRaises(text_format.ParseError):
2029      tokenizer.ConsumeInteger()  # 08
2030    tokenizer.NextToken()
2031    with self.assertRaises(text_format.ParseError):
2032      tokenizer.ConsumeInteger()  # -09
2033    tokenizer.NextToken()
2034    self.assertEqual(1, tokenizer.ConsumeInteger())
2035    self.assertTrue(tokenizer.AtEnd())
2036
2037  def testConsumeByteString(self):
2038    text = '"string1\''
2039    tokenizer = text_format.Tokenizer(text.splitlines())
2040    self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
2041
2042    text = 'string1"'
2043    tokenizer = text_format.Tokenizer(text.splitlines())
2044    self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
2045
2046    text = '\n"\\xt"'
2047    tokenizer = text_format.Tokenizer(text.splitlines())
2048    self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
2049
2050    text = '\n"\\"'
2051    tokenizer = text_format.Tokenizer(text.splitlines())
2052    self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
2053
2054    text = '\n"\\x"'
2055    tokenizer = text_format.Tokenizer(text.splitlines())
2056    self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
2057
2058  def testConsumeBool(self):
2059    text = 'not-a-bool'
2060    tokenizer = text_format.Tokenizer(text.splitlines())
2061    self.assertRaises(text_format.ParseError, tokenizer.ConsumeBool)
2062
2063  def testSkipComment(self):
2064    tokenizer = text_format.Tokenizer('# some comment'.splitlines())
2065    self.assertTrue(tokenizer.AtEnd())
2066    self.assertRaises(text_format.ParseError, tokenizer.ConsumeComment)
2067
2068  def testConsumeComment(self):
2069    tokenizer = text_format.Tokenizer('# some comment'.splitlines(),
2070                                      skip_comments=False)
2071    self.assertFalse(tokenizer.AtEnd())
2072    self.assertEqual('# some comment', tokenizer.ConsumeComment())
2073    self.assertTrue(tokenizer.AtEnd())
2074
2075  def testConsumeTwoComments(self):
2076    text = '# some comment\n# another comment'
2077    tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False)
2078    self.assertEqual('# some comment', tokenizer.ConsumeComment())
2079    self.assertFalse(tokenizer.AtEnd())
2080    self.assertEqual('# another comment', tokenizer.ConsumeComment())
2081    self.assertTrue(tokenizer.AtEnd())
2082
2083  def testConsumeTrailingComment(self):
2084    text = 'some_number: 4\n# some comment'
2085    tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False)
2086    self.assertRaises(text_format.ParseError, tokenizer.ConsumeComment)
2087
2088    self.assertEqual('some_number', tokenizer.ConsumeIdentifier())
2089    self.assertEqual(tokenizer.token, ':')
2090    tokenizer.NextToken()
2091    self.assertRaises(text_format.ParseError, tokenizer.ConsumeComment)
2092    self.assertEqual(4, tokenizer.ConsumeInteger())
2093    self.assertFalse(tokenizer.AtEnd())
2094
2095    self.assertEqual('# some comment', tokenizer.ConsumeComment())
2096    self.assertTrue(tokenizer.AtEnd())
2097
2098  def testConsumeLineComment(self):
2099    tokenizer = text_format.Tokenizer('# some comment'.splitlines(),
2100                                      skip_comments=False)
2101    self.assertFalse(tokenizer.AtEnd())
2102    self.assertEqual((False, '# some comment'),
2103                     tokenizer.ConsumeCommentOrTrailingComment())
2104    self.assertTrue(tokenizer.AtEnd())
2105
2106  def testConsumeTwoLineComments(self):
2107    text = '# some comment\n# another comment'
2108    tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False)
2109    self.assertEqual((False, '# some comment'),
2110                     tokenizer.ConsumeCommentOrTrailingComment())
2111    self.assertFalse(tokenizer.AtEnd())
2112    self.assertEqual((False, '# another comment'),
2113                     tokenizer.ConsumeCommentOrTrailingComment())
2114    self.assertTrue(tokenizer.AtEnd())
2115
2116  def testConsumeAndCheckTrailingComment(self):
2117    text = 'some_number: 4  # some comment'  # trailing comment on the same line
2118    tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False)
2119    self.assertRaises(text_format.ParseError,
2120                      tokenizer.ConsumeCommentOrTrailingComment)
2121
2122    self.assertEqual('some_number', tokenizer.ConsumeIdentifier())
2123    self.assertEqual(tokenizer.token, ':')
2124    tokenizer.NextToken()
2125    self.assertRaises(text_format.ParseError,
2126                      tokenizer.ConsumeCommentOrTrailingComment)
2127    self.assertEqual(4, tokenizer.ConsumeInteger())
2128    self.assertFalse(tokenizer.AtEnd())
2129
2130    self.assertEqual((True, '# some comment'),
2131                     tokenizer.ConsumeCommentOrTrailingComment())
2132    self.assertTrue(tokenizer.AtEnd())
2133
2134  def testHashinComment(self):
2135    text = 'some_number: 4  # some comment # not a new comment'
2136    tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False)
2137    self.assertEqual('some_number', tokenizer.ConsumeIdentifier())
2138    self.assertEqual(tokenizer.token, ':')
2139    tokenizer.NextToken()
2140    self.assertEqual(4, tokenizer.ConsumeInteger())
2141    self.assertEqual((True, '# some comment # not a new comment'),
2142                     tokenizer.ConsumeCommentOrTrailingComment())
2143    self.assertTrue(tokenizer.AtEnd())
2144
2145  def testHugeString(self):
2146    # With pathologic backtracking, fails with Forge OOM.
2147    text = '"' + 'a' * (10 * 1024 * 1024) + '"'
2148    tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False)
2149    tokenizer.ConsumeString()
2150
2151
2152# Tests for pretty printer functionality.
2153@_parameterized.parameters((unittest_pb2), (unittest_proto3_arena_pb2))
2154class PrettyPrinterTest(TextFormatBase):
2155
2156  def testPrettyPrintNoMatch(self, message_module):
2157
2158    def printer(message, indent, as_one_line):
2159      del message, indent, as_one_line
2160      return None
2161
2162    message = message_module.TestAllTypes()
2163    msg = message.repeated_nested_message.add()
2164    msg.bb = 42
2165    self.CompareToGoldenText(
2166        text_format.MessageToString(
2167            message, as_one_line=True, message_formatter=printer),
2168        'repeated_nested_message { bb: 42 }')
2169
2170  def testPrettyPrintOneLine(self, message_module):
2171
2172    def printer(m, indent, as_one_line):
2173      del indent, as_one_line
2174      if m.DESCRIPTOR == message_module.TestAllTypes.NestedMessage.DESCRIPTOR:
2175        return 'My lucky number is %s' % m.bb
2176
2177    message = message_module.TestAllTypes()
2178    msg = message.repeated_nested_message.add()
2179    msg.bb = 42
2180    self.CompareToGoldenText(
2181        text_format.MessageToString(
2182            message, as_one_line=True, message_formatter=printer),
2183        'repeated_nested_message { My lucky number is 42 }')
2184
2185  def testPrettyPrintMultiLine(self, message_module):
2186
2187    def printer(m, indent, as_one_line):
2188      if m.DESCRIPTOR == message_module.TestAllTypes.NestedMessage.DESCRIPTOR:
2189        line_deliminator = (' ' if as_one_line else '\n') + ' ' * indent
2190        return 'My lucky number is:%s%s' % (line_deliminator, m.bb)
2191      return None
2192
2193    message = message_module.TestAllTypes()
2194    msg = message.repeated_nested_message.add()
2195    msg.bb = 42
2196    self.CompareToGoldenText(
2197        text_format.MessageToString(
2198            message, as_one_line=True, message_formatter=printer),
2199        'repeated_nested_message { My lucky number is: 42 }')
2200    self.CompareToGoldenText(
2201        text_format.MessageToString(
2202            message, as_one_line=False, message_formatter=printer),
2203        'repeated_nested_message {\n  My lucky number is:\n  42\n}\n')
2204
2205  def testPrettyPrintEntireMessage(self, message_module):
2206
2207    def printer(m, indent, as_one_line):
2208      del indent, as_one_line
2209      if m.DESCRIPTOR == message_module.TestAllTypes.DESCRIPTOR:
2210        return 'The is the message!'
2211      return None
2212
2213    message = message_module.TestAllTypes()
2214    self.CompareToGoldenText(
2215        text_format.MessageToString(
2216            message, as_one_line=False, message_formatter=printer),
2217        'The is the message!\n')
2218    self.CompareToGoldenText(
2219        text_format.MessageToString(
2220            message, as_one_line=True, message_formatter=printer),
2221        'The is the message!')
2222
2223  def testPrettyPrintMultipleParts(self, message_module):
2224
2225    def printer(m, indent, as_one_line):
2226      del indent, as_one_line
2227      if m.DESCRIPTOR == message_module.TestAllTypes.NestedMessage.DESCRIPTOR:
2228        return 'My lucky number is %s' % m.bb
2229      return None
2230
2231    message = message_module.TestAllTypes()
2232    message.optional_int32 = 61
2233    msg = message.repeated_nested_message.add()
2234    msg.bb = 42
2235    msg = message.repeated_nested_message.add()
2236    msg.bb = 99
2237    msg = message.optional_nested_message
2238    msg.bb = 1
2239    self.CompareToGoldenText(
2240        text_format.MessageToString(
2241            message, as_one_line=True, message_formatter=printer),
2242        ('optional_int32: 61 '
2243         'optional_nested_message { My lucky number is 1 } '
2244         'repeated_nested_message { My lucky number is 42 } '
2245         'repeated_nested_message { My lucky number is 99 }'))
2246
2247    out = text_format.TextWriter(False)
2248    text_format.PrintField(
2249        message_module.TestAllTypes.DESCRIPTOR.fields_by_name[
2250            'optional_nested_message'],
2251        message.optional_nested_message,
2252        out,
2253        message_formatter=printer)
2254    self.assertEqual(
2255        'optional_nested_message {\n  My lucky number is 1\n}\n',
2256        out.getvalue())
2257    out.close()
2258
2259    out = text_format.TextWriter(False)
2260    text_format.PrintFieldValue(
2261        message_module.TestAllTypes.DESCRIPTOR.fields_by_name[
2262            'optional_nested_message'],
2263        message.optional_nested_message,
2264        out,
2265        message_formatter=printer)
2266    self.assertEqual(
2267        '{\n  My lucky number is 1\n}',
2268        out.getvalue())
2269    out.close()
2270
2271
2272class WhitespaceTest(TextFormatBase):
2273
2274  def setUp(self):
2275    self.out = text_format.TextWriter(False)
2276    self.addCleanup(self.out.close)
2277    self.message = unittest_pb2.NestedTestAllTypes()
2278    self.message.child.payload.optional_string = 'value'
2279    self.field = self.message.DESCRIPTOR.fields_by_name['child']
2280    self.value = self.message.child
2281
2282  def testMessageToString(self):
2283    self.CompareToGoldenText(
2284        text_format.MessageToString(self.message),
2285        textwrap.dedent("""\
2286            child {
2287              payload {
2288                optional_string: "value"
2289              }
2290            }
2291            """))
2292
2293  def testPrintMessage(self):
2294    text_format.PrintMessage(self.message, self.out)
2295    self.CompareToGoldenText(
2296        self.out.getvalue(),
2297        textwrap.dedent("""\
2298            child {
2299              payload {
2300                optional_string: "value"
2301              }
2302            }
2303            """))
2304
2305  def testPrintField(self):
2306    text_format.PrintField(self.field, self.value, self.out)
2307    self.CompareToGoldenText(
2308        self.out.getvalue(),
2309        textwrap.dedent("""\
2310            child {
2311              payload {
2312                optional_string: "value"
2313              }
2314            }
2315            """))
2316
2317  def testPrintFieldValue(self):
2318    text_format.PrintFieldValue(
2319        self.field, self.value, self.out)
2320    self.CompareToGoldenText(
2321        self.out.getvalue(),
2322        textwrap.dedent("""\
2323            {
2324              payload {
2325                optional_string: "value"
2326              }
2327            }"""))
2328
2329
2330class OptionalColonMessageToStringTest(unittest.TestCase):
2331
2332  def testForcePrintOptionalColon(self):
2333    packed_message = unittest_pb2.OneString()
2334    packed_message.data = 'string'
2335    message = any_test_pb2.TestAny()
2336    message.any_value.Pack(packed_message)
2337    output = text_format.MessageToString(
2338        message,
2339        force_colon=True)
2340    expected = ('any_value: {\n'
2341                '  [type.googleapis.com/protobuf_unittest.OneString]: {\n'
2342                '    data: "string"\n'
2343                '  }\n'
2344                '}\n')
2345    self.assertEqual(expected, output)
2346
2347
2348if __name__ == '__main__':
2349  unittest.main()
2350