1# -*- coding: utf-8 -*- 2# Protocol Buffers - Google's data interchange format 3# Copyright 2008 Google Inc. All rights reserved. 4# https://developers.google.com/protocol-buffers/ 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are 8# met: 9# 10# * Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# * Redistributions in binary form must reproduce the above 13# copyright notice, this list of conditions and the following disclaimer 14# in the documentation and/or other materials provided with the 15# distribution. 16# * Neither the name of Google Inc. nor the names of its 17# contributors may be used to endorse or promote products derived from 18# this software without specific prior written permission. 19# 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 32"""Test for google.protobuf.text_format.""" 33 34import io 35import math 36import re 37import string 38import textwrap 39 40import unittest 41 42from google.protobuf import any_pb2 43from google.protobuf import struct_pb2 44from google.protobuf import any_test_pb2 45from google.protobuf import map_unittest_pb2 46from google.protobuf import unittest_custom_options_pb2 47from google.protobuf import unittest_mset_pb2 48from google.protobuf import unittest_pb2 49from google.protobuf import unittest_proto3_arena_pb2 50from google.protobuf import descriptor_pb2 51from google.protobuf.internal import any_test_pb2 as test_extend_any 52from google.protobuf.internal import api_implementation 53from google.protobuf.internal import message_set_extensions_pb2 54from google.protobuf.internal import test_proto3_optional_pb2 55from google.protobuf.internal import test_util 56from google.protobuf import descriptor_pool 57from google.protobuf import text_format 58from google.protobuf.internal import _parameterized 59# pylint: enable=g-import-not-at-top 60 61 62# Low-level nuts-n-bolts tests. 63class SimpleTextFormatTests(unittest.TestCase): 64 65 # The members of _QUOTES are formatted into a regexp template that 66 # expects single characters. Therefore it's an error (in addition to being 67 # non-sensical in the first place) to try to specify a "quote mark" that is 68 # more than one character. 69 def testQuoteMarksAreSingleChars(self): 70 for quote in text_format._QUOTES: 71 self.assertEqual(1, len(quote)) 72 73 74# Base class with some common functionality. 75class TextFormatBase(unittest.TestCase): 76 77 def ReadGolden(self, golden_filename): 78 with test_util.GoldenFile(golden_filename) as f: 79 return (f.readlines() if str is bytes else # PY3 80 [golden_line.decode('utf-8') for golden_line in f]) 81 82 def CompareToGoldenFile(self, text, golden_filename): 83 golden_lines = self.ReadGolden(golden_filename) 84 self.assertMultiLineEqual(text, ''.join(golden_lines)) 85 86 def CompareToGoldenText(self, text, golden_text): 87 self.assertEqual(text, golden_text) 88 89 def RemoveRedundantZeros(self, text): 90 # Some platforms print 1e+5 as 1e+005. This is fine, but we need to remove 91 # these zeros in order to match the golden file. 92 text = text.replace('e+0','e+').replace('e+0','e+') \ 93 .replace('e-0','e-').replace('e-0','e-') 94 # Floating point fields are printed with .0 suffix even if they are 95 # actually integer numbers. 96 text = re.compile(r'\.0$', re.MULTILINE).sub('', text) 97 return text 98 99 100@_parameterized.parameters(unittest_pb2, unittest_proto3_arena_pb2) 101class TextFormatMessageToStringTests(TextFormatBase): 102 103 def testPrintExotic(self, message_module): 104 message = message_module.TestAllTypes() 105 message.repeated_int64.append(-9223372036854775808) 106 message.repeated_uint64.append(18446744073709551615) 107 message.repeated_double.append(123.456) 108 message.repeated_double.append(1.23e22) 109 message.repeated_double.append(1.23e-18) 110 message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"') 111 message.repeated_string.append(u'\u00fc\ua71f') 112 self.CompareToGoldenText( 113 self.RemoveRedundantZeros(text_format.MessageToString(message)), 114 'repeated_int64: -9223372036854775808\n' 115 'repeated_uint64: 18446744073709551615\n' 116 'repeated_double: 123.456\n' 117 'repeated_double: 1.23e+22\n' 118 'repeated_double: 1.23e-18\n' 119 'repeated_string:' 120 ' "\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n' 121 'repeated_string: "\\303\\274\\352\\234\\237"\n') 122 123 def testPrintFloatPrecision(self, message_module): 124 message = message_module.TestAllTypes() 125 126 message.repeated_float.append(0.0) 127 message.repeated_float.append(0.8) 128 message.repeated_float.append(1.0) 129 message.repeated_float.append(1.2) 130 message.repeated_float.append(1.23) 131 message.repeated_float.append(1.234) 132 message.repeated_float.append(1.2345) 133 message.repeated_float.append(1.23456) 134 message.repeated_float.append(1.2e10) 135 message.repeated_float.append(1.23e10) 136 message.repeated_float.append(1.234e10) 137 message.repeated_float.append(1.2345e10) 138 message.repeated_float.append(1.23456e10) 139 message.repeated_float.append(float('NaN')) 140 message.repeated_float.append(float('inf')) 141 message.repeated_double.append(0.0) 142 message.repeated_double.append(0.8) 143 message.repeated_double.append(1.0) 144 message.repeated_double.append(1.2) 145 message.repeated_double.append(1.23) 146 message.repeated_double.append(1.234) 147 message.repeated_double.append(1.2345) 148 message.repeated_double.append(1.23456) 149 message.repeated_double.append(1.234567) 150 message.repeated_double.append(1.2345678) 151 message.repeated_double.append(1.23456789) 152 message.repeated_double.append(1.234567898) 153 message.repeated_double.append(1.2345678987) 154 message.repeated_double.append(1.23456789876) 155 message.repeated_double.append(1.234567898765) 156 message.repeated_double.append(1.2345678987654) 157 message.repeated_double.append(1.23456789876543) 158 message.repeated_double.append(1.2e100) 159 message.repeated_double.append(1.23e100) 160 message.repeated_double.append(1.234e100) 161 message.repeated_double.append(1.2345e100) 162 message.repeated_double.append(1.23456e100) 163 message.repeated_double.append(1.234567e100) 164 message.repeated_double.append(1.2345678e100) 165 message.repeated_double.append(1.23456789e100) 166 message.repeated_double.append(1.234567898e100) 167 message.repeated_double.append(1.2345678987e100) 168 message.repeated_double.append(1.23456789876e100) 169 message.repeated_double.append(1.234567898765e100) 170 message.repeated_double.append(1.2345678987654e100) 171 message.repeated_double.append(1.23456789876543e100) 172 # pylint: disable=g-long-ternary 173 self.CompareToGoldenText( 174 self.RemoveRedundantZeros(text_format.MessageToString(message)), 175 'repeated_float: 0\n' 176 'repeated_float: 0.8\n' 177 'repeated_float: 1\n' 178 'repeated_float: 1.2\n' 179 'repeated_float: 1.23\n' 180 'repeated_float: 1.234\n' 181 'repeated_float: 1.2345\n' 182 'repeated_float: 1.23456\n' 183 # Note that these don't use scientific notation. 184 'repeated_float: 12000000000\n' 185 'repeated_float: 12300000000\n' 186 'repeated_float: 12340000000\n' 187 'repeated_float: 12345000000\n' 188 'repeated_float: 12345600000\n' 189 'repeated_float: nan\n' 190 'repeated_float: inf\n' 191 'repeated_double: 0\n' 192 'repeated_double: 0.8\n' 193 'repeated_double: 1\n' 194 'repeated_double: 1.2\n' 195 'repeated_double: 1.23\n' 196 'repeated_double: 1.234\n' 197 'repeated_double: 1.2345\n' 198 'repeated_double: 1.23456\n' 199 'repeated_double: 1.234567\n' 200 'repeated_double: 1.2345678\n' 201 'repeated_double: 1.23456789\n' 202 'repeated_double: 1.234567898\n' 203 'repeated_double: 1.2345678987\n' 204 'repeated_double: 1.23456789876\n' 205 'repeated_double: 1.234567898765\n' 206 'repeated_double: 1.2345678987654\n' 207 'repeated_double: 1.23456789876543\n' 208 'repeated_double: 1.2e+100\n' 209 'repeated_double: 1.23e+100\n' 210 'repeated_double: 1.234e+100\n' 211 'repeated_double: 1.2345e+100\n' 212 'repeated_double: 1.23456e+100\n' 213 'repeated_double: 1.234567e+100\n' 214 'repeated_double: 1.2345678e+100\n' 215 'repeated_double: 1.23456789e+100\n' 216 'repeated_double: 1.234567898e+100\n' 217 'repeated_double: 1.2345678987e+100\n' 218 'repeated_double: 1.23456789876e+100\n' 219 'repeated_double: 1.234567898765e+100\n' 220 'repeated_double: 1.2345678987654e+100\n' 221 'repeated_double: 1.23456789876543e+100\n') 222 223 def testPrintExoticUnicodeSubclass(self, message_module): 224 225 class UnicodeSub(str): 226 pass 227 228 message = message_module.TestAllTypes() 229 message.repeated_string.append(UnicodeSub(u'\u00fc\ua71f')) 230 self.CompareToGoldenText( 231 text_format.MessageToString(message), 232 'repeated_string: "\\303\\274\\352\\234\\237"\n') 233 234 def testPrintNestedMessageAsOneLine(self, message_module): 235 message = message_module.TestAllTypes() 236 msg = message.repeated_nested_message.add() 237 msg.bb = 42 238 self.CompareToGoldenText( 239 text_format.MessageToString(message, as_one_line=True), 240 'repeated_nested_message { bb: 42 }') 241 242 def testPrintRepeatedFieldsAsOneLine(self, message_module): 243 message = message_module.TestAllTypes() 244 message.repeated_int32.append(1) 245 message.repeated_int32.append(1) 246 message.repeated_int32.append(3) 247 message.repeated_string.append('Google') 248 message.repeated_string.append('Zurich') 249 self.CompareToGoldenText( 250 text_format.MessageToString(message, as_one_line=True), 251 'repeated_int32: 1 repeated_int32: 1 repeated_int32: 3 ' 252 'repeated_string: "Google" repeated_string: "Zurich"') 253 254 def VerifyPrintShortFormatRepeatedFields(self, message_module, as_one_line): 255 message = message_module.TestAllTypes() 256 message.repeated_int32.append(1) 257 message.repeated_string.append('Google') 258 message.repeated_string.append('Hello,World') 259 message.repeated_foreign_enum.append(unittest_pb2.FOREIGN_FOO) 260 message.repeated_foreign_enum.append(unittest_pb2.FOREIGN_BAR) 261 message.repeated_foreign_enum.append(unittest_pb2.FOREIGN_BAZ) 262 message.optional_nested_message.bb = 3 263 for i in (21, 32): 264 msg = message.repeated_nested_message.add() 265 msg.bb = i 266 expected_ascii = ( 267 'optional_nested_message {\n bb: 3\n}\n' 268 'repeated_int32: [1]\n' 269 'repeated_string: "Google"\n' 270 'repeated_string: "Hello,World"\n' 271 'repeated_nested_message {\n bb: 21\n}\n' 272 'repeated_nested_message {\n bb: 32\n}\n' 273 'repeated_foreign_enum: [FOREIGN_FOO, FOREIGN_BAR, FOREIGN_BAZ]\n') 274 if as_one_line: 275 expected_ascii = expected_ascii.replace('\n', ' ') 276 expected_ascii = re.sub(r'\s+', ' ', expected_ascii) 277 expected_ascii = re.sub(r'\s$', '', expected_ascii) 278 279 actual_ascii = text_format.MessageToString( 280 message, use_short_repeated_primitives=True, 281 as_one_line=as_one_line) 282 self.CompareToGoldenText(actual_ascii, expected_ascii) 283 parsed_message = message_module.TestAllTypes() 284 text_format.Parse(actual_ascii, parsed_message) 285 self.assertEqual(parsed_message, message) 286 287 def testPrintShortFormatRepeatedFields(self, message_module): 288 self.VerifyPrintShortFormatRepeatedFields(message_module, False) 289 self.VerifyPrintShortFormatRepeatedFields(message_module, True) 290 291 def testPrintNestedNewLineInStringAsOneLine(self, message_module): 292 message = message_module.TestAllTypes() 293 message.optional_string = 'a\nnew\nline' 294 self.CompareToGoldenText( 295 text_format.MessageToString(message, as_one_line=True), 296 'optional_string: "a\\nnew\\nline"') 297 298 def testPrintExoticAsOneLine(self, message_module): 299 message = message_module.TestAllTypes() 300 message.repeated_int64.append(-9223372036854775808) 301 message.repeated_uint64.append(18446744073709551615) 302 message.repeated_double.append(123.456) 303 message.repeated_double.append(1.23e22) 304 message.repeated_double.append(1.23e-18) 305 message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"') 306 message.repeated_string.append(u'\u00fc\ua71f') 307 self.CompareToGoldenText( 308 self.RemoveRedundantZeros(text_format.MessageToString( 309 message, as_one_line=True)), 310 'repeated_int64: -9223372036854775808' 311 ' repeated_uint64: 18446744073709551615' 312 ' repeated_double: 123.456' 313 ' repeated_double: 1.23e+22' 314 ' repeated_double: 1.23e-18' 315 ' repeated_string: ' 316 '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""' 317 ' repeated_string: "\\303\\274\\352\\234\\237"') 318 319 def testRoundTripExoticAsOneLine(self, message_module): 320 message = message_module.TestAllTypes() 321 message.repeated_int64.append(-9223372036854775808) 322 message.repeated_uint64.append(18446744073709551615) 323 message.repeated_double.append(123.456) 324 message.repeated_double.append(1.23e22) 325 message.repeated_double.append(1.23e-18) 326 message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"') 327 message.repeated_string.append(u'\u00fc\ua71f') 328 329 # Test as_utf8 = False. 330 wire_text = text_format.MessageToString(message, 331 as_one_line=True, 332 as_utf8=False) 333 parsed_message = message_module.TestAllTypes() 334 r = text_format.Parse(wire_text, parsed_message) 335 self.assertIs(r, parsed_message) 336 self.assertEqual(message, parsed_message) 337 338 # Test as_utf8 = True. 339 wire_text = text_format.MessageToString(message, 340 as_one_line=True, 341 as_utf8=True) 342 parsed_message = message_module.TestAllTypes() 343 r = text_format.Parse(wire_text, parsed_message) 344 self.assertIs(r, parsed_message) 345 self.assertEqual(message, parsed_message, 346 '\n%s != %s' % (message, parsed_message)) 347 348 def testPrintRawUtf8String(self, message_module): 349 message = message_module.TestAllTypes() 350 message.repeated_string.append(u'\u00fc\t\ua71f') 351 text = text_format.MessageToString(message, as_utf8=True) 352 golden_unicode = u'repeated_string: "\u00fc\\t\ua71f"\n' 353 golden_text = golden_unicode 354 # MessageToString always returns a native str. 355 self.CompareToGoldenText(text, golden_text) 356 parsed_message = message_module.TestAllTypes() 357 text_format.Parse(text, parsed_message) 358 self.assertEqual( 359 message, parsed_message, '\n%s != %s (%s != %s)' % 360 (message, parsed_message, message.repeated_string[0], 361 parsed_message.repeated_string[0])) 362 363 def testPrintFloatFormat(self, message_module): 364 # Check that float_format argument is passed to sub-message formatting. 365 message = message_module.NestedTestAllTypes() 366 message.payload.optional_float = 1.25 367 # Check rounding at 15 significant digits 368 message.payload.optional_double = -.000003456789012345678 369 # Check no decimal point. 370 message.payload.repeated_float.append(-5642) 371 # Check no trailing zeros. 372 message.payload.repeated_double.append(.000078900) 373 formatted_fields = ['optional_float: 1.25', 374 'optional_double: -3.45678901234568e-6', 375 'repeated_float: -5642', 'repeated_double: 7.89e-5'] 376 text_message = text_format.MessageToString(message, float_format='.15g') 377 self.CompareToGoldenText( 378 self.RemoveRedundantZeros(text_message), 379 'payload {{\n {0}\n {1}\n {2}\n {3}\n}}\n'.format( 380 *formatted_fields)) 381 # as_one_line=True is a separate code branch where float_format is passed. 382 text_message = text_format.MessageToString(message, 383 as_one_line=True, 384 float_format='.15g') 385 self.CompareToGoldenText( 386 self.RemoveRedundantZeros(text_message), 387 'payload {{ {0} {1} {2} {3} }}'.format(*formatted_fields)) 388 389 # 32-bit 1.2 is noisy when extended to 64-bit: 390 # >>> struct.unpack('f', struct.pack('f', 1.2))[0] 391 # 1.2000000476837158 392 message.payload.optional_float = 1.2 393 formatted_fields = ['optional_float: 1.2', 394 'optional_double: -3.45678901234568e-6', 395 'repeated_float: -5642', 'repeated_double: 7.89e-5'] 396 text_message = text_format.MessageToString(message, float_format='.7g', 397 double_format='.15g') 398 self.CompareToGoldenText( 399 self.RemoveRedundantZeros(text_message), 400 'payload {{\n {0}\n {1}\n {2}\n {3}\n}}\n'.format( 401 *formatted_fields)) 402 403 # Test only set float_format affect both float and double fields. 404 formatted_fields = ['optional_float: 1.2', 405 'optional_double: -3.456789e-6', 406 'repeated_float: -5642', 'repeated_double: 7.89e-5'] 407 text_message = text_format.MessageToString(message, float_format='.7g') 408 self.CompareToGoldenText( 409 self.RemoveRedundantZeros(text_message), 410 'payload {{\n {0}\n {1}\n {2}\n {3}\n}}\n'.format( 411 *formatted_fields)) 412 413 # Test default float_format will automatic print shortest float. 414 message.payload.optional_float = 1.2345678912 415 message.payload.optional_double = 1.2345678912 416 formatted_fields = ['optional_float: 1.2345679', 417 'optional_double: 1.2345678912', 418 'repeated_float: -5642', 'repeated_double: 7.89e-5'] 419 text_message = text_format.MessageToString(message) 420 self.CompareToGoldenText( 421 self.RemoveRedundantZeros(text_message), 422 'payload {{\n {0}\n {1}\n {2}\n {3}\n}}\n'.format( 423 *formatted_fields)) 424 425 message.Clear() 426 message.payload.optional_float = 1.1000000000011 427 self.assertEqual(text_format.MessageToString(message), 428 'payload {\n optional_float: 1.1\n}\n') 429 message.payload.optional_float = 1.00000075e-36 430 self.assertEqual(text_format.MessageToString(message), 431 'payload {\n optional_float: 1.00000075e-36\n}\n') 432 message.payload.optional_float = 12345678912345e+11 433 self.assertEqual(text_format.MessageToString(message), 434 'payload {\n optional_float: 1.234568e+24\n}\n') 435 436 def testMessageToString(self, message_module): 437 message = message_module.ForeignMessage() 438 message.c = 123 439 self.assertEqual('c: 123\n', str(message)) 440 441 def testMessageToStringUnicode(self, message_module): 442 golden_unicode = u'Á short desçription and a .' 443 golden_bytes = golden_unicode.encode('utf-8') 444 message = message_module.TestAllTypes() 445 message.optional_string = golden_unicode 446 message.optional_bytes = golden_bytes 447 text = text_format.MessageToString(message, as_utf8=True) 448 golden_message = textwrap.dedent( 449 'optional_string: "Á short desçription and a ."\n' 450 'optional_bytes: ' 451 r'"\303\201 short des\303\247ription and a \360\237\215\214."' 452 '\n') 453 self.CompareToGoldenText(text, golden_message) 454 455 def testMessageToStringASCII(self, message_module): 456 golden_unicode = u'Á short desçription and a .' 457 golden_bytes = golden_unicode.encode('utf-8') 458 message = message_module.TestAllTypes() 459 message.optional_string = golden_unicode 460 message.optional_bytes = golden_bytes 461 text = text_format.MessageToString(message, as_utf8=False) # ASCII 462 golden_message = ( 463 'optional_string: ' 464 r'"\303\201 short des\303\247ription and a \360\237\215\214."' 465 '\n' 466 'optional_bytes: ' 467 r'"\303\201 short des\303\247ription and a \360\237\215\214."' 468 '\n') 469 self.CompareToGoldenText(text, golden_message) 470 471 def testPrintField(self, message_module): 472 message = message_module.TestAllTypes() 473 field = message.DESCRIPTOR.fields_by_name['optional_float'] 474 value = message.optional_float 475 out = text_format.TextWriter(False) 476 text_format.PrintField(field, value, out) 477 self.assertEqual('optional_float: 0.0\n', out.getvalue()) 478 out.close() 479 # Test Printer 480 out = text_format.TextWriter(False) 481 printer = text_format._Printer(out) 482 printer.PrintField(field, value) 483 self.assertEqual('optional_float: 0.0\n', out.getvalue()) 484 out.close() 485 486 def testPrintFieldValue(self, message_module): 487 message = message_module.TestAllTypes() 488 field = message.DESCRIPTOR.fields_by_name['optional_float'] 489 value = message.optional_float 490 out = text_format.TextWriter(False) 491 text_format.PrintFieldValue(field, value, out) 492 self.assertEqual('0.0', out.getvalue()) 493 out.close() 494 # Test Printer 495 out = text_format.TextWriter(False) 496 printer = text_format._Printer(out) 497 printer.PrintFieldValue(field, value) 498 self.assertEqual('0.0', out.getvalue()) 499 out.close() 500 501 def testCustomOptions(self, message_module): 502 message_descriptor = (unittest_custom_options_pb2. 503 TestMessageWithCustomOptions.DESCRIPTOR) 504 message_proto = descriptor_pb2.DescriptorProto() 505 message_descriptor.CopyToProto(message_proto) 506 expected_text = ( 507 'name: "TestMessageWithCustomOptions"\n' 508 'field {\n' 509 ' name: "field1"\n' 510 ' number: 1\n' 511 ' label: LABEL_OPTIONAL\n' 512 ' type: TYPE_STRING\n' 513 ' options {\n' 514 ' ctype: CORD\n' 515 ' [protobuf_unittest.field_opt1]: 8765432109\n' 516 ' }\n' 517 '}\n' 518 'field {\n' 519 ' name: "oneof_field"\n' 520 ' number: 2\n' 521 ' label: LABEL_OPTIONAL\n' 522 ' type: TYPE_INT32\n' 523 ' oneof_index: 0\n' 524 '}\n' 525 'field {\n' 526 ' name: "map_field"\n' 527 ' number: 3\n' 528 ' label: LABEL_REPEATED\n' 529 ' type: TYPE_MESSAGE\n' 530 ' type_name: ".protobuf_unittest.TestMessageWithCustomOptions.' 531 'MapFieldEntry"\n' 532 ' options {\n' 533 ' [protobuf_unittest.field_opt1]: 12345\n' 534 ' }\n' 535 '}\n' 536 'nested_type {\n' 537 ' name: "MapFieldEntry"\n' 538 ' field {\n' 539 ' name: "key"\n' 540 ' number: 1\n' 541 ' label: LABEL_OPTIONAL\n' 542 ' type: TYPE_STRING\n' 543 ' }\n' 544 ' field {\n' 545 ' name: "value"\n' 546 ' number: 2\n' 547 ' label: LABEL_OPTIONAL\n' 548 ' type: TYPE_STRING\n' 549 ' }\n' 550 ' options {\n' 551 ' map_entry: true\n' 552 ' }\n' 553 '}\n' 554 'enum_type {\n' 555 ' name: "AnEnum"\n' 556 ' value {\n' 557 ' name: "ANENUM_VAL1"\n' 558 ' number: 1\n' 559 ' }\n' 560 ' value {\n' 561 ' name: "ANENUM_VAL2"\n' 562 ' number: 2\n' 563 ' options {\n' 564 ' [protobuf_unittest.enum_value_opt1]: 123\n' 565 ' }\n' 566 ' }\n' 567 ' options {\n' 568 ' [protobuf_unittest.enum_opt1]: -789\n' 569 ' }\n' 570 '}\n' 571 'options {\n' 572 ' message_set_wire_format: false\n' 573 ' [protobuf_unittest.message_opt1]: -56\n' 574 '}\n' 575 'oneof_decl {\n' 576 ' name: "AnOneof"\n' 577 ' options {\n' 578 ' [protobuf_unittest.oneof_opt1]: -99\n' 579 ' }\n' 580 '}\n') 581 self.assertEqual(expected_text, 582 text_format.MessageToString(message_proto)) 583 parsed_proto = descriptor_pb2.DescriptorProto() 584 text_format.Parse(expected_text, parsed_proto) 585 self.assertEqual(message_proto, parsed_proto) 586 587 @unittest.skipIf( 588 api_implementation.Type() == 'upb', 589 "upb API doesn't support old UnknownField API. The TextFormat library " 590 "needs to convert to the new API.") 591 def testPrintUnknownFieldsEmbeddedMessageInBytes(self, message_module): 592 inner_msg = message_module.TestAllTypes() 593 inner_msg.optional_int32 = 101 594 inner_msg.optional_double = 102.0 595 inner_msg.optional_string = u'hello' 596 inner_msg.optional_bytes = b'103' 597 inner_msg.optional_nested_message.bb = 105 598 inner_data = inner_msg.SerializeToString() 599 outer_message = message_module.TestAllTypes() 600 outer_message.optional_int32 = 101 601 outer_message.optional_bytes = inner_data 602 all_data = outer_message.SerializeToString() 603 empty_message = message_module.TestEmptyMessage() 604 empty_message.ParseFromString(all_data) 605 606 self.assertEqual(' 1: 101\n' 607 ' 15 {\n' 608 ' 1: 101\n' 609 ' 12: 4636878028842991616\n' 610 ' 14: "hello"\n' 611 ' 15: "103"\n' 612 ' 18 {\n' 613 ' 1: 105\n' 614 ' }\n' 615 ' }\n', 616 text_format.MessageToString(empty_message, 617 indent=2, 618 print_unknown_fields=True)) 619 self.assertEqual('1: 101 ' 620 '15 { ' 621 '1: 101 ' 622 '12: 4636878028842991616 ' 623 '14: "hello" ' 624 '15: "103" ' 625 '18 { 1: 105 } ' 626 '}', 627 text_format.MessageToString(empty_message, 628 print_unknown_fields=True, 629 as_one_line=True)) 630 631 632@_parameterized.parameters(unittest_pb2, unittest_proto3_arena_pb2) 633class TextFormatMessageToTextBytesTests(TextFormatBase): 634 635 def testMessageToBytes(self, message_module): 636 message = message_module.ForeignMessage() 637 message.c = 123 638 self.assertEqual(b'c: 123\n', text_format.MessageToBytes(message)) 639 640 def testRawUtf8RoundTrip(self, message_module): 641 message = message_module.TestAllTypes() 642 message.repeated_string.append(u'\u00fc\t\ua71f') 643 utf8_text = text_format.MessageToBytes(message, as_utf8=True) 644 golden_bytes = b'repeated_string: "\xc3\xbc\\t\xea\x9c\x9f"\n' 645 self.CompareToGoldenText(utf8_text, golden_bytes) 646 parsed_message = message_module.TestAllTypes() 647 text_format.Parse(utf8_text, parsed_message) 648 self.assertEqual( 649 message, parsed_message, '\n%s != %s (%s != %s)' % 650 (message, parsed_message, message.repeated_string[0], 651 parsed_message.repeated_string[0])) 652 653 def testEscapedUtf8ASCIIRoundTrip(self, message_module): 654 message = message_module.TestAllTypes() 655 message.repeated_string.append(u'\u00fc\t\ua71f') 656 ascii_text = text_format.MessageToBytes(message) # as_utf8=False default 657 golden_bytes = b'repeated_string: "\\303\\274\\t\\352\\234\\237"\n' 658 self.CompareToGoldenText(ascii_text, golden_bytes) 659 parsed_message = message_module.TestAllTypes() 660 text_format.Parse(ascii_text, parsed_message) 661 self.assertEqual( 662 message, parsed_message, '\n%s != %s (%s != %s)' % 663 (message, parsed_message, message.repeated_string[0], 664 parsed_message.repeated_string[0])) 665 666 667@_parameterized.parameters(unittest_pb2, unittest_proto3_arena_pb2) 668class TextFormatParserTests(TextFormatBase): 669 670 def testParseAllFields(self, message_module): 671 message = message_module.TestAllTypes() 672 test_util.SetAllFields(message) 673 ascii_text = text_format.MessageToString(message) 674 675 parsed_message = message_module.TestAllTypes() 676 text_format.Parse(ascii_text, parsed_message) 677 self.assertEqual(message, parsed_message) 678 if message_module is unittest_pb2: 679 test_util.ExpectAllFieldsSet(self, message) 680 681 def testParseAndMergeUtf8(self, message_module): 682 message = message_module.TestAllTypes() 683 test_util.SetAllFields(message) 684 ascii_text = text_format.MessageToString(message) 685 ascii_text = ascii_text.encode('utf-8') 686 687 parsed_message = message_module.TestAllTypes() 688 text_format.Parse(ascii_text, parsed_message) 689 self.assertEqual(message, parsed_message) 690 if message_module is unittest_pb2: 691 test_util.ExpectAllFieldsSet(self, message) 692 693 parsed_message.Clear() 694 text_format.Merge(ascii_text, parsed_message) 695 self.assertEqual(message, parsed_message) 696 if message_module is unittest_pb2: 697 test_util.ExpectAllFieldsSet(self, message) 698 699 msg2 = message_module.TestAllTypes() 700 text = (u'optional_string: "café"') 701 text_format.Merge(text, msg2) 702 self.assertEqual(msg2.optional_string, u'café') 703 msg2.Clear() 704 self.assertEqual(msg2.optional_string, u'') 705 text_format.Parse(text, msg2) 706 self.assertEqual(msg2.optional_string, u'café') 707 708 def testParseDoubleToFloat(self, message_module): 709 message = message_module.TestAllTypes() 710 text = ('repeated_float: 3.4028235e+39\n' 711 'repeated_float: 1.4028235e-39\n') 712 text_format.Parse(text, message) 713 self.assertEqual(message.repeated_float[0], float('inf')) 714 self.assertAlmostEqual(message.repeated_float[1], 1.4028235e-39) 715 716 def testParseExotic(self, message_module): 717 message = message_module.TestAllTypes() 718 text = ('repeated_int64: -9223372036854775808\n' 719 'repeated_uint64: 18446744073709551615\n' 720 'repeated_double: 123.456\n' 721 'repeated_double: 1.23e+22\n' 722 'repeated_double: 1.23e-18\n' 723 'repeated_string: \n' 724 '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n' 725 'repeated_string: "foo" \'corge\' "grault"\n' 726 'repeated_string: "\\303\\274\\352\\234\\237"\n' 727 'repeated_string: "\\xc3\\xbc"\n' 728 'repeated_string: "\xc3\xbc"\n') 729 text_format.Parse(text, message) 730 731 self.assertEqual(-9223372036854775808, message.repeated_int64[0]) 732 self.assertEqual(18446744073709551615, message.repeated_uint64[0]) 733 self.assertEqual(123.456, message.repeated_double[0]) 734 self.assertEqual(1.23e22, message.repeated_double[1]) 735 self.assertEqual(1.23e-18, message.repeated_double[2]) 736 self.assertEqual('\000\001\a\b\f\n\r\t\v\\\'"', message.repeated_string[0]) 737 self.assertEqual('foocorgegrault', message.repeated_string[1]) 738 self.assertEqual(u'\u00fc\ua71f', message.repeated_string[2]) 739 self.assertEqual(u'\u00fc', message.repeated_string[3]) 740 741 def testParseTrailingCommas(self, message_module): 742 message = message_module.TestAllTypes() 743 text = ('repeated_int64: 100;\n' 744 'repeated_int64: 200;\n' 745 'repeated_int64: 300,\n' 746 'repeated_string: "one",\n' 747 'repeated_string: "two";\n') 748 text_format.Parse(text, message) 749 750 self.assertEqual(100, message.repeated_int64[0]) 751 self.assertEqual(200, message.repeated_int64[1]) 752 self.assertEqual(300, message.repeated_int64[2]) 753 self.assertEqual(u'one', message.repeated_string[0]) 754 self.assertEqual(u'two', message.repeated_string[1]) 755 756 def testParseRepeatedScalarShortFormat(self, message_module): 757 message = message_module.TestAllTypes() 758 text = ('repeated_int64: [100, 200];\n' 759 'repeated_int64: []\n' 760 'repeated_int64: 300,\n' 761 'repeated_string: ["one", "two"];\n') 762 text_format.Parse(text, message) 763 764 self.assertEqual(100, message.repeated_int64[0]) 765 self.assertEqual(200, message.repeated_int64[1]) 766 self.assertEqual(300, message.repeated_int64[2]) 767 self.assertEqual(u'one', message.repeated_string[0]) 768 self.assertEqual(u'two', message.repeated_string[1]) 769 770 def testParseRepeatedMessageShortFormat(self, message_module): 771 message = message_module.TestAllTypes() 772 text = ('repeated_nested_message: [{bb: 100}, {bb: 200}],\n' 773 'repeated_nested_message: {bb: 300}\n' 774 'repeated_nested_message [{bb: 400}];\n') 775 text_format.Parse(text, message) 776 777 self.assertEqual(100, message.repeated_nested_message[0].bb) 778 self.assertEqual(200, message.repeated_nested_message[1].bb) 779 self.assertEqual(300, message.repeated_nested_message[2].bb) 780 self.assertEqual(400, message.repeated_nested_message[3].bb) 781 782 def testParseEmptyText(self, message_module): 783 message = message_module.TestAllTypes() 784 text = '' 785 text_format.Parse(text, message) 786 self.assertEqual(message_module.TestAllTypes(), message) 787 788 def testParseInvalidUtf8(self, message_module): 789 message = message_module.TestAllTypes() 790 text = 'repeated_string: "\\xc3\\xc3"' 791 with self.assertRaises(text_format.ParseError) as e: 792 text_format.Parse(text, message) 793 self.assertEqual(e.exception.GetLine(), 1) 794 self.assertEqual(e.exception.GetColumn(), 28) 795 796 def testParseSingleWord(self, message_module): 797 message = message_module.TestAllTypes() 798 text = 'foo' 799 self.assertRaisesRegex( 800 text_format.ParseError, 801 (r'1:1 : Message type "\w+.TestAllTypes" has no field named ' 802 r'"foo".'), text_format.Parse, text, message) 803 804 def testParseUnknownField(self, message_module): 805 message = message_module.TestAllTypes() 806 text = 'unknown_field: 8\n' 807 self.assertRaisesRegex( 808 text_format.ParseError, 809 (r'1:1 : Message type "\w+.TestAllTypes" has no field named ' 810 r'"unknown_field".'), text_format.Parse, text, message) 811 text = ('optional_int32: 123\n' 812 'unknown_field: 8\n' 813 'optional_nested_message { bb: 45 }') 814 text_format.Parse(text, message, allow_unknown_field=True) 815 self.assertEqual(message.optional_nested_message.bb, 45) 816 self.assertEqual(message.optional_int32, 123) 817 818 def testParseBadEnumValue(self, message_module): 819 message = message_module.TestAllTypes() 820 text = 'optional_nested_enum: BARR' 821 self.assertRaisesRegex(text_format.ParseError, 822 (r'1:23 : \'optional_nested_enum: BARR\': ' 823 r'Enum type "\w+.TestAllTypes.NestedEnum" ' 824 r'has no value named BARR.'), text_format.Parse, 825 text, message) 826 827 def testParseBadIntValue(self, message_module): 828 message = message_module.TestAllTypes() 829 text = 'optional_int32: bork' 830 self.assertRaisesRegex(text_format.ParseError, 831 ('1:17 : \'optional_int32: bork\': ' 832 'Couldn\'t parse integer: bork'), text_format.Parse, 833 text, message) 834 835 def testParseStringFieldUnescape(self, message_module): 836 message = message_module.TestAllTypes() 837 text = r'''repeated_string: "\xf\x62" 838 repeated_string: "\\xf\\x62" 839 repeated_string: "\\\xf\\\x62" 840 repeated_string: "\\\\xf\\\\x62" 841 repeated_string: "\\\\\xf\\\\\x62" 842 repeated_string: "\x5cx20"''' 843 844 text_format.Parse(text, message) 845 846 SLASH = '\\' 847 self.assertEqual('\x0fb', message.repeated_string[0]) 848 self.assertEqual(SLASH + 'xf' + SLASH + 'x62', message.repeated_string[1]) 849 self.assertEqual(SLASH + '\x0f' + SLASH + 'b', message.repeated_string[2]) 850 self.assertEqual(SLASH + SLASH + 'xf' + SLASH + SLASH + 'x62', 851 message.repeated_string[3]) 852 self.assertEqual(SLASH + SLASH + '\x0f' + SLASH + SLASH + 'b', 853 message.repeated_string[4]) 854 self.assertEqual(SLASH + 'x20', message.repeated_string[5]) 855 856 def testParseOneof(self, message_module): 857 m = message_module.TestAllTypes() 858 m.oneof_uint32 = 11 859 m2 = message_module.TestAllTypes() 860 text_format.Parse(text_format.MessageToString(m), m2) 861 self.assertEqual('oneof_uint32', m2.WhichOneof('oneof_field')) 862 863 def testParseMultipleOneof(self, message_module): 864 m_string = '\n'.join(['oneof_uint32: 11', 'oneof_string: "foo"']) 865 m2 = message_module.TestAllTypes() 866 with self.assertRaisesRegex(text_format.ParseError, 867 ' is specified along with field '): 868 text_format.Parse(m_string, m2) 869 870 # This example contains non-ASCII codepoint unicode data as literals 871 # which should come through as utf-8 for bytes, and as the unicode 872 # itself for string fields. It also demonstrates escaped binary data. 873 # The ur"" string prefix is unfortunately missing from Python 3 874 # so we resort to double escaping our \s so that they come through. 875 _UNICODE_SAMPLE = u""" 876 optional_bytes: 'Á short desçription' 877 optional_string: 'Á short desçription' 878 repeated_bytes: '\\303\\201 short des\\303\\247ription' 879 repeated_bytes: '\\x12\\x34\\x56\\x78\\x90\\xab\\xcd\\xef' 880 repeated_string: '\\xd0\\x9f\\xd1\\x80\\xd0\\xb8\\xd0\\xb2\\xd0\\xb5\\xd1\\x82' 881 """ 882 _BYTES_SAMPLE = _UNICODE_SAMPLE.encode('utf-8') 883 _GOLDEN_UNICODE = u'Á short desçription' 884 _GOLDEN_BYTES = _GOLDEN_UNICODE.encode('utf-8') 885 _GOLDEN_BYTES_1 = b'\x12\x34\x56\x78\x90\xab\xcd\xef' 886 _GOLDEN_STR_0 = u'Привет' 887 888 def testParseUnicode(self, message_module): 889 m = message_module.TestAllTypes() 890 text_format.Parse(self._UNICODE_SAMPLE, m) 891 self.assertEqual(m.optional_bytes, self._GOLDEN_BYTES) 892 self.assertEqual(m.optional_string, self._GOLDEN_UNICODE) 893 self.assertEqual(m.repeated_bytes[0], self._GOLDEN_BYTES) 894 # repeated_bytes[1] contained simple \ escaped non-UTF-8 raw binary data. 895 self.assertEqual(m.repeated_bytes[1], self._GOLDEN_BYTES_1) 896 # repeated_string[0] contained \ escaped data representing the UTF-8 897 # representation of _GOLDEN_STR_0 - it needs to decode as such. 898 self.assertEqual(m.repeated_string[0], self._GOLDEN_STR_0) 899 900 def testParseBytes(self, message_module): 901 m = message_module.TestAllTypes() 902 text_format.Parse(self._BYTES_SAMPLE, m) 903 self.assertEqual(m.optional_bytes, self._GOLDEN_BYTES) 904 self.assertEqual(m.optional_string, self._GOLDEN_UNICODE) 905 self.assertEqual(m.repeated_bytes[0], self._GOLDEN_BYTES) 906 # repeated_bytes[1] contained simple \ escaped non-UTF-8 raw binary data. 907 self.assertEqual(m.repeated_bytes[1], self._GOLDEN_BYTES_1) 908 # repeated_string[0] contained \ escaped data representing the UTF-8 909 # representation of _GOLDEN_STR_0 - it needs to decode as such. 910 self.assertEqual(m.repeated_string[0], self._GOLDEN_STR_0) 911 912 def testFromBytesFile(self, message_module): 913 m = message_module.TestAllTypes() 914 f = io.BytesIO(self._BYTES_SAMPLE) 915 text_format.ParseLines(f, m) 916 self.assertEqual(m.optional_bytes, self._GOLDEN_BYTES) 917 self.assertEqual(m.optional_string, self._GOLDEN_UNICODE) 918 self.assertEqual(m.repeated_bytes[0], self._GOLDEN_BYTES) 919 920 def testFromUnicodeFile(self, message_module): 921 m = message_module.TestAllTypes() 922 f = io.StringIO(self._UNICODE_SAMPLE) 923 text_format.ParseLines(f, m) 924 self.assertEqual(m.optional_bytes, self._GOLDEN_BYTES) 925 self.assertEqual(m.optional_string, self._GOLDEN_UNICODE) 926 self.assertEqual(m.repeated_bytes[0], self._GOLDEN_BYTES) 927 928 def testFromBytesLines(self, message_module): 929 m = message_module.TestAllTypes() 930 text_format.ParseLines(self._BYTES_SAMPLE.split(b'\n'), m) 931 self.assertEqual(m.optional_bytes, self._GOLDEN_BYTES) 932 self.assertEqual(m.optional_string, self._GOLDEN_UNICODE) 933 self.assertEqual(m.repeated_bytes[0], self._GOLDEN_BYTES) 934 935 def testFromUnicodeLines(self, message_module): 936 m = message_module.TestAllTypes() 937 text_format.ParseLines(self._UNICODE_SAMPLE.split(u'\n'), m) 938 self.assertEqual(m.optional_bytes, self._GOLDEN_BYTES) 939 self.assertEqual(m.optional_string, self._GOLDEN_UNICODE) 940 self.assertEqual(m.repeated_bytes[0], self._GOLDEN_BYTES) 941 942 def testParseDuplicateMessages(self, message_module): 943 message = message_module.TestAllTypes() 944 text = ('optional_nested_message { bb: 1 } ' 945 'optional_nested_message { bb: 2 }') 946 self.assertRaisesRegex( 947 text_format.ParseError, 948 (r'1:59 : Message type "\w+.TestAllTypes" ' 949 r'should not have multiple "optional_nested_message" fields.'), 950 text_format.Parse, text, message) 951 952 def testParseDuplicateScalars(self, message_module): 953 message = message_module.TestAllTypes() 954 text = ('optional_int32: 42 ' 'optional_int32: 67') 955 self.assertRaisesRegex( 956 text_format.ParseError, 957 (r'1:36 : Message type "\w+.TestAllTypes" should not ' 958 r'have multiple "optional_int32" fields.'), text_format.Parse, text, 959 message) 960 961 def testParseExistingScalarInMessage(self, message_module): 962 message = message_module.TestAllTypes(optional_int32=42) 963 text = 'optional_int32: 67' 964 self.assertRaisesRegex(text_format.ParseError, 965 (r'Message type "\w+.TestAllTypes" should not ' 966 r'have multiple "optional_int32" fields.'), 967 text_format.Parse, text, message) 968 969 970@_parameterized.parameters(unittest_pb2, unittest_proto3_arena_pb2) 971class TextFormatMergeTests(TextFormatBase): 972 973 def testMergeDuplicateScalarsInText(self, message_module): 974 message = message_module.TestAllTypes() 975 text = ('optional_int32: 42 ' 'optional_int32: 67') 976 r = text_format.Merge(text, message) 977 self.assertIs(r, message) 978 self.assertEqual(67, message.optional_int32) 979 980 def testMergeDuplicateNestedMessageScalars(self, message_module): 981 message = message_module.TestAllTypes() 982 text = ('optional_nested_message { bb: 1 } ' 983 'optional_nested_message { bb: 2 }') 984 r = text_format.Merge(text, message) 985 self.assertTrue(r is message) 986 self.assertEqual(2, message.optional_nested_message.bb) 987 988 def testReplaceScalarInMessage(self, message_module): 989 message = message_module.TestAllTypes(optional_int32=42) 990 text = 'optional_int32: 67' 991 r = text_format.Merge(text, message) 992 self.assertIs(r, message) 993 self.assertEqual(67, message.optional_int32) 994 995 def testReplaceMessageInMessage(self, message_module): 996 message = message_module.TestAllTypes( 997 optional_int32=42, optional_nested_message=dict()) 998 self.assertTrue(message.HasField('optional_nested_message')) 999 text = 'optional_nested_message{ bb: 3 }' 1000 r = text_format.Merge(text, message) 1001 self.assertIs(r, message) 1002 self.assertEqual(3, message.optional_nested_message.bb) 1003 1004 def testMergeMultipleOneof(self, message_module): 1005 m_string = '\n'.join(['oneof_uint32: 11', 'oneof_string: "foo"']) 1006 m2 = message_module.TestAllTypes() 1007 text_format.Merge(m_string, m2) 1008 self.assertEqual('oneof_string', m2.WhichOneof('oneof_field')) 1009 1010 1011# These are tests that aren't fundamentally specific to proto2, but are at 1012# the moment because of differences between the proto2 and proto3 test schemas. 1013# Ideally the schemas would be made more similar so these tests could pass. 1014class OnlyWorksWithProto2RightNowTests(TextFormatBase): 1015 1016 def testPrintAllFieldsPointy(self): 1017 message = unittest_pb2.TestAllTypes() 1018 test_util.SetAllFields(message) 1019 self.CompareToGoldenFile( 1020 self.RemoveRedundantZeros(text_format.MessageToString( 1021 message, pointy_brackets=True)), 1022 'text_format_unittest_data_pointy_oneof.txt') 1023 1024 def testParseGolden(self): 1025 golden_text = '\n'.join(self.ReadGolden( 1026 'text_format_unittest_data_oneof_implemented.txt')) 1027 parsed_message = unittest_pb2.TestAllTypes() 1028 r = text_format.Parse(golden_text, parsed_message) 1029 self.assertIs(r, parsed_message) 1030 1031 message = unittest_pb2.TestAllTypes() 1032 test_util.SetAllFields(message) 1033 self.assertEqual(message, parsed_message) 1034 1035 def testPrintAllFields(self): 1036 message = unittest_pb2.TestAllTypes() 1037 test_util.SetAllFields(message) 1038 self.CompareToGoldenFile( 1039 self.RemoveRedundantZeros(text_format.MessageToString(message)), 1040 'text_format_unittest_data_oneof_implemented.txt') 1041 1042 def testPrintUnknownFields(self): 1043 message = unittest_pb2.TestAllTypes() 1044 message.optional_int32 = 101 1045 message.optional_double = 102.0 1046 message.optional_string = u'hello' 1047 message.optional_bytes = b'103' 1048 message.optionalgroup.a = 104 1049 message.optional_nested_message.bb = 105 1050 all_data = message.SerializeToString() 1051 empty_message = unittest_pb2.TestEmptyMessage() 1052 empty_message.ParseFromString(all_data) 1053 self.assertEqual(' 1: 101\n' 1054 ' 12: 4636878028842991616\n' 1055 ' 14: "hello"\n' 1056 ' 15: "103"\n' 1057 ' 16 {\n' 1058 ' 17: 104\n' 1059 ' }\n' 1060 ' 18 {\n' 1061 ' 1: 105\n' 1062 ' }\n', 1063 text_format.MessageToString(empty_message, 1064 indent=2, 1065 print_unknown_fields=True)) 1066 self.assertEqual('1: 101 ' 1067 '12: 4636878028842991616 ' 1068 '14: "hello" ' 1069 '15: "103" ' 1070 '16 { 17: 104 } ' 1071 '18 { 1: 105 }', 1072 text_format.MessageToString(empty_message, 1073 print_unknown_fields=True, 1074 as_one_line=True)) 1075 1076 def testPrintInIndexOrder(self): 1077 message = unittest_pb2.TestFieldOrderings() 1078 # Fields are listed in index order instead of field number. 1079 message.my_string = 'str' 1080 message.my_int = 101 1081 message.my_float = 111 1082 message.optional_nested_message.oo = 0 1083 message.optional_nested_message.bb = 1 1084 message.Extensions[unittest_pb2.my_extension_string] = 'ext_str0' 1085 # Extensions are listed based on the order of extension number. 1086 # Extension number 12. 1087 message.Extensions[unittest_pb2.TestExtensionOrderings2. 1088 test_ext_orderings2].my_string = 'ext_str2' 1089 # Extension number 13. 1090 message.Extensions[unittest_pb2.TestExtensionOrderings1. 1091 test_ext_orderings1].my_string = 'ext_str1' 1092 # Extension number 14. 1093 message.Extensions[ 1094 unittest_pb2.TestExtensionOrderings2.TestExtensionOrderings3. 1095 test_ext_orderings3].my_string = 'ext_str3' 1096 1097 # Print in index order. 1098 self.CompareToGoldenText( 1099 self.RemoveRedundantZeros( 1100 text_format.MessageToString(message, use_index_order=True)), 1101 'my_string: "str"\n' 1102 'my_int: 101\n' 1103 'my_float: 111\n' 1104 'optional_nested_message {\n' 1105 ' oo: 0\n' 1106 ' bb: 1\n' 1107 '}\n' 1108 '[protobuf_unittest.TestExtensionOrderings2.test_ext_orderings2] {\n' 1109 ' my_string: "ext_str2"\n' 1110 '}\n' 1111 '[protobuf_unittest.TestExtensionOrderings1.test_ext_orderings1] {\n' 1112 ' my_string: "ext_str1"\n' 1113 '}\n' 1114 '[protobuf_unittest.TestExtensionOrderings2.TestExtensionOrderings3' 1115 '.test_ext_orderings3] {\n' 1116 ' my_string: "ext_str3"\n' 1117 '}\n' 1118 '[protobuf_unittest.my_extension_string]: "ext_str0"\n') 1119 # By default, print in field number order. 1120 self.CompareToGoldenText( 1121 self.RemoveRedundantZeros(text_format.MessageToString(message)), 1122 'my_int: 101\n' 1123 'my_string: "str"\n' 1124 '[protobuf_unittest.TestExtensionOrderings2.test_ext_orderings2] {\n' 1125 ' my_string: "ext_str2"\n' 1126 '}\n' 1127 '[protobuf_unittest.TestExtensionOrderings1.test_ext_orderings1] {\n' 1128 ' my_string: "ext_str1"\n' 1129 '}\n' 1130 '[protobuf_unittest.TestExtensionOrderings2.TestExtensionOrderings3' 1131 '.test_ext_orderings3] {\n' 1132 ' my_string: "ext_str3"\n' 1133 '}\n' 1134 '[protobuf_unittest.my_extension_string]: "ext_str0"\n' 1135 'my_float: 111\n' 1136 'optional_nested_message {\n' 1137 ' bb: 1\n' 1138 ' oo: 0\n' 1139 '}\n') 1140 1141 def testMergeLinesGolden(self): 1142 opened = self.ReadGolden('text_format_unittest_data_oneof_implemented.txt') 1143 parsed_message = unittest_pb2.TestAllTypes() 1144 r = text_format.MergeLines(opened, parsed_message) 1145 self.assertIs(r, parsed_message) 1146 1147 message = unittest_pb2.TestAllTypes() 1148 test_util.SetAllFields(message) 1149 self.assertEqual(message, parsed_message) 1150 1151 def testParseLinesGolden(self): 1152 opened = self.ReadGolden('text_format_unittest_data_oneof_implemented.txt') 1153 parsed_message = unittest_pb2.TestAllTypes() 1154 r = text_format.ParseLines(opened, parsed_message) 1155 self.assertIs(r, parsed_message) 1156 1157 message = unittest_pb2.TestAllTypes() 1158 test_util.SetAllFields(message) 1159 self.assertEqual(message, parsed_message) 1160 1161 def testPrintMap(self): 1162 message = map_unittest_pb2.TestMap() 1163 1164 message.map_int32_int32[-123] = -456 1165 message.map_int64_int64[-2**33] = -2**34 1166 message.map_uint32_uint32[123] = 456 1167 message.map_uint64_uint64[2**33] = 2**34 1168 message.map_string_string['abc'] = '123' 1169 message.map_int32_foreign_message[111].c = 5 1170 1171 # Maps are serialized to text format using their underlying repeated 1172 # representation. 1173 self.CompareToGoldenText( 1174 text_format.MessageToString(message), 'map_int32_int32 {\n' 1175 ' key: -123\n' 1176 ' value: -456\n' 1177 '}\n' 1178 'map_int64_int64 {\n' 1179 ' key: -8589934592\n' 1180 ' value: -17179869184\n' 1181 '}\n' 1182 'map_uint32_uint32 {\n' 1183 ' key: 123\n' 1184 ' value: 456\n' 1185 '}\n' 1186 'map_uint64_uint64 {\n' 1187 ' key: 8589934592\n' 1188 ' value: 17179869184\n' 1189 '}\n' 1190 'map_string_string {\n' 1191 ' key: "abc"\n' 1192 ' value: "123"\n' 1193 '}\n' 1194 'map_int32_foreign_message {\n' 1195 ' key: 111\n' 1196 ' value {\n' 1197 ' c: 5\n' 1198 ' }\n' 1199 '}\n') 1200 1201 def testDuplicateMapKey(self): 1202 message = map_unittest_pb2.TestMap() 1203 text = ( 1204 'map_uint64_uint64 {\n' 1205 ' key: 123\n' 1206 ' value: 17179869184\n' 1207 '}\n' 1208 'map_string_string {\n' 1209 ' key: "abc"\n' 1210 ' value: "first"\n' 1211 '}\n' 1212 'map_int32_foreign_message {\n' 1213 ' key: 111\n' 1214 ' value {\n' 1215 ' c: 5\n' 1216 ' }\n' 1217 '}\n' 1218 'map_uint64_uint64 {\n' 1219 ' key: 123\n' 1220 ' value: 321\n' 1221 '}\n' 1222 'map_string_string {\n' 1223 ' key: "abc"\n' 1224 ' value: "second"\n' 1225 '}\n' 1226 'map_int32_foreign_message {\n' 1227 ' key: 111\n' 1228 ' value {\n' 1229 ' d: 5\n' 1230 ' }\n' 1231 '}\n') 1232 text_format.Parse(text, message) 1233 self.CompareToGoldenText( 1234 text_format.MessageToString(message), 'map_uint64_uint64 {\n' 1235 ' key: 123\n' 1236 ' value: 321\n' 1237 '}\n' 1238 'map_string_string {\n' 1239 ' key: "abc"\n' 1240 ' value: "second"\n' 1241 '}\n' 1242 'map_int32_foreign_message {\n' 1243 ' key: 111\n' 1244 ' value {\n' 1245 ' d: 5\n' 1246 ' }\n' 1247 '}\n') 1248 1249 # In cpp implementation, __str__ calls the cpp implementation of text format. 1250 def testPrintMapUsingCppImplementation(self): 1251 message = map_unittest_pb2.TestMap() 1252 inner_msg = message.map_int32_foreign_message[111] 1253 inner_msg.c = 1 1254 self.assertEqual( 1255 str(message), 1256 'map_int32_foreign_message {\n' 1257 ' key: 111\n' 1258 ' value {\n' 1259 ' c: 1\n' 1260 ' }\n' 1261 '}\n') 1262 inner_msg.c = 2 1263 self.assertEqual( 1264 str(message), 1265 'map_int32_foreign_message {\n' 1266 ' key: 111\n' 1267 ' value {\n' 1268 ' c: 2\n' 1269 ' }\n' 1270 '}\n') 1271 1272 def testMapOrderEnforcement(self): 1273 message = map_unittest_pb2.TestMap() 1274 for letter in string.ascii_uppercase[13:26]: 1275 message.map_string_string[letter] = 'dummy' 1276 for letter in reversed(string.ascii_uppercase[0:13]): 1277 message.map_string_string[letter] = 'dummy' 1278 golden = ''.join(('map_string_string {\n key: "%c"\n value: "dummy"\n}\n' 1279 % (letter,) for letter in string.ascii_uppercase)) 1280 self.CompareToGoldenText(text_format.MessageToString(message), golden) 1281 1282 # TODO(teboring): In c/137553523, not serializing default value for map entry 1283 # message has been fixed. This test needs to be disabled in order to submit 1284 # that cl. Add this back when c/137553523 has been submitted. 1285 # def testMapOrderSemantics(self): 1286 # golden_lines = self.ReadGolden('map_test_data.txt') 1287 1288 # message = map_unittest_pb2.TestMap() 1289 # text_format.ParseLines(golden_lines, message) 1290 # candidate = text_format.MessageToString(message) 1291 # # The Python implementation emits "1.0" for the double value that the C++ 1292 # # implementation emits as "1". 1293 # candidate = candidate.replace('1.0', '1', 2) 1294 # candidate = candidate.replace('0.0', '0', 2) 1295 # self.assertMultiLineEqual(candidate, ''.join(golden_lines)) 1296 1297 1298# Tests of proto2-only features (MessageSet, extensions, etc.). 1299class Proto2Tests(TextFormatBase): 1300 1301 def testPrintMessageSet(self): 1302 message = unittest_mset_pb2.TestMessageSetContainer() 1303 ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension 1304 ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension 1305 message.message_set.Extensions[ext1].i = 23 1306 message.message_set.Extensions[ext2].str = 'foo' 1307 self.CompareToGoldenText( 1308 text_format.MessageToString(message), 'message_set {\n' 1309 ' [protobuf_unittest.TestMessageSetExtension1] {\n' 1310 ' i: 23\n' 1311 ' }\n' 1312 ' [protobuf_unittest.TestMessageSetExtension2] {\n' 1313 ' str: \"foo\"\n' 1314 ' }\n' 1315 '}\n') 1316 1317 message = message_set_extensions_pb2.TestMessageSet() 1318 ext = message_set_extensions_pb2.message_set_extension3 1319 message.Extensions[ext].text = 'bar' 1320 self.CompareToGoldenText( 1321 text_format.MessageToString(message), 1322 '[google.protobuf.internal.TestMessageSetExtension3] {\n' 1323 ' text: \"bar\"\n' 1324 '}\n') 1325 1326 def testPrintMessageSetByFieldNumber(self): 1327 out = text_format.TextWriter(False) 1328 message = unittest_mset_pb2.TestMessageSetContainer() 1329 ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension 1330 ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension 1331 message.message_set.Extensions[ext1].i = 23 1332 message.message_set.Extensions[ext2].str = 'foo' 1333 text_format.PrintMessage(message, out, use_field_number=True) 1334 self.CompareToGoldenText(out.getvalue(), '1 {\n' 1335 ' 1545008 {\n' 1336 ' 15: 23\n' 1337 ' }\n' 1338 ' 1547769 {\n' 1339 ' 25: \"foo\"\n' 1340 ' }\n' 1341 '}\n') 1342 out.close() 1343 1344 def testPrintMessageSetAsOneLine(self): 1345 message = unittest_mset_pb2.TestMessageSetContainer() 1346 ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension 1347 ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension 1348 message.message_set.Extensions[ext1].i = 23 1349 message.message_set.Extensions[ext2].str = 'foo' 1350 self.CompareToGoldenText( 1351 text_format.MessageToString(message, as_one_line=True), 1352 'message_set {' 1353 ' [protobuf_unittest.TestMessageSetExtension1] {' 1354 ' i: 23' 1355 ' }' 1356 ' [protobuf_unittest.TestMessageSetExtension2] {' 1357 ' str: \"foo\"' 1358 ' }' 1359 ' }') 1360 1361 def testParseMessageSet(self): 1362 message = unittest_pb2.TestAllTypes() 1363 text = ('repeated_uint64: 1\n' 'repeated_uint64: 2\n') 1364 text_format.Parse(text, message) 1365 self.assertEqual(1, message.repeated_uint64[0]) 1366 self.assertEqual(2, message.repeated_uint64[1]) 1367 1368 message = unittest_mset_pb2.TestMessageSetContainer() 1369 text = ('message_set {\n' 1370 ' [protobuf_unittest.TestMessageSetExtension1] {\n' 1371 ' i: 23\n' 1372 ' }\n' 1373 ' [protobuf_unittest.TestMessageSetExtension2] {\n' 1374 ' str: \"foo\"\n' 1375 ' }\n' 1376 '}\n') 1377 text_format.Parse(text, message) 1378 ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension 1379 ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension 1380 self.assertEqual(23, message.message_set.Extensions[ext1].i) 1381 self.assertEqual('foo', message.message_set.Extensions[ext2].str) 1382 1383 def testExtensionInsideAnyMessage(self): 1384 message = test_extend_any.TestAny() 1385 text = ('value {\n' 1386 ' [type.googleapis.com/google.protobuf.internal.TestAny] {\n' 1387 ' [google.protobuf.internal.TestAnyExtension1.extension1] {\n' 1388 ' i: 10\n' 1389 ' }\n' 1390 ' }\n' 1391 '}\n') 1392 text_format.Merge(text, message, descriptor_pool=descriptor_pool.Default()) 1393 self.CompareToGoldenText( 1394 text_format.MessageToString( 1395 message, descriptor_pool=descriptor_pool.Default()), 1396 text) 1397 1398 def testParseMessageByFieldNumber(self): 1399 message = unittest_pb2.TestAllTypes() 1400 text = ('34: 1\n' 'repeated_uint64: 2\n') 1401 text_format.Parse(text, message, allow_field_number=True) 1402 self.assertEqual(1, message.repeated_uint64[0]) 1403 self.assertEqual(2, message.repeated_uint64[1]) 1404 1405 message = unittest_mset_pb2.TestMessageSetContainer() 1406 text = ('1 {\n' 1407 ' 1545008 {\n' 1408 ' 15: 23\n' 1409 ' }\n' 1410 ' 1547769 {\n' 1411 ' 25: \"foo\"\n' 1412 ' }\n' 1413 '}\n') 1414 text_format.Parse(text, message, allow_field_number=True) 1415 ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension 1416 ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension 1417 self.assertEqual(23, message.message_set.Extensions[ext1].i) 1418 self.assertEqual('foo', message.message_set.Extensions[ext2].str) 1419 1420 # Can't parse field number without set allow_field_number=True. 1421 message = unittest_pb2.TestAllTypes() 1422 text = '34:1\n' 1423 self.assertRaisesRegex( 1424 text_format.ParseError, 1425 (r'1:1 : Message type "\w+.TestAllTypes" has no field named ' 1426 r'"34".'), text_format.Parse, text, message) 1427 1428 # Can't parse if field number is not found. 1429 text = '1234:1\n' 1430 self.assertRaisesRegex( 1431 text_format.ParseError, 1432 (r'1:1 : Message type "\w+.TestAllTypes" has no field named ' 1433 r'"1234".'), 1434 text_format.Parse, 1435 text, 1436 message, 1437 allow_field_number=True) 1438 1439 def testPrintAllExtensions(self): 1440 message = unittest_pb2.TestAllExtensions() 1441 test_util.SetAllExtensions(message) 1442 self.CompareToGoldenFile( 1443 self.RemoveRedundantZeros(text_format.MessageToString(message)), 1444 'text_format_unittest_extensions_data.txt') 1445 1446 def testPrintAllExtensionsPointy(self): 1447 message = unittest_pb2.TestAllExtensions() 1448 test_util.SetAllExtensions(message) 1449 self.CompareToGoldenFile( 1450 self.RemoveRedundantZeros(text_format.MessageToString( 1451 message, pointy_brackets=True)), 1452 'text_format_unittest_extensions_data_pointy.txt') 1453 1454 def testParseGoldenExtensions(self): 1455 golden_text = '\n'.join(self.ReadGolden( 1456 'text_format_unittest_extensions_data.txt')) 1457 parsed_message = unittest_pb2.TestAllExtensions() 1458 text_format.Parse(golden_text, parsed_message) 1459 1460 message = unittest_pb2.TestAllExtensions() 1461 test_util.SetAllExtensions(message) 1462 self.assertEqual(message, parsed_message) 1463 1464 def testParseAllExtensions(self): 1465 message = unittest_pb2.TestAllExtensions() 1466 test_util.SetAllExtensions(message) 1467 ascii_text = text_format.MessageToString(message) 1468 1469 parsed_message = unittest_pb2.TestAllExtensions() 1470 text_format.Parse(ascii_text, parsed_message) 1471 self.assertEqual(message, parsed_message) 1472 1473 def testParseAllowedUnknownExtension(self): 1474 # Skip over unknown extension correctly. 1475 message = unittest_mset_pb2.TestMessageSetContainer() 1476 text = ('message_set {\n' 1477 ' [unknown_extension] {\n' 1478 ' i: 23\n' 1479 ' repeated_i: []\n' 1480 ' bin: "\xe0"\n' 1481 ' [nested_unknown_ext]: {\n' 1482 ' i: 23\n' 1483 ' repeated_i: [1, 2]\n' 1484 ' x: x\n' 1485 ' test: "test_string"\n' 1486 ' floaty_float: -0.315\n' 1487 ' num: -inf\n' 1488 ' multiline_str: "abc"\n' 1489 ' "def"\n' 1490 ' "xyz."\n' 1491 ' [nested_unknown_ext.ext]: <\n' 1492 ' i: 23\n' 1493 ' i: 24\n' 1494 ' pointfloat: .3\n' 1495 ' test: "test_string"\n' 1496 ' repeated_test: ["test_string1", "test_string2"]\n' 1497 ' floaty_float: -0.315\n' 1498 ' num: -inf\n' 1499 ' long_string: "test" "test2" \n' 1500 ' >\n' 1501 ' }\n' 1502 ' }\n' 1503 ' [unknown_extension]: 5\n' 1504 ' [unknown_extension_with_number_field] {\n' 1505 ' 1: "some_field"\n' 1506 ' 2: -0.451\n' 1507 ' }\n' 1508 '}\n') 1509 text_format.Parse(text, message, allow_unknown_extension=True) 1510 golden = 'message_set {\n}\n' 1511 self.CompareToGoldenText(text_format.MessageToString(message), golden) 1512 1513 # Catch parse errors in unknown extension. 1514 message = unittest_mset_pb2.TestMessageSetContainer() 1515 malformed = ('message_set {\n' 1516 ' [unknown_extension] {\n' 1517 ' i:\n' # Missing value. 1518 ' }\n' 1519 '}\n') 1520 self.assertRaisesRegex( 1521 text_format.ParseError, 1522 'Invalid field value: }', 1523 text_format.Parse, 1524 malformed, 1525 message, 1526 allow_unknown_extension=True) 1527 1528 message = unittest_mset_pb2.TestMessageSetContainer() 1529 malformed = ('message_set {\n' 1530 ' [unknown_extension] {\n' 1531 ' str: "malformed string\n' # Missing closing quote. 1532 ' }\n' 1533 '}\n') 1534 self.assertRaisesRegex( 1535 text_format.ParseError, 1536 'Invalid field value: "', 1537 text_format.Parse, 1538 malformed, 1539 message, 1540 allow_unknown_extension=True) 1541 1542 message = unittest_mset_pb2.TestMessageSetContainer() 1543 malformed = ('message_set {\n' 1544 ' [unknown_extension] {\n' 1545 ' str: "malformed\n multiline\n string\n' 1546 ' }\n' 1547 '}\n') 1548 self.assertRaisesRegex( 1549 text_format.ParseError, 1550 'Invalid field value: "', 1551 text_format.Parse, 1552 malformed, 1553 message, 1554 allow_unknown_extension=True) 1555 1556 message = unittest_mset_pb2.TestMessageSetContainer() 1557 malformed = ('message_set {\n' 1558 ' [malformed_extension] <\n' 1559 ' i: -5\n' 1560 ' \n' # Missing '>' here. 1561 '}\n') 1562 self.assertRaisesRegex( 1563 text_format.ParseError, 1564 '5:1 : \'}\': Expected ">".', 1565 text_format.Parse, 1566 malformed, 1567 message, 1568 allow_unknown_extension=True) 1569 1570 # Don't allow unknown fields with allow_unknown_extension=True. 1571 message = unittest_mset_pb2.TestMessageSetContainer() 1572 malformed = ('message_set {\n' 1573 ' unknown_field: true\n' 1574 '}\n') 1575 self.assertRaisesRegex( 1576 text_format.ParseError, 1577 ('2:3 : Message type ' 1578 '"proto2_wireformat_unittest.TestMessageSet" has no' 1579 ' field named "unknown_field".'), 1580 text_format.Parse, 1581 malformed, 1582 message, 1583 allow_unknown_extension=True) 1584 1585 # Parse known extension correctly. 1586 message = unittest_mset_pb2.TestMessageSetContainer() 1587 text = ('message_set {\n' 1588 ' [protobuf_unittest.TestMessageSetExtension1] {\n' 1589 ' i: 23\n' 1590 ' }\n' 1591 ' [protobuf_unittest.TestMessageSetExtension2] {\n' 1592 ' str: \"foo\"\n' 1593 ' }\n' 1594 '}\n') 1595 text_format.Parse(text, message, allow_unknown_extension=True) 1596 ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension 1597 ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension 1598 self.assertEqual(23, message.message_set.Extensions[ext1].i) 1599 self.assertEqual('foo', message.message_set.Extensions[ext2].str) 1600 1601 def testParseBadIdentifier(self): 1602 message = unittest_pb2.TestAllTypes() 1603 text = ('optional_nested_message { "bb": 1 }') 1604 with self.assertRaises(text_format.ParseError) as e: 1605 text_format.Parse(text, message) 1606 self.assertEqual(str(e.exception), 1607 '1:27 : \'optional_nested_message { "bb": 1 }\': ' 1608 'Expected identifier or number, got "bb".') 1609 1610 def testParseBadExtension(self): 1611 message = unittest_pb2.TestAllExtensions() 1612 text = '[unknown_extension]: 8\n' 1613 self.assertRaisesRegex( 1614 text_format.ParseError, 1615 '1:2 : Extension "unknown_extension" not registered.', 1616 text_format.Parse, text, message) 1617 message = unittest_pb2.TestAllTypes() 1618 self.assertRaisesRegex( 1619 text_format.ParseError, 1620 ('1:2 : Message type "protobuf_unittest.TestAllTypes" does not have ' 1621 'extensions.'), text_format.Parse, text, message) 1622 1623 def testParseNumericUnknownEnum(self): 1624 message = unittest_pb2.TestAllTypes() 1625 text = 'optional_nested_enum: 100' 1626 self.assertRaisesRegex(text_format.ParseError, 1627 (r'1:23 : \'optional_nested_enum: 100\': ' 1628 r'Enum type "\w+.TestAllTypes.NestedEnum" ' 1629 r'has no value with number 100.'), 1630 text_format.Parse, text, message) 1631 1632 def testMergeDuplicateExtensionScalars(self): 1633 message = unittest_pb2.TestAllExtensions() 1634 text = ('[protobuf_unittest.optional_int32_extension]: 42 ' 1635 '[protobuf_unittest.optional_int32_extension]: 67') 1636 text_format.Merge(text, message) 1637 self.assertEqual(67, 1638 message.Extensions[unittest_pb2.optional_int32_extension]) 1639 1640 def testParseDuplicateExtensionScalars(self): 1641 message = unittest_pb2.TestAllExtensions() 1642 text = ('[protobuf_unittest.optional_int32_extension]: 42 ' 1643 '[protobuf_unittest.optional_int32_extension]: 67') 1644 self.assertRaisesRegex( 1645 text_format.ParseError, 1646 ('1:96 : Message type "protobuf_unittest.TestAllExtensions" ' 1647 'should not have multiple ' 1648 '"protobuf_unittest.optional_int32_extension" extensions.'), 1649 text_format.Parse, text, message) 1650 1651 def testParseDuplicateExtensionMessages(self): 1652 message = unittest_pb2.TestAllExtensions() 1653 text = ('[protobuf_unittest.optional_nested_message_extension]: {} ' 1654 '[protobuf_unittest.optional_nested_message_extension]: {}') 1655 self.assertRaisesRegex( 1656 text_format.ParseError, 1657 ('1:114 : Message type "protobuf_unittest.TestAllExtensions" ' 1658 'should not have multiple ' 1659 '"protobuf_unittest.optional_nested_message_extension" extensions.'), 1660 text_format.Parse, text, message) 1661 1662 def testParseGroupNotClosed(self): 1663 message = unittest_pb2.TestAllTypes() 1664 text = 'RepeatedGroup: <' 1665 self.assertRaisesRegex(text_format.ParseError, '1:16 : Expected ">".', 1666 text_format.Parse, text, message) 1667 text = 'RepeatedGroup: {' 1668 self.assertRaisesRegex(text_format.ParseError, '1:16 : Expected "}".', 1669 text_format.Parse, text, message) 1670 1671 def testParseEmptyGroup(self): 1672 message = unittest_pb2.TestAllTypes() 1673 text = 'OptionalGroup: {}' 1674 text_format.Parse(text, message) 1675 self.assertTrue(message.HasField('optionalgroup')) 1676 1677 message.Clear() 1678 1679 message = unittest_pb2.TestAllTypes() 1680 text = 'OptionalGroup: <>' 1681 text_format.Parse(text, message) 1682 self.assertTrue(message.HasField('optionalgroup')) 1683 1684 # Maps aren't really proto2-only, but our test schema only has maps for 1685 # proto2. 1686 def testParseMap(self): 1687 text = ('map_int32_int32 {\n' 1688 ' key: -123\n' 1689 ' value: -456\n' 1690 '}\n' 1691 'map_int64_int64 {\n' 1692 ' key: -8589934592\n' 1693 ' value: -17179869184\n' 1694 '}\n' 1695 'map_uint32_uint32 {\n' 1696 ' key: 123\n' 1697 ' value: 456\n' 1698 '}\n' 1699 'map_uint64_uint64 {\n' 1700 ' key: 8589934592\n' 1701 ' value: 17179869184\n' 1702 '}\n' 1703 'map_string_string {\n' 1704 ' key: "abc"\n' 1705 ' value: "123"\n' 1706 '}\n' 1707 'map_int32_foreign_message {\n' 1708 ' key: 111\n' 1709 ' value {\n' 1710 ' c: 5\n' 1711 ' }\n' 1712 '}\n') 1713 message = map_unittest_pb2.TestMap() 1714 text_format.Parse(text, message) 1715 1716 self.assertEqual(-456, message.map_int32_int32[-123]) 1717 self.assertEqual(-2**34, message.map_int64_int64[-2**33]) 1718 self.assertEqual(456, message.map_uint32_uint32[123]) 1719 self.assertEqual(2**34, message.map_uint64_uint64[2**33]) 1720 self.assertEqual('123', message.map_string_string['abc']) 1721 self.assertEqual(5, message.map_int32_foreign_message[111].c) 1722 1723 1724class Proto3Tests(unittest.TestCase): 1725 1726 def testPrintMessageExpandAny(self): 1727 packed_message = unittest_pb2.OneString() 1728 packed_message.data = 'string' 1729 message = any_test_pb2.TestAny() 1730 message.any_value.Pack(packed_message) 1731 self.assertEqual( 1732 text_format.MessageToString(message, 1733 descriptor_pool=descriptor_pool.Default()), 1734 'any_value {\n' 1735 ' [type.googleapis.com/protobuf_unittest.OneString] {\n' 1736 ' data: "string"\n' 1737 ' }\n' 1738 '}\n') 1739 1740 def testTopAnyMessage(self): 1741 packed_msg = unittest_pb2.OneString() 1742 msg = any_pb2.Any() 1743 msg.Pack(packed_msg) 1744 text = text_format.MessageToString(msg) 1745 other_msg = text_format.Parse(text, any_pb2.Any()) 1746 self.assertEqual(msg, other_msg) 1747 1748 def testPrintMessageExpandAnyRepeated(self): 1749 packed_message = unittest_pb2.OneString() 1750 message = any_test_pb2.TestAny() 1751 packed_message.data = 'string0' 1752 message.repeated_any_value.add().Pack(packed_message) 1753 packed_message.data = 'string1' 1754 message.repeated_any_value.add().Pack(packed_message) 1755 self.assertEqual( 1756 text_format.MessageToString(message), 1757 'repeated_any_value {\n' 1758 ' [type.googleapis.com/protobuf_unittest.OneString] {\n' 1759 ' data: "string0"\n' 1760 ' }\n' 1761 '}\n' 1762 'repeated_any_value {\n' 1763 ' [type.googleapis.com/protobuf_unittest.OneString] {\n' 1764 ' data: "string1"\n' 1765 ' }\n' 1766 '}\n') 1767 1768 def testPrintMessageExpandAnyDescriptorPoolMissingType(self): 1769 packed_message = unittest_pb2.OneString() 1770 packed_message.data = 'string' 1771 message = any_test_pb2.TestAny() 1772 message.any_value.Pack(packed_message) 1773 empty_pool = descriptor_pool.DescriptorPool() 1774 self.assertEqual( 1775 text_format.MessageToString(message, descriptor_pool=empty_pool), 1776 'any_value {\n' 1777 ' type_url: "type.googleapis.com/protobuf_unittest.OneString"\n' 1778 ' value: "\\n\\006string"\n' 1779 '}\n') 1780 1781 def testPrintMessageExpandAnyPointyBrackets(self): 1782 packed_message = unittest_pb2.OneString() 1783 packed_message.data = 'string' 1784 message = any_test_pb2.TestAny() 1785 message.any_value.Pack(packed_message) 1786 self.assertEqual( 1787 text_format.MessageToString(message, 1788 pointy_brackets=True), 1789 'any_value <\n' 1790 ' [type.googleapis.com/protobuf_unittest.OneString] <\n' 1791 ' data: "string"\n' 1792 ' >\n' 1793 '>\n') 1794 1795 def testPrintMessageExpandAnyAsOneLine(self): 1796 packed_message = unittest_pb2.OneString() 1797 packed_message.data = 'string' 1798 message = any_test_pb2.TestAny() 1799 message.any_value.Pack(packed_message) 1800 self.assertEqual( 1801 text_format.MessageToString(message, 1802 as_one_line=True), 1803 'any_value {' 1804 ' [type.googleapis.com/protobuf_unittest.OneString]' 1805 ' { data: "string" } ' 1806 '}') 1807 1808 def testPrintMessageExpandAnyAsOneLinePointyBrackets(self): 1809 packed_message = unittest_pb2.OneString() 1810 packed_message.data = 'string' 1811 message = any_test_pb2.TestAny() 1812 message.any_value.Pack(packed_message) 1813 self.assertEqual( 1814 text_format.MessageToString(message, 1815 as_one_line=True, 1816 pointy_brackets=True, 1817 descriptor_pool=descriptor_pool.Default()), 1818 'any_value <' 1819 ' [type.googleapis.com/protobuf_unittest.OneString]' 1820 ' < data: "string" > ' 1821 '>') 1822 1823 def testPrintAndParseMessageInvalidAny(self): 1824 packed_message = unittest_pb2.OneString() 1825 packed_message.data = 'string' 1826 message = any_test_pb2.TestAny() 1827 message.any_value.Pack(packed_message) 1828 # Only include string after last '/' in type_url. 1829 message.any_value.type_url = message.any_value.TypeName() 1830 text = text_format.MessageToString(message) 1831 self.assertEqual( 1832 text, 'any_value {\n' 1833 ' type_url: "protobuf_unittest.OneString"\n' 1834 ' value: "\\n\\006string"\n' 1835 '}\n') 1836 1837 parsed_message = any_test_pb2.TestAny() 1838 text_format.Parse(text, parsed_message) 1839 self.assertEqual(message, parsed_message) 1840 1841 def testUnknownEnums(self): 1842 message = unittest_proto3_arena_pb2.TestAllTypes() 1843 message2 = unittest_proto3_arena_pb2.TestAllTypes() 1844 message.optional_nested_enum = 999 1845 text_string = text_format.MessageToString(message) 1846 text_format.Parse(text_string, message2) 1847 self.assertEqual(999, message2.optional_nested_enum) 1848 1849 def testMergeExpandedAny(self): 1850 message = any_test_pb2.TestAny() 1851 text = ('any_value {\n' 1852 ' [type.googleapis.com/protobuf_unittest.OneString] {\n' 1853 ' data: "string"\n' 1854 ' }\n' 1855 '}\n') 1856 text_format.Merge(text, message) 1857 packed_message = unittest_pb2.OneString() 1858 message.any_value.Unpack(packed_message) 1859 self.assertEqual('string', packed_message.data) 1860 message.Clear() 1861 text_format.Parse(text, message) 1862 packed_message = unittest_pb2.OneString() 1863 message.any_value.Unpack(packed_message) 1864 self.assertEqual('string', packed_message.data) 1865 1866 def testMergeExpandedAnyRepeated(self): 1867 message = any_test_pb2.TestAny() 1868 text = ('repeated_any_value {\n' 1869 ' [type.googleapis.com/protobuf_unittest.OneString] {\n' 1870 ' data: "string0"\n' 1871 ' }\n' 1872 '}\n' 1873 'repeated_any_value {\n' 1874 ' [type.googleapis.com/protobuf_unittest.OneString] {\n' 1875 ' data: "string1"\n' 1876 ' }\n' 1877 '}\n') 1878 text_format.Merge(text, message) 1879 packed_message = unittest_pb2.OneString() 1880 message.repeated_any_value[0].Unpack(packed_message) 1881 self.assertEqual('string0', packed_message.data) 1882 message.repeated_any_value[1].Unpack(packed_message) 1883 self.assertEqual('string1', packed_message.data) 1884 1885 def testMergeExpandedAnyPointyBrackets(self): 1886 message = any_test_pb2.TestAny() 1887 text = ('any_value {\n' 1888 ' [type.googleapis.com/protobuf_unittest.OneString] <\n' 1889 ' data: "string"\n' 1890 ' >\n' 1891 '}\n') 1892 text_format.Merge(text, message) 1893 packed_message = unittest_pb2.OneString() 1894 message.any_value.Unpack(packed_message) 1895 self.assertEqual('string', packed_message.data) 1896 1897 def testMergeAlternativeUrl(self): 1898 message = any_test_pb2.TestAny() 1899 text = ('any_value {\n' 1900 ' [type.otherapi.com/protobuf_unittest.OneString] {\n' 1901 ' data: "string"\n' 1902 ' }\n' 1903 '}\n') 1904 text_format.Merge(text, message) 1905 packed_message = unittest_pb2.OneString() 1906 self.assertEqual('type.otherapi.com/protobuf_unittest.OneString', 1907 message.any_value.type_url) 1908 1909 def testMergeExpandedAnyDescriptorPoolMissingType(self): 1910 message = any_test_pb2.TestAny() 1911 text = ('any_value {\n' 1912 ' [type.googleapis.com/protobuf_unittest.OneString] {\n' 1913 ' data: "string"\n' 1914 ' }\n' 1915 '}\n') 1916 with self.assertRaises(text_format.ParseError) as e: 1917 empty_pool = descriptor_pool.DescriptorPool() 1918 text_format.Merge(text, message, descriptor_pool=empty_pool) 1919 self.assertEqual( 1920 str(e.exception), 1921 'Type protobuf_unittest.OneString not found in descriptor pool') 1922 1923 def testMergeUnexpandedAny(self): 1924 text = ('any_value {\n' 1925 ' type_url: "type.googleapis.com/protobuf_unittest.OneString"\n' 1926 ' value: "\\n\\006string"\n' 1927 '}\n') 1928 message = any_test_pb2.TestAny() 1929 text_format.Merge(text, message) 1930 packed_message = unittest_pb2.OneString() 1931 message.any_value.Unpack(packed_message) 1932 self.assertEqual('string', packed_message.data) 1933 1934 def testMergeMissingAnyEndToken(self): 1935 message = any_test_pb2.TestAny() 1936 text = ('any_value {\n' 1937 ' [type.googleapis.com/protobuf_unittest.OneString] {\n' 1938 ' data: "string"\n') 1939 with self.assertRaises(text_format.ParseError) as e: 1940 text_format.Merge(text, message) 1941 self.assertEqual(str(e.exception), '3:11 : Expected "}".') 1942 1943 def testParseExpandedAnyListValue(self): 1944 any_msg = any_pb2.Any() 1945 any_msg.Pack(struct_pb2.ListValue()) 1946 msg = any_test_pb2.TestAny(any_value=any_msg) 1947 text = ('any_value {\n' 1948 ' [type.googleapis.com/google.protobuf.ListValue] {}\n' 1949 '}\n') 1950 parsed_msg = text_format.Parse(text, any_test_pb2.TestAny()) 1951 self.assertEqual(msg, parsed_msg) 1952 1953 def testProto3Optional(self): 1954 msg = test_proto3_optional_pb2.TestProto3Optional() 1955 self.assertEqual(text_format.MessageToString(msg), '') 1956 msg.optional_int32 = 0 1957 msg.optional_float = 0.0 1958 msg.optional_string = '' 1959 msg.optional_nested_message.bb = 0 1960 text = ('optional_int32: 0\n' 1961 'optional_float: 0.0\n' 1962 'optional_string: ""\n' 1963 'optional_nested_message {\n' 1964 ' bb: 0\n' 1965 '}\n') 1966 self.assertEqual(text_format.MessageToString(msg), text) 1967 msg2 = test_proto3_optional_pb2.TestProto3Optional() 1968 text_format.Parse(text, msg2) 1969 self.assertEqual(text_format.MessageToString(msg2), text) 1970 1971 1972class TokenizerTest(unittest.TestCase): 1973 1974 def testSimpleTokenCases(self): 1975 text = ('identifier1:"string1"\n \n\n' 1976 'identifier2 : \n \n123 \n identifier3 :\'string\'\n' 1977 'identifiER_4 : 1.1e+2 ID5:-0.23 ID6:\'aaaa\\\'bbbb\'\n' 1978 'ID7 : "aa\\"bb"\n\n\n\n ID8: {A:inf B:-inf C:true D:false}\n' 1979 'ID9: 22 ID10: -111111111111111111 ID11: -22\n' 1980 'ID12: 2222222222222222222 ID13: 1.23456f ID14: 1.2e+2f ' 1981 'false_bool: 0 true_BOOL:t \n true_bool1: 1 false_BOOL1:f ' 1982 'False_bool: False True_bool: True X:iNf Y:-inF Z:nAN') 1983 tokenizer = text_format.Tokenizer(text.splitlines()) 1984 methods = [(tokenizer.ConsumeIdentifier, 'identifier1'), ':', 1985 (tokenizer.ConsumeString, 'string1'), 1986 (tokenizer.ConsumeIdentifier, 'identifier2'), ':', 1987 (tokenizer.ConsumeInteger, 123), 1988 (tokenizer.ConsumeIdentifier, 'identifier3'), ':', 1989 (tokenizer.ConsumeString, 'string'), 1990 (tokenizer.ConsumeIdentifier, 'identifiER_4'), ':', 1991 (tokenizer.ConsumeFloat, 1.1e+2), 1992 (tokenizer.ConsumeIdentifier, 'ID5'), ':', 1993 (tokenizer.ConsumeFloat, -0.23), 1994 (tokenizer.ConsumeIdentifier, 'ID6'), ':', 1995 (tokenizer.ConsumeString, 'aaaa\'bbbb'), 1996 (tokenizer.ConsumeIdentifier, 'ID7'), ':', 1997 (tokenizer.ConsumeString, 'aa\"bb'), 1998 (tokenizer.ConsumeIdentifier, 'ID8'), ':', '{', 1999 (tokenizer.ConsumeIdentifier, 'A'), ':', 2000 (tokenizer.ConsumeFloat, float('inf')), 2001 (tokenizer.ConsumeIdentifier, 'B'), ':', 2002 (tokenizer.ConsumeFloat, -float('inf')), 2003 (tokenizer.ConsumeIdentifier, 'C'), ':', 2004 (tokenizer.ConsumeBool, True), 2005 (tokenizer.ConsumeIdentifier, 'D'), ':', 2006 (tokenizer.ConsumeBool, False), '}', 2007 (tokenizer.ConsumeIdentifier, 'ID9'), ':', 2008 (tokenizer.ConsumeInteger, 22), 2009 (tokenizer.ConsumeIdentifier, 'ID10'), ':', 2010 (tokenizer.ConsumeInteger, -111111111111111111), 2011 (tokenizer.ConsumeIdentifier, 'ID11'), ':', 2012 (tokenizer.ConsumeInteger, -22), 2013 (tokenizer.ConsumeIdentifier, 'ID12'), ':', 2014 (tokenizer.ConsumeInteger, 2222222222222222222), 2015 (tokenizer.ConsumeIdentifier, 'ID13'), ':', 2016 (tokenizer.ConsumeFloat, 1.23456), 2017 (tokenizer.ConsumeIdentifier, 'ID14'), ':', 2018 (tokenizer.ConsumeFloat, 1.2e+2), 2019 (tokenizer.ConsumeIdentifier, 'false_bool'), ':', 2020 (tokenizer.ConsumeBool, False), 2021 (tokenizer.ConsumeIdentifier, 'true_BOOL'), ':', 2022 (tokenizer.ConsumeBool, True), 2023 (tokenizer.ConsumeIdentifier, 'true_bool1'), ':', 2024 (tokenizer.ConsumeBool, True), 2025 (tokenizer.ConsumeIdentifier, 'false_BOOL1'), ':', 2026 (tokenizer.ConsumeBool, False), 2027 (tokenizer.ConsumeIdentifier, 'False_bool'), ':', 2028 (tokenizer.ConsumeBool, False), 2029 (tokenizer.ConsumeIdentifier, 'True_bool'), ':', 2030 (tokenizer.ConsumeBool, True), 2031 (tokenizer.ConsumeIdentifier, 'X'), ':', 2032 (tokenizer.ConsumeFloat, float('inf')), 2033 (tokenizer.ConsumeIdentifier, 'Y'), ':', 2034 (tokenizer.ConsumeFloat, float('-inf')), 2035 (tokenizer.ConsumeIdentifier, 'Z'), ':', 2036 (tokenizer.ConsumeFloat, float('nan'))] 2037 2038 i = 0 2039 while not tokenizer.AtEnd(): 2040 m = methods[i] 2041 if isinstance(m, str): 2042 token = tokenizer.token 2043 self.assertEqual(token, m) 2044 tokenizer.NextToken() 2045 elif isinstance(m[1], float) and math.isnan(m[1]): 2046 self.assertTrue(math.isnan(m[0]())) 2047 else: 2048 self.assertEqual(m[1], m[0]()) 2049 i += 1 2050 2051 def testConsumeAbstractIntegers(self): 2052 # This test only tests the failures in the integer parsing methods as well 2053 # as the '0' special cases. 2054 int64_max = (1 << 63) - 1 2055 uint32_max = (1 << 32) - 1 2056 text = '-1 %d %d' % (uint32_max + 1, int64_max + 1) 2057 tokenizer = text_format.Tokenizer(text.splitlines()) 2058 self.assertEqual(-1, tokenizer.ConsumeInteger()) 2059 2060 self.assertEqual(uint32_max + 1, tokenizer.ConsumeInteger()) 2061 2062 self.assertEqual(int64_max + 1, tokenizer.ConsumeInteger()) 2063 self.assertTrue(tokenizer.AtEnd()) 2064 2065 text = '-0 0 0 1.2' 2066 tokenizer = text_format.Tokenizer(text.splitlines()) 2067 self.assertEqual(0, tokenizer.ConsumeInteger()) 2068 self.assertEqual(0, tokenizer.ConsumeInteger()) 2069 self.assertEqual(True, tokenizer.TryConsumeInteger()) 2070 self.assertEqual(False, tokenizer.TryConsumeInteger()) 2071 with self.assertRaises(text_format.ParseError): 2072 tokenizer.ConsumeInteger() 2073 self.assertEqual(1.2, tokenizer.ConsumeFloat()) 2074 self.assertTrue(tokenizer.AtEnd()) 2075 2076 def testConsumeIntegers(self): 2077 # This test only tests the failures in the integer parsing methods as well 2078 # as the '0' special cases. 2079 int64_max = (1 << 63) - 1 2080 uint32_max = (1 << 32) - 1 2081 text = '-1 %d %d' % (uint32_max + 1, int64_max + 1) 2082 tokenizer = text_format.Tokenizer(text.splitlines()) 2083 self.assertRaises(text_format.ParseError, 2084 text_format._ConsumeUint32, tokenizer) 2085 self.assertRaises(text_format.ParseError, 2086 text_format._ConsumeUint64, tokenizer) 2087 self.assertEqual(-1, text_format._ConsumeInt32(tokenizer)) 2088 2089 self.assertRaises(text_format.ParseError, 2090 text_format._ConsumeUint32, tokenizer) 2091 self.assertRaises(text_format.ParseError, 2092 text_format._ConsumeInt32, tokenizer) 2093 self.assertEqual(uint32_max + 1, text_format._ConsumeInt64(tokenizer)) 2094 2095 self.assertRaises(text_format.ParseError, 2096 text_format._ConsumeInt64, tokenizer) 2097 self.assertEqual(int64_max + 1, text_format._ConsumeUint64(tokenizer)) 2098 self.assertTrue(tokenizer.AtEnd()) 2099 2100 text = '-0 -0 0 0' 2101 tokenizer = text_format.Tokenizer(text.splitlines()) 2102 self.assertEqual(0, text_format._ConsumeUint32(tokenizer)) 2103 self.assertEqual(0, text_format._ConsumeUint64(tokenizer)) 2104 self.assertEqual(0, text_format._ConsumeUint32(tokenizer)) 2105 self.assertEqual(0, text_format._ConsumeUint64(tokenizer)) 2106 self.assertTrue(tokenizer.AtEnd()) 2107 2108 def testConsumeOctalIntegers(self): 2109 """Test support for C style octal integers.""" 2110 text = '00 -00 04 0755 -010 007 -0033 08 -09 01' 2111 tokenizer = text_format.Tokenizer(text.splitlines()) 2112 self.assertEqual(0, tokenizer.ConsumeInteger()) 2113 self.assertEqual(0, tokenizer.ConsumeInteger()) 2114 self.assertEqual(4, tokenizer.ConsumeInteger()) 2115 self.assertEqual(0o755, tokenizer.ConsumeInteger()) 2116 self.assertEqual(-0o10, tokenizer.ConsumeInteger()) 2117 self.assertEqual(7, tokenizer.ConsumeInteger()) 2118 self.assertEqual(-0o033, tokenizer.ConsumeInteger()) 2119 with self.assertRaises(text_format.ParseError): 2120 tokenizer.ConsumeInteger() # 08 2121 tokenizer.NextToken() 2122 with self.assertRaises(text_format.ParseError): 2123 tokenizer.ConsumeInteger() # -09 2124 tokenizer.NextToken() 2125 self.assertEqual(1, tokenizer.ConsumeInteger()) 2126 self.assertTrue(tokenizer.AtEnd()) 2127 2128 def testConsumeByteString(self): 2129 text = '"string1\'' 2130 tokenizer = text_format.Tokenizer(text.splitlines()) 2131 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString) 2132 2133 text = 'string1"' 2134 tokenizer = text_format.Tokenizer(text.splitlines()) 2135 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString) 2136 2137 text = '\n"\\xt"' 2138 tokenizer = text_format.Tokenizer(text.splitlines()) 2139 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString) 2140 2141 text = '\n"\\"' 2142 tokenizer = text_format.Tokenizer(text.splitlines()) 2143 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString) 2144 2145 text = '\n"\\x"' 2146 tokenizer = text_format.Tokenizer(text.splitlines()) 2147 self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString) 2148 2149 def testConsumeBool(self): 2150 text = 'not-a-bool' 2151 tokenizer = text_format.Tokenizer(text.splitlines()) 2152 self.assertRaises(text_format.ParseError, tokenizer.ConsumeBool) 2153 2154 def testSkipComment(self): 2155 tokenizer = text_format.Tokenizer('# some comment'.splitlines()) 2156 self.assertTrue(tokenizer.AtEnd()) 2157 self.assertRaises(text_format.ParseError, tokenizer.ConsumeComment) 2158 2159 def testConsumeComment(self): 2160 tokenizer = text_format.Tokenizer('# some comment'.splitlines(), 2161 skip_comments=False) 2162 self.assertFalse(tokenizer.AtEnd()) 2163 self.assertEqual('# some comment', tokenizer.ConsumeComment()) 2164 self.assertTrue(tokenizer.AtEnd()) 2165 2166 def testConsumeTwoComments(self): 2167 text = '# some comment\n# another comment' 2168 tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False) 2169 self.assertEqual('# some comment', tokenizer.ConsumeComment()) 2170 self.assertFalse(tokenizer.AtEnd()) 2171 self.assertEqual('# another comment', tokenizer.ConsumeComment()) 2172 self.assertTrue(tokenizer.AtEnd()) 2173 2174 def testConsumeTrailingComment(self): 2175 text = 'some_number: 4\n# some comment' 2176 tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False) 2177 self.assertRaises(text_format.ParseError, tokenizer.ConsumeComment) 2178 2179 self.assertEqual('some_number', tokenizer.ConsumeIdentifier()) 2180 self.assertEqual(tokenizer.token, ':') 2181 tokenizer.NextToken() 2182 self.assertRaises(text_format.ParseError, tokenizer.ConsumeComment) 2183 self.assertEqual(4, tokenizer.ConsumeInteger()) 2184 self.assertFalse(tokenizer.AtEnd()) 2185 2186 self.assertEqual('# some comment', tokenizer.ConsumeComment()) 2187 self.assertTrue(tokenizer.AtEnd()) 2188 2189 def testConsumeLineComment(self): 2190 tokenizer = text_format.Tokenizer('# some comment'.splitlines(), 2191 skip_comments=False) 2192 self.assertFalse(tokenizer.AtEnd()) 2193 self.assertEqual((False, '# some comment'), 2194 tokenizer.ConsumeCommentOrTrailingComment()) 2195 self.assertTrue(tokenizer.AtEnd()) 2196 2197 def testConsumeTwoLineComments(self): 2198 text = '# some comment\n# another comment' 2199 tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False) 2200 self.assertEqual((False, '# some comment'), 2201 tokenizer.ConsumeCommentOrTrailingComment()) 2202 self.assertFalse(tokenizer.AtEnd()) 2203 self.assertEqual((False, '# another comment'), 2204 tokenizer.ConsumeCommentOrTrailingComment()) 2205 self.assertTrue(tokenizer.AtEnd()) 2206 2207 def testConsumeAndCheckTrailingComment(self): 2208 text = 'some_number: 4 # some comment' # trailing comment on the same line 2209 tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False) 2210 self.assertRaises(text_format.ParseError, 2211 tokenizer.ConsumeCommentOrTrailingComment) 2212 2213 self.assertEqual('some_number', tokenizer.ConsumeIdentifier()) 2214 self.assertEqual(tokenizer.token, ':') 2215 tokenizer.NextToken() 2216 self.assertRaises(text_format.ParseError, 2217 tokenizer.ConsumeCommentOrTrailingComment) 2218 self.assertEqual(4, tokenizer.ConsumeInteger()) 2219 self.assertFalse(tokenizer.AtEnd()) 2220 2221 self.assertEqual((True, '# some comment'), 2222 tokenizer.ConsumeCommentOrTrailingComment()) 2223 self.assertTrue(tokenizer.AtEnd()) 2224 2225 def testHashinComment(self): 2226 text = 'some_number: 4 # some comment # not a new comment' 2227 tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False) 2228 self.assertEqual('some_number', tokenizer.ConsumeIdentifier()) 2229 self.assertEqual(tokenizer.token, ':') 2230 tokenizer.NextToken() 2231 self.assertEqual(4, tokenizer.ConsumeInteger()) 2232 self.assertEqual((True, '# some comment # not a new comment'), 2233 tokenizer.ConsumeCommentOrTrailingComment()) 2234 self.assertTrue(tokenizer.AtEnd()) 2235 2236 def testHugeString(self): 2237 # With pathologic backtracking, fails with Forge OOM. 2238 text = '"' + 'a' * (10 * 1024 * 1024) + '"' 2239 tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False) 2240 tokenizer.ConsumeString() 2241 2242 2243# Tests for pretty printer functionality. 2244@_parameterized.parameters((unittest_pb2), (unittest_proto3_arena_pb2)) 2245class PrettyPrinterTest(TextFormatBase): 2246 2247 def testPrettyPrintNoMatch(self, message_module): 2248 2249 def printer(message, indent, as_one_line): 2250 del message, indent, as_one_line 2251 return None 2252 2253 message = message_module.TestAllTypes() 2254 msg = message.repeated_nested_message.add() 2255 msg.bb = 42 2256 self.CompareToGoldenText( 2257 text_format.MessageToString( 2258 message, as_one_line=True, message_formatter=printer), 2259 'repeated_nested_message { bb: 42 }') 2260 2261 def testPrettyPrintOneLine(self, message_module): 2262 2263 def printer(m, indent, as_one_line): 2264 del indent, as_one_line 2265 if m.DESCRIPTOR == message_module.TestAllTypes.NestedMessage.DESCRIPTOR: 2266 return 'My lucky number is %s' % m.bb 2267 2268 message = message_module.TestAllTypes() 2269 msg = message.repeated_nested_message.add() 2270 msg.bb = 42 2271 self.CompareToGoldenText( 2272 text_format.MessageToString( 2273 message, as_one_line=True, message_formatter=printer), 2274 'repeated_nested_message { My lucky number is 42 }') 2275 2276 def testPrettyPrintMultiLine(self, message_module): 2277 2278 def printer(m, indent, as_one_line): 2279 if m.DESCRIPTOR == message_module.TestAllTypes.NestedMessage.DESCRIPTOR: 2280 line_deliminator = (' ' if as_one_line else '\n') + ' ' * indent 2281 return 'My lucky number is:%s%s' % (line_deliminator, m.bb) 2282 return None 2283 2284 message = message_module.TestAllTypes() 2285 msg = message.repeated_nested_message.add() 2286 msg.bb = 42 2287 self.CompareToGoldenText( 2288 text_format.MessageToString( 2289 message, as_one_line=True, message_formatter=printer), 2290 'repeated_nested_message { My lucky number is: 42 }') 2291 self.CompareToGoldenText( 2292 text_format.MessageToString( 2293 message, as_one_line=False, message_formatter=printer), 2294 'repeated_nested_message {\n My lucky number is:\n 42\n}\n') 2295 2296 def testPrettyPrintEntireMessage(self, message_module): 2297 2298 def printer(m, indent, as_one_line): 2299 del indent, as_one_line 2300 if m.DESCRIPTOR == message_module.TestAllTypes.DESCRIPTOR: 2301 return 'The is the message!' 2302 return None 2303 2304 message = message_module.TestAllTypes() 2305 self.CompareToGoldenText( 2306 text_format.MessageToString( 2307 message, as_one_line=False, message_formatter=printer), 2308 'The is the message!\n') 2309 self.CompareToGoldenText( 2310 text_format.MessageToString( 2311 message, as_one_line=True, message_formatter=printer), 2312 'The is the message!') 2313 2314 def testPrettyPrintMultipleParts(self, message_module): 2315 2316 def printer(m, indent, as_one_line): 2317 del indent, as_one_line 2318 if m.DESCRIPTOR == message_module.TestAllTypes.NestedMessage.DESCRIPTOR: 2319 return 'My lucky number is %s' % m.bb 2320 return None 2321 2322 message = message_module.TestAllTypes() 2323 message.optional_int32 = 61 2324 msg = message.repeated_nested_message.add() 2325 msg.bb = 42 2326 msg = message.repeated_nested_message.add() 2327 msg.bb = 99 2328 msg = message.optional_nested_message 2329 msg.bb = 1 2330 self.CompareToGoldenText( 2331 text_format.MessageToString( 2332 message, as_one_line=True, message_formatter=printer), 2333 ('optional_int32: 61 ' 2334 'optional_nested_message { My lucky number is 1 } ' 2335 'repeated_nested_message { My lucky number is 42 } ' 2336 'repeated_nested_message { My lucky number is 99 }')) 2337 2338 out = text_format.TextWriter(False) 2339 text_format.PrintField( 2340 message_module.TestAllTypes.DESCRIPTOR.fields_by_name[ 2341 'optional_nested_message'], 2342 message.optional_nested_message, 2343 out, 2344 message_formatter=printer) 2345 self.assertEqual( 2346 'optional_nested_message {\n My lucky number is 1\n}\n', 2347 out.getvalue()) 2348 out.close() 2349 2350 out = text_format.TextWriter(False) 2351 text_format.PrintFieldValue( 2352 message_module.TestAllTypes.DESCRIPTOR.fields_by_name[ 2353 'optional_nested_message'], 2354 message.optional_nested_message, 2355 out, 2356 message_formatter=printer) 2357 self.assertEqual( 2358 '{\n My lucky number is 1\n}', 2359 out.getvalue()) 2360 out.close() 2361 2362 2363class WhitespaceTest(TextFormatBase): 2364 2365 def setUp(self): 2366 self.out = text_format.TextWriter(False) 2367 self.addCleanup(self.out.close) 2368 self.message = unittest_pb2.NestedTestAllTypes() 2369 self.message.child.payload.optional_string = 'value' 2370 self.field = self.message.DESCRIPTOR.fields_by_name['child'] 2371 self.value = self.message.child 2372 2373 def testMessageToString(self): 2374 self.CompareToGoldenText( 2375 text_format.MessageToString(self.message), 2376 textwrap.dedent("""\ 2377 child { 2378 payload { 2379 optional_string: "value" 2380 } 2381 } 2382 """)) 2383 2384 def testPrintMessage(self): 2385 text_format.PrintMessage(self.message, self.out) 2386 self.CompareToGoldenText( 2387 self.out.getvalue(), 2388 textwrap.dedent("""\ 2389 child { 2390 payload { 2391 optional_string: "value" 2392 } 2393 } 2394 """)) 2395 2396 def testPrintField(self): 2397 text_format.PrintField(self.field, self.value, self.out) 2398 self.CompareToGoldenText( 2399 self.out.getvalue(), 2400 textwrap.dedent("""\ 2401 child { 2402 payload { 2403 optional_string: "value" 2404 } 2405 } 2406 """)) 2407 2408 def testPrintFieldValue(self): 2409 text_format.PrintFieldValue( 2410 self.field, self.value, self.out) 2411 self.CompareToGoldenText( 2412 self.out.getvalue(), 2413 textwrap.dedent("""\ 2414 { 2415 payload { 2416 optional_string: "value" 2417 } 2418 }""")) 2419 2420 2421class OptionalColonMessageToStringTest(unittest.TestCase): 2422 2423 def testForcePrintOptionalColon(self): 2424 packed_message = unittest_pb2.OneString() 2425 packed_message.data = 'string' 2426 message = any_test_pb2.TestAny() 2427 message.any_value.Pack(packed_message) 2428 output = text_format.MessageToString( 2429 message, 2430 force_colon=True) 2431 expected = ('any_value: {\n' 2432 ' [type.googleapis.com/protobuf_unittest.OneString]: {\n' 2433 ' data: "string"\n' 2434 ' }\n' 2435 '}\n') 2436 self.assertEqual(expected, output) 2437 2438 def testPrintShortFormatRepeatedFields(self): 2439 message = unittest_pb2.TestAllTypes() 2440 message.repeated_int32.append(1) 2441 output = text_format.MessageToString( 2442 message, use_short_repeated_primitives=True, force_colon=True) 2443 self.assertEqual('repeated_int32: [1]\n', output) 2444 2445 2446if __name__ == '__main__': 2447 unittest.main() 2448