text_format_test.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. #! /usr/bin/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. from google.apputils import basetest
  36. from google.protobuf.internal import _parameterized
  37. from google.protobuf import unittest_mset_pb2
  38. from google.protobuf import unittest_pb2
  39. from google.protobuf import unittest_proto3_arena_pb2
  40. from google.protobuf.internal import api_implementation
  41. from google.protobuf.internal import test_util
  42. from google.protobuf import text_format
  43. # Base class with some common functionality.
  44. class TextFormatBase(basetest.TestCase):
  45. def ReadGolden(self, golden_filename):
  46. with test_util.GoldenFile(golden_filename) as f:
  47. return (f.readlines() if str is bytes else # PY3
  48. [golden_line.decode('utf-8') for golden_line in f])
  49. def CompareToGoldenFile(self, text, golden_filename):
  50. golden_lines = self.ReadGolden(golden_filename)
  51. self.assertMultiLineEqual(text, ''.join(golden_lines))
  52. def CompareToGoldenText(self, text, golden_text):
  53. self.assertMultiLineEqual(text, golden_text)
  54. def RemoveRedundantZeros(self, text):
  55. # Some platforms print 1e+5 as 1e+005. This is fine, but we need to remove
  56. # these zeros in order to match the golden file.
  57. text = text.replace('e+0','e+').replace('e+0','e+') \
  58. .replace('e-0','e-').replace('e-0','e-')
  59. # Floating point fields are printed with .0 suffix even if they are
  60. # actualy integer numbers.
  61. text = re.compile('\.0$', re.MULTILINE).sub('', text)
  62. return text
  63. @_parameterized.Parameters(
  64. (unittest_pb2),
  65. (unittest_proto3_arena_pb2))
  66. class TextFormatTest(TextFormatBase):
  67. def testPrintExotic(self, message_module):
  68. message = message_module.TestAllTypes()
  69. message.repeated_int64.append(-9223372036854775808)
  70. message.repeated_uint64.append(18446744073709551615)
  71. message.repeated_double.append(123.456)
  72. message.repeated_double.append(1.23e22)
  73. message.repeated_double.append(1.23e-18)
  74. message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
  75. message.repeated_string.append(u'\u00fc\ua71f')
  76. self.CompareToGoldenText(
  77. self.RemoveRedundantZeros(text_format.MessageToString(message)),
  78. 'repeated_int64: -9223372036854775808\n'
  79. 'repeated_uint64: 18446744073709551615\n'
  80. 'repeated_double: 123.456\n'
  81. 'repeated_double: 1.23e+22\n'
  82. 'repeated_double: 1.23e-18\n'
  83. 'repeated_string:'
  84. ' "\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
  85. 'repeated_string: "\\303\\274\\352\\234\\237"\n')
  86. def testPrintExoticUnicodeSubclass(self, message_module):
  87. class UnicodeSub(unicode):
  88. pass
  89. message = message_module.TestAllTypes()
  90. message.repeated_string.append(UnicodeSub(u'\u00fc\ua71f'))
  91. self.CompareToGoldenText(
  92. text_format.MessageToString(message),
  93. 'repeated_string: "\\303\\274\\352\\234\\237"\n')
  94. def testPrintNestedMessageAsOneLine(self, message_module):
  95. message = message_module.TestAllTypes()
  96. msg = message.repeated_nested_message.add()
  97. msg.bb = 42
  98. self.CompareToGoldenText(
  99. text_format.MessageToString(message, as_one_line=True),
  100. 'repeated_nested_message { bb: 42 }')
  101. def testPrintRepeatedFieldsAsOneLine(self, message_module):
  102. message = message_module.TestAllTypes()
  103. message.repeated_int32.append(1)
  104. message.repeated_int32.append(1)
  105. message.repeated_int32.append(3)
  106. message.repeated_string.append('Google')
  107. message.repeated_string.append('Zurich')
  108. self.CompareToGoldenText(
  109. text_format.MessageToString(message, as_one_line=True),
  110. 'repeated_int32: 1 repeated_int32: 1 repeated_int32: 3 '
  111. 'repeated_string: "Google" repeated_string: "Zurich"')
  112. def testPrintNestedNewLineInStringAsOneLine(self, message_module):
  113. message = message_module.TestAllTypes()
  114. message.optional_string = 'a\nnew\nline'
  115. self.CompareToGoldenText(
  116. text_format.MessageToString(message, as_one_line=True),
  117. 'optional_string: "a\\nnew\\nline"')
  118. def testPrintExoticAsOneLine(self, message_module):
  119. message = message_module.TestAllTypes()
  120. message.repeated_int64.append(-9223372036854775808)
  121. message.repeated_uint64.append(18446744073709551615)
  122. message.repeated_double.append(123.456)
  123. message.repeated_double.append(1.23e22)
  124. message.repeated_double.append(1.23e-18)
  125. message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
  126. message.repeated_string.append(u'\u00fc\ua71f')
  127. self.CompareToGoldenText(
  128. self.RemoveRedundantZeros(
  129. text_format.MessageToString(message, as_one_line=True)),
  130. 'repeated_int64: -9223372036854775808'
  131. ' repeated_uint64: 18446744073709551615'
  132. ' repeated_double: 123.456'
  133. ' repeated_double: 1.23e+22'
  134. ' repeated_double: 1.23e-18'
  135. ' repeated_string: '
  136. '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""'
  137. ' repeated_string: "\\303\\274\\352\\234\\237"')
  138. def testRoundTripExoticAsOneLine(self, message_module):
  139. message = message_module.TestAllTypes()
  140. message.repeated_int64.append(-9223372036854775808)
  141. message.repeated_uint64.append(18446744073709551615)
  142. message.repeated_double.append(123.456)
  143. message.repeated_double.append(1.23e22)
  144. message.repeated_double.append(1.23e-18)
  145. message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
  146. message.repeated_string.append(u'\u00fc\ua71f')
  147. # Test as_utf8 = False.
  148. wire_text = text_format.MessageToString(
  149. message, as_one_line=True, as_utf8=False)
  150. parsed_message = message_module.TestAllTypes()
  151. r = text_format.Parse(wire_text, parsed_message)
  152. self.assertIs(r, parsed_message)
  153. self.assertEquals(message, parsed_message)
  154. # Test as_utf8 = True.
  155. wire_text = text_format.MessageToString(
  156. message, as_one_line=True, as_utf8=True)
  157. parsed_message = message_module.TestAllTypes()
  158. r = text_format.Parse(wire_text, parsed_message)
  159. self.assertIs(r, parsed_message)
  160. self.assertEquals(message, parsed_message,
  161. '\n%s != %s' % (message, parsed_message))
  162. def testPrintRawUtf8String(self, message_module):
  163. message = message_module.TestAllTypes()
  164. message.repeated_string.append(u'\u00fc\ua71f')
  165. text = text_format.MessageToString(message, as_utf8=True)
  166. self.CompareToGoldenText(text, 'repeated_string: "\303\274\352\234\237"\n')
  167. parsed_message = message_module.TestAllTypes()
  168. text_format.Parse(text, parsed_message)
  169. self.assertEquals(message, parsed_message,
  170. '\n%s != %s' % (message, parsed_message))
  171. def testPrintFloatFormat(self, message_module):
  172. # Check that float_format argument is passed to sub-message formatting.
  173. message = message_module.NestedTestAllTypes()
  174. # We use 1.25 as it is a round number in binary. The proto 32-bit float
  175. # will not gain additional imprecise digits as a 64-bit Python float and
  176. # show up in its str. 32-bit 1.2 is noisy when extended to 64-bit:
  177. # >>> struct.unpack('f', struct.pack('f', 1.2))[0]
  178. # 1.2000000476837158
  179. # >>> struct.unpack('f', struct.pack('f', 1.25))[0]
  180. # 1.25
  181. message.payload.optional_float = 1.25
  182. # Check rounding at 15 significant digits
  183. message.payload.optional_double = -.000003456789012345678
  184. # Check no decimal point.
  185. message.payload.repeated_float.append(-5642)
  186. # Check no trailing zeros.
  187. message.payload.repeated_double.append(.000078900)
  188. formatted_fields = ['optional_float: 1.25',
  189. 'optional_double: -3.45678901234568e-6',
  190. 'repeated_float: -5642',
  191. 'repeated_double: 7.89e-5']
  192. text_message = text_format.MessageToString(message, float_format='.15g')
  193. self.CompareToGoldenText(
  194. self.RemoveRedundantZeros(text_message),
  195. 'payload {{\n {}\n {}\n {}\n {}\n}}\n'.format(*formatted_fields))
  196. # as_one_line=True is a separate code branch where float_format is passed.
  197. text_message = text_format.MessageToString(message, as_one_line=True,
  198. float_format='.15g')
  199. self.CompareToGoldenText(
  200. self.RemoveRedundantZeros(text_message),
  201. 'payload {{ {} {} {} {} }}'.format(*formatted_fields))
  202. def testMessageToString(self, message_module):
  203. message = message_module.ForeignMessage()
  204. message.c = 123
  205. self.assertEqual('c: 123\n', str(message))
  206. def testParseAllFields(self, message_module):
  207. message = message_module.TestAllTypes()
  208. test_util.SetAllFields(message)
  209. ascii_text = text_format.MessageToString(message)
  210. parsed_message = message_module.TestAllTypes()
  211. text_format.Parse(ascii_text, parsed_message)
  212. self.assertEqual(message, parsed_message)
  213. if message_module is unittest_pb2:
  214. test_util.ExpectAllFieldsSet(self, message)
  215. def testParseExotic(self, message_module):
  216. message = message_module.TestAllTypes()
  217. text = ('repeated_int64: -9223372036854775808\n'
  218. 'repeated_uint64: 18446744073709551615\n'
  219. 'repeated_double: 123.456\n'
  220. 'repeated_double: 1.23e+22\n'
  221. 'repeated_double: 1.23e-18\n'
  222. 'repeated_string: \n'
  223. '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
  224. 'repeated_string: "foo" \'corge\' "grault"\n'
  225. 'repeated_string: "\\303\\274\\352\\234\\237"\n'
  226. 'repeated_string: "\\xc3\\xbc"\n'
  227. 'repeated_string: "\xc3\xbc"\n')
  228. text_format.Parse(text, message)
  229. self.assertEqual(-9223372036854775808, message.repeated_int64[0])
  230. self.assertEqual(18446744073709551615, message.repeated_uint64[0])
  231. self.assertEqual(123.456, message.repeated_double[0])
  232. self.assertEqual(1.23e22, message.repeated_double[1])
  233. self.assertEqual(1.23e-18, message.repeated_double[2])
  234. self.assertEqual(
  235. '\000\001\a\b\f\n\r\t\v\\\'"', message.repeated_string[0])
  236. self.assertEqual('foocorgegrault', message.repeated_string[1])
  237. self.assertEqual(u'\u00fc\ua71f', message.repeated_string[2])
  238. self.assertEqual(u'\u00fc', message.repeated_string[3])
  239. def testParseTrailingCommas(self, message_module):
  240. message = message_module.TestAllTypes()
  241. text = ('repeated_int64: 100;\n'
  242. 'repeated_int64: 200;\n'
  243. 'repeated_int64: 300,\n'
  244. 'repeated_string: "one",\n'
  245. 'repeated_string: "two";\n')
  246. text_format.Parse(text, message)
  247. self.assertEqual(100, message.repeated_int64[0])
  248. self.assertEqual(200, message.repeated_int64[1])
  249. self.assertEqual(300, message.repeated_int64[2])
  250. self.assertEqual(u'one', message.repeated_string[0])
  251. self.assertEqual(u'two', message.repeated_string[1])
  252. def testParseEmptyText(self, message_module):
  253. message = message_module.TestAllTypes()
  254. text = ''
  255. text_format.Parse(text, message)
  256. self.assertEquals(message_module.TestAllTypes(), message)
  257. def testParseInvalidUtf8(self, message_module):
  258. message = message_module.TestAllTypes()
  259. text = 'repeated_string: "\\xc3\\xc3"'
  260. self.assertRaises(text_format.ParseError, text_format.Parse, text, message)
  261. def testParseSingleWord(self, message_module):
  262. message = message_module.TestAllTypes()
  263. text = 'foo'
  264. self.assertRaisesRegexp(
  265. text_format.ParseError,
  266. (r'1:1 : Message type "\w+.TestAllTypes" has no field named '
  267. r'"foo".'),
  268. text_format.Parse, text, message)
  269. def testParseUnknownField(self, message_module):
  270. message = message_module.TestAllTypes()
  271. text = 'unknown_field: 8\n'
  272. self.assertRaisesRegexp(
  273. text_format.ParseError,
  274. (r'1:1 : Message type "\w+.TestAllTypes" has no field named '
  275. r'"unknown_field".'),
  276. text_format.Parse, text, message)
  277. def testParseGroupNotClosed(self, message_module):
  278. message = message_module.TestAllTypes()
  279. text = 'RepeatedGroup: <'
  280. self.assertRaisesWithLiteralMatch(
  281. text_format.ParseError, '1:16 : Expected ">".',
  282. text_format.Parse, text, message)
  283. text = 'RepeatedGroup: {'
  284. self.assertRaisesWithLiteralMatch(
  285. text_format.ParseError, '1:16 : Expected "}".',
  286. text_format.Parse, text, message)
  287. def testParseEmptyGroup(self, message_module):
  288. message = message_module.TestAllTypes()
  289. text = 'OptionalGroup: {}'
  290. text_format.Parse(text, message)
  291. self.assertTrue(message.HasField('optionalgroup'))
  292. message.Clear()
  293. message = message_module.TestAllTypes()
  294. text = 'OptionalGroup: <>'
  295. text_format.Parse(text, message)
  296. self.assertTrue(message.HasField('optionalgroup'))
  297. def testParseBadEnumValue(self, message_module):
  298. message = message_module.TestAllTypes()
  299. text = 'optional_nested_enum: BARR'
  300. self.assertRaisesRegexp(
  301. text_format.ParseError,
  302. (r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
  303. r'has no value named BARR.'),
  304. text_format.Parse, text, message)
  305. message = message_module.TestAllTypes()
  306. text = 'optional_nested_enum: 100'
  307. self.assertRaisesRegexp(
  308. text_format.ParseError,
  309. (r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
  310. r'has no value with number 100.'),
  311. text_format.Parse, text, message)
  312. def testParseBadIntValue(self, message_module):
  313. message = message_module.TestAllTypes()
  314. text = 'optional_int32: bork'
  315. self.assertRaisesWithLiteralMatch(
  316. text_format.ParseError,
  317. ('1:17 : Couldn\'t parse integer: bork'),
  318. text_format.Parse, text, message)
  319. def testParseStringFieldUnescape(self, message_module):
  320. message = message_module.TestAllTypes()
  321. text = r'''repeated_string: "\xf\x62"
  322. repeated_string: "\\xf\\x62"
  323. repeated_string: "\\\xf\\\x62"
  324. repeated_string: "\\\\xf\\\\x62"
  325. repeated_string: "\\\\\xf\\\\\x62"
  326. repeated_string: "\x5cx20"'''
  327. text_format.Parse(text, message)
  328. SLASH = '\\'
  329. self.assertEqual('\x0fb', message.repeated_string[0])
  330. self.assertEqual(SLASH + 'xf' + SLASH + 'x62', message.repeated_string[1])
  331. self.assertEqual(SLASH + '\x0f' + SLASH + 'b', message.repeated_string[2])
  332. self.assertEqual(SLASH + SLASH + 'xf' + SLASH + SLASH + 'x62',
  333. message.repeated_string[3])
  334. self.assertEqual(SLASH + SLASH + '\x0f' + SLASH + SLASH + 'b',
  335. message.repeated_string[4])
  336. self.assertEqual(SLASH + 'x20', message.repeated_string[5])
  337. def testMergeDuplicateScalars(self, message_module):
  338. message = message_module.TestAllTypes()
  339. text = ('optional_int32: 42 '
  340. 'optional_int32: 67')
  341. r = text_format.Merge(text, message)
  342. self.assertIs(r, message)
  343. self.assertEqual(67, message.optional_int32)
  344. def testMergeDuplicateNestedMessageScalars(self, message_module):
  345. message = message_module.TestAllTypes()
  346. text = ('optional_nested_message { bb: 1 } '
  347. 'optional_nested_message { bb: 2 }')
  348. r = text_format.Merge(text, message)
  349. self.assertTrue(r is message)
  350. self.assertEqual(2, message.optional_nested_message.bb)
  351. def testParseOneof(self, message_module):
  352. m = message_module.TestAllTypes()
  353. m.oneof_uint32 = 11
  354. m2 = message_module.TestAllTypes()
  355. text_format.Parse(text_format.MessageToString(m), m2)
  356. self.assertEqual('oneof_uint32', m2.WhichOneof('oneof_field'))
  357. # These are tests that aren't fundamentally specific to proto2, but are at
  358. # the moment because of differences between the proto2 and proto3 test schemas.
  359. # Ideally the schemas would be made more similar so these tests could pass.
  360. class OnlyWorksWithProto2RightNowTests(TextFormatBase):
  361. def testParseGolden(self):
  362. golden_text = '\n'.join(self.ReadGolden('text_format_unittest_data.txt'))
  363. parsed_message = unittest_pb2.TestAllTypes()
  364. r = text_format.Parse(golden_text, parsed_message)
  365. self.assertIs(r, parsed_message)
  366. message = unittest_pb2.TestAllTypes()
  367. test_util.SetAllFields(message)
  368. self.assertEquals(message, parsed_message)
  369. def testPrintAllFields(self):
  370. message = unittest_pb2.TestAllTypes()
  371. test_util.SetAllFields(message)
  372. self.CompareToGoldenFile(
  373. self.RemoveRedundantZeros(text_format.MessageToString(message)),
  374. 'text_format_unittest_data_oneof_implemented.txt')
  375. def testPrintAllFieldsPointy(self):
  376. message = unittest_pb2.TestAllTypes()
  377. test_util.SetAllFields(message)
  378. self.CompareToGoldenFile(
  379. self.RemoveRedundantZeros(
  380. text_format.MessageToString(message, pointy_brackets=True)),
  381. 'text_format_unittest_data_pointy_oneof.txt')
  382. def testPrintInIndexOrder(self):
  383. message = unittest_pb2.TestFieldOrderings()
  384. message.my_string = '115'
  385. message.my_int = 101
  386. message.my_float = 111
  387. message.optional_nested_message.oo = 0
  388. message.optional_nested_message.bb = 1
  389. self.CompareToGoldenText(
  390. self.RemoveRedundantZeros(text_format.MessageToString(
  391. message, use_index_order=True)),
  392. 'my_string: \"115\"\nmy_int: 101\nmy_float: 111\n'
  393. 'optional_nested_message {\n oo: 0\n bb: 1\n}\n')
  394. self.CompareToGoldenText(
  395. self.RemoveRedundantZeros(text_format.MessageToString(
  396. message)),
  397. 'my_int: 101\nmy_string: \"115\"\nmy_float: 111\n'
  398. 'optional_nested_message {\n bb: 1\n oo: 0\n}\n')
  399. def testMergeLinesGolden(self):
  400. opened = self.ReadGolden('text_format_unittest_data.txt')
  401. parsed_message = unittest_pb2.TestAllTypes()
  402. r = text_format.MergeLines(opened, parsed_message)
  403. self.assertIs(r, parsed_message)
  404. message = unittest_pb2.TestAllTypes()
  405. test_util.SetAllFields(message)
  406. self.assertEqual(message, parsed_message)
  407. def testParseLinesGolden(self):
  408. opened = self.ReadGolden('text_format_unittest_data.txt')
  409. parsed_message = unittest_pb2.TestAllTypes()
  410. r = text_format.ParseLines(opened, parsed_message)
  411. self.assertIs(r, parsed_message)
  412. message = unittest_pb2.TestAllTypes()
  413. test_util.SetAllFields(message)
  414. self.assertEquals(message, parsed_message)
  415. # Tests of proto2-only features (MessageSet and extensions).
  416. class Proto2Tests(TextFormatBase):
  417. def testPrintMessageSet(self):
  418. message = unittest_mset_pb2.TestMessageSetContainer()
  419. ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
  420. ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
  421. message.message_set.Extensions[ext1].i = 23
  422. message.message_set.Extensions[ext2].str = 'foo'
  423. self.CompareToGoldenText(
  424. text_format.MessageToString(message),
  425. 'message_set {\n'
  426. ' [protobuf_unittest.TestMessageSetExtension1] {\n'
  427. ' i: 23\n'
  428. ' }\n'
  429. ' [protobuf_unittest.TestMessageSetExtension2] {\n'
  430. ' str: \"foo\"\n'
  431. ' }\n'
  432. '}\n')
  433. def testPrintMessageSetAsOneLine(self):
  434. message = unittest_mset_pb2.TestMessageSetContainer()
  435. ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
  436. ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
  437. message.message_set.Extensions[ext1].i = 23
  438. message.message_set.Extensions[ext2].str = 'foo'
  439. self.CompareToGoldenText(
  440. text_format.MessageToString(message, as_one_line=True),
  441. 'message_set {'
  442. ' [protobuf_unittest.TestMessageSetExtension1] {'
  443. ' i: 23'
  444. ' }'
  445. ' [protobuf_unittest.TestMessageSetExtension2] {'
  446. ' str: \"foo\"'
  447. ' }'
  448. ' }')
  449. def testParseMessageSet(self):
  450. message = unittest_pb2.TestAllTypes()
  451. text = ('repeated_uint64: 1\n'
  452. 'repeated_uint64: 2\n')
  453. text_format.Parse(text, message)
  454. self.assertEqual(1, message.repeated_uint64[0])
  455. self.assertEqual(2, message.repeated_uint64[1])
  456. message = unittest_mset_pb2.TestMessageSetContainer()
  457. text = ('message_set {\n'
  458. ' [protobuf_unittest.TestMessageSetExtension1] {\n'
  459. ' i: 23\n'
  460. ' }\n'
  461. ' [protobuf_unittest.TestMessageSetExtension2] {\n'
  462. ' str: \"foo\"\n'
  463. ' }\n'
  464. '}\n')
  465. text_format.Parse(text, message)
  466. ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
  467. ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
  468. self.assertEquals(23, message.message_set.Extensions[ext1].i)
  469. self.assertEquals('foo', message.message_set.Extensions[ext2].str)
  470. def testPrintAllExtensions(self):
  471. message = unittest_pb2.TestAllExtensions()
  472. test_util.SetAllExtensions(message)
  473. self.CompareToGoldenFile(
  474. self.RemoveRedundantZeros(text_format.MessageToString(message)),
  475. 'text_format_unittest_extensions_data.txt')
  476. def testPrintAllExtensionsPointy(self):
  477. message = unittest_pb2.TestAllExtensions()
  478. test_util.SetAllExtensions(message)
  479. self.CompareToGoldenFile(
  480. self.RemoveRedundantZeros(text_format.MessageToString(
  481. message, pointy_brackets=True)),
  482. 'text_format_unittest_extensions_data_pointy.txt')
  483. def testParseGoldenExtensions(self):
  484. golden_text = '\n'.join(self.ReadGolden(
  485. 'text_format_unittest_extensions_data.txt'))
  486. parsed_message = unittest_pb2.TestAllExtensions()
  487. text_format.Parse(golden_text, parsed_message)
  488. message = unittest_pb2.TestAllExtensions()
  489. test_util.SetAllExtensions(message)
  490. self.assertEquals(message, parsed_message)
  491. def testParseAllExtensions(self):
  492. message = unittest_pb2.TestAllExtensions()
  493. test_util.SetAllExtensions(message)
  494. ascii_text = text_format.MessageToString(message)
  495. parsed_message = unittest_pb2.TestAllExtensions()
  496. text_format.Parse(ascii_text, parsed_message)
  497. self.assertEqual(message, parsed_message)
  498. def testParseBadExtension(self):
  499. message = unittest_pb2.TestAllExtensions()
  500. text = '[unknown_extension]: 8\n'
  501. self.assertRaisesWithLiteralMatch(
  502. text_format.ParseError,
  503. '1:2 : Extension "unknown_extension" not registered.',
  504. text_format.Parse, text, message)
  505. message = unittest_pb2.TestAllTypes()
  506. self.assertRaisesWithLiteralMatch(
  507. text_format.ParseError,
  508. ('1:2 : Message type "protobuf_unittest.TestAllTypes" does not have '
  509. 'extensions.'),
  510. text_format.Parse, text, message)
  511. def testMergeDuplicateExtensionScalars(self):
  512. message = unittest_pb2.TestAllExtensions()
  513. text = ('[protobuf_unittest.optional_int32_extension]: 42 '
  514. '[protobuf_unittest.optional_int32_extension]: 67')
  515. text_format.Merge(text, message)
  516. self.assertEqual(
  517. 67,
  518. message.Extensions[unittest_pb2.optional_int32_extension])
  519. def testParseDuplicateExtensionScalars(self):
  520. message = unittest_pb2.TestAllExtensions()
  521. text = ('[protobuf_unittest.optional_int32_extension]: 42 '
  522. '[protobuf_unittest.optional_int32_extension]: 67')
  523. self.assertRaisesWithLiteralMatch(
  524. text_format.ParseError,
  525. ('1:96 : Message type "protobuf_unittest.TestAllExtensions" '
  526. 'should not have multiple '
  527. '"protobuf_unittest.optional_int32_extension" extensions.'),
  528. text_format.Parse, text, message)
  529. def testParseDuplicateNestedMessageScalars(self):
  530. message = unittest_pb2.TestAllTypes()
  531. text = ('optional_nested_message { bb: 1 } '
  532. 'optional_nested_message { bb: 2 }')
  533. self.assertRaisesWithLiteralMatch(
  534. text_format.ParseError,
  535. ('1:65 : Message type "protobuf_unittest.TestAllTypes.NestedMessage" '
  536. 'should not have multiple "bb" fields.'),
  537. text_format.Parse, text, message)
  538. def testParseDuplicateScalars(self):
  539. message = unittest_pb2.TestAllTypes()
  540. text = ('optional_int32: 42 '
  541. 'optional_int32: 67')
  542. self.assertRaisesWithLiteralMatch(
  543. text_format.ParseError,
  544. ('1:36 : Message type "protobuf_unittest.TestAllTypes" should not '
  545. 'have multiple "optional_int32" fields.'),
  546. text_format.Parse, text, message)
  547. class TokenizerTest(basetest.TestCase):
  548. def testSimpleTokenCases(self):
  549. text = ('identifier1:"string1"\n \n\n'
  550. 'identifier2 : \n \n123 \n identifier3 :\'string\'\n'
  551. 'identifiER_4 : 1.1e+2 ID5:-0.23 ID6:\'aaaa\\\'bbbb\'\n'
  552. 'ID7 : "aa\\"bb"\n\n\n\n ID8: {A:inf B:-inf C:true D:false}\n'
  553. 'ID9: 22 ID10: -111111111111111111 ID11: -22\n'
  554. 'ID12: 2222222222222222222 ID13: 1.23456f ID14: 1.2e+2f '
  555. 'false_bool: 0 true_BOOL:t \n true_bool1: 1 false_BOOL1:f ')
  556. tokenizer = text_format._Tokenizer(text.splitlines())
  557. methods = [(tokenizer.ConsumeIdentifier, 'identifier1'),
  558. ':',
  559. (tokenizer.ConsumeString, 'string1'),
  560. (tokenizer.ConsumeIdentifier, 'identifier2'),
  561. ':',
  562. (tokenizer.ConsumeInt32, 123),
  563. (tokenizer.ConsumeIdentifier, 'identifier3'),
  564. ':',
  565. (tokenizer.ConsumeString, 'string'),
  566. (tokenizer.ConsumeIdentifier, 'identifiER_4'),
  567. ':',
  568. (tokenizer.ConsumeFloat, 1.1e+2),
  569. (tokenizer.ConsumeIdentifier, 'ID5'),
  570. ':',
  571. (tokenizer.ConsumeFloat, -0.23),
  572. (tokenizer.ConsumeIdentifier, 'ID6'),
  573. ':',
  574. (tokenizer.ConsumeString, 'aaaa\'bbbb'),
  575. (tokenizer.ConsumeIdentifier, 'ID7'),
  576. ':',
  577. (tokenizer.ConsumeString, 'aa\"bb'),
  578. (tokenizer.ConsumeIdentifier, 'ID8'),
  579. ':',
  580. '{',
  581. (tokenizer.ConsumeIdentifier, 'A'),
  582. ':',
  583. (tokenizer.ConsumeFloat, float('inf')),
  584. (tokenizer.ConsumeIdentifier, 'B'),
  585. ':',
  586. (tokenizer.ConsumeFloat, -float('inf')),
  587. (tokenizer.ConsumeIdentifier, 'C'),
  588. ':',
  589. (tokenizer.ConsumeBool, True),
  590. (tokenizer.ConsumeIdentifier, 'D'),
  591. ':',
  592. (tokenizer.ConsumeBool, False),
  593. '}',
  594. (tokenizer.ConsumeIdentifier, 'ID9'),
  595. ':',
  596. (tokenizer.ConsumeUint32, 22),
  597. (tokenizer.ConsumeIdentifier, 'ID10'),
  598. ':',
  599. (tokenizer.ConsumeInt64, -111111111111111111),
  600. (tokenizer.ConsumeIdentifier, 'ID11'),
  601. ':',
  602. (tokenizer.ConsumeInt32, -22),
  603. (tokenizer.ConsumeIdentifier, 'ID12'),
  604. ':',
  605. (tokenizer.ConsumeUint64, 2222222222222222222),
  606. (tokenizer.ConsumeIdentifier, 'ID13'),
  607. ':',
  608. (tokenizer.ConsumeFloat, 1.23456),
  609. (tokenizer.ConsumeIdentifier, 'ID14'),
  610. ':',
  611. (tokenizer.ConsumeFloat, 1.2e+2),
  612. (tokenizer.ConsumeIdentifier, 'false_bool'),
  613. ':',
  614. (tokenizer.ConsumeBool, False),
  615. (tokenizer.ConsumeIdentifier, 'true_BOOL'),
  616. ':',
  617. (tokenizer.ConsumeBool, True),
  618. (tokenizer.ConsumeIdentifier, 'true_bool1'),
  619. ':',
  620. (tokenizer.ConsumeBool, True),
  621. (tokenizer.ConsumeIdentifier, 'false_BOOL1'),
  622. ':',
  623. (tokenizer.ConsumeBool, False)]
  624. i = 0
  625. while not tokenizer.AtEnd():
  626. m = methods[i]
  627. if type(m) == str:
  628. token = tokenizer.token
  629. self.assertEqual(token, m)
  630. tokenizer.NextToken()
  631. else:
  632. self.assertEqual(m[1], m[0]())
  633. i += 1
  634. def testConsumeIntegers(self):
  635. # This test only tests the failures in the integer parsing methods as well
  636. # as the '0' special cases.
  637. int64_max = (1 << 63) - 1
  638. uint32_max = (1 << 32) - 1
  639. text = '-1 %d %d' % (uint32_max + 1, int64_max + 1)
  640. tokenizer = text_format._Tokenizer(text.splitlines())
  641. self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint32)
  642. self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint64)
  643. self.assertEqual(-1, tokenizer.ConsumeInt32())
  644. self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint32)
  645. self.assertRaises(text_format.ParseError, tokenizer.ConsumeInt32)
  646. self.assertEqual(uint32_max + 1, tokenizer.ConsumeInt64())
  647. self.assertRaises(text_format.ParseError, tokenizer.ConsumeInt64)
  648. self.assertEqual(int64_max + 1, tokenizer.ConsumeUint64())
  649. self.assertTrue(tokenizer.AtEnd())
  650. text = '-0 -0 0 0'
  651. tokenizer = text_format._Tokenizer(text.splitlines())
  652. self.assertEqual(0, tokenizer.ConsumeUint32())
  653. self.assertEqual(0, tokenizer.ConsumeUint64())
  654. self.assertEqual(0, tokenizer.ConsumeUint32())
  655. self.assertEqual(0, tokenizer.ConsumeUint64())
  656. self.assertTrue(tokenizer.AtEnd())
  657. def testConsumeByteString(self):
  658. text = '"string1\''
  659. tokenizer = text_format._Tokenizer(text.splitlines())
  660. self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
  661. text = 'string1"'
  662. tokenizer = text_format._Tokenizer(text.splitlines())
  663. self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
  664. text = '\n"\\xt"'
  665. tokenizer = text_format._Tokenizer(text.splitlines())
  666. self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
  667. text = '\n"\\"'
  668. tokenizer = text_format._Tokenizer(text.splitlines())
  669. self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
  670. text = '\n"\\x"'
  671. tokenizer = text_format._Tokenizer(text.splitlines())
  672. self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
  673. def testConsumeBool(self):
  674. text = 'not-a-bool'
  675. tokenizer = text_format._Tokenizer(text.splitlines())
  676. self.assertRaises(text_format.ParseError, tokenizer.ConsumeBool)
  677. if __name__ == '__main__':
  678. basetest.main()