Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(184)

Side by Side Diff: tools/asan/chrome_tests.py

Issue 7922010: Implement the EmbeddedTool class in valgrind_test.py to allow running tools that are embedded int... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tools/asan/base_unittests.gtest-asan.txt ('k') | tools/asan/chrome_tests.sh » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python
2
3 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6
7 ''' Runs various chrome tests through asan_test.py.
8
9 Most of this code is copied from ../valgrind/chrome_tests.py.
10 TODO(glider): put common functions to a standalone module.
11 '''
12
13 import glob
14 import logging
15 import optparse
16 import os
17 import stat
18 import sys
19
20 import logging_utils
21 import path_utils
22
23 import common
24 import asan_test
25
26 class TestNotFound(Exception): pass
27
28 def Dir2IsNewer(dir1, dir2):
29 if dir2 is None or not os.path.isdir(dir2):
30 return False
31 if dir1 is None or not os.path.isdir(dir1):
32 return True
33 return os.stat(dir2)[stat.ST_MTIME] > os.stat(dir1)[stat.ST_MTIME]
34
35 def FindNewestDir(dirs):
36 newest_dir = None
37 for dir in dirs:
38 if Dir2IsNewer(newest_dir, dir):
39 newest_dir = dir
40 return newest_dir
41
42 def File2IsNewer(file1, file2):
43 if file2 is None or not os.path.isfile(file2):
44 return False
45 if file1 is None or not os.path.isfile(file1):
46 return True
47 return os.stat(file2)[stat.ST_MTIME] > os.stat(file1)[stat.ST_MTIME]
48
49 def FindDirContainingNewestFile(dirs, file):
50 """Searches for the directory containing the newest copy of |file|.
51
52 Args:
53 dirs: A list of paths to the directories to search among.
54 file: A string containing the file name to search.
55
56 Returns:
57 The string representing the the directory containing the newest copy of
58 |file|.
59
60 Raises:
61 IOError: |file| was not found.
62 """
63 newest_dir = None
64 newest_file = None
65 for dir in dirs:
66 the_file = os.path.join(dir, file)
67 if File2IsNewer(newest_file, the_file):
68 newest_dir = dir
69 newest_file = the_file
70 if newest_dir is None:
71 raise IOError("cannot find file %s anywhere, have you built it?" % file)
72 return newest_dir
73
74 class ChromeTests(object):
75 '''This class is derived from the chrome_tests.py file in ../purify/.
76 '''
77
78 def __init__(self, options, args, test):
79 # The known list of tests.
80 # Recognise the original abbreviations as well as full executable names.
81 self._test_list = {
82 "base": self.TestBase, "base_unittests": self.TestBase,
83 "browser": self.TestBrowser, "browser_tests": self.TestBrowser,
84 "crypto": self.TestCrypto, "crypto_unittests": self.TestCrypto,
85 "googleurl": self.TestGURL, "googleurl_unittests": self.TestGURL,
86 "content": self.TestContent, "content_unittests": self.TestContent,
87 "courgette": self.TestCourgette,
88 "courgette_unittests": self.TestCourgette,
89 "ipc": self.TestIpc, "ipc_tests": self.TestIpc,
90 "layout": self.TestLayout, "layout_tests": self.TestLayout,
91 "media": self.TestMedia, "media_unittests": self.TestMedia,
92 "net": self.TestNet, "net_unittests": self.TestNet,
93 "printing": self.TestPrinting, "printing_unittests": self.TestPrinting,
94 "remoting": self.TestRemoting, "remoting_unittests": self.TestRemoting,
95 "startup": self.TestStartup, "startup_tests": self.TestStartup,
96 "sync": self.TestSync, "sync_unit_tests": self.TestSync,
97 "test_shell": self.TestTestShell, "test_shell_tests": self.TestTestShell,
98 "ui": self.TestUI, "ui_tests": self.TestUI,
99 "unit": self.TestUnit, "unit_tests": self.TestUnit,
100 "views": self.TestViews, "views_unittests": self.TestViews,
101 "sql": self.TestSql, "sql_unittests": self.TestSql,
102 "ui_unit": self.TestUIUnit, "ui_unittests": self.TestUIUnit,
103 "gfx": self.TestGfx, "gfx_unittests": self.TestGfx,
104 }
105
106 if test not in self._test_list:
107 raise TestNotFound("Unknown test: %s" % test)
108
109 self._options = options
110 self._args = args
111 self._test = test
112
113 script_dir = path_utils.ScriptDir()
114
115 # Compute the top of the tree (the "source dir") from the script dir (where
116 # this script lives). We assume that the script dir is in tools/asan/
117 # relative to the top of the tree.
118 self._source_dir = os.path.dirname(os.path.dirname(script_dir))
119
120 # Since this path is used for string matching, make sure it's always
121 # an absolute Unix-style path.
122 self._source_dir = os.path.abspath(self._source_dir).replace('\\', '/')
123
124 asan_test_script = os.path.join(script_dir, "asan_test.py")
125 self._command_preamble = [asan_test_script]
126
127 def _DefaultCommand(self, module, exe=None, asan_test_args=None):
128 '''Generates the default command array that most tests will use.
129
130 Args:
131 module: The module name (corresponds to the dir in src/ where the test
132 data resides).
133 exe: The executable name.
134 asan_test_args: additional arguments to append to the command line.
135 Returns:
136 A string with the command to run the test.
137 '''
138 if not self._options.build_dir:
139 dirs = [
140 os.path.join(self._source_dir, "xcodebuild", "Debug"),
141 os.path.join(self._source_dir, "out", "Debug"),
142 ]
143 if exe:
144 self._options.build_dir = FindDirContainingNewestFile(dirs, exe)
145 else:
146 self._options.build_dir = FindNewestDir(dirs)
147
148 cmd = list(self._command_preamble)
149
150 if asan_test_args != None:
151 for arg in asan_test_args:
152 cmd.append(arg)
153 if exe:
154 cmd.append(os.path.join(self._options.build_dir, exe))
155 # Show elapased time so we can find the slowpokes.
156 cmd.append("--gtest_print_time")
157 if self._options.gtest_repeat:
158 cmd.append("--gtest_repeat=%s" % self._options.gtest_repeat)
159 return cmd
160
161 def Suppressions(self):
162 '''Builds the list of available suppressions files.'''
163 ret = []
164 directory = path_utils.ScriptDir()
165 suppression_file = os.path.join(directory, "suppressions.txt")
166 if os.path.exists(suppression_file):
167 ret.append(suppression_file)
168 suppression_file = os.path.join(directory, "suppressions_linux.txt")
169 if os.path.exists(suppression_file):
170 ret.append(suppression_file)
171 return ret
172
173 def Run(self):
174 '''Runs the test specified by command-line argument --test.'''
175 logging.info("running test %s" % (self._test))
176 return self._test_list[self._test]()
177
178 def _ReadGtestFilterFile(self, name, cmd):
179 '''Reads files which contain lists of tests to filter out with
180 --gtest_filter and appends the command-line option to |cmd|.
181
182 Args:
183 name: the test executable name.
184 cmd: the test running command line to be modified.
185 '''
186 filters = []
187 directory = path_utils.ScriptDir()
188 gtest_filter_files = [
189 os.path.join(directory, name + ".gtest-asan.txt"),
190 # TODO(glider): Linux vs. CrOS?
191 ]
192 logging.info("Reading gtest exclude filter files:")
193 for filename in gtest_filter_files:
194 # strip the leading absolute path (may be very long on the bot)
195 # and the following / or \.
196 readable_filename = filename.replace(self._source_dir, "")[1:]
197 if not os.path.exists(filename):
198 logging.info(" \"%s\" - not found" % readable_filename)
199 continue
200 logging.info(" \"%s\" - OK" % readable_filename)
201 f = open(filename, 'r')
202 for line in f.readlines():
203 if line.startswith("#") or line.startswith("//") or line.isspace():
204 continue
205 line = line.rstrip()
206 filters.append(line)
207 gtest_filter = self._options.gtest_filter
208 if len(filters):
209 if gtest_filter:
210 gtest_filter += ":"
211 if gtest_filter.find("-") < 0:
212 gtest_filter += "-"
213 else:
214 gtest_filter = "-"
215 gtest_filter += ":".join(filters)
216 if gtest_filter:
217 cmd.append("--gtest_filter=%s" % gtest_filter)
218
219 def SimpleTest(self, module, name, asan_test_args=None, cmd_args=None):
220 '''Builds the command line and runs the specified test.
221
222 Args:
223 module: The module name (corresponds to the dir in src/ where the test
224 data resides).
225 name: The executable name.
226 asan_test_args: Additional command line args for asan.
227 cmd_args: Additional command line args for the test.
228 '''
229 cmd = self._DefaultCommand(module, name, asan_test_args)
230 supp = self.Suppressions()
231 self._ReadGtestFilterFile(name, cmd)
232 if cmd_args:
233 cmd.extend(["--"])
234 cmd.extend(cmd_args)
235
236 # Sets LD_LIBRARY_PATH to the build folder so external libraries can be
237 # loaded.
238 if (os.getenv("LD_LIBRARY_PATH")):
239 os.putenv("LD_LIBRARY_PATH", "%s:%s" % (os.getenv("LD_LIBRARY_PATH"),
240 self._options.build_dir))
241 else:
242 os.putenv("LD_LIBRARY_PATH", self._options.build_dir)
243 return asan_test.RunTool(cmd, supp, module)
244
245 def TestBase(self):
246 return self.SimpleTest("base", "base_unittests")
247
248 def TestBrowser(self):
249 return self.SimpleTest("chrome", "browser_tests")
250
251 def TestCrypto(self):
252 return self.SimpleTest("crypto", "crypto_unittests")
253
254 def TestGURL(self):
255 return self.SimpleTest("chrome", "googleurl_unittests")
256
257 def TestContent(self):
258 return self.SimpleTest("content", "content_unittests")
259
260 def TestCourgette(self):
261 return self.SimpleTest("courgette", "courgette_unittests")
262
263 def TestMedia(self):
264 return self.SimpleTest("chrome", "media_unittests")
265
266 def TestPrinting(self):
267 return self.SimpleTest("chrome", "printing_unittests")
268
269 def TestRemoting(self):
270 return self.SimpleTest("chrome", "remoting_unittests")
271
272 def TestSync(self):
273 return self.SimpleTest("chrome", "sync_unit_tests")
274
275 def TestIpc(self):
276 return self.SimpleTest("ipc", "ipc_tests")
277
278 def TestNet(self):
279 return self.SimpleTest("net", "net_unittests")
280
281 def TestStartup(self):
282 # We don't need the performance results, we're just looking for pointer
283 # errors, so set number of iterations down to the minimum.
284 os.putenv("STARTUP_TESTS_NUMCYCLES", "1")
285 logging.info("export STARTUP_TESTS_NUMCYCLES=1");
286 return self.SimpleTest("chrome", "startup_tests")
287
288 def TestTestShell(self):
289 return self.SimpleTest("webkit", "test_shell_tests")
290
291 def TestUnit(self):
292 return self.SimpleTest("chrome", "unit_tests")
293
294 def TestViews(self):
295 return self.SimpleTest("views", "views_unittests")
296
297 def TestSql(self):
298 return self.SimpleTest("chrome", "sql_unittests")
299
300 def TestUIUnit(self):
301 return self.SimpleTest("chrome", "ui_unittests")
302
303 def TestGfx(self):
304 return self.SimpleTest("chrome", "gfx_unittests")
305
306 def TestUI(self):
307 return self.SimpleTest("chrome", "ui_tests",
308 cmd_args=[
309 "--ui-test-action-timeout=80000",
310 "--ui-test-action-max-timeout=180000"])
311
312 def TestLayoutChunk(self, chunk_num, chunk_size):
313 '''Runs tests [chunk_num*chunk_size .. (chunk_num+1)*chunk_size).
314
315 Wrap around to beginning of list at end. If chunk_size is zero, run all
316 tests in the list once. If a text file is given as argument, it is used as
317 the list of tests.
318 '''
319 # Build the ginormous commandline in 'cmd'.
320 # It's going to be roughly
321 # python asan_test.py ... python run_webkit_tests.py ...
322 # but we'll use the --indirect flag to asan_test.py
323 # to avoid asaning python.
324 # Start by building the asan_test.py commandline.
325 cmd = self._DefaultCommand("webkit")
326
327 # Now build script_cmd, the run_webkits_tests.py commandline
328 # Store each chunk in its own directory so that we can find the data later
329 chunk_dir = os.path.join("layout", "chunk_%05d" % chunk_num)
330 test_shell = os.path.join(self._options.build_dir, "test_shell")
331 out_dir = os.path.join(path_utils.ScriptDir(), "latest")
332 out_dir = os.path.join(out_dir, chunk_dir)
333 if os.path.exists(out_dir):
334 old_files = glob.glob(os.path.join(out_dir, "*.txt"))
335 for f in old_files:
336 os.remove(f)
337 else:
338 os.makedirs(out_dir)
339
340 script = os.path.join(self._source_dir, "webkit", "tools", "layout_tests",
341 "run_webkit_tests.py")
342 script_cmd = ["python", script, "--run-singly", "-v",
343 "--noshow-results", "--time-out-ms=200000",
344 "--nocheck-sys-deps"]
345
346 # Pass build mode to run_webkit_tests.py. We aren't passed it directly,
347 # so parse it out of build_dir. run_webkit_tests.py can only handle
348 # the two values "Release" and "Debug".
349 # TODO(Hercules): unify how all our scripts pass around build mode
350 # (--mode / --target / --build_dir / --debug)
351 if self._options.build_dir.endswith("Debug"):
352 script_cmd.append("--debug");
353 if (chunk_size > 0):
354 script_cmd.append("--run-chunk=%d:%d" % (chunk_num, chunk_size))
355 if len(self._args):
356 # if the arg is a txt file, then treat it as a list of tests
357 if os.path.isfile(self._args[0]) and self._args[0][-4:] == ".txt":
358 script_cmd.append("--test-list=%s" % self._args[0])
359 else:
360 script_cmd.extend(self._args)
361 self._ReadGtestFilterFile("layout", script_cmd)
362
363 # Now run script_cmd with the wrapper in cmd
364 cmd.extend(["--"])
365 cmd.extend(script_cmd)
366 supp = self.Suppressions()
367 return asan_test.RunTool(cmd, supp, "layout")
368
369 def TestLayout(self):
370 '''Runs the layout tests.'''
371 # A "chunk file" is maintained in the local directory so that each test
372 # runs a slice of the layout tests of size chunk_size that increments with
373 # each run. Since tests can be added and removed from the layout tests at
374 # any time, this is not going to give exact coverage, but it will allow us
375 # to continuously run small slices of the layout tests under purify rather
376 # than having to run all of them in one shot.
377 chunk_size = self._options.num_tests
378 if (chunk_size == 0):
379 return self.TestLayoutChunk(0, 0)
380 chunk_num = 0
381 chunk_file = os.path.join("asan_layout_chunk.txt")
382
383 logging.info("Reading state from " + chunk_file)
384 try:
385 f = open(chunk_file)
386 if f:
387 str = f.read()
388 if len(str):
389 chunk_num = int(str)
390 # This should be enough so that we have a couple of complete runs
391 # of test data stored in the archive (although note that when we loop
392 # that we almost guaranteed won't be at the end of the test list)
393 if chunk_num > 10000:
394 chunk_num = 0
395 f.close()
396 except IOError, (errno, strerror):
397 logging.error("error reading from file %s (%d, %s)" % (chunk_file,
398 errno, strerror))
399 ret = self.TestLayoutChunk(chunk_num, chunk_size)
400
401 # Wait until after the test runs to completion to write out the new chunk
402 # number. This way, if the bot is killed, we'll start running again from
403 # the current chunk rather than skipping it.
404 logging.info("Saving state to " + chunk_file)
405 try:
406 f = open(chunk_file, "w")
407 chunk_num += 1
408 f.write("%d" % chunk_num)
409 f.close()
410 except IOError, (errno, strerror):
411 logging.error("error writing to file %s (%d, %s)" % (chunk_file, errno,
412 strerror))
413
414 # Since we're running small chunks of the layout tests, it's important to
415 # mark the ones that have errors in them. These won't be visible in the
416 # summary list for long, but will be useful for someone reviewing this bot.
417 return ret
418
419 def _main(_):
420 parser = optparse.OptionParser("usage: %prog -b <dir> -t <test> "
421 "[-t <test> ...]")
422 parser.disable_interspersed_args()
423 parser.add_option("-b", "--build_dir",
424 help="the location of the output of the compiler output")
425 parser.add_option("-t", "--test", action="append",
426 help="which test to run")
427 parser.add_option("", "--gtest_filter",
428 help="additional arguments to --gtest_filter")
429 parser.add_option("", "--gtest_repeat",
430 help="argument for --gtest_repeat")
431 parser.add_option("-v", "--verbose", action="store_true", default=False,
432 help="verbose output - enable debug log messages")
433 # My machine can do about 120 layout tests/hour in release mode.
434 # Let's do 30 minutes worth per run.
435 # The CPU is mostly idle, so perhaps we can raise this when
436 # we figure out how to run them more efficiently.
437 parser.add_option("-n", "--num_tests", default=60, type="int",
438 help="for layout tests: # of subtests per run. 0 for all.")
439
440 options, args = parser.parse_args()
441
442 if options.verbose:
443 logging_utils.config_root(logging.DEBUG)
444 else:
445 logging_utils.config_root()
446
447 if not options.test or not len(options.test):
448 parser.error("--test not specified")
449
450 for t in options.test:
451 tests = ChromeTests(options, args, t)
452 ret = tests.Run()
453 if ret:
454 return ret
455 return 0
456
457
458 if __name__ == "__main__":
459 if sys.platform.startswith('linux'):
460 ret = _main(sys.argv)
461 elif sys.platform.startswith('darwin'):
462 ret = _main(sys.argv)
463 else:
464 logging.error("AddressSanitizer works only on Linux and Mac OS "
465 "at the moment.")
466 ret = 1
467 sys.exit(ret)
OLDNEW
« no previous file with comments | « tools/asan/base_unittests.gtest-asan.txt ('k') | tools/asan/chrome_tests.sh » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698