• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#! /usr/bin/python
2#
3# Protocol Buffers - Google's data interchange format
4# Copyright 2008 Google Inc.  All rights reserved.
5# https://developers.google.com/protocol-buffers/
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions are
9# met:
10#
11#     * Redistributions of source code must retain the above copyright
12# notice, this list of conditions and the following disclaimer.
13#     * Redistributions in binary form must reproduce the above
14# copyright notice, this list of conditions and the following disclaimer
15# in the documentation and/or other materials provided with the
16# distribution.
17#     * Neither the name of Google Inc. nor the names of its
18# contributors may be used to endorse or promote products derived from
19# this software without specific prior written permission.
20#
21# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33"""Test for google.protobuf.text_format."""
34
35__author__ = 'kenton@google.com (Kenton Varda)'
36
37import re
38
39from google.apputils import basetest
40from google.protobuf import text_format
41from google.protobuf.internal import api_implementation
42from google.protobuf.internal import test_util
43from google.protobuf import unittest_pb2
44from google.protobuf import unittest_mset_pb2
45
46class TextFormatTest(basetest.TestCase):
47
48  def ReadGolden(self, golden_filename):
49    with test_util.GoldenFile(golden_filename) as f:
50      return (f.readlines() if str is bytes else  # PY3
51              [golden_line.decode('utf-8') for golden_line in f])
52
53  def CompareToGoldenFile(self, text, golden_filename):
54    golden_lines = self.ReadGolden(golden_filename)
55    self.assertMultiLineEqual(text, ''.join(golden_lines))
56
57  def CompareToGoldenText(self, text, golden_text):
58    self.assertMultiLineEqual(text, golden_text)
59
60  def testPrintAllFields(self):
61    message = unittest_pb2.TestAllTypes()
62    test_util.SetAllFields(message)
63    self.CompareToGoldenFile(
64        self.RemoveRedundantZeros(text_format.MessageToString(message)),
65        'text_format_unittest_data_oneof_implemented.txt')
66
67  def testPrintInIndexOrder(self):
68    message = unittest_pb2.TestFieldOrderings()
69    message.my_string = '115'
70    message.my_int = 101
71    message.my_float = 111
72    self.CompareToGoldenText(
73        self.RemoveRedundantZeros(text_format.MessageToString(
74            message, use_index_order=True)),
75        'my_string: \"115\"\nmy_int: 101\nmy_float: 111\n')
76    self.CompareToGoldenText(
77        self.RemoveRedundantZeros(text_format.MessageToString(
78            message)), 'my_int: 101\nmy_string: \"115\"\nmy_float: 111\n')
79
80  def testPrintAllExtensions(self):
81    message = unittest_pb2.TestAllExtensions()
82    test_util.SetAllExtensions(message)
83    self.CompareToGoldenFile(
84        self.RemoveRedundantZeros(text_format.MessageToString(message)),
85        'text_format_unittest_extensions_data.txt')
86
87  def testPrintAllFieldsPointy(self):
88    message = unittest_pb2.TestAllTypes()
89    test_util.SetAllFields(message)
90    self.CompareToGoldenFile(
91        self.RemoveRedundantZeros(
92            text_format.MessageToString(message, pointy_brackets=True)),
93        'text_format_unittest_data_pointy_oneof.txt')
94
95  def testPrintAllExtensionsPointy(self):
96    message = unittest_pb2.TestAllExtensions()
97    test_util.SetAllExtensions(message)
98    self.CompareToGoldenFile(
99        self.RemoveRedundantZeros(text_format.MessageToString(
100            message, pointy_brackets=True)),
101        'text_format_unittest_extensions_data_pointy.txt')
102
103  def testPrintMessageSet(self):
104    message = unittest_mset_pb2.TestMessageSetContainer()
105    ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
106    ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
107    message.message_set.Extensions[ext1].i = 23
108    message.message_set.Extensions[ext2].str = 'foo'
109    self.CompareToGoldenText(
110        text_format.MessageToString(message),
111        'message_set {\n'
112        '  [protobuf_unittest.TestMessageSetExtension1] {\n'
113        '    i: 23\n'
114        '  }\n'
115        '  [protobuf_unittest.TestMessageSetExtension2] {\n'
116        '    str: \"foo\"\n'
117        '  }\n'
118        '}\n')
119
120  def testPrintExotic(self):
121    message = unittest_pb2.TestAllTypes()
122    message.repeated_int64.append(-9223372036854775808)
123    message.repeated_uint64.append(18446744073709551615)
124    message.repeated_double.append(123.456)
125    message.repeated_double.append(1.23e22)
126    message.repeated_double.append(1.23e-18)
127    message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
128    message.repeated_string.append(u'\u00fc\ua71f')
129    self.CompareToGoldenText(
130        self.RemoveRedundantZeros(text_format.MessageToString(message)),
131        'repeated_int64: -9223372036854775808\n'
132        'repeated_uint64: 18446744073709551615\n'
133        'repeated_double: 123.456\n'
134        'repeated_double: 1.23e+22\n'
135        'repeated_double: 1.23e-18\n'
136        'repeated_string:'
137        ' "\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
138        'repeated_string: "\\303\\274\\352\\234\\237"\n')
139
140  def testPrintExoticUnicodeSubclass(self):
141    class UnicodeSub(unicode):
142      pass
143    message = unittest_pb2.TestAllTypes()
144    message.repeated_string.append(UnicodeSub(u'\u00fc\ua71f'))
145    self.CompareToGoldenText(
146        text_format.MessageToString(message),
147        'repeated_string: "\\303\\274\\352\\234\\237"\n')
148
149  def testPrintNestedMessageAsOneLine(self):
150    message = unittest_pb2.TestAllTypes()
151    msg = message.repeated_nested_message.add()
152    msg.bb = 42
153    self.CompareToGoldenText(
154        text_format.MessageToString(message, as_one_line=True),
155        'repeated_nested_message { bb: 42 }')
156
157  def testPrintRepeatedFieldsAsOneLine(self):
158    message = unittest_pb2.TestAllTypes()
159    message.repeated_int32.append(1)
160    message.repeated_int32.append(1)
161    message.repeated_int32.append(3)
162    message.repeated_string.append("Google")
163    message.repeated_string.append("Zurich")
164    self.CompareToGoldenText(
165        text_format.MessageToString(message, as_one_line=True),
166        'repeated_int32: 1 repeated_int32: 1 repeated_int32: 3 '
167        'repeated_string: "Google" repeated_string: "Zurich"')
168
169  def testPrintNestedNewLineInStringAsOneLine(self):
170    message = unittest_pb2.TestAllTypes()
171    message.optional_string = "a\nnew\nline"
172    self.CompareToGoldenText(
173        text_format.MessageToString(message, as_one_line=True),
174        'optional_string: "a\\nnew\\nline"')
175
176  def testPrintMessageSetAsOneLine(self):
177    message = unittest_mset_pb2.TestMessageSetContainer()
178    ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
179    ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
180    message.message_set.Extensions[ext1].i = 23
181    message.message_set.Extensions[ext2].str = 'foo'
182    self.CompareToGoldenText(
183        text_format.MessageToString(message, as_one_line=True),
184        'message_set {'
185        ' [protobuf_unittest.TestMessageSetExtension1] {'
186        ' i: 23'
187        ' }'
188        ' [protobuf_unittest.TestMessageSetExtension2] {'
189        ' str: \"foo\"'
190        ' }'
191        ' }')
192
193  def testPrintExoticAsOneLine(self):
194    message = unittest_pb2.TestAllTypes()
195    message.repeated_int64.append(-9223372036854775808)
196    message.repeated_uint64.append(18446744073709551615)
197    message.repeated_double.append(123.456)
198    message.repeated_double.append(1.23e22)
199    message.repeated_double.append(1.23e-18)
200    message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
201    message.repeated_string.append(u'\u00fc\ua71f')
202    self.CompareToGoldenText(
203        self.RemoveRedundantZeros(
204            text_format.MessageToString(message, as_one_line=True)),
205        'repeated_int64: -9223372036854775808'
206        ' repeated_uint64: 18446744073709551615'
207        ' repeated_double: 123.456'
208        ' repeated_double: 1.23e+22'
209        ' repeated_double: 1.23e-18'
210        ' repeated_string: '
211        '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""'
212        ' repeated_string: "\\303\\274\\352\\234\\237"')
213
214  def testRoundTripExoticAsOneLine(self):
215    message = unittest_pb2.TestAllTypes()
216    message.repeated_int64.append(-9223372036854775808)
217    message.repeated_uint64.append(18446744073709551615)
218    message.repeated_double.append(123.456)
219    message.repeated_double.append(1.23e22)
220    message.repeated_double.append(1.23e-18)
221    message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
222    message.repeated_string.append(u'\u00fc\ua71f')
223
224    # Test as_utf8 = False.
225    wire_text = text_format.MessageToString(
226        message, as_one_line=True, as_utf8=False)
227    parsed_message = unittest_pb2.TestAllTypes()
228    r = text_format.Parse(wire_text, parsed_message)
229    self.assertIs(r, parsed_message)
230    self.assertEquals(message, parsed_message)
231
232    # Test as_utf8 = True.
233    wire_text = text_format.MessageToString(
234        message, as_one_line=True, as_utf8=True)
235    parsed_message = unittest_pb2.TestAllTypes()
236    r = text_format.Parse(wire_text, parsed_message)
237    self.assertIs(r, parsed_message)
238    self.assertEquals(message, parsed_message,
239                      '\n%s != %s' % (message, parsed_message))
240
241  def testPrintRawUtf8String(self):
242    message = unittest_pb2.TestAllTypes()
243    message.repeated_string.append(u'\u00fc\ua71f')
244    text = text_format.MessageToString(message, as_utf8=True)
245    self.CompareToGoldenText(text, 'repeated_string: "\303\274\352\234\237"\n')
246    parsed_message = unittest_pb2.TestAllTypes()
247    text_format.Parse(text, parsed_message)
248    self.assertEquals(message, parsed_message,
249                      '\n%s != %s' % (message, parsed_message))
250
251  def testPrintFloatFormat(self):
252    # Check that float_format argument is passed to sub-message formatting.
253    message = unittest_pb2.NestedTestAllTypes()
254    # We use 1.25 as it is a round number in binary.  The proto 32-bit float
255    # will not gain additional imprecise digits as a 64-bit Python float and
256    # show up in its str.  32-bit 1.2 is noisy when extended to 64-bit:
257    #  >>> struct.unpack('f', struct.pack('f', 1.2))[0]
258    #  1.2000000476837158
259    #  >>> struct.unpack('f', struct.pack('f', 1.25))[0]
260    #  1.25
261    message.payload.optional_float = 1.25
262    # Check rounding at 15 significant digits
263    message.payload.optional_double = -.000003456789012345678
264    # Check no decimal point.
265    message.payload.repeated_float.append(-5642)
266    # Check no trailing zeros.
267    message.payload.repeated_double.append(.000078900)
268    formatted_fields = ['optional_float: 1.25',
269                        'optional_double: -3.45678901234568e-6',
270                        'repeated_float: -5642',
271                        'repeated_double: 7.89e-5']
272    text_message = text_format.MessageToString(message, float_format='.15g')
273    self.CompareToGoldenText(
274        self.RemoveRedundantZeros(text_message),
275        'payload {{\n  {}\n  {}\n  {}\n  {}\n}}\n'.format(*formatted_fields))
276    # as_one_line=True is a separate code branch where float_format is passed.
277    text_message = text_format.MessageToString(message, as_one_line=True,
278                                               float_format='.15g')
279    self.CompareToGoldenText(
280        self.RemoveRedundantZeros(text_message),
281        'payload {{ {} {} {} {} }}'.format(*formatted_fields))
282
283  def testMessageToString(self):
284    message = unittest_pb2.ForeignMessage()
285    message.c = 123
286    self.assertEqual('c: 123\n', str(message))
287
288  def RemoveRedundantZeros(self, text):
289    # Some platforms print 1e+5 as 1e+005.  This is fine, but we need to remove
290    # these zeros in order to match the golden file.
291    text = text.replace('e+0','e+').replace('e+0','e+') \
292               .replace('e-0','e-').replace('e-0','e-')
293    # Floating point fields are printed with .0 suffix even if they are
294    # actualy integer numbers.
295    text = re.compile('\.0$', re.MULTILINE).sub('', text)
296    return text
297
298  def testParseGolden(self):
299    golden_text = '\n'.join(self.ReadGolden('text_format_unittest_data.txt'))
300    parsed_message = unittest_pb2.TestAllTypes()
301    r = text_format.Parse(golden_text, parsed_message)
302    self.assertIs(r, parsed_message)
303
304    message = unittest_pb2.TestAllTypes()
305    test_util.SetAllFields(message)
306    self.assertEquals(message, parsed_message)
307
308  def testParseGoldenExtensions(self):
309    golden_text = '\n'.join(self.ReadGolden(
310        'text_format_unittest_extensions_data.txt'))
311    parsed_message = unittest_pb2.TestAllExtensions()
312    text_format.Parse(golden_text, parsed_message)
313
314    message = unittest_pb2.TestAllExtensions()
315    test_util.SetAllExtensions(message)
316    self.assertEquals(message, parsed_message)
317
318  def testParseAllFields(self):
319    message = unittest_pb2.TestAllTypes()
320    test_util.SetAllFields(message)
321    ascii_text = text_format.MessageToString(message)
322
323    parsed_message = unittest_pb2.TestAllTypes()
324    text_format.Parse(ascii_text, parsed_message)
325    self.assertEqual(message, parsed_message)
326    test_util.ExpectAllFieldsSet(self, message)
327
328  def testParseAllExtensions(self):
329    message = unittest_pb2.TestAllExtensions()
330    test_util.SetAllExtensions(message)
331    ascii_text = text_format.MessageToString(message)
332
333    parsed_message = unittest_pb2.TestAllExtensions()
334    text_format.Parse(ascii_text, parsed_message)
335    self.assertEqual(message, parsed_message)
336
337  def testParseMessageSet(self):
338    message = unittest_pb2.TestAllTypes()
339    text = ('repeated_uint64: 1\n'
340            'repeated_uint64: 2\n')
341    text_format.Parse(text, message)
342    self.assertEqual(1, message.repeated_uint64[0])
343    self.assertEqual(2, message.repeated_uint64[1])
344
345    message = unittest_mset_pb2.TestMessageSetContainer()
346    text = ('message_set {\n'
347            '  [protobuf_unittest.TestMessageSetExtension1] {\n'
348            '    i: 23\n'
349            '  }\n'
350            '  [protobuf_unittest.TestMessageSetExtension2] {\n'
351            '    str: \"foo\"\n'
352            '  }\n'
353            '}\n')
354    text_format.Parse(text, message)
355    ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
356    ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
357    self.assertEquals(23, message.message_set.Extensions[ext1].i)
358    self.assertEquals('foo', message.message_set.Extensions[ext2].str)
359
360  def testParseExotic(self):
361    message = unittest_pb2.TestAllTypes()
362    text = ('repeated_int64: -9223372036854775808\n'
363            'repeated_uint64: 18446744073709551615\n'
364            'repeated_double: 123.456\n'
365            'repeated_double: 1.23e+22\n'
366            'repeated_double: 1.23e-18\n'
367            'repeated_string: \n'
368            '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
369            'repeated_string: "foo" \'corge\' "grault"\n'
370            'repeated_string: "\\303\\274\\352\\234\\237"\n'
371            'repeated_string: "\\xc3\\xbc"\n'
372            'repeated_string: "\xc3\xbc"\n')
373    text_format.Parse(text, message)
374
375    self.assertEqual(-9223372036854775808, message.repeated_int64[0])
376    self.assertEqual(18446744073709551615, message.repeated_uint64[0])
377    self.assertEqual(123.456, message.repeated_double[0])
378    self.assertEqual(1.23e22, message.repeated_double[1])
379    self.assertEqual(1.23e-18, message.repeated_double[2])
380    self.assertEqual(
381        '\000\001\a\b\f\n\r\t\v\\\'"', message.repeated_string[0])
382    self.assertEqual('foocorgegrault', message.repeated_string[1])
383    self.assertEqual(u'\u00fc\ua71f', message.repeated_string[2])
384    self.assertEqual(u'\u00fc', message.repeated_string[3])
385
386  def testParseTrailingCommas(self):
387    message = unittest_pb2.TestAllTypes()
388    text = ('repeated_int64: 100;\n'
389            'repeated_int64: 200;\n'
390            'repeated_int64: 300,\n'
391            'repeated_string: "one",\n'
392            'repeated_string: "two";\n')
393    text_format.Parse(text, message)
394
395    self.assertEqual(100, message.repeated_int64[0])
396    self.assertEqual(200, message.repeated_int64[1])
397    self.assertEqual(300, message.repeated_int64[2])
398    self.assertEqual(u'one', message.repeated_string[0])
399    self.assertEqual(u'two', message.repeated_string[1])
400
401  def testParseEmptyText(self):
402    message = unittest_pb2.TestAllTypes()
403    text = ''
404    text_format.Parse(text, message)
405    self.assertEquals(unittest_pb2.TestAllTypes(), message)
406
407  def testParseInvalidUtf8(self):
408    message = unittest_pb2.TestAllTypes()
409    text = 'repeated_string: "\\xc3\\xc3"'
410    self.assertRaises(text_format.ParseError, text_format.Parse, text, message)
411
412  def testParseSingleWord(self):
413    message = unittest_pb2.TestAllTypes()
414    text = 'foo'
415    self.assertRaisesWithLiteralMatch(
416        text_format.ParseError,
417        ('1:1 : Message type "protobuf_unittest.TestAllTypes" has no field named '
418         '"foo".'),
419        text_format.Parse, text, message)
420
421  def testParseUnknownField(self):
422    message = unittest_pb2.TestAllTypes()
423    text = 'unknown_field: 8\n'
424    self.assertRaisesWithLiteralMatch(
425        text_format.ParseError,
426        ('1:1 : Message type "protobuf_unittest.TestAllTypes" has no field named '
427         '"unknown_field".'),
428        text_format.Parse, text, message)
429
430  def testParseBadExtension(self):
431    message = unittest_pb2.TestAllExtensions()
432    text = '[unknown_extension]: 8\n'
433    self.assertRaisesWithLiteralMatch(
434        text_format.ParseError,
435        '1:2 : Extension "unknown_extension" not registered.',
436        text_format.Parse, text, message)
437    message = unittest_pb2.TestAllTypes()
438    self.assertRaisesWithLiteralMatch(
439        text_format.ParseError,
440        ('1:2 : Message type "protobuf_unittest.TestAllTypes" does not have '
441         'extensions.'),
442        text_format.Parse, text, message)
443
444  def testParseGroupNotClosed(self):
445    message = unittest_pb2.TestAllTypes()
446    text = 'RepeatedGroup: <'
447    self.assertRaisesWithLiteralMatch(
448        text_format.ParseError, '1:16 : Expected ">".',
449        text_format.Parse, text, message)
450
451    text = 'RepeatedGroup: {'
452    self.assertRaisesWithLiteralMatch(
453        text_format.ParseError, '1:16 : Expected "}".',
454        text_format.Parse, text, message)
455
456  def testParseEmptyGroup(self):
457    message = unittest_pb2.TestAllTypes()
458    text = 'OptionalGroup: {}'
459    text_format.Parse(text, message)
460    self.assertTrue(message.HasField('optionalgroup'))
461
462    message.Clear()
463
464    message = unittest_pb2.TestAllTypes()
465    text = 'OptionalGroup: <>'
466    text_format.Parse(text, message)
467    self.assertTrue(message.HasField('optionalgroup'))
468
469  def testParseBadEnumValue(self):
470    message = unittest_pb2.TestAllTypes()
471    text = 'optional_nested_enum: BARR'
472    self.assertRaisesWithLiteralMatch(
473        text_format.ParseError,
474        ('1:23 : Enum type "protobuf_unittest.TestAllTypes.NestedEnum" '
475         'has no value named BARR.'),
476        text_format.Parse, text, message)
477
478    message = unittest_pb2.TestAllTypes()
479    text = 'optional_nested_enum: 100'
480    self.assertRaisesWithLiteralMatch(
481        text_format.ParseError,
482        ('1:23 : Enum type "protobuf_unittest.TestAllTypes.NestedEnum" '
483         'has no value with number 100.'),
484        text_format.Parse, text, message)
485
486  def testParseBadIntValue(self):
487    message = unittest_pb2.TestAllTypes()
488    text = 'optional_int32: bork'
489    self.assertRaisesWithLiteralMatch(
490        text_format.ParseError,
491        ('1:17 : Couldn\'t parse integer: bork'),
492        text_format.Parse, text, message)
493
494  def testParseStringFieldUnescape(self):
495    message = unittest_pb2.TestAllTypes()
496    text = r'''repeated_string: "\xf\x62"
497               repeated_string: "\\xf\\x62"
498               repeated_string: "\\\xf\\\x62"
499               repeated_string: "\\\\xf\\\\x62"
500               repeated_string: "\\\\\xf\\\\\x62"
501               repeated_string: "\x5cx20"'''
502    text_format.Parse(text, message)
503
504    SLASH = '\\'
505    self.assertEqual('\x0fb', message.repeated_string[0])
506    self.assertEqual(SLASH + 'xf' + SLASH + 'x62', message.repeated_string[1])
507    self.assertEqual(SLASH + '\x0f' + SLASH + 'b', message.repeated_string[2])
508    self.assertEqual(SLASH + SLASH + 'xf' + SLASH + SLASH + 'x62',
509                     message.repeated_string[3])
510    self.assertEqual(SLASH + SLASH + '\x0f' + SLASH + SLASH + 'b',
511                     message.repeated_string[4])
512    self.assertEqual(SLASH + 'x20', message.repeated_string[5])
513
514  def testMergeRepeatedScalars(self):
515    message = unittest_pb2.TestAllTypes()
516    text = ('optional_int32: 42 '
517            'optional_int32: 67')
518    r = text_format.Merge(text, message)
519    self.assertIs(r, message)
520    self.assertEqual(67, message.optional_int32)
521
522  def testParseRepeatedScalars(self):
523    message = unittest_pb2.TestAllTypes()
524    text = ('optional_int32: 42 '
525            'optional_int32: 67')
526    self.assertRaisesWithLiteralMatch(
527        text_format.ParseError,
528        ('1:36 : Message type "protobuf_unittest.TestAllTypes" should not '
529         'have multiple "optional_int32" fields.'),
530        text_format.Parse, text, message)
531
532  def testMergeRepeatedNestedMessageScalars(self):
533    message = unittest_pb2.TestAllTypes()
534    text = ('optional_nested_message { bb: 1 } '
535            'optional_nested_message { bb: 2 }')
536    r = text_format.Merge(text, message)
537    self.assertTrue(r is message)
538    self.assertEqual(2, message.optional_nested_message.bb)
539
540  def testParseRepeatedNestedMessageScalars(self):
541    message = unittest_pb2.TestAllTypes()
542    text = ('optional_nested_message { bb: 1 } '
543            'optional_nested_message { bb: 2 }')
544    self.assertRaisesWithLiteralMatch(
545        text_format.ParseError,
546        ('1:65 : Message type "protobuf_unittest.TestAllTypes.NestedMessage" '
547         'should not have multiple "bb" fields.'),
548        text_format.Parse, text, message)
549
550  def testMergeRepeatedExtensionScalars(self):
551    message = unittest_pb2.TestAllExtensions()
552    text = ('[protobuf_unittest.optional_int32_extension]: 42 '
553            '[protobuf_unittest.optional_int32_extension]: 67')
554    text_format.Merge(text, message)
555    self.assertEqual(
556        67,
557        message.Extensions[unittest_pb2.optional_int32_extension])
558
559  def testParseRepeatedExtensionScalars(self):
560    message = unittest_pb2.TestAllExtensions()
561    text = ('[protobuf_unittest.optional_int32_extension]: 42 '
562            '[protobuf_unittest.optional_int32_extension]: 67')
563    self.assertRaisesWithLiteralMatch(
564        text_format.ParseError,
565        ('1:96 : Message type "protobuf_unittest.TestAllExtensions" '
566         'should not have multiple '
567         '"protobuf_unittest.optional_int32_extension" extensions.'),
568        text_format.Parse, text, message)
569
570  def testParseLinesGolden(self):
571    opened = self.ReadGolden('text_format_unittest_data.txt')
572    parsed_message = unittest_pb2.TestAllTypes()
573    r = text_format.ParseLines(opened, parsed_message)
574    self.assertIs(r, parsed_message)
575
576    message = unittest_pb2.TestAllTypes()
577    test_util.SetAllFields(message)
578    self.assertEquals(message, parsed_message)
579
580  def testMergeLinesGolden(self):
581    opened = self.ReadGolden('text_format_unittest_data.txt')
582    parsed_message = unittest_pb2.TestAllTypes()
583    r = text_format.MergeLines(opened, parsed_message)
584    self.assertIs(r, parsed_message)
585
586    message = unittest_pb2.TestAllTypes()
587    test_util.SetAllFields(message)
588    self.assertEqual(message, parsed_message)
589
590  def testParseOneof(self):
591    m = unittest_pb2.TestAllTypes()
592    m.oneof_uint32 = 11
593    m2 = unittest_pb2.TestAllTypes()
594    text_format.Parse(text_format.MessageToString(m), m2)
595    self.assertEqual('oneof_uint32', m2.WhichOneof('oneof_field'))
596
597
598class TokenizerTest(basetest.TestCase):
599
600  def testSimpleTokenCases(self):
601    text = ('identifier1:"string1"\n     \n\n'
602            'identifier2 : \n \n123  \n  identifier3 :\'string\'\n'
603            'identifiER_4 : 1.1e+2 ID5:-0.23 ID6:\'aaaa\\\'bbbb\'\n'
604            'ID7 : "aa\\"bb"\n\n\n\n ID8: {A:inf B:-inf C:true D:false}\n'
605            'ID9: 22 ID10: -111111111111111111 ID11: -22\n'
606            'ID12: 2222222222222222222 ID13: 1.23456f ID14: 1.2e+2f '
607            'false_bool:  0 true_BOOL:t \n true_bool1:  1 false_BOOL1:f ')
608    tokenizer = text_format._Tokenizer(text.splitlines())
609    methods = [(tokenizer.ConsumeIdentifier, 'identifier1'),
610               ':',
611               (tokenizer.ConsumeString, 'string1'),
612               (tokenizer.ConsumeIdentifier, 'identifier2'),
613               ':',
614               (tokenizer.ConsumeInt32, 123),
615               (tokenizer.ConsumeIdentifier, 'identifier3'),
616               ':',
617               (tokenizer.ConsumeString, 'string'),
618               (tokenizer.ConsumeIdentifier, 'identifiER_4'),
619               ':',
620               (tokenizer.ConsumeFloat, 1.1e+2),
621               (tokenizer.ConsumeIdentifier, 'ID5'),
622               ':',
623               (tokenizer.ConsumeFloat, -0.23),
624               (tokenizer.ConsumeIdentifier, 'ID6'),
625               ':',
626               (tokenizer.ConsumeString, 'aaaa\'bbbb'),
627               (tokenizer.ConsumeIdentifier, 'ID7'),
628               ':',
629               (tokenizer.ConsumeString, 'aa\"bb'),
630               (tokenizer.ConsumeIdentifier, 'ID8'),
631               ':',
632               '{',
633               (tokenizer.ConsumeIdentifier, 'A'),
634               ':',
635               (tokenizer.ConsumeFloat, float('inf')),
636               (tokenizer.ConsumeIdentifier, 'B'),
637               ':',
638               (tokenizer.ConsumeFloat, -float('inf')),
639               (tokenizer.ConsumeIdentifier, 'C'),
640               ':',
641               (tokenizer.ConsumeBool, True),
642               (tokenizer.ConsumeIdentifier, 'D'),
643               ':',
644               (tokenizer.ConsumeBool, False),
645               '}',
646               (tokenizer.ConsumeIdentifier, 'ID9'),
647               ':',
648               (tokenizer.ConsumeUint32, 22),
649               (tokenizer.ConsumeIdentifier, 'ID10'),
650               ':',
651               (tokenizer.ConsumeInt64, -111111111111111111),
652               (tokenizer.ConsumeIdentifier, 'ID11'),
653               ':',
654               (tokenizer.ConsumeInt32, -22),
655               (tokenizer.ConsumeIdentifier, 'ID12'),
656               ':',
657               (tokenizer.ConsumeUint64, 2222222222222222222),
658               (tokenizer.ConsumeIdentifier, 'ID13'),
659               ':',
660               (tokenizer.ConsumeFloat, 1.23456),
661               (tokenizer.ConsumeIdentifier, 'ID14'),
662               ':',
663               (tokenizer.ConsumeFloat, 1.2e+2),
664               (tokenizer.ConsumeIdentifier, 'false_bool'),
665               ':',
666               (tokenizer.ConsumeBool, False),
667               (tokenizer.ConsumeIdentifier, 'true_BOOL'),
668               ':',
669               (tokenizer.ConsumeBool, True),
670               (tokenizer.ConsumeIdentifier, 'true_bool1'),
671               ':',
672               (tokenizer.ConsumeBool, True),
673               (tokenizer.ConsumeIdentifier, 'false_BOOL1'),
674               ':',
675               (tokenizer.ConsumeBool, False)]
676
677    i = 0
678    while not tokenizer.AtEnd():
679      m = methods[i]
680      if type(m) == str:
681        token = tokenizer.token
682        self.assertEqual(token, m)
683        tokenizer.NextToken()
684      else:
685        self.assertEqual(m[1], m[0]())
686      i += 1
687
688  def testConsumeIntegers(self):
689    # This test only tests the failures in the integer parsing methods as well
690    # as the '0' special cases.
691    int64_max = (1 << 63) - 1
692    uint32_max = (1 << 32) - 1
693    text = '-1 %d %d' % (uint32_max + 1, int64_max + 1)
694    tokenizer = text_format._Tokenizer(text.splitlines())
695    self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint32)
696    self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint64)
697    self.assertEqual(-1, tokenizer.ConsumeInt32())
698
699    self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint32)
700    self.assertRaises(text_format.ParseError, tokenizer.ConsumeInt32)
701    self.assertEqual(uint32_max + 1, tokenizer.ConsumeInt64())
702
703    self.assertRaises(text_format.ParseError, tokenizer.ConsumeInt64)
704    self.assertEqual(int64_max + 1, tokenizer.ConsumeUint64())
705    self.assertTrue(tokenizer.AtEnd())
706
707    text = '-0 -0 0 0'
708    tokenizer = text_format._Tokenizer(text.splitlines())
709    self.assertEqual(0, tokenizer.ConsumeUint32())
710    self.assertEqual(0, tokenizer.ConsumeUint64())
711    self.assertEqual(0, tokenizer.ConsumeUint32())
712    self.assertEqual(0, tokenizer.ConsumeUint64())
713    self.assertTrue(tokenizer.AtEnd())
714
715  def testConsumeByteString(self):
716    text = '"string1\''
717    tokenizer = text_format._Tokenizer(text.splitlines())
718    self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
719
720    text = 'string1"'
721    tokenizer = text_format._Tokenizer(text.splitlines())
722    self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
723
724    text = '\n"\\xt"'
725    tokenizer = text_format._Tokenizer(text.splitlines())
726    self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
727
728    text = '\n"\\"'
729    tokenizer = text_format._Tokenizer(text.splitlines())
730    self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
731
732    text = '\n"\\x"'
733    tokenizer = text_format._Tokenizer(text.splitlines())
734    self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
735
736  def testConsumeBool(self):
737    text = 'not-a-bool'
738    tokenizer = text_format._Tokenizer(text.splitlines())
739    self.assertRaises(text_format.ParseError, tokenizer.ConsumeBool)
740
741
742if __name__ == '__main__':
743  basetest.main()
744