#! /usr/bin/env python3 # # Protocol Buffers - Google's data interchange format # Copyright 2015 Google Inc. All rights reserved. # https://developers.google.com/protocol-buffers/ # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following disclaimer # in the documentation and/or other materials provided with the # distribution. # * Neither the name of Google Inc. nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """Tests for pddm.py.""" import io import unittest from objectivec.DevTools import pddm class TestParsingMacros(unittest.TestCase): def testParseEmpty(self): f = io.StringIO('') result = pddm.MacroCollection(f) self.assertEqual(len(result._macros), 0) def testParseOne(self): f = io.StringIO("""PDDM-DEFINE foo( ) body""") result = pddm.MacroCollection(f) self.assertEqual(len(result._macros), 1) macro = result._macros.get('foo') self.assertIsNotNone(macro) self.assertEqual(macro.name, 'foo') self.assertEqual(macro.args, tuple()) self.assertEqual(macro.body, 'body') def testParseGeneral(self): # Tests multiple defines, spaces in all places, etc. f = io.StringIO(""" PDDM-DEFINE noArgs( ) body1 body2 PDDM-DEFINE-END PDDM-DEFINE oneArg(foo) body3 PDDM-DEFINE twoArgs( bar_ , baz ) body4 body5""") result = pddm.MacroCollection(f) self.assertEqual(len(result._macros), 3) macro = result._macros.get('noArgs') self.assertIsNotNone(macro) self.assertEqual(macro.name, 'noArgs') self.assertEqual(macro.args, tuple()) self.assertEqual(macro.body, 'body1\nbody2\n') macro = result._macros.get('oneArg') self.assertIsNotNone(macro) self.assertEqual(macro.name, 'oneArg') self.assertEqual(macro.args, ('foo',)) self.assertEqual(macro.body, 'body3') macro = result._macros.get('twoArgs') self.assertIsNotNone(macro) self.assertEqual(macro.name, 'twoArgs') self.assertEqual(macro.args, ('bar_', 'baz')) self.assertEqual(macro.body, 'body4\nbody5') # Add into existing collection f = io.StringIO(""" PDDM-DEFINE another(a,b,c) body1 body2""") result.ParseInput(f) self.assertEqual(len(result._macros), 4) macro = result._macros.get('another') self.assertIsNotNone(macro) self.assertEqual(macro.name, 'another') self.assertEqual(macro.args, ('a', 'b', 'c')) self.assertEqual(macro.body, 'body1\nbody2') def testParseDirectiveIssues(self): test_list = [ # Unknown directive ('PDDM-DEFINE foo()\nbody\nPDDM-DEFINED foo\nbaz', 'Hit a line with an unknown directive: '), # End without begin ('PDDM-DEFINE foo()\nbody\nPDDM-DEFINE-END\nPDDM-DEFINE-END\n', 'Got DEFINE-END directive without an active macro: '), # Line not in macro block ('PDDM-DEFINE foo()\nbody\nPDDM-DEFINE-END\nmumble\n', 'Hit a line that wasn\'t a directive and no open macro definition: '), # Redefine macro ('PDDM-DEFINE foo()\nbody\nPDDM-DEFINE foo(a)\nmumble\n', 'Attempt to redefine macro: '), ] for idx, (input_str, expected_prefix) in enumerate(test_list, 1): f = io.StringIO(input_str) try: result = pddm.MacroCollection(f) self.fail('Should throw exception, entry %d' % idx) except pddm.PDDMError as e: self.assertTrue(e.message.startswith(expected_prefix), 'Entry %d failed: %r' % (idx, e)) def testParseBeginIssues(self): test_list = [ # 1. No name ('PDDM-DEFINE\nmumble', 'Failed to parse macro definition: '), # 2. No name (with spaces) ('PDDM-DEFINE \nmumble', 'Failed to parse macro definition: '), # 3. No open paren ('PDDM-DEFINE foo\nmumble', 'Failed to parse macro definition: '), # 4. No close paren ('PDDM-DEFINE foo(\nmumble', 'Failed to parse macro definition: '), # 5. No close paren (with args) ('PDDM-DEFINE foo(a, b\nmumble', 'Failed to parse macro definition: '), # 6. No name before args ('PDDM-DEFINE (a, b)\nmumble', 'Failed to parse macro definition: '), # 7. No name before args ('PDDM-DEFINE foo bar(a, b)\nmumble', 'Failed to parse macro definition: '), # 8. Empty arg name ('PDDM-DEFINE foo(a, ,b)\nmumble', 'Empty arg name in macro definition: '), ('PDDM-DEFINE foo(a,,b)\nmumble', 'Empty arg name in macro definition: '), # 10. Duplicate name ('PDDM-DEFINE foo(a,b,a,c)\nmumble', 'Arg name "a" used more than once in macro definition: '), # 11. Invalid arg name ('PDDM-DEFINE foo(a b,c)\nmumble', 'Invalid arg name "a b" in macro definition: '), ('PDDM-DEFINE foo(a.b,c)\nmumble', 'Invalid arg name "a.b" in macro definition: '), ('PDDM-DEFINE foo(a-b,c)\nmumble', 'Invalid arg name "a-b" in macro definition: '), ('PDDM-DEFINE foo(a,b,c.)\nmumble', 'Invalid arg name "c." in macro definition: '), # 15. Extra stuff after the name ('PDDM-DEFINE foo(a,c) foo\nmumble', 'Failed to parse macro definition: '), ('PDDM-DEFINE foo(a,c) foo)\nmumble', 'Failed to parse macro definition: '), ] for idx, (input_str, expected_prefix) in enumerate(test_list, 1): f = io.StringIO(input_str) try: result = pddm.MacroCollection(f) self.fail('Should throw exception, entry %d' % idx) except pddm.PDDMError as e: self.assertTrue(e.message.startswith(expected_prefix), 'Entry %d failed: %r' % (idx, e)) class TestExpandingMacros(unittest.TestCase): def testExpandBasics(self): f = io.StringIO(""" PDDM-DEFINE noArgs( ) body1 body2 PDDM-DEFINE-END PDDM-DEFINE oneArg(a) body3 a PDDM-DEFINE-END PDDM-DEFINE twoArgs(b,c) body4 b c body5 PDDM-DEFINE-END """) mc = pddm.MacroCollection(f) test_list = [ ('noArgs()', 'body1\nbody2\n'), ('oneArg(wee)', 'body3 wee\n'), ('twoArgs(having some, fun)', 'body4 having some fun\nbody5'), # One arg, pass empty. ('oneArg()', 'body3 \n'), # Two args, gets empty in each slot. ('twoArgs(, empty)', 'body4 empty\nbody5'), ('twoArgs(empty, )', 'body4 empty \nbody5'), ('twoArgs(, )', 'body4 \nbody5'), ] for idx, (input_str, expected) in enumerate(test_list, 1): result = mc.Expand(input_str) self.assertEqual(result, expected, 'Entry %d --\n Result: %r\n Expected: %r' % (idx, result, expected)) def testExpandArgOptions(self): f = io.StringIO(""" PDDM-DEFINE bar(a) a-a$S-a$l-a$L-a$u-a$U PDDM-DEFINE-END """) mc = pddm.MacroCollection(f) self.assertEqual(mc.Expand('bar(xYz)'), 'xYz- -xYz-xyz-XYz-XYZ') self.assertEqual(mc.Expand('bar(MnoP)'), 'MnoP- -mnoP-mnop-MnoP-MNOP') # Test empty self.assertEqual(mc.Expand('bar()'), '-----') def testExpandSimpleMacroErrors(self): f = io.StringIO(""" PDDM-DEFINE foo(a, b) PDDM-DEFINE baz(a) a - a$z """) mc = pddm.MacroCollection(f) test_list = [ # 1. Unknown macro ('bar()', 'No macro named "bar".'), ('bar(a)', 'No macro named "bar".'), # 3. Arg mismatch ('foo()', 'Expected 2 args, got: "foo()".'), ('foo(a b)', 'Expected 2 args, got: "foo(a b)".'), ('foo(a,b,c)', 'Expected 2 args, got: "foo(a,b,c)".'), # 6. Unknown option in expansion ('baz(mumble)', 'Unknown arg option "a$z" while expanding "baz(mumble)".'), ] for idx, (input_str, expected_err) in enumerate(test_list, 1): try: result = mc.Expand(input_str) self.fail('Should throw exception, entry %d' % idx) except pddm.PDDMError as e: self.assertEqual(e.message, expected_err, 'Entry %d failed: %r' % (idx, e)) def testExpandReferences(self): f = io.StringIO(""" PDDM-DEFINE StartIt() foo(abc, def) foo(ghi, jkl) PDDM-DEFINE foo(a, b) bar(a, int) bar(b, NSString *) PDDM-DEFINE bar(n, t) - (t)n; - (void)set##n$u##:(t)value; """) mc = pddm.MacroCollection(f) expected = """- (int)abc; - (void)setAbc:(int)value; - (NSString *)def; - (void)setDef:(NSString *)value; - (int)ghi; - (void)setGhi:(int)value; - (NSString *)jkl; - (void)setJkl:(NSString *)value; """ self.assertEqual(mc.Expand('StartIt()'), expected) def testCatchRecursion(self): f = io.StringIO(""" PDDM-DEFINE foo(a, b) bar(1, a) bar(2, b) PDDM-DEFINE bar(x, y) foo(x, y) """) mc = pddm.MacroCollection(f) try: result = mc.Expand('foo(A,B)') self.fail('Should throw exception! Test failed to catch recursion.') except pddm.PDDMError as e: self.assertEqual(e.message, 'Found macro recursion, invoking "foo(1, A)":\n...while expanding "bar(1, A)".\n...while expanding "foo(A,B)".') class TestParsingSource(unittest.TestCase): def testBasicParse(self): test_list = [ # 1. no directives ('a\nb\nc', (3,) ), # 2. One define ('a\n//%PDDM-DEFINE foo()\n//%body\nc', (1, 2, 1) ), # 3. Two defines ('a\n//%PDDM-DEFINE foo()\n//%body\n//%PDDM-DEFINE bar()\n//%body2\nc', (1, 4, 1) ), # 4. Two defines with ends ('a\n//%PDDM-DEFINE foo()\n//%body\n//%PDDM-DEFINE-END\n' '//%PDDM-DEFINE bar()\n//%body2\n//%PDDM-DEFINE-END\nc', (1, 6, 1) ), # 5. One expand, one define (that runs to end of file) ('a\n//%PDDM-EXPAND foo()\nbody\n//%PDDM-EXPAND-END\n' '//%PDDM-DEFINE bar()\n//%body2\n', (1, 1, 2) ), # 6. One define ended with an expand. ('a\nb\n//%PDDM-DEFINE bar()\n//%body2\n' '//%PDDM-EXPAND bar()\nbody2\n//%PDDM-EXPAND-END\n', (2, 2, 1) ), # 7. Two expands (one end), one define. ('a\n//%PDDM-EXPAND foo(1)\nbody\n//%PDDM-EXPAND foo(2)\nbody2\n//%PDDM-EXPAND-END\n' '//%PDDM-DEFINE foo()\n//%body2\n', (1, 2, 2) ), ] for idx, (input_str, line_counts) in enumerate(test_list, 1): f = io.StringIO(input_str) sf = pddm.SourceFile(f) sf._ParseFile() self.assertEqual(len(sf._sections), len(line_counts), 'Entry %d -- %d != %d' % (idx, len(sf._sections), len(line_counts))) for idx2, (sec, expected) in enumerate(zip(sf._sections, line_counts), 1): self.assertEqual(sec.num_lines_captured, expected, 'Entry %d, section %d -- %d != %d' % (idx, idx2, sec.num_lines_captured, expected)) def testErrors(self): test_list = [ # 1. Directive within expansion ('//%PDDM-EXPAND a()\n//%PDDM-BOGUS', 'Ran into directive ("//%PDDM-BOGUS", line 2) while in "//%PDDM-EXPAND a()".'), ('//%PDDM-EXPAND a()\n//%PDDM-DEFINE a()\n//%body\n', 'Ran into directive ("//%PDDM-DEFINE", line 2) while in "//%PDDM-EXPAND a()".'), # 3. Expansion ran off end of file ('//%PDDM-EXPAND a()\na\nb\n', 'Hit the end of the file while in "//%PDDM-EXPAND a()".'), # 4. Directive within define ('//%PDDM-DEFINE a()\n//%body\n//%PDDM-BOGUS', 'Ran into directive ("//%PDDM-BOGUS", line 3) while in "//%PDDM-DEFINE a()".'), ('//%PDDM-DEFINE a()\n//%body\n//%PDDM-EXPAND-END a()', 'Ran into directive ("//%PDDM-EXPAND-END", line 3) while in "//%PDDM-DEFINE a()".'), # 6. Directives that shouldn't start sections ('a\n//%PDDM-DEFINE-END a()\n//a\n', 'Unexpected line 2: "//%PDDM-DEFINE-END a()".'), ('a\n//%PDDM-EXPAND-END a()\n//a\n', 'Unexpected line 2: "//%PDDM-EXPAND-END a()".'), ('//%PDDM-BOGUS\n//a\n', 'Unexpected line 1: "//%PDDM-BOGUS".'), ] for idx, (input_str, expected_err) in enumerate(test_list, 1): f = io.StringIO(input_str) try: pddm.SourceFile(f)._ParseFile() self.fail('Should throw exception, entry %d' % idx) except pddm.PDDMError as e: self.assertEqual(e.message, expected_err, 'Entry %d failed: %r' % (idx, e)) class TestProcessingSource(unittest.TestCase): def testBasics(self): self.maxDiff = None input_str = """ //%PDDM-IMPORT-DEFINES ImportFile foo //%PDDM-EXPAND mumble(abc) //%PDDM-EXPAND-END bar //%PDDM-EXPAND mumble(def) //%PDDM-EXPAND mumble(ghi) //%PDDM-EXPAND-END baz //%PDDM-DEFINE mumble(a_) //%a_: getName(a_) """ input_str2 = """ //%PDDM-DEFINE getName(x_) //%do##x_$u##(int x_); """ expected = """ //%PDDM-IMPORT-DEFINES ImportFile foo //%PDDM-EXPAND mumble(abc) // This block of code is generated, do not edit it directly. abc: doAbc(int abc); //%PDDM-EXPAND-END mumble(abc) bar //%PDDM-EXPAND mumble(def) // This block of code is generated, do not edit it directly. def: doDef(int def); //%PDDM-EXPAND mumble(ghi) // This block of code is generated, do not edit it directly. ghi: doGhi(int ghi); //%PDDM-EXPAND-END (2 expansions) baz //%PDDM-DEFINE mumble(a_) //%a_: getName(a_) """ expected_stripped = """ //%PDDM-IMPORT-DEFINES ImportFile foo //%PDDM-EXPAND mumble(abc) //%PDDM-EXPAND-END mumble(abc) bar //%PDDM-EXPAND mumble(def) //%PDDM-EXPAND mumble(ghi) //%PDDM-EXPAND-END (2 expansions) baz //%PDDM-DEFINE mumble(a_) //%a_: getName(a_) """ def _Resolver(name): self.assertEqual(name, 'ImportFile') return io.StringIO(input_str2) f = io.StringIO(input_str) sf = pddm.SourceFile(f, _Resolver) sf.ProcessContent() self.assertEqual(sf.processed_content, expected) # Feed it through and nothing should change. f2 = io.StringIO(sf.processed_content) sf2 = pddm.SourceFile(f2, _Resolver) sf2.ProcessContent() self.assertEqual(sf2.processed_content, expected) self.assertEqual(sf2.processed_content, sf.processed_content) # Test stripping (with the original input and expanded version). f2 = io.StringIO(input_str) sf2 = pddm.SourceFile(f2) sf2.ProcessContent(strip_expansion=True) self.assertEqual(sf2.processed_content, expected_stripped) f2 = io.StringIO(sf.processed_content) sf2 = pddm.SourceFile(f2, _Resolver) sf2.ProcessContent(strip_expansion=True) self.assertEqual(sf2.processed_content, expected_stripped) def testProcessFileWithMacroParseError(self): input_str = """ foo //%PDDM-DEFINE mumble(a_) //%body //%PDDM-DEFINE mumble(x_) //%body2 """ f = io.StringIO(input_str) sf = pddm.SourceFile(f) try: sf.ProcessContent() self.fail('Should throw exception! Test failed to catch macro parsing error.') except pddm.PDDMError as e: self.assertEqual(e.message, 'Attempt to redefine macro: "PDDM-DEFINE mumble(x_)"\n' '...while parsing section that started:\n' ' Line 3: //%PDDM-DEFINE mumble(a_)') def testProcessFileWithExpandError(self): input_str = """ foo //%PDDM-DEFINE mumble(a_) //%body //%PDDM-EXPAND foobar(x_) //%PDDM-EXPAND-END """ f = io.StringIO(input_str) sf = pddm.SourceFile(f) try: sf.ProcessContent() self.fail('Should throw exception! Test failed to catch expand error.') except pddm.PDDMError as e: self.assertEqual(e.message, 'No macro named "foobar".\n' '...while expanding "foobar(x_)" from the section that' ' started:\n Line 5: //%PDDM-EXPAND foobar(x_)') if __name__ == '__main__': unittest.main()