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