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 35import io 36import unittest 37 38import pddm 39 40 41class 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( ) 50body""") 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""" 62PDDM-DEFINE noArgs( ) 63body1 64body2 65 66PDDM-DEFINE-END 67 68PDDM-DEFINE oneArg(foo) 69body3 70PDDM-DEFINE twoArgs( bar_ , baz ) 71body4 72body5""") 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""" 92PDDM-DEFINE another(a,b,c) 93body1 94body2""") 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 183class TestExpandingMacros(unittest.TestCase): 184 185 def testExpandBasics(self): 186 f = io.StringIO(u""" 187PDDM-DEFINE noArgs( ) 188body1 189body2 190 191PDDM-DEFINE-END 192 193PDDM-DEFINE oneArg(a) 194body3 a 195 196PDDM-DEFINE-END 197 198PDDM-DEFINE twoArgs(b,c) 199body4 b c 200body5 201PDDM-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""" 231PDDM-DEFINE bar(a) 232a-a$S-a$l-a$L-a$u-a$U 233PDDM-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""" 244PDDM-DEFINE foo(a, b) 245<a-z> 246PDDM-DEFINE baz(a) 247a - 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""" 277PDDM-DEFINE StartIt() 278foo(abc, def) 279foo(ghi, jkl) 280PDDM-DEFINE foo(a, b) 281bar(a, int) 282bar(b, NSString *) 283PDDM-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""" 305PDDM-DEFINE foo(a, b) 306bar(1, a) 307bar(2, b) 308PDDM-DEFINE bar(x, y) 309foo(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 320class 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 394class TestProcessingSource(unittest.TestCase): 395 396 def testBasics(self): 397 input_str = u""" 398//%PDDM-IMPORT-DEFINES ImportFile 399foo 400//%PDDM-EXPAND mumble(abc) 401//%PDDM-EXPAND-END 402bar 403//%PDDM-EXPAND mumble(def) 404//%PDDM-EXPAND mumble(ghi) 405//%PDDM-EXPAND-END 406baz 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 417foo 418//%PDDM-EXPAND mumble(abc) 419// This block of code is generated, do not edit it directly. 420 421abc: doAbc(int abc); 422//%PDDM-EXPAND-END mumble(abc) 423bar 424//%PDDM-EXPAND mumble(def) 425// This block of code is generated, do not edit it directly. 426 427def: doDef(int def); 428//%PDDM-EXPAND mumble(ghi) 429// This block of code is generated, do not edit it directly. 430 431ghi: doGhi(int ghi); 432//%PDDM-EXPAND-END (2 expansions) 433baz 434//%PDDM-DEFINE mumble(a_) 435//%a_: getName(a_) 436""" 437 expected_stripped = u""" 438//%PDDM-IMPORT-DEFINES ImportFile 439foo 440//%PDDM-EXPAND mumble(abc) 441//%PDDM-EXPAND-END mumble(abc) 442bar 443//%PDDM-EXPAND mumble(def) 444//%PDDM-EXPAND mumble(ghi) 445//%PDDM-EXPAND-END (2 expansions) 446baz 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""" 475foo 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""" 495foo 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 514if __name__ == '__main__': 515 unittest.main() 516