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