1Let's pick test/settings/TestSettings.py as our example.  First, notice the file
2name "TestSettings.py", the Test*.py pattern is the default mechanism that the
3test driver uses for discovery of tests.  As to TestSettings.py, it defines a
4class:
5
6class SettingsCommandTestCase(TestBase):
7
8derived from TestBase, which is defined in test/lldbtest.py and is itself
9derived from Python's unittest framework's TestCase class.  See also
10http://docs.python.org/library/unittest.html for more details.
11
12To just run the TestSettings.py test, chdir to the lldb test directory, and then
13type the following command:
14
15/Volumes/data/lldb/svn/trunk/test $ ./dotest.py settings
16----------------------------------------------------------------------
17Collected 6 tests
18
19----------------------------------------------------------------------
20Ran 6 tests in 8.699s
21
22OK (expected failures=1)
23/Volumes/data/lldb/svn/trunk/test $
24
25Pass '-v' option to the test driver to also output verbose descriptions of the
26individual test cases and their test status:
27
28/Volumes/data/lldb/svn/trunk/test $ ./dotest.py -v settings
29----------------------------------------------------------------------
30Collected 6 tests
31
32test_set_auto_confirm (TestSettings.SettingsCommandTestCase)
33Test that after 'set auto-confirm true', manual confirmation should not kick in. ... ok
34test_set_output_path (TestSettings.SettingsCommandTestCase)
35Test that setting target.process.output-path for the launched process works. ... expected failure
36test_set_prompt (TestSettings.SettingsCommandTestCase)
37Test that 'set prompt' actually changes the prompt. ... ok
38test_set_term_width (TestSettings.SettingsCommandTestCase)
39Test that 'set term-width' actually changes the term-width. ... ok
40test_with_dsym (TestSettings.SettingsCommandTestCase)
41Test that run-args and env-vars are passed to the launched process. ... ok
42test_with_dwarf (TestSettings.SettingsCommandTestCase)
43Test that run-args and env-vars are passed to the launched process. ... ok
44
45----------------------------------------------------------------------
46Ran 6 tests in 5.735s
47
48OK (expected failures=1)
49/Volumes/data/lldb/svn/trunk/test $
50
51Underneath, the '-v' option passes keyword argument verbosity=2 to the
52Python's unittest.TextTestRunner (see also
53http://docs.python.org/library/unittest.html#unittest.TextTestRunner).  For very
54detailed descriptions about what's going on during the test, pass '-t' to the
55test driver, which asks the test driver to trace the commands executed and to
56display their output.  For brevity, the '-t' output is not included here.
57
58Notice the 'expected failures=1' message at the end of the run.  This is because
59of a bug currently in lldb such that setting target.process.output-path to
60'stdout.txt' does not have any effect on the redirection of the standard output
61of the subsequent launched process.  We are using unittest2 (a backport of new
62unittest features for Python 2.4-2.6) to decorate (mark) the particular test
63method as such:
64
65    @unittest2.expectedFailure
66    # rdar://problem/8435794
67    # settings set target.process.output-path does not seem to work
68    def test_set_output_path(self):
69
70See http://pypi.python.org/pypi/unittest2 for more details.
71
72Now let's look inside the test method:
73
74    def test_set_output_path(self):
75        """Test that setting target.process.output-path for the launched process works."""
76        self.build()
77
78        exe = os.path.join(os.getcwd(), "a.out")
79        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
80
81        # Set the output-path and verify it is set.
82        self.runCmd("settings set target.process.output-path 'stdout.txt'")
83        self.expect("settings show target.process.output-path",
84            startstr = "target.process.output-path (string) = 'stdout.txt'")
85
86        self.runCmd("run", RUN_SUCCEEDED)
87
88        # The 'stdout.txt' file should now exist.
89        self.assertTrue(os.path.isfile("stdout.txt"),
90                        "'stdout.txt' exists due to target.process.output-path.")
91
92        # Read the output file produced by running the program.
93        with open('stdout.txt', 'r') as f:
94            output = f.read()
95
96        self.expect(output, exe=False,
97            startstr = "This message should go to standard out.")
98
99The self.build() statement is used to build a binary for this
100test instance. This will build the binary for the current debug info format. If
101we wanted to avoid running the test for every supported debug info format we
102could annotate it with @no_debug_info_test. The test would then only be run for
103the default format.  The logic for building a test binary resides in the builder
104modules (packages/Python/lldbsuite/test/builders/builder.py)
105
106After the binary is built, it is time to specify the file to be used as the main
107executable by lldb:
108
109        # Construct the path to a file "a.out" inside the test's build folder.
110        exe = self.getBuildArtifact("a.out")
111        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
112
113The runCmd() method is defined in the TestBase base class and its purpose is to
114pass the specified command to the lldb command interpreter. It's like you're
115typing the command within an interactive lldb session.
116
117The CURRENT_EXECUTABLE_SET is an assert message defined in the lldbtest module
118so that it can be reused from other test modules.
119
120By default, the runCmd() is going to check the return status of the command
121execution and fails the test if it is not a success.  The assert message, in our
122case CURRENT_EXECUTABLE_SET, is used in the exception printout if this happens.
123
124There are cases when we don't care about the return status from the command
125execution.  This can be accomplished by passing the keyword argument pair
126'check=False' to the method.
127
128After the current executable is set, we'll then execute two more commands:
129
130        # Set the output-path and verify it is set.
131        stdout = self.getBuildArtifact('stdout.txt')
132        self.runCmd("settings set target.process.output-path '%s'" %stdout)
133        self.expect("settings show target.process.output-path",
134                    SETTING_MSG("target.process.output-path"),
135            startstr = "target.process.output-path (string) = '.*stdout.txt'")
136
137The first uses the 'settings set' command to set the static setting
138target.process.output-path to be 'stdout.txt', instead of the default
139'/dev/stdout'.  We then immediately issue a 'settings show' command to check
140that, indeed, the setting did take place.  Notice that we use a new method
141expect() to accomplish the task, which in effect issues a runCmd() behind the
142door and grabs the output from the command execution and expects to match the
143start string of the output against what we pass in as the value of the keyword
144argument pair:
145
146            startstr = "target.process.output-path (string) = '%s'" %stdout
147
148Take a look at TestBase.expect() within lldbtest.py for more details.  Among
149other things, it can also match against a list of regexp patterns as well as a
150list of sub strings.  And it can also perform negative matching, i.e., instead
151of expecting something from the output of command execution, it can perform the
152action of 'not expecting' something.
153
154This will launch/run the program:
155
156        self.runCmd("run", RUN_SUCCEEDED)
157
158And this asserts that the file 'stdout.txt' should be present after running the
159program.
160
161        # The 'stdout.txt' file should now exist.
162        self.assertTrue(os.path.isfile(stdout),
163                        "stdout.txt' exists due to target.process.output-path.")
164
165Also take a look at main.cpp which emits some message to the stdout.  Now, if we
166pass this assertion, it's time to examine the contents of the file to make sure
167it contains the same message as programmed in main.cpp:
168
169        # Read the output file produced by running the program.
170        with open(stdout, 'r') as f:
171            output = f.read()
172
173        self.expect(output, exe=False,
174            startstr = "This message should go to standard out.")
175
176We open the file and read its contents into output, then issue an expect()
177method.  The 'exe=False' keyword argument pair tells expect() that don't try to
178execute the first arg as a command at all.  Instead, treat it as a string to
179match against whatever is thrown in as keyword argument pairs!
180
181There are also other test methods present in the TestSettings.py mode:
182test_set_prompt(), test_set_term_width(), test_set_auto_confirm(),
183test_with_dsym(), and test_with_dwarf().  We are using the default test loader
184from unittest framework, which uses the 'test' method name prefix to identify
185test methods automatically.
186
187This finishes the walkthrough of the test method test_set_output_path(self).
188Before we say goodbye, notice the little method definition at the top of the
189file:
190
191    @classmethod
192    def classCleanup(cls):
193        system(["/bin/sh", "-c", "rm -f "+self.getBuildArtifact("output.txt")])
194        system(["/bin/sh", "-c", "rm -f "+self.getBuildArtifact("stdout.txt")])
195
196This is a classmethod (as shown by the @classmethod decorator) which allows the
197individual test class to perform cleanup actions after the test harness finishes
198with the particular test class.  This is part of the so-called test fixture in
199the unittest framework.  From http://docs.python.org/library/unittest.html:
200
201A test fixture represents the preparation needed to perform one or more tests,
202and any associate cleanup actions. This may involve, for example, creating
203temporary or proxy databases, directories, or starting a server process.
204
205The TestBase class uses such fixture with setUp(self), tearDown(self),
206setUpClass(cls), and tearDownClass(cls).  And within teraDownClass(cls), it
207checks whether the current class has an attribute named 'classCleanup', and
208executes as a method if present.  In this particular case, the classCleanup()
209calls a utility function system() defined in lldbtest.py in order to remove the
210files created by running the program as the tests are executed.
211
212This system() function uses the Python subprocess module to spawn the process
213and to retrieve its results.  If the test instance passes the keyword argument
214pair 'sender=self', the detailed command execution through the operating system
215also gets recorded in a session object.  If the test instance fails or errors,
216the session info automatically gets dumped to a file grouped under a directory
217named after the timestamp of the particular test suite run.
218
219For simple cases, look for the timestamp directory in the same directory of the
220test driver program dotest.py.  For example, if we comment out the
221@expectedFailure decorator for TestSettings.py, and then run the test module:
222
223/Volumes/data/lldb/svn/trunk/test $ ./dotest.py -v settings
224----------------------------------------------------------------------
225Collected 6 tests
226
227test_set_auto_confirm (TestSettings.SettingsCommandTestCase)
228Test that after 'set auto-confirm true', manual confirmation should not kick in. ... ok
229test_set_output_path (TestSettings.SettingsCommandTestCase)
230Test that setting target.process.output-path for the launched process works. ... FAIL
231test_set_prompt (TestSettings.SettingsCommandTestCase)
232Test that 'set prompt' actually changes the prompt. ... ok
233test_set_term_width (TestSettings.SettingsCommandTestCase)
234Test that 'set term-width' actually changes the term-width. ... ok
235test_with_dsym (TestSettings.SettingsCommandTestCase)
236Test that run-args and env-vars are passed to the launched process. ... ok
237test_with_dwarf (TestSettings.SettingsCommandTestCase)
238Test that run-args and env-vars are passed to the launched process. ... ok
239
240======================================================================
241FAIL: test_set_output_path (TestSettings.SettingsCommandTestCase)
242Test that setting target.process.output-path for the launched process works.
243----------------------------------------------------------------------
244Traceback (most recent call last):
245  File "/Volumes/data/lldb/svn/trunk/test/settings/TestSettings.py", line 125, in test_set_output_path
246    "'stdout.txt' exists due to target.process.output-path.")
247AssertionError: False is not True : 'stdout.txt' exists due to target.process.output-path.
248
249----------------------------------------------------------------------
250Ran 6 tests in 8.219s
251
252FAILED (failures=1)
253/Volumes/data/lldb/svn/trunk/test $ ls 2010-10-19-14:10:49.059609
254
255NOTE: This directory name has been changed to not contain the ':' character
256      which is not allowed in windows platforms.  We'll change the ':' to '_'
257      and get rid of the microsecond resolution by modifying the test driver.
258
259TestSettings.SettingsCommandTestCase.test_set_output_path.log
260/Volumes/data/lldb/svn/trunk/test $
261
262We get one failure and a timestamp directory 2010-10-19-14:10:49.059609.
263For education purposes, the directory and its contents are reproduced here in
264the same directory as the current file.
265