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