aboutsummaryrefslogtreecommitdiff
path: root/docs/testsuite/a-detailed-walkthrough.txt
blob: 12543baff65924700f01daa682a298c0d13306e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
Let's pick test/settings/TestSettings.py as our example.  First, notice the file
name "TestSettings.py", the Test*.py pattern is the default mechanism that the
test driver uses for discovery of tests.  As to TestSettings.py, it defines a
class:

class SettingsCommandTestCase(TestBase):

derived from TestBase, which is defined in test/lldbtest.py and is itself
derived from Python's unittest framework's TestCase class.  See also
http://docs.python.org/library/unittest.html for more details.

To just run the TestSettings.py test, chdir to the lldb test directory, and then
type the following command:

/Volumes/data/lldb/svn/trunk/test $ ./dotest.py settings
----------------------------------------------------------------------
Collected 6 tests

----------------------------------------------------------------------
Ran 6 tests in 8.699s

OK (expected failures=1)
/Volumes/data/lldb/svn/trunk/test $ 

Pass '-v' option to the test driver to also output verbose descriptions of the
individual test cases and their test status:

/Volumes/data/lldb/svn/trunk/test $ ./dotest.py -v settings
----------------------------------------------------------------------
Collected 6 tests

test_set_auto_confirm (TestSettings.SettingsCommandTestCase)
Test that after 'set auto-confirm true', manual confirmation should not kick in. ... ok
test_set_output_path (TestSettings.SettingsCommandTestCase)
Test that setting target.process.output-path for the launched process works. ... expected failure
test_set_prompt (TestSettings.SettingsCommandTestCase)
Test that 'set prompt' actually changes the prompt. ... ok
test_set_term_width (TestSettings.SettingsCommandTestCase)
Test that 'set term-width' actually changes the term-width. ... ok
test_with_dsym (TestSettings.SettingsCommandTestCase)
Test that run-args and env-vars are passed to the launched process. ... ok
test_with_dwarf (TestSettings.SettingsCommandTestCase)
Test that run-args and env-vars are passed to the launched process. ... ok

----------------------------------------------------------------------
Ran 6 tests in 5.735s

OK (expected failures=1)
/Volumes/data/lldb/svn/trunk/test $ 

Underneath, the '-v' option passes keyword argument verbosity=2 to the
Python's unittest.TextTestRunner (see also
http://docs.python.org/library/unittest.html#unittest.TextTestRunner).  For very
detailed descriptions about what's going on during the test, pass '-t' to the
test driver, which asks the test driver to trace the commands executed and to
display their output.  For brevity, the '-t' output is not included here.

Notice the 'expected failures=1' message at the end of the run.  This is because
of a bug currently in lldb such that setting target.process.output-path to
'stdout.txt' does not have any effect on the redirection of the standard output
of the subsequent launched process.  We are using unittest2 (a backport of new
unittest features for Python 2.4-2.6) to decorate (mark) the particular test
method as such:

    @unittest2.expectedFailure
    # rdar://problem/8435794
    # settings set target.process.output-path does not seem to work
    def test_set_output_path(self):

See http://pypi.python.org/pypi/unittest2 for more details.

Now let's look inside the test method:

    def test_set_output_path(self):
        """Test that setting target.process.output-path for the launched process works."""
        self.buildDefault()

        exe = os.path.join(os.getcwd(), "a.out")
        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)

        # Set the output-path and verify it is set.
        self.runCmd("settings set target.process.output-path 'stdout.txt'")
        self.expect("settings show target.process.output-path",
            startstr = "target.process.output-path (string) = 'stdout.txt'")

        self.runCmd("run", RUN_SUCCEEDED)

        # The 'stdout.txt' file should now exist.
        self.assertTrue(os.path.isfile("stdout.txt"),
                        "'stdout.txt' exists due to target.process.output-path.")

        # Read the output file produced by running the program.
        with open('stdout.txt', 'r') as f:
            output = f.read()

        self.expect(output, exe=False,
            startstr = "This message should go to standard out.")

The self.buildDefault() statement is used to build a default binary for this
test instance.  For this particular test case, since we don't really care what
debugging format is used, we instruct the build subsystem to build the default
binary for us.  The base class TestBase has defined three instance methods:

    def buildDefault(self, architecture=None, compiler=None, dictionary=None):
        """Platform specific way to build the default binaries."""
        module = __import__(sys.platform)
        if not module.buildDefault(self, architecture, compiler, dictionary):
            raise Exception("Don't know how to build default binary")

    def buildDsym(self, architecture=None, compiler=None, dictionary=None):
        """Platform specific way to build binaries with dsym info."""
        module = __import__(sys.platform)
        if not module.buildDsym(self, architecture, compiler, dictionary):
            raise Exception("Don't know how to build binary with dsym")

    def buildDwarf(self, architecture=None, compiler=None, dictionary=None):
        """Platform specific way to build binaries with dwarf maps."""
        module = __import__(sys.platform)
        if not module.buildDwarf(self, architecture, compiler, dictionary):
            raise Exception("Don't know how to build binary with dwarf")

And the test/plugins/darwin.py provides the implmentation for all three build
methods using the makefile mechanism.  We envision that linux plugin can use a
similar approach to accomplish the task of building the binaries.

Mac OS X provides an additional way to manipulate archived DWARF debug symbol
files and produces dSYM files.  The buildDsym() instance method is used by the
test method to build the binary with dsym info.  For an example of this,
see test/array_types/TestArrayTypes.py:

    @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
    def test_with_dsym_and_run_command(self):
        """Test 'frame variable var_name' on some variables with array types."""
        self.buildDsym()
        self.array_types()

This method is decorated with a skipUnless decorator so that it will only gets
included into the test suite if the platform it is running on is 'darwin', aka
Mac OS X.

Type 'man dsymutil' for more details. 

After the binary is built, it is time to specify the file to be used as the main
executable by lldb:

        exe = os.path.join(os.getcwd(), "a.out")
        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)

This is where the attribute assignment:

class SettingsCommandTestCase(TestBase):

    mydir = "settings"

which happens right after the SettingsCommandTestCase class declaration comes
into place.  It specifies the relative directory to the top level 'test' so that
the test harness runtime can change its working directory in order to find the
executable as well as the source code files.  The runCmd() method is defined
in the TestBase base class (within test/lldbtest.py) and its purpose is to pass
the specified command to the lldb command interpreter.  It's like you're typing
the command within an interactive lldb session.

The CURRENT_EXECUTABLE_SET is an assert message defined in the lldbtest module
so that it can reused from other test modules.