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