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