text_format_test.py 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512
  1. #! /usr/bin/env python
  2. #
  3. # Protocol Buffers - Google's data interchange format
  4. # Copyright 2008 Google Inc. All rights reserved.
  5. # https://developers.google.com/protocol-buffers/
  6. #
  7. # Redistribution and use in source and binary forms, with or without
  8. # modification, are permitted provided that the following conditions are
  9. # met:
  10. #
  11. # * Redistributions of source code must retain the above copyright
  12. # notice, this list of conditions and the following disclaimer.
  13. # * Redistributions in binary form must reproduce the above
  14. # copyright notice, this list of conditions and the following disclaimer
  15. # in the documentation and/or other materials provided with the
  16. # distribution.
  17. # * Neither the name of Google Inc. nor the names of its
  18. # contributors may be used to endorse or promote products derived from
  19. # this software without specific prior written permission.
  20. #
  21. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  25. # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  26. # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  27. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  28. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  29. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  31. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. """Test for google.protobuf.text_format."""
  33. __author__ = 'kenton@google.com (Kenton Varda)'
  34. import re
  35. import six
  36. import string
  37. try:
  38. import unittest2 as unittest # PY26, pylint: disable=g-import-not-at-top
  39. except ImportError:
  40. import unittest # pylint: disable=g-import-not-at-top
  41. from google.protobuf.internal import _parameterized
  42. from google.protobuf import any_test_pb2
  43. from google.protobuf import map_unittest_pb2
  44. from google.protobuf import unittest_mset_pb2
  45. from google.protobuf import unittest_pb2
  46. from google.protobuf import unittest_proto3_arena_pb2
  47. from google.protobuf.internal import api_implementation
  48. from google.protobuf.internal import any_test_pb2 as test_extend_any
  49. from google.protobuf.internal import test_util
  50. from google.protobuf.internal import message_set_extensions_pb2
  51. from google.protobuf import descriptor_pool
  52. from google.protobuf import text_format
  53. # Low-level nuts-n-bolts tests.
  54. class SimpleTextFormatTests(unittest.TestCase):
  55. # The members of _QUOTES are formatted into a regexp template that
  56. # expects single characters. Therefore it's an error (in addition to being
  57. # non-sensical in the first place) to try to specify a "quote mark" that is
  58. # more than one character.
  59. def testQuoteMarksAreSingleChars(self):
  60. for quote in text_format._QUOTES:
  61. self.assertEqual(1, len(quote))
  62. # Base class with some common functionality.
  63. class TextFormatBase(unittest.TestCase):
  64. def ReadGolden(self, golden_filename):
  65. with test_util.GoldenFile(golden_filename) as f:
  66. return (f.readlines() if str is bytes else # PY3
  67. [golden_line.decode('utf-8') for golden_line in f])
  68. def CompareToGoldenFile(self, text, golden_filename):
  69. golden_lines = self.ReadGolden(golden_filename)
  70. self.assertMultiLineEqual(text, ''.join(golden_lines))
  71. def CompareToGoldenText(self, text, golden_text):
  72. self.assertEqual(text, golden_text)
  73. def RemoveRedundantZeros(self, text):
  74. # Some platforms print 1e+5 as 1e+005. This is fine, but we need to remove
  75. # these zeros in order to match the golden file.
  76. text = text.replace('e+0','e+').replace('e+0','e+') \
  77. .replace('e-0','e-').replace('e-0','e-')
  78. # Floating point fields are printed with .0 suffix even if they are
  79. # actualy integer numbers.
  80. text = re.compile(r'\.0$', re.MULTILINE).sub('', text)
  81. return text
  82. @_parameterized.Parameters((unittest_pb2), (unittest_proto3_arena_pb2))
  83. class TextFormatTest(TextFormatBase):
  84. def testPrintExotic(self, message_module):
  85. message = message_module.TestAllTypes()
  86. message.repeated_int64.append(-9223372036854775808)
  87. message.repeated_uint64.append(18446744073709551615)
  88. message.repeated_double.append(123.456)
  89. message.repeated_double.append(1.23e22)
  90. message.repeated_double.append(1.23e-18)
  91. message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
  92. message.repeated_string.append(u'\u00fc\ua71f')
  93. self.CompareToGoldenText(
  94. self.RemoveRedundantZeros(text_format.MessageToString(message)),
  95. 'repeated_int64: -9223372036854775808\n'
  96. 'repeated_uint64: 18446744073709551615\n'
  97. 'repeated_double: 123.456\n'
  98. 'repeated_double: 1.23e+22\n'
  99. 'repeated_double: 1.23e-18\n'
  100. 'repeated_string:'
  101. ' "\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
  102. 'repeated_string: "\\303\\274\\352\\234\\237"\n')
  103. def testPrintExoticUnicodeSubclass(self, message_module):
  104. class UnicodeSub(six.text_type):
  105. pass
  106. message = message_module.TestAllTypes()
  107. message.repeated_string.append(UnicodeSub(u'\u00fc\ua71f'))
  108. self.CompareToGoldenText(
  109. text_format.MessageToString(message),
  110. 'repeated_string: "\\303\\274\\352\\234\\237"\n')
  111. def testPrintNestedMessageAsOneLine(self, message_module):
  112. message = message_module.TestAllTypes()
  113. msg = message.repeated_nested_message.add()
  114. msg.bb = 42
  115. self.CompareToGoldenText(
  116. text_format.MessageToString(message, as_one_line=True),
  117. 'repeated_nested_message { bb: 42 }')
  118. def testPrintRepeatedFieldsAsOneLine(self, message_module):
  119. message = message_module.TestAllTypes()
  120. message.repeated_int32.append(1)
  121. message.repeated_int32.append(1)
  122. message.repeated_int32.append(3)
  123. message.repeated_string.append('Google')
  124. message.repeated_string.append('Zurich')
  125. self.CompareToGoldenText(
  126. text_format.MessageToString(message, as_one_line=True),
  127. 'repeated_int32: 1 repeated_int32: 1 repeated_int32: 3 '
  128. 'repeated_string: "Google" repeated_string: "Zurich"')
  129. def testPrintNestedNewLineInStringAsOneLine(self, message_module):
  130. message = message_module.TestAllTypes()
  131. message.optional_string = 'a\nnew\nline'
  132. self.CompareToGoldenText(
  133. text_format.MessageToString(message, as_one_line=True),
  134. 'optional_string: "a\\nnew\\nline"')
  135. def testPrintExoticAsOneLine(self, message_module):
  136. message = message_module.TestAllTypes()
  137. message.repeated_int64.append(-9223372036854775808)
  138. message.repeated_uint64.append(18446744073709551615)
  139. message.repeated_double.append(123.456)
  140. message.repeated_double.append(1.23e22)
  141. message.repeated_double.append(1.23e-18)
  142. message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
  143. message.repeated_string.append(u'\u00fc\ua71f')
  144. self.CompareToGoldenText(
  145. self.RemoveRedundantZeros(text_format.MessageToString(
  146. message, as_one_line=True)),
  147. 'repeated_int64: -9223372036854775808'
  148. ' repeated_uint64: 18446744073709551615'
  149. ' repeated_double: 123.456'
  150. ' repeated_double: 1.23e+22'
  151. ' repeated_double: 1.23e-18'
  152. ' repeated_string: '
  153. '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""'
  154. ' repeated_string: "\\303\\274\\352\\234\\237"')
  155. def testRoundTripExoticAsOneLine(self, message_module):
  156. message = message_module.TestAllTypes()
  157. message.repeated_int64.append(-9223372036854775808)
  158. message.repeated_uint64.append(18446744073709551615)
  159. message.repeated_double.append(123.456)
  160. message.repeated_double.append(1.23e22)
  161. message.repeated_double.append(1.23e-18)
  162. message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
  163. message.repeated_string.append(u'\u00fc\ua71f')
  164. # Test as_utf8 = False.
  165. wire_text = text_format.MessageToString(message,
  166. as_one_line=True,
  167. as_utf8=False)
  168. parsed_message = message_module.TestAllTypes()
  169. r = text_format.Parse(wire_text, parsed_message)
  170. self.assertIs(r, parsed_message)
  171. self.assertEqual(message, parsed_message)
  172. # Test as_utf8 = True.
  173. wire_text = text_format.MessageToString(message,
  174. as_one_line=True,
  175. as_utf8=True)
  176. parsed_message = message_module.TestAllTypes()
  177. r = text_format.Parse(wire_text, parsed_message)
  178. self.assertIs(r, parsed_message)
  179. self.assertEqual(message, parsed_message,
  180. '\n%s != %s' % (message, parsed_message))
  181. def testPrintRawUtf8String(self, message_module):
  182. message = message_module.TestAllTypes()
  183. message.repeated_string.append(u'\u00fc\ua71f')
  184. text = text_format.MessageToString(message, as_utf8=True)
  185. self.CompareToGoldenText(text, 'repeated_string: "\303\274\352\234\237"\n')
  186. parsed_message = message_module.TestAllTypes()
  187. text_format.Parse(text, parsed_message)
  188. self.assertEqual(message, parsed_message,
  189. '\n%s != %s' % (message, parsed_message))
  190. def testPrintFloatFormat(self, message_module):
  191. # Check that float_format argument is passed to sub-message formatting.
  192. message = message_module.NestedTestAllTypes()
  193. # We use 1.25 as it is a round number in binary. The proto 32-bit float
  194. # will not gain additional imprecise digits as a 64-bit Python float and
  195. # show up in its str. 32-bit 1.2 is noisy when extended to 64-bit:
  196. # >>> struct.unpack('f', struct.pack('f', 1.2))[0]
  197. # 1.2000000476837158
  198. # >>> struct.unpack('f', struct.pack('f', 1.25))[0]
  199. # 1.25
  200. message.payload.optional_float = 1.25
  201. # Check rounding at 15 significant digits
  202. message.payload.optional_double = -.000003456789012345678
  203. # Check no decimal point.
  204. message.payload.repeated_float.append(-5642)
  205. # Check no trailing zeros.
  206. message.payload.repeated_double.append(.000078900)
  207. formatted_fields = ['optional_float: 1.25',
  208. 'optional_double: -3.45678901234568e-6',
  209. 'repeated_float: -5642', 'repeated_double: 7.89e-5']
  210. text_message = text_format.MessageToString(message, float_format='.15g')
  211. self.CompareToGoldenText(
  212. self.RemoveRedundantZeros(text_message),
  213. 'payload {{\n {0}\n {1}\n {2}\n {3}\n}}\n'.format(
  214. *formatted_fields))
  215. # as_one_line=True is a separate code branch where float_format is passed.
  216. text_message = text_format.MessageToString(message,
  217. as_one_line=True,
  218. float_format='.15g')
  219. self.CompareToGoldenText(
  220. self.RemoveRedundantZeros(text_message),
  221. 'payload {{ {0} {1} {2} {3} }}'.format(*formatted_fields))
  222. def testMessageToString(self, message_module):
  223. message = message_module.ForeignMessage()
  224. message.c = 123
  225. self.assertEqual('c: 123\n', str(message))
  226. def testPrintField(self, message_module):
  227. message = message_module.TestAllTypes()
  228. field = message.DESCRIPTOR.fields_by_name['optional_float']
  229. value = message.optional_float
  230. out = text_format.TextWriter(False)
  231. text_format.PrintField(field, value, out)
  232. self.assertEqual('optional_float: 0.0\n', out.getvalue())
  233. out.close()
  234. # Test Printer
  235. out = text_format.TextWriter(False)
  236. printer = text_format._Printer(out)
  237. printer.PrintField(field, value)
  238. self.assertEqual('optional_float: 0.0\n', out.getvalue())
  239. out.close()
  240. def testPrintFieldValue(self, message_module):
  241. message = message_module.TestAllTypes()
  242. field = message.DESCRIPTOR.fields_by_name['optional_float']
  243. value = message.optional_float
  244. out = text_format.TextWriter(False)
  245. text_format.PrintFieldValue(field, value, out)
  246. self.assertEqual('0.0', out.getvalue())
  247. out.close()
  248. # Test Printer
  249. out = text_format.TextWriter(False)
  250. printer = text_format._Printer(out)
  251. printer.PrintFieldValue(field, value)
  252. self.assertEqual('0.0', out.getvalue())
  253. out.close()
  254. def testParseAllFields(self, message_module):
  255. message = message_module.TestAllTypes()
  256. test_util.SetAllFields(message)
  257. ascii_text = text_format.MessageToString(message)
  258. parsed_message = message_module.TestAllTypes()
  259. text_format.Parse(ascii_text, parsed_message)
  260. self.assertEqual(message, parsed_message)
  261. if message_module is unittest_pb2:
  262. test_util.ExpectAllFieldsSet(self, message)
  263. def testParseExotic(self, message_module):
  264. message = message_module.TestAllTypes()
  265. text = ('repeated_int64: -9223372036854775808\n'
  266. 'repeated_uint64: 18446744073709551615\n'
  267. 'repeated_double: 123.456\n'
  268. 'repeated_double: 1.23e+22\n'
  269. 'repeated_double: 1.23e-18\n'
  270. 'repeated_string: \n'
  271. '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
  272. 'repeated_string: "foo" \'corge\' "grault"\n'
  273. 'repeated_string: "\\303\\274\\352\\234\\237"\n'
  274. 'repeated_string: "\\xc3\\xbc"\n'
  275. 'repeated_string: "\xc3\xbc"\n')
  276. text_format.Parse(text, message)
  277. self.assertEqual(-9223372036854775808, message.repeated_int64[0])
  278. self.assertEqual(18446744073709551615, message.repeated_uint64[0])
  279. self.assertEqual(123.456, message.repeated_double[0])
  280. self.assertEqual(1.23e22, message.repeated_double[1])
  281. self.assertEqual(1.23e-18, message.repeated_double[2])
  282. self.assertEqual('\000\001\a\b\f\n\r\t\v\\\'"', message.repeated_string[0])
  283. self.assertEqual('foocorgegrault', message.repeated_string[1])
  284. self.assertEqual(u'\u00fc\ua71f', message.repeated_string[2])
  285. self.assertEqual(u'\u00fc', message.repeated_string[3])
  286. def testParseTrailingCommas(self, message_module):
  287. message = message_module.TestAllTypes()
  288. text = ('repeated_int64: 100;\n'
  289. 'repeated_int64: 200;\n'
  290. 'repeated_int64: 300,\n'
  291. 'repeated_string: "one",\n'
  292. 'repeated_string: "two";\n')
  293. text_format.Parse(text, message)
  294. self.assertEqual(100, message.repeated_int64[0])
  295. self.assertEqual(200, message.repeated_int64[1])
  296. self.assertEqual(300, message.repeated_int64[2])
  297. self.assertEqual(u'one', message.repeated_string[0])
  298. self.assertEqual(u'two', message.repeated_string[1])
  299. def testParseRepeatedScalarShortFormat(self, message_module):
  300. message = message_module.TestAllTypes()
  301. text = ('repeated_int64: [100, 200];\n'
  302. 'repeated_int64: 300,\n'
  303. 'repeated_string: ["one", "two"];\n')
  304. text_format.Parse(text, message)
  305. self.assertEqual(100, message.repeated_int64[0])
  306. self.assertEqual(200, message.repeated_int64[1])
  307. self.assertEqual(300, message.repeated_int64[2])
  308. self.assertEqual(u'one', message.repeated_string[0])
  309. self.assertEqual(u'two', message.repeated_string[1])
  310. def testParseRepeatedMessageShortFormat(self, message_module):
  311. message = message_module.TestAllTypes()
  312. text = ('repeated_nested_message: [{bb: 100}, {bb: 200}],\n'
  313. 'repeated_nested_message: {bb: 300}\n'
  314. 'repeated_nested_message [{bb: 400}];\n')
  315. text_format.Parse(text, message)
  316. self.assertEqual(100, message.repeated_nested_message[0].bb)
  317. self.assertEqual(200, message.repeated_nested_message[1].bb)
  318. self.assertEqual(300, message.repeated_nested_message[2].bb)
  319. self.assertEqual(400, message.repeated_nested_message[3].bb)
  320. def testParseEmptyText(self, message_module):
  321. message = message_module.TestAllTypes()
  322. text = ''
  323. text_format.Parse(text, message)
  324. self.assertEqual(message_module.TestAllTypes(), message)
  325. def testParseInvalidUtf8(self, message_module):
  326. message = message_module.TestAllTypes()
  327. text = 'repeated_string: "\\xc3\\xc3"'
  328. self.assertRaises(text_format.ParseError, text_format.Parse, text, message)
  329. def testParseSingleWord(self, message_module):
  330. message = message_module.TestAllTypes()
  331. text = 'foo'
  332. six.assertRaisesRegex(self, text_format.ParseError, (
  333. r'1:1 : Message type "\w+.TestAllTypes" has no field named '
  334. r'"foo".'), text_format.Parse, text, message)
  335. def testParseUnknownField(self, message_module):
  336. message = message_module.TestAllTypes()
  337. text = 'unknown_field: 8\n'
  338. six.assertRaisesRegex(self, text_format.ParseError, (
  339. r'1:1 : Message type "\w+.TestAllTypes" has no field named '
  340. r'"unknown_field".'), text_format.Parse, text, message)
  341. def testParseBadEnumValue(self, message_module):
  342. message = message_module.TestAllTypes()
  343. text = 'optional_nested_enum: BARR'
  344. six.assertRaisesRegex(self, text_format.ParseError,
  345. (r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
  346. r'has no value named BARR.'), text_format.Parse,
  347. text, message)
  348. message = message_module.TestAllTypes()
  349. text = 'optional_nested_enum: 100'
  350. six.assertRaisesRegex(self, text_format.ParseError,
  351. (r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
  352. r'has no value with number 100.'), text_format.Parse,
  353. text, message)
  354. def testParseBadIntValue(self, message_module):
  355. message = message_module.TestAllTypes()
  356. text = 'optional_int32: bork'
  357. six.assertRaisesRegex(self, text_format.ParseError,
  358. ('1:17 : Couldn\'t parse integer: bork'),
  359. text_format.Parse, text, message)
  360. def testParseStringFieldUnescape(self, message_module):
  361. message = message_module.TestAllTypes()
  362. text = r'''repeated_string: "\xf\x62"
  363. repeated_string: "\\xf\\x62"
  364. repeated_string: "\\\xf\\\x62"
  365. repeated_string: "\\\\xf\\\\x62"
  366. repeated_string: "\\\\\xf\\\\\x62"
  367. repeated_string: "\x5cx20"'''
  368. text_format.Parse(text, message)
  369. SLASH = '\\'
  370. self.assertEqual('\x0fb', message.repeated_string[0])
  371. self.assertEqual(SLASH + 'xf' + SLASH + 'x62', message.repeated_string[1])
  372. self.assertEqual(SLASH + '\x0f' + SLASH + 'b', message.repeated_string[2])
  373. self.assertEqual(SLASH + SLASH + 'xf' + SLASH + SLASH + 'x62',
  374. message.repeated_string[3])
  375. self.assertEqual(SLASH + SLASH + '\x0f' + SLASH + SLASH + 'b',
  376. message.repeated_string[4])
  377. self.assertEqual(SLASH + 'x20', message.repeated_string[5])
  378. def testMergeDuplicateScalars(self, message_module):
  379. message = message_module.TestAllTypes()
  380. text = ('optional_int32: 42 ' 'optional_int32: 67')
  381. r = text_format.Merge(text, message)
  382. self.assertIs(r, message)
  383. self.assertEqual(67, message.optional_int32)
  384. def testMergeDuplicateNestedMessageScalars(self, message_module):
  385. message = message_module.TestAllTypes()
  386. text = ('optional_nested_message { bb: 1 } '
  387. 'optional_nested_message { bb: 2 }')
  388. r = text_format.Merge(text, message)
  389. self.assertTrue(r is message)
  390. self.assertEqual(2, message.optional_nested_message.bb)
  391. def testParseOneof(self, message_module):
  392. m = message_module.TestAllTypes()
  393. m.oneof_uint32 = 11
  394. m2 = message_module.TestAllTypes()
  395. text_format.Parse(text_format.MessageToString(m), m2)
  396. self.assertEqual('oneof_uint32', m2.WhichOneof('oneof_field'))
  397. def testMergeMultipleOneof(self, message_module):
  398. m_string = '\n'.join(['oneof_uint32: 11', 'oneof_string: "foo"'])
  399. m2 = message_module.TestAllTypes()
  400. text_format.Merge(m_string, m2)
  401. self.assertEqual('oneof_string', m2.WhichOneof('oneof_field'))
  402. def testParseMultipleOneof(self, message_module):
  403. m_string = '\n'.join(['oneof_uint32: 11', 'oneof_string: "foo"'])
  404. m2 = message_module.TestAllTypes()
  405. with self.assertRaisesRegexp(text_format.ParseError,
  406. ' is specified along with field '):
  407. text_format.Parse(m_string, m2)
  408. # These are tests that aren't fundamentally specific to proto2, but are at
  409. # the moment because of differences between the proto2 and proto3 test schemas.
  410. # Ideally the schemas would be made more similar so these tests could pass.
  411. class OnlyWorksWithProto2RightNowTests(TextFormatBase):
  412. def testPrintAllFieldsPointy(self):
  413. message = unittest_pb2.TestAllTypes()
  414. test_util.SetAllFields(message)
  415. self.CompareToGoldenFile(
  416. self.RemoveRedundantZeros(text_format.MessageToString(
  417. message, pointy_brackets=True)),
  418. 'text_format_unittest_data_pointy_oneof.txt')
  419. def testParseGolden(self):
  420. golden_text = '\n'.join(self.ReadGolden(
  421. 'text_format_unittest_data_oneof_implemented.txt'))
  422. parsed_message = unittest_pb2.TestAllTypes()
  423. r = text_format.Parse(golden_text, parsed_message)
  424. self.assertIs(r, parsed_message)
  425. message = unittest_pb2.TestAllTypes()
  426. test_util.SetAllFields(message)
  427. self.assertEqual(message, parsed_message)
  428. def testPrintAllFields(self):
  429. message = unittest_pb2.TestAllTypes()
  430. test_util.SetAllFields(message)
  431. self.CompareToGoldenFile(
  432. self.RemoveRedundantZeros(text_format.MessageToString(message)),
  433. 'text_format_unittest_data_oneof_implemented.txt')
  434. def testPrintInIndexOrder(self):
  435. message = unittest_pb2.TestFieldOrderings()
  436. message.my_string = '115'
  437. message.my_int = 101
  438. message.my_float = 111
  439. message.optional_nested_message.oo = 0
  440. message.optional_nested_message.bb = 1
  441. self.CompareToGoldenText(
  442. self.RemoveRedundantZeros(text_format.MessageToString(
  443. message, use_index_order=True)),
  444. 'my_string: \"115\"\nmy_int: 101\nmy_float: 111\n'
  445. 'optional_nested_message {\n oo: 0\n bb: 1\n}\n')
  446. self.CompareToGoldenText(
  447. self.RemoveRedundantZeros(text_format.MessageToString(message)),
  448. 'my_int: 101\nmy_string: \"115\"\nmy_float: 111\n'
  449. 'optional_nested_message {\n bb: 1\n oo: 0\n}\n')
  450. def testMergeLinesGolden(self):
  451. opened = self.ReadGolden('text_format_unittest_data_oneof_implemented.txt')
  452. parsed_message = unittest_pb2.TestAllTypes()
  453. r = text_format.MergeLines(opened, parsed_message)
  454. self.assertIs(r, parsed_message)
  455. message = unittest_pb2.TestAllTypes()
  456. test_util.SetAllFields(message)
  457. self.assertEqual(message, parsed_message)
  458. def testParseLinesGolden(self):
  459. opened = self.ReadGolden('text_format_unittest_data_oneof_implemented.txt')
  460. parsed_message = unittest_pb2.TestAllTypes()
  461. r = text_format.ParseLines(opened, parsed_message)
  462. self.assertIs(r, parsed_message)
  463. message = unittest_pb2.TestAllTypes()
  464. test_util.SetAllFields(message)
  465. self.assertEqual(message, parsed_message)
  466. def testPrintMap(self):
  467. message = map_unittest_pb2.TestMap()
  468. message.map_int32_int32[-123] = -456
  469. message.map_int64_int64[-2**33] = -2**34
  470. message.map_uint32_uint32[123] = 456
  471. message.map_uint64_uint64[2**33] = 2**34
  472. message.map_string_string['abc'] = '123'
  473. message.map_int32_foreign_message[111].c = 5
  474. # Maps are serialized to text format using their underlying repeated
  475. # representation.
  476. self.CompareToGoldenText(
  477. text_format.MessageToString(message), 'map_int32_int32 {\n'
  478. ' key: -123\n'
  479. ' value: -456\n'
  480. '}\n'
  481. 'map_int64_int64 {\n'
  482. ' key: -8589934592\n'
  483. ' value: -17179869184\n'
  484. '}\n'
  485. 'map_uint32_uint32 {\n'
  486. ' key: 123\n'
  487. ' value: 456\n'
  488. '}\n'
  489. 'map_uint64_uint64 {\n'
  490. ' key: 8589934592\n'
  491. ' value: 17179869184\n'
  492. '}\n'
  493. 'map_string_string {\n'
  494. ' key: "abc"\n'
  495. ' value: "123"\n'
  496. '}\n'
  497. 'map_int32_foreign_message {\n'
  498. ' key: 111\n'
  499. ' value {\n'
  500. ' c: 5\n'
  501. ' }\n'
  502. '}\n')
  503. def testMapOrderEnforcement(self):
  504. message = map_unittest_pb2.TestMap()
  505. for letter in string.ascii_uppercase[13:26]:
  506. message.map_string_string[letter] = 'dummy'
  507. for letter in reversed(string.ascii_uppercase[0:13]):
  508. message.map_string_string[letter] = 'dummy'
  509. golden = ''.join(('map_string_string {\n key: "%c"\n value: "dummy"\n}\n'
  510. % (letter,) for letter in string.ascii_uppercase))
  511. self.CompareToGoldenText(text_format.MessageToString(message), golden)
  512. # TODO(teboring): In c/137553523, not serializing default value for map entry
  513. # message has been fixed. This test needs to be disabled in order to submit
  514. # that cl. Add this back when c/137553523 has been submitted.
  515. # def testMapOrderSemantics(self):
  516. # golden_lines = self.ReadGolden('map_test_data.txt')
  517. # message = map_unittest_pb2.TestMap()
  518. # text_format.ParseLines(golden_lines, message)
  519. # candidate = text_format.MessageToString(message)
  520. # # The Python implementation emits "1.0" for the double value that the C++
  521. # # implementation emits as "1".
  522. # candidate = candidate.replace('1.0', '1', 2)
  523. # candidate = candidate.replace('0.0', '0', 2)
  524. # self.assertMultiLineEqual(candidate, ''.join(golden_lines))
  525. # Tests of proto2-only features (MessageSet, extensions, etc.).
  526. class Proto2Tests(TextFormatBase):
  527. def testPrintMessageSet(self):
  528. message = unittest_mset_pb2.TestMessageSetContainer()
  529. ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
  530. ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
  531. message.message_set.Extensions[ext1].i = 23
  532. message.message_set.Extensions[ext2].str = 'foo'
  533. self.CompareToGoldenText(
  534. text_format.MessageToString(message), 'message_set {\n'
  535. ' [protobuf_unittest.TestMessageSetExtension1] {\n'
  536. ' i: 23\n'
  537. ' }\n'
  538. ' [protobuf_unittest.TestMessageSetExtension2] {\n'
  539. ' str: \"foo\"\n'
  540. ' }\n'
  541. '}\n')
  542. message = message_set_extensions_pb2.TestMessageSet()
  543. ext = message_set_extensions_pb2.message_set_extension3
  544. message.Extensions[ext].text = 'bar'
  545. self.CompareToGoldenText(
  546. text_format.MessageToString(message),
  547. '[google.protobuf.internal.TestMessageSetExtension3] {\n'
  548. ' text: \"bar\"\n'
  549. '}\n')
  550. def testPrintMessageSetByFieldNumber(self):
  551. out = text_format.TextWriter(False)
  552. message = unittest_mset_pb2.TestMessageSetContainer()
  553. ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
  554. ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
  555. message.message_set.Extensions[ext1].i = 23
  556. message.message_set.Extensions[ext2].str = 'foo'
  557. text_format.PrintMessage(message, out, use_field_number=True)
  558. self.CompareToGoldenText(out.getvalue(), '1 {\n'
  559. ' 1545008 {\n'
  560. ' 15: 23\n'
  561. ' }\n'
  562. ' 1547769 {\n'
  563. ' 25: \"foo\"\n'
  564. ' }\n'
  565. '}\n')
  566. out.close()
  567. def testPrintMessageSetAsOneLine(self):
  568. message = unittest_mset_pb2.TestMessageSetContainer()
  569. ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
  570. ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
  571. message.message_set.Extensions[ext1].i = 23
  572. message.message_set.Extensions[ext2].str = 'foo'
  573. self.CompareToGoldenText(
  574. text_format.MessageToString(message, as_one_line=True),
  575. 'message_set {'
  576. ' [protobuf_unittest.TestMessageSetExtension1] {'
  577. ' i: 23'
  578. ' }'
  579. ' [protobuf_unittest.TestMessageSetExtension2] {'
  580. ' str: \"foo\"'
  581. ' }'
  582. ' }')
  583. def testParseMessageSet(self):
  584. message = unittest_pb2.TestAllTypes()
  585. text = ('repeated_uint64: 1\n' 'repeated_uint64: 2\n')
  586. text_format.Parse(text, message)
  587. self.assertEqual(1, message.repeated_uint64[0])
  588. self.assertEqual(2, message.repeated_uint64[1])
  589. message = unittest_mset_pb2.TestMessageSetContainer()
  590. text = ('message_set {\n'
  591. ' [protobuf_unittest.TestMessageSetExtension1] {\n'
  592. ' i: 23\n'
  593. ' }\n'
  594. ' [protobuf_unittest.TestMessageSetExtension2] {\n'
  595. ' str: \"foo\"\n'
  596. ' }\n'
  597. '}\n')
  598. text_format.Parse(text, message)
  599. ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
  600. ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
  601. self.assertEqual(23, message.message_set.Extensions[ext1].i)
  602. self.assertEqual('foo', message.message_set.Extensions[ext2].str)
  603. def testExtensionInsideAnyMessage(self):
  604. message = test_extend_any.TestAny()
  605. text = ('value {\n'
  606. ' [type.googleapis.com/google.protobuf.internal.TestAny] {\n'
  607. ' [google.protobuf.internal.TestAnyExtension1.extension1] {\n'
  608. ' i: 10\n'
  609. ' }\n'
  610. ' }\n'
  611. '}\n')
  612. text_format.Merge(text, message, descriptor_pool=descriptor_pool.Default())
  613. self.CompareToGoldenText(
  614. text_format.MessageToString(
  615. message, descriptor_pool=descriptor_pool.Default()),
  616. text)
  617. def testParseMessageByFieldNumber(self):
  618. message = unittest_pb2.TestAllTypes()
  619. text = ('34: 1\n' 'repeated_uint64: 2\n')
  620. text_format.Parse(text, message, allow_field_number=True)
  621. self.assertEqual(1, message.repeated_uint64[0])
  622. self.assertEqual(2, message.repeated_uint64[1])
  623. message = unittest_mset_pb2.TestMessageSetContainer()
  624. text = ('1 {\n'
  625. ' 1545008 {\n'
  626. ' 15: 23\n'
  627. ' }\n'
  628. ' 1547769 {\n'
  629. ' 25: \"foo\"\n'
  630. ' }\n'
  631. '}\n')
  632. text_format.Parse(text, message, allow_field_number=True)
  633. ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
  634. ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
  635. self.assertEqual(23, message.message_set.Extensions[ext1].i)
  636. self.assertEqual('foo', message.message_set.Extensions[ext2].str)
  637. # Can't parse field number without set allow_field_number=True.
  638. message = unittest_pb2.TestAllTypes()
  639. text = '34:1\n'
  640. six.assertRaisesRegex(self, text_format.ParseError, (
  641. r'1:1 : Message type "\w+.TestAllTypes" has no field named '
  642. r'"34".'), text_format.Parse, text, message)
  643. # Can't parse if field number is not found.
  644. text = '1234:1\n'
  645. six.assertRaisesRegex(
  646. self,
  647. text_format.ParseError,
  648. (r'1:1 : Message type "\w+.TestAllTypes" has no field named '
  649. r'"1234".'),
  650. text_format.Parse,
  651. text,
  652. message,
  653. allow_field_number=True)
  654. def testPrintAllExtensions(self):
  655. message = unittest_pb2.TestAllExtensions()
  656. test_util.SetAllExtensions(message)
  657. self.CompareToGoldenFile(
  658. self.RemoveRedundantZeros(text_format.MessageToString(message)),
  659. 'text_format_unittest_extensions_data.txt')
  660. def testPrintAllExtensionsPointy(self):
  661. message = unittest_pb2.TestAllExtensions()
  662. test_util.SetAllExtensions(message)
  663. self.CompareToGoldenFile(
  664. self.RemoveRedundantZeros(text_format.MessageToString(
  665. message, pointy_brackets=True)),
  666. 'text_format_unittest_extensions_data_pointy.txt')
  667. def testParseGoldenExtensions(self):
  668. golden_text = '\n'.join(self.ReadGolden(
  669. 'text_format_unittest_extensions_data.txt'))
  670. parsed_message = unittest_pb2.TestAllExtensions()
  671. text_format.Parse(golden_text, parsed_message)
  672. message = unittest_pb2.TestAllExtensions()
  673. test_util.SetAllExtensions(message)
  674. self.assertEqual(message, parsed_message)
  675. def testParseAllExtensions(self):
  676. message = unittest_pb2.TestAllExtensions()
  677. test_util.SetAllExtensions(message)
  678. ascii_text = text_format.MessageToString(message)
  679. parsed_message = unittest_pb2.TestAllExtensions()
  680. text_format.Parse(ascii_text, parsed_message)
  681. self.assertEqual(message, parsed_message)
  682. def testParseAllowedUnknownExtension(self):
  683. # Skip over unknown extension correctly.
  684. message = unittest_mset_pb2.TestMessageSetContainer()
  685. text = ('message_set {\n'
  686. ' [unknown_extension] {\n'
  687. ' i: 23\n'
  688. ' bin: "\xe0"'
  689. ' [nested_unknown_ext]: {\n'
  690. ' i: 23\n'
  691. ' test: "test_string"\n'
  692. ' floaty_float: -0.315\n'
  693. ' num: -inf\n'
  694. ' multiline_str: "abc"\n'
  695. ' "def"\n'
  696. ' "xyz."\n'
  697. ' [nested_unknown_ext]: <\n'
  698. ' i: 23\n'
  699. ' i: 24\n'
  700. ' pointfloat: .3\n'
  701. ' test: "test_string"\n'
  702. ' floaty_float: -0.315\n'
  703. ' num: -inf\n'
  704. ' long_string: "test" "test2" \n'
  705. ' >\n'
  706. ' }\n'
  707. ' }\n'
  708. ' [unknown_extension]: 5\n'
  709. '}\n')
  710. text_format.Parse(text, message, allow_unknown_extension=True)
  711. golden = 'message_set {\n}\n'
  712. self.CompareToGoldenText(text_format.MessageToString(message), golden)
  713. # Catch parse errors in unknown extension.
  714. message = unittest_mset_pb2.TestMessageSetContainer()
  715. malformed = ('message_set {\n'
  716. ' [unknown_extension] {\n'
  717. ' i:\n' # Missing value.
  718. ' }\n'
  719. '}\n')
  720. six.assertRaisesRegex(self,
  721. text_format.ParseError,
  722. 'Invalid field value: }',
  723. text_format.Parse,
  724. malformed,
  725. message,
  726. allow_unknown_extension=True)
  727. message = unittest_mset_pb2.TestMessageSetContainer()
  728. malformed = ('message_set {\n'
  729. ' [unknown_extension] {\n'
  730. ' str: "malformed string\n' # Missing closing quote.
  731. ' }\n'
  732. '}\n')
  733. six.assertRaisesRegex(self,
  734. text_format.ParseError,
  735. 'Invalid field value: "',
  736. text_format.Parse,
  737. malformed,
  738. message,
  739. allow_unknown_extension=True)
  740. message = unittest_mset_pb2.TestMessageSetContainer()
  741. malformed = ('message_set {\n'
  742. ' [unknown_extension] {\n'
  743. ' str: "malformed\n multiline\n string\n'
  744. ' }\n'
  745. '}\n')
  746. six.assertRaisesRegex(self,
  747. text_format.ParseError,
  748. 'Invalid field value: "',
  749. text_format.Parse,
  750. malformed,
  751. message,
  752. allow_unknown_extension=True)
  753. message = unittest_mset_pb2.TestMessageSetContainer()
  754. malformed = ('message_set {\n'
  755. ' [malformed_extension] <\n'
  756. ' i: -5\n'
  757. ' \n' # Missing '>' here.
  758. '}\n')
  759. six.assertRaisesRegex(self,
  760. text_format.ParseError,
  761. '5:1 : Expected ">".',
  762. text_format.Parse,
  763. malformed,
  764. message,
  765. allow_unknown_extension=True)
  766. # Don't allow unknown fields with allow_unknown_extension=True.
  767. message = unittest_mset_pb2.TestMessageSetContainer()
  768. malformed = ('message_set {\n'
  769. ' unknown_field: true\n'
  770. ' \n' # Missing '>' here.
  771. '}\n')
  772. six.assertRaisesRegex(self,
  773. text_format.ParseError,
  774. ('2:3 : Message type '
  775. '"proto2_wireformat_unittest.TestMessageSet" has no'
  776. ' field named "unknown_field".'),
  777. text_format.Parse,
  778. malformed,
  779. message,
  780. allow_unknown_extension=True)
  781. # Parse known extension correcty.
  782. message = unittest_mset_pb2.TestMessageSetContainer()
  783. text = ('message_set {\n'
  784. ' [protobuf_unittest.TestMessageSetExtension1] {\n'
  785. ' i: 23\n'
  786. ' }\n'
  787. ' [protobuf_unittest.TestMessageSetExtension2] {\n'
  788. ' str: \"foo\"\n'
  789. ' }\n'
  790. '}\n')
  791. text_format.Parse(text, message, allow_unknown_extension=True)
  792. ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
  793. ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
  794. self.assertEqual(23, message.message_set.Extensions[ext1].i)
  795. self.assertEqual('foo', message.message_set.Extensions[ext2].str)
  796. def testParseBadExtension(self):
  797. message = unittest_pb2.TestAllExtensions()
  798. text = '[unknown_extension]: 8\n'
  799. six.assertRaisesRegex(self, text_format.ParseError,
  800. '1:2 : Extension "unknown_extension" not registered.',
  801. text_format.Parse, text, message)
  802. message = unittest_pb2.TestAllTypes()
  803. six.assertRaisesRegex(self, text_format.ParseError, (
  804. '1:2 : Message type "protobuf_unittest.TestAllTypes" does not have '
  805. 'extensions.'), text_format.Parse, text, message)
  806. def testMergeDuplicateExtensionScalars(self):
  807. message = unittest_pb2.TestAllExtensions()
  808. text = ('[protobuf_unittest.optional_int32_extension]: 42 '
  809. '[protobuf_unittest.optional_int32_extension]: 67')
  810. text_format.Merge(text, message)
  811. self.assertEqual(67,
  812. message.Extensions[unittest_pb2.optional_int32_extension])
  813. def testParseDuplicateExtensionScalars(self):
  814. message = unittest_pb2.TestAllExtensions()
  815. text = ('[protobuf_unittest.optional_int32_extension]: 42 '
  816. '[protobuf_unittest.optional_int32_extension]: 67')
  817. six.assertRaisesRegex(self, text_format.ParseError, (
  818. '1:96 : Message type "protobuf_unittest.TestAllExtensions" '
  819. 'should not have multiple '
  820. '"protobuf_unittest.optional_int32_extension" extensions.'),
  821. text_format.Parse, text, message)
  822. def testParseDuplicateNestedMessageScalars(self):
  823. message = unittest_pb2.TestAllTypes()
  824. text = ('optional_nested_message { bb: 1 } '
  825. 'optional_nested_message { bb: 2 }')
  826. six.assertRaisesRegex(self, text_format.ParseError, (
  827. '1:65 : Message type "protobuf_unittest.TestAllTypes.NestedMessage" '
  828. 'should not have multiple "bb" fields.'), text_format.Parse, text,
  829. message)
  830. def testParseDuplicateScalars(self):
  831. message = unittest_pb2.TestAllTypes()
  832. text = ('optional_int32: 42 ' 'optional_int32: 67')
  833. six.assertRaisesRegex(self, text_format.ParseError, (
  834. '1:36 : Message type "protobuf_unittest.TestAllTypes" should not '
  835. 'have multiple "optional_int32" fields.'), text_format.Parse, text,
  836. message)
  837. def testParseGroupNotClosed(self):
  838. message = unittest_pb2.TestAllTypes()
  839. text = 'RepeatedGroup: <'
  840. six.assertRaisesRegex(self, text_format.ParseError, '1:16 : Expected ">".',
  841. text_format.Parse, text, message)
  842. text = 'RepeatedGroup: {'
  843. six.assertRaisesRegex(self, text_format.ParseError, '1:16 : Expected "}".',
  844. text_format.Parse, text, message)
  845. def testParseEmptyGroup(self):
  846. message = unittest_pb2.TestAllTypes()
  847. text = 'OptionalGroup: {}'
  848. text_format.Parse(text, message)
  849. self.assertTrue(message.HasField('optionalgroup'))
  850. message.Clear()
  851. message = unittest_pb2.TestAllTypes()
  852. text = 'OptionalGroup: <>'
  853. text_format.Parse(text, message)
  854. self.assertTrue(message.HasField('optionalgroup'))
  855. # Maps aren't really proto2-only, but our test schema only has maps for
  856. # proto2.
  857. def testParseMap(self):
  858. text = ('map_int32_int32 {\n'
  859. ' key: -123\n'
  860. ' value: -456\n'
  861. '}\n'
  862. 'map_int64_int64 {\n'
  863. ' key: -8589934592\n'
  864. ' value: -17179869184\n'
  865. '}\n'
  866. 'map_uint32_uint32 {\n'
  867. ' key: 123\n'
  868. ' value: 456\n'
  869. '}\n'
  870. 'map_uint64_uint64 {\n'
  871. ' key: 8589934592\n'
  872. ' value: 17179869184\n'
  873. '}\n'
  874. 'map_string_string {\n'
  875. ' key: "abc"\n'
  876. ' value: "123"\n'
  877. '}\n'
  878. 'map_int32_foreign_message {\n'
  879. ' key: 111\n'
  880. ' value {\n'
  881. ' c: 5\n'
  882. ' }\n'
  883. '}\n')
  884. message = map_unittest_pb2.TestMap()
  885. text_format.Parse(text, message)
  886. self.assertEqual(-456, message.map_int32_int32[-123])
  887. self.assertEqual(-2**34, message.map_int64_int64[-2**33])
  888. self.assertEqual(456, message.map_uint32_uint32[123])
  889. self.assertEqual(2**34, message.map_uint64_uint64[2**33])
  890. self.assertEqual('123', message.map_string_string['abc'])
  891. self.assertEqual(5, message.map_int32_foreign_message[111].c)
  892. class Proto3Tests(unittest.TestCase):
  893. def testPrintMessageExpandAny(self):
  894. packed_message = unittest_pb2.OneString()
  895. packed_message.data = 'string'
  896. message = any_test_pb2.TestAny()
  897. message.any_value.Pack(packed_message)
  898. self.assertEqual(
  899. text_format.MessageToString(message,
  900. descriptor_pool=descriptor_pool.Default()),
  901. 'any_value {\n'
  902. ' [type.googleapis.com/protobuf_unittest.OneString] {\n'
  903. ' data: "string"\n'
  904. ' }\n'
  905. '}\n')
  906. def testPrintMessageExpandAnyRepeated(self):
  907. packed_message = unittest_pb2.OneString()
  908. message = any_test_pb2.TestAny()
  909. packed_message.data = 'string0'
  910. message.repeated_any_value.add().Pack(packed_message)
  911. packed_message.data = 'string1'
  912. message.repeated_any_value.add().Pack(packed_message)
  913. self.assertEqual(
  914. text_format.MessageToString(message),
  915. 'repeated_any_value {\n'
  916. ' [type.googleapis.com/protobuf_unittest.OneString] {\n'
  917. ' data: "string0"\n'
  918. ' }\n'
  919. '}\n'
  920. 'repeated_any_value {\n'
  921. ' [type.googleapis.com/protobuf_unittest.OneString] {\n'
  922. ' data: "string1"\n'
  923. ' }\n'
  924. '}\n')
  925. def testPrintMessageExpandAnyDescriptorPoolMissingType(self):
  926. packed_message = unittest_pb2.OneString()
  927. packed_message.data = 'string'
  928. message = any_test_pb2.TestAny()
  929. message.any_value.Pack(packed_message)
  930. empty_pool = descriptor_pool.DescriptorPool()
  931. self.assertEqual(
  932. text_format.MessageToString(message, descriptor_pool=empty_pool),
  933. 'any_value {\n'
  934. ' type_url: "type.googleapis.com/protobuf_unittest.OneString"\n'
  935. ' value: "\\n\\006string"\n'
  936. '}\n')
  937. def testPrintMessageExpandAnyPointyBrackets(self):
  938. packed_message = unittest_pb2.OneString()
  939. packed_message.data = 'string'
  940. message = any_test_pb2.TestAny()
  941. message.any_value.Pack(packed_message)
  942. self.assertEqual(
  943. text_format.MessageToString(message,
  944. pointy_brackets=True),
  945. 'any_value <\n'
  946. ' [type.googleapis.com/protobuf_unittest.OneString] <\n'
  947. ' data: "string"\n'
  948. ' >\n'
  949. '>\n')
  950. def testPrintMessageExpandAnyAsOneLine(self):
  951. packed_message = unittest_pb2.OneString()
  952. packed_message.data = 'string'
  953. message = any_test_pb2.TestAny()
  954. message.any_value.Pack(packed_message)
  955. self.assertEqual(
  956. text_format.MessageToString(message,
  957. as_one_line=True),
  958. 'any_value {'
  959. ' [type.googleapis.com/protobuf_unittest.OneString]'
  960. ' { data: "string" } '
  961. '}')
  962. def testPrintMessageExpandAnyAsOneLinePointyBrackets(self):
  963. packed_message = unittest_pb2.OneString()
  964. packed_message.data = 'string'
  965. message = any_test_pb2.TestAny()
  966. message.any_value.Pack(packed_message)
  967. self.assertEqual(
  968. text_format.MessageToString(message,
  969. as_one_line=True,
  970. pointy_brackets=True,
  971. descriptor_pool=descriptor_pool.Default()),
  972. 'any_value <'
  973. ' [type.googleapis.com/protobuf_unittest.OneString]'
  974. ' < data: "string" > '
  975. '>')
  976. def testMergeExpandedAny(self):
  977. message = any_test_pb2.TestAny()
  978. text = ('any_value {\n'
  979. ' [type.googleapis.com/protobuf_unittest.OneString] {\n'
  980. ' data: "string"\n'
  981. ' }\n'
  982. '}\n')
  983. text_format.Merge(text, message)
  984. packed_message = unittest_pb2.OneString()
  985. message.any_value.Unpack(packed_message)
  986. self.assertEqual('string', packed_message.data)
  987. message.Clear()
  988. text_format.Parse(text, message)
  989. packed_message = unittest_pb2.OneString()
  990. message.any_value.Unpack(packed_message)
  991. self.assertEqual('string', packed_message.data)
  992. def testMergeExpandedAnyRepeated(self):
  993. message = any_test_pb2.TestAny()
  994. text = ('repeated_any_value {\n'
  995. ' [type.googleapis.com/protobuf_unittest.OneString] {\n'
  996. ' data: "string0"\n'
  997. ' }\n'
  998. '}\n'
  999. 'repeated_any_value {\n'
  1000. ' [type.googleapis.com/protobuf_unittest.OneString] {\n'
  1001. ' data: "string1"\n'
  1002. ' }\n'
  1003. '}\n')
  1004. text_format.Merge(text, message)
  1005. packed_message = unittest_pb2.OneString()
  1006. message.repeated_any_value[0].Unpack(packed_message)
  1007. self.assertEqual('string0', packed_message.data)
  1008. message.repeated_any_value[1].Unpack(packed_message)
  1009. self.assertEqual('string1', packed_message.data)
  1010. def testMergeExpandedAnyPointyBrackets(self):
  1011. message = any_test_pb2.TestAny()
  1012. text = ('any_value {\n'
  1013. ' [type.googleapis.com/protobuf_unittest.OneString] <\n'
  1014. ' data: "string"\n'
  1015. ' >\n'
  1016. '}\n')
  1017. text_format.Merge(text, message)
  1018. packed_message = unittest_pb2.OneString()
  1019. message.any_value.Unpack(packed_message)
  1020. self.assertEqual('string', packed_message.data)
  1021. def testMergeAlternativeUrl(self):
  1022. message = any_test_pb2.TestAny()
  1023. text = ('any_value {\n'
  1024. ' [type.otherapi.com/protobuf_unittest.OneString] {\n'
  1025. ' data: "string"\n'
  1026. ' }\n'
  1027. '}\n')
  1028. text_format.Merge(text, message)
  1029. packed_message = unittest_pb2.OneString()
  1030. self.assertEqual('type.otherapi.com/protobuf_unittest.OneString',
  1031. message.any_value.type_url)
  1032. def testMergeExpandedAnyDescriptorPoolMissingType(self):
  1033. message = any_test_pb2.TestAny()
  1034. text = ('any_value {\n'
  1035. ' [type.googleapis.com/protobuf_unittest.OneString] {\n'
  1036. ' data: "string"\n'
  1037. ' }\n'
  1038. '}\n')
  1039. with self.assertRaises(text_format.ParseError) as e:
  1040. empty_pool = descriptor_pool.DescriptorPool()
  1041. text_format.Merge(text, message, descriptor_pool=empty_pool)
  1042. self.assertEqual(
  1043. str(e.exception),
  1044. 'Type protobuf_unittest.OneString not found in descriptor pool')
  1045. def testMergeUnexpandedAny(self):
  1046. text = ('any_value {\n'
  1047. ' type_url: "type.googleapis.com/protobuf_unittest.OneString"\n'
  1048. ' value: "\\n\\006string"\n'
  1049. '}\n')
  1050. message = any_test_pb2.TestAny()
  1051. text_format.Merge(text, message)
  1052. packed_message = unittest_pb2.OneString()
  1053. message.any_value.Unpack(packed_message)
  1054. self.assertEqual('string', packed_message.data)
  1055. class TokenizerTest(unittest.TestCase):
  1056. def testSimpleTokenCases(self):
  1057. text = ('identifier1:"string1"\n \n\n'
  1058. 'identifier2 : \n \n123 \n identifier3 :\'string\'\n'
  1059. 'identifiER_4 : 1.1e+2 ID5:-0.23 ID6:\'aaaa\\\'bbbb\'\n'
  1060. 'ID7 : "aa\\"bb"\n\n\n\n ID8: {A:inf B:-inf C:true D:false}\n'
  1061. 'ID9: 22 ID10: -111111111111111111 ID11: -22\n'
  1062. 'ID12: 2222222222222222222 ID13: 1.23456f ID14: 1.2e+2f '
  1063. 'false_bool: 0 true_BOOL:t \n true_bool1: 1 false_BOOL1:f '
  1064. 'False_bool: False True_bool: True')
  1065. tokenizer = text_format.Tokenizer(text.splitlines())
  1066. methods = [(tokenizer.ConsumeIdentifier, 'identifier1'), ':',
  1067. (tokenizer.ConsumeString, 'string1'),
  1068. (tokenizer.ConsumeIdentifier, 'identifier2'), ':',
  1069. (tokenizer.ConsumeInteger, 123),
  1070. (tokenizer.ConsumeIdentifier, 'identifier3'), ':',
  1071. (tokenizer.ConsumeString, 'string'),
  1072. (tokenizer.ConsumeIdentifier, 'identifiER_4'), ':',
  1073. (tokenizer.ConsumeFloat, 1.1e+2),
  1074. (tokenizer.ConsumeIdentifier, 'ID5'), ':',
  1075. (tokenizer.ConsumeFloat, -0.23),
  1076. (tokenizer.ConsumeIdentifier, 'ID6'), ':',
  1077. (tokenizer.ConsumeString, 'aaaa\'bbbb'),
  1078. (tokenizer.ConsumeIdentifier, 'ID7'), ':',
  1079. (tokenizer.ConsumeString, 'aa\"bb'),
  1080. (tokenizer.ConsumeIdentifier, 'ID8'), ':', '{',
  1081. (tokenizer.ConsumeIdentifier, 'A'), ':',
  1082. (tokenizer.ConsumeFloat, float('inf')),
  1083. (tokenizer.ConsumeIdentifier, 'B'), ':',
  1084. (tokenizer.ConsumeFloat, -float('inf')),
  1085. (tokenizer.ConsumeIdentifier, 'C'), ':',
  1086. (tokenizer.ConsumeBool, True),
  1087. (tokenizer.ConsumeIdentifier, 'D'), ':',
  1088. (tokenizer.ConsumeBool, False), '}',
  1089. (tokenizer.ConsumeIdentifier, 'ID9'), ':',
  1090. (tokenizer.ConsumeInteger, 22),
  1091. (tokenizer.ConsumeIdentifier, 'ID10'), ':',
  1092. (tokenizer.ConsumeInteger, -111111111111111111),
  1093. (tokenizer.ConsumeIdentifier, 'ID11'), ':',
  1094. (tokenizer.ConsumeInteger, -22),
  1095. (tokenizer.ConsumeIdentifier, 'ID12'), ':',
  1096. (tokenizer.ConsumeInteger, 2222222222222222222),
  1097. (tokenizer.ConsumeIdentifier, 'ID13'), ':',
  1098. (tokenizer.ConsumeFloat, 1.23456),
  1099. (tokenizer.ConsumeIdentifier, 'ID14'), ':',
  1100. (tokenizer.ConsumeFloat, 1.2e+2),
  1101. (tokenizer.ConsumeIdentifier, 'false_bool'), ':',
  1102. (tokenizer.ConsumeBool, False),
  1103. (tokenizer.ConsumeIdentifier, 'true_BOOL'), ':',
  1104. (tokenizer.ConsumeBool, True),
  1105. (tokenizer.ConsumeIdentifier, 'true_bool1'), ':',
  1106. (tokenizer.ConsumeBool, True),
  1107. (tokenizer.ConsumeIdentifier, 'false_BOOL1'), ':',
  1108. (tokenizer.ConsumeBool, False),
  1109. (tokenizer.ConsumeIdentifier, 'False_bool'), ':',
  1110. (tokenizer.ConsumeBool, False),
  1111. (tokenizer.ConsumeIdentifier, 'True_bool'), ':',
  1112. (tokenizer.ConsumeBool, True)]
  1113. i = 0
  1114. while not tokenizer.AtEnd():
  1115. m = methods[i]
  1116. if isinstance(m, str):
  1117. token = tokenizer.token
  1118. self.assertEqual(token, m)
  1119. tokenizer.NextToken()
  1120. else:
  1121. self.assertEqual(m[1], m[0]())
  1122. i += 1
  1123. def testConsumeAbstractIntegers(self):
  1124. # This test only tests the failures in the integer parsing methods as well
  1125. # as the '0' special cases.
  1126. int64_max = (1 << 63) - 1
  1127. uint32_max = (1 << 32) - 1
  1128. text = '-1 %d %d' % (uint32_max + 1, int64_max + 1)
  1129. tokenizer = text_format.Tokenizer(text.splitlines())
  1130. self.assertEqual(-1, tokenizer.ConsumeInteger())
  1131. self.assertEqual(uint32_max + 1, tokenizer.ConsumeInteger())
  1132. self.assertEqual(int64_max + 1, tokenizer.ConsumeInteger())
  1133. self.assertTrue(tokenizer.AtEnd())
  1134. text = '-0 0'
  1135. tokenizer = text_format.Tokenizer(text.splitlines())
  1136. self.assertEqual(0, tokenizer.ConsumeInteger())
  1137. self.assertEqual(0, tokenizer.ConsumeInteger())
  1138. self.assertTrue(tokenizer.AtEnd())
  1139. def testConsumeIntegers(self):
  1140. # This test only tests the failures in the integer parsing methods as well
  1141. # as the '0' special cases.
  1142. int64_max = (1 << 63) - 1
  1143. uint32_max = (1 << 32) - 1
  1144. text = '-1 %d %d' % (uint32_max + 1, int64_max + 1)
  1145. tokenizer = text_format.Tokenizer(text.splitlines())
  1146. self.assertRaises(text_format.ParseError,
  1147. text_format._ConsumeUint32, tokenizer)
  1148. self.assertRaises(text_format.ParseError,
  1149. text_format._ConsumeUint64, tokenizer)
  1150. self.assertEqual(-1, text_format._ConsumeInt32(tokenizer))
  1151. self.assertRaises(text_format.ParseError,
  1152. text_format._ConsumeUint32, tokenizer)
  1153. self.assertRaises(text_format.ParseError,
  1154. text_format._ConsumeInt32, tokenizer)
  1155. self.assertEqual(uint32_max + 1, text_format._ConsumeInt64(tokenizer))
  1156. self.assertRaises(text_format.ParseError,
  1157. text_format._ConsumeInt64, tokenizer)
  1158. self.assertEqual(int64_max + 1, text_format._ConsumeUint64(tokenizer))
  1159. self.assertTrue(tokenizer.AtEnd())
  1160. text = '-0 -0 0 0'
  1161. tokenizer = text_format.Tokenizer(text.splitlines())
  1162. self.assertEqual(0, text_format._ConsumeUint32(tokenizer))
  1163. self.assertEqual(0, text_format._ConsumeUint64(tokenizer))
  1164. self.assertEqual(0, text_format._ConsumeUint32(tokenizer))
  1165. self.assertEqual(0, text_format._ConsumeUint64(tokenizer))
  1166. self.assertTrue(tokenizer.AtEnd())
  1167. def testConsumeByteString(self):
  1168. text = '"string1\''
  1169. tokenizer = text_format.Tokenizer(text.splitlines())
  1170. self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
  1171. text = 'string1"'
  1172. tokenizer = text_format.Tokenizer(text.splitlines())
  1173. self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
  1174. text = '\n"\\xt"'
  1175. tokenizer = text_format.Tokenizer(text.splitlines())
  1176. self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
  1177. text = '\n"\\"'
  1178. tokenizer = text_format.Tokenizer(text.splitlines())
  1179. self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
  1180. text = '\n"\\x"'
  1181. tokenizer = text_format.Tokenizer(text.splitlines())
  1182. self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
  1183. def testConsumeBool(self):
  1184. text = 'not-a-bool'
  1185. tokenizer = text_format.Tokenizer(text.splitlines())
  1186. self.assertRaises(text_format.ParseError, tokenizer.ConsumeBool)
  1187. def testSkipComment(self):
  1188. tokenizer = text_format.Tokenizer('# some comment'.splitlines())
  1189. self.assertTrue(tokenizer.AtEnd())
  1190. self.assertRaises(text_format.ParseError, tokenizer.ConsumeComment)
  1191. def testConsumeComment(self):
  1192. tokenizer = text_format.Tokenizer('# some comment'.splitlines(),
  1193. skip_comments=False)
  1194. self.assertFalse(tokenizer.AtEnd())
  1195. self.assertEqual('# some comment', tokenizer.ConsumeComment())
  1196. self.assertTrue(tokenizer.AtEnd())
  1197. def testConsumeTwoComments(self):
  1198. text = '# some comment\n# another comment'
  1199. tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False)
  1200. self.assertEqual('# some comment', tokenizer.ConsumeComment())
  1201. self.assertFalse(tokenizer.AtEnd())
  1202. self.assertEqual('# another comment', tokenizer.ConsumeComment())
  1203. self.assertTrue(tokenizer.AtEnd())
  1204. def testConsumeTrailingComment(self):
  1205. text = 'some_number: 4\n# some comment'
  1206. tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False)
  1207. self.assertRaises(text_format.ParseError, tokenizer.ConsumeComment)
  1208. self.assertEqual('some_number', tokenizer.ConsumeIdentifier())
  1209. self.assertEqual(tokenizer.token, ':')
  1210. tokenizer.NextToken()
  1211. self.assertRaises(text_format.ParseError, tokenizer.ConsumeComment)
  1212. self.assertEqual(4, tokenizer.ConsumeInteger())
  1213. self.assertFalse(tokenizer.AtEnd())
  1214. self.assertEqual('# some comment', tokenizer.ConsumeComment())
  1215. self.assertTrue(tokenizer.AtEnd())
  1216. def testConsumeLineComment(self):
  1217. tokenizer = text_format.Tokenizer('# some comment'.splitlines(),
  1218. skip_comments=False)
  1219. self.assertFalse(tokenizer.AtEnd())
  1220. self.assertEqual((False, '# some comment'),
  1221. tokenizer.ConsumeCommentOrTrailingComment())
  1222. self.assertTrue(tokenizer.AtEnd())
  1223. def testConsumeTwoLineComments(self):
  1224. text = '# some comment\n# another comment'
  1225. tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False)
  1226. self.assertEqual((False, '# some comment'),
  1227. tokenizer.ConsumeCommentOrTrailingComment())
  1228. self.assertFalse(tokenizer.AtEnd())
  1229. self.assertEqual((False, '# another comment'),
  1230. tokenizer.ConsumeCommentOrTrailingComment())
  1231. self.assertTrue(tokenizer.AtEnd())
  1232. def testConsumeAndCheckTrailingComment(self):
  1233. text = 'some_number: 4 # some comment' # trailing comment on the same line
  1234. tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False)
  1235. self.assertRaises(text_format.ParseError,
  1236. tokenizer.ConsumeCommentOrTrailingComment)
  1237. self.assertEqual('some_number', tokenizer.ConsumeIdentifier())
  1238. self.assertEqual(tokenizer.token, ':')
  1239. tokenizer.NextToken()
  1240. self.assertRaises(text_format.ParseError,
  1241. tokenizer.ConsumeCommentOrTrailingComment)
  1242. self.assertEqual(4, tokenizer.ConsumeInteger())
  1243. self.assertFalse(tokenizer.AtEnd())
  1244. self.assertEqual((True, '# some comment'),
  1245. tokenizer.ConsumeCommentOrTrailingComment())
  1246. self.assertTrue(tokenizer.AtEnd())
  1247. def testHashinComment(self):
  1248. text = 'some_number: 4 # some comment # not a new comment'
  1249. tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False)
  1250. self.assertEqual('some_number', tokenizer.ConsumeIdentifier())
  1251. self.assertEqual(tokenizer.token, ':')
  1252. tokenizer.NextToken()
  1253. self.assertEqual(4, tokenizer.ConsumeInteger())
  1254. self.assertEqual((True, '# some comment # not a new comment'),
  1255. tokenizer.ConsumeCommentOrTrailingComment())
  1256. self.assertTrue(tokenizer.AtEnd())
  1257. # Tests for pretty printer functionality.
  1258. @_parameterized.Parameters((unittest_pb2), (unittest_proto3_arena_pb2))
  1259. class PrettyPrinterTest(TextFormatBase):
  1260. def testPrettyPrintNoMatch(self, message_module):
  1261. def printer(message, indent, as_one_line):
  1262. del message, indent, as_one_line
  1263. return None
  1264. message = message_module.TestAllTypes()
  1265. msg = message.repeated_nested_message.add()
  1266. msg.bb = 42
  1267. self.CompareToGoldenText(
  1268. text_format.MessageToString(
  1269. message, as_one_line=True, message_formatter=printer),
  1270. 'repeated_nested_message { bb: 42 }')
  1271. def testPrettyPrintOneLine(self, message_module):
  1272. def printer(m, indent, as_one_line):
  1273. del indent, as_one_line
  1274. if m.DESCRIPTOR == message_module.TestAllTypes.NestedMessage.DESCRIPTOR:
  1275. return 'My lucky number is %s' % m.bb
  1276. message = message_module.TestAllTypes()
  1277. msg = message.repeated_nested_message.add()
  1278. msg.bb = 42
  1279. self.CompareToGoldenText(
  1280. text_format.MessageToString(
  1281. message, as_one_line=True, message_formatter=printer),
  1282. 'repeated_nested_message { My lucky number is 42 }')
  1283. def testPrettyPrintMultiLine(self, message_module):
  1284. def printer(m, indent, as_one_line):
  1285. if m.DESCRIPTOR == message_module.TestAllTypes.NestedMessage.DESCRIPTOR:
  1286. line_deliminator = (' ' if as_one_line else '\n') + ' ' * indent
  1287. return 'My lucky number is:%s%s' % (line_deliminator, m.bb)
  1288. return None
  1289. message = message_module.TestAllTypes()
  1290. msg = message.repeated_nested_message.add()
  1291. msg.bb = 42
  1292. self.CompareToGoldenText(
  1293. text_format.MessageToString(
  1294. message, as_one_line=True, message_formatter=printer),
  1295. 'repeated_nested_message { My lucky number is: 42 }')
  1296. self.CompareToGoldenText(
  1297. text_format.MessageToString(
  1298. message, as_one_line=False, message_formatter=printer),
  1299. 'repeated_nested_message {\n My lucky number is:\n 42\n}\n')
  1300. def testPrettyPrintEntireMessage(self, message_module):
  1301. def printer(m, indent, as_one_line):
  1302. del indent, as_one_line
  1303. if m.DESCRIPTOR == message_module.TestAllTypes.DESCRIPTOR:
  1304. return 'The is the message!'
  1305. return None
  1306. message = message_module.TestAllTypes()
  1307. self.CompareToGoldenText(
  1308. text_format.MessageToString(
  1309. message, as_one_line=False, message_formatter=printer),
  1310. 'The is the message!\n')
  1311. self.CompareToGoldenText(
  1312. text_format.MessageToString(
  1313. message, as_one_line=True, message_formatter=printer),
  1314. 'The is the message!')
  1315. def testPrettyPrintMultipleParts(self, message_module):
  1316. def printer(m, indent, as_one_line):
  1317. del indent, as_one_line
  1318. if m.DESCRIPTOR == message_module.TestAllTypes.NestedMessage.DESCRIPTOR:
  1319. return 'My lucky number is %s' % m.bb
  1320. return None
  1321. message = message_module.TestAllTypes()
  1322. message.optional_int32 = 61
  1323. msg = message.repeated_nested_message.add()
  1324. msg.bb = 42
  1325. msg = message.repeated_nested_message.add()
  1326. msg.bb = 99
  1327. msg = message.optional_nested_message
  1328. msg.bb = 1
  1329. self.CompareToGoldenText(
  1330. text_format.MessageToString(
  1331. message, as_one_line=True, message_formatter=printer),
  1332. ('optional_int32: 61 '
  1333. 'optional_nested_message { My lucky number is 1 } '
  1334. 'repeated_nested_message { My lucky number is 42 } '
  1335. 'repeated_nested_message { My lucky number is 99 }'))
  1336. if __name__ == '__main__':
  1337. unittest.main()