blob: 0e14556c6f029af7940498231f558cc78db24e2d [file] [log] [blame]
Tamir Duberstein9d9d0b72015-04-11 20:23:45 -07001#! /usr/bin/env python
kenton@google.com26bd9ee2008-11-21 00:06:27 +00002#
temporal40ee5512008-07-10 02:12:20 +00003# Protocol Buffers - Google's data interchange format
kenton@google.com24bf56f2008-09-24 20:31:01 +00004# Copyright 2008 Google Inc. All rights reserved.
Feng Xiaoe4288622014-10-01 16:26:23 -07005# https://developers.google.com/protocol-buffers/
temporal40ee5512008-07-10 02:12:20 +00006#
kenton@google.com24bf56f2008-09-24 20:31:01 +00007# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions are
9# met:
temporal40ee5512008-07-10 02:12:20 +000010#
kenton@google.com24bf56f2008-09-24 20:31:01 +000011# * 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.
temporal40ee5512008-07-10 02:12:20 +000020#
kenton@google.com24bf56f2008-09-24 20:31:01 +000021# 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.
temporal40ee5512008-07-10 02:12:20 +000032
33"""Test for google.protobuf.text_format."""
34
35__author__ = 'kenton@google.com (Kenton Varda)'
36
Jisi Liu46e8ff62015-10-05 11:59:43 -070037
liujisi@google.com33165fe2010-11-02 13:14:58 +000038import re
Tres Seaverf336d4b2015-01-13 14:21:29 -050039import six
Feng Xiaoeee38b02015-08-22 18:25:48 -070040import string
temporal40ee5512008-07-10 02:12:20 +000041
Jisi Liudbea00a2015-10-05 16:08:22 -070042try:
43 import unittest2 as unittest
44except ImportError:
45 import unittest
Jisi Liu885b6122015-02-28 14:51:22 -080046from google.protobuf.internal import _parameterized
Jisi Liuada65562015-02-25 16:39:11 -080047
Bo Yang5db21732015-05-21 14:28:59 -070048from google.protobuf import map_unittest_pb2
Jisi Liuada65562015-02-25 16:39:11 -080049from google.protobuf import unittest_mset_pb2
50from google.protobuf import unittest_pb2
51from google.protobuf import unittest_proto3_arena_pb2
jieluo@google.combde4a322014-08-12 21:10:30 +000052from google.protobuf.internal import api_implementation
temporal40ee5512008-07-10 02:12:20 +000053from google.protobuf.internal import test_util
Feng Xiaoe841bac2015-12-11 17:09:20 -080054from google.protobuf.internal import message_set_extensions_pb2
Jisi Liuada65562015-02-25 16:39:11 -080055from google.protobuf import text_format
temporal40ee5512008-07-10 02:12:20 +000056
Feng Xiaoe841bac2015-12-11 17:09:20 -080057
58# Low-level nuts-n-bolts tests.
59class SimpleTextFormatTests(unittest.TestCase):
60
61 # The members of _QUOTES are formatted into a regexp template that
62 # expects single characters. Therefore it's an error (in addition to being
63 # non-sensical in the first place) to try to specify a "quote mark" that is
64 # more than one character.
65 def TestQuoteMarksAreSingleChars(self):
66 for quote in text_format._QUOTES:
67 self.assertEqual(1, len(quote))
68
69
Jisi Liuada65562015-02-25 16:39:11 -080070# Base class with some common functionality.
Tamir Duberstein9f42f5f2015-01-13 14:47:32 -050071class TextFormatBase(unittest.TestCase):
kenton@google.com80b1d622009-07-29 01:13:20 +000072
kenton@google.com80b1d622009-07-29 01:13:20 +000073 def ReadGolden(self, golden_filename):
jieluo@google.combde4a322014-08-12 21:10:30 +000074 with test_util.GoldenFile(golden_filename) as f:
75 return (f.readlines() if str is bytes else # PY3
76 [golden_line.decode('utf-8') for golden_line in f])
kenton@google.com80b1d622009-07-29 01:13:20 +000077
78 def CompareToGoldenFile(self, text, golden_filename):
79 golden_lines = self.ReadGolden(golden_filename)
jieluo@google.combde4a322014-08-12 21:10:30 +000080 self.assertMultiLineEqual(text, ''.join(golden_lines))
temporal40ee5512008-07-10 02:12:20 +000081
82 def CompareToGoldenText(self, text, golden_text):
Dan O'Reilly2621c8a2015-08-14 22:54:53 -040083 self.assertEqual(text, golden_text)
temporal40ee5512008-07-10 02:12:20 +000084
Jisi Liuada65562015-02-25 16:39:11 -080085 def RemoveRedundantZeros(self, text):
86 # Some platforms print 1e+5 as 1e+005. This is fine, but we need to remove
87 # these zeros in order to match the golden file.
88 text = text.replace('e+0','e+').replace('e+0','e+') \
89 .replace('e-0','e-').replace('e-0','e-')
90 # Floating point fields are printed with .0 suffix even if they are
91 # actualy integer numbers.
92 text = re.compile('\.0$', re.MULTILINE).sub('', text)
93 return text
jieluo@google.combde4a322014-08-12 21:10:30 +000094
temporal40ee5512008-07-10 02:12:20 +000095
Josh Haberman0b70a432015-02-25 20:17:32 -080096@_parameterized.Parameters(
Jisi Liuada65562015-02-25 16:39:11 -080097 (unittest_pb2),
98 (unittest_proto3_arena_pb2))
99class TextFormatTest(TextFormatBase):
jieluo@google.combde4a322014-08-12 21:10:30 +0000100
Jisi Liuada65562015-02-25 16:39:11 -0800101 def testPrintExotic(self, message_module):
102 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000103 message.repeated_int64.append(-9223372036854775808)
104 message.repeated_uint64.append(18446744073709551615)
105 message.repeated_double.append(123.456)
106 message.repeated_double.append(1.23e22)
107 message.repeated_double.append(1.23e-18)
108 message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
109 message.repeated_string.append(u'\u00fc\ua71f')
temporalf2063512008-07-23 01:19:07 +0000110 self.CompareToGoldenText(
jieluo@google.combde4a322014-08-12 21:10:30 +0000111 self.RemoveRedundantZeros(text_format.MessageToString(message)),
112 'repeated_int64: -9223372036854775808\n'
113 'repeated_uint64: 18446744073709551615\n'
114 'repeated_double: 123.456\n'
115 'repeated_double: 1.23e+22\n'
116 'repeated_double: 1.23e-18\n'
117 'repeated_string:'
118 ' "\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
119 'repeated_string: "\\303\\274\\352\\234\\237"\n')
120
Jisi Liuada65562015-02-25 16:39:11 -0800121 def testPrintExoticUnicodeSubclass(self, message_module):
Dan O'Reilly3d5aa6a2015-08-14 16:12:34 -0400122 class UnicodeSub(six.text_type):
jieluo@google.combde4a322014-08-12 21:10:30 +0000123 pass
Jisi Liuada65562015-02-25 16:39:11 -0800124 message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000125 message.repeated_string.append(UnicodeSub(u'\u00fc\ua71f'))
126 self.CompareToGoldenText(
127 text_format.MessageToString(message),
128 'repeated_string: "\\303\\274\\352\\234\\237"\n')
liujisi@google.com33165fe2010-11-02 13:14:58 +0000129
Jisi Liuada65562015-02-25 16:39:11 -0800130 def testPrintNestedMessageAsOneLine(self, message_module):
131 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000132 msg = message.repeated_nested_message.add()
jieluo@google.combde4a322014-08-12 21:10:30 +0000133 msg.bb = 42
liujisi@google.com33165fe2010-11-02 13:14:58 +0000134 self.CompareToGoldenText(
135 text_format.MessageToString(message, as_one_line=True),
136 'repeated_nested_message { bb: 42 }')
137
Jisi Liuada65562015-02-25 16:39:11 -0800138 def testPrintRepeatedFieldsAsOneLine(self, message_module):
139 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000140 message.repeated_int32.append(1)
141 message.repeated_int32.append(1)
142 message.repeated_int32.append(3)
Jisi Liuada65562015-02-25 16:39:11 -0800143 message.repeated_string.append('Google')
144 message.repeated_string.append('Zurich')
liujisi@google.com33165fe2010-11-02 13:14:58 +0000145 self.CompareToGoldenText(
146 text_format.MessageToString(message, as_one_line=True),
147 'repeated_int32: 1 repeated_int32: 1 repeated_int32: 3 '
148 'repeated_string: "Google" repeated_string: "Zurich"')
149
Jisi Liuada65562015-02-25 16:39:11 -0800150 def testPrintNestedNewLineInStringAsOneLine(self, message_module):
151 message = message_module.TestAllTypes()
152 message.optional_string = 'a\nnew\nline'
liujisi@google.com33165fe2010-11-02 13:14:58 +0000153 self.CompareToGoldenText(
154 text_format.MessageToString(message, as_one_line=True),
155 'optional_string: "a\\nnew\\nline"')
156
Jisi Liuada65562015-02-25 16:39:11 -0800157 def testPrintExoticAsOneLine(self, message_module):
158 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000159 message.repeated_int64.append(-9223372036854775808)
160 message.repeated_uint64.append(18446744073709551615)
161 message.repeated_double.append(123.456)
162 message.repeated_double.append(1.23e22)
163 message.repeated_double.append(1.23e-18)
164 message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
165 message.repeated_string.append(u'\u00fc\ua71f')
166 self.CompareToGoldenText(
jieluo@google.combde4a322014-08-12 21:10:30 +0000167 self.RemoveRedundantZeros(
168 text_format.MessageToString(message, as_one_line=True)),
169 'repeated_int64: -9223372036854775808'
170 ' repeated_uint64: 18446744073709551615'
171 ' repeated_double: 123.456'
172 ' repeated_double: 1.23e+22'
173 ' repeated_double: 1.23e-18'
174 ' repeated_string: '
175 '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""'
176 ' repeated_string: "\\303\\274\\352\\234\\237"')
liujisi@google.com33165fe2010-11-02 13:14:58 +0000177
Jisi Liuada65562015-02-25 16:39:11 -0800178 def testRoundTripExoticAsOneLine(self, message_module):
179 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000180 message.repeated_int64.append(-9223372036854775808)
181 message.repeated_uint64.append(18446744073709551615)
182 message.repeated_double.append(123.456)
183 message.repeated_double.append(1.23e22)
184 message.repeated_double.append(1.23e-18)
185 message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
186 message.repeated_string.append(u'\u00fc\ua71f')
187
liujisi@google.com6f2c3b82010-12-09 08:59:21 +0000188 # Test as_utf8 = False.
189 wire_text = text_format.MessageToString(
190 message, as_one_line=True, as_utf8=False)
Jisi Liuada65562015-02-25 16:39:11 -0800191 parsed_message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000192 r = text_format.Parse(wire_text, parsed_message)
193 self.assertIs(r, parsed_message)
Tres Seavera2abc942015-01-13 15:47:55 -0500194 self.assertEqual(message, parsed_message)
liujisi@google.com6f2c3b82010-12-09 08:59:21 +0000195
196 # Test as_utf8 = True.
197 wire_text = text_format.MessageToString(
198 message, as_one_line=True, as_utf8=True)
Jisi Liuada65562015-02-25 16:39:11 -0800199 parsed_message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000200 r = text_format.Parse(wire_text, parsed_message)
201 self.assertIs(r, parsed_message)
Tres Seavera2abc942015-01-13 15:47:55 -0500202 self.assertEqual(message, parsed_message,
jieluo@google.combde4a322014-08-12 21:10:30 +0000203 '\n%s != %s' % (message, parsed_message))
liujisi@google.com33165fe2010-11-02 13:14:58 +0000204
Jisi Liuada65562015-02-25 16:39:11 -0800205 def testPrintRawUtf8String(self, message_module):
206 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000207 message.repeated_string.append(u'\u00fc\ua71f')
jieluo@google.combde4a322014-08-12 21:10:30 +0000208 text = text_format.MessageToString(message, as_utf8=True)
liujisi@google.com6f2c3b82010-12-09 08:59:21 +0000209 self.CompareToGoldenText(text, 'repeated_string: "\303\274\352\234\237"\n')
Jisi Liuada65562015-02-25 16:39:11 -0800210 parsed_message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000211 text_format.Parse(text, parsed_message)
Tres Seavera2abc942015-01-13 15:47:55 -0500212 self.assertEqual(message, parsed_message,
jieluo@google.combde4a322014-08-12 21:10:30 +0000213 '\n%s != %s' % (message, parsed_message))
214
Jisi Liuada65562015-02-25 16:39:11 -0800215 def testPrintFloatFormat(self, message_module):
jieluo@google.combde4a322014-08-12 21:10:30 +0000216 # Check that float_format argument is passed to sub-message formatting.
Jisi Liuada65562015-02-25 16:39:11 -0800217 message = message_module.NestedTestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000218 # We use 1.25 as it is a round number in binary. The proto 32-bit float
219 # will not gain additional imprecise digits as a 64-bit Python float and
220 # show up in its str. 32-bit 1.2 is noisy when extended to 64-bit:
221 # >>> struct.unpack('f', struct.pack('f', 1.2))[0]
222 # 1.2000000476837158
223 # >>> struct.unpack('f', struct.pack('f', 1.25))[0]
224 # 1.25
225 message.payload.optional_float = 1.25
226 # Check rounding at 15 significant digits
227 message.payload.optional_double = -.000003456789012345678
228 # Check no decimal point.
229 message.payload.repeated_float.append(-5642)
230 # Check no trailing zeros.
231 message.payload.repeated_double.append(.000078900)
232 formatted_fields = ['optional_float: 1.25',
233 'optional_double: -3.45678901234568e-6',
234 'repeated_float: -5642',
235 'repeated_double: 7.89e-5']
236 text_message = text_format.MessageToString(message, float_format='.15g')
237 self.CompareToGoldenText(
238 self.RemoveRedundantZeros(text_message),
Dan O'Reilly2621c8a2015-08-14 22:54:53 -0400239 'payload {{\n {0}\n {1}\n {2}\n {3}\n}}\n'.format(*formatted_fields))
jieluo@google.combde4a322014-08-12 21:10:30 +0000240 # as_one_line=True is a separate code branch where float_format is passed.
241 text_message = text_format.MessageToString(message, as_one_line=True,
242 float_format='.15g')
243 self.CompareToGoldenText(
244 self.RemoveRedundantZeros(text_message),
Dan O'Reilly2621c8a2015-08-14 22:54:53 -0400245 'payload {{ {0} {1} {2} {3} }}'.format(*formatted_fields))
temporal40ee5512008-07-10 02:12:20 +0000246
Jisi Liuada65562015-02-25 16:39:11 -0800247 def testMessageToString(self, message_module):
248 message = message_module.ForeignMessage()
temporal40ee5512008-07-10 02:12:20 +0000249 message.c = 123
250 self.assertEqual('c: 123\n', str(message))
251
Jisi Liuada65562015-02-25 16:39:11 -0800252 def testParseAllFields(self, message_module):
253 message = message_module.TestAllTypes()
kenton@google.com80b1d622009-07-29 01:13:20 +0000254 test_util.SetAllFields(message)
255 ascii_text = text_format.MessageToString(message)
256
Jisi Liuada65562015-02-25 16:39:11 -0800257 parsed_message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000258 text_format.Parse(ascii_text, parsed_message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000259 self.assertEqual(message, parsed_message)
Jisi Liuada65562015-02-25 16:39:11 -0800260 if message_module is unittest_pb2:
261 test_util.ExpectAllFieldsSet(self, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000262
Jisi Liuada65562015-02-25 16:39:11 -0800263 def testParseExotic(self, message_module):
264 message = message_module.TestAllTypes()
kenton@google.com80b1d622009-07-29 01:13:20 +0000265 text = ('repeated_int64: -9223372036854775808\n'
266 'repeated_uint64: 18446744073709551615\n'
267 'repeated_double: 123.456\n'
268 'repeated_double: 1.23e+22\n'
269 'repeated_double: 1.23e-18\n'
270 'repeated_string: \n'
liujisi@google.com33165fe2010-11-02 13:14:58 +0000271 '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
272 'repeated_string: "foo" \'corge\' "grault"\n'
273 'repeated_string: "\\303\\274\\352\\234\\237"\n'
274 'repeated_string: "\\xc3\\xbc"\n'
275 'repeated_string: "\xc3\xbc"\n')
jieluo@google.combde4a322014-08-12 21:10:30 +0000276 text_format.Parse(text, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000277
278 self.assertEqual(-9223372036854775808, message.repeated_int64[0])
279 self.assertEqual(18446744073709551615, message.repeated_uint64[0])
280 self.assertEqual(123.456, message.repeated_double[0])
281 self.assertEqual(1.23e22, message.repeated_double[1])
282 self.assertEqual(1.23e-18, message.repeated_double[2])
283 self.assertEqual(
liujisi@google.com33165fe2010-11-02 13:14:58 +0000284 '\000\001\a\b\f\n\r\t\v\\\'"', message.repeated_string[0])
kenton@google.comeef5f832009-12-23 01:32:45 +0000285 self.assertEqual('foocorgegrault', message.repeated_string[1])
liujisi@google.com33165fe2010-11-02 13:14:58 +0000286 self.assertEqual(u'\u00fc\ua71f', message.repeated_string[2])
287 self.assertEqual(u'\u00fc', message.repeated_string[3])
288
Jisi Liuada65562015-02-25 16:39:11 -0800289 def testParseTrailingCommas(self, message_module):
290 message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000291 text = ('repeated_int64: 100;\n'
292 'repeated_int64: 200;\n'
293 'repeated_int64: 300,\n'
294 'repeated_string: "one",\n'
295 'repeated_string: "two";\n')
296 text_format.Parse(text, message)
297
298 self.assertEqual(100, message.repeated_int64[0])
299 self.assertEqual(200, message.repeated_int64[1])
300 self.assertEqual(300, message.repeated_int64[2])
301 self.assertEqual(u'one', message.repeated_string[0])
302 self.assertEqual(u'two', message.repeated_string[1])
303
Feng Xiaoe841bac2015-12-11 17:09:20 -0800304 def testParseRepeatedScalarShortFormat(self, message_module):
305 message = message_module.TestAllTypes()
306 text = ('repeated_int64: [100, 200];\n'
307 'repeated_int64: 300,\n'
308 'repeated_string: ["one", "two"];\n')
309 text_format.Parse(text, message)
310
311 self.assertEqual(100, message.repeated_int64[0])
312 self.assertEqual(200, message.repeated_int64[1])
313 self.assertEqual(300, message.repeated_int64[2])
314 self.assertEqual(u'one', message.repeated_string[0])
315 self.assertEqual(u'two', message.repeated_string[1])
316
Jisi Liuada65562015-02-25 16:39:11 -0800317 def testParseEmptyText(self, message_module):
318 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000319 text = ''
jieluo@google.combde4a322014-08-12 21:10:30 +0000320 text_format.Parse(text, message)
Dan O'Reillye47cdd52015-08-12 23:57:46 -0400321 self.assertEqual(message_module.TestAllTypes(), message)
liujisi@google.com33165fe2010-11-02 13:14:58 +0000322
Jisi Liuada65562015-02-25 16:39:11 -0800323 def testParseInvalidUtf8(self, message_module):
324 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000325 text = 'repeated_string: "\\xc3\\xc3"'
jieluo@google.combde4a322014-08-12 21:10:30 +0000326 self.assertRaises(text_format.ParseError, text_format.Parse, text, message)
liujisi@google.com33165fe2010-11-02 13:14:58 +0000327
Jisi Liuada65562015-02-25 16:39:11 -0800328 def testParseSingleWord(self, message_module):
329 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000330 text = 'foo'
Feng Xiaoe841bac2015-12-11 17:09:20 -0800331 six.assertRaisesRegex(self,
liujisi@google.com33165fe2010-11-02 13:14:58 +0000332 text_format.ParseError,
Jisi Liuada65562015-02-25 16:39:11 -0800333 (r'1:1 : Message type "\w+.TestAllTypes" has no field named '
334 r'"foo".'),
jieluo@google.combde4a322014-08-12 21:10:30 +0000335 text_format.Parse, text, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000336
Jisi Liuada65562015-02-25 16:39:11 -0800337 def testParseUnknownField(self, message_module):
338 message = message_module.TestAllTypes()
kenton@google.com80b1d622009-07-29 01:13:20 +0000339 text = 'unknown_field: 8\n'
Feng Xiaoe841bac2015-12-11 17:09:20 -0800340 six.assertRaisesRegex(self,
kenton@google.com80b1d622009-07-29 01:13:20 +0000341 text_format.ParseError,
Jisi Liuada65562015-02-25 16:39:11 -0800342 (r'1:1 : Message type "\w+.TestAllTypes" has no field named '
343 r'"unknown_field".'),
jieluo@google.combde4a322014-08-12 21:10:30 +0000344 text_format.Parse, text, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000345
Jisi Liuada65562015-02-25 16:39:11 -0800346 def testParseBadEnumValue(self, message_module):
347 message = message_module.TestAllTypes()
kenton@google.com80b1d622009-07-29 01:13:20 +0000348 text = 'optional_nested_enum: BARR'
Feng Xiaoe841bac2015-12-11 17:09:20 -0800349 six.assertRaisesRegex(self,
kenton@google.com80b1d622009-07-29 01:13:20 +0000350 text_format.ParseError,
Jisi Liuada65562015-02-25 16:39:11 -0800351 (r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
352 r'has no value named BARR.'),
jieluo@google.combde4a322014-08-12 21:10:30 +0000353 text_format.Parse, text, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000354
Jisi Liuada65562015-02-25 16:39:11 -0800355 message = message_module.TestAllTypes()
kenton@google.com80b1d622009-07-29 01:13:20 +0000356 text = 'optional_nested_enum: 100'
Feng Xiaoe841bac2015-12-11 17:09:20 -0800357 six.assertRaisesRegex(self,
kenton@google.com80b1d622009-07-29 01:13:20 +0000358 text_format.ParseError,
Jisi Liuada65562015-02-25 16:39:11 -0800359 (r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
360 r'has no value with number 100.'),
jieluo@google.combde4a322014-08-12 21:10:30 +0000361 text_format.Parse, text, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000362
Jisi Liuada65562015-02-25 16:39:11 -0800363 def testParseBadIntValue(self, message_module):
364 message = message_module.TestAllTypes()
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000365 text = 'optional_int32: bork'
Feng Xiaoe841bac2015-12-11 17:09:20 -0800366 six.assertRaisesRegex(self,
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000367 text_format.ParseError,
368 ('1:17 : Couldn\'t parse integer: bork'),
jieluo@google.combde4a322014-08-12 21:10:30 +0000369 text_format.Parse, text, message)
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000370
Jisi Liuada65562015-02-25 16:39:11 -0800371 def testParseStringFieldUnescape(self, message_module):
372 message = message_module.TestAllTypes()
xiaofeng@google.com7f372552013-02-25 10:39:39 +0000373 text = r'''repeated_string: "\xf\x62"
374 repeated_string: "\\xf\\x62"
375 repeated_string: "\\\xf\\\x62"
376 repeated_string: "\\\\xf\\\\x62"
377 repeated_string: "\\\\\xf\\\\\x62"
378 repeated_string: "\x5cx20"'''
jieluo@google.combde4a322014-08-12 21:10:30 +0000379 text_format.Parse(text, message)
xiaofeng@google.com7f372552013-02-25 10:39:39 +0000380
381 SLASH = '\\'
382 self.assertEqual('\x0fb', message.repeated_string[0])
383 self.assertEqual(SLASH + 'xf' + SLASH + 'x62', message.repeated_string[1])
384 self.assertEqual(SLASH + '\x0f' + SLASH + 'b', message.repeated_string[2])
385 self.assertEqual(SLASH + SLASH + 'xf' + SLASH + SLASH + 'x62',
386 message.repeated_string[3])
387 self.assertEqual(SLASH + SLASH + '\x0f' + SLASH + SLASH + 'b',
388 message.repeated_string[4])
389 self.assertEqual(SLASH + 'x20', message.repeated_string[5])
390
Jisi Liuada65562015-02-25 16:39:11 -0800391 def testMergeDuplicateScalars(self, message_module):
392 message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000393 text = ('optional_int32: 42 '
394 'optional_int32: 67')
395 r = text_format.Merge(text, message)
396 self.assertIs(r, message)
397 self.assertEqual(67, message.optional_int32)
kenton@google.com80b1d622009-07-29 01:13:20 +0000398
Jisi Liuada65562015-02-25 16:39:11 -0800399 def testMergeDuplicateNestedMessageScalars(self, message_module):
400 message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000401 text = ('optional_nested_message { bb: 1 } '
402 'optional_nested_message { bb: 2 }')
403 r = text_format.Merge(text, message)
404 self.assertTrue(r is message)
405 self.assertEqual(2, message.optional_nested_message.bb)
406
Jisi Liuada65562015-02-25 16:39:11 -0800407 def testParseOneof(self, message_module):
408 m = message_module.TestAllTypes()
409 m.oneof_uint32 = 11
410 m2 = message_module.TestAllTypes()
411 text_format.Parse(text_format.MessageToString(m), m2)
412 self.assertEqual('oneof_uint32', m2.WhichOneof('oneof_field'))
413
414
415# These are tests that aren't fundamentally specific to proto2, but are at
416# the moment because of differences between the proto2 and proto3 test schemas.
417# Ideally the schemas would be made more similar so these tests could pass.
418class OnlyWorksWithProto2RightNowTests(TextFormatBase):
419
Jisi Liu46e8ff62015-10-05 11:59:43 -0700420 def testPrintAllFieldsPointy(self):
Bo Yang5db21732015-05-21 14:28:59 -0700421 message = unittest_pb2.TestAllTypes()
422 test_util.SetAllFields(message)
423 self.CompareToGoldenFile(
424 self.RemoveRedundantZeros(
425 text_format.MessageToString(message, pointy_brackets=True)),
426 'text_format_unittest_data_pointy_oneof.txt')
427
Jisi Liuada65562015-02-25 16:39:11 -0800428 def testParseGolden(self):
429 golden_text = '\n'.join(self.ReadGolden('text_format_unittest_data.txt'))
430 parsed_message = unittest_pb2.TestAllTypes()
431 r = text_format.Parse(golden_text, parsed_message)
432 self.assertIs(r, parsed_message)
433
jieluo@google.combde4a322014-08-12 21:10:30 +0000434 message = unittest_pb2.TestAllTypes()
Jisi Liuada65562015-02-25 16:39:11 -0800435 test_util.SetAllFields(message)
Dan O'Reillye47cdd52015-08-12 23:57:46 -0400436 self.assertEqual(message, parsed_message)
Jisi Liuada65562015-02-25 16:39:11 -0800437
438 def testPrintAllFields(self):
439 message = unittest_pb2.TestAllTypes()
440 test_util.SetAllFields(message)
441 self.CompareToGoldenFile(
442 self.RemoveRedundantZeros(text_format.MessageToString(message)),
443 'text_format_unittest_data_oneof_implemented.txt')
444
445 def testPrintAllFieldsPointy(self):
446 message = unittest_pb2.TestAllTypes()
447 test_util.SetAllFields(message)
448 self.CompareToGoldenFile(
449 self.RemoveRedundantZeros(
450 text_format.MessageToString(message, pointy_brackets=True)),
451 'text_format_unittest_data_pointy_oneof.txt')
452
453 def testPrintInIndexOrder(self):
454 message = unittest_pb2.TestFieldOrderings()
455 message.my_string = '115'
456 message.my_int = 101
457 message.my_float = 111
458 message.optional_nested_message.oo = 0
459 message.optional_nested_message.bb = 1
460 self.CompareToGoldenText(
461 self.RemoveRedundantZeros(text_format.MessageToString(
462 message, use_index_order=True)),
463 'my_string: \"115\"\nmy_int: 101\nmy_float: 111\n'
464 'optional_nested_message {\n oo: 0\n bb: 1\n}\n')
465 self.CompareToGoldenText(
466 self.RemoveRedundantZeros(text_format.MessageToString(
467 message)),
468 'my_int: 101\nmy_string: \"115\"\nmy_float: 111\n'
469 'optional_nested_message {\n bb: 1\n oo: 0\n}\n')
470
471 def testMergeLinesGolden(self):
472 opened = self.ReadGolden('text_format_unittest_data.txt')
473 parsed_message = unittest_pb2.TestAllTypes()
474 r = text_format.MergeLines(opened, parsed_message)
475 self.assertIs(r, parsed_message)
476
477 message = unittest_pb2.TestAllTypes()
478 test_util.SetAllFields(message)
479 self.assertEqual(message, parsed_message)
480
481 def testParseLinesGolden(self):
482 opened = self.ReadGolden('text_format_unittest_data.txt')
483 parsed_message = unittest_pb2.TestAllTypes()
484 r = text_format.ParseLines(opened, parsed_message)
485 self.assertIs(r, parsed_message)
486
487 message = unittest_pb2.TestAllTypes()
488 test_util.SetAllFields(message)
Dan O'Reillye47cdd52015-08-12 23:57:46 -0400489 self.assertEqual(message, parsed_message)
Jisi Liuada65562015-02-25 16:39:11 -0800490
Bo Yang5db21732015-05-21 14:28:59 -0700491 def testPrintMap(self):
492 message = map_unittest_pb2.TestMap()
Jisi Liuada65562015-02-25 16:39:11 -0800493
Bo Yang5db21732015-05-21 14:28:59 -0700494 message.map_int32_int32[-123] = -456
495 message.map_int64_int64[-2**33] = -2**34
496 message.map_uint32_uint32[123] = 456
497 message.map_uint64_uint64[2**33] = 2**34
498 message.map_string_string["abc"] = "123"
499 message.map_int32_foreign_message[111].c = 5
500
501 # Maps are serialized to text format using their underlying repeated
502 # representation.
503 self.CompareToGoldenText(
504 text_format.MessageToString(message),
505 'map_int32_int32 {\n'
506 ' key: -123\n'
507 ' value: -456\n'
508 '}\n'
509 'map_int64_int64 {\n'
510 ' key: -8589934592\n'
511 ' value: -17179869184\n'
512 '}\n'
513 'map_uint32_uint32 {\n'
514 ' key: 123\n'
515 ' value: 456\n'
516 '}\n'
517 'map_uint64_uint64 {\n'
518 ' key: 8589934592\n'
519 ' value: 17179869184\n'
520 '}\n'
521 'map_string_string {\n'
522 ' key: "abc"\n'
523 ' value: "123"\n'
524 '}\n'
525 'map_int32_foreign_message {\n'
526 ' key: 111\n'
527 ' value {\n'
528 ' c: 5\n'
529 ' }\n'
530 '}\n')
531
Feng Xiaoeee38b02015-08-22 18:25:48 -0700532 def testMapOrderEnforcement(self):
533 message = map_unittest_pb2.TestMap()
534 for letter in string.ascii_uppercase[13:26]:
535 message.map_string_string[letter] = 'dummy'
536 for letter in reversed(string.ascii_uppercase[0:13]):
537 message.map_string_string[letter] = 'dummy'
538 golden = ''.join((
539 'map_string_string {\n key: "%c"\n value: "dummy"\n}\n' % (letter,)
540 for letter in string.ascii_uppercase))
541 self.CompareToGoldenText(text_format.MessageToString(message), golden)
542
543 def testMapOrderSemantics(self):
544 golden_lines = self.ReadGolden('map_test_data.txt')
545 # The C++ implementation emits defaulted-value fields, while the Python
546 # implementation does not. Adjusting for this is awkward, but it is
547 # valuable to test against a common golden file.
548 line_blacklist = (' key: 0\n',
549 ' value: 0\n',
550 ' key: false\n',
551 ' value: false\n')
552 golden_lines = [line for line in golden_lines if line not in line_blacklist]
553
554 message = map_unittest_pb2.TestMap()
555 text_format.ParseLines(golden_lines, message)
556 candidate = text_format.MessageToString(message)
557 # The Python implementation emits "1.0" for the double value that the C++
558 # implementation emits as "1".
559 candidate = candidate.replace('1.0', '1', 2)
560 self.assertMultiLineEqual(candidate, ''.join(golden_lines))
561
Bo Yang5db21732015-05-21 14:28:59 -0700562
563# Tests of proto2-only features (MessageSet, extensions, etc.).
Jisi Liuada65562015-02-25 16:39:11 -0800564class Proto2Tests(TextFormatBase):
565
566 def testPrintMessageSet(self):
567 message = unittest_mset_pb2.TestMessageSetContainer()
568 ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
569 ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
570 message.message_set.Extensions[ext1].i = 23
571 message.message_set.Extensions[ext2].str = 'foo'
572 self.CompareToGoldenText(
573 text_format.MessageToString(message),
574 'message_set {\n'
575 ' [protobuf_unittest.TestMessageSetExtension1] {\n'
576 ' i: 23\n'
577 ' }\n'
578 ' [protobuf_unittest.TestMessageSetExtension2] {\n'
579 ' str: \"foo\"\n'
580 ' }\n'
581 '}\n')
582
Feng Xiaoe841bac2015-12-11 17:09:20 -0800583 message = message_set_extensions_pb2.TestMessageSet()
584 ext = message_set_extensions_pb2.message_set_extension3
585 message.Extensions[ext].text = 'bar'
586 self.CompareToGoldenText(
587 text_format.MessageToString(message),
588 '[google.protobuf.internal.TestMessageSetExtension3] {\n'
589 ' text: \"bar\"\n'
590 '}\n')
591
Jisi Liuada65562015-02-25 16:39:11 -0800592 def testPrintMessageSetAsOneLine(self):
593 message = unittest_mset_pb2.TestMessageSetContainer()
594 ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
595 ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
596 message.message_set.Extensions[ext1].i = 23
597 message.message_set.Extensions[ext2].str = 'foo'
598 self.CompareToGoldenText(
599 text_format.MessageToString(message, as_one_line=True),
600 'message_set {'
601 ' [protobuf_unittest.TestMessageSetExtension1] {'
602 ' i: 23'
603 ' }'
604 ' [protobuf_unittest.TestMessageSetExtension2] {'
605 ' str: \"foo\"'
606 ' }'
607 ' }')
608
609 def testParseMessageSet(self):
610 message = unittest_pb2.TestAllTypes()
611 text = ('repeated_uint64: 1\n'
612 'repeated_uint64: 2\n')
613 text_format.Parse(text, message)
614 self.assertEqual(1, message.repeated_uint64[0])
615 self.assertEqual(2, message.repeated_uint64[1])
616
617 message = unittest_mset_pb2.TestMessageSetContainer()
618 text = ('message_set {\n'
619 ' [protobuf_unittest.TestMessageSetExtension1] {\n'
620 ' i: 23\n'
621 ' }\n'
622 ' [protobuf_unittest.TestMessageSetExtension2] {\n'
623 ' str: \"foo\"\n'
624 ' }\n'
625 '}\n')
626 text_format.Parse(text, message)
627 ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
628 ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
Dan O'Reillye47cdd52015-08-12 23:57:46 -0400629 self.assertEqual(23, message.message_set.Extensions[ext1].i)
630 self.assertEqual('foo', message.message_set.Extensions[ext2].str)
Jisi Liuada65562015-02-25 16:39:11 -0800631
632 def testPrintAllExtensions(self):
633 message = unittest_pb2.TestAllExtensions()
634 test_util.SetAllExtensions(message)
635 self.CompareToGoldenFile(
636 self.RemoveRedundantZeros(text_format.MessageToString(message)),
637 'text_format_unittest_extensions_data.txt')
638
639 def testPrintAllExtensionsPointy(self):
640 message = unittest_pb2.TestAllExtensions()
641 test_util.SetAllExtensions(message)
642 self.CompareToGoldenFile(
643 self.RemoveRedundantZeros(text_format.MessageToString(
644 message, pointy_brackets=True)),
645 'text_format_unittest_extensions_data_pointy.txt')
646
647 def testParseGoldenExtensions(self):
648 golden_text = '\n'.join(self.ReadGolden(
649 'text_format_unittest_extensions_data.txt'))
650 parsed_message = unittest_pb2.TestAllExtensions()
651 text_format.Parse(golden_text, parsed_message)
652
653 message = unittest_pb2.TestAllExtensions()
654 test_util.SetAllExtensions(message)
Dan O'Reillye47cdd52015-08-12 23:57:46 -0400655 self.assertEqual(message, parsed_message)
Jisi Liuada65562015-02-25 16:39:11 -0800656
657 def testParseAllExtensions(self):
658 message = unittest_pb2.TestAllExtensions()
659 test_util.SetAllExtensions(message)
660 ascii_text = text_format.MessageToString(message)
661
662 parsed_message = unittest_pb2.TestAllExtensions()
663 text_format.Parse(ascii_text, parsed_message)
664 self.assertEqual(message, parsed_message)
665
Feng Xiaoe841bac2015-12-11 17:09:20 -0800666 def testParseAllowedUnknownExtension(self):
667 # Skip over unknown extension correctly.
668 message = unittest_mset_pb2.TestMessageSetContainer()
669 text = ('message_set {\n'
670 ' [unknown_extension] {\n'
671 ' i: 23\n'
672 ' [nested_unknown_ext]: {\n'
673 ' i: 23\n'
674 ' test: "test_string"\n'
675 ' floaty_float: -0.315\n'
676 ' num: -inf\n'
677 ' multiline_str: "abc"\n'
678 ' "def"\n'
679 ' "xyz."\n'
680 ' [nested_unknown_ext]: <\n'
681 ' i: 23\n'
682 ' i: 24\n'
683 ' pointfloat: .3\n'
684 ' test: "test_string"\n'
685 ' floaty_float: -0.315\n'
686 ' num: -inf\n'
687 ' long_string: "test" "test2" \n'
688 ' >\n'
689 ' }\n'
690 ' }\n'
691 ' [unknown_extension]: 5\n'
692 '}\n')
693 text_format.Parse(text, message, allow_unknown_extension=True)
694 golden = 'message_set {\n}\n'
695 self.CompareToGoldenText(text_format.MessageToString(message), golden)
696
697 # Catch parse errors in unknown extension.
698 message = unittest_mset_pb2.TestMessageSetContainer()
699 malformed = ('message_set {\n'
700 ' [unknown_extension] {\n'
701 ' i:\n' # Missing value.
702 ' }\n'
703 '}\n')
704 six.assertRaisesRegex(self,
705 text_format.ParseError,
706 'Invalid field value: }',
707 text_format.Parse, malformed, message,
708 allow_unknown_extension=True)
709
710 message = unittest_mset_pb2.TestMessageSetContainer()
711 malformed = ('message_set {\n'
712 ' [unknown_extension] {\n'
713 ' str: "malformed string\n' # Missing closing quote.
714 ' }\n'
715 '}\n')
716 six.assertRaisesRegex(self,
717 text_format.ParseError,
718 'Invalid field value: "',
719 text_format.Parse, malformed, message,
720 allow_unknown_extension=True)
721
722 message = unittest_mset_pb2.TestMessageSetContainer()
723 malformed = ('message_set {\n'
724 ' [unknown_extension] {\n'
725 ' str: "malformed\n multiline\n string\n'
726 ' }\n'
727 '}\n')
728 six.assertRaisesRegex(self,
729 text_format.ParseError,
730 'Invalid field value: "',
731 text_format.Parse, malformed, message,
732 allow_unknown_extension=True)
733
734 message = unittest_mset_pb2.TestMessageSetContainer()
735 malformed = ('message_set {\n'
736 ' [malformed_extension] <\n'
737 ' i: -5\n'
738 ' \n' # Missing '>' here.
739 '}\n')
740 six.assertRaisesRegex(self,
741 text_format.ParseError,
742 '5:1 : Expected ">".',
743 text_format.Parse, malformed, message,
744 allow_unknown_extension=True)
745
746 # Don't allow unknown fields with allow_unknown_extension=True.
747 message = unittest_mset_pb2.TestMessageSetContainer()
748 malformed = ('message_set {\n'
749 ' unknown_field: true\n'
750 ' \n' # Missing '>' here.
751 '}\n')
752 six.assertRaisesRegex(self,
753 text_format.ParseError,
754 ('2:3 : Message type '
755 '"proto2_wireformat_unittest.TestMessageSet" has no'
756 ' field named "unknown_field".'),
757 text_format.Parse, malformed, message,
758 allow_unknown_extension=True)
759
760 # Parse known extension correcty.
761 message = unittest_mset_pb2.TestMessageSetContainer()
762 text = ('message_set {\n'
763 ' [protobuf_unittest.TestMessageSetExtension1] {\n'
764 ' i: 23\n'
765 ' }\n'
766 ' [protobuf_unittest.TestMessageSetExtension2] {\n'
767 ' str: \"foo\"\n'
768 ' }\n'
769 '}\n')
770 text_format.Parse(text, message, allow_unknown_extension=True)
771 ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
772 ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
773 self.assertEqual(23, message.message_set.Extensions[ext1].i)
774 self.assertEqual('foo', message.message_set.Extensions[ext2].str)
775
Jisi Liuada65562015-02-25 16:39:11 -0800776 def testParseBadExtension(self):
777 message = unittest_pb2.TestAllExtensions()
778 text = '[unknown_extension]: 8\n'
Feng Xiaoe841bac2015-12-11 17:09:20 -0800779 six.assertRaisesRegex(self,
jieluo@google.combde4a322014-08-12 21:10:30 +0000780 text_format.ParseError,
Jisi Liuada65562015-02-25 16:39:11 -0800781 '1:2 : Extension "unknown_extension" not registered.',
782 text_format.Parse, text, message)
783 message = unittest_pb2.TestAllTypes()
Feng Xiaoe841bac2015-12-11 17:09:20 -0800784 six.assertRaisesRegex(self,
Jisi Liuada65562015-02-25 16:39:11 -0800785 text_format.ParseError,
786 ('1:2 : Message type "protobuf_unittest.TestAllTypes" does not have '
787 'extensions.'),
jieluo@google.combde4a322014-08-12 21:10:30 +0000788 text_format.Parse, text, message)
789
Feng Xiao6ef984a2014-11-10 17:34:54 -0800790 def testMergeDuplicateExtensionScalars(self):
jieluo@google.combde4a322014-08-12 21:10:30 +0000791 message = unittest_pb2.TestAllExtensions()
792 text = ('[protobuf_unittest.optional_int32_extension]: 42 '
793 '[protobuf_unittest.optional_int32_extension]: 67')
794 text_format.Merge(text, message)
795 self.assertEqual(
796 67,
797 message.Extensions[unittest_pb2.optional_int32_extension])
798
Feng Xiao6ef984a2014-11-10 17:34:54 -0800799 def testParseDuplicateExtensionScalars(self):
jieluo@google.combde4a322014-08-12 21:10:30 +0000800 message = unittest_pb2.TestAllExtensions()
801 text = ('[protobuf_unittest.optional_int32_extension]: 42 '
802 '[protobuf_unittest.optional_int32_extension]: 67')
Feng Xiaoe841bac2015-12-11 17:09:20 -0800803 six.assertRaisesRegex(self,
jieluo@google.combde4a322014-08-12 21:10:30 +0000804 text_format.ParseError,
805 ('1:96 : Message type "protobuf_unittest.TestAllExtensions" '
806 'should not have multiple '
807 '"protobuf_unittest.optional_int32_extension" extensions.'),
808 text_format.Parse, text, message)
809
Jisi Liuada65562015-02-25 16:39:11 -0800810 def testParseDuplicateNestedMessageScalars(self):
jieluo@google.combde4a322014-08-12 21:10:30 +0000811 message = unittest_pb2.TestAllTypes()
Jisi Liuada65562015-02-25 16:39:11 -0800812 text = ('optional_nested_message { bb: 1 } '
813 'optional_nested_message { bb: 2 }')
Feng Xiaoe841bac2015-12-11 17:09:20 -0800814 six.assertRaisesRegex(self,
Jisi Liuada65562015-02-25 16:39:11 -0800815 text_format.ParseError,
816 ('1:65 : Message type "protobuf_unittest.TestAllTypes.NestedMessage" '
817 'should not have multiple "bb" fields.'),
818 text_format.Parse, text, message)
jieluo@google.combde4a322014-08-12 21:10:30 +0000819
Jisi Liuada65562015-02-25 16:39:11 -0800820 def testParseDuplicateScalars(self):
jieluo@google.combde4a322014-08-12 21:10:30 +0000821 message = unittest_pb2.TestAllTypes()
Jisi Liuada65562015-02-25 16:39:11 -0800822 text = ('optional_int32: 42 '
823 'optional_int32: 67')
Feng Xiaoe841bac2015-12-11 17:09:20 -0800824 six.assertRaisesRegex(self,
Jisi Liuada65562015-02-25 16:39:11 -0800825 text_format.ParseError,
826 ('1:36 : Message type "protobuf_unittest.TestAllTypes" should not '
827 'have multiple "optional_int32" fields.'),
828 text_format.Parse, text, message)
Feng Xiao0971bb02014-10-07 17:44:33 -0700829
Bo Yang5db21732015-05-21 14:28:59 -0700830 def testParseGroupNotClosed(self):
831 message = unittest_pb2.TestAllTypes()
832 text = 'RepeatedGroup: <'
Feng Xiaoe841bac2015-12-11 17:09:20 -0800833 six.assertRaisesRegex(self,
Bo Yang5db21732015-05-21 14:28:59 -0700834 text_format.ParseError, '1:16 : Expected ">".',
835 text_format.Parse, text, message)
836 text = 'RepeatedGroup: {'
Feng Xiaoe841bac2015-12-11 17:09:20 -0800837 six.assertRaisesRegex(self,
Bo Yang5db21732015-05-21 14:28:59 -0700838 text_format.ParseError, '1:16 : Expected "}".',
839 text_format.Parse, text, message)
840
841 def testParseEmptyGroup(self):
842 message = unittest_pb2.TestAllTypes()
843 text = 'OptionalGroup: {}'
844 text_format.Parse(text, message)
845 self.assertTrue(message.HasField('optionalgroup'))
846
847 message.Clear()
848
849 message = unittest_pb2.TestAllTypes()
850 text = 'OptionalGroup: <>'
851 text_format.Parse(text, message)
852 self.assertTrue(message.HasField('optionalgroup'))
853
854 # Maps aren't really proto2-only, but our test schema only has maps for
855 # proto2.
856 def testParseMap(self):
857 text = ('map_int32_int32 {\n'
858 ' key: -123\n'
859 ' value: -456\n'
860 '}\n'
861 'map_int64_int64 {\n'
862 ' key: -8589934592\n'
863 ' value: -17179869184\n'
864 '}\n'
865 'map_uint32_uint32 {\n'
866 ' key: 123\n'
867 ' value: 456\n'
868 '}\n'
869 'map_uint64_uint64 {\n'
870 ' key: 8589934592\n'
871 ' value: 17179869184\n'
872 '}\n'
873 'map_string_string {\n'
874 ' key: "abc"\n'
875 ' value: "123"\n'
876 '}\n'
877 'map_int32_foreign_message {\n'
878 ' key: 111\n'
879 ' value {\n'
880 ' c: 5\n'
881 ' }\n'
882 '}\n')
883 message = map_unittest_pb2.TestMap()
884 text_format.Parse(text, message)
885
886 self.assertEqual(-456, message.map_int32_int32[-123])
887 self.assertEqual(-2**34, message.map_int64_int64[-2**33])
888 self.assertEqual(456, message.map_uint32_uint32[123])
889 self.assertEqual(2**34, message.map_uint64_uint64[2**33])
890 self.assertEqual("123", message.map_string_string["abc"])
891 self.assertEqual(5, message.map_int32_foreign_message[111].c)
892
kenton@google.com80b1d622009-07-29 01:13:20 +0000893
Tamir Duberstein9f42f5f2015-01-13 14:47:32 -0500894class TokenizerTest(unittest.TestCase):
kenton@google.com80b1d622009-07-29 01:13:20 +0000895
896 def testSimpleTokenCases(self):
897 text = ('identifier1:"string1"\n \n\n'
898 'identifier2 : \n \n123 \n identifier3 :\'string\'\n'
899 'identifiER_4 : 1.1e+2 ID5:-0.23 ID6:\'aaaa\\\'bbbb\'\n'
900 'ID7 : "aa\\"bb"\n\n\n\n ID8: {A:inf B:-inf C:true D:false}\n'
901 'ID9: 22 ID10: -111111111111111111 ID11: -22\n'
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000902 'ID12: 2222222222222222222 ID13: 1.23456f ID14: 1.2e+2f '
jieluo@google.combde4a322014-08-12 21:10:30 +0000903 'false_bool: 0 true_BOOL:t \n true_bool1: 1 false_BOOL1:f ')
904 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +0000905 methods = [(tokenizer.ConsumeIdentifier, 'identifier1'),
906 ':',
907 (tokenizer.ConsumeString, 'string1'),
908 (tokenizer.ConsumeIdentifier, 'identifier2'),
909 ':',
910 (tokenizer.ConsumeInt32, 123),
911 (tokenizer.ConsumeIdentifier, 'identifier3'),
912 ':',
913 (tokenizer.ConsumeString, 'string'),
914 (tokenizer.ConsumeIdentifier, 'identifiER_4'),
915 ':',
916 (tokenizer.ConsumeFloat, 1.1e+2),
917 (tokenizer.ConsumeIdentifier, 'ID5'),
918 ':',
919 (tokenizer.ConsumeFloat, -0.23),
920 (tokenizer.ConsumeIdentifier, 'ID6'),
921 ':',
922 (tokenizer.ConsumeString, 'aaaa\'bbbb'),
923 (tokenizer.ConsumeIdentifier, 'ID7'),
924 ':',
925 (tokenizer.ConsumeString, 'aa\"bb'),
926 (tokenizer.ConsumeIdentifier, 'ID8'),
927 ':',
928 '{',
929 (tokenizer.ConsumeIdentifier, 'A'),
930 ':',
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000931 (tokenizer.ConsumeFloat, float('inf')),
kenton@google.com80b1d622009-07-29 01:13:20 +0000932 (tokenizer.ConsumeIdentifier, 'B'),
933 ':',
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000934 (tokenizer.ConsumeFloat, -float('inf')),
kenton@google.com80b1d622009-07-29 01:13:20 +0000935 (tokenizer.ConsumeIdentifier, 'C'),
936 ':',
937 (tokenizer.ConsumeBool, True),
938 (tokenizer.ConsumeIdentifier, 'D'),
939 ':',
940 (tokenizer.ConsumeBool, False),
941 '}',
942 (tokenizer.ConsumeIdentifier, 'ID9'),
943 ':',
944 (tokenizer.ConsumeUint32, 22),
945 (tokenizer.ConsumeIdentifier, 'ID10'),
946 ':',
947 (tokenizer.ConsumeInt64, -111111111111111111),
948 (tokenizer.ConsumeIdentifier, 'ID11'),
949 ':',
950 (tokenizer.ConsumeInt32, -22),
951 (tokenizer.ConsumeIdentifier, 'ID12'),
952 ':',
liujisi@google.com33165fe2010-11-02 13:14:58 +0000953 (tokenizer.ConsumeUint64, 2222222222222222222),
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000954 (tokenizer.ConsumeIdentifier, 'ID13'),
955 ':',
956 (tokenizer.ConsumeFloat, 1.23456),
957 (tokenizer.ConsumeIdentifier, 'ID14'),
958 ':',
959 (tokenizer.ConsumeFloat, 1.2e+2),
liujisi@google.com33165fe2010-11-02 13:14:58 +0000960 (tokenizer.ConsumeIdentifier, 'false_bool'),
961 ':',
962 (tokenizer.ConsumeBool, False),
963 (tokenizer.ConsumeIdentifier, 'true_BOOL'),
964 ':',
965 (tokenizer.ConsumeBool, True),
966 (tokenizer.ConsumeIdentifier, 'true_bool1'),
967 ':',
968 (tokenizer.ConsumeBool, True),
969 (tokenizer.ConsumeIdentifier, 'false_BOOL1'),
970 ':',
971 (tokenizer.ConsumeBool, False)]
kenton@google.com80b1d622009-07-29 01:13:20 +0000972
973 i = 0
974 while not tokenizer.AtEnd():
975 m = methods[i]
976 if type(m) == str:
977 token = tokenizer.token
978 self.assertEqual(token, m)
979 tokenizer.NextToken()
980 else:
981 self.assertEqual(m[1], m[0]())
982 i += 1
983
984 def testConsumeIntegers(self):
985 # This test only tests the failures in the integer parsing methods as well
986 # as the '0' special cases.
987 int64_max = (1 << 63) - 1
988 uint32_max = (1 << 32) - 1
989 text = '-1 %d %d' % (uint32_max + 1, int64_max + 1)
jieluo@google.combde4a322014-08-12 21:10:30 +0000990 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +0000991 self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint32)
992 self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint64)
993 self.assertEqual(-1, tokenizer.ConsumeInt32())
994
995 self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint32)
996 self.assertRaises(text_format.ParseError, tokenizer.ConsumeInt32)
997 self.assertEqual(uint32_max + 1, tokenizer.ConsumeInt64())
998
999 self.assertRaises(text_format.ParseError, tokenizer.ConsumeInt64)
1000 self.assertEqual(int64_max + 1, tokenizer.ConsumeUint64())
1001 self.assertTrue(tokenizer.AtEnd())
1002
1003 text = '-0 -0 0 0'
jieluo@google.combde4a322014-08-12 21:10:30 +00001004 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +00001005 self.assertEqual(0, tokenizer.ConsumeUint32())
1006 self.assertEqual(0, tokenizer.ConsumeUint64())
1007 self.assertEqual(0, tokenizer.ConsumeUint32())
1008 self.assertEqual(0, tokenizer.ConsumeUint64())
1009 self.assertTrue(tokenizer.AtEnd())
1010
1011 def testConsumeByteString(self):
1012 text = '"string1\''
jieluo@google.combde4a322014-08-12 21:10:30 +00001013 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +00001014 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
1015
1016 text = 'string1"'
jieluo@google.combde4a322014-08-12 21:10:30 +00001017 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +00001018 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
1019
1020 text = '\n"\\xt"'
jieluo@google.combde4a322014-08-12 21:10:30 +00001021 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +00001022 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
1023
1024 text = '\n"\\"'
jieluo@google.combde4a322014-08-12 21:10:30 +00001025 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +00001026 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
1027
1028 text = '\n"\\x"'
jieluo@google.combde4a322014-08-12 21:10:30 +00001029 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +00001030 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
1031
1032 def testConsumeBool(self):
1033 text = 'not-a-bool'
jieluo@google.combde4a322014-08-12 21:10:30 +00001034 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +00001035 self.assertRaises(text_format.ParseError, tokenizer.ConsumeBool)
1036
1037
temporal40ee5512008-07-10 02:12:20 +00001038if __name__ == '__main__':
Tamir Duberstein9f42f5f2015-01-13 14:47:32 -05001039 unittest.main()