pddm_tests.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. #! /usr/bin/python
  2. #
  3. # Protocol Buffers - Google's data interchange format
  4. # Copyright 2015 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. """Tests for pddm.py."""
  33. import io
  34. import unittest
  35. import pddm
  36. class TestParsingMacros(unittest.TestCase):
  37. def testParseEmpty(self):
  38. f = io.StringIO(u'')
  39. result = pddm.MacroCollection(f)
  40. self.assertEqual(len(result._macros), 0)
  41. def testParseOne(self):
  42. f = io.StringIO(u"""PDDM-DEFINE foo( )
  43. body""")
  44. result = pddm.MacroCollection(f)
  45. self.assertEqual(len(result._macros), 1)
  46. macro = result._macros.get('foo')
  47. self.assertIsNotNone(macro)
  48. self.assertEquals(macro.name, 'foo')
  49. self.assertEquals(macro.args, tuple())
  50. self.assertEquals(macro.body, 'body')
  51. def testParseGeneral(self):
  52. # Tests multiple defines, spaces in all places, etc.
  53. f = io.StringIO(u"""
  54. PDDM-DEFINE noArgs( )
  55. body1
  56. body2
  57. PDDM-DEFINE-END
  58. PDDM-DEFINE oneArg(foo)
  59. body3
  60. PDDM-DEFINE twoArgs( bar_ , baz )
  61. body4
  62. body5""")
  63. result = pddm.MacroCollection(f)
  64. self.assertEqual(len(result._macros), 3)
  65. macro = result._macros.get('noArgs')
  66. self.assertIsNotNone(macro)
  67. self.assertEquals(macro.name, 'noArgs')
  68. self.assertEquals(macro.args, tuple())
  69. self.assertEquals(macro.body, 'body1\nbody2\n')
  70. macro = result._macros.get('oneArg')
  71. self.assertIsNotNone(macro)
  72. self.assertEquals(macro.name, 'oneArg')
  73. self.assertEquals(macro.args, ('foo',))
  74. self.assertEquals(macro.body, 'body3')
  75. macro = result._macros.get('twoArgs')
  76. self.assertIsNotNone(macro)
  77. self.assertEquals(macro.name, 'twoArgs')
  78. self.assertEquals(macro.args, ('bar_', 'baz'))
  79. self.assertEquals(macro.body, 'body4\nbody5')
  80. # Add into existing collection
  81. f = io.StringIO(u"""
  82. PDDM-DEFINE another(a,b,c)
  83. body1
  84. body2""")
  85. result.ParseInput(f)
  86. self.assertEqual(len(result._macros), 4)
  87. macro = result._macros.get('another')
  88. self.assertIsNotNone(macro)
  89. self.assertEquals(macro.name, 'another')
  90. self.assertEquals(macro.args, ('a', 'b', 'c'))
  91. self.assertEquals(macro.body, 'body1\nbody2')
  92. def testParseDirectiveIssues(self):
  93. test_list = [
  94. # Unknown directive
  95. (u'PDDM-DEFINE foo()\nbody\nPDDM-DEFINED foo\nbaz',
  96. 'Hit a line with an unknown directive: '),
  97. # End without begin
  98. (u'PDDM-DEFINE foo()\nbody\nPDDM-DEFINE-END\nPDDM-DEFINE-END\n',
  99. 'Got DEFINE-END directive without an active macro: '),
  100. # Line not in macro block
  101. (u'PDDM-DEFINE foo()\nbody\nPDDM-DEFINE-END\nmumble\n',
  102. 'Hit a line that wasn\'t a directive and no open macro definition: '),
  103. # Redefine macro
  104. (u'PDDM-DEFINE foo()\nbody\nPDDM-DEFINE foo(a)\nmumble\n',
  105. 'Attempt to redefine macro: '),
  106. ]
  107. for idx, (input_str, expected_prefix) in enumerate(test_list, 1):
  108. f = io.StringIO(input_str)
  109. try:
  110. result = pddm.MacroCollection(f)
  111. self.fail('Should throw exception, entry %d' % idx)
  112. except pddm.PDDMError as e:
  113. self.assertTrue(e.message.startswith(expected_prefix),
  114. 'Entry %d failed: %r' % (idx, e))
  115. def testParseBeginIssues(self):
  116. test_list = [
  117. # 1. No name
  118. (u'PDDM-DEFINE\nmumble',
  119. 'Failed to parse macro definition: '),
  120. # 2. No name (with spaces)
  121. (u'PDDM-DEFINE \nmumble',
  122. 'Failed to parse macro definition: '),
  123. # 3. No open paren
  124. (u'PDDM-DEFINE foo\nmumble',
  125. 'Failed to parse macro definition: '),
  126. # 4. No close paren
  127. (u'PDDM-DEFINE foo(\nmumble',
  128. 'Failed to parse macro definition: '),
  129. # 5. No close paren (with args)
  130. (u'PDDM-DEFINE foo(a, b\nmumble',
  131. 'Failed to parse macro definition: '),
  132. # 6. No name before args
  133. (u'PDDM-DEFINE (a, b)\nmumble',
  134. 'Failed to parse macro definition: '),
  135. # 7. No name before args
  136. (u'PDDM-DEFINE foo bar(a, b)\nmumble',
  137. 'Failed to parse macro definition: '),
  138. # 8. Empty arg name
  139. (u'PDDM-DEFINE foo(a, ,b)\nmumble',
  140. 'Empty arg name in macro definition: '),
  141. (u'PDDM-DEFINE foo(a,,b)\nmumble',
  142. 'Empty arg name in macro definition: '),
  143. # 10. Duplicate name
  144. (u'PDDM-DEFINE foo(a,b,a,c)\nmumble',
  145. 'Arg name "a" used more than once in macro definition: '),
  146. # 11. Invalid arg name
  147. (u'PDDM-DEFINE foo(a b,c)\nmumble',
  148. 'Invalid arg name "a b" in macro definition: '),
  149. (u'PDDM-DEFINE foo(a.b,c)\nmumble',
  150. 'Invalid arg name "a.b" in macro definition: '),
  151. (u'PDDM-DEFINE foo(a-b,c)\nmumble',
  152. 'Invalid arg name "a-b" in macro definition: '),
  153. (u'PDDM-DEFINE foo(a,b,c.)\nmumble',
  154. 'Invalid arg name "c." in macro definition: '),
  155. # 15. Extra stuff after the name
  156. (u'PDDM-DEFINE foo(a,c) foo\nmumble',
  157. 'Failed to parse macro definition: '),
  158. (u'PDDM-DEFINE foo(a,c) foo)\nmumble',
  159. 'Failed to parse macro definition: '),
  160. ]
  161. for idx, (input_str, expected_prefix) in enumerate(test_list, 1):
  162. f = io.StringIO(input_str)
  163. try:
  164. result = pddm.MacroCollection(f)
  165. self.fail('Should throw exception, entry %d' % idx)
  166. except pddm.PDDMError as e:
  167. self.assertTrue(e.message.startswith(expected_prefix),
  168. 'Entry %d failed: %r' % (idx, e))
  169. class TestExpandingMacros(unittest.TestCase):
  170. def testExpandBasics(self):
  171. f = io.StringIO(u"""
  172. PDDM-DEFINE noArgs( )
  173. body1
  174. body2
  175. PDDM-DEFINE-END
  176. PDDM-DEFINE oneArg(a)
  177. body3 a
  178. PDDM-DEFINE-END
  179. PDDM-DEFINE twoArgs(b,c)
  180. body4 b c
  181. body5
  182. PDDM-DEFINE-END
  183. """)
  184. mc = pddm.MacroCollection(f)
  185. test_list = [
  186. (u'noArgs()',
  187. 'body1\nbody2\n'),
  188. (u'oneArg(wee)',
  189. 'body3 wee\n'),
  190. (u'twoArgs(having some, fun)',
  191. 'body4 having some fun\nbody5'),
  192. # One arg, pass empty.
  193. (u'oneArg()',
  194. 'body3 \n'),
  195. # Two args, gets empty in each slot.
  196. (u'twoArgs(, empty)',
  197. 'body4 empty\nbody5'),
  198. (u'twoArgs(empty, )',
  199. 'body4 empty \nbody5'),
  200. (u'twoArgs(, )',
  201. 'body4 \nbody5'),
  202. ]
  203. for idx, (input_str, expected) in enumerate(test_list, 1):
  204. result = mc.Expand(input_str)
  205. self.assertEqual(result, expected,
  206. 'Entry %d --\n Result: %r\n Expected: %r' %
  207. (idx, result, expected))
  208. def testExpandArgOptions(self):
  209. f = io.StringIO(u"""
  210. PDDM-DEFINE bar(a)
  211. a-a$S-a$l-a$L-a$u-a$U
  212. PDDM-DEFINE-END
  213. """)
  214. mc = pddm.MacroCollection(f)
  215. self.assertEqual(mc.Expand('bar(xYz)'), 'xYz- -xYz-xyz-XYz-XYZ')
  216. self.assertEqual(mc.Expand('bar(MnoP)'), 'MnoP- -mnoP-mnop-MnoP-MNOP')
  217. # Test empty
  218. self.assertEqual(mc.Expand('bar()'), '-----')
  219. def testExpandSimpleMacroErrors(self):
  220. f = io.StringIO(u"""
  221. PDDM-DEFINE foo(a, b)
  222. <a-z>
  223. PDDM-DEFINE baz(a)
  224. a - a$z
  225. """)
  226. mc = pddm.MacroCollection(f)
  227. test_list = [
  228. # 1. Unknown macro
  229. (u'bar()',
  230. 'No macro named "bar".'),
  231. (u'bar(a)',
  232. 'No macro named "bar".'),
  233. # 3. Arg mismatch
  234. (u'foo()',
  235. 'Expected 2 args, got: "foo()".'),
  236. (u'foo(a b)',
  237. 'Expected 2 args, got: "foo(a b)".'),
  238. (u'foo(a,b,c)',
  239. 'Expected 2 args, got: "foo(a,b,c)".'),
  240. # 6. Unknown option in expansion
  241. (u'baz(mumble)',
  242. 'Unknown arg option "a$z" while expanding "baz(mumble)".'),
  243. ]
  244. for idx, (input_str, expected_err) in enumerate(test_list, 1):
  245. try:
  246. result = mc.Expand(input_str)
  247. self.fail('Should throw exception, entry %d' % idx)
  248. except pddm.PDDMError as e:
  249. self.assertEqual(e.message, expected_err,
  250. 'Entry %d failed: %r' % (idx, e))
  251. def testExpandReferences(self):
  252. f = io.StringIO(u"""
  253. PDDM-DEFINE StartIt()
  254. foo(abc, def)
  255. foo(ghi, jkl)
  256. PDDM-DEFINE foo(a, b)
  257. bar(a, int)
  258. bar(b, NSString *)
  259. PDDM-DEFINE bar(n, t)
  260. - (t)n;
  261. - (void)set##n$u##:(t)value;
  262. """)
  263. mc = pddm.MacroCollection(f)
  264. expected = """- (int)abc;
  265. - (void)setAbc:(int)value;
  266. - (NSString *)def;
  267. - (void)setDef:(NSString *)value;
  268. - (int)ghi;
  269. - (void)setGhi:(int)value;
  270. - (NSString *)jkl;
  271. - (void)setJkl:(NSString *)value;
  272. """
  273. self.assertEqual(mc.Expand('StartIt()'), expected)
  274. def testCatchRecursion(self):
  275. f = io.StringIO(u"""
  276. PDDM-DEFINE foo(a, b)
  277. bar(1, a)
  278. bar(2, b)
  279. PDDM-DEFINE bar(x, y)
  280. foo(x, y)
  281. """)
  282. mc = pddm.MacroCollection(f)
  283. try:
  284. result = mc.Expand('foo(A,B)')
  285. self.fail('Should throw exception! Test failed to catch recursion.')
  286. except pddm.PDDMError as e:
  287. self.assertEqual(e.message,
  288. 'Found macro recursion, invoking "foo(1, A)":\n...while expanding "bar(1, A)".\n...while expanding "foo(A,B)".')
  289. class TestParsingSource(unittest.TestCase):
  290. def testBasicParse(self):
  291. test_list = [
  292. # 1. no directives
  293. (u'a\nb\nc',
  294. (3,) ),
  295. # 2. One define
  296. (u'a\n//%PDDM-DEFINE foo()\n//%body\nc',
  297. (1, 2, 1) ),
  298. # 3. Two defines
  299. (u'a\n//%PDDM-DEFINE foo()\n//%body\n//%PDDM-DEFINE bar()\n//%body2\nc',
  300. (1, 4, 1) ),
  301. # 4. Two defines with ends
  302. (u'a\n//%PDDM-DEFINE foo()\n//%body\n//%PDDM-DEFINE-END\n'
  303. u'//%PDDM-DEFINE bar()\n//%body2\n//%PDDM-DEFINE-END\nc',
  304. (1, 6, 1) ),
  305. # 5. One expand, one define (that runs to end of file)
  306. (u'a\n//%PDDM-EXPAND foo()\nbody\n//%PDDM-EXPAND-END\n'
  307. u'//%PDDM-DEFINE bar()\n//%body2\n',
  308. (1, 1, 2) ),
  309. # 6. One define ended with an expand.
  310. (u'a\nb\n//%PDDM-DEFINE bar()\n//%body2\n'
  311. u'//%PDDM-EXPAND bar()\nbody2\n//%PDDM-EXPAND-END\n',
  312. (2, 2, 1) ),
  313. # 7. Two expands (one end), one define.
  314. (u'a\n//%PDDM-EXPAND foo(1)\nbody\n//%PDDM-EXPAND foo(2)\nbody2\n//%PDDM-EXPAND-END\n'
  315. u'//%PDDM-DEFINE foo()\n//%body2\n',
  316. (1, 2, 2) ),
  317. ]
  318. for idx, (input_str, line_counts) in enumerate(test_list, 1):
  319. f = io.StringIO(input_str)
  320. sf = pddm.SourceFile(f)
  321. sf._ParseFile()
  322. self.assertEqual(len(sf._sections), len(line_counts),
  323. 'Entry %d -- %d != %d' %
  324. (idx, len(sf._sections), len(line_counts)))
  325. for idx2, (sec, expected) in enumerate(zip(sf._sections, line_counts), 1):
  326. self.assertEqual(sec.num_lines_captured, expected,
  327. 'Entry %d, section %d -- %d != %d' %
  328. (idx, idx2, sec.num_lines_captured, expected))
  329. def testErrors(self):
  330. test_list = [
  331. # 1. Directive within expansion
  332. (u'//%PDDM-EXPAND a()\n//%PDDM-BOGUS',
  333. 'Ran into directive ("//%PDDM-BOGUS", line 2) while in "//%PDDM-EXPAND a()".'),
  334. (u'//%PDDM-EXPAND a()\n//%PDDM-DEFINE a()\n//%body\n',
  335. 'Ran into directive ("//%PDDM-DEFINE", line 2) while in "//%PDDM-EXPAND a()".'),
  336. # 3. Expansion ran off end of file
  337. (u'//%PDDM-EXPAND a()\na\nb\n',
  338. 'Hit the end of the file while in "//%PDDM-EXPAND a()".'),
  339. # 4. Directive within define
  340. (u'//%PDDM-DEFINE a()\n//%body\n//%PDDM-BOGUS',
  341. 'Ran into directive ("//%PDDM-BOGUS", line 3) while in "//%PDDM-DEFINE a()".'),
  342. (u'//%PDDM-DEFINE a()\n//%body\n//%PDDM-EXPAND-END a()',
  343. 'Ran into directive ("//%PDDM-EXPAND-END", line 3) while in "//%PDDM-DEFINE a()".'),
  344. # 6. Directives that shouldn't start sections
  345. (u'a\n//%PDDM-DEFINE-END a()\n//a\n',
  346. 'Unexpected line 2: "//%PDDM-DEFINE-END a()".'),
  347. (u'a\n//%PDDM-EXPAND-END a()\n//a\n',
  348. 'Unexpected line 2: "//%PDDM-EXPAND-END a()".'),
  349. (u'//%PDDM-BOGUS\n//a\n',
  350. 'Unexpected line 1: "//%PDDM-BOGUS".'),
  351. ]
  352. for idx, (input_str, expected_err) in enumerate(test_list, 1):
  353. f = io.StringIO(input_str)
  354. try:
  355. pddm.SourceFile(f)._ParseFile()
  356. self.fail('Should throw exception, entry %d' % idx)
  357. except pddm.PDDMError as e:
  358. self.assertEqual(e.message, expected_err,
  359. 'Entry %d failed: %r' % (idx, e))
  360. class TestProcessingSource(unittest.TestCase):
  361. def testBasics(self):
  362. self.maxDiff = None
  363. input_str = u"""
  364. //%PDDM-IMPORT-DEFINES ImportFile
  365. foo
  366. //%PDDM-EXPAND mumble(abc)
  367. //%PDDM-EXPAND-END
  368. bar
  369. //%PDDM-EXPAND mumble(def)
  370. //%PDDM-EXPAND mumble(ghi)
  371. //%PDDM-EXPAND-END
  372. baz
  373. //%PDDM-DEFINE mumble(a_)
  374. //%a_: getName(a_)
  375. """
  376. input_str2 = u"""
  377. //%PDDM-DEFINE getName(x_)
  378. //%do##x_$u##(int x_);
  379. """
  380. expected = u"""
  381. //%PDDM-IMPORT-DEFINES ImportFile
  382. foo
  383. //%PDDM-EXPAND mumble(abc)
  384. // This block of code is generated, do not edit it directly.
  385. // clang-format off
  386. abc: doAbc(int abc);
  387. // clang-format on
  388. //%PDDM-EXPAND-END mumble(abc)
  389. bar
  390. //%PDDM-EXPAND mumble(def)
  391. // This block of code is generated, do not edit it directly.
  392. // clang-format off
  393. def: doDef(int def);
  394. // clang-format on
  395. //%PDDM-EXPAND mumble(ghi)
  396. // This block of code is generated, do not edit it directly.
  397. // clang-format off
  398. ghi: doGhi(int ghi);
  399. // clang-format on
  400. //%PDDM-EXPAND-END (2 expansions)
  401. baz
  402. //%PDDM-DEFINE mumble(a_)
  403. //%a_: getName(a_)
  404. """
  405. expected_stripped = u"""
  406. //%PDDM-IMPORT-DEFINES ImportFile
  407. foo
  408. //%PDDM-EXPAND mumble(abc)
  409. //%PDDM-EXPAND-END mumble(abc)
  410. bar
  411. //%PDDM-EXPAND mumble(def)
  412. //%PDDM-EXPAND mumble(ghi)
  413. //%PDDM-EXPAND-END (2 expansions)
  414. baz
  415. //%PDDM-DEFINE mumble(a_)
  416. //%a_: getName(a_)
  417. """
  418. def _Resolver(name):
  419. self.assertEqual(name, 'ImportFile')
  420. return io.StringIO(input_str2)
  421. f = io.StringIO(input_str)
  422. sf = pddm.SourceFile(f, _Resolver)
  423. sf.ProcessContent()
  424. self.assertEqual(sf.processed_content, expected)
  425. # Feed it through and nothing should change.
  426. f2 = io.StringIO(sf.processed_content)
  427. sf2 = pddm.SourceFile(f2, _Resolver)
  428. sf2.ProcessContent()
  429. self.assertEqual(sf2.processed_content, expected)
  430. self.assertEqual(sf2.processed_content, sf.processed_content)
  431. # Test stripping (with the original input and expanded version).
  432. f2 = io.StringIO(input_str)
  433. sf2 = pddm.SourceFile(f2)
  434. sf2.ProcessContent(strip_expansion=True)
  435. self.assertEqual(sf2.processed_content, expected_stripped)
  436. f2 = io.StringIO(sf.processed_content)
  437. sf2 = pddm.SourceFile(f2, _Resolver)
  438. sf2.ProcessContent(strip_expansion=True)
  439. self.assertEqual(sf2.processed_content, expected_stripped)
  440. def testProcessFileWithMacroParseError(self):
  441. input_str = u"""
  442. foo
  443. //%PDDM-DEFINE mumble(a_)
  444. //%body
  445. //%PDDM-DEFINE mumble(x_)
  446. //%body2
  447. """
  448. f = io.StringIO(input_str)
  449. sf = pddm.SourceFile(f)
  450. try:
  451. sf.ProcessContent()
  452. self.fail('Should throw exception! Test failed to catch macro parsing error.')
  453. except pddm.PDDMError as e:
  454. self.assertEqual(e.message,
  455. 'Attempt to redefine macro: "PDDM-DEFINE mumble(x_)"\n'
  456. '...while parsing section that started:\n'
  457. ' Line 3: //%PDDM-DEFINE mumble(a_)')
  458. def testProcessFileWithExpandError(self):
  459. input_str = u"""
  460. foo
  461. //%PDDM-DEFINE mumble(a_)
  462. //%body
  463. //%PDDM-EXPAND foobar(x_)
  464. //%PDDM-EXPAND-END
  465. """
  466. f = io.StringIO(input_str)
  467. sf = pddm.SourceFile(f)
  468. try:
  469. sf.ProcessContent()
  470. self.fail('Should throw exception! Test failed to catch expand error.')
  471. except pddm.PDDMError as e:
  472. self.assertEqual(e.message,
  473. 'No macro named "foobar".\n'
  474. '...while expanding "foobar(x_)" from the section that'
  475. ' started:\n Line 5: //%PDDM-EXPAND foobar(x_)')
  476. if __name__ == '__main__':
  477. unittest.main()