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