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