1#! /usr/bin/env 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
37
38import re
39import six
40import string
41
42try:
43  import unittest2 as unittest  #PY26
44except ImportError:
45  import unittest
46
47from google.protobuf.internal import _parameterized
48
49from google.protobuf import map_unittest_pb2
50from google.protobuf import unittest_mset_pb2
51from google.protobuf import unittest_pb2
52from google.protobuf import unittest_proto3_arena_pb2
53from google.protobuf.internal import api_implementation
54from google.protobuf.internal import test_util
55from google.protobuf.internal import message_set_extensions_pb2
56from google.protobuf import text_format
57
58
59# Low-level nuts-n-bolts tests.
60class SimpleTextFormatTests(unittest.TestCase):
61
62  # The members of _QUOTES are formatted into a regexp template that
63  # expects single characters.  Therefore it's an error (in addition to being
64  # non-sensical in the first place) to try to specify a "quote mark" that is
65  # more than one character.
66  def testQuoteMarksAreSingleChars(self):
67    for quote in text_format._QUOTES:
68      self.assertEqual(1, len(quote))
69
70
71# Base class with some common functionality.
72class TextFormatBase(unittest.TestCase):
73
74  def ReadGolden(self, golden_filename):
75    with test_util.GoldenFile(golden_filename) as f:
76      return (f.readlines() if str is bytes else  # PY3
77              [golden_line.decode('utf-8') for golden_line in f])
78
79  def CompareToGoldenFile(self, text, golden_filename):
80    golden_lines = self.ReadGolden(golden_filename)
81    self.assertMultiLineEqual(text, ''.join(golden_lines))
82
83  def CompareToGoldenText(self, text, golden_text):
84    self.assertEqual(text, golden_text)
85
86  def RemoveRedundantZeros(self, text):
87    # Some platforms print 1e+5 as 1e+005.  This is fine, but we need to remove
88    # these zeros in order to match the golden file.
89    text = text.replace('e+0','e+').replace('e+0','e+') \
90               .replace('e-0','e-').replace('e-0','e-')
91    # Floating point fields are printed with .0 suffix even if they are
92    # actualy integer numbers.
93    text = re.compile('\.0$', re.MULTILINE).sub('', text)
94    return text
95
96
97@_parameterized.Parameters(
98    (unittest_pb2),
99    (unittest_proto3_arena_pb2))
100class TextFormatTest(TextFormatBase):
101
102  def testPrintExotic(self, message_module):
103    message = message_module.TestAllTypes()
104    message.repeated_int64.append(-9223372036854775808)
105    message.repeated_uint64.append(18446744073709551615)
106    message.repeated_double.append(123.456)
107    message.repeated_double.append(1.23e22)
108    message.repeated_double.append(1.23e-18)
109    message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
110    message.repeated_string.append(u'\u00fc\ua71f')
111    self.CompareToGoldenText(
112        self.RemoveRedundantZeros(text_format.MessageToString(message)),
113        'repeated_int64: -9223372036854775808\n'
114        'repeated_uint64: 18446744073709551615\n'
115        'repeated_double: 123.456\n'
116        'repeated_double: 1.23e+22\n'
117        'repeated_double: 1.23e-18\n'
118        'repeated_string:'
119        ' "\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
120        'repeated_string: "\\303\\274\\352\\234\\237"\n')
121
122  def testPrintExoticUnicodeSubclass(self, message_module):
123    class UnicodeSub(six.text_type):
124      pass
125    message = message_module.TestAllTypes()
126    message.repeated_string.append(UnicodeSub(u'\u00fc\ua71f'))
127    self.CompareToGoldenText(
128        text_format.MessageToString(message),
129        'repeated_string: "\\303\\274\\352\\234\\237"\n')
130
131  def testPrintNestedMessageAsOneLine(self, message_module):
132    message = message_module.TestAllTypes()
133    msg = message.repeated_nested_message.add()
134    msg.bb = 42
135    self.CompareToGoldenText(
136        text_format.MessageToString(message, as_one_line=True),
137        'repeated_nested_message { bb: 42 }')
138
139  def testPrintRepeatedFieldsAsOneLine(self, message_module):
140    message = message_module.TestAllTypes()
141    message.repeated_int32.append(1)
142    message.repeated_int32.append(1)
143    message.repeated_int32.append(3)
144    message.repeated_string.append('Google')
145    message.repeated_string.append('Zurich')
146    self.CompareToGoldenText(
147        text_format.MessageToString(message, as_one_line=True),
148        'repeated_int32: 1 repeated_int32: 1 repeated_int32: 3 '
149        'repeated_string: "Google" repeated_string: "Zurich"')
150
151  def testPrintNestedNewLineInStringAsOneLine(self, message_module):
152    message = message_module.TestAllTypes()
153    message.optional_string = 'a\nnew\nline'
154    self.CompareToGoldenText(
155        text_format.MessageToString(message, as_one_line=True),
156        'optional_string: "a\\nnew\\nline"')
157
158  def testPrintExoticAsOneLine(self, message_module):
159    message = message_module.TestAllTypes()
160    message.repeated_int64.append(-9223372036854775808)
161    message.repeated_uint64.append(18446744073709551615)
162    message.repeated_double.append(123.456)
163    message.repeated_double.append(1.23e22)
164    message.repeated_double.append(1.23e-18)
165    message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
166    message.repeated_string.append(u'\u00fc\ua71f')
167    self.CompareToGoldenText(
168        self.RemoveRedundantZeros(
169            text_format.MessageToString(message, as_one_line=True)),
170        'repeated_int64: -9223372036854775808'
171        ' repeated_uint64: 18446744073709551615'
172        ' repeated_double: 123.456'
173        ' repeated_double: 1.23e+22'
174        ' repeated_double: 1.23e-18'
175        ' repeated_string: '
176        '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""'
177        ' repeated_string: "\\303\\274\\352\\234\\237"')
178
179  def testRoundTripExoticAsOneLine(self, message_module):
180    message = message_module.TestAllTypes()
181    message.repeated_int64.append(-9223372036854775808)
182    message.repeated_uint64.append(18446744073709551615)
183    message.repeated_double.append(123.456)
184    message.repeated_double.append(1.23e22)
185    message.repeated_double.append(1.23e-18)
186    message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
187    message.repeated_string.append(u'\u00fc\ua71f')
188
189    # Test as_utf8 = False.
190    wire_text = text_format.MessageToString(
191        message, as_one_line=True, as_utf8=False)
192    parsed_message = message_module.TestAllTypes()
193    r = text_format.Parse(wire_text, parsed_message)
194    self.assertIs(r, parsed_message)
195    self.assertEqual(message, parsed_message)
196
197    # Test as_utf8 = True.
198    wire_text = text_format.MessageToString(
199        message, as_one_line=True, as_utf8=True)
200    parsed_message = message_module.TestAllTypes()
201    r = text_format.Parse(wire_text, parsed_message)
202    self.assertIs(r, parsed_message)
203    self.assertEqual(message, parsed_message,
204                      '\n%s != %s' % (message, parsed_message))
205
206  def testPrintRawUtf8String(self, message_module):
207    message = message_module.TestAllTypes()
208    message.repeated_string.append(u'\u00fc\ua71f')
209    text = text_format.MessageToString(message, as_utf8=True)
210    self.CompareToGoldenText(text, 'repeated_string: "\303\274\352\234\237"\n')
211    parsed_message = message_module.TestAllTypes()
212    text_format.Parse(text, parsed_message)
213    self.assertEqual(message, parsed_message,
214                      '\n%s != %s' % (message, parsed_message))
215
216  def testPrintFloatFormat(self, message_module):
217    # Check that float_format argument is passed to sub-message formatting.
218    message = message_module.NestedTestAllTypes()
219    # We use 1.25 as it is a round number in binary.  The proto 32-bit float
220    # will not gain additional imprecise digits as a 64-bit Python float and
221    # show up in its str.  32-bit 1.2 is noisy when extended to 64-bit:
222    #  >>> struct.unpack('f', struct.pack('f', 1.2))[0]
223    #  1.2000000476837158
224    #  >>> struct.unpack('f', struct.pack('f', 1.25))[0]
225    #  1.25
226    message.payload.optional_float = 1.25
227    # Check rounding at 15 significant digits
228    message.payload.optional_double = -.000003456789012345678
229    # Check no decimal point.
230    message.payload.repeated_float.append(-5642)
231    # Check no trailing zeros.
232    message.payload.repeated_double.append(.000078900)
233    formatted_fields = ['optional_float: 1.25',
234                        'optional_double: -3.45678901234568e-6',
235                        'repeated_float: -5642',
236                        'repeated_double: 7.89e-5']
237    text_message = text_format.MessageToString(message, float_format='.15g')
238    self.CompareToGoldenText(
239        self.RemoveRedundantZeros(text_message),
240        'payload {{\n  {0}\n  {1}\n  {2}\n  {3}\n}}\n'.format(*formatted_fields))
241    # as_one_line=True is a separate code branch where float_format is passed.
242    text_message = text_format.MessageToString(message, as_one_line=True,
243                                               float_format='.15g')
244    self.CompareToGoldenText(
245        self.RemoveRedundantZeros(text_message),
246        'payload {{ {0} {1} {2} {3} }}'.format(*formatted_fields))
247
248  def testMessageToString(self, message_module):
249    message = message_module.ForeignMessage()
250    message.c = 123
251    self.assertEqual('c: 123\n', str(message))
252
253  def testPrintField(self, message_module):
254    message = message_module.TestAllTypes()
255    field = message.DESCRIPTOR.fields_by_name['optional_float']
256    value = message.optional_float
257    out = text_format.TextWriter(False)
258    text_format.PrintField(field, value, out)
259    self.assertEqual('optional_float: 0.0\n', out.getvalue())
260    out.close()
261    # Test Printer
262    out = text_format.TextWriter(False)
263    printer = text_format._Printer(out)
264    printer.PrintField(field, value)
265    self.assertEqual('optional_float: 0.0\n', out.getvalue())
266    out.close()
267
268  def testPrintFieldValue(self, message_module):
269    message = message_module.TestAllTypes()
270    field = message.DESCRIPTOR.fields_by_name['optional_float']
271    value = message.optional_float
272    out = text_format.TextWriter(False)
273    text_format.PrintFieldValue(field, value, out)
274    self.assertEqual('0.0', out.getvalue())
275    out.close()
276    # Test Printer
277    out = text_format.TextWriter(False)
278    printer = text_format._Printer(out)
279    printer.PrintFieldValue(field, value)
280    self.assertEqual('0.0', out.getvalue())
281    out.close()
282
283  def testParseAllFields(self, message_module):
284    message = message_module.TestAllTypes()
285    test_util.SetAllFields(message)
286    ascii_text = text_format.MessageToString(message)
287
288    parsed_message = message_module.TestAllTypes()
289    text_format.Parse(ascii_text, parsed_message)
290    self.assertEqual(message, parsed_message)
291    if message_module is unittest_pb2:
292      test_util.ExpectAllFieldsSet(self, message)
293
294  def testParseExotic(self, message_module):
295    message = message_module.TestAllTypes()
296    text = ('repeated_int64: -9223372036854775808\n'
297            'repeated_uint64: 18446744073709551615\n'
298            'repeated_double: 123.456\n'
299            'repeated_double: 1.23e+22\n'
300            'repeated_double: 1.23e-18\n'
301            'repeated_string: \n'
302            '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
303            'repeated_string: "foo" \'corge\' "grault"\n'
304            'repeated_string: "\\303\\274\\352\\234\\237"\n'
305            'repeated_string: "\\xc3\\xbc"\n'
306            'repeated_string: "\xc3\xbc"\n')
307    text_format.Parse(text, message)
308
309    self.assertEqual(-9223372036854775808, message.repeated_int64[0])
310    self.assertEqual(18446744073709551615, message.repeated_uint64[0])
311    self.assertEqual(123.456, message.repeated_double[0])
312    self.assertEqual(1.23e22, message.repeated_double[1])
313    self.assertEqual(1.23e-18, message.repeated_double[2])
314    self.assertEqual(
315        '\000\001\a\b\f\n\r\t\v\\\'"', message.repeated_string[0])
316    self.assertEqual('foocorgegrault', message.repeated_string[1])
317    self.assertEqual(u'\u00fc\ua71f', message.repeated_string[2])
318    self.assertEqual(u'\u00fc', message.repeated_string[3])
319
320  def testParseTrailingCommas(self, message_module):
321    message = message_module.TestAllTypes()
322    text = ('repeated_int64: 100;\n'
323            'repeated_int64: 200;\n'
324            'repeated_int64: 300,\n'
325            'repeated_string: "one",\n'
326            'repeated_string: "two";\n')
327    text_format.Parse(text, message)
328
329    self.assertEqual(100, message.repeated_int64[0])
330    self.assertEqual(200, message.repeated_int64[1])
331    self.assertEqual(300, message.repeated_int64[2])
332    self.assertEqual(u'one', message.repeated_string[0])
333    self.assertEqual(u'two', message.repeated_string[1])
334
335  def testParseRepeatedScalarShortFormat(self, message_module):
336    message = message_module.TestAllTypes()
337    text = ('repeated_int64: [100, 200];\n'
338            'repeated_int64: 300,\n'
339            'repeated_string: ["one", "two"];\n')
340    text_format.Parse(text, message)
341
342    self.assertEqual(100, message.repeated_int64[0])
343    self.assertEqual(200, message.repeated_int64[1])
344    self.assertEqual(300, message.repeated_int64[2])
345    self.assertEqual(u'one', message.repeated_string[0])
346    self.assertEqual(u'two', message.repeated_string[1])
347
348  def testParseRepeatedMessageShortFormat(self, message_module):
349    message = message_module.TestAllTypes()
350    text = ('repeated_nested_message: [{bb: 100}, {bb: 200}],\n'
351            'repeated_nested_message: {bb: 300}\n'
352            'repeated_nested_message [{bb: 400}];\n')
353    text_format.Parse(text, message)
354
355    self.assertEqual(100, message.repeated_nested_message[0].bb)
356    self.assertEqual(200, message.repeated_nested_message[1].bb)
357    self.assertEqual(300, message.repeated_nested_message[2].bb)
358    self.assertEqual(400, message.repeated_nested_message[3].bb)
359
360  def testParseEmptyText(self, message_module):
361    message = message_module.TestAllTypes()
362    text = ''
363    text_format.Parse(text, message)
364    self.assertEqual(message_module.TestAllTypes(), message)
365
366  def testParseInvalidUtf8(self, message_module):
367    message = message_module.TestAllTypes()
368    text = 'repeated_string: "\\xc3\\xc3"'
369    self.assertRaises(text_format.ParseError, text_format.Parse, text, message)
370
371  def testParseSingleWord(self, message_module):
372    message = message_module.TestAllTypes()
373    text = 'foo'
374    six.assertRaisesRegex(self,
375        text_format.ParseError,
376        (r'1:1 : Message type "\w+.TestAllTypes" has no field named '
377         r'"foo".'),
378        text_format.Parse, text, message)
379
380  def testParseUnknownField(self, message_module):
381    message = message_module.TestAllTypes()
382    text = 'unknown_field: 8\n'
383    six.assertRaisesRegex(self,
384        text_format.ParseError,
385        (r'1:1 : Message type "\w+.TestAllTypes" has no field named '
386         r'"unknown_field".'),
387        text_format.Parse, text, message)
388
389  def testParseBadEnumValue(self, message_module):
390    message = message_module.TestAllTypes()
391    text = 'optional_nested_enum: BARR'
392    six.assertRaisesRegex(self,
393        text_format.ParseError,
394        (r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
395         r'has no value named BARR.'),
396        text_format.Parse, text, message)
397
398    message = message_module.TestAllTypes()
399    text = 'optional_nested_enum: 100'
400    six.assertRaisesRegex(self,
401        text_format.ParseError,
402        (r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
403         r'has no value with number 100.'),
404        text_format.Parse, text, message)
405
406  def testParseBadIntValue(self, message_module):
407    message = message_module.TestAllTypes()
408    text = 'optional_int32: bork'
409    six.assertRaisesRegex(self,
410        text_format.ParseError,
411        ('1:17 : Couldn\'t parse integer: bork'),
412        text_format.Parse, text, message)
413
414  def testParseStringFieldUnescape(self, message_module):
415    message = message_module.TestAllTypes()
416    text = r'''repeated_string: "\xf\x62"
417               repeated_string: "\\xf\\x62"
418               repeated_string: "\\\xf\\\x62"
419               repeated_string: "\\\\xf\\\\x62"
420               repeated_string: "\\\\\xf\\\\\x62"
421               repeated_string: "\x5cx20"'''
422    text_format.Parse(text, message)
423
424    SLASH = '\\'
425    self.assertEqual('\x0fb', message.repeated_string[0])
426    self.assertEqual(SLASH + 'xf' + SLASH + 'x62', message.repeated_string[1])
427    self.assertEqual(SLASH + '\x0f' + SLASH + 'b', message.repeated_string[2])
428    self.assertEqual(SLASH + SLASH + 'xf' + SLASH + SLASH + 'x62',
429                     message.repeated_string[3])
430    self.assertEqual(SLASH + SLASH + '\x0f' + SLASH + SLASH + 'b',
431                     message.repeated_string[4])
432    self.assertEqual(SLASH + 'x20', message.repeated_string[5])
433
434  def testMergeDuplicateScalars(self, message_module):
435    message = message_module.TestAllTypes()
436    text = ('optional_int32: 42 '
437            'optional_int32: 67')
438    r = text_format.Merge(text, message)
439    self.assertIs(r, message)
440    self.assertEqual(67, message.optional_int32)
441
442  def testMergeDuplicateNestedMessageScalars(self, message_module):
443    message = message_module.TestAllTypes()
444    text = ('optional_nested_message { bb: 1 } '
445            'optional_nested_message { bb: 2 }')
446    r = text_format.Merge(text, message)
447    self.assertTrue(r is message)
448    self.assertEqual(2, message.optional_nested_message.bb)
449
450  def testParseOneof(self, message_module):
451    m = message_module.TestAllTypes()
452    m.oneof_uint32 = 11
453    m2 = message_module.TestAllTypes()
454    text_format.Parse(text_format.MessageToString(m), m2)
455    self.assertEqual('oneof_uint32', m2.WhichOneof('oneof_field'))
456
457  def testParseMultipleOneof(self, message_module):
458    m_string = '\n'.join([
459        'oneof_uint32: 11',
460        'oneof_string: "foo"'])
461    m2 = message_module.TestAllTypes()
462    if message_module is unittest_pb2:
463      with self.assertRaisesRegexp(
464          text_format.ParseError, ' is specified along with field '):
465        text_format.Parse(m_string, m2)
466    else:
467      text_format.Parse(m_string, m2)
468      self.assertEqual('oneof_string', m2.WhichOneof('oneof_field'))
469
470
471# These are tests that aren't fundamentally specific to proto2, but are at
472# the moment because of differences between the proto2 and proto3 test schemas.
473# Ideally the schemas would be made more similar so these tests could pass.
474class OnlyWorksWithProto2RightNowTests(TextFormatBase):
475
476  def testPrintAllFieldsPointy(self):
477    message = unittest_pb2.TestAllTypes()
478    test_util.SetAllFields(message)
479    self.CompareToGoldenFile(
480        self.RemoveRedundantZeros(
481            text_format.MessageToString(message, pointy_brackets=True)),
482        'text_format_unittest_data_pointy_oneof.txt')
483
484  def testParseGolden(self):
485    golden_text = '\n'.join(self.ReadGolden(
486        'text_format_unittest_data_oneof_implemented.txt'))
487    parsed_message = unittest_pb2.TestAllTypes()
488    r = text_format.Parse(golden_text, parsed_message)
489    self.assertIs(r, parsed_message)
490
491    message = unittest_pb2.TestAllTypes()
492    test_util.SetAllFields(message)
493    self.assertEqual(message, parsed_message)
494
495  def testPrintAllFields(self):
496    message = unittest_pb2.TestAllTypes()
497    test_util.SetAllFields(message)
498    self.CompareToGoldenFile(
499        self.RemoveRedundantZeros(text_format.MessageToString(message)),
500        'text_format_unittest_data_oneof_implemented.txt')
501
502  def testPrintAllFieldsPointy(self):
503    message = unittest_pb2.TestAllTypes()
504    test_util.SetAllFields(message)
505    self.CompareToGoldenFile(
506        self.RemoveRedundantZeros(
507            text_format.MessageToString(message, pointy_brackets=True)),
508        'text_format_unittest_data_pointy_oneof.txt')
509
510  def testPrintInIndexOrder(self):
511    message = unittest_pb2.TestFieldOrderings()
512    message.my_string = '115'
513    message.my_int = 101
514    message.my_float = 111
515    message.optional_nested_message.oo = 0
516    message.optional_nested_message.bb = 1
517    self.CompareToGoldenText(
518        self.RemoveRedundantZeros(text_format.MessageToString(
519            message, use_index_order=True)),
520        'my_string: \"115\"\nmy_int: 101\nmy_float: 111\n'
521        'optional_nested_message {\n  oo: 0\n  bb: 1\n}\n')
522    self.CompareToGoldenText(
523        self.RemoveRedundantZeros(text_format.MessageToString(
524            message)),
525        'my_int: 101\nmy_string: \"115\"\nmy_float: 111\n'
526        'optional_nested_message {\n  bb: 1\n  oo: 0\n}\n')
527
528  def testMergeLinesGolden(self):
529    opened = self.ReadGolden('text_format_unittest_data_oneof_implemented.txt')
530    parsed_message = unittest_pb2.TestAllTypes()
531    r = text_format.MergeLines(opened, parsed_message)
532    self.assertIs(r, parsed_message)
533
534    message = unittest_pb2.TestAllTypes()
535    test_util.SetAllFields(message)
536    self.assertEqual(message, parsed_message)
537
538  def testParseLinesGolden(self):
539    opened = self.ReadGolden('text_format_unittest_data_oneof_implemented.txt')
540    parsed_message = unittest_pb2.TestAllTypes()
541    r = text_format.ParseLines(opened, parsed_message)
542    self.assertIs(r, parsed_message)
543
544    message = unittest_pb2.TestAllTypes()
545    test_util.SetAllFields(message)
546    self.assertEqual(message, parsed_message)
547
548  def testPrintMap(self):
549    message = map_unittest_pb2.TestMap()
550
551    message.map_int32_int32[-123] = -456
552    message.map_int64_int64[-2**33] = -2**34
553    message.map_uint32_uint32[123] = 456
554    message.map_uint64_uint64[2**33] = 2**34
555    message.map_string_string["abc"] = "123"
556    message.map_int32_foreign_message[111].c = 5
557
558    # Maps are serialized to text format using their underlying repeated
559    # representation.
560    self.CompareToGoldenText(
561        text_format.MessageToString(message),
562        'map_int32_int32 {\n'
563        '  key: -123\n'
564        '  value: -456\n'
565        '}\n'
566        'map_int64_int64 {\n'
567        '  key: -8589934592\n'
568        '  value: -17179869184\n'
569        '}\n'
570        'map_uint32_uint32 {\n'
571        '  key: 123\n'
572        '  value: 456\n'
573        '}\n'
574        'map_uint64_uint64 {\n'
575        '  key: 8589934592\n'
576        '  value: 17179869184\n'
577        '}\n'
578        'map_string_string {\n'
579        '  key: "abc"\n'
580        '  value: "123"\n'
581        '}\n'
582        'map_int32_foreign_message {\n'
583        '  key: 111\n'
584        '  value {\n'
585        '    c: 5\n'
586        '  }\n'
587        '}\n')
588
589  def testMapOrderEnforcement(self):
590    message = map_unittest_pb2.TestMap()
591    for letter in string.ascii_uppercase[13:26]:
592      message.map_string_string[letter] = 'dummy'
593    for letter in reversed(string.ascii_uppercase[0:13]):
594      message.map_string_string[letter] = 'dummy'
595    golden = ''.join((
596        'map_string_string {\n  key: "%c"\n  value: "dummy"\n}\n' % (letter,)
597        for letter in string.ascii_uppercase))
598    self.CompareToGoldenText(text_format.MessageToString(message), golden)
599
600  def testMapOrderSemantics(self):
601    golden_lines = self.ReadGolden('map_test_data.txt')
602    # The C++ implementation emits defaulted-value fields, while the Python
603    # implementation does not.  Adjusting for this is awkward, but it is
604    # valuable to test against a common golden file.
605    line_blacklist = ('  key: 0\n',
606                      '  value: 0\n',
607                      '  key: false\n',
608                      '  value: false\n')
609    golden_lines = [line for line in golden_lines if line not in line_blacklist]
610
611    message = map_unittest_pb2.TestMap()
612    text_format.ParseLines(golden_lines, message)
613    candidate = text_format.MessageToString(message)
614    # The Python implementation emits "1.0" for the double value that the C++
615    # implementation emits as "1".
616    candidate = candidate.replace('1.0', '1', 2)
617    self.assertMultiLineEqual(candidate, ''.join(golden_lines))
618
619
620# Tests of proto2-only features (MessageSet, extensions, etc.).
621class Proto2Tests(TextFormatBase):
622
623  def testPrintMessageSet(self):
624    message = unittest_mset_pb2.TestMessageSetContainer()
625    ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
626    ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
627    message.message_set.Extensions[ext1].i = 23
628    message.message_set.Extensions[ext2].str = 'foo'
629    self.CompareToGoldenText(
630        text_format.MessageToString(message),
631        'message_set {\n'
632        '  [protobuf_unittest.TestMessageSetExtension1] {\n'
633        '    i: 23\n'
634        '  }\n'
635        '  [protobuf_unittest.TestMessageSetExtension2] {\n'
636        '    str: \"foo\"\n'
637        '  }\n'
638        '}\n')
639
640    message = message_set_extensions_pb2.TestMessageSet()
641    ext = message_set_extensions_pb2.message_set_extension3
642    message.Extensions[ext].text = 'bar'
643    self.CompareToGoldenText(
644        text_format.MessageToString(message),
645        '[google.protobuf.internal.TestMessageSetExtension3] {\n'
646        '  text: \"bar\"\n'
647        '}\n')
648
649  def testPrintMessageSetByFieldNumber(self):
650    out = text_format.TextWriter(False)
651    message = unittest_mset_pb2.TestMessageSetContainer()
652    ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
653    ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
654    message.message_set.Extensions[ext1].i = 23
655    message.message_set.Extensions[ext2].str = 'foo'
656    text_format.PrintMessage(message, out, use_field_number=True)
657    self.CompareToGoldenText(
658        out.getvalue(),
659        '1 {\n'
660        '  1545008 {\n'
661        '    15: 23\n'
662        '  }\n'
663        '  1547769 {\n'
664        '    25: \"foo\"\n'
665        '  }\n'
666        '}\n')
667    out.close()
668
669  def testPrintMessageSetAsOneLine(self):
670    message = unittest_mset_pb2.TestMessageSetContainer()
671    ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
672    ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
673    message.message_set.Extensions[ext1].i = 23
674    message.message_set.Extensions[ext2].str = 'foo'
675    self.CompareToGoldenText(
676        text_format.MessageToString(message, as_one_line=True),
677        'message_set {'
678        ' [protobuf_unittest.TestMessageSetExtension1] {'
679        ' i: 23'
680        ' }'
681        ' [protobuf_unittest.TestMessageSetExtension2] {'
682        ' str: \"foo\"'
683        ' }'
684        ' }')
685
686  def testParseMessageSet(self):
687    message = unittest_pb2.TestAllTypes()
688    text = ('repeated_uint64: 1\n'
689            'repeated_uint64: 2\n')
690    text_format.Parse(text, message)
691    self.assertEqual(1, message.repeated_uint64[0])
692    self.assertEqual(2, message.repeated_uint64[1])
693
694    message = unittest_mset_pb2.TestMessageSetContainer()
695    text = ('message_set {\n'
696            '  [protobuf_unittest.TestMessageSetExtension1] {\n'
697            '    i: 23\n'
698            '  }\n'
699            '  [protobuf_unittest.TestMessageSetExtension2] {\n'
700            '    str: \"foo\"\n'
701            '  }\n'
702            '}\n')
703    text_format.Parse(text, message)
704    ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
705    ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
706    self.assertEqual(23, message.message_set.Extensions[ext1].i)
707    self.assertEqual('foo', message.message_set.Extensions[ext2].str)
708
709  def testParseMessageByFieldNumber(self):
710    message = unittest_pb2.TestAllTypes()
711    text = ('34: 1\n'
712            'repeated_uint64: 2\n')
713    text_format.Parse(text, message, allow_field_number=True)
714    self.assertEqual(1, message.repeated_uint64[0])
715    self.assertEqual(2, message.repeated_uint64[1])
716
717    message = unittest_mset_pb2.TestMessageSetContainer()
718    text = ('1 {\n'
719            '  1545008 {\n'
720            '    15: 23\n'
721            '  }\n'
722            '  1547769 {\n'
723            '    25: \"foo\"\n'
724            '  }\n'
725            '}\n')
726    text_format.Parse(text, message, allow_field_number=True)
727    ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
728    ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
729    self.assertEqual(23, message.message_set.Extensions[ext1].i)
730    self.assertEqual('foo', message.message_set.Extensions[ext2].str)
731
732    # Can't parse field number without set allow_field_number=True.
733    message = unittest_pb2.TestAllTypes()
734    text = '34:1\n'
735    six.assertRaisesRegex(
736        self,
737        text_format.ParseError,
738        (r'1:1 : Message type "\w+.TestAllTypes" has no field named '
739         r'"34".'),
740        text_format.Parse, text, message)
741
742    # Can't parse if field number is not found.
743    text = '1234:1\n'
744    six.assertRaisesRegex(
745        self,
746        text_format.ParseError,
747        (r'1:1 : Message type "\w+.TestAllTypes" has no field named '
748         r'"1234".'),
749        text_format.Parse, text, message, allow_field_number=True)
750
751  def testPrintAllExtensions(self):
752    message = unittest_pb2.TestAllExtensions()
753    test_util.SetAllExtensions(message)
754    self.CompareToGoldenFile(
755        self.RemoveRedundantZeros(text_format.MessageToString(message)),
756        'text_format_unittest_extensions_data.txt')
757
758  def testPrintAllExtensionsPointy(self):
759    message = unittest_pb2.TestAllExtensions()
760    test_util.SetAllExtensions(message)
761    self.CompareToGoldenFile(
762        self.RemoveRedundantZeros(text_format.MessageToString(
763            message, pointy_brackets=True)),
764        'text_format_unittest_extensions_data_pointy.txt')
765
766  def testParseGoldenExtensions(self):
767    golden_text = '\n'.join(self.ReadGolden(
768        'text_format_unittest_extensions_data.txt'))
769    parsed_message = unittest_pb2.TestAllExtensions()
770    text_format.Parse(golden_text, parsed_message)
771
772    message = unittest_pb2.TestAllExtensions()
773    test_util.SetAllExtensions(message)
774    self.assertEqual(message, parsed_message)
775
776  def testParseAllExtensions(self):
777    message = unittest_pb2.TestAllExtensions()
778    test_util.SetAllExtensions(message)
779    ascii_text = text_format.MessageToString(message)
780
781    parsed_message = unittest_pb2.TestAllExtensions()
782    text_format.Parse(ascii_text, parsed_message)
783    self.assertEqual(message, parsed_message)
784
785  def testParseAllowedUnknownExtension(self):
786    # Skip over unknown extension correctly.
787    message = unittest_mset_pb2.TestMessageSetContainer()
788    text = ('message_set {\n'
789            '  [unknown_extension] {\n'
790            '    i: 23\n'
791            '    bin: "\xe0"'
792            '    [nested_unknown_ext]: {\n'
793            '      i: 23\n'
794            '      test: "test_string"\n'
795            '      floaty_float: -0.315\n'
796            '      num: -inf\n'
797            '      multiline_str: "abc"\n'
798            '          "def"\n'
799            '          "xyz."\n'
800            '      [nested_unknown_ext]: <\n'
801            '        i: 23\n'
802            '        i: 24\n'
803            '        pointfloat: .3\n'
804            '        test: "test_string"\n'
805            '        floaty_float: -0.315\n'
806            '        num: -inf\n'
807            '        long_string: "test" "test2" \n'
808            '      >\n'
809            '    }\n'
810            '  }\n'
811            '  [unknown_extension]: 5\n'
812            '}\n')
813    text_format.Parse(text, message, allow_unknown_extension=True)
814    golden = 'message_set {\n}\n'
815    self.CompareToGoldenText(text_format.MessageToString(message), golden)
816
817    # Catch parse errors in unknown extension.
818    message = unittest_mset_pb2.TestMessageSetContainer()
819    malformed = ('message_set {\n'
820                 '  [unknown_extension] {\n'
821                 '    i:\n'  # Missing value.
822                 '  }\n'
823                 '}\n')
824    six.assertRaisesRegex(self,
825                          text_format.ParseError,
826                          'Invalid field value: }',
827                          text_format.Parse, malformed, message,
828                          allow_unknown_extension=True)
829
830    message = unittest_mset_pb2.TestMessageSetContainer()
831    malformed = ('message_set {\n'
832                 '  [unknown_extension] {\n'
833                 '    str: "malformed string\n'  # Missing closing quote.
834                 '  }\n'
835                 '}\n')
836    six.assertRaisesRegex(self,
837                          text_format.ParseError,
838                          'Invalid field value: "',
839                          text_format.Parse, malformed, message,
840                          allow_unknown_extension=True)
841
842    message = unittest_mset_pb2.TestMessageSetContainer()
843    malformed = ('message_set {\n'
844                 '  [unknown_extension] {\n'
845                 '    str: "malformed\n multiline\n string\n'
846                 '  }\n'
847                 '}\n')
848    six.assertRaisesRegex(self,
849                          text_format.ParseError,
850                          'Invalid field value: "',
851                          text_format.Parse, malformed, message,
852                          allow_unknown_extension=True)
853
854    message = unittest_mset_pb2.TestMessageSetContainer()
855    malformed = ('message_set {\n'
856                 '  [malformed_extension] <\n'
857                 '    i: -5\n'
858                 '  \n'  # Missing '>' here.
859                 '}\n')
860    six.assertRaisesRegex(self,
861                          text_format.ParseError,
862                          '5:1 : Expected ">".',
863                          text_format.Parse, malformed, message,
864                          allow_unknown_extension=True)
865
866    # Don't allow unknown fields with allow_unknown_extension=True.
867    message = unittest_mset_pb2.TestMessageSetContainer()
868    malformed = ('message_set {\n'
869                 '  unknown_field: true\n'
870                 '  \n'  # Missing '>' here.
871                 '}\n')
872    six.assertRaisesRegex(self,
873                          text_format.ParseError,
874                          ('2:3 : Message type '
875                           '"proto2_wireformat_unittest.TestMessageSet" has no'
876                           ' field named "unknown_field".'),
877                          text_format.Parse, malformed, message,
878                          allow_unknown_extension=True)
879
880    # Parse known extension correcty.
881    message = unittest_mset_pb2.TestMessageSetContainer()
882    text = ('message_set {\n'
883            '  [protobuf_unittest.TestMessageSetExtension1] {\n'
884            '    i: 23\n'
885            '  }\n'
886            '  [protobuf_unittest.TestMessageSetExtension2] {\n'
887            '    str: \"foo\"\n'
888            '  }\n'
889            '}\n')
890    text_format.Parse(text, message, allow_unknown_extension=True)
891    ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
892    ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
893    self.assertEqual(23, message.message_set.Extensions[ext1].i)
894    self.assertEqual('foo', message.message_set.Extensions[ext2].str)
895
896  def testParseBadExtension(self):
897    message = unittest_pb2.TestAllExtensions()
898    text = '[unknown_extension]: 8\n'
899    six.assertRaisesRegex(self,
900        text_format.ParseError,
901        '1:2 : Extension "unknown_extension" not registered.',
902        text_format.Parse, text, message)
903    message = unittest_pb2.TestAllTypes()
904    six.assertRaisesRegex(self,
905        text_format.ParseError,
906        ('1:2 : Message type "protobuf_unittest.TestAllTypes" does not have '
907         'extensions.'),
908        text_format.Parse, text, message)
909
910  def testMergeDuplicateExtensionScalars(self):
911    message = unittest_pb2.TestAllExtensions()
912    text = ('[protobuf_unittest.optional_int32_extension]: 42 '
913            '[protobuf_unittest.optional_int32_extension]: 67')
914    text_format.Merge(text, message)
915    self.assertEqual(
916        67,
917        message.Extensions[unittest_pb2.optional_int32_extension])
918
919  def testParseDuplicateExtensionScalars(self):
920    message = unittest_pb2.TestAllExtensions()
921    text = ('[protobuf_unittest.optional_int32_extension]: 42 '
922            '[protobuf_unittest.optional_int32_extension]: 67')
923    six.assertRaisesRegex(self,
924        text_format.ParseError,
925        ('1:96 : Message type "protobuf_unittest.TestAllExtensions" '
926         'should not have multiple '
927         '"protobuf_unittest.optional_int32_extension" extensions.'),
928        text_format.Parse, text, message)
929
930  def testParseDuplicateNestedMessageScalars(self):
931    message = unittest_pb2.TestAllTypes()
932    text = ('optional_nested_message { bb: 1 } '
933            'optional_nested_message { bb: 2 }')
934    six.assertRaisesRegex(self,
935        text_format.ParseError,
936        ('1:65 : Message type "protobuf_unittest.TestAllTypes.NestedMessage" '
937         'should not have multiple "bb" fields.'),
938        text_format.Parse, text, message)
939
940  def testParseDuplicateScalars(self):
941    message = unittest_pb2.TestAllTypes()
942    text = ('optional_int32: 42 '
943            'optional_int32: 67')
944    six.assertRaisesRegex(self,
945        text_format.ParseError,
946        ('1:36 : Message type "protobuf_unittest.TestAllTypes" should not '
947         'have multiple "optional_int32" fields.'),
948        text_format.Parse, text, message)
949
950  def testParseGroupNotClosed(self):
951    message = unittest_pb2.TestAllTypes()
952    text = 'RepeatedGroup: <'
953    six.assertRaisesRegex(self,
954        text_format.ParseError, '1:16 : Expected ">".',
955        text_format.Parse, text, message)
956    text = 'RepeatedGroup: {'
957    six.assertRaisesRegex(self,
958        text_format.ParseError, '1:16 : Expected "}".',
959        text_format.Parse, text, message)
960
961  def testParseEmptyGroup(self):
962    message = unittest_pb2.TestAllTypes()
963    text = 'OptionalGroup: {}'
964    text_format.Parse(text, message)
965    self.assertTrue(message.HasField('optionalgroup'))
966
967    message.Clear()
968
969    message = unittest_pb2.TestAllTypes()
970    text = 'OptionalGroup: <>'
971    text_format.Parse(text, message)
972    self.assertTrue(message.HasField('optionalgroup'))
973
974  # Maps aren't really proto2-only, but our test schema only has maps for
975  # proto2.
976  def testParseMap(self):
977    text = ('map_int32_int32 {\n'
978            '  key: -123\n'
979            '  value: -456\n'
980            '}\n'
981            'map_int64_int64 {\n'
982            '  key: -8589934592\n'
983            '  value: -17179869184\n'
984            '}\n'
985            'map_uint32_uint32 {\n'
986            '  key: 123\n'
987            '  value: 456\n'
988            '}\n'
989            'map_uint64_uint64 {\n'
990            '  key: 8589934592\n'
991            '  value: 17179869184\n'
992            '}\n'
993            'map_string_string {\n'
994            '  key: "abc"\n'
995            '  value: "123"\n'
996            '}\n'
997            'map_int32_foreign_message {\n'
998            '  key: 111\n'
999            '  value {\n'
1000            '    c: 5\n'
1001            '  }\n'
1002            '}\n')
1003    message = map_unittest_pb2.TestMap()
1004    text_format.Parse(text, message)
1005
1006    self.assertEqual(-456, message.map_int32_int32[-123])
1007    self.assertEqual(-2**34, message.map_int64_int64[-2**33])
1008    self.assertEqual(456, message.map_uint32_uint32[123])
1009    self.assertEqual(2**34, message.map_uint64_uint64[2**33])
1010    self.assertEqual("123", message.map_string_string["abc"])
1011    self.assertEqual(5, message.map_int32_foreign_message[111].c)
1012
1013
1014class TokenizerTest(unittest.TestCase):
1015
1016  def testSimpleTokenCases(self):
1017    text = ('identifier1:"string1"\n     \n\n'
1018            'identifier2 : \n \n123  \n  identifier3 :\'string\'\n'
1019            'identifiER_4 : 1.1e+2 ID5:-0.23 ID6:\'aaaa\\\'bbbb\'\n'
1020            'ID7 : "aa\\"bb"\n\n\n\n ID8: {A:inf B:-inf C:true D:false}\n'
1021            'ID9: 22 ID10: -111111111111111111 ID11: -22\n'
1022            'ID12: 2222222222222222222 ID13: 1.23456f ID14: 1.2e+2f '
1023            'false_bool:  0 true_BOOL:t \n true_bool1:  1 false_BOOL1:f ')
1024    tokenizer = text_format._Tokenizer(text.splitlines())
1025    methods = [(tokenizer.ConsumeIdentifier, 'identifier1'),
1026               ':',
1027               (tokenizer.ConsumeString, 'string1'),
1028               (tokenizer.ConsumeIdentifier, 'identifier2'),
1029               ':',
1030               (tokenizer.ConsumeInt32, 123),
1031               (tokenizer.ConsumeIdentifier, 'identifier3'),
1032               ':',
1033               (tokenizer.ConsumeString, 'string'),
1034               (tokenizer.ConsumeIdentifier, 'identifiER_4'),
1035               ':',
1036               (tokenizer.ConsumeFloat, 1.1e+2),
1037               (tokenizer.ConsumeIdentifier, 'ID5'),
1038               ':',
1039               (tokenizer.ConsumeFloat, -0.23),
1040               (tokenizer.ConsumeIdentifier, 'ID6'),
1041               ':',
1042               (tokenizer.ConsumeString, 'aaaa\'bbbb'),
1043               (tokenizer.ConsumeIdentifier, 'ID7'),
1044               ':',
1045               (tokenizer.ConsumeString, 'aa\"bb'),
1046               (tokenizer.ConsumeIdentifier, 'ID8'),
1047               ':',
1048               '{',
1049               (tokenizer.ConsumeIdentifier, 'A'),
1050               ':',
1051               (tokenizer.ConsumeFloat, float('inf')),
1052               (tokenizer.ConsumeIdentifier, 'B'),
1053               ':',
1054               (tokenizer.ConsumeFloat, -float('inf')),
1055               (tokenizer.ConsumeIdentifier, 'C'),
1056               ':',
1057               (tokenizer.ConsumeBool, True),
1058               (tokenizer.ConsumeIdentifier, 'D'),
1059               ':',
1060               (tokenizer.ConsumeBool, False),
1061               '}',
1062               (tokenizer.ConsumeIdentifier, 'ID9'),
1063               ':',
1064               (tokenizer.ConsumeUint32, 22),
1065               (tokenizer.ConsumeIdentifier, 'ID10'),
1066               ':',
1067               (tokenizer.ConsumeInt64, -111111111111111111),
1068               (tokenizer.ConsumeIdentifier, 'ID11'),
1069               ':',
1070               (tokenizer.ConsumeInt32, -22),
1071               (tokenizer.ConsumeIdentifier, 'ID12'),
1072               ':',
1073               (tokenizer.ConsumeUint64, 2222222222222222222),
1074               (tokenizer.ConsumeIdentifier, 'ID13'),
1075               ':',
1076               (tokenizer.ConsumeFloat, 1.23456),
1077               (tokenizer.ConsumeIdentifier, 'ID14'),
1078               ':',
1079               (tokenizer.ConsumeFloat, 1.2e+2),
1080               (tokenizer.ConsumeIdentifier, 'false_bool'),
1081               ':',
1082               (tokenizer.ConsumeBool, False),
1083               (tokenizer.ConsumeIdentifier, 'true_BOOL'),
1084               ':',
1085               (tokenizer.ConsumeBool, True),
1086               (tokenizer.ConsumeIdentifier, 'true_bool1'),
1087               ':',
1088               (tokenizer.ConsumeBool, True),
1089               (tokenizer.ConsumeIdentifier, 'false_BOOL1'),
1090               ':',
1091               (tokenizer.ConsumeBool, False)]
1092
1093    i = 0
1094    while not tokenizer.AtEnd():
1095      m = methods[i]
1096      if type(m) == str:
1097        token = tokenizer.token
1098        self.assertEqual(token, m)
1099        tokenizer.NextToken()
1100      else:
1101        self.assertEqual(m[1], m[0]())
1102      i += 1
1103
1104  def testConsumeIntegers(self):
1105    # This test only tests the failures in the integer parsing methods as well
1106    # as the '0' special cases.
1107    int64_max = (1 << 63) - 1
1108    uint32_max = (1 << 32) - 1
1109    text = '-1 %d %d' % (uint32_max + 1, int64_max + 1)
1110    tokenizer = text_format._Tokenizer(text.splitlines())
1111    self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint32)
1112    self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint64)
1113    self.assertEqual(-1, tokenizer.ConsumeInt32())
1114
1115    self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint32)
1116    self.assertRaises(text_format.ParseError, tokenizer.ConsumeInt32)
1117    self.assertEqual(uint32_max + 1, tokenizer.ConsumeInt64())
1118
1119    self.assertRaises(text_format.ParseError, tokenizer.ConsumeInt64)
1120    self.assertEqual(int64_max + 1, tokenizer.ConsumeUint64())
1121    self.assertTrue(tokenizer.AtEnd())
1122
1123    text = '-0 -0 0 0'
1124    tokenizer = text_format._Tokenizer(text.splitlines())
1125    self.assertEqual(0, tokenizer.ConsumeUint32())
1126    self.assertEqual(0, tokenizer.ConsumeUint64())
1127    self.assertEqual(0, tokenizer.ConsumeUint32())
1128    self.assertEqual(0, tokenizer.ConsumeUint64())
1129    self.assertTrue(tokenizer.AtEnd())
1130
1131  def testConsumeByteString(self):
1132    text = '"string1\''
1133    tokenizer = text_format._Tokenizer(text.splitlines())
1134    self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
1135
1136    text = 'string1"'
1137    tokenizer = text_format._Tokenizer(text.splitlines())
1138    self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
1139
1140    text = '\n"\\xt"'
1141    tokenizer = text_format._Tokenizer(text.splitlines())
1142    self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
1143
1144    text = '\n"\\"'
1145    tokenizer = text_format._Tokenizer(text.splitlines())
1146    self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
1147
1148    text = '\n"\\x"'
1149    tokenizer = text_format._Tokenizer(text.splitlines())
1150    self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
1151
1152  def testConsumeBool(self):
1153    text = 'not-a-bool'
1154    tokenizer = text_format._Tokenizer(text.splitlines())
1155    self.assertRaises(text_format.ParseError, tokenizer.ConsumeBool)
1156
1157
1158if __name__ == '__main__':
1159  unittest.main()
1160