lldbtest.py revision 4f93bf1b34e9828ff630eddb908adfb04e05aadf
1""" 2LLDB module which provides the abstract base class of lldb test case. 3 4The concrete subclass can override lldbtest.TesBase in order to inherit the 5common behavior for unitest.TestCase.setUp/tearDown implemented in this file. 6 7The subclass should override the attribute mydir in order for the python runtime 8to locate the individual test cases when running as part of a large test suite 9or when running each test case as a separate python invocation. 10 11./dotest.py provides a test driver which sets up the environment to run the 12entire test suite. Users who want to run a test case on its own can specify the 13LLDB_TEST and PYTHONPATH environment variables, for example: 14 15$ export LLDB_TEST=$PWD 16$ export PYTHONPATH=/Volumes/data/lldb/svn/trunk/build/Debug/LLDB.framework/Resources/Python:$LLDB_TEST:$LLDB_TEST/plugins 17$ echo $LLDB_TEST 18/Volumes/data/lldb/svn/trunk/test 19$ echo $PYTHONPATH 20/Volumes/data/lldb/svn/trunk/build/Debug/LLDB.framework/Resources/Python:/Volumes/data/lldb/svn/trunk/test:/Volumes/data/lldb/svn/trunk/test/plugins 21$ python function_types/TestFunctionTypes.py 22. 23---------------------------------------------------------------------- 24Ran 1 test in 0.363s 25 26OK 27$ LLDB_COMMAND_TRACE=YES python array_types/TestArrayTypes.py 28 29... 30 31runCmd: breakpoint set -f main.c -l 42 32output: Breakpoint created: 1: file ='main.c', line = 42, locations = 1 33 34runCmd: run 35output: Launching '/Volumes/data/lldb/svn/trunk/test/array_types/a.out' (x86_64) 36 37... 38 39runCmd: frame variable strings 40output: (char *[4]) strings = { 41 (char *) strings[0] = 0x0000000100000f0c "Hello", 42 (char *) strings[1] = 0x0000000100000f12 "Hola", 43 (char *) strings[2] = 0x0000000100000f17 "Bonjour", 44 (char *) strings[3] = 0x0000000100000f1f "Guten Tag" 45} 46 47runCmd: frame variable char_16 48output: (char [16]) char_16 = { 49 (char) char_16[0] = 'H', 50 (char) char_16[1] = 'e', 51 (char) char_16[2] = 'l', 52 (char) char_16[3] = 'l', 53 (char) char_16[4] = 'o', 54 (char) char_16[5] = ' ', 55 (char) char_16[6] = 'W', 56 (char) char_16[7] = 'o', 57 (char) char_16[8] = 'r', 58 (char) char_16[9] = 'l', 59 (char) char_16[10] = 'd', 60 (char) char_16[11] = '\n', 61 (char) char_16[12] = '\0', 62 (char) char_16[13] = '\0', 63 (char) char_16[14] = '\0', 64 (char) char_16[15] = '\0' 65} 66 67runCmd: frame variable ushort_matrix 68output: (unsigned short [2][3]) ushort_matrix = { 69 (unsigned short [3]) ushort_matrix[0] = { 70 (unsigned short) ushort_matrix[0][0] = 0x0001, 71 (unsigned short) ushort_matrix[0][1] = 0x0002, 72 (unsigned short) ushort_matrix[0][2] = 0x0003 73 }, 74 (unsigned short [3]) ushort_matrix[1] = { 75 (unsigned short) ushort_matrix[1][0] = 0x000b, 76 (unsigned short) ushort_matrix[1][1] = 0x0016, 77 (unsigned short) ushort_matrix[1][2] = 0x0021 78 } 79} 80 81runCmd: frame variable long_6 82output: (long [6]) long_6 = { 83 (long) long_6[0] = 1, 84 (long) long_6[1] = 2, 85 (long) long_6[2] = 3, 86 (long) long_6[3] = 4, 87 (long) long_6[4] = 5, 88 (long) long_6[5] = 6 89} 90 91. 92---------------------------------------------------------------------- 93Ran 1 test in 0.349s 94 95OK 96$ 97""" 98 99import os, sys, traceback 100import re 101from subprocess import * 102import StringIO 103import time 104import types 105import unittest2 106import lldb 107 108# See also dotest.parseOptionsAndInitTestdirs(), where the environment variables 109# LLDB_COMMAND_TRACE and LLDB_NO_CLEANUP are set from '-t' and '-r dir' options. 110 111# By default, traceAlways is False. 112if "LLDB_COMMAND_TRACE" in os.environ and os.environ["LLDB_COMMAND_TRACE"]=="YES": 113 traceAlways = True 114else: 115 traceAlways = False 116 117# By default, doCleanup is True. 118if "LLDB_DO_CLEANUP" in os.environ and os.environ["LLDB_DO_CLEANUP"]=="NO": 119 doCleanup = False 120else: 121 doCleanup = True 122 123 124# 125# Some commonly used assert messages. 126# 127 128COMMAND_FAILED_AS_EXPECTED = "Command has failed as expected" 129 130CURRENT_EXECUTABLE_SET = "Current executable set successfully" 131 132PROCESS_IS_VALID = "Process is valid" 133 134PROCESS_KILLED = "Process is killed successfully" 135 136RUN_SUCCEEDED = "Process is launched successfully" 137 138RUN_COMPLETED = "Process exited successfully" 139 140BACKTRACE_DISPLAYED_CORRECTLY = "Backtrace displayed correctly" 141 142BREAKPOINT_CREATED = "Breakpoint created successfully" 143 144BREAKPOINT_STATE_CORRECT = "Breakpoint state is correct" 145 146BREAKPOINT_PENDING_CREATED = "Pending breakpoint created successfully" 147 148BREAKPOINT_HIT_ONCE = "Breakpoint resolved with hit cout = 1" 149 150BREAKPOINT_HIT_TWICE = "Breakpoint resolved with hit cout = 2" 151 152BREAKPOINT_HIT_THRICE = "Breakpoint resolved with hit cout = 3" 153 154SOURCE_DISPLAYED_CORRECTLY = "Source code displayed correctly" 155 156STEP_OUT_SUCCEEDED = "Thread step-out succeeded" 157 158PROCESS_STOPPED = "Process status should be stopped" 159 160STOPPED_DUE_TO_BREAKPOINT = "Process should be stopped due to breakpoint" 161 162STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS = "%s, %s" % ( 163 STOPPED_DUE_TO_BREAKPOINT, "instead, the actual stop reason is: '%s'") 164 165STOPPED_DUE_TO_BREAKPOINT_CONDITION = "Stopped due to breakpoint condition" 166 167STOPPED_DUE_TO_SIGNAL = "Process state is stopped due to signal" 168 169STOPPED_DUE_TO_STEP_IN = "Process state is stopped due to step in" 170 171DATA_TYPES_DISPLAYED_CORRECTLY = "Data type(s) displayed correctly" 172 173VALID_BREAKPOINT = "Got a valid breakpoint" 174 175VALID_BREAKPOINT_LOCATION = "Got a valid breakpoint location" 176 177VALID_FILESPEC = "Got a valid filespec" 178 179VALID_MODULE = "Got a valid module" 180 181VALID_PROCESS = "Got a valid process" 182 183VALID_SYMBOL = "Got a valid symbol" 184 185VALID_TARGET = "Got a valid target" 186 187VARIABLES_DISPLAYED_CORRECTLY = "Variable(s) displayed correctly" 188 189 190# 191# And a generic "Command '%s' returns successfully" message generator. 192# 193def CMD_MSG(str): 194 return "Command '%s' returns successfully" % str 195 196# 197# And a generic "'%s' returns expected result" message generator if exe. 198# Otherwise, it's "'%s' matches expected result" 199# 200def EXP_MSG(str, exe): 201 return "'%s' %s expected result" % (str, 'returns' if exe else 'matches') 202 203# 204# And a generic "Value of setting '%s' is correct" message generator. 205# 206def SETTING_MSG(setting): 207 return "Value of setting '%s' is correct" % setting 208 209# 210# Returns an env variable array from the os.environ map object. 211# 212def EnvArray(): 213 return map(lambda k,v: k+"="+v, os.environ.keys(), os.environ.values()) 214 215def line_number(filename, string_to_match): 216 """Helper function to return the line number of the first matched string.""" 217 with open(filename, 'r') as f: 218 for i, line in enumerate(f): 219 if line.find(string_to_match) != -1: 220 # Found our match. 221 return i+1 222 raise Exception("Unable to find %s within file %s" % (string_to_match, filename)) 223 224def pointer_size(): 225 """Return the pointer size of the host system.""" 226 import ctypes 227 a_pointer = ctypes.c_void_p(0xffff) 228 return 8 * ctypes.sizeof(a_pointer) 229 230from functools import wraps 231def python_api_test(func): 232 """Decorate the item as a Python API only test.""" 233 @wraps(func) 234 def wrapper(self, *args, **kwargs): 235 if lldb.dont_do_python_api_test: 236 self.skipTest("Skip Python API tests") 237 return func(self, *args, **kwargs) 238 239 return wrapper 240 241class recording(StringIO.StringIO): 242 """ 243 A nice little context manager for recording the debugger interactions into 244 our session object. If trace flag is ON, it also emits the interactions 245 into the stderr. 246 """ 247 def __init__(self, test, trace): 248 """Create a StringIO instance; record the session obj and trace flag.""" 249 StringIO.StringIO.__init__(self) 250 self.session = test.session if test else None 251 self.trace = trace 252 253 def __enter__(self): 254 """ 255 Context management protocol on entry to the body of the with statement. 256 Just return the StringIO object. 257 """ 258 return self 259 260 def __exit__(self, type, value, tb): 261 """ 262 Context management protocol on exit from the body of the with statement. 263 If trace is ON, it emits the recordings into stderr. Always add the 264 recordings to our session object. And close the StringIO object, too. 265 """ 266 if self.trace: 267 print >> sys.stderr, self.getvalue() 268 if self.session: 269 print >> self.session, self.getvalue() 270 self.close() 271 272# From 2.7's subprocess.check_output() convenience function. 273def system(*popenargs, **kwargs): 274 r"""Run command with arguments and return its output as a byte string. 275 276 If the exit code was non-zero it raises a CalledProcessError. The 277 CalledProcessError object will have the return code in the returncode 278 attribute and output in the output attribute. 279 280 The arguments are the same as for the Popen constructor. Example: 281 282 >>> check_output(["ls", "-l", "/dev/null"]) 283 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' 284 285 The stdout argument is not allowed as it is used internally. 286 To capture standard error in the result, use stderr=STDOUT. 287 288 >>> check_output(["/bin/sh", "-c", 289 ... "ls -l non_existent_file ; exit 0"], 290 ... stderr=STDOUT) 291 'ls: non_existent_file: No such file or directory\n' 292 """ 293 294 # Assign the sender object to variable 'test' and remove it from kwargs. 295 test = kwargs.pop('sender', None) 296 297 if 'stdout' in kwargs: 298 raise ValueError('stdout argument not allowed, it will be overridden.') 299 process = Popen(stdout=PIPE, *popenargs, **kwargs) 300 output, error = process.communicate() 301 retcode = process.poll() 302 303 with recording(test, traceAlways) as sbuf: 304 if isinstance(popenargs, types.StringTypes): 305 args = [popenargs] 306 else: 307 args = list(popenargs) 308 print >> sbuf 309 print >> sbuf, "os command:", args 310 print >> sbuf, "stdout:", output 311 print >> sbuf, "stderr:", error 312 print >> sbuf, "retcode:", retcode 313 print >> sbuf 314 315 if retcode: 316 cmd = kwargs.get("args") 317 if cmd is None: 318 cmd = popenargs[0] 319 raise CalledProcessError(retcode, cmd) 320 return output 321 322def getsource_if_available(obj): 323 """ 324 Return the text of the source code for an object if available. Otherwise, 325 a print representation is returned. 326 """ 327 import inspect 328 try: 329 return inspect.getsource(obj) 330 except: 331 return repr(obj) 332 333class TestBase(unittest2.TestCase): 334 """ 335 This abstract base class is meant to be subclassed. It provides default 336 implementations for setUpClass(), tearDownClass(), setUp(), and tearDown(), 337 among other things. 338 339 Important things for test class writers: 340 341 - Overwrite the mydir class attribute, otherwise your test class won't 342 run. It specifies the relative directory to the top level 'test' so 343 the test harness can change to the correct working directory before 344 running your test. 345 346 - The setUp method sets up things to facilitate subsequent interactions 347 with the debugger as part of the test. These include: 348 - create/get a debugger set with synchronous mode (self.dbg) 349 - get the command interpreter from with the debugger (self.ci) 350 - create a result object for use with the command interpreter 351 (self.result) 352 - plus other stuffs 353 354 - The tearDown method tries to perform some necessary cleanup on behalf 355 of the test to return the debugger to a good state for the next test. 356 These include: 357 - execute any tearDown hooks registered by the test method with 358 TestBase.addTearDownHook(); examples can be found in 359 settings/TestSettings.py 360 - kill the inferior process launched during the test method 361 - if by 'run' or 'process launch' command, 'process kill' 362 command is used 363 - if the test method uses LLDB Python API to launch process, 364 it should assign the process object to self.process; that 365 way, tearDown will use self.process.Kill() on the object 366 - perform build cleanup before running the next test method in the 367 same test class; examples of registering for this service can be 368 found in types/TestIntegerTypes.py with the call: 369 - self.setTearDownCleanup(dictionary=d) 370 371 - Similarly setUpClass and tearDownClass perform classwise setup and 372 teardown fixtures. The tearDownClass method invokes a default build 373 cleanup for the entire test class; also, subclasses can implement the 374 classmethod classCleanup(cls) to perform special class cleanup action. 375 376 - The instance methods runCmd and expect are used heavily by existing 377 test cases to send a command to the command interpreter and to perform 378 string/pattern matching on the output of such command execution. The 379 expect method also provides a mode to peform string/pattern matching 380 without running a command. 381 382 - The build methods buildDefault, buildDsym, and buildDwarf are used to 383 build the binaries used during a particular test scenario. A plugin 384 should be provided for the sys.platform running the test suite. The 385 Mac OS X implementation is located in plugins/darwin.py. 386 387 """ 388 389 @classmethod 390 def skipLongRunningTest(cls): 391 """ 392 By default, we skip long running test case. 393 This can be overridden by passing '-l' to the test driver (dotest.py). 394 """ 395 if "LLDB_SKIP_LONG_RUNNING_TEST" in os.environ and "NO" == os.environ["LLDB_SKIP_LONG_RUNNING_TEST"]: 396 return False 397 else: 398 return True 399 400 # The concrete subclass should override this attribute. 401 mydir = None 402 403 # State pertaining to the inferior process, if any. 404 # This reflects inferior process started through the command interface with 405 # either the lldb "run" or "process launch" command. 406 # See also self.runCmd(). 407 runStarted = False 408 409 # Maximum allowed attempts when launching the inferior process. 410 # Can be overridden by the LLDB_MAX_LAUNCH_COUNT environment variable. 411 maxLaunchCount = 3; 412 413 # Time to wait before the next launching attempt in second(s). 414 # Can be overridden by the LLDB_TIME_WAIT_NEXT_LAUNCH environment variable. 415 timeWaitNextLaunch = 1.0; 416 417 # Keep track of the old current working directory. 418 oldcwd = None 419 420 @classmethod 421 def setUpClass(cls): 422 """ 423 Python unittest framework class setup fixture. 424 Do current directory manipulation. 425 """ 426 427 # Fail fast if 'mydir' attribute is not overridden. 428 if not cls.mydir or len(cls.mydir) == 0: 429 raise Exception("Subclasses must override the 'mydir' attribute.") 430 # Save old working directory. 431 cls.oldcwd = os.getcwd() 432 433 # Change current working directory if ${LLDB_TEST} is defined. 434 # See also dotest.py which sets up ${LLDB_TEST}. 435 if ("LLDB_TEST" in os.environ): 436 if traceAlways: 437 print >> sys.stderr, "Change dir to:", os.path.join(os.environ["LLDB_TEST"], cls.mydir) 438 os.chdir(os.path.join(os.environ["LLDB_TEST"], cls.mydir)) 439 440 @classmethod 441 def tearDownClass(cls): 442 """ 443 Python unittest framework class teardown fixture. 444 Do class-wide cleanup. 445 """ 446 447 if doCleanup: 448 # First, let's do the platform-specific cleanup. 449 module = __import__(sys.platform) 450 if not module.cleanup(): 451 raise Exception("Don't know how to do cleanup") 452 453 # Subclass might have specific cleanup function defined. 454 if getattr(cls, "classCleanup", None): 455 if traceAlways: 456 print >> sys.stderr, "Call class-specific cleanup function for class:", cls 457 try: 458 cls.classCleanup() 459 except: 460 exc_type, exc_value, exc_tb = sys.exc_info() 461 traceback.print_exception(exc_type, exc_value, exc_tb) 462 463 # Restore old working directory. 464 if traceAlways: 465 print >> sys.stderr, "Restore dir to:", cls.oldcwd 466 os.chdir(cls.oldcwd) 467 468 def setUp(self): 469 #import traceback 470 #traceback.print_stack() 471 472 if lldb.blacklist: 473 className = self.__class__.__name__ 474 classAndMethodName = "%s.%s" % (className, self._testMethodName) 475 if className in lldb.blacklist: 476 self.skipTest(lldb.blacklist.get(className)) 477 elif classAndMethodName in lldb.blacklist: 478 self.skipTest(lldb.blacklist.get(classAndMethodName)) 479 480 if ("LLDB_WAIT_BETWEEN_TEST_CASES" in os.environ and 481 os.environ["LLDB_WAIT_BETWEEN_TEST_CASES"] == 'YES'): 482 waitTime = 1.0 483 if "LLDB_TIME_WAIT_BETWEEN_TEST_CASES" in os.environ: 484 waitTime = float(os.environ["LLDB_TIME_WAIT_BETWEEN_TEST_CASES"]) 485 time.sleep(waitTime) 486 487 if "LLDB_MAX_LAUNCH_COUNT" in os.environ: 488 self.maxLaunchCount = int(os.environ["LLDB_MAX_LAUNCH_COUNT"]) 489 490 if "LLDB_TIME_WAIT_NEXT_LAUNCH" in os.environ: 491 self.timeWaitNextLaunch = float(os.environ["LLDB_TIME_WAIT_NEXT_LAUNCH"]) 492 493 # Create the debugger instance if necessary. 494 try: 495 self.dbg = lldb.DBG 496 except AttributeError: 497 self.dbg = lldb.SBDebugger.Create() 498 499 if not self.dbg.IsValid(): 500 raise Exception('Invalid debugger instance') 501 502 # We want our debugger to be synchronous. 503 self.dbg.SetAsync(False) 504 505 # There is no process associated with the debugger as yet. 506 # See also self.tearDown() where it checks whether self.process has a 507 # valid reference and calls self.process.Kill() to kill the process. 508 self.process = None 509 510 # Retrieve the associated command interpreter instance. 511 self.ci = self.dbg.GetCommandInterpreter() 512 if not self.ci: 513 raise Exception('Could not get the command interpreter') 514 515 # And the result object. 516 self.res = lldb.SBCommandReturnObject() 517 518 # These are for customized teardown cleanup. 519 self.dict = None 520 self.doTearDownCleanup = False 521 522 # Create a string buffer to record the session info, to be dumped into a 523 # test case specific file if test failure is encountered. 524 self.session = StringIO.StringIO() 525 526 # Optimistically set __errored__, __failed__, __expected__ to False 527 # initially. If the test errored/failed, the session info 528 # (self.session) is then dumped into a session specific file for 529 # diagnosis. 530 self.__errored__ = False 531 self.__failed__ = False 532 self.__expected__ = False 533 534 # See addTearDownHook(self, hook) which allows the client to add a hook 535 # function to be run during tearDown() time. 536 self.hooks = [] 537 538 def markError(self): 539 """Callback invoked when an error (unexpected exception) errored.""" 540 self.__errored__ = True 541 with recording(self, False) as sbuf: 542 # False because there's no need to write "ERROR" to the stderr twice. 543 # Once by the Python unittest framework, and a second time by us. 544 print >> sbuf, "ERROR" 545 546 def markFailure(self): 547 """Callback invoked when a failure (test assertion failure) occurred.""" 548 self.__failed__ = True 549 with recording(self, False) as sbuf: 550 # False because there's no need to write "FAIL" to the stderr twice. 551 # Once by the Python unittest framework, and a second time by us. 552 print >> sbuf, "FAIL" 553 554 def markExpectedFailure(self): 555 """Callback invoked when an expected failure/error occurred.""" 556 self.__expected__ = True 557 with recording(self, False) as sbuf: 558 # False because there's no need to write "expected failure" to the 559 # stderr twice. 560 # Once by the Python unittest framework, and a second time by us. 561 print >> sbuf, "expected failure" 562 563 def dumpSessionInfo(self): 564 """ 565 Dump the debugger interactions leading to a test error/failure. This 566 allows for more convenient postmortem analysis. 567 568 See also LLDBTestResult (dotest.py) which is a singlton class derived 569 from TextTestResult and overwrites addError, addFailure, and 570 addExpectedFailure methods to allow us to to mark the test instance as 571 such. 572 """ 573 574 # We are here because self.tearDown() detected that this test instance 575 # either errored or failed. The lldb.test_result singleton contains 576 # two lists (erros and failures) which get populated by the unittest 577 # framework. Look over there for stack trace information. 578 # 579 # The lists contain 2-tuples of TestCase instances and strings holding 580 # formatted tracebacks. 581 # 582 # See http://docs.python.org/library/unittest.html#unittest.TestResult. 583 if self.__errored__: 584 pairs = lldb.test_result.errors 585 prefix = 'Error' 586 elif self.__failed__: 587 pairs = lldb.test_result.failures 588 prefix = 'Failure' 589 elif self.__expected__: 590 pairs = lldb.test_result.expectedFailures 591 prefix = 'ExpectedFailure' 592 else: 593 # Simply return, there's no session info to dump! 594 return 595 596 for test, traceback in pairs: 597 if test is self: 598 print >> self.session, traceback 599 600 dname = os.path.join(os.environ["LLDB_TEST"], 601 os.environ["LLDB_SESSION_DIRNAME"]) 602 if not os.path.isdir(dname): 603 os.mkdir(dname) 604 fname = os.path.join(dname, "%s-%s.log" % (prefix, self.id())) 605 with open(fname, "w") as f: 606 import datetime 607 print >> f, "Session info generated @", datetime.datetime.now().ctime() 608 print >> f, self.session.getvalue() 609 print >> f, "To rerun this test, issue the following command from the 'test' directory:\n" 610 print >> f, "%s ./dotest.py -v -t -f %s.%s" % (self.getRunSpec(), 611 self.__class__.__name__, 612 self._testMethodName) 613 614 def setTearDownCleanup(self, dictionary=None): 615 """Register a cleanup action at tearDown() time with a dictinary""" 616 self.dict = dictionary 617 self.doTearDownCleanup = True 618 619 def addTearDownHook(self, hook): 620 """ 621 Add a function to be run during tearDown() time. 622 623 Hooks are executed in a first come first serve manner. 624 """ 625 if callable(hook): 626 with recording(self, traceAlways) as sbuf: 627 print >> sbuf, "Adding tearDown hook:", getsource_if_available(hook) 628 self.hooks.append(hook) 629 630 def tearDown(self): 631 #import traceback 632 #traceback.print_stack() 633 634 # Check and run any hook functions. 635 for hook in self.hooks: 636 with recording(self, traceAlways) as sbuf: 637 print >> sbuf, "Executing tearDown hook:", getsource_if_available(hook) 638 hook() 639 640 # Terminate the current process being debugged, if any. 641 if self.runStarted: 642 self.runCmd("process kill", PROCESS_KILLED, check=False) 643 elif self.process and self.process.IsValid(): 644 rc = self.invoke(self.process, "Kill") 645 self.assertTrue(rc.Success(), PROCESS_KILLED) 646 del self.process 647 648 del self.dbg 649 del self.hooks 650 651 # Perform registered teardown cleanup. 652 if doCleanup and self.doTearDownCleanup: 653 module = __import__(sys.platform) 654 if not module.cleanup(self, dictionary=self.dict): 655 raise Exception("Don't know how to do cleanup") 656 657 # Decide whether to dump the session info. 658 self.dumpSessionInfo() 659 660 def runCmd(self, cmd, msg=None, check=True, trace=False, setCookie=True): 661 """ 662 Ask the command interpreter to handle the command and then check its 663 return status. 664 """ 665 # Fail fast if 'cmd' is not meaningful. 666 if not cmd or len(cmd) == 0: 667 raise Exception("Bad 'cmd' parameter encountered") 668 669 trace = (True if traceAlways else trace) 670 671 running = (cmd.startswith("run") or cmd.startswith("process launch")) 672 673 for i in range(self.maxLaunchCount if running else 1): 674 self.ci.HandleCommand(cmd, self.res) 675 676 with recording(self, trace) as sbuf: 677 print >> sbuf, "runCmd:", cmd 678 if not check: 679 print >> sbuf, "check of return status not required" 680 if self.res.Succeeded(): 681 print >> sbuf, "output:", self.res.GetOutput() 682 else: 683 print >> sbuf, "runCmd failed!" 684 print >> sbuf, self.res.GetError() 685 686 if running: 687 # For process launch, wait some time before possible next try. 688 time.sleep(self.timeWaitNextLaunch) 689 690 if self.res.Succeeded(): 691 break 692 elif running: 693 with recording(self, True) as sbuf: 694 print >> sbuf, "Command '" + cmd + "' failed!" 695 696 # Modify runStarted only if "run" or "process launch" was encountered. 697 if running: 698 self.runStarted = running and setCookie 699 700 if check: 701 self.assertTrue(self.res.Succeeded(), 702 msg if msg else CMD_MSG(cmd)) 703 704 def expect(self, str, msg=None, patterns=None, startstr=None, substrs=None, trace=False, error=False, matching=True, exe=True): 705 """ 706 Similar to runCmd; with additional expect style output matching ability. 707 708 Ask the command interpreter to handle the command and then check its 709 return status. The 'msg' parameter specifies an informational assert 710 message. We expect the output from running the command to start with 711 'startstr', matches the substrings contained in 'substrs', and regexp 712 matches the patterns contained in 'patterns'. 713 714 If the keyword argument error is set to True, it signifies that the API 715 client is expecting the command to fail. In this case, the error stream 716 from running the command is retrieved and compared against the golden 717 input, instead. 718 719 If the keyword argument matching is set to False, it signifies that the API 720 client is expecting the output of the command not to match the golden 721 input. 722 723 Finally, the required argument 'str' represents the lldb command to be 724 sent to the command interpreter. In case the keyword argument 'exe' is 725 set to False, the 'str' is treated as a string to be matched/not-matched 726 against the golden input. 727 """ 728 trace = (True if traceAlways else trace) 729 730 if exe: 731 # First run the command. If we are expecting error, set check=False. 732 # Pass the assert message along since it provides more semantic info. 733 self.runCmd(str, msg=msg, trace = (True if trace else False), check = not error) 734 735 # Then compare the output against expected strings. 736 output = self.res.GetError() if error else self.res.GetOutput() 737 738 # If error is True, the API client expects the command to fail! 739 if error: 740 self.assertFalse(self.res.Succeeded(), 741 "Command '" + str + "' is expected to fail!") 742 else: 743 # No execution required, just compare str against the golden input. 744 output = str 745 with recording(self, trace) as sbuf: 746 print >> sbuf, "looking at:", output 747 748 # The heading says either "Expecting" or "Not expecting". 749 heading = "Expecting" if matching else "Not expecting" 750 751 # Start from the startstr, if specified. 752 # If there's no startstr, set the initial state appropriately. 753 matched = output.startswith(startstr) if startstr else (True if matching else False) 754 755 if startstr: 756 with recording(self, trace) as sbuf: 757 print >> sbuf, "%s start string: %s" % (heading, startstr) 758 print >> sbuf, "Matched" if matched else "Not matched" 759 760 # Look for sub strings, if specified. 761 keepgoing = matched if matching else not matched 762 if substrs and keepgoing: 763 for str in substrs: 764 matched = output.find(str) != -1 765 with recording(self, trace) as sbuf: 766 print >> sbuf, "%s sub string: %s" % (heading, str) 767 print >> sbuf, "Matched" if matched else "Not matched" 768 keepgoing = matched if matching else not matched 769 if not keepgoing: 770 break 771 772 # Search for regular expression patterns, if specified. 773 keepgoing = matched if matching else not matched 774 if patterns and keepgoing: 775 for pattern in patterns: 776 # Match Objects always have a boolean value of True. 777 matched = bool(re.search(pattern, output)) 778 with recording(self, trace) as sbuf: 779 print >> sbuf, "%s pattern: %s" % (heading, pattern) 780 print >> sbuf, "Matched" if matched else "Not matched" 781 keepgoing = matched if matching else not matched 782 if not keepgoing: 783 break 784 785 self.assertTrue(matched if matching else not matched, 786 msg if msg else EXP_MSG(str, exe)) 787 788 def invoke(self, obj, name, trace=False): 789 """Use reflection to call a method dynamically with no argument.""" 790 trace = (True if traceAlways else trace) 791 792 method = getattr(obj, name) 793 import inspect 794 self.assertTrue(inspect.ismethod(method), 795 name + "is a method name of object: " + str(obj)) 796 result = method() 797 with recording(self, trace) as sbuf: 798 print >> sbuf, str(method) + ":", result 799 return result 800 801 def breakAfterLaunch(self, process, func, trace=False): 802 """ 803 Perform some dancees after LaunchProcess() to break at func name. 804 805 Return True if we can successfully break at the func name in due time. 806 """ 807 trace = (True if traceAlways else trace) 808 809 count = 0 810 while True: 811 # The stop reason of the thread should be breakpoint. 812 thread = process.GetThreadAtIndex(0) 813 SR = thread.GetStopReason() 814 with recording(self, trace) as sbuf: 815 print >> sbuf, "StopReason =", StopReasonString(SR) 816 817 if SR == StopReasonEnum("Breakpoint"): 818 frame = thread.GetFrameAtIndex(0) 819 name = frame.GetFunction().GetName() 820 with recording(self, trace) as sbuf: 821 print >> sbuf, "function =", name 822 if (name == func): 823 # We got what we want; now break out of the loop. 824 return True 825 826 # The inferior is in a transient state; continue the process. 827 time.sleep(1.0) 828 with recording(self, trace) as sbuf: 829 print >> sbuf, "Continuing the process:", process 830 process.Continue() 831 832 count = count + 1 833 if count == 15: 834 with recording(self, trace) as sbuf: 835 print >> sbuf, "Reached 15 iterations, giving up..." 836 # Enough iterations already, break out of the loop. 837 return False 838 839 # End of while loop. 840 841 842 def getCompiler(self): 843 """Returns the compiler in effect the test suite is now running with.""" 844 module = __import__(sys.platform) 845 return module.getCompiler() 846 847 def getRunSpec(self): 848 """Environment variable spec to run this test again, invoked from within 849 dumpSessionInfo().""" 850 module = __import__(sys.platform) 851 return module.getRunSpec() 852 853 def buildDefault(self, architecture=None, compiler=None, dictionary=None): 854 """Platform specific way to build the default binaries.""" 855 module = __import__(sys.platform) 856 if not module.buildDefault(self, architecture, compiler, dictionary): 857 raise Exception("Don't know how to build default binary") 858 859 def buildDsym(self, architecture=None, compiler=None, dictionary=None): 860 """Platform specific way to build binaries with dsym info.""" 861 module = __import__(sys.platform) 862 if not module.buildDsym(self, architecture, compiler, dictionary): 863 raise Exception("Don't know how to build binary with dsym") 864 865 def buildDwarf(self, architecture=None, compiler=None, dictionary=None): 866 """Platform specific way to build binaries with dwarf maps.""" 867 module = __import__(sys.platform) 868 if not module.buildDwarf(self, architecture, compiler, dictionary): 869 raise Exception("Don't know how to build binary with dwarf") 870 871 def DebugSBValue(self, frame, val): 872 """Debug print a SBValue object, if traceAlways is True.""" 873 from lldbutil import ValueTypeString 874 875 if not traceAlways: 876 return 877 878 err = sys.stderr 879 err.write(val.GetName() + ":\n") 880 err.write('\t' + "TypeName -> " + val.GetTypeName() + '\n') 881 err.write('\t' + "ByteSize -> " + str(val.GetByteSize()) + '\n') 882 err.write('\t' + "NumChildren -> " + str(val.GetNumChildren()) + '\n') 883 err.write('\t' + "Value -> " + str(val.GetValue(frame)) + '\n') 884 err.write('\t' + "ValueType -> " + ValueTypeString(val.GetValueType()) + '\n') 885 err.write('\t' + "Summary -> " + str(val.GetSummary(frame)) + '\n') 886 err.write('\t' + "IsPointerType -> " + str(val.TypeIsPointerType()) + '\n') 887 err.write('\t' + "Location -> " + val.GetLocation(frame) + '\n') 888 889