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