lldbtest.py revision ac91027caa22212ac35de969fa53348b70e9bb28
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_DO_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 136PROCESS_EXITED = "Process exited successfully" 137 138PROCESS_STOPPED = "Process status should be stopped" 139 140RUN_SUCCEEDED = "Process is launched successfully" 141 142RUN_COMPLETED = "Process exited successfully" 143 144BACKTRACE_DISPLAYED_CORRECTLY = "Backtrace displayed correctly" 145 146BREAKPOINT_CREATED = "Breakpoint created successfully" 147 148BREAKPOINT_STATE_CORRECT = "Breakpoint state is correct" 149 150BREAKPOINT_PENDING_CREATED = "Pending breakpoint created successfully" 151 152BREAKPOINT_HIT_ONCE = "Breakpoint resolved with hit cout = 1" 153 154BREAKPOINT_HIT_TWICE = "Breakpoint resolved with hit cout = 2" 155 156BREAKPOINT_HIT_THRICE = "Breakpoint resolved with hit cout = 3" 157 158SOURCE_DISPLAYED_CORRECTLY = "Source code displayed correctly" 159 160STEP_OUT_SUCCEEDED = "Thread step-out succeeded" 161 162STOPPED_DUE_TO_EXC_BAD_ACCESS = "Process should be stopped due to bad access exception" 163 164STOPPED_DUE_TO_BREAKPOINT = "Process should be stopped due to breakpoint" 165 166STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS = "%s, %s" % ( 167 STOPPED_DUE_TO_BREAKPOINT, "instead, the actual stop reason is: '%s'") 168 169STOPPED_DUE_TO_BREAKPOINT_CONDITION = "Stopped due to breakpoint condition" 170 171STOPPED_DUE_TO_BREAKPOINT_IGNORE_COUNT = "Stopped due to breakpoint and ignore count" 172 173STOPPED_DUE_TO_SIGNAL = "Process state is stopped due to signal" 174 175STOPPED_DUE_TO_STEP_IN = "Process state is stopped due to step in" 176 177DATA_TYPES_DISPLAYED_CORRECTLY = "Data type(s) displayed correctly" 178 179VALID_BREAKPOINT = "Got a valid breakpoint" 180 181VALID_BREAKPOINT_LOCATION = "Got a valid breakpoint location" 182 183VALID_COMMAND_INTERPRETER = "Got a valid command interpreter" 184 185VALID_FILESPEC = "Got a valid filespec" 186 187VALID_MODULE = "Got a valid module" 188 189VALID_PROCESS = "Got a valid process" 190 191VALID_SYMBOL = "Got a valid symbol" 192 193VALID_TARGET = "Got a valid target" 194 195VARIABLES_DISPLAYED_CORRECTLY = "Variable(s) displayed correctly" 196 197 198# 199# And a generic "Command '%s' returns successfully" message generator. 200# 201def CMD_MSG(str): 202 return "Command '%s' returns successfully" % str 203 204# 205# And a generic "'%s' returns expected result" message generator if exe. 206# Otherwise, it's "'%s' matches expected result" 207# 208def EXP_MSG(str, exe): 209 return "'%s' %s expected result" % (str, 'returns' if exe else 'matches') 210 211# 212# And a generic "Value of setting '%s' is correct" message generator. 213# 214def SETTING_MSG(setting): 215 return "Value of setting '%s' is correct" % setting 216 217# 218# Returns an env variable array from the os.environ map object. 219# 220def EnvArray(): 221 return map(lambda k,v: k+"="+v, os.environ.keys(), os.environ.values()) 222 223def line_number(filename, string_to_match): 224 """Helper function to return the line number of the first matched string.""" 225 with open(filename, 'r') as f: 226 for i, line in enumerate(f): 227 if line.find(string_to_match) != -1: 228 # Found our match. 229 return i+1 230 raise Exception("Unable to find '%s' within file %s" % (string_to_match, filename)) 231 232def pointer_size(): 233 """Return the pointer size of the host system.""" 234 import ctypes 235 a_pointer = ctypes.c_void_p(0xffff) 236 return 8 * ctypes.sizeof(a_pointer) 237 238from functools import wraps 239def python_api_test(func): 240 """Decorate the item as a Python API only test.""" 241 if isinstance(func, type) and issubclass(func, unittest2.TestCase): 242 raise Exception("@python_api_test can only be used to decorate a test method") 243 @wraps(func) 244 def wrapper(self, *args, **kwargs): 245 if lldb.dont_do_python_api_test: 246 self.skipTest("Skip Python API tests") 247 return func(self, *args, **kwargs) 248 249 # Mark this function as such to separate them from lldb command line tests. 250 wrapper.__python_api_test__ = True 251 return wrapper 252 253class recording(StringIO.StringIO): 254 """ 255 A nice little context manager for recording the debugger interactions into 256 our session object. If trace flag is ON, it also emits the interactions 257 into the stderr. 258 """ 259 def __init__(self, test, trace): 260 """Create a StringIO instance; record the session obj and trace flag.""" 261 StringIO.StringIO.__init__(self) 262 self.session = test.session if test else None 263 self.trace = trace 264 265 def __enter__(self): 266 """ 267 Context management protocol on entry to the body of the with statement. 268 Just return the StringIO object. 269 """ 270 return self 271 272 def __exit__(self, type, value, tb): 273 """ 274 Context management protocol on exit from the body of the with statement. 275 If trace is ON, it emits the recordings into stderr. Always add the 276 recordings to our session object. And close the StringIO object, too. 277 """ 278 if self.trace: 279 print >> sys.stderr, self.getvalue() 280 if self.session: 281 print >> self.session, self.getvalue() 282 self.close() 283 284# From 2.7's subprocess.check_output() convenience function. 285# Return a tuple (stdoutdata, stderrdata). 286def system(*popenargs, **kwargs): 287 r"""Run command with arguments and return its output as a byte string. 288 289 If the exit code was non-zero it raises a CalledProcessError. The 290 CalledProcessError object will have the return code in the returncode 291 attribute and output in the output attribute. 292 293 The arguments are the same as for the Popen constructor. Example: 294 295 >>> check_output(["ls", "-l", "/dev/null"]) 296 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' 297 298 The stdout argument is not allowed as it is used internally. 299 To capture standard error in the result, use stderr=STDOUT. 300 301 >>> check_output(["/bin/sh", "-c", 302 ... "ls -l non_existent_file ; exit 0"], 303 ... stderr=STDOUT) 304 'ls: non_existent_file: No such file or directory\n' 305 """ 306 307 # Assign the sender object to variable 'test' and remove it from kwargs. 308 test = kwargs.pop('sender', None) 309 310 if 'stdout' in kwargs: 311 raise ValueError('stdout argument not allowed, it will be overridden.') 312 process = Popen(stdout=PIPE, stderr=PIPE, *popenargs, **kwargs) 313 output, error = process.communicate() 314 retcode = process.poll() 315 316 with recording(test, traceAlways) as sbuf: 317 if isinstance(popenargs, types.StringTypes): 318 args = [popenargs] 319 else: 320 args = list(popenargs) 321 print >> sbuf 322 print >> sbuf, "os command:", args 323 print >> sbuf, "stdout:", output 324 print >> sbuf, "stderr:", error 325 print >> sbuf, "retcode:", retcode 326 print >> sbuf 327 328 if retcode: 329 cmd = kwargs.get("args") 330 if cmd is None: 331 cmd = popenargs[0] 332 raise CalledProcessError(retcode, cmd) 333 return (output, error) 334 335def getsource_if_available(obj): 336 """ 337 Return the text of the source code for an object if available. Otherwise, 338 a print representation is returned. 339 """ 340 import inspect 341 try: 342 return inspect.getsource(obj) 343 except: 344 return repr(obj) 345 346class TestBase(unittest2.TestCase): 347 """ 348 This abstract base class is meant to be subclassed. It provides default 349 implementations for setUpClass(), tearDownClass(), setUp(), and tearDown(), 350 among other things. 351 352 Important things for test class writers: 353 354 - Overwrite the mydir class attribute, otherwise your test class won't 355 run. It specifies the relative directory to the top level 'test' so 356 the test harness can change to the correct working directory before 357 running your test. 358 359 - The setUp method sets up things to facilitate subsequent interactions 360 with the debugger as part of the test. These include: 361 - create/get a debugger set with synchronous mode (self.dbg) 362 - get the command interpreter from with the debugger (self.ci) 363 - create a result object for use with the command interpreter 364 (self.result) 365 - plus other stuffs 366 367 - The tearDown method tries to perform some necessary cleanup on behalf 368 of the test to return the debugger to a good state for the next test. 369 These include: 370 - execute any tearDown hooks registered by the test method with 371 TestBase.addTearDownHook(); examples can be found in 372 settings/TestSettings.py 373 - kill the inferior process launched during the test method 374 - if by 'run' or 'process launch' command, 'process kill' 375 command is used 376 - if the test method uses LLDB Python API to launch process, 377 it should assign the process object to self.process; that 378 way, tearDown will use self.process.Kill() on the object 379 - perform build cleanup before running the next test method in the 380 same test class; examples of registering for this service can be 381 found in types/TestIntegerTypes.py with the call: 382 - self.setTearDownCleanup(dictionary=d) 383 384 - Similarly setUpClass and tearDownClass perform classwise setup and 385 teardown fixtures. The tearDownClass method invokes a default build 386 cleanup for the entire test class; also, subclasses can implement the 387 classmethod classCleanup(cls) to perform special class cleanup action. 388 389 - The instance methods runCmd and expect are used heavily by existing 390 test cases to send a command to the command interpreter and to perform 391 string/pattern matching on the output of such command execution. The 392 expect method also provides a mode to peform string/pattern matching 393 without running a command. 394 395 - The build methods buildDefault, buildDsym, and buildDwarf are used to 396 build the binaries used during a particular test scenario. A plugin 397 should be provided for the sys.platform running the test suite. The 398 Mac OS X implementation is located in plugins/darwin.py. 399 400 """ 401 402 @classmethod 403 def skipLongRunningTest(cls): 404 """ 405 By default, we skip long running test case. 406 This can be overridden by passing '-l' to the test driver (dotest.py). 407 """ 408 if "LLDB_SKIP_LONG_RUNNING_TEST" in os.environ and "NO" == os.environ["LLDB_SKIP_LONG_RUNNING_TEST"]: 409 return False 410 else: 411 return True 412 413 # The concrete subclass should override this attribute. 414 mydir = None 415 416 # State pertaining to the inferior process, if any. 417 # This reflects inferior process started through the command interface with 418 # either the lldb "run" or "process launch" command. 419 # See also self.runCmd(). 420 runStarted = False 421 422 # Maximum allowed attempts when launching the inferior process. 423 # Can be overridden by the LLDB_MAX_LAUNCH_COUNT environment variable. 424 maxLaunchCount = 3; 425 426 # Time to wait before the next launching attempt in second(s). 427 # Can be overridden by the LLDB_TIME_WAIT_NEXT_LAUNCH environment variable. 428 timeWaitNextLaunch = 1.0; 429 430 # Keep track of the old current working directory. 431 oldcwd = None 432 433 @classmethod 434 def setUpClass(cls): 435 """ 436 Python unittest framework class setup fixture. 437 Do current directory manipulation. 438 """ 439 440 # Fail fast if 'mydir' attribute is not overridden. 441 if not cls.mydir or len(cls.mydir) == 0: 442 raise Exception("Subclasses must override the 'mydir' attribute.") 443 # Save old working directory. 444 cls.oldcwd = os.getcwd() 445 446 # Change current working directory if ${LLDB_TEST} is defined. 447 # See also dotest.py which sets up ${LLDB_TEST}. 448 if ("LLDB_TEST" in os.environ): 449 if traceAlways: 450 print >> sys.stderr, "Change dir to:", os.path.join(os.environ["LLDB_TEST"], cls.mydir) 451 os.chdir(os.path.join(os.environ["LLDB_TEST"], cls.mydir)) 452 453 @classmethod 454 def tearDownClass(cls): 455 """ 456 Python unittest framework class teardown fixture. 457 Do class-wide cleanup. 458 """ 459 460 if doCleanup: 461 # First, let's do the platform-specific cleanup. 462 module = __import__(sys.platform) 463 if not module.cleanup(): 464 raise Exception("Don't know how to do cleanup") 465 466 # Subclass might have specific cleanup function defined. 467 if getattr(cls, "classCleanup", None): 468 if traceAlways: 469 print >> sys.stderr, "Call class-specific cleanup function for class:", cls 470 try: 471 cls.classCleanup() 472 except: 473 exc_type, exc_value, exc_tb = sys.exc_info() 474 traceback.print_exception(exc_type, exc_value, exc_tb) 475 476 # Restore old working directory. 477 if traceAlways: 478 print >> sys.stderr, "Restore dir to:", cls.oldcwd 479 os.chdir(cls.oldcwd) 480 481 def setUp(self): 482 #import traceback 483 #traceback.print_stack() 484 485 if "LLDB_EXEC" in os.environ: 486 self.lldbExec = os.environ["LLDB_EXEC"] 487 488 if lldb.blacklist: 489 className = self.__class__.__name__ 490 classAndMethodName = "%s.%s" % (className, self._testMethodName) 491 if className in lldb.blacklist: 492 self.skipTest(lldb.blacklist.get(className)) 493 elif classAndMethodName in lldb.blacklist: 494 self.skipTest(lldb.blacklist.get(classAndMethodName)) 495 496 # Python API only test is decorated with @python_api_test, 497 # which also sets the "__python_api_test__" attribute of the 498 # function object to True. 499 if lldb.just_do_python_api_test: 500 testMethod = getattr(self, self._testMethodName) 501 if getattr(testMethod, "__python_api_test__", False): 502 pass 503 else: 504 self.skipTest("Skip lldb command line test") 505 506 if ("LLDB_WAIT_BETWEEN_TEST_CASES" in os.environ and 507 os.environ["LLDB_WAIT_BETWEEN_TEST_CASES"] == 'YES'): 508 waitTime = 1.0 509 if "LLDB_TIME_WAIT_BETWEEN_TEST_CASES" in os.environ: 510 waitTime = float(os.environ["LLDB_TIME_WAIT_BETWEEN_TEST_CASES"]) 511 time.sleep(waitTime) 512 513 if "LLDB_MAX_LAUNCH_COUNT" in os.environ: 514 self.maxLaunchCount = int(os.environ["LLDB_MAX_LAUNCH_COUNT"]) 515 516 if "LLDB_TIME_WAIT_NEXT_LAUNCH" in os.environ: 517 self.timeWaitNextLaunch = float(os.environ["LLDB_TIME_WAIT_NEXT_LAUNCH"]) 518 519 # Create the debugger instance if necessary. 520 try: 521 self.dbg = lldb.DBG 522 except AttributeError: 523 self.dbg = lldb.SBDebugger.Create() 524 525 if not self.dbg.IsValid(): 526 raise Exception('Invalid debugger instance') 527 528 # We want our debugger to be synchronous. 529 self.dbg.SetAsync(False) 530 531 # This is for the case of directly spawning 'lldb' and interacting with 532 # it using pexpect. 533 self.child = None 534 # If the child is interacting with the embedded script interpreter, 535 # there are two exits required during tear down, first to quit the 536 # embedded script interpreter and second to quit the lldb command 537 # interpreter. 538 self.child_in_script_interpreter = False 539 540 # There is no process associated with the debugger as yet. 541 # See also self.tearDown() where it checks whether self.process has a 542 # valid reference and calls self.process.Kill() to kill the process. 543 self.process = None 544 545 # Retrieve the associated command interpreter instance. 546 self.ci = self.dbg.GetCommandInterpreter() 547 if not self.ci: 548 raise Exception('Could not get the command interpreter') 549 550 # And the result object. 551 self.res = lldb.SBCommandReturnObject() 552 553 # These are for customized teardown cleanup. 554 self.dict = None 555 self.doTearDownCleanup = False 556 # And in rare cases where there are multiple teardown cleanups. 557 self.dicts = [] 558 self.doTearDownCleanups = False 559 560 # Create a string buffer to record the session info, to be dumped into a 561 # test case specific file if test failure is encountered. 562 self.session = StringIO.StringIO() 563 564 # Optimistically set __errored__, __failed__, __expected__ to False 565 # initially. If the test errored/failed, the session info 566 # (self.session) is then dumped into a session specific file for 567 # diagnosis. 568 self.__errored__ = False 569 self.__failed__ = False 570 self.__expected__ = False 571 # We are also interested in unexpected success. 572 self.__unexpected__ = False 573 574 # See addTearDownHook(self, hook) which allows the client to add a hook 575 # function to be run during tearDown() time. 576 self.hooks = [] 577 578 # See HideStdout(self). 579 self.sys_stdout_hidden = False 580 581 def markError(self): 582 """Callback invoked when an error (unexpected exception) errored.""" 583 self.__errored__ = True 584 with recording(self, False) as sbuf: 585 # False because there's no need to write "ERROR" to the stderr twice. 586 # Once by the Python unittest framework, and a second time by us. 587 print >> sbuf, "ERROR" 588 589 def markFailure(self): 590 """Callback invoked when a failure (test assertion failure) occurred.""" 591 self.__failed__ = True 592 with recording(self, False) as sbuf: 593 # False because there's no need to write "FAIL" to the stderr twice. 594 # Once by the Python unittest framework, and a second time by us. 595 print >> sbuf, "FAIL" 596 597 def markExpectedFailure(self): 598 """Callback invoked when an expected failure/error occurred.""" 599 self.__expected__ = True 600 with recording(self, False) as sbuf: 601 # False because there's no need to write "expected failure" to the 602 # stderr twice. 603 # Once by the Python unittest framework, and a second time by us. 604 print >> sbuf, "expected failure" 605 606 def markUnexpectedSuccess(self): 607 """Callback invoked when an unexpected success occurred.""" 608 self.__unexpected__ = True 609 with recording(self, False) as sbuf: 610 # False because there's no need to write "unexpected success" to the 611 # stderr twice. 612 # Once by the Python unittest framework, and a second time by us. 613 print >> sbuf, "unexpected success" 614 615 def dumpSessionInfo(self): 616 """ 617 Dump the debugger interactions leading to a test error/failure. This 618 allows for more convenient postmortem analysis. 619 620 See also LLDBTestResult (dotest.py) which is a singlton class derived 621 from TextTestResult and overwrites addError, addFailure, and 622 addExpectedFailure methods to allow us to to mark the test instance as 623 such. 624 """ 625 626 # We are here because self.tearDown() detected that this test instance 627 # either errored or failed. The lldb.test_result singleton contains 628 # two lists (erros and failures) which get populated by the unittest 629 # framework. Look over there for stack trace information. 630 # 631 # The lists contain 2-tuples of TestCase instances and strings holding 632 # formatted tracebacks. 633 # 634 # See http://docs.python.org/library/unittest.html#unittest.TestResult. 635 if self.__errored__: 636 pairs = lldb.test_result.errors 637 prefix = 'Error' 638 elif self.__failed__: 639 pairs = lldb.test_result.failures 640 prefix = 'Failure' 641 elif self.__expected__: 642 pairs = lldb.test_result.expectedFailures 643 prefix = 'ExpectedFailure' 644 elif self.__unexpected__: 645 prefix = "UnexpectedSuccess" 646 else: 647 # Simply return, there's no session info to dump! 648 return 649 650 if not self.__unexpected__: 651 for test, traceback in pairs: 652 if test is self: 653 print >> self.session, traceback 654 655 dname = os.path.join(os.environ["LLDB_TEST"], 656 os.environ["LLDB_SESSION_DIRNAME"]) 657 if not os.path.isdir(dname): 658 os.mkdir(dname) 659 fname = os.path.join(dname, "%s-%s.log" % (prefix, self.id())) 660 with open(fname, "w") as f: 661 import datetime 662 print >> f, "Session info generated @", datetime.datetime.now().ctime() 663 print >> f, self.session.getvalue() 664 print >> f, "To rerun this test, issue the following command from the 'test' directory:\n" 665 print >> f, "./dotest.py %s -v -t -f %s.%s" % (self.getRunOptions(), 666 self.__class__.__name__, 667 self._testMethodName) 668 669 def setTearDownCleanup(self, dictionary=None): 670 """Register a cleanup action at tearDown() time with a dictinary""" 671 self.dict = dictionary 672 self.doTearDownCleanup = True 673 674 def addTearDownCleanup(self, dictionary): 675 """Add a cleanup action at tearDown() time with a dictinary""" 676 self.dicts.append(dictionary) 677 self.doTearDownCleanups = True 678 679 def addTearDownHook(self, hook): 680 """ 681 Add a function to be run during tearDown() time. 682 683 Hooks are executed in a first come first serve manner. 684 """ 685 if callable(hook): 686 with recording(self, traceAlways) as sbuf: 687 print >> sbuf, "Adding tearDown hook:", getsource_if_available(hook) 688 self.hooks.append(hook) 689 690 def tearDown(self): 691 #import traceback 692 #traceback.print_stack() 693 694 # Check and run any hook functions. 695 for hook in reversed(self.hooks): 696 with recording(self, traceAlways) as sbuf: 697 print >> sbuf, "Executing tearDown hook:", getsource_if_available(hook) 698 hook() 699 700 # This is for the case of directly spawning 'lldb' and interacting with it 701 # using pexpect. 702 import pexpect 703 if self.child and self.child.isalive(): 704 with recording(self, traceAlways) as sbuf: 705 print >> sbuf, "tearing down the child process...." 706 if self.child_in_script_interpreter: 707 self.child.sendline('quit()') 708 self.child.expect_exact('(lldb) ') 709 self.child.sendline('quit') 710 try: 711 self.child.expect(pexpect.EOF) 712 except: 713 pass 714 715 # Terminate the current process being debugged, if any. 716 if self.runStarted: 717 self.runCmd("process kill", PROCESS_KILLED, check=False) 718 elif self.process and self.process.IsValid(): 719 rc = self.invoke(self.process, "Kill") 720 self.assertTrue(rc.Success(), PROCESS_KILLED) 721 del self.process 722 723 del self.dbg 724 del self.hooks 725 726 # Perform registered teardown cleanup. 727 if doCleanup and self.doTearDownCleanup: 728 module = __import__(sys.platform) 729 if not module.cleanup(self, dictionary=self.dict): 730 raise Exception("Don't know how to do cleanup with dictionary: " + self.dict) 731 732 # In rare cases where there are multiple teardown cleanups added. 733 if doCleanup and self.doTearDownCleanups: 734 module = __import__(sys.platform) 735 if self.dicts: 736 for dict in reversed(self.dicts): 737 if not module.cleanup(self, dictionary=dict): 738 raise Exception("Don't know how to do cleanup with dictionary: " + dict) 739 740 # Decide whether to dump the session info. 741 self.dumpSessionInfo() 742 743 def runCmd(self, cmd, msg=None, check=True, trace=False, setCookie=True): 744 """ 745 Ask the command interpreter to handle the command and then check its 746 return status. 747 """ 748 # Fail fast if 'cmd' is not meaningful. 749 if not cmd or len(cmd) == 0: 750 raise Exception("Bad 'cmd' parameter encountered") 751 752 trace = (True if traceAlways else trace) 753 754 running = (cmd.startswith("run") or cmd.startswith("process launch")) 755 756 for i in range(self.maxLaunchCount if running else 1): 757 self.ci.HandleCommand(cmd, self.res) 758 759 with recording(self, trace) as sbuf: 760 print >> sbuf, "runCmd:", cmd 761 if not check: 762 print >> sbuf, "check of return status not required" 763 if self.res.Succeeded(): 764 print >> sbuf, "output:", self.res.GetOutput() 765 else: 766 print >> sbuf, "runCmd failed!" 767 print >> sbuf, self.res.GetError() 768 769 if self.res.Succeeded(): 770 break 771 elif running: 772 # For process launch, wait some time before possible next try. 773 time.sleep(self.timeWaitNextLaunch) 774 with recording(self, True) as sbuf: 775 print >> sbuf, "Command '" + cmd + "' failed!" 776 777 # Modify runStarted only if "run" or "process launch" was encountered. 778 if running: 779 self.runStarted = running and setCookie 780 781 if check: 782 self.assertTrue(self.res.Succeeded(), 783 msg if msg else CMD_MSG(cmd)) 784 785 def expect(self, str, msg=None, patterns=None, startstr=None, substrs=None, trace=False, error=False, matching=True, exe=True): 786 """ 787 Similar to runCmd; with additional expect style output matching ability. 788 789 Ask the command interpreter to handle the command and then check its 790 return status. The 'msg' parameter specifies an informational assert 791 message. We expect the output from running the command to start with 792 'startstr', matches the substrings contained in 'substrs', and regexp 793 matches the patterns contained in 'patterns'. 794 795 If the keyword argument error is set to True, it signifies that the API 796 client is expecting the command to fail. In this case, the error stream 797 from running the command is retrieved and compared against the golden 798 input, instead. 799 800 If the keyword argument matching is set to False, it signifies that the API 801 client is expecting the output of the command not to match the golden 802 input. 803 804 Finally, the required argument 'str' represents the lldb command to be 805 sent to the command interpreter. In case the keyword argument 'exe' is 806 set to False, the 'str' is treated as a string to be matched/not-matched 807 against the golden input. 808 """ 809 trace = (True if traceAlways else trace) 810 811 if exe: 812 # First run the command. If we are expecting error, set check=False. 813 # Pass the assert message along since it provides more semantic info. 814 self.runCmd(str, msg=msg, trace = (True if trace else False), check = not error) 815 816 # Then compare the output against expected strings. 817 output = self.res.GetError() if error else self.res.GetOutput() 818 819 # If error is True, the API client expects the command to fail! 820 if error: 821 self.assertFalse(self.res.Succeeded(), 822 "Command '" + str + "' is expected to fail!") 823 else: 824 # No execution required, just compare str against the golden input. 825 output = str 826 with recording(self, trace) as sbuf: 827 print >> sbuf, "looking at:", output 828 829 # The heading says either "Expecting" or "Not expecting". 830 heading = "Expecting" if matching else "Not expecting" 831 832 # Start from the startstr, if specified. 833 # If there's no startstr, set the initial state appropriately. 834 matched = output.startswith(startstr) if startstr else (True if matching else False) 835 836 if startstr: 837 with recording(self, trace) as sbuf: 838 print >> sbuf, "%s start string: %s" % (heading, startstr) 839 print >> sbuf, "Matched" if matched else "Not matched" 840 841 # Look for sub strings, if specified. 842 keepgoing = matched if matching else not matched 843 if substrs and keepgoing: 844 for str in substrs: 845 matched = output.find(str) != -1 846 with recording(self, trace) as sbuf: 847 print >> sbuf, "%s sub string: %s" % (heading, str) 848 print >> sbuf, "Matched" if matched else "Not matched" 849 keepgoing = matched if matching else not matched 850 if not keepgoing: 851 break 852 853 # Search for regular expression patterns, if specified. 854 keepgoing = matched if matching else not matched 855 if patterns and keepgoing: 856 for pattern in patterns: 857 # Match Objects always have a boolean value of True. 858 matched = bool(re.search(pattern, output)) 859 with recording(self, trace) as sbuf: 860 print >> sbuf, "%s pattern: %s" % (heading, pattern) 861 print >> sbuf, "Matched" if matched else "Not matched" 862 keepgoing = matched if matching else not matched 863 if not keepgoing: 864 break 865 866 self.assertTrue(matched if matching else not matched, 867 msg if msg else EXP_MSG(str, exe)) 868 869 def invoke(self, obj, name, trace=False): 870 """Use reflection to call a method dynamically with no argument.""" 871 trace = (True if traceAlways else trace) 872 873 method = getattr(obj, name) 874 import inspect 875 self.assertTrue(inspect.ismethod(method), 876 name + "is a method name of object: " + str(obj)) 877 result = method() 878 with recording(self, trace) as sbuf: 879 print >> sbuf, str(method) + ":", result 880 return result 881 882 def breakAfterLaunch(self, process, func, trace=False): 883 """ 884 Perform some dances after Launch() to break at func name. 885 886 Return True if we can successfully break at the func name in due time. 887 """ 888 trace = (True if traceAlways else trace) 889 890 count = 0 891 while True: 892 # The stop reason of the thread should be breakpoint. 893 thread = process.GetThreadAtIndex(0) 894 SR = thread.GetStopReason() 895 with recording(self, trace) as sbuf: 896 print >> sbuf, "StopReason =", stop_reason_to_str(SR) 897 898 if SR == lldb.eStopReasonBreakpoint: 899 frame = thread.GetFrameAtIndex(0) 900 name = frame.GetFunction().GetName() 901 with recording(self, trace) as sbuf: 902 print >> sbuf, "function =", name 903 if (name == func): 904 # We got what we want; now break out of the loop. 905 return True 906 907 # The inferior is in a transient state; continue the process. 908 time.sleep(1.0) 909 with recording(self, trace) as sbuf: 910 print >> sbuf, "Continuing the process:", process 911 process.Continue() 912 913 count = count + 1 914 if count == 15: 915 with recording(self, trace) as sbuf: 916 print >> sbuf, "Reached 15 iterations, giving up..." 917 # Enough iterations already, break out of the loop. 918 return False 919 920 # End of while loop. 921 922 923 def getArchitecture(self): 924 """Returns the architecture in effect the test suite is now running with.""" 925 module = __import__(sys.platform) 926 return module.getArchitecture() 927 928 def getCompiler(self): 929 """Returns the compiler in effect the test suite is now running with.""" 930 module = __import__(sys.platform) 931 return module.getCompiler() 932 933 def getRunOptions(self): 934 """Command line option for -A and -C to run this test again, called from 935 within dumpSessionInfo().""" 936 module = __import__(sys.platform) 937 arch = self.getArchitecture() 938 comp = self.getCompiler() 939 if not arch and not comp: 940 return "" 941 else: 942 return "%s %s" % ("-A "+arch if arch else "", 943 "-C "+comp if comp else "") 944 945 def buildDefault(self, architecture=None, compiler=None, dictionary=None): 946 """Platform specific way to build the default binaries.""" 947 module = __import__(sys.platform) 948 if not module.buildDefault(self, architecture, compiler, dictionary): 949 raise Exception("Don't know how to build default binary") 950 951 def buildDsym(self, architecture=None, compiler=None, dictionary=None): 952 """Platform specific way to build binaries with dsym info.""" 953 module = __import__(sys.platform) 954 if not module.buildDsym(self, architecture, compiler, dictionary): 955 raise Exception("Don't know how to build binary with dsym") 956 957 def buildDwarf(self, architecture=None, compiler=None, dictionary=None): 958 """Platform specific way to build binaries with dwarf maps.""" 959 module = __import__(sys.platform) 960 if not module.buildDwarf(self, architecture, compiler, dictionary): 961 raise Exception("Don't know how to build binary with dwarf") 962 963 def DebugSBValue(self, frame, val): 964 """Debug print a SBValue object, if traceAlways is True.""" 965 from lldbutil import value_type_to_str 966 967 if not traceAlways: 968 return 969 970 err = sys.stderr 971 err.write(val.GetName() + ":\n") 972 err.write('\t' + "TypeName -> " + val.GetTypeName() + '\n') 973 err.write('\t' + "ByteSize -> " + str(val.GetByteSize()) + '\n') 974 err.write('\t' + "NumChildren -> " + str(val.GetNumChildren()) + '\n') 975 err.write('\t' + "Value -> " + str(val.GetValue(frame)) + '\n') 976 err.write('\t' + "ValueType -> " + value_type_to_str(val.GetValueType()) + '\n') 977 err.write('\t' + "Summary -> " + str(val.GetSummary(frame)) + '\n') 978 err.write('\t' + "IsPointerType -> " + str(val.TypeIsPointerType()) + '\n') 979 err.write('\t' + "Location -> " + val.GetLocation(frame) + '\n') 980 981 def DebugPExpect(self, child): 982 """Debug the spwaned pexpect object.""" 983 if not traceAlways: 984 return 985 986 print child 987 988 def TraceOn(self): 989 """Returns True if we are in trace mode (i.e., tracing lldb command execution).""" 990 return traceAlways 991 992 def HideStdout(self): 993 """Hide output to stdout from the user. 994 995 During test execution, there might be cases where we don't want to show the 996 standard output to the user. For example, 997 998 self.runCmd(r'''sc print "\n\n\tHello!\n"''') 999 1000 tests whether command abbreviation for 'script' works or not. There is no 1001 need to show the 'Hello' output to the user as long as the 'script' command 1002 succeeds and we are not in TraceOn() mode (see the '-t' option). 1003 1004 In this case, the test method calls self.HideStdout(self) to redirect the 1005 sys.stdout to a null device, and restores the sys.stdout upon teardown. 1006 1007 Note that you should only call this method at most once during a test case 1008 execution. Any subsequent call has no effect at all.""" 1009 if self.sys_stdout_hidden: 1010 return 1011 1012 self.sys_stdout_hidden = True 1013 old_stdout = sys.stdout 1014 sys.stdout = open(os.devnull, 'w') 1015 def restore_stdout(): 1016 sys.stdout = old_stdout 1017 self.addTearDownHook(restore_stdout) 1018