lldbtest.py revision 3ebdaccbbb3567d6de652b91083997fbf10079ba
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 # Mark this function as such to separate them from lldb command line tests. 240 wrapper.__python_api_test__ = True 241 return wrapper 242 243class recording(StringIO.StringIO): 244 """ 245 A nice little context manager for recording the debugger interactions into 246 our session object. If trace flag is ON, it also emits the interactions 247 into the stderr. 248 """ 249 def __init__(self, test, trace): 250 """Create a StringIO instance; record the session obj and trace flag.""" 251 StringIO.StringIO.__init__(self) 252 self.session = test.session if test else None 253 self.trace = trace 254 255 def __enter__(self): 256 """ 257 Context management protocol on entry to the body of the with statement. 258 Just return the StringIO object. 259 """ 260 return self 261 262 def __exit__(self, type, value, tb): 263 """ 264 Context management protocol on exit from the body of the with statement. 265 If trace is ON, it emits the recordings into stderr. Always add the 266 recordings to our session object. And close the StringIO object, too. 267 """ 268 if self.trace: 269 print >> sys.stderr, self.getvalue() 270 if self.session: 271 print >> self.session, self.getvalue() 272 self.close() 273 274# From 2.7's subprocess.check_output() convenience function. 275def system(*popenargs, **kwargs): 276 r"""Run command with arguments and return its output as a byte string. 277 278 If the exit code was non-zero it raises a CalledProcessError. The 279 CalledProcessError object will have the return code in the returncode 280 attribute and output in the output attribute. 281 282 The arguments are the same as for the Popen constructor. Example: 283 284 >>> check_output(["ls", "-l", "/dev/null"]) 285 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' 286 287 The stdout argument is not allowed as it is used internally. 288 To capture standard error in the result, use stderr=STDOUT. 289 290 >>> check_output(["/bin/sh", "-c", 291 ... "ls -l non_existent_file ; exit 0"], 292 ... stderr=STDOUT) 293 'ls: non_existent_file: No such file or directory\n' 294 """ 295 296 # Assign the sender object to variable 'test' and remove it from kwargs. 297 test = kwargs.pop('sender', None) 298 299 if 'stdout' in kwargs: 300 raise ValueError('stdout argument not allowed, it will be overridden.') 301 process = Popen(stdout=PIPE, *popenargs, **kwargs) 302 output, error = process.communicate() 303 retcode = process.poll() 304 305 with recording(test, traceAlways) as sbuf: 306 if isinstance(popenargs, types.StringTypes): 307 args = [popenargs] 308 else: 309 args = list(popenargs) 310 print >> sbuf 311 print >> sbuf, "os command:", args 312 print >> sbuf, "stdout:", output 313 print >> sbuf, "stderr:", error 314 print >> sbuf, "retcode:", retcode 315 print >> sbuf 316 317 if retcode: 318 cmd = kwargs.get("args") 319 if cmd is None: 320 cmd = popenargs[0] 321 raise CalledProcessError(retcode, cmd) 322 return output 323 324def getsource_if_available(obj): 325 """ 326 Return the text of the source code for an object if available. Otherwise, 327 a print representation is returned. 328 """ 329 import inspect 330 try: 331 return inspect.getsource(obj) 332 except: 333 return repr(obj) 334 335class TestBase(unittest2.TestCase): 336 """ 337 This abstract base class is meant to be subclassed. It provides default 338 implementations for setUpClass(), tearDownClass(), setUp(), and tearDown(), 339 among other things. 340 341 Important things for test class writers: 342 343 - Overwrite the mydir class attribute, otherwise your test class won't 344 run. It specifies the relative directory to the top level 'test' so 345 the test harness can change to the correct working directory before 346 running your test. 347 348 - The setUp method sets up things to facilitate subsequent interactions 349 with the debugger as part of the test. These include: 350 - create/get a debugger set with synchronous mode (self.dbg) 351 - get the command interpreter from with the debugger (self.ci) 352 - create a result object for use with the command interpreter 353 (self.result) 354 - plus other stuffs 355 356 - The tearDown method tries to perform some necessary cleanup on behalf 357 of the test to return the debugger to a good state for the next test. 358 These include: 359 - execute any tearDown hooks registered by the test method with 360 TestBase.addTearDownHook(); examples can be found in 361 settings/TestSettings.py 362 - kill the inferior process launched during the test method 363 - if by 'run' or 'process launch' command, 'process kill' 364 command is used 365 - if the test method uses LLDB Python API to launch process, 366 it should assign the process object to self.process; that 367 way, tearDown will use self.process.Kill() on the object 368 - perform build cleanup before running the next test method in the 369 same test class; examples of registering for this service can be 370 found in types/TestIntegerTypes.py with the call: 371 - self.setTearDownCleanup(dictionary=d) 372 373 - Similarly setUpClass and tearDownClass perform classwise setup and 374 teardown fixtures. The tearDownClass method invokes a default build 375 cleanup for the entire test class; also, subclasses can implement the 376 classmethod classCleanup(cls) to perform special class cleanup action. 377 378 - The instance methods runCmd and expect are used heavily by existing 379 test cases to send a command to the command interpreter and to perform 380 string/pattern matching on the output of such command execution. The 381 expect method also provides a mode to peform string/pattern matching 382 without running a command. 383 384 - The build methods buildDefault, buildDsym, and buildDwarf are used to 385 build the binaries used during a particular test scenario. A plugin 386 should be provided for the sys.platform running the test suite. The 387 Mac OS X implementation is located in plugins/darwin.py. 388 389 """ 390 391 @classmethod 392 def skipLongRunningTest(cls): 393 """ 394 By default, we skip long running test case. 395 This can be overridden by passing '-l' to the test driver (dotest.py). 396 """ 397 if "LLDB_SKIP_LONG_RUNNING_TEST" in os.environ and "NO" == os.environ["LLDB_SKIP_LONG_RUNNING_TEST"]: 398 return False 399 else: 400 return True 401 402 # The concrete subclass should override this attribute. 403 mydir = None 404 405 # State pertaining to the inferior process, if any. 406 # This reflects inferior process started through the command interface with 407 # either the lldb "run" or "process launch" command. 408 # See also self.runCmd(). 409 runStarted = False 410 411 # Maximum allowed attempts when launching the inferior process. 412 # Can be overridden by the LLDB_MAX_LAUNCH_COUNT environment variable. 413 maxLaunchCount = 3; 414 415 # Time to wait before the next launching attempt in second(s). 416 # Can be overridden by the LLDB_TIME_WAIT_NEXT_LAUNCH environment variable. 417 timeWaitNextLaunch = 1.0; 418 419 # Keep track of the old current working directory. 420 oldcwd = None 421 422 @classmethod 423 def setUpClass(cls): 424 """ 425 Python unittest framework class setup fixture. 426 Do current directory manipulation. 427 """ 428 429 # Fail fast if 'mydir' attribute is not overridden. 430 if not cls.mydir or len(cls.mydir) == 0: 431 raise Exception("Subclasses must override the 'mydir' attribute.") 432 # Save old working directory. 433 cls.oldcwd = os.getcwd() 434 435 # Change current working directory if ${LLDB_TEST} is defined. 436 # See also dotest.py which sets up ${LLDB_TEST}. 437 if ("LLDB_TEST" in os.environ): 438 if traceAlways: 439 print >> sys.stderr, "Change dir to:", os.path.join(os.environ["LLDB_TEST"], cls.mydir) 440 os.chdir(os.path.join(os.environ["LLDB_TEST"], cls.mydir)) 441 442 @classmethod 443 def tearDownClass(cls): 444 """ 445 Python unittest framework class teardown fixture. 446 Do class-wide cleanup. 447 """ 448 449 if doCleanup: 450 # First, let's do the platform-specific cleanup. 451 module = __import__(sys.platform) 452 if not module.cleanup(): 453 raise Exception("Don't know how to do cleanup") 454 455 # Subclass might have specific cleanup function defined. 456 if getattr(cls, "classCleanup", None): 457 if traceAlways: 458 print >> sys.stderr, "Call class-specific cleanup function for class:", cls 459 try: 460 cls.classCleanup() 461 except: 462 exc_type, exc_value, exc_tb = sys.exc_info() 463 traceback.print_exception(exc_type, exc_value, exc_tb) 464 465 # Restore old working directory. 466 if traceAlways: 467 print >> sys.stderr, "Restore dir to:", cls.oldcwd 468 os.chdir(cls.oldcwd) 469 470 def setUp(self): 471 #import traceback 472 #traceback.print_stack() 473 474 if lldb.blacklist: 475 className = self.__class__.__name__ 476 classAndMethodName = "%s.%s" % (className, self._testMethodName) 477 if className in lldb.blacklist: 478 self.skipTest(lldb.blacklist.get(className)) 479 elif classAndMethodName in lldb.blacklist: 480 self.skipTest(lldb.blacklist.get(classAndMethodName)) 481 482 # Python API only test is decorated with @python_api_test, 483 # which also sets the "__python_api_test__" attribute of the 484 # function object to True. 485 if lldb.just_do_python_api_test: 486 testMethod = getattr(self, self._testMethodName) 487 if getattr(testMethod, "__python_api_test__", False): 488 pass 489 else: 490 self.skipTest("Skip lldb command line test") 491 492 if ("LLDB_WAIT_BETWEEN_TEST_CASES" in os.environ and 493 os.environ["LLDB_WAIT_BETWEEN_TEST_CASES"] == 'YES'): 494 waitTime = 1.0 495 if "LLDB_TIME_WAIT_BETWEEN_TEST_CASES" in os.environ: 496 waitTime = float(os.environ["LLDB_TIME_WAIT_BETWEEN_TEST_CASES"]) 497 time.sleep(waitTime) 498 499 if "LLDB_MAX_LAUNCH_COUNT" in os.environ: 500 self.maxLaunchCount = int(os.environ["LLDB_MAX_LAUNCH_COUNT"]) 501 502 if "LLDB_TIME_WAIT_NEXT_LAUNCH" in os.environ: 503 self.timeWaitNextLaunch = float(os.environ["LLDB_TIME_WAIT_NEXT_LAUNCH"]) 504 505 # Create the debugger instance if necessary. 506 try: 507 self.dbg = lldb.DBG 508 except AttributeError: 509 self.dbg = lldb.SBDebugger.Create() 510 511 if not self.dbg.IsValid(): 512 raise Exception('Invalid debugger instance') 513 514 # We want our debugger to be synchronous. 515 self.dbg.SetAsync(False) 516 517 # There is no process associated with the debugger as yet. 518 # See also self.tearDown() where it checks whether self.process has a 519 # valid reference and calls self.process.Kill() to kill the process. 520 self.process = None 521 522 # Retrieve the associated command interpreter instance. 523 self.ci = self.dbg.GetCommandInterpreter() 524 if not self.ci: 525 raise Exception('Could not get the command interpreter') 526 527 # And the result object. 528 self.res = lldb.SBCommandReturnObject() 529 530 # These are for customized teardown cleanup. 531 self.dict = None 532 self.doTearDownCleanup = False 533 534 # Create a string buffer to record the session info, to be dumped into a 535 # test case specific file if test failure is encountered. 536 self.session = StringIO.StringIO() 537 538 # Optimistically set __errored__, __failed__, __expected__ to False 539 # initially. If the test errored/failed, the session info 540 # (self.session) is then dumped into a session specific file for 541 # diagnosis. 542 self.__errored__ = False 543 self.__failed__ = False 544 self.__expected__ = False 545 546 # See addTearDownHook(self, hook) which allows the client to add a hook 547 # function to be run during tearDown() time. 548 self.hooks = [] 549 550 def markError(self): 551 """Callback invoked when an error (unexpected exception) errored.""" 552 self.__errored__ = True 553 with recording(self, False) as sbuf: 554 # False because there's no need to write "ERROR" to the stderr twice. 555 # Once by the Python unittest framework, and a second time by us. 556 print >> sbuf, "ERROR" 557 558 def markFailure(self): 559 """Callback invoked when a failure (test assertion failure) occurred.""" 560 self.__failed__ = True 561 with recording(self, False) as sbuf: 562 # False because there's no need to write "FAIL" to the stderr twice. 563 # Once by the Python unittest framework, and a second time by us. 564 print >> sbuf, "FAIL" 565 566 def markExpectedFailure(self): 567 """Callback invoked when an expected failure/error occurred.""" 568 self.__expected__ = True 569 with recording(self, False) as sbuf: 570 # False because there's no need to write "expected failure" to the 571 # stderr twice. 572 # Once by the Python unittest framework, and a second time by us. 573 print >> sbuf, "expected failure" 574 575 def dumpSessionInfo(self): 576 """ 577 Dump the debugger interactions leading to a test error/failure. This 578 allows for more convenient postmortem analysis. 579 580 See also LLDBTestResult (dotest.py) which is a singlton class derived 581 from TextTestResult and overwrites addError, addFailure, and 582 addExpectedFailure methods to allow us to to mark the test instance as 583 such. 584 """ 585 586 # We are here because self.tearDown() detected that this test instance 587 # either errored or failed. The lldb.test_result singleton contains 588 # two lists (erros and failures) which get populated by the unittest 589 # framework. Look over there for stack trace information. 590 # 591 # The lists contain 2-tuples of TestCase instances and strings holding 592 # formatted tracebacks. 593 # 594 # See http://docs.python.org/library/unittest.html#unittest.TestResult. 595 if self.__errored__: 596 pairs = lldb.test_result.errors 597 prefix = 'Error' 598 elif self.__failed__: 599 pairs = lldb.test_result.failures 600 prefix = 'Failure' 601 elif self.__expected__: 602 pairs = lldb.test_result.expectedFailures 603 prefix = 'ExpectedFailure' 604 else: 605 # Simply return, there's no session info to dump! 606 return 607 608 for test, traceback in pairs: 609 if test is self: 610 print >> self.session, traceback 611 612 dname = os.path.join(os.environ["LLDB_TEST"], 613 os.environ["LLDB_SESSION_DIRNAME"]) 614 if not os.path.isdir(dname): 615 os.mkdir(dname) 616 fname = os.path.join(dname, "%s-%s.log" % (prefix, self.id())) 617 with open(fname, "w") as f: 618 import datetime 619 print >> f, "Session info generated @", datetime.datetime.now().ctime() 620 print >> f, self.session.getvalue() 621 print >> f, "To rerun this test, issue the following command from the 'test' directory:\n" 622 print >> f, "%s ./dotest.py -v -t -f %s.%s" % (self.getRunSpec(), 623 self.__class__.__name__, 624 self._testMethodName) 625 626 def setTearDownCleanup(self, dictionary=None): 627 """Register a cleanup action at tearDown() time with a dictinary""" 628 self.dict = dictionary 629 self.doTearDownCleanup = True 630 631 def addTearDownHook(self, hook): 632 """ 633 Add a function to be run during tearDown() time. 634 635 Hooks are executed in a first come first serve manner. 636 """ 637 if callable(hook): 638 with recording(self, traceAlways) as sbuf: 639 print >> sbuf, "Adding tearDown hook:", getsource_if_available(hook) 640 self.hooks.append(hook) 641 642 def tearDown(self): 643 #import traceback 644 #traceback.print_stack() 645 646 # Check and run any hook functions. 647 for hook in self.hooks: 648 with recording(self, traceAlways) as sbuf: 649 print >> sbuf, "Executing tearDown hook:", getsource_if_available(hook) 650 hook() 651 652 # Terminate the current process being debugged, if any. 653 if self.runStarted: 654 self.runCmd("process kill", PROCESS_KILLED, check=False) 655 elif self.process and self.process.IsValid(): 656 rc = self.invoke(self.process, "Kill") 657 self.assertTrue(rc.Success(), PROCESS_KILLED) 658 del self.process 659 660 del self.dbg 661 del self.hooks 662 663 # Perform registered teardown cleanup. 664 if doCleanup and self.doTearDownCleanup: 665 module = __import__(sys.platform) 666 if not module.cleanup(self, dictionary=self.dict): 667 raise Exception("Don't know how to do cleanup") 668 669 # Decide whether to dump the session info. 670 self.dumpSessionInfo() 671 672 def runCmd(self, cmd, msg=None, check=True, trace=False, setCookie=True): 673 """ 674 Ask the command interpreter to handle the command and then check its 675 return status. 676 """ 677 # Fail fast if 'cmd' is not meaningful. 678 if not cmd or len(cmd) == 0: 679 raise Exception("Bad 'cmd' parameter encountered") 680 681 trace = (True if traceAlways else trace) 682 683 running = (cmd.startswith("run") or cmd.startswith("process launch")) 684 685 for i in range(self.maxLaunchCount if running else 1): 686 self.ci.HandleCommand(cmd, self.res) 687 688 with recording(self, trace) as sbuf: 689 print >> sbuf, "runCmd:", cmd 690 if not check: 691 print >> sbuf, "check of return status not required" 692 if self.res.Succeeded(): 693 print >> sbuf, "output:", self.res.GetOutput() 694 else: 695 print >> sbuf, "runCmd failed!" 696 print >> sbuf, self.res.GetError() 697 698 if running: 699 # For process launch, wait some time before possible next try. 700 time.sleep(self.timeWaitNextLaunch) 701 702 if self.res.Succeeded(): 703 break 704 elif running: 705 with recording(self, True) as sbuf: 706 print >> sbuf, "Command '" + cmd + "' failed!" 707 708 # Modify runStarted only if "run" or "process launch" was encountered. 709 if running: 710 self.runStarted = running and setCookie 711 712 if check: 713 self.assertTrue(self.res.Succeeded(), 714 msg if msg else CMD_MSG(cmd)) 715 716 def expect(self, str, msg=None, patterns=None, startstr=None, substrs=None, trace=False, error=False, matching=True, exe=True): 717 """ 718 Similar to runCmd; with additional expect style output matching ability. 719 720 Ask the command interpreter to handle the command and then check its 721 return status. The 'msg' parameter specifies an informational assert 722 message. We expect the output from running the command to start with 723 'startstr', matches the substrings contained in 'substrs', and regexp 724 matches the patterns contained in 'patterns'. 725 726 If the keyword argument error is set to True, it signifies that the API 727 client is expecting the command to fail. In this case, the error stream 728 from running the command is retrieved and compared against the golden 729 input, instead. 730 731 If the keyword argument matching is set to False, it signifies that the API 732 client is expecting the output of the command not to match the golden 733 input. 734 735 Finally, the required argument 'str' represents the lldb command to be 736 sent to the command interpreter. In case the keyword argument 'exe' is 737 set to False, the 'str' is treated as a string to be matched/not-matched 738 against the golden input. 739 """ 740 trace = (True if traceAlways else trace) 741 742 if exe: 743 # First run the command. If we are expecting error, set check=False. 744 # Pass the assert message along since it provides more semantic info. 745 self.runCmd(str, msg=msg, trace = (True if trace else False), check = not error) 746 747 # Then compare the output against expected strings. 748 output = self.res.GetError() if error else self.res.GetOutput() 749 750 # If error is True, the API client expects the command to fail! 751 if error: 752 self.assertFalse(self.res.Succeeded(), 753 "Command '" + str + "' is expected to fail!") 754 else: 755 # No execution required, just compare str against the golden input. 756 output = str 757 with recording(self, trace) as sbuf: 758 print >> sbuf, "looking at:", output 759 760 # The heading says either "Expecting" or "Not expecting". 761 heading = "Expecting" if matching else "Not expecting" 762 763 # Start from the startstr, if specified. 764 # If there's no startstr, set the initial state appropriately. 765 matched = output.startswith(startstr) if startstr else (True if matching else False) 766 767 if startstr: 768 with recording(self, trace) as sbuf: 769 print >> sbuf, "%s start string: %s" % (heading, startstr) 770 print >> sbuf, "Matched" if matched else "Not matched" 771 772 # Look for sub strings, if specified. 773 keepgoing = matched if matching else not matched 774 if substrs and keepgoing: 775 for str in substrs: 776 matched = output.find(str) != -1 777 with recording(self, trace) as sbuf: 778 print >> sbuf, "%s sub string: %s" % (heading, str) 779 print >> sbuf, "Matched" if matched else "Not matched" 780 keepgoing = matched if matching else not matched 781 if not keepgoing: 782 break 783 784 # Search for regular expression patterns, if specified. 785 keepgoing = matched if matching else not matched 786 if patterns and keepgoing: 787 for pattern in patterns: 788 # Match Objects always have a boolean value of True. 789 matched = bool(re.search(pattern, output)) 790 with recording(self, trace) as sbuf: 791 print >> sbuf, "%s pattern: %s" % (heading, pattern) 792 print >> sbuf, "Matched" if matched else "Not matched" 793 keepgoing = matched if matching else not matched 794 if not keepgoing: 795 break 796 797 self.assertTrue(matched if matching else not matched, 798 msg if msg else EXP_MSG(str, exe)) 799 800 def invoke(self, obj, name, trace=False): 801 """Use reflection to call a method dynamically with no argument.""" 802 trace = (True if traceAlways else trace) 803 804 method = getattr(obj, name) 805 import inspect 806 self.assertTrue(inspect.ismethod(method), 807 name + "is a method name of object: " + str(obj)) 808 result = method() 809 with recording(self, trace) as sbuf: 810 print >> sbuf, str(method) + ":", result 811 return result 812 813 def breakAfterLaunch(self, process, func, trace=False): 814 """ 815 Perform some dancees after LaunchProcess() to break at func name. 816 817 Return True if we can successfully break at the func name in due time. 818 """ 819 trace = (True if traceAlways else trace) 820 821 count = 0 822 while True: 823 # The stop reason of the thread should be breakpoint. 824 thread = process.GetThreadAtIndex(0) 825 SR = thread.GetStopReason() 826 with recording(self, trace) as sbuf: 827 print >> sbuf, "StopReason =", StopReasonString(SR) 828 829 if SR == StopReasonEnum("Breakpoint"): 830 frame = thread.GetFrameAtIndex(0) 831 name = frame.GetFunction().GetName() 832 with recording(self, trace) as sbuf: 833 print >> sbuf, "function =", name 834 if (name == func): 835 # We got what we want; now break out of the loop. 836 return True 837 838 # The inferior is in a transient state; continue the process. 839 time.sleep(1.0) 840 with recording(self, trace) as sbuf: 841 print >> sbuf, "Continuing the process:", process 842 process.Continue() 843 844 count = count + 1 845 if count == 15: 846 with recording(self, trace) as sbuf: 847 print >> sbuf, "Reached 15 iterations, giving up..." 848 # Enough iterations already, break out of the loop. 849 return False 850 851 # End of while loop. 852 853 854 def getCompiler(self): 855 """Returns the compiler in effect the test suite is now running with.""" 856 module = __import__(sys.platform) 857 return module.getCompiler() 858 859 def getRunSpec(self): 860 """Environment variable spec to run this test again, invoked from within 861 dumpSessionInfo().""" 862 module = __import__(sys.platform) 863 return module.getRunSpec() 864 865 def buildDefault(self, architecture=None, compiler=None, dictionary=None): 866 """Platform specific way to build the default binaries.""" 867 module = __import__(sys.platform) 868 if not module.buildDefault(self, architecture, compiler, dictionary): 869 raise Exception("Don't know how to build default binary") 870 871 def buildDsym(self, architecture=None, compiler=None, dictionary=None): 872 """Platform specific way to build binaries with dsym info.""" 873 module = __import__(sys.platform) 874 if not module.buildDsym(self, architecture, compiler, dictionary): 875 raise Exception("Don't know how to build binary with dsym") 876 877 def buildDwarf(self, architecture=None, compiler=None, dictionary=None): 878 """Platform specific way to build binaries with dwarf maps.""" 879 module = __import__(sys.platform) 880 if not module.buildDwarf(self, architecture, compiler, dictionary): 881 raise Exception("Don't know how to build binary with dwarf") 882 883 def DebugSBValue(self, frame, val): 884 """Debug print a SBValue object, if traceAlways is True.""" 885 from lldbutil import ValueTypeString 886 887 if not traceAlways: 888 return 889 890 err = sys.stderr 891 err.write(val.GetName() + ":\n") 892 err.write('\t' + "TypeName -> " + val.GetTypeName() + '\n') 893 err.write('\t' + "ByteSize -> " + str(val.GetByteSize()) + '\n') 894 err.write('\t' + "NumChildren -> " + str(val.GetNumChildren()) + '\n') 895 err.write('\t' + "Value -> " + str(val.GetValue(frame)) + '\n') 896 err.write('\t' + "ValueType -> " + ValueTypeString(val.GetValueType()) + '\n') 897 err.write('\t' + "Summary -> " + str(val.GetSummary(frame)) + '\n') 898 err.write('\t' + "IsPointerType -> " + str(val.TypeIsPointerType()) + '\n') 899 err.write('\t' + "Location -> " + val.GetLocation(frame) + '\n') 900 901