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