blob: 8ce0a44f23266c616fea243885178ccf7c43f7ec [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:
Jisi Liu3b3c8ab2016-03-30 11:39:59 -070043 import unittest2 as unittest #PY26
Jisi Liudbea00a2015-10-05 16:08:22 -070044except ImportError:
45 import unittest
Jisi Liu3b3c8ab2016-03-30 11:39:59 -070046
Jisi Liu885b6122015-02-28 14:51:22 -080047from google.protobuf.internal import _parameterized
Jisi Liuada65562015-02-25 16:39:11 -080048
Bo Yang5db21732015-05-21 14:28:59 -070049from google.protobuf import map_unittest_pb2
Jisi Liuada65562015-02-25 16:39:11 -080050from google.protobuf import unittest_mset_pb2
51from google.protobuf import unittest_pb2
52from google.protobuf import unittest_proto3_arena_pb2
jieluo@google.combde4a322014-08-12 21:10:30 +000053from google.protobuf.internal import api_implementation
temporal40ee5512008-07-10 02:12:20 +000054from google.protobuf.internal import test_util
Feng Xiaoe841bac2015-12-11 17:09:20 -080055from google.protobuf.internal import message_set_extensions_pb2
Jisi Liuada65562015-02-25 16:39:11 -080056from google.protobuf import text_format
temporal40ee5512008-07-10 02:12:20 +000057
Feng Xiaoe841bac2015-12-11 17:09:20 -080058
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.
Jisi Liu3b3c8ab2016-03-30 11:39:59 -070066 def testQuoteMarksAreSingleChars(self):
Feng Xiaoe841bac2015-12-11 17:09:20 -080067 for quote in text_format._QUOTES:
68 self.assertEqual(1, len(quote))
69
70
Jisi Liuada65562015-02-25 16:39:11 -080071# Base class with some common functionality.
Tamir Duberstein9f42f5f2015-01-13 14:47:32 -050072class TextFormatBase(unittest.TestCase):
kenton@google.com80b1d622009-07-29 01:13:20 +000073
kenton@google.com80b1d622009-07-29 01:13:20 +000074 def ReadGolden(self, golden_filename):
jieluo@google.combde4a322014-08-12 21:10:30 +000075 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])
kenton@google.com80b1d622009-07-29 01:13:20 +000078
79 def CompareToGoldenFile(self, text, golden_filename):
80 golden_lines = self.ReadGolden(golden_filename)
jieluo@google.combde4a322014-08-12 21:10:30 +000081 self.assertMultiLineEqual(text, ''.join(golden_lines))
temporal40ee5512008-07-10 02:12:20 +000082
83 def CompareToGoldenText(self, text, golden_text):
Dan O'Reilly2621c8a2015-08-14 22:54:53 -040084 self.assertEqual(text, golden_text)
temporal40ee5512008-07-10 02:12:20 +000085
Jisi Liuada65562015-02-25 16:39:11 -080086 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
jieluo@google.combde4a322014-08-12 21:10:30 +000095
temporal40ee5512008-07-10 02:12:20 +000096
Josh Haberman0b70a432015-02-25 20:17:32 -080097@_parameterized.Parameters(
Jisi Liuada65562015-02-25 16:39:11 -080098 (unittest_pb2),
99 (unittest_proto3_arena_pb2))
100class TextFormatTest(TextFormatBase):
jieluo@google.combde4a322014-08-12 21:10:30 +0000101
Jisi Liuada65562015-02-25 16:39:11 -0800102 def testPrintExotic(self, message_module):
103 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000104 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')
temporalf2063512008-07-23 01:19:07 +0000111 self.CompareToGoldenText(
jieluo@google.combde4a322014-08-12 21:10:30 +0000112 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
Jisi Liuada65562015-02-25 16:39:11 -0800122 def testPrintExoticUnicodeSubclass(self, message_module):
Dan O'Reilly3d5aa6a2015-08-14 16:12:34 -0400123 class UnicodeSub(six.text_type):
jieluo@google.combde4a322014-08-12 21:10:30 +0000124 pass
Jisi Liuada65562015-02-25 16:39:11 -0800125 message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000126 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')
liujisi@google.com33165fe2010-11-02 13:14:58 +0000130
Jisi Liuada65562015-02-25 16:39:11 -0800131 def testPrintNestedMessageAsOneLine(self, message_module):
132 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000133 msg = message.repeated_nested_message.add()
jieluo@google.combde4a322014-08-12 21:10:30 +0000134 msg.bb = 42
liujisi@google.com33165fe2010-11-02 13:14:58 +0000135 self.CompareToGoldenText(
136 text_format.MessageToString(message, as_one_line=True),
137 'repeated_nested_message { bb: 42 }')
138
Jisi Liuada65562015-02-25 16:39:11 -0800139 def testPrintRepeatedFieldsAsOneLine(self, message_module):
140 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000141 message.repeated_int32.append(1)
142 message.repeated_int32.append(1)
143 message.repeated_int32.append(3)
Jisi Liuada65562015-02-25 16:39:11 -0800144 message.repeated_string.append('Google')
145 message.repeated_string.append('Zurich')
liujisi@google.com33165fe2010-11-02 13:14:58 +0000146 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
Jisi Liuada65562015-02-25 16:39:11 -0800151 def testPrintNestedNewLineInStringAsOneLine(self, message_module):
152 message = message_module.TestAllTypes()
153 message.optional_string = 'a\nnew\nline'
liujisi@google.com33165fe2010-11-02 13:14:58 +0000154 self.CompareToGoldenText(
155 text_format.MessageToString(message, as_one_line=True),
156 'optional_string: "a\\nnew\\nline"')
157
Jisi Liuada65562015-02-25 16:39:11 -0800158 def testPrintExoticAsOneLine(self, message_module):
159 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000160 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(
jieluo@google.combde4a322014-08-12 21:10:30 +0000168 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"')
liujisi@google.com33165fe2010-11-02 13:14:58 +0000178
Jisi Liuada65562015-02-25 16:39:11 -0800179 def testRoundTripExoticAsOneLine(self, message_module):
180 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000181 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
liujisi@google.com6f2c3b82010-12-09 08:59:21 +0000189 # Test as_utf8 = False.
190 wire_text = text_format.MessageToString(
191 message, as_one_line=True, as_utf8=False)
Jisi Liuada65562015-02-25 16:39:11 -0800192 parsed_message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000193 r = text_format.Parse(wire_text, parsed_message)
194 self.assertIs(r, parsed_message)
Tres Seavera2abc942015-01-13 15:47:55 -0500195 self.assertEqual(message, parsed_message)
liujisi@google.com6f2c3b82010-12-09 08:59:21 +0000196
197 # Test as_utf8 = True.
198 wire_text = text_format.MessageToString(
199 message, as_one_line=True, as_utf8=True)
Jisi Liuada65562015-02-25 16:39:11 -0800200 parsed_message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000201 r = text_format.Parse(wire_text, parsed_message)
202 self.assertIs(r, parsed_message)
Tres Seavera2abc942015-01-13 15:47:55 -0500203 self.assertEqual(message, parsed_message,
jieluo@google.combde4a322014-08-12 21:10:30 +0000204 '\n%s != %s' % (message, parsed_message))
liujisi@google.com33165fe2010-11-02 13:14:58 +0000205
Jisi Liuada65562015-02-25 16:39:11 -0800206 def testPrintRawUtf8String(self, message_module):
207 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000208 message.repeated_string.append(u'\u00fc\ua71f')
jieluo@google.combde4a322014-08-12 21:10:30 +0000209 text = text_format.MessageToString(message, as_utf8=True)
liujisi@google.com6f2c3b82010-12-09 08:59:21 +0000210 self.CompareToGoldenText(text, 'repeated_string: "\303\274\352\234\237"\n')
Jisi Liuada65562015-02-25 16:39:11 -0800211 parsed_message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000212 text_format.Parse(text, parsed_message)
Tres Seavera2abc942015-01-13 15:47:55 -0500213 self.assertEqual(message, parsed_message,
jieluo@google.combde4a322014-08-12 21:10:30 +0000214 '\n%s != %s' % (message, parsed_message))
215
Jisi Liuada65562015-02-25 16:39:11 -0800216 def testPrintFloatFormat(self, message_module):
jieluo@google.combde4a322014-08-12 21:10:30 +0000217 # Check that float_format argument is passed to sub-message formatting.
Jisi Liuada65562015-02-25 16:39:11 -0800218 message = message_module.NestedTestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000219 # 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),
Dan O'Reilly2621c8a2015-08-14 22:54:53 -0400240 'payload {{\n {0}\n {1}\n {2}\n {3}\n}}\n'.format(*formatted_fields))
jieluo@google.combde4a322014-08-12 21:10:30 +0000241 # 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),
Dan O'Reilly2621c8a2015-08-14 22:54:53 -0400246 'payload {{ {0} {1} {2} {3} }}'.format(*formatted_fields))
temporal40ee5512008-07-10 02:12:20 +0000247
Jisi Liuada65562015-02-25 16:39:11 -0800248 def testMessageToString(self, message_module):
249 message = message_module.ForeignMessage()
temporal40ee5512008-07-10 02:12:20 +0000250 message.c = 123
251 self.assertEqual('c: 123\n', str(message))
252
Jisi Liuada65562015-02-25 16:39:11 -0800253 def testParseAllFields(self, message_module):
254 message = message_module.TestAllTypes()
kenton@google.com80b1d622009-07-29 01:13:20 +0000255 test_util.SetAllFields(message)
256 ascii_text = text_format.MessageToString(message)
257
Jisi Liuada65562015-02-25 16:39:11 -0800258 parsed_message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000259 text_format.Parse(ascii_text, parsed_message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000260 self.assertEqual(message, parsed_message)
Jisi Liuada65562015-02-25 16:39:11 -0800261 if message_module is unittest_pb2:
262 test_util.ExpectAllFieldsSet(self, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000263
Jisi Liuada65562015-02-25 16:39:11 -0800264 def testParseExotic(self, message_module):
265 message = message_module.TestAllTypes()
kenton@google.com80b1d622009-07-29 01:13:20 +0000266 text = ('repeated_int64: -9223372036854775808\n'
267 'repeated_uint64: 18446744073709551615\n'
268 'repeated_double: 123.456\n'
269 'repeated_double: 1.23e+22\n'
270 'repeated_double: 1.23e-18\n'
271 'repeated_string: \n'
liujisi@google.com33165fe2010-11-02 13:14:58 +0000272 '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
273 'repeated_string: "foo" \'corge\' "grault"\n'
274 'repeated_string: "\\303\\274\\352\\234\\237"\n'
275 'repeated_string: "\\xc3\\xbc"\n'
276 'repeated_string: "\xc3\xbc"\n')
jieluo@google.combde4a322014-08-12 21:10:30 +0000277 text_format.Parse(text, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000278
279 self.assertEqual(-9223372036854775808, message.repeated_int64[0])
280 self.assertEqual(18446744073709551615, message.repeated_uint64[0])
281 self.assertEqual(123.456, message.repeated_double[0])
282 self.assertEqual(1.23e22, message.repeated_double[1])
283 self.assertEqual(1.23e-18, message.repeated_double[2])
284 self.assertEqual(
liujisi@google.com33165fe2010-11-02 13:14:58 +0000285 '\000\001\a\b\f\n\r\t\v\\\'"', message.repeated_string[0])
kenton@google.comeef5f832009-12-23 01:32:45 +0000286 self.assertEqual('foocorgegrault', message.repeated_string[1])
liujisi@google.com33165fe2010-11-02 13:14:58 +0000287 self.assertEqual(u'\u00fc\ua71f', message.repeated_string[2])
288 self.assertEqual(u'\u00fc', message.repeated_string[3])
289
Jisi Liuada65562015-02-25 16:39:11 -0800290 def testParseTrailingCommas(self, message_module):
291 message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000292 text = ('repeated_int64: 100;\n'
293 'repeated_int64: 200;\n'
294 'repeated_int64: 300,\n'
295 'repeated_string: "one",\n'
296 'repeated_string: "two";\n')
297 text_format.Parse(text, message)
298
299 self.assertEqual(100, message.repeated_int64[0])
300 self.assertEqual(200, message.repeated_int64[1])
301 self.assertEqual(300, message.repeated_int64[2])
302 self.assertEqual(u'one', message.repeated_string[0])
303 self.assertEqual(u'two', message.repeated_string[1])
304
Feng Xiaoe841bac2015-12-11 17:09:20 -0800305 def testParseRepeatedScalarShortFormat(self, message_module):
306 message = message_module.TestAllTypes()
307 text = ('repeated_int64: [100, 200];\n'
308 'repeated_int64: 300,\n'
309 'repeated_string: ["one", "two"];\n')
310 text_format.Parse(text, message)
311
312 self.assertEqual(100, message.repeated_int64[0])
313 self.assertEqual(200, message.repeated_int64[1])
314 self.assertEqual(300, message.repeated_int64[2])
315 self.assertEqual(u'one', message.repeated_string[0])
316 self.assertEqual(u'two', message.repeated_string[1])
317
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700318 def testParseRepeatedMessageShortFormat(self, message_module):
319 message = message_module.TestAllTypes()
320 text = ('repeated_nested_message: [{bb: 100}, {bb: 200}],\n'
321 'repeated_nested_message: {bb: 300}\n'
322 'repeated_nested_message [{bb: 400}];\n')
323 text_format.Parse(text, message)
324
325 self.assertEqual(100, message.repeated_nested_message[0].bb)
326 self.assertEqual(200, message.repeated_nested_message[1].bb)
327 self.assertEqual(300, message.repeated_nested_message[2].bb)
328 self.assertEqual(400, message.repeated_nested_message[3].bb)
329
Jisi Liuada65562015-02-25 16:39:11 -0800330 def testParseEmptyText(self, message_module):
331 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000332 text = ''
jieluo@google.combde4a322014-08-12 21:10:30 +0000333 text_format.Parse(text, message)
Dan O'Reillye47cdd52015-08-12 23:57:46 -0400334 self.assertEqual(message_module.TestAllTypes(), message)
liujisi@google.com33165fe2010-11-02 13:14:58 +0000335
Jisi Liuada65562015-02-25 16:39:11 -0800336 def testParseInvalidUtf8(self, message_module):
337 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000338 text = 'repeated_string: "\\xc3\\xc3"'
jieluo@google.combde4a322014-08-12 21:10:30 +0000339 self.assertRaises(text_format.ParseError, text_format.Parse, text, message)
liujisi@google.com33165fe2010-11-02 13:14:58 +0000340
Jisi Liuada65562015-02-25 16:39:11 -0800341 def testParseSingleWord(self, message_module):
342 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000343 text = 'foo'
Feng Xiaoe841bac2015-12-11 17:09:20 -0800344 six.assertRaisesRegex(self,
liujisi@google.com33165fe2010-11-02 13:14:58 +0000345 text_format.ParseError,
Jisi Liuada65562015-02-25 16:39:11 -0800346 (r'1:1 : Message type "\w+.TestAllTypes" has no field named '
347 r'"foo".'),
jieluo@google.combde4a322014-08-12 21:10:30 +0000348 text_format.Parse, text, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000349
Jisi Liuada65562015-02-25 16:39:11 -0800350 def testParseUnknownField(self, message_module):
351 message = message_module.TestAllTypes()
kenton@google.com80b1d622009-07-29 01:13:20 +0000352 text = 'unknown_field: 8\n'
Feng Xiaoe841bac2015-12-11 17:09:20 -0800353 six.assertRaisesRegex(self,
kenton@google.com80b1d622009-07-29 01:13:20 +0000354 text_format.ParseError,
Jisi Liuada65562015-02-25 16:39:11 -0800355 (r'1:1 : Message type "\w+.TestAllTypes" has no field named '
356 r'"unknown_field".'),
jieluo@google.combde4a322014-08-12 21:10:30 +0000357 text_format.Parse, text, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000358
Jisi Liuada65562015-02-25 16:39:11 -0800359 def testParseBadEnumValue(self, message_module):
360 message = message_module.TestAllTypes()
kenton@google.com80b1d622009-07-29 01:13:20 +0000361 text = 'optional_nested_enum: BARR'
Feng Xiaoe841bac2015-12-11 17:09:20 -0800362 six.assertRaisesRegex(self,
kenton@google.com80b1d622009-07-29 01:13:20 +0000363 text_format.ParseError,
Jisi Liuada65562015-02-25 16:39:11 -0800364 (r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
365 r'has no value named BARR.'),
jieluo@google.combde4a322014-08-12 21:10:30 +0000366 text_format.Parse, text, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000367
Jisi Liuada65562015-02-25 16:39:11 -0800368 message = message_module.TestAllTypes()
kenton@google.com80b1d622009-07-29 01:13:20 +0000369 text = 'optional_nested_enum: 100'
Feng Xiaoe841bac2015-12-11 17:09:20 -0800370 six.assertRaisesRegex(self,
kenton@google.com80b1d622009-07-29 01:13:20 +0000371 text_format.ParseError,
Jisi Liuada65562015-02-25 16:39:11 -0800372 (r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
373 r'has no value with number 100.'),
jieluo@google.combde4a322014-08-12 21:10:30 +0000374 text_format.Parse, text, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000375
Jisi Liuada65562015-02-25 16:39:11 -0800376 def testParseBadIntValue(self, message_module):
377 message = message_module.TestAllTypes()
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000378 text = 'optional_int32: bork'
Feng Xiaoe841bac2015-12-11 17:09:20 -0800379 six.assertRaisesRegex(self,
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000380 text_format.ParseError,
381 ('1:17 : Couldn\'t parse integer: bork'),
jieluo@google.combde4a322014-08-12 21:10:30 +0000382 text_format.Parse, text, message)
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000383
Jisi Liuada65562015-02-25 16:39:11 -0800384 def testParseStringFieldUnescape(self, message_module):
385 message = message_module.TestAllTypes()
xiaofeng@google.com7f372552013-02-25 10:39:39 +0000386 text = r'''repeated_string: "\xf\x62"
387 repeated_string: "\\xf\\x62"
388 repeated_string: "\\\xf\\\x62"
389 repeated_string: "\\\\xf\\\\x62"
390 repeated_string: "\\\\\xf\\\\\x62"
391 repeated_string: "\x5cx20"'''
jieluo@google.combde4a322014-08-12 21:10:30 +0000392 text_format.Parse(text, message)
xiaofeng@google.com7f372552013-02-25 10:39:39 +0000393
394 SLASH = '\\'
395 self.assertEqual('\x0fb', message.repeated_string[0])
396 self.assertEqual(SLASH + 'xf' + SLASH + 'x62', message.repeated_string[1])
397 self.assertEqual(SLASH + '\x0f' + SLASH + 'b', message.repeated_string[2])
398 self.assertEqual(SLASH + SLASH + 'xf' + SLASH + SLASH + 'x62',
399 message.repeated_string[3])
400 self.assertEqual(SLASH + SLASH + '\x0f' + SLASH + SLASH + 'b',
401 message.repeated_string[4])
402 self.assertEqual(SLASH + 'x20', message.repeated_string[5])
403
Jisi Liuada65562015-02-25 16:39:11 -0800404 def testMergeDuplicateScalars(self, message_module):
405 message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000406 text = ('optional_int32: 42 '
407 'optional_int32: 67')
408 r = text_format.Merge(text, message)
409 self.assertIs(r, message)
410 self.assertEqual(67, message.optional_int32)
kenton@google.com80b1d622009-07-29 01:13:20 +0000411
Jisi Liuada65562015-02-25 16:39:11 -0800412 def testMergeDuplicateNestedMessageScalars(self, message_module):
413 message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000414 text = ('optional_nested_message { bb: 1 } '
415 'optional_nested_message { bb: 2 }')
416 r = text_format.Merge(text, message)
417 self.assertTrue(r is message)
418 self.assertEqual(2, message.optional_nested_message.bb)
419
Jisi Liuada65562015-02-25 16:39:11 -0800420 def testParseOneof(self, message_module):
421 m = message_module.TestAllTypes()
422 m.oneof_uint32 = 11
423 m2 = message_module.TestAllTypes()
424 text_format.Parse(text_format.MessageToString(m), m2)
425 self.assertEqual('oneof_uint32', m2.WhichOneof('oneof_field'))
426
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700427 def testParseMultipleOneof(self, message_module):
428 m_string = '\n'.join([
429 'oneof_uint32: 11',
430 'oneof_string: "foo"'])
431 m2 = message_module.TestAllTypes()
432 if message_module is unittest_pb2:
433 with self.assertRaisesRegexp(
434 text_format.ParseError, ' is specified along with field '):
435 text_format.Parse(m_string, m2)
436 else:
437 text_format.Parse(m_string, m2)
438 self.assertEqual('oneof_string', m2.WhichOneof('oneof_field'))
439
Jisi Liuada65562015-02-25 16:39:11 -0800440
441# These are tests that aren't fundamentally specific to proto2, but are at
442# the moment because of differences between the proto2 and proto3 test schemas.
443# Ideally the schemas would be made more similar so these tests could pass.
444class OnlyWorksWithProto2RightNowTests(TextFormatBase):
445
Jisi Liu46e8ff62015-10-05 11:59:43 -0700446 def testPrintAllFieldsPointy(self):
Bo Yang5db21732015-05-21 14:28:59 -0700447 message = unittest_pb2.TestAllTypes()
448 test_util.SetAllFields(message)
449 self.CompareToGoldenFile(
450 self.RemoveRedundantZeros(
451 text_format.MessageToString(message, pointy_brackets=True)),
452 'text_format_unittest_data_pointy_oneof.txt')
453
Jisi Liuada65562015-02-25 16:39:11 -0800454 def testParseGolden(self):
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700455 golden_text = '\n'.join(self.ReadGolden(
456 'text_format_unittest_data_oneof_implemented.txt'))
Jisi Liuada65562015-02-25 16:39:11 -0800457 parsed_message = unittest_pb2.TestAllTypes()
458 r = text_format.Parse(golden_text, parsed_message)
459 self.assertIs(r, parsed_message)
460
jieluo@google.combde4a322014-08-12 21:10:30 +0000461 message = unittest_pb2.TestAllTypes()
Jisi Liuada65562015-02-25 16:39:11 -0800462 test_util.SetAllFields(message)
Dan O'Reillye47cdd52015-08-12 23:57:46 -0400463 self.assertEqual(message, parsed_message)
Jisi Liuada65562015-02-25 16:39:11 -0800464
465 def testPrintAllFields(self):
466 message = unittest_pb2.TestAllTypes()
467 test_util.SetAllFields(message)
468 self.CompareToGoldenFile(
469 self.RemoveRedundantZeros(text_format.MessageToString(message)),
470 'text_format_unittest_data_oneof_implemented.txt')
471
472 def testPrintAllFieldsPointy(self):
473 message = unittest_pb2.TestAllTypes()
474 test_util.SetAllFields(message)
475 self.CompareToGoldenFile(
476 self.RemoveRedundantZeros(
477 text_format.MessageToString(message, pointy_brackets=True)),
478 'text_format_unittest_data_pointy_oneof.txt')
479
480 def testPrintInIndexOrder(self):
481 message = unittest_pb2.TestFieldOrderings()
482 message.my_string = '115'
483 message.my_int = 101
484 message.my_float = 111
485 message.optional_nested_message.oo = 0
486 message.optional_nested_message.bb = 1
487 self.CompareToGoldenText(
488 self.RemoveRedundantZeros(text_format.MessageToString(
489 message, use_index_order=True)),
490 'my_string: \"115\"\nmy_int: 101\nmy_float: 111\n'
491 'optional_nested_message {\n oo: 0\n bb: 1\n}\n')
492 self.CompareToGoldenText(
493 self.RemoveRedundantZeros(text_format.MessageToString(
494 message)),
495 'my_int: 101\nmy_string: \"115\"\nmy_float: 111\n'
496 'optional_nested_message {\n bb: 1\n oo: 0\n}\n')
497
498 def testMergeLinesGolden(self):
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700499 opened = self.ReadGolden('text_format_unittest_data_oneof_implemented.txt')
Jisi Liuada65562015-02-25 16:39:11 -0800500 parsed_message = unittest_pb2.TestAllTypes()
501 r = text_format.MergeLines(opened, parsed_message)
502 self.assertIs(r, parsed_message)
503
504 message = unittest_pb2.TestAllTypes()
505 test_util.SetAllFields(message)
506 self.assertEqual(message, parsed_message)
507
508 def testParseLinesGolden(self):
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700509 opened = self.ReadGolden('text_format_unittest_data_oneof_implemented.txt')
Jisi Liuada65562015-02-25 16:39:11 -0800510 parsed_message = unittest_pb2.TestAllTypes()
511 r = text_format.ParseLines(opened, parsed_message)
512 self.assertIs(r, parsed_message)
513
514 message = unittest_pb2.TestAllTypes()
515 test_util.SetAllFields(message)
Dan O'Reillye47cdd52015-08-12 23:57:46 -0400516 self.assertEqual(message, parsed_message)
Jisi Liuada65562015-02-25 16:39:11 -0800517
Bo Yang5db21732015-05-21 14:28:59 -0700518 def testPrintMap(self):
519 message = map_unittest_pb2.TestMap()
Jisi Liuada65562015-02-25 16:39:11 -0800520
Bo Yang5db21732015-05-21 14:28:59 -0700521 message.map_int32_int32[-123] = -456
522 message.map_int64_int64[-2**33] = -2**34
523 message.map_uint32_uint32[123] = 456
524 message.map_uint64_uint64[2**33] = 2**34
525 message.map_string_string["abc"] = "123"
526 message.map_int32_foreign_message[111].c = 5
527
528 # Maps are serialized to text format using their underlying repeated
529 # representation.
530 self.CompareToGoldenText(
531 text_format.MessageToString(message),
532 'map_int32_int32 {\n'
533 ' key: -123\n'
534 ' value: -456\n'
535 '}\n'
536 'map_int64_int64 {\n'
537 ' key: -8589934592\n'
538 ' value: -17179869184\n'
539 '}\n'
540 'map_uint32_uint32 {\n'
541 ' key: 123\n'
542 ' value: 456\n'
543 '}\n'
544 'map_uint64_uint64 {\n'
545 ' key: 8589934592\n'
546 ' value: 17179869184\n'
547 '}\n'
548 'map_string_string {\n'
549 ' key: "abc"\n'
550 ' value: "123"\n'
551 '}\n'
552 'map_int32_foreign_message {\n'
553 ' key: 111\n'
554 ' value {\n'
555 ' c: 5\n'
556 ' }\n'
557 '}\n')
558
Feng Xiaoeee38b02015-08-22 18:25:48 -0700559 def testMapOrderEnforcement(self):
560 message = map_unittest_pb2.TestMap()
561 for letter in string.ascii_uppercase[13:26]:
562 message.map_string_string[letter] = 'dummy'
563 for letter in reversed(string.ascii_uppercase[0:13]):
564 message.map_string_string[letter] = 'dummy'
565 golden = ''.join((
566 'map_string_string {\n key: "%c"\n value: "dummy"\n}\n' % (letter,)
567 for letter in string.ascii_uppercase))
568 self.CompareToGoldenText(text_format.MessageToString(message), golden)
569
570 def testMapOrderSemantics(self):
571 golden_lines = self.ReadGolden('map_test_data.txt')
572 # The C++ implementation emits defaulted-value fields, while the Python
573 # implementation does not. Adjusting for this is awkward, but it is
574 # valuable to test against a common golden file.
575 line_blacklist = (' key: 0\n',
576 ' value: 0\n',
577 ' key: false\n',
578 ' value: false\n')
579 golden_lines = [line for line in golden_lines if line not in line_blacklist]
580
581 message = map_unittest_pb2.TestMap()
582 text_format.ParseLines(golden_lines, message)
583 candidate = text_format.MessageToString(message)
584 # The Python implementation emits "1.0" for the double value that the C++
585 # implementation emits as "1".
586 candidate = candidate.replace('1.0', '1', 2)
587 self.assertMultiLineEqual(candidate, ''.join(golden_lines))
588
Bo Yang5db21732015-05-21 14:28:59 -0700589
590# Tests of proto2-only features (MessageSet, extensions, etc.).
Jisi Liuada65562015-02-25 16:39:11 -0800591class Proto2Tests(TextFormatBase):
592
593 def testPrintMessageSet(self):
594 message = unittest_mset_pb2.TestMessageSetContainer()
595 ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
596 ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
597 message.message_set.Extensions[ext1].i = 23
598 message.message_set.Extensions[ext2].str = 'foo'
599 self.CompareToGoldenText(
600 text_format.MessageToString(message),
601 'message_set {\n'
602 ' [protobuf_unittest.TestMessageSetExtension1] {\n'
603 ' i: 23\n'
604 ' }\n'
605 ' [protobuf_unittest.TestMessageSetExtension2] {\n'
606 ' str: \"foo\"\n'
607 ' }\n'
608 '}\n')
609
Feng Xiaoe841bac2015-12-11 17:09:20 -0800610 message = message_set_extensions_pb2.TestMessageSet()
611 ext = message_set_extensions_pb2.message_set_extension3
612 message.Extensions[ext].text = 'bar'
613 self.CompareToGoldenText(
614 text_format.MessageToString(message),
615 '[google.protobuf.internal.TestMessageSetExtension3] {\n'
616 ' text: \"bar\"\n'
617 '}\n')
618
Jisi Liuada65562015-02-25 16:39:11 -0800619 def testPrintMessageSetAsOneLine(self):
620 message = unittest_mset_pb2.TestMessageSetContainer()
621 ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
622 ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
623 message.message_set.Extensions[ext1].i = 23
624 message.message_set.Extensions[ext2].str = 'foo'
625 self.CompareToGoldenText(
626 text_format.MessageToString(message, as_one_line=True),
627 'message_set {'
628 ' [protobuf_unittest.TestMessageSetExtension1] {'
629 ' i: 23'
630 ' }'
631 ' [protobuf_unittest.TestMessageSetExtension2] {'
632 ' str: \"foo\"'
633 ' }'
634 ' }')
635
636 def testParseMessageSet(self):
637 message = unittest_pb2.TestAllTypes()
638 text = ('repeated_uint64: 1\n'
639 'repeated_uint64: 2\n')
640 text_format.Parse(text, message)
641 self.assertEqual(1, message.repeated_uint64[0])
642 self.assertEqual(2, message.repeated_uint64[1])
643
644 message = unittest_mset_pb2.TestMessageSetContainer()
645 text = ('message_set {\n'
646 ' [protobuf_unittest.TestMessageSetExtension1] {\n'
647 ' i: 23\n'
648 ' }\n'
649 ' [protobuf_unittest.TestMessageSetExtension2] {\n'
650 ' str: \"foo\"\n'
651 ' }\n'
652 '}\n')
653 text_format.Parse(text, message)
654 ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
655 ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
Dan O'Reillye47cdd52015-08-12 23:57:46 -0400656 self.assertEqual(23, message.message_set.Extensions[ext1].i)
657 self.assertEqual('foo', message.message_set.Extensions[ext2].str)
Jisi Liuada65562015-02-25 16:39:11 -0800658
659 def testPrintAllExtensions(self):
660 message = unittest_pb2.TestAllExtensions()
661 test_util.SetAllExtensions(message)
662 self.CompareToGoldenFile(
663 self.RemoveRedundantZeros(text_format.MessageToString(message)),
664 'text_format_unittest_extensions_data.txt')
665
666 def testPrintAllExtensionsPointy(self):
667 message = unittest_pb2.TestAllExtensions()
668 test_util.SetAllExtensions(message)
669 self.CompareToGoldenFile(
670 self.RemoveRedundantZeros(text_format.MessageToString(
671 message, pointy_brackets=True)),
672 'text_format_unittest_extensions_data_pointy.txt')
673
674 def testParseGoldenExtensions(self):
675 golden_text = '\n'.join(self.ReadGolden(
676 'text_format_unittest_extensions_data.txt'))
677 parsed_message = unittest_pb2.TestAllExtensions()
678 text_format.Parse(golden_text, parsed_message)
679
680 message = unittest_pb2.TestAllExtensions()
681 test_util.SetAllExtensions(message)
Dan O'Reillye47cdd52015-08-12 23:57:46 -0400682 self.assertEqual(message, parsed_message)
Jisi Liuada65562015-02-25 16:39:11 -0800683
684 def testParseAllExtensions(self):
685 message = unittest_pb2.TestAllExtensions()
686 test_util.SetAllExtensions(message)
687 ascii_text = text_format.MessageToString(message)
688
689 parsed_message = unittest_pb2.TestAllExtensions()
690 text_format.Parse(ascii_text, parsed_message)
691 self.assertEqual(message, parsed_message)
692
Feng Xiaoe841bac2015-12-11 17:09:20 -0800693 def testParseAllowedUnknownExtension(self):
694 # Skip over unknown extension correctly.
695 message = unittest_mset_pb2.TestMessageSetContainer()
696 text = ('message_set {\n'
697 ' [unknown_extension] {\n'
698 ' i: 23\n'
699 ' [nested_unknown_ext]: {\n'
700 ' i: 23\n'
701 ' test: "test_string"\n'
702 ' floaty_float: -0.315\n'
703 ' num: -inf\n'
704 ' multiline_str: "abc"\n'
705 ' "def"\n'
706 ' "xyz."\n'
707 ' [nested_unknown_ext]: <\n'
708 ' i: 23\n'
709 ' i: 24\n'
710 ' pointfloat: .3\n'
711 ' test: "test_string"\n'
712 ' floaty_float: -0.315\n'
713 ' num: -inf\n'
714 ' long_string: "test" "test2" \n'
715 ' >\n'
716 ' }\n'
717 ' }\n'
718 ' [unknown_extension]: 5\n'
719 '}\n')
720 text_format.Parse(text, message, allow_unknown_extension=True)
721 golden = 'message_set {\n}\n'
722 self.CompareToGoldenText(text_format.MessageToString(message), golden)
723
724 # Catch parse errors in unknown extension.
725 message = unittest_mset_pb2.TestMessageSetContainer()
726 malformed = ('message_set {\n'
727 ' [unknown_extension] {\n'
728 ' i:\n' # Missing value.
729 ' }\n'
730 '}\n')
731 six.assertRaisesRegex(self,
732 text_format.ParseError,
733 'Invalid field value: }',
734 text_format.Parse, malformed, message,
735 allow_unknown_extension=True)
736
737 message = unittest_mset_pb2.TestMessageSetContainer()
738 malformed = ('message_set {\n'
739 ' [unknown_extension] {\n'
740 ' str: "malformed string\n' # Missing closing quote.
741 ' }\n'
742 '}\n')
743 six.assertRaisesRegex(self,
744 text_format.ParseError,
745 'Invalid field value: "',
746 text_format.Parse, malformed, message,
747 allow_unknown_extension=True)
748
749 message = unittest_mset_pb2.TestMessageSetContainer()
750 malformed = ('message_set {\n'
751 ' [unknown_extension] {\n'
752 ' str: "malformed\n multiline\n string\n'
753 ' }\n'
754 '}\n')
755 six.assertRaisesRegex(self,
756 text_format.ParseError,
757 'Invalid field value: "',
758 text_format.Parse, malformed, message,
759 allow_unknown_extension=True)
760
761 message = unittest_mset_pb2.TestMessageSetContainer()
762 malformed = ('message_set {\n'
763 ' [malformed_extension] <\n'
764 ' i: -5\n'
765 ' \n' # Missing '>' here.
766 '}\n')
767 six.assertRaisesRegex(self,
768 text_format.ParseError,
769 '5:1 : Expected ">".',
770 text_format.Parse, malformed, message,
771 allow_unknown_extension=True)
772
773 # Don't allow unknown fields with allow_unknown_extension=True.
774 message = unittest_mset_pb2.TestMessageSetContainer()
775 malformed = ('message_set {\n'
776 ' unknown_field: true\n'
777 ' \n' # Missing '>' here.
778 '}\n')
779 six.assertRaisesRegex(self,
780 text_format.ParseError,
781 ('2:3 : Message type '
782 '"proto2_wireformat_unittest.TestMessageSet" has no'
783 ' field named "unknown_field".'),
784 text_format.Parse, malformed, message,
785 allow_unknown_extension=True)
786
787 # Parse known extension correcty.
788 message = unittest_mset_pb2.TestMessageSetContainer()
789 text = ('message_set {\n'
790 ' [protobuf_unittest.TestMessageSetExtension1] {\n'
791 ' i: 23\n'
792 ' }\n'
793 ' [protobuf_unittest.TestMessageSetExtension2] {\n'
794 ' str: \"foo\"\n'
795 ' }\n'
796 '}\n')
797 text_format.Parse(text, message, allow_unknown_extension=True)
798 ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
799 ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
800 self.assertEqual(23, message.message_set.Extensions[ext1].i)
801 self.assertEqual('foo', message.message_set.Extensions[ext2].str)
802
Jisi Liuada65562015-02-25 16:39:11 -0800803 def testParseBadExtension(self):
804 message = unittest_pb2.TestAllExtensions()
805 text = '[unknown_extension]: 8\n'
Feng Xiaoe841bac2015-12-11 17:09:20 -0800806 six.assertRaisesRegex(self,
jieluo@google.combde4a322014-08-12 21:10:30 +0000807 text_format.ParseError,
Jisi Liuada65562015-02-25 16:39:11 -0800808 '1:2 : Extension "unknown_extension" not registered.',
809 text_format.Parse, text, message)
810 message = unittest_pb2.TestAllTypes()
Feng Xiaoe841bac2015-12-11 17:09:20 -0800811 six.assertRaisesRegex(self,
Jisi Liuada65562015-02-25 16:39:11 -0800812 text_format.ParseError,
813 ('1:2 : Message type "protobuf_unittest.TestAllTypes" does not have '
814 'extensions.'),
jieluo@google.combde4a322014-08-12 21:10:30 +0000815 text_format.Parse, text, message)
816
Feng Xiao6ef984a2014-11-10 17:34:54 -0800817 def testMergeDuplicateExtensionScalars(self):
jieluo@google.combde4a322014-08-12 21:10:30 +0000818 message = unittest_pb2.TestAllExtensions()
819 text = ('[protobuf_unittest.optional_int32_extension]: 42 '
820 '[protobuf_unittest.optional_int32_extension]: 67')
821 text_format.Merge(text, message)
822 self.assertEqual(
823 67,
824 message.Extensions[unittest_pb2.optional_int32_extension])
825
Feng Xiao6ef984a2014-11-10 17:34:54 -0800826 def testParseDuplicateExtensionScalars(self):
jieluo@google.combde4a322014-08-12 21:10:30 +0000827 message = unittest_pb2.TestAllExtensions()
828 text = ('[protobuf_unittest.optional_int32_extension]: 42 '
829 '[protobuf_unittest.optional_int32_extension]: 67')
Feng Xiaoe841bac2015-12-11 17:09:20 -0800830 six.assertRaisesRegex(self,
jieluo@google.combde4a322014-08-12 21:10:30 +0000831 text_format.ParseError,
832 ('1:96 : Message type "protobuf_unittest.TestAllExtensions" '
833 'should not have multiple '
834 '"protobuf_unittest.optional_int32_extension" extensions.'),
835 text_format.Parse, text, message)
836
Jisi Liuada65562015-02-25 16:39:11 -0800837 def testParseDuplicateNestedMessageScalars(self):
jieluo@google.combde4a322014-08-12 21:10:30 +0000838 message = unittest_pb2.TestAllTypes()
Jisi Liuada65562015-02-25 16:39:11 -0800839 text = ('optional_nested_message { bb: 1 } '
840 'optional_nested_message { bb: 2 }')
Feng Xiaoe841bac2015-12-11 17:09:20 -0800841 six.assertRaisesRegex(self,
Jisi Liuada65562015-02-25 16:39:11 -0800842 text_format.ParseError,
843 ('1:65 : Message type "protobuf_unittest.TestAllTypes.NestedMessage" '
844 'should not have multiple "bb" fields.'),
845 text_format.Parse, text, message)
jieluo@google.combde4a322014-08-12 21:10:30 +0000846
Jisi Liuada65562015-02-25 16:39:11 -0800847 def testParseDuplicateScalars(self):
jieluo@google.combde4a322014-08-12 21:10:30 +0000848 message = unittest_pb2.TestAllTypes()
Jisi Liuada65562015-02-25 16:39:11 -0800849 text = ('optional_int32: 42 '
850 'optional_int32: 67')
Feng Xiaoe841bac2015-12-11 17:09:20 -0800851 six.assertRaisesRegex(self,
Jisi Liuada65562015-02-25 16:39:11 -0800852 text_format.ParseError,
853 ('1:36 : Message type "protobuf_unittest.TestAllTypes" should not '
854 'have multiple "optional_int32" fields.'),
855 text_format.Parse, text, message)
Feng Xiao0971bb02014-10-07 17:44:33 -0700856
Bo Yang5db21732015-05-21 14:28:59 -0700857 def testParseGroupNotClosed(self):
858 message = unittest_pb2.TestAllTypes()
859 text = 'RepeatedGroup: <'
Feng Xiaoe841bac2015-12-11 17:09:20 -0800860 six.assertRaisesRegex(self,
Bo Yang5db21732015-05-21 14:28:59 -0700861 text_format.ParseError, '1:16 : Expected ">".',
862 text_format.Parse, text, message)
863 text = 'RepeatedGroup: {'
Feng Xiaoe841bac2015-12-11 17:09:20 -0800864 six.assertRaisesRegex(self,
Bo Yang5db21732015-05-21 14:28:59 -0700865 text_format.ParseError, '1:16 : Expected "}".',
866 text_format.Parse, text, message)
867
868 def testParseEmptyGroup(self):
869 message = unittest_pb2.TestAllTypes()
870 text = 'OptionalGroup: {}'
871 text_format.Parse(text, message)
872 self.assertTrue(message.HasField('optionalgroup'))
873
874 message.Clear()
875
876 message = unittest_pb2.TestAllTypes()
877 text = 'OptionalGroup: <>'
878 text_format.Parse(text, message)
879 self.assertTrue(message.HasField('optionalgroup'))
880
881 # Maps aren't really proto2-only, but our test schema only has maps for
882 # proto2.
883 def testParseMap(self):
884 text = ('map_int32_int32 {\n'
885 ' key: -123\n'
886 ' value: -456\n'
887 '}\n'
888 'map_int64_int64 {\n'
889 ' key: -8589934592\n'
890 ' value: -17179869184\n'
891 '}\n'
892 'map_uint32_uint32 {\n'
893 ' key: 123\n'
894 ' value: 456\n'
895 '}\n'
896 'map_uint64_uint64 {\n'
897 ' key: 8589934592\n'
898 ' value: 17179869184\n'
899 '}\n'
900 'map_string_string {\n'
901 ' key: "abc"\n'
902 ' value: "123"\n'
903 '}\n'
904 'map_int32_foreign_message {\n'
905 ' key: 111\n'
906 ' value {\n'
907 ' c: 5\n'
908 ' }\n'
909 '}\n')
910 message = map_unittest_pb2.TestMap()
911 text_format.Parse(text, message)
912
913 self.assertEqual(-456, message.map_int32_int32[-123])
914 self.assertEqual(-2**34, message.map_int64_int64[-2**33])
915 self.assertEqual(456, message.map_uint32_uint32[123])
916 self.assertEqual(2**34, message.map_uint64_uint64[2**33])
917 self.assertEqual("123", message.map_string_string["abc"])
918 self.assertEqual(5, message.map_int32_foreign_message[111].c)
919
kenton@google.com80b1d622009-07-29 01:13:20 +0000920
Tamir Duberstein9f42f5f2015-01-13 14:47:32 -0500921class TokenizerTest(unittest.TestCase):
kenton@google.com80b1d622009-07-29 01:13:20 +0000922
923 def testSimpleTokenCases(self):
924 text = ('identifier1:"string1"\n \n\n'
925 'identifier2 : \n \n123 \n identifier3 :\'string\'\n'
926 'identifiER_4 : 1.1e+2 ID5:-0.23 ID6:\'aaaa\\\'bbbb\'\n'
927 'ID7 : "aa\\"bb"\n\n\n\n ID8: {A:inf B:-inf C:true D:false}\n'
928 'ID9: 22 ID10: -111111111111111111 ID11: -22\n'
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000929 'ID12: 2222222222222222222 ID13: 1.23456f ID14: 1.2e+2f '
jieluo@google.combde4a322014-08-12 21:10:30 +0000930 'false_bool: 0 true_BOOL:t \n true_bool1: 1 false_BOOL1:f ')
931 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +0000932 methods = [(tokenizer.ConsumeIdentifier, 'identifier1'),
933 ':',
934 (tokenizer.ConsumeString, 'string1'),
935 (tokenizer.ConsumeIdentifier, 'identifier2'),
936 ':',
937 (tokenizer.ConsumeInt32, 123),
938 (tokenizer.ConsumeIdentifier, 'identifier3'),
939 ':',
940 (tokenizer.ConsumeString, 'string'),
941 (tokenizer.ConsumeIdentifier, 'identifiER_4'),
942 ':',
943 (tokenizer.ConsumeFloat, 1.1e+2),
944 (tokenizer.ConsumeIdentifier, 'ID5'),
945 ':',
946 (tokenizer.ConsumeFloat, -0.23),
947 (tokenizer.ConsumeIdentifier, 'ID6'),
948 ':',
949 (tokenizer.ConsumeString, 'aaaa\'bbbb'),
950 (tokenizer.ConsumeIdentifier, 'ID7'),
951 ':',
952 (tokenizer.ConsumeString, 'aa\"bb'),
953 (tokenizer.ConsumeIdentifier, 'ID8'),
954 ':',
955 '{',
956 (tokenizer.ConsumeIdentifier, 'A'),
957 ':',
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000958 (tokenizer.ConsumeFloat, float('inf')),
kenton@google.com80b1d622009-07-29 01:13:20 +0000959 (tokenizer.ConsumeIdentifier, 'B'),
960 ':',
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000961 (tokenizer.ConsumeFloat, -float('inf')),
kenton@google.com80b1d622009-07-29 01:13:20 +0000962 (tokenizer.ConsumeIdentifier, 'C'),
963 ':',
964 (tokenizer.ConsumeBool, True),
965 (tokenizer.ConsumeIdentifier, 'D'),
966 ':',
967 (tokenizer.ConsumeBool, False),
968 '}',
969 (tokenizer.ConsumeIdentifier, 'ID9'),
970 ':',
971 (tokenizer.ConsumeUint32, 22),
972 (tokenizer.ConsumeIdentifier, 'ID10'),
973 ':',
974 (tokenizer.ConsumeInt64, -111111111111111111),
975 (tokenizer.ConsumeIdentifier, 'ID11'),
976 ':',
977 (tokenizer.ConsumeInt32, -22),
978 (tokenizer.ConsumeIdentifier, 'ID12'),
979 ':',
liujisi@google.com33165fe2010-11-02 13:14:58 +0000980 (tokenizer.ConsumeUint64, 2222222222222222222),
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000981 (tokenizer.ConsumeIdentifier, 'ID13'),
982 ':',
983 (tokenizer.ConsumeFloat, 1.23456),
984 (tokenizer.ConsumeIdentifier, 'ID14'),
985 ':',
986 (tokenizer.ConsumeFloat, 1.2e+2),
liujisi@google.com33165fe2010-11-02 13:14:58 +0000987 (tokenizer.ConsumeIdentifier, 'false_bool'),
988 ':',
989 (tokenizer.ConsumeBool, False),
990 (tokenizer.ConsumeIdentifier, 'true_BOOL'),
991 ':',
992 (tokenizer.ConsumeBool, True),
993 (tokenizer.ConsumeIdentifier, 'true_bool1'),
994 ':',
995 (tokenizer.ConsumeBool, True),
996 (tokenizer.ConsumeIdentifier, 'false_BOOL1'),
997 ':',
998 (tokenizer.ConsumeBool, False)]
kenton@google.com80b1d622009-07-29 01:13:20 +0000999
1000 i = 0
1001 while not tokenizer.AtEnd():
1002 m = methods[i]
1003 if type(m) == str:
1004 token = tokenizer.token
1005 self.assertEqual(token, m)
1006 tokenizer.NextToken()
1007 else:
1008 self.assertEqual(m[1], m[0]())
1009 i += 1
1010
1011 def testConsumeIntegers(self):
1012 # This test only tests the failures in the integer parsing methods as well
1013 # as the '0' special cases.
1014 int64_max = (1 << 63) - 1
1015 uint32_max = (1 << 32) - 1
1016 text = '-1 %d %d' % (uint32_max + 1, int64_max + 1)
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.ConsumeUint32)
1019 self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint64)
1020 self.assertEqual(-1, tokenizer.ConsumeInt32())
1021
1022 self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint32)
1023 self.assertRaises(text_format.ParseError, tokenizer.ConsumeInt32)
1024 self.assertEqual(uint32_max + 1, tokenizer.ConsumeInt64())
1025
1026 self.assertRaises(text_format.ParseError, tokenizer.ConsumeInt64)
1027 self.assertEqual(int64_max + 1, tokenizer.ConsumeUint64())
1028 self.assertTrue(tokenizer.AtEnd())
1029
1030 text = '-0 -0 0 0'
jieluo@google.combde4a322014-08-12 21:10:30 +00001031 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +00001032 self.assertEqual(0, tokenizer.ConsumeUint32())
1033 self.assertEqual(0, tokenizer.ConsumeUint64())
1034 self.assertEqual(0, tokenizer.ConsumeUint32())
1035 self.assertEqual(0, tokenizer.ConsumeUint64())
1036 self.assertTrue(tokenizer.AtEnd())
1037
1038 def testConsumeByteString(self):
1039 text = '"string1\''
jieluo@google.combde4a322014-08-12 21:10:30 +00001040 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +00001041 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
1042
1043 text = 'string1"'
jieluo@google.combde4a322014-08-12 21:10:30 +00001044 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +00001045 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
1046
1047 text = '\n"\\xt"'
jieluo@google.combde4a322014-08-12 21:10:30 +00001048 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +00001049 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
1050
1051 text = '\n"\\"'
jieluo@google.combde4a322014-08-12 21:10:30 +00001052 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +00001053 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
1054
1055 text = '\n"\\x"'
jieluo@google.combde4a322014-08-12 21:10:30 +00001056 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +00001057 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
1058
1059 def testConsumeBool(self):
1060 text = 'not-a-bool'
jieluo@google.combde4a322014-08-12 21:10:30 +00001061 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +00001062 self.assertRaises(text_format.ParseError, tokenizer.ConsumeBool)
1063
1064
temporal40ee5512008-07-10 02:12:20 +00001065if __name__ == '__main__':
Tamir Duberstein9f42f5f2015-01-13 14:47:32 -05001066 unittest.main()