blob: 7d5813fb95ffc506b8b0462df3e935feb15ffb63 [file] [log] [blame]
kenton@google.com26bd9ee2008-11-21 00:06:27 +00001#! /usr/bin/python
2#
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
liujisi@google.com33165fe2010-11-02 13:14:58 +000037import re
temporal40ee5512008-07-10 02:12:20 +000038
jieluo@google.combde4a322014-08-12 21:10:30 +000039from google.apputils import basetest
Jisi Liu885b6122015-02-28 14:51:22 -080040from google.protobuf.internal import _parameterized
Jisi Liuada65562015-02-25 16:39:11 -080041
42from google.protobuf import unittest_mset_pb2
43from google.protobuf import unittest_pb2
44from google.protobuf import unittest_proto3_arena_pb2
jieluo@google.combde4a322014-08-12 21:10:30 +000045from google.protobuf.internal import api_implementation
temporal40ee5512008-07-10 02:12:20 +000046from google.protobuf.internal import test_util
Jisi Liuada65562015-02-25 16:39:11 -080047from google.protobuf import text_format
temporal40ee5512008-07-10 02:12:20 +000048
Jisi Liuada65562015-02-25 16:39:11 -080049# Base class with some common functionality.
50class TextFormatBase(basetest.TestCase):
kenton@google.com80b1d622009-07-29 01:13:20 +000051
kenton@google.com80b1d622009-07-29 01:13:20 +000052 def ReadGolden(self, golden_filename):
jieluo@google.combde4a322014-08-12 21:10:30 +000053 with test_util.GoldenFile(golden_filename) as f:
54 return (f.readlines() if str is bytes else # PY3
55 [golden_line.decode('utf-8') for golden_line in f])
kenton@google.com80b1d622009-07-29 01:13:20 +000056
57 def CompareToGoldenFile(self, text, golden_filename):
58 golden_lines = self.ReadGolden(golden_filename)
jieluo@google.combde4a322014-08-12 21:10:30 +000059 self.assertMultiLineEqual(text, ''.join(golden_lines))
temporal40ee5512008-07-10 02:12:20 +000060
61 def CompareToGoldenText(self, text, golden_text):
jieluo@google.combde4a322014-08-12 21:10:30 +000062 self.assertMultiLineEqual(text, golden_text)
temporal40ee5512008-07-10 02:12:20 +000063
Jisi Liuada65562015-02-25 16:39:11 -080064 def RemoveRedundantZeros(self, text):
65 # Some platforms print 1e+5 as 1e+005. This is fine, but we need to remove
66 # these zeros in order to match the golden file.
67 text = text.replace('e+0','e+').replace('e+0','e+') \
68 .replace('e-0','e-').replace('e-0','e-')
69 # Floating point fields are printed with .0 suffix even if they are
70 # actualy integer numbers.
71 text = re.compile('\.0$', re.MULTILINE).sub('', text)
72 return text
jieluo@google.combde4a322014-08-12 21:10:30 +000073
temporal40ee5512008-07-10 02:12:20 +000074
Josh Haberman0b70a432015-02-25 20:17:32 -080075@_parameterized.Parameters(
Jisi Liuada65562015-02-25 16:39:11 -080076 (unittest_pb2),
77 (unittest_proto3_arena_pb2))
78class TextFormatTest(TextFormatBase):
jieluo@google.combde4a322014-08-12 21:10:30 +000079
Jisi Liuada65562015-02-25 16:39:11 -080080 def testPrintExotic(self, message_module):
81 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +000082 message.repeated_int64.append(-9223372036854775808)
83 message.repeated_uint64.append(18446744073709551615)
84 message.repeated_double.append(123.456)
85 message.repeated_double.append(1.23e22)
86 message.repeated_double.append(1.23e-18)
87 message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
88 message.repeated_string.append(u'\u00fc\ua71f')
temporalf2063512008-07-23 01:19:07 +000089 self.CompareToGoldenText(
jieluo@google.combde4a322014-08-12 21:10:30 +000090 self.RemoveRedundantZeros(text_format.MessageToString(message)),
91 'repeated_int64: -9223372036854775808\n'
92 'repeated_uint64: 18446744073709551615\n'
93 'repeated_double: 123.456\n'
94 'repeated_double: 1.23e+22\n'
95 'repeated_double: 1.23e-18\n'
96 'repeated_string:'
97 ' "\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
98 'repeated_string: "\\303\\274\\352\\234\\237"\n')
99
Jisi Liuada65562015-02-25 16:39:11 -0800100 def testPrintExoticUnicodeSubclass(self, message_module):
jieluo@google.combde4a322014-08-12 21:10:30 +0000101 class UnicodeSub(unicode):
102 pass
Jisi Liuada65562015-02-25 16:39:11 -0800103 message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000104 message.repeated_string.append(UnicodeSub(u'\u00fc\ua71f'))
105 self.CompareToGoldenText(
106 text_format.MessageToString(message),
107 'repeated_string: "\\303\\274\\352\\234\\237"\n')
liujisi@google.com33165fe2010-11-02 13:14:58 +0000108
Jisi Liuada65562015-02-25 16:39:11 -0800109 def testPrintNestedMessageAsOneLine(self, message_module):
110 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000111 msg = message.repeated_nested_message.add()
jieluo@google.combde4a322014-08-12 21:10:30 +0000112 msg.bb = 42
liujisi@google.com33165fe2010-11-02 13:14:58 +0000113 self.CompareToGoldenText(
114 text_format.MessageToString(message, as_one_line=True),
115 'repeated_nested_message { bb: 42 }')
116
Jisi Liuada65562015-02-25 16:39:11 -0800117 def testPrintRepeatedFieldsAsOneLine(self, message_module):
118 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000119 message.repeated_int32.append(1)
120 message.repeated_int32.append(1)
121 message.repeated_int32.append(3)
Jisi Liuada65562015-02-25 16:39:11 -0800122 message.repeated_string.append('Google')
123 message.repeated_string.append('Zurich')
liujisi@google.com33165fe2010-11-02 13:14:58 +0000124 self.CompareToGoldenText(
125 text_format.MessageToString(message, as_one_line=True),
126 'repeated_int32: 1 repeated_int32: 1 repeated_int32: 3 '
127 'repeated_string: "Google" repeated_string: "Zurich"')
128
Jisi Liuada65562015-02-25 16:39:11 -0800129 def testPrintNestedNewLineInStringAsOneLine(self, message_module):
130 message = message_module.TestAllTypes()
131 message.optional_string = 'a\nnew\nline'
liujisi@google.com33165fe2010-11-02 13:14:58 +0000132 self.CompareToGoldenText(
133 text_format.MessageToString(message, as_one_line=True),
134 'optional_string: "a\\nnew\\nline"')
135
Jisi Liuada65562015-02-25 16:39:11 -0800136 def testPrintExoticAsOneLine(self, message_module):
137 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000138 message.repeated_int64.append(-9223372036854775808)
139 message.repeated_uint64.append(18446744073709551615)
140 message.repeated_double.append(123.456)
141 message.repeated_double.append(1.23e22)
142 message.repeated_double.append(1.23e-18)
143 message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
144 message.repeated_string.append(u'\u00fc\ua71f')
145 self.CompareToGoldenText(
jieluo@google.combde4a322014-08-12 21:10:30 +0000146 self.RemoveRedundantZeros(
147 text_format.MessageToString(message, as_one_line=True)),
148 'repeated_int64: -9223372036854775808'
149 ' repeated_uint64: 18446744073709551615'
150 ' repeated_double: 123.456'
151 ' repeated_double: 1.23e+22'
152 ' repeated_double: 1.23e-18'
153 ' repeated_string: '
154 '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""'
155 ' repeated_string: "\\303\\274\\352\\234\\237"')
liujisi@google.com33165fe2010-11-02 13:14:58 +0000156
Jisi Liuada65562015-02-25 16:39:11 -0800157 def testRoundTripExoticAsOneLine(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
liujisi@google.com6f2c3b82010-12-09 08:59:21 +0000167 # Test as_utf8 = False.
168 wire_text = text_format.MessageToString(
169 message, as_one_line=True, as_utf8=False)
Jisi Liuada65562015-02-25 16:39:11 -0800170 parsed_message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000171 r = text_format.Parse(wire_text, parsed_message)
172 self.assertIs(r, parsed_message)
liujisi@google.com6f2c3b82010-12-09 08:59:21 +0000173 self.assertEquals(message, parsed_message)
174
175 # Test as_utf8 = True.
176 wire_text = text_format.MessageToString(
177 message, as_one_line=True, as_utf8=True)
Jisi Liuada65562015-02-25 16:39:11 -0800178 parsed_message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000179 r = text_format.Parse(wire_text, parsed_message)
180 self.assertIs(r, parsed_message)
181 self.assertEquals(message, parsed_message,
182 '\n%s != %s' % (message, parsed_message))
liujisi@google.com33165fe2010-11-02 13:14:58 +0000183
Jisi Liuada65562015-02-25 16:39:11 -0800184 def testPrintRawUtf8String(self, message_module):
185 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000186 message.repeated_string.append(u'\u00fc\ua71f')
jieluo@google.combde4a322014-08-12 21:10:30 +0000187 text = text_format.MessageToString(message, as_utf8=True)
liujisi@google.com6f2c3b82010-12-09 08:59:21 +0000188 self.CompareToGoldenText(text, 'repeated_string: "\303\274\352\234\237"\n')
Jisi Liuada65562015-02-25 16:39:11 -0800189 parsed_message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000190 text_format.Parse(text, parsed_message)
191 self.assertEquals(message, parsed_message,
192 '\n%s != %s' % (message, parsed_message))
193
Jisi Liuada65562015-02-25 16:39:11 -0800194 def testPrintFloatFormat(self, message_module):
jieluo@google.combde4a322014-08-12 21:10:30 +0000195 # Check that float_format argument is passed to sub-message formatting.
Jisi Liuada65562015-02-25 16:39:11 -0800196 message = message_module.NestedTestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000197 # We use 1.25 as it is a round number in binary. The proto 32-bit float
198 # will not gain additional imprecise digits as a 64-bit Python float and
199 # show up in its str. 32-bit 1.2 is noisy when extended to 64-bit:
200 # >>> struct.unpack('f', struct.pack('f', 1.2))[0]
201 # 1.2000000476837158
202 # >>> struct.unpack('f', struct.pack('f', 1.25))[0]
203 # 1.25
204 message.payload.optional_float = 1.25
205 # Check rounding at 15 significant digits
206 message.payload.optional_double = -.000003456789012345678
207 # Check no decimal point.
208 message.payload.repeated_float.append(-5642)
209 # Check no trailing zeros.
210 message.payload.repeated_double.append(.000078900)
211 formatted_fields = ['optional_float: 1.25',
212 'optional_double: -3.45678901234568e-6',
213 'repeated_float: -5642',
214 'repeated_double: 7.89e-5']
215 text_message = text_format.MessageToString(message, float_format='.15g')
216 self.CompareToGoldenText(
217 self.RemoveRedundantZeros(text_message),
218 'payload {{\n {}\n {}\n {}\n {}\n}}\n'.format(*formatted_fields))
219 # as_one_line=True is a separate code branch where float_format is passed.
220 text_message = text_format.MessageToString(message, as_one_line=True,
221 float_format='.15g')
222 self.CompareToGoldenText(
223 self.RemoveRedundantZeros(text_message),
224 'payload {{ {} {} {} {} }}'.format(*formatted_fields))
temporal40ee5512008-07-10 02:12:20 +0000225
Jisi Liuada65562015-02-25 16:39:11 -0800226 def testMessageToString(self, message_module):
227 message = message_module.ForeignMessage()
temporal40ee5512008-07-10 02:12:20 +0000228 message.c = 123
229 self.assertEqual('c: 123\n', str(message))
230
Jisi Liuada65562015-02-25 16:39:11 -0800231 def testParseAllFields(self, message_module):
232 message = message_module.TestAllTypes()
kenton@google.com80b1d622009-07-29 01:13:20 +0000233 test_util.SetAllFields(message)
234 ascii_text = text_format.MessageToString(message)
235
Jisi Liuada65562015-02-25 16:39:11 -0800236 parsed_message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000237 text_format.Parse(ascii_text, parsed_message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000238 self.assertEqual(message, parsed_message)
Jisi Liuada65562015-02-25 16:39:11 -0800239 if message_module is unittest_pb2:
240 test_util.ExpectAllFieldsSet(self, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000241
Jisi Liuada65562015-02-25 16:39:11 -0800242 def testParseExotic(self, message_module):
243 message = message_module.TestAllTypes()
kenton@google.com80b1d622009-07-29 01:13:20 +0000244 text = ('repeated_int64: -9223372036854775808\n'
245 'repeated_uint64: 18446744073709551615\n'
246 'repeated_double: 123.456\n'
247 'repeated_double: 1.23e+22\n'
248 'repeated_double: 1.23e-18\n'
249 'repeated_string: \n'
liujisi@google.com33165fe2010-11-02 13:14:58 +0000250 '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
251 'repeated_string: "foo" \'corge\' "grault"\n'
252 'repeated_string: "\\303\\274\\352\\234\\237"\n'
253 'repeated_string: "\\xc3\\xbc"\n'
254 'repeated_string: "\xc3\xbc"\n')
jieluo@google.combde4a322014-08-12 21:10:30 +0000255 text_format.Parse(text, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000256
257 self.assertEqual(-9223372036854775808, message.repeated_int64[0])
258 self.assertEqual(18446744073709551615, message.repeated_uint64[0])
259 self.assertEqual(123.456, message.repeated_double[0])
260 self.assertEqual(1.23e22, message.repeated_double[1])
261 self.assertEqual(1.23e-18, message.repeated_double[2])
262 self.assertEqual(
liujisi@google.com33165fe2010-11-02 13:14:58 +0000263 '\000\001\a\b\f\n\r\t\v\\\'"', message.repeated_string[0])
kenton@google.comeef5f832009-12-23 01:32:45 +0000264 self.assertEqual('foocorgegrault', message.repeated_string[1])
liujisi@google.com33165fe2010-11-02 13:14:58 +0000265 self.assertEqual(u'\u00fc\ua71f', message.repeated_string[2])
266 self.assertEqual(u'\u00fc', message.repeated_string[3])
267
Jisi Liuada65562015-02-25 16:39:11 -0800268 def testParseTrailingCommas(self, message_module):
269 message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000270 text = ('repeated_int64: 100;\n'
271 'repeated_int64: 200;\n'
272 'repeated_int64: 300,\n'
273 'repeated_string: "one",\n'
274 'repeated_string: "two";\n')
275 text_format.Parse(text, message)
276
277 self.assertEqual(100, message.repeated_int64[0])
278 self.assertEqual(200, message.repeated_int64[1])
279 self.assertEqual(300, message.repeated_int64[2])
280 self.assertEqual(u'one', message.repeated_string[0])
281 self.assertEqual(u'two', message.repeated_string[1])
282
Jisi Liuada65562015-02-25 16:39:11 -0800283 def testParseEmptyText(self, message_module):
284 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000285 text = ''
jieluo@google.combde4a322014-08-12 21:10:30 +0000286 text_format.Parse(text, message)
Jisi Liuada65562015-02-25 16:39:11 -0800287 self.assertEquals(message_module.TestAllTypes(), message)
liujisi@google.com33165fe2010-11-02 13:14:58 +0000288
Jisi Liuada65562015-02-25 16:39:11 -0800289 def testParseInvalidUtf8(self, message_module):
290 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000291 text = 'repeated_string: "\\xc3\\xc3"'
jieluo@google.combde4a322014-08-12 21:10:30 +0000292 self.assertRaises(text_format.ParseError, text_format.Parse, text, message)
liujisi@google.com33165fe2010-11-02 13:14:58 +0000293
Jisi Liuada65562015-02-25 16:39:11 -0800294 def testParseSingleWord(self, message_module):
295 message = message_module.TestAllTypes()
liujisi@google.com33165fe2010-11-02 13:14:58 +0000296 text = 'foo'
Jisi Liuada65562015-02-25 16:39:11 -0800297 self.assertRaisesRegexp(
liujisi@google.com33165fe2010-11-02 13:14:58 +0000298 text_format.ParseError,
Jisi Liuada65562015-02-25 16:39:11 -0800299 (r'1:1 : Message type "\w+.TestAllTypes" has no field named '
300 r'"foo".'),
jieluo@google.combde4a322014-08-12 21:10:30 +0000301 text_format.Parse, text, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000302
Jisi Liuada65562015-02-25 16:39:11 -0800303 def testParseUnknownField(self, message_module):
304 message = message_module.TestAllTypes()
kenton@google.com80b1d622009-07-29 01:13:20 +0000305 text = 'unknown_field: 8\n'
Jisi Liuada65562015-02-25 16:39:11 -0800306 self.assertRaisesRegexp(
kenton@google.com80b1d622009-07-29 01:13:20 +0000307 text_format.ParseError,
Jisi Liuada65562015-02-25 16:39:11 -0800308 (r'1:1 : Message type "\w+.TestAllTypes" has no field named '
309 r'"unknown_field".'),
jieluo@google.combde4a322014-08-12 21:10:30 +0000310 text_format.Parse, text, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000311
Jisi Liuada65562015-02-25 16:39:11 -0800312 def testParseGroupNotClosed(self, message_module):
313 message = message_module.TestAllTypes()
kenton@google.com80b1d622009-07-29 01:13:20 +0000314 text = 'RepeatedGroup: <'
jieluo@google.combde4a322014-08-12 21:10:30 +0000315 self.assertRaisesWithLiteralMatch(
kenton@google.com80b1d622009-07-29 01:13:20 +0000316 text_format.ParseError, '1:16 : Expected ">".',
jieluo@google.combde4a322014-08-12 21:10:30 +0000317 text_format.Parse, text, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000318
319 text = 'RepeatedGroup: {'
jieluo@google.combde4a322014-08-12 21:10:30 +0000320 self.assertRaisesWithLiteralMatch(
kenton@google.com80b1d622009-07-29 01:13:20 +0000321 text_format.ParseError, '1:16 : Expected "}".',
jieluo@google.combde4a322014-08-12 21:10:30 +0000322 text_format.Parse, text, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000323
Jisi Liuada65562015-02-25 16:39:11 -0800324 def testParseEmptyGroup(self, message_module):
325 message = message_module.TestAllTypes()
kenton@google.comfccb1462009-12-18 02:11:36 +0000326 text = 'OptionalGroup: {}'
jieluo@google.combde4a322014-08-12 21:10:30 +0000327 text_format.Parse(text, message)
kenton@google.comfccb1462009-12-18 02:11:36 +0000328 self.assertTrue(message.HasField('optionalgroup'))
329
330 message.Clear()
331
Jisi Liuada65562015-02-25 16:39:11 -0800332 message = message_module.TestAllTypes()
kenton@google.comfccb1462009-12-18 02:11:36 +0000333 text = 'OptionalGroup: <>'
jieluo@google.combde4a322014-08-12 21:10:30 +0000334 text_format.Parse(text, message)
kenton@google.comfccb1462009-12-18 02:11:36 +0000335 self.assertTrue(message.HasField('optionalgroup'))
336
Jisi Liuada65562015-02-25 16:39:11 -0800337 def testParseBadEnumValue(self, message_module):
338 message = message_module.TestAllTypes()
kenton@google.com80b1d622009-07-29 01:13:20 +0000339 text = 'optional_nested_enum: BARR'
Jisi Liuada65562015-02-25 16:39:11 -0800340 self.assertRaisesRegexp(
kenton@google.com80b1d622009-07-29 01:13:20 +0000341 text_format.ParseError,
Jisi Liuada65562015-02-25 16:39:11 -0800342 (r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
343 r'has no value named BARR.'),
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 message = message_module.TestAllTypes()
kenton@google.com80b1d622009-07-29 01:13:20 +0000347 text = 'optional_nested_enum: 100'
Jisi Liuada65562015-02-25 16:39:11 -0800348 self.assertRaisesRegexp(
kenton@google.com80b1d622009-07-29 01:13:20 +0000349 text_format.ParseError,
Jisi Liuada65562015-02-25 16:39:11 -0800350 (r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
351 r'has no value with number 100.'),
jieluo@google.combde4a322014-08-12 21:10:30 +0000352 text_format.Parse, text, message)
kenton@google.com80b1d622009-07-29 01:13:20 +0000353
Jisi Liuada65562015-02-25 16:39:11 -0800354 def testParseBadIntValue(self, message_module):
355 message = message_module.TestAllTypes()
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000356 text = 'optional_int32: bork'
jieluo@google.combde4a322014-08-12 21:10:30 +0000357 self.assertRaisesWithLiteralMatch(
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000358 text_format.ParseError,
359 ('1:17 : Couldn\'t parse integer: bork'),
jieluo@google.combde4a322014-08-12 21:10:30 +0000360 text_format.Parse, text, message)
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000361
Jisi Liuada65562015-02-25 16:39:11 -0800362 def testParseStringFieldUnescape(self, message_module):
363 message = message_module.TestAllTypes()
xiaofeng@google.com7f372552013-02-25 10:39:39 +0000364 text = r'''repeated_string: "\xf\x62"
365 repeated_string: "\\xf\\x62"
366 repeated_string: "\\\xf\\\x62"
367 repeated_string: "\\\\xf\\\\x62"
368 repeated_string: "\\\\\xf\\\\\x62"
369 repeated_string: "\x5cx20"'''
jieluo@google.combde4a322014-08-12 21:10:30 +0000370 text_format.Parse(text, message)
xiaofeng@google.com7f372552013-02-25 10:39:39 +0000371
372 SLASH = '\\'
373 self.assertEqual('\x0fb', message.repeated_string[0])
374 self.assertEqual(SLASH + 'xf' + SLASH + 'x62', message.repeated_string[1])
375 self.assertEqual(SLASH + '\x0f' + SLASH + 'b', message.repeated_string[2])
376 self.assertEqual(SLASH + SLASH + 'xf' + SLASH + SLASH + 'x62',
377 message.repeated_string[3])
378 self.assertEqual(SLASH + SLASH + '\x0f' + SLASH + SLASH + 'b',
379 message.repeated_string[4])
380 self.assertEqual(SLASH + 'x20', message.repeated_string[5])
381
Jisi Liuada65562015-02-25 16:39:11 -0800382 def testMergeDuplicateScalars(self, message_module):
383 message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000384 text = ('optional_int32: 42 '
385 'optional_int32: 67')
386 r = text_format.Merge(text, message)
387 self.assertIs(r, message)
388 self.assertEqual(67, message.optional_int32)
kenton@google.com80b1d622009-07-29 01:13:20 +0000389
Jisi Liuada65562015-02-25 16:39:11 -0800390 def testMergeDuplicateNestedMessageScalars(self, message_module):
391 message = message_module.TestAllTypes()
jieluo@google.combde4a322014-08-12 21:10:30 +0000392 text = ('optional_nested_message { bb: 1 } '
393 'optional_nested_message { bb: 2 }')
394 r = text_format.Merge(text, message)
395 self.assertTrue(r is message)
396 self.assertEqual(2, message.optional_nested_message.bb)
397
Jisi Liuada65562015-02-25 16:39:11 -0800398 def testParseOneof(self, message_module):
399 m = message_module.TestAllTypes()
400 m.oneof_uint32 = 11
401 m2 = message_module.TestAllTypes()
402 text_format.Parse(text_format.MessageToString(m), m2)
403 self.assertEqual('oneof_uint32', m2.WhichOneof('oneof_field'))
404
405
406# These are tests that aren't fundamentally specific to proto2, but are at
407# the moment because of differences between the proto2 and proto3 test schemas.
408# Ideally the schemas would be made more similar so these tests could pass.
409class OnlyWorksWithProto2RightNowTests(TextFormatBase):
410
411 def testParseGolden(self):
412 golden_text = '\n'.join(self.ReadGolden('text_format_unittest_data.txt'))
413 parsed_message = unittest_pb2.TestAllTypes()
414 r = text_format.Parse(golden_text, parsed_message)
415 self.assertIs(r, parsed_message)
416
jieluo@google.combde4a322014-08-12 21:10:30 +0000417 message = unittest_pb2.TestAllTypes()
Jisi Liuada65562015-02-25 16:39:11 -0800418 test_util.SetAllFields(message)
419 self.assertEquals(message, parsed_message)
420
421 def testPrintAllFields(self):
422 message = unittest_pb2.TestAllTypes()
423 test_util.SetAllFields(message)
424 self.CompareToGoldenFile(
425 self.RemoveRedundantZeros(text_format.MessageToString(message)),
426 'text_format_unittest_data_oneof_implemented.txt')
427
428 def testPrintAllFieldsPointy(self):
429 message = unittest_pb2.TestAllTypes()
430 test_util.SetAllFields(message)
431 self.CompareToGoldenFile(
432 self.RemoveRedundantZeros(
433 text_format.MessageToString(message, pointy_brackets=True)),
434 'text_format_unittest_data_pointy_oneof.txt')
435
436 def testPrintInIndexOrder(self):
437 message = unittest_pb2.TestFieldOrderings()
438 message.my_string = '115'
439 message.my_int = 101
440 message.my_float = 111
441 message.optional_nested_message.oo = 0
442 message.optional_nested_message.bb = 1
443 self.CompareToGoldenText(
444 self.RemoveRedundantZeros(text_format.MessageToString(
445 message, use_index_order=True)),
446 'my_string: \"115\"\nmy_int: 101\nmy_float: 111\n'
447 'optional_nested_message {\n oo: 0\n bb: 1\n}\n')
448 self.CompareToGoldenText(
449 self.RemoveRedundantZeros(text_format.MessageToString(
450 message)),
451 'my_int: 101\nmy_string: \"115\"\nmy_float: 111\n'
452 'optional_nested_message {\n bb: 1\n oo: 0\n}\n')
453
454 def testMergeLinesGolden(self):
455 opened = self.ReadGolden('text_format_unittest_data.txt')
456 parsed_message = unittest_pb2.TestAllTypes()
457 r = text_format.MergeLines(opened, parsed_message)
458 self.assertIs(r, parsed_message)
459
460 message = unittest_pb2.TestAllTypes()
461 test_util.SetAllFields(message)
462 self.assertEqual(message, parsed_message)
463
464 def testParseLinesGolden(self):
465 opened = self.ReadGolden('text_format_unittest_data.txt')
466 parsed_message = unittest_pb2.TestAllTypes()
467 r = text_format.ParseLines(opened, parsed_message)
468 self.assertIs(r, parsed_message)
469
470 message = unittest_pb2.TestAllTypes()
471 test_util.SetAllFields(message)
472 self.assertEquals(message, parsed_message)
473
474
475# Tests of proto2-only features (MessageSet and extensions).
476class Proto2Tests(TextFormatBase):
477
478 def testPrintMessageSet(self):
479 message = unittest_mset_pb2.TestMessageSetContainer()
480 ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
481 ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
482 message.message_set.Extensions[ext1].i = 23
483 message.message_set.Extensions[ext2].str = 'foo'
484 self.CompareToGoldenText(
485 text_format.MessageToString(message),
486 'message_set {\n'
487 ' [protobuf_unittest.TestMessageSetExtension1] {\n'
488 ' i: 23\n'
489 ' }\n'
490 ' [protobuf_unittest.TestMessageSetExtension2] {\n'
491 ' str: \"foo\"\n'
492 ' }\n'
493 '}\n')
494
495 def testPrintMessageSetAsOneLine(self):
496 message = unittest_mset_pb2.TestMessageSetContainer()
497 ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
498 ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
499 message.message_set.Extensions[ext1].i = 23
500 message.message_set.Extensions[ext2].str = 'foo'
501 self.CompareToGoldenText(
502 text_format.MessageToString(message, as_one_line=True),
503 'message_set {'
504 ' [protobuf_unittest.TestMessageSetExtension1] {'
505 ' i: 23'
506 ' }'
507 ' [protobuf_unittest.TestMessageSetExtension2] {'
508 ' str: \"foo\"'
509 ' }'
510 ' }')
511
512 def testParseMessageSet(self):
513 message = unittest_pb2.TestAllTypes()
514 text = ('repeated_uint64: 1\n'
515 'repeated_uint64: 2\n')
516 text_format.Parse(text, message)
517 self.assertEqual(1, message.repeated_uint64[0])
518 self.assertEqual(2, message.repeated_uint64[1])
519
520 message = unittest_mset_pb2.TestMessageSetContainer()
521 text = ('message_set {\n'
522 ' [protobuf_unittest.TestMessageSetExtension1] {\n'
523 ' i: 23\n'
524 ' }\n'
525 ' [protobuf_unittest.TestMessageSetExtension2] {\n'
526 ' str: \"foo\"\n'
527 ' }\n'
528 '}\n')
529 text_format.Parse(text, message)
530 ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
531 ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
532 self.assertEquals(23, message.message_set.Extensions[ext1].i)
533 self.assertEquals('foo', message.message_set.Extensions[ext2].str)
534
535 def testPrintAllExtensions(self):
536 message = unittest_pb2.TestAllExtensions()
537 test_util.SetAllExtensions(message)
538 self.CompareToGoldenFile(
539 self.RemoveRedundantZeros(text_format.MessageToString(message)),
540 'text_format_unittest_extensions_data.txt')
541
542 def testPrintAllExtensionsPointy(self):
543 message = unittest_pb2.TestAllExtensions()
544 test_util.SetAllExtensions(message)
545 self.CompareToGoldenFile(
546 self.RemoveRedundantZeros(text_format.MessageToString(
547 message, pointy_brackets=True)),
548 'text_format_unittest_extensions_data_pointy.txt')
549
550 def testParseGoldenExtensions(self):
551 golden_text = '\n'.join(self.ReadGolden(
552 'text_format_unittest_extensions_data.txt'))
553 parsed_message = unittest_pb2.TestAllExtensions()
554 text_format.Parse(golden_text, parsed_message)
555
556 message = unittest_pb2.TestAllExtensions()
557 test_util.SetAllExtensions(message)
558 self.assertEquals(message, parsed_message)
559
560 def testParseAllExtensions(self):
561 message = unittest_pb2.TestAllExtensions()
562 test_util.SetAllExtensions(message)
563 ascii_text = text_format.MessageToString(message)
564
565 parsed_message = unittest_pb2.TestAllExtensions()
566 text_format.Parse(ascii_text, parsed_message)
567 self.assertEqual(message, parsed_message)
568
569 def testParseBadExtension(self):
570 message = unittest_pb2.TestAllExtensions()
571 text = '[unknown_extension]: 8\n'
jieluo@google.combde4a322014-08-12 21:10:30 +0000572 self.assertRaisesWithLiteralMatch(
573 text_format.ParseError,
Jisi Liuada65562015-02-25 16:39:11 -0800574 '1:2 : Extension "unknown_extension" not registered.',
575 text_format.Parse, text, message)
576 message = unittest_pb2.TestAllTypes()
577 self.assertRaisesWithLiteralMatch(
578 text_format.ParseError,
579 ('1:2 : Message type "protobuf_unittest.TestAllTypes" does not have '
580 'extensions.'),
jieluo@google.combde4a322014-08-12 21:10:30 +0000581 text_format.Parse, text, message)
582
Feng Xiao6ef984a2014-11-10 17:34:54 -0800583 def testMergeDuplicateExtensionScalars(self):
jieluo@google.combde4a322014-08-12 21:10:30 +0000584 message = unittest_pb2.TestAllExtensions()
585 text = ('[protobuf_unittest.optional_int32_extension]: 42 '
586 '[protobuf_unittest.optional_int32_extension]: 67')
587 text_format.Merge(text, message)
588 self.assertEqual(
589 67,
590 message.Extensions[unittest_pb2.optional_int32_extension])
591
Feng Xiao6ef984a2014-11-10 17:34:54 -0800592 def testParseDuplicateExtensionScalars(self):
jieluo@google.combde4a322014-08-12 21:10:30 +0000593 message = unittest_pb2.TestAllExtensions()
594 text = ('[protobuf_unittest.optional_int32_extension]: 42 '
595 '[protobuf_unittest.optional_int32_extension]: 67')
596 self.assertRaisesWithLiteralMatch(
597 text_format.ParseError,
598 ('1:96 : Message type "protobuf_unittest.TestAllExtensions" '
599 'should not have multiple '
600 '"protobuf_unittest.optional_int32_extension" extensions.'),
601 text_format.Parse, text, message)
602
Jisi Liuada65562015-02-25 16:39:11 -0800603 def testParseDuplicateNestedMessageScalars(self):
jieluo@google.combde4a322014-08-12 21:10:30 +0000604 message = unittest_pb2.TestAllTypes()
Jisi Liuada65562015-02-25 16:39:11 -0800605 text = ('optional_nested_message { bb: 1 } '
606 'optional_nested_message { bb: 2 }')
607 self.assertRaisesWithLiteralMatch(
608 text_format.ParseError,
609 ('1:65 : Message type "protobuf_unittest.TestAllTypes.NestedMessage" '
610 'should not have multiple "bb" fields.'),
611 text_format.Parse, text, message)
jieluo@google.combde4a322014-08-12 21:10:30 +0000612
Jisi Liuada65562015-02-25 16:39:11 -0800613 def testParseDuplicateScalars(self):
jieluo@google.combde4a322014-08-12 21:10:30 +0000614 message = unittest_pb2.TestAllTypes()
Jisi Liuada65562015-02-25 16:39:11 -0800615 text = ('optional_int32: 42 '
616 'optional_int32: 67')
617 self.assertRaisesWithLiteralMatch(
618 text_format.ParseError,
619 ('1:36 : Message type "protobuf_unittest.TestAllTypes" should not '
620 'have multiple "optional_int32" fields.'),
621 text_format.Parse, text, message)
Feng Xiao0971bb02014-10-07 17:44:33 -0700622
kenton@google.com80b1d622009-07-29 01:13:20 +0000623
jieluo@google.combde4a322014-08-12 21:10:30 +0000624class TokenizerTest(basetest.TestCase):
kenton@google.com80b1d622009-07-29 01:13:20 +0000625
626 def testSimpleTokenCases(self):
627 text = ('identifier1:"string1"\n \n\n'
628 'identifier2 : \n \n123 \n identifier3 :\'string\'\n'
629 'identifiER_4 : 1.1e+2 ID5:-0.23 ID6:\'aaaa\\\'bbbb\'\n'
630 'ID7 : "aa\\"bb"\n\n\n\n ID8: {A:inf B:-inf C:true D:false}\n'
631 'ID9: 22 ID10: -111111111111111111 ID11: -22\n'
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000632 'ID12: 2222222222222222222 ID13: 1.23456f ID14: 1.2e+2f '
jieluo@google.combde4a322014-08-12 21:10:30 +0000633 'false_bool: 0 true_BOOL:t \n true_bool1: 1 false_BOOL1:f ')
634 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +0000635 methods = [(tokenizer.ConsumeIdentifier, 'identifier1'),
636 ':',
637 (tokenizer.ConsumeString, 'string1'),
638 (tokenizer.ConsumeIdentifier, 'identifier2'),
639 ':',
640 (tokenizer.ConsumeInt32, 123),
641 (tokenizer.ConsumeIdentifier, 'identifier3'),
642 ':',
643 (tokenizer.ConsumeString, 'string'),
644 (tokenizer.ConsumeIdentifier, 'identifiER_4'),
645 ':',
646 (tokenizer.ConsumeFloat, 1.1e+2),
647 (tokenizer.ConsumeIdentifier, 'ID5'),
648 ':',
649 (tokenizer.ConsumeFloat, -0.23),
650 (tokenizer.ConsumeIdentifier, 'ID6'),
651 ':',
652 (tokenizer.ConsumeString, 'aaaa\'bbbb'),
653 (tokenizer.ConsumeIdentifier, 'ID7'),
654 ':',
655 (tokenizer.ConsumeString, 'aa\"bb'),
656 (tokenizer.ConsumeIdentifier, 'ID8'),
657 ':',
658 '{',
659 (tokenizer.ConsumeIdentifier, 'A'),
660 ':',
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000661 (tokenizer.ConsumeFloat, float('inf')),
kenton@google.com80b1d622009-07-29 01:13:20 +0000662 (tokenizer.ConsumeIdentifier, 'B'),
663 ':',
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000664 (tokenizer.ConsumeFloat, -float('inf')),
kenton@google.com80b1d622009-07-29 01:13:20 +0000665 (tokenizer.ConsumeIdentifier, 'C'),
666 ':',
667 (tokenizer.ConsumeBool, True),
668 (tokenizer.ConsumeIdentifier, 'D'),
669 ':',
670 (tokenizer.ConsumeBool, False),
671 '}',
672 (tokenizer.ConsumeIdentifier, 'ID9'),
673 ':',
674 (tokenizer.ConsumeUint32, 22),
675 (tokenizer.ConsumeIdentifier, 'ID10'),
676 ':',
677 (tokenizer.ConsumeInt64, -111111111111111111),
678 (tokenizer.ConsumeIdentifier, 'ID11'),
679 ':',
680 (tokenizer.ConsumeInt32, -22),
681 (tokenizer.ConsumeIdentifier, 'ID12'),
682 ':',
liujisi@google.com33165fe2010-11-02 13:14:58 +0000683 (tokenizer.ConsumeUint64, 2222222222222222222),
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000684 (tokenizer.ConsumeIdentifier, 'ID13'),
685 ':',
686 (tokenizer.ConsumeFloat, 1.23456),
687 (tokenizer.ConsumeIdentifier, 'ID14'),
688 ':',
689 (tokenizer.ConsumeFloat, 1.2e+2),
liujisi@google.com33165fe2010-11-02 13:14:58 +0000690 (tokenizer.ConsumeIdentifier, 'false_bool'),
691 ':',
692 (tokenizer.ConsumeBool, False),
693 (tokenizer.ConsumeIdentifier, 'true_BOOL'),
694 ':',
695 (tokenizer.ConsumeBool, True),
696 (tokenizer.ConsumeIdentifier, 'true_bool1'),
697 ':',
698 (tokenizer.ConsumeBool, True),
699 (tokenizer.ConsumeIdentifier, 'false_BOOL1'),
700 ':',
701 (tokenizer.ConsumeBool, False)]
kenton@google.com80b1d622009-07-29 01:13:20 +0000702
703 i = 0
704 while not tokenizer.AtEnd():
705 m = methods[i]
706 if type(m) == str:
707 token = tokenizer.token
708 self.assertEqual(token, m)
709 tokenizer.NextToken()
710 else:
711 self.assertEqual(m[1], m[0]())
712 i += 1
713
714 def testConsumeIntegers(self):
715 # This test only tests the failures in the integer parsing methods as well
716 # as the '0' special cases.
717 int64_max = (1 << 63) - 1
718 uint32_max = (1 << 32) - 1
719 text = '-1 %d %d' % (uint32_max + 1, int64_max + 1)
jieluo@google.combde4a322014-08-12 21:10:30 +0000720 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +0000721 self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint32)
722 self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint64)
723 self.assertEqual(-1, tokenizer.ConsumeInt32())
724
725 self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint32)
726 self.assertRaises(text_format.ParseError, tokenizer.ConsumeInt32)
727 self.assertEqual(uint32_max + 1, tokenizer.ConsumeInt64())
728
729 self.assertRaises(text_format.ParseError, tokenizer.ConsumeInt64)
730 self.assertEqual(int64_max + 1, tokenizer.ConsumeUint64())
731 self.assertTrue(tokenizer.AtEnd())
732
733 text = '-0 -0 0 0'
jieluo@google.combde4a322014-08-12 21:10:30 +0000734 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +0000735 self.assertEqual(0, tokenizer.ConsumeUint32())
736 self.assertEqual(0, tokenizer.ConsumeUint64())
737 self.assertEqual(0, tokenizer.ConsumeUint32())
738 self.assertEqual(0, tokenizer.ConsumeUint64())
739 self.assertTrue(tokenizer.AtEnd())
740
741 def testConsumeByteString(self):
742 text = '"string1\''
jieluo@google.combde4a322014-08-12 21:10:30 +0000743 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +0000744 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
745
746 text = 'string1"'
jieluo@google.combde4a322014-08-12 21:10:30 +0000747 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +0000748 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
749
750 text = '\n"\\xt"'
jieluo@google.combde4a322014-08-12 21:10:30 +0000751 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +0000752 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
753
754 text = '\n"\\"'
jieluo@google.combde4a322014-08-12 21:10:30 +0000755 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +0000756 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
757
758 text = '\n"\\x"'
jieluo@google.combde4a322014-08-12 21:10:30 +0000759 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +0000760 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
761
762 def testConsumeBool(self):
763 text = 'not-a-bool'
jieluo@google.combde4a322014-08-12 21:10:30 +0000764 tokenizer = text_format._Tokenizer(text.splitlines())
kenton@google.com80b1d622009-07-29 01:13:20 +0000765 self.assertRaises(text_format.ParseError, tokenizer.ConsumeBool)
766
767
temporal40ee5512008-07-10 02:12:20 +0000768if __name__ == '__main__':
jieluo@google.combde4a322014-08-12 21:10:30 +0000769 basetest.main()