Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 # chrome_tests.py | 6 # chrome_tests.py |
| 7 | 7 |
| 8 ''' Runs various chrome tests through valgrind_test.py. | 8 ''' Runs various chrome tests through valgrind_test.py. |
| 9 | 9 |
| 10 This file is a copy of ../purify/chrome_tests.py. Eventually, it would be nice | 10 This file is a copy of ../purify/chrome_tests.py. Eventually, it would be nice |
| 11 to merge these two files. For now, I'm leaving it here with sections that | 11 to merge these two files. |
| 12 aren't supported commented out as this is more of a work in progress. | |
| 13 ''' | 12 ''' |
| 14 | 13 |
| 15 import glob | 14 import glob |
| 16 import logging | 15 import logging |
| 17 import optparse | 16 import optparse |
| 18 import os | 17 import os |
| 19 import stat | 18 import stat |
| 20 import sys | 19 import sys |
| 21 | 20 |
| 22 import google.logging_utils | 21 import google.logging_utils |
| 23 import google.path_utils | 22 import google.path_utils |
| 24 # Import the platform_utils up in the layout tests which have been modified to | 23 # Import the platform_utils up in the layout tests which have been modified to |
| 25 # work under non-Windows platforms instead of the ones that are in the | 24 # work under non-Windows platforms instead of the ones that are in the |
| 26 # tools/python/google directory. (See chrome_tests.sh which sets PYTHONPATH | 25 # tools/python/google directory. (See chrome_tests.sh which sets PYTHONPATH |
| 27 # correctly.) | 26 # correctly.) |
| 28 # | 27 # |
| 29 # TODO(erg): Copy/Move the relevant functions from the layout_package version | 28 # TODO(erg): Copy/Move the relevant functions from the layout_package version |
| 30 # of platform_utils back up to google.platform_utils | 29 # of platform_utils back up to google.platform_utils |
| 31 # package. http://crbug.com/6164 | 30 # package. http://crbug.com/6164 |
| 32 import layout_package.platform_utils | 31 import layout_package.platform_utils |
| 33 | 32 |
| 34 import common | 33 import common |
| 35 | 34 |
| 36 | 35 |
| 37 class TestNotFound(Exception): pass | 36 class TestNotFound(Exception): pass |
| 38 | 37 |
| 39 | 38 |
| 40 class ChromeTests: | 39 class ChromeTests: |
| 41 '''This class is derived from the chrome_tests.py file in ../purify/. | 40 '''This class is derived from the chrome_tests.py file in ../purify/. |
| 42 | |
| 43 TODO(erg): Finish implementing this. I've commented out all the parts that I | |
| 44 don't have working yet. We still need to deal with layout tests, and long | |
| 45 term, the UI tests. | |
| 46 ''' | 41 ''' |
| 47 | 42 |
| 48 def __init__(self, options, args, test): | 43 def __init__(self, options, args, test): |
| 49 # the known list of tests | 44 # the known list of tests |
| 50 self._test_list = { | 45 self._test_list = { |
| 51 "test_shell": self.TestTestShell, | 46 "test_shell": self.TestTestShell, |
| 52 "unit": self.TestUnit, | 47 "unit": self.TestUnit, |
| 53 "net": self.TestNet, | 48 "net": self.TestNet, |
| 54 "ipc": self.TestIpc, | 49 "ipc": self.TestIpc, |
| 55 "base": self.TestBase, | 50 "base": self.TestBase, |
| 56 "googleurl": self.TestGoogleurl, | 51 "googleurl": self.TestGoogleurl, |
| 57 "media": self.TestMedia, | 52 "media": self.TestMedia, |
| 58 "printing": self.TestPrinting, | 53 "printing": self.TestPrinting, |
| 59 # "layout": self.TestLayout, | 54 "layout": self.TestLayout, |
| 60 # "layout_all": self.TestLayoutAll, | |
| 61 "ui": self.TestUI | 55 "ui": self.TestUI |
| 62 } | 56 } |
| 63 | 57 |
| 64 if test not in self._test_list: | 58 if test not in self._test_list: |
| 65 raise TestNotFound("Unknown test: %s" % test) | 59 raise TestNotFound("Unknown test: %s" % test) |
| 66 | 60 |
| 67 self._options = options | 61 self._options = options |
| 68 self._args = args | 62 self._args = args |
| 69 self._test = test | 63 self._test = test |
| 70 | 64 |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 136 cmd.append("--show_all_leaks") | 130 cmd.append("--show_all_leaks") |
| 137 if self._options.track_origins: | 131 if self._options.track_origins: |
| 138 cmd.append("--track_origins") | 132 cmd.append("--track_origins") |
| 139 if self._options.generate_suppressions: | 133 if self._options.generate_suppressions: |
| 140 cmd.append("--generate_suppressions") | 134 cmd.append("--generate_suppressions") |
| 141 if exe == "ui_tests": | 135 if exe == "ui_tests": |
| 142 cmd.append("--trace_children") | 136 cmd.append("--trace_children") |
| 143 cmd.append("--indirect") | 137 cmd.append("--indirect") |
| 144 if exe: | 138 if exe: |
| 145 cmd.append(os.path.join(self._options.build_dir, exe)) | 139 cmd.append(os.path.join(self._options.build_dir, exe)) |
| 146 # Valgrind runs tests slowly, so slow tests hurt more; show elapased time | 140 # Valgrind runs tests slowly, so slow tests hurt more; show elapased time |
| 147 # so we can find the slowpokes. | 141 # so we can find the slowpokes. |
| 148 cmd.append("--gtest_print_time"); | 142 cmd.append("--gtest_print_time") |
| 149 return cmd | 143 return cmd |
| 150 | 144 |
| 151 def Run(self): | 145 def Run(self): |
| 152 ''' Runs the test specified by command-line argument --test ''' | 146 ''' Runs the test specified by command-line argument --test ''' |
| 153 logging.info("running test %s" % (self._test)) | 147 logging.info("running test %s" % (self._test)) |
| 154 return self._test_list[self._test]() | 148 return self._test_list[self._test]() |
| 155 | 149 |
| 156 def _ReadGtestFilterFile(self, name, cmd): | 150 def _ReadGtestFilterFile(self, name, cmd): |
| 157 '''Read a file which is a list of tests to filter out with --gtest_filter | 151 '''Read a file which is a list of tests to filter out with --gtest_filter |
| 158 and append the command-line option to cmd. | 152 and append the command-line option to cmd. |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 179 if gtest_filter: | 173 if gtest_filter: |
| 180 cmd.append("--gtest_filter=%s" % gtest_filter) | 174 cmd.append("--gtest_filter=%s" % gtest_filter) |
| 181 | 175 |
| 182 def SimpleTest(self, module, name, cmd_args=None): | 176 def SimpleTest(self, module, name, cmd_args=None): |
| 183 cmd = self._DefaultCommand(module, name) | 177 cmd = self._DefaultCommand(module, name) |
| 184 self._ReadGtestFilterFile(name, cmd) | 178 self._ReadGtestFilterFile(name, cmd) |
| 185 if cmd_args: | 179 if cmd_args: |
| 186 cmd.extend(cmd_args) | 180 cmd.extend(cmd_args) |
| 187 return common.RunSubprocess(cmd, 0) | 181 return common.RunSubprocess(cmd, 0) |
| 188 | 182 |
| 189 def ScriptedTest(self, module, exe, name, script, multi=False, cmd_args=None, | |
| 190 out_dir_extra=None): | |
| 191 '''Valgrind a target binary, which will be executed one or more times via a | |
| 192 script or driver program. | |
| 193 Args: | |
| 194 module - which top level component this test is from (webkit, base, etc.) | |
| 195 exe - the name of the exe (it's assumed to exist in build_dir) | |
| 196 name - the name of this test (used to name output files) | |
| 197 script - the driver program or script. If it's python.exe, we use | |
| 198 search-path behavior to execute, otherwise we assume that it is in | |
| 199 build_dir. | |
| 200 multi - a boolean hint that the exe will be run multiple times, generating | |
| 201 multiple output files (without this option, only the last run will be | |
| 202 recorded and analyzed) | |
| 203 cmd_args - extra arguments to pass to the valgrind_test.py script | |
| 204 ''' | |
| 205 cmd = self._DefaultCommand(module) | |
| 206 exe = os.path.join(self._options.build_dir, exe) | |
| 207 cmd.append("--exe=%s" % exe) | |
| 208 cmd.append("--name=%s" % name) | |
| 209 if multi: | |
| 210 out = os.path.join(google.path_utils.ScriptDir(), | |
| 211 "latest") | |
| 212 if out_dir_extra: | |
| 213 out = os.path.join(out, out_dir_extra) | |
| 214 if os.path.exists(out): | |
| 215 old_files = glob.glob(os.path.join(out, "*.txt")) | |
| 216 for f in old_files: | |
| 217 os.remove(f) | |
| 218 else: | |
| 219 os.makedirs(out) | |
| 220 out = os.path.join(out, "%s%%5d.txt" % name) | |
| 221 cmd.append("--out_file=%s" % out) | |
| 222 if cmd_args: | |
| 223 cmd.extend(cmd_args) | |
| 224 if script[0] != "python.exe" and not os.path.exists(script[0]): | |
| 225 script[0] = os.path.join(self._options.build_dir, script[0]) | |
| 226 cmd.extend(script) | |
| 227 self._ReadGtestFilterFile(name, cmd) | |
| 228 return common.RunSubprocess(cmd, 0) | |
| 229 | |
| 230 def TestBase(self): | 183 def TestBase(self): |
| 231 return self.SimpleTest("base", "base_unittests") | 184 return self.SimpleTest("base", "base_unittests") |
| 232 | 185 |
| 233 def TestGoogleurl(self): | 186 def TestGoogleurl(self): |
| 234 return self.SimpleTest("chrome", "googleurl_unittests") | 187 return self.SimpleTest("chrome", "googleurl_unittests") |
| 235 | 188 |
| 236 def TestMedia(self): | 189 def TestMedia(self): |
| 237 return self.SimpleTest("chrome", "media_unittests") | 190 return self.SimpleTest("chrome", "media_unittests") |
| 238 | 191 |
| 239 def TestPrinting(self): | 192 def TestPrinting(self): |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 251 def TestUnit(self): | 204 def TestUnit(self): |
| 252 return self.SimpleTest("chrome", "unit_tests") | 205 return self.SimpleTest("chrome", "unit_tests") |
| 253 | 206 |
| 254 def TestUI(self): | 207 def TestUI(self): |
| 255 return self.SimpleTest("chrome", "ui_tests", | 208 return self.SimpleTest("chrome", "ui_tests", |
| 256 cmd_args=["--", | 209 cmd_args=["--", |
| 257 "--ui-test-timeout=120000", | 210 "--ui-test-timeout=120000", |
| 258 "--ui-test-action-timeout=80000", | 211 "--ui-test-action-timeout=80000", |
| 259 "--ui-test-action-max-timeout=180000"]) | 212 "--ui-test-action-max-timeout=180000"]) |
| 260 | 213 |
| 261 # def TestLayoutAll(self): | 214 def TestLayoutChunk(self, chunk_num, chunk_size): |
| 262 # return self.TestLayout(run_all=True) | 215 # Run tests [chunk_num*chunk_size .. (chunk_num+1)*chunk_size) from the |
| 216 # list of tests. Wrap around to beginning of list at end. | |
| 217 # If chunk_size is zero, run all tests in the list once. | |
| 218 # If a text file is given as argument, it is used as the list of tests. | |
|
Erik does not do reviews
2009/03/30 16:22:48
clarify this comment to refer explicitly to comman
| |
| 219 # | |
| 220 # Build the ginormous commandline in 'cmd'. | |
| 221 # It's going to be roughly | |
| 222 # python valgrind_test.py ... python run_webkit_tests.py ... | |
| 223 # but we'll use the --indirect flag to valgrind_test.py | |
| 224 # to avoid valgrinding python. | |
| 225 # Start by building the valgrind_test.py commandline. | |
| 226 cmd = self._DefaultCommand("webkit") | |
| 227 cmd.append("--trace_children") | |
| 228 cmd.append("--indirect") | |
| 229 # Now build script_cmd, the run_webkits_tests.py commandline | |
| 230 # Store each chunk in its own directory so that we can find the data later | |
| 231 chunk_dir = os.path.join("layout", "chunk_%05d" % chunk_num) | |
| 232 test_shell = os.path.join(self._options.build_dir, "test_shell") | |
| 233 out_dir = os.path.join(google.path_utils.ScriptDir(), "latest") | |
| 234 out_dir = os.path.join(out_dir, chunk_dir) | |
| 235 if os.path.exists(out_dir): | |
| 236 old_files = glob.glob(os.path.join(out_dir, "*.txt")) | |
| 237 for f in old_files: | |
| 238 os.remove(f) | |
| 239 else: | |
| 240 os.makedirs(out_dir) | |
| 241 script = os.path.join(self._source_dir, "webkit", "tools", "layout_tests", | |
| 242 "run_webkit_tests.py") | |
| 243 script_cmd = ["python", script, "--run-singly", "-v", | |
| 244 "--noshow-results", "--time-out-ms=200000", | |
| 245 "--nocheck-sys-deps"] | |
| 246 if (chunk_size > 0): | |
| 247 script_cmd.append("--run-chunk=%d:%d" % (chunk_num, chunk_size)) | |
| 248 if len(self._args): | |
| 249 # if the arg is a txt file, then treat it as a list of tests | |
| 250 if os.path.isfile(self._args[0]) and self._args[0][-4:] == ".txt": | |
| 251 script_cmd.append("--test-list=%s" % self._args[0]) | |
| 252 else: | |
| 253 script_cmd.extend(self._args) | |
| 254 self._ReadGtestFilterFile("layout", script_cmd) | |
| 255 # Now run script_cmd with the wrapper in cmd | |
| 256 cmd.extend(["--"]) | |
| 257 cmd.extend(script_cmd) | |
| 258 ret = common.RunSubprocess(cmd, 0) | |
| 259 return ret | |
| 263 | 260 |
| 264 # def TestLayout(self, run_all=False): | 261 def TestLayout(self): |
| 265 # # A "chunk file" is maintained in the local directory so that each test | 262 # A "chunk file" is maintained in the local directory so that each test |
| 266 # # runs a slice of the layout tests of size chunk_size that increments with | 263 # runs a slice of the layout tests of size chunk_size that increments with |
| 267 # # each run. Since tests can be added and removed from the layout tests at | 264 # each run. Since tests can be added and removed from the layout tests at |
| 268 # # any time, this is not going to give exact coverage, but it will allow us | 265 # any time, this is not going to give exact coverage, but it will allow us |
| 269 # # to continuously run small slices of the layout tests under purify rather | 266 # to continuously run small slices of the layout tests under purify rather |
| 270 # # than having to run all of them in one shot. | 267 # than having to run all of them in one shot. |
| 271 # chunk_num = 0 | 268 chunk_size = self._options.num_tests |
| 272 # # Tests currently seem to take about 20-30s each. | 269 if (chunk_size == 0): |
| 273 # chunk_size = 120 # so about 40-60 minutes per run | 270 return self.TestLayoutChunk(0, 0) |
| 274 # chunk_file = os.path.join(os.environ["TEMP"], "purify_layout_chunk.txt") | 271 chunk_num = 0 |
| 275 # if not run_all: | 272 chunk_file = os.path.join("valgrind_layout_chunk.txt") |
| 276 # try: | 273 logging.info("Reading state from " + chunk_file) |
| 277 # f = open(chunk_file) | 274 try: |
| 278 # if f: | 275 f = open(chunk_file) |
| 279 # str = f.read() | 276 if f: |
| 280 # if len(str): | 277 str = f.read() |
| 281 # chunk_num = int(str) | 278 if len(str): |
| 282 # # This should be enough so that we have a couple of complete runs | 279 chunk_num = int(str) |
| 283 # # of test data stored in the archive (although note that when we loo p | 280 # This should be enough so that we have a couple of complete runs |
| 284 # # that we almost guaranteed won't be at the end of the test list) | 281 # of test data stored in the archive (although note that when we loop |
| 285 # if chunk_num > 10000: | 282 # that we almost guaranteed won't be at the end of the test list) |
| 286 # chunk_num = 0 | 283 if chunk_num > 10000: |
| 287 # f.close() | 284 chunk_num = 0 |
| 288 # except IOError, (errno, strerror): | 285 f.close() |
| 289 # logging.error("error reading from file %s (%d, %s)" % (chunk_file, | 286 except IOError, (errno, strerror): |
| 290 # errno, strerror)) | 287 logging.error("error reading from file %s (%d, %s)" % (chunk_file, |
| 291 | 288 errno, strerror)) |
| 292 # script = os.path.join(self._source_dir, "webkit", "tools", "layout_tests", | 289 ret = self.TestLayoutChunk(chunk_num, chunk_size) |
| 293 # "run_webkit_tests.py") | 290 # Wait until after the test runs to completion to write out the new chunk |
| 294 # script_cmd = ["python.exe", script, "--run-singly", "-v", | 291 # number. This way, if the bot is killed, we'll start running again from |
| 295 # "--noshow-results", "--time-out-ms=200000", | 292 # the current chunk rather than skipping it. |
| 296 # "--nocheck-sys-deps"] | 293 logging.info("Saving state to " + chunk_file) |
| 297 # if not run_all: | 294 try: |
| 298 # script_cmd.append("--run-chunk=%d:%d" % (chunk_num, chunk_size)) | 295 f = open(chunk_file, "w") |
| 299 | 296 chunk_num += 1 |
| 300 # if len(self._args): | 297 f.write("%d" % chunk_num) |
| 301 # # if the arg is a txt file, then treat it as a list of tests | 298 f.close() |
| 302 # if os.path.isfile(self._args[0]) and self._args[0][-4:] == ".txt": | 299 except IOError, (errno, strerror): |
| 303 # script_cmd.append("--test-list=%s" % self._args[0]) | 300 logging.error("error writing to file %s (%d, %s)" % (chunk_file, errno, |
| 304 # else: | 301 strerror)) |
| 305 # script_cmd.extend(self._args) | 302 # Since we're running small chunks of the layout tests, it's important to |
| 306 | 303 # mark the ones that have errors in them. These won't be visible in the |
| 307 # if run_all: | 304 # summary list for long, but will be useful for someone reviewing this bot. |
| 308 # ret = self.ScriptedTest("webkit", "test_shell.exe", "layout", | 305 return ret |
| 309 # script_cmd, multi=True, cmd_args=["--timeout=0"] ) | |
| 310 # return ret | |
| 311 | |
| 312 # # store each chunk in its own directory so that we can find the data later | |
| 313 # chunk_dir = os.path.join("layout", "chunk_%05d" % chunk_num) | |
| 314 # ret = self.ScriptedTest("webkit", "test_shell.exe", "layout", | |
| 315 # script_cmd, multi=True, cmd_args=["--timeout=0"], | |
| 316 # out_dir_extra=chunk_dir) | |
| 317 | |
| 318 # # Wait until after the test runs to completion to write out the new chunk | |
| 319 # # number. This way, if the bot is killed, we'll start running again from | |
| 320 # # the current chunk rather than skipping it. | |
| 321 # try: | |
| 322 # f = open(chunk_file, "w") | |
| 323 # chunk_num += 1 | |
| 324 # f.write("%d" % chunk_num) | |
| 325 # f.close() | |
| 326 # except IOError, (errno, strerror): | |
| 327 # logging.error("error writing to file %s (%d, %s)" % (chunk_file, errno, | |
| 328 # strerror)) | |
| 329 # # Since we're running small chunks of the layout tests, it's important to | |
| 330 # # mark the ones that have errors in them. These won't be visible in the | |
| 331 # # summary list for long, but will be useful for someone reviewing this bot . | |
| 332 # return ret | |
| 333 | |
| 334 # def TestUI(self): | |
| 335 # if not self._options.no_reinstrument: | |
| 336 # instrumentation_error = self.InstrumentDll() | |
| 337 # if instrumentation_error: | |
| 338 # return instrumentation_error | |
| 339 # return self.ScriptedTest("chrome", "chrome.exe", "ui_tests", | |
| 340 # ["ui_tests.exe", | |
| 341 # "--single-process", | |
| 342 # "--ui-test-timeout=120000", | |
| 343 # "--ui-test-action-timeout=80000", | |
| 344 # "--ui-test-action-max-timeout=180000"], | |
| 345 # multi=True) | |
| 346 | |
| 347 | 306 |
| 348 def _main(_): | 307 def _main(_): |
| 349 parser = optparse.OptionParser("usage: %prog -b <dir> -t <test> " | 308 parser = optparse.OptionParser("usage: %prog -b <dir> -t <test> " |
| 350 "[-t <test> ...]") | 309 "[-t <test> ...]") |
| 351 parser.disable_interspersed_args() | 310 parser.disable_interspersed_args() |
| 352 parser.add_option("-b", "--build_dir", | 311 parser.add_option("-b", "--build_dir", |
| 353 help="the location of the output of the compiler output") | 312 help="the location of the output of the compiler output") |
| 354 parser.add_option("-t", "--test", action="append", | 313 parser.add_option("-t", "--test", action="append", |
| 355 help="which test to run") | 314 help="which test to run") |
| 356 parser.add_option("", "--baseline", action="store_true", default=False, | 315 parser.add_option("", "--baseline", action="store_true", default=False, |
| 357 help="generate baseline data instead of validating") | 316 help="generate baseline data instead of validating") |
| 358 parser.add_option("", "--gtest_filter", | 317 parser.add_option("", "--gtest_filter", |
| 359 help="additional arguments to --gtest_filter") | 318 help="additional arguments to --gtest_filter") |
| 360 parser.add_option("-v", "--verbose", action="store_true", default=False, | 319 parser.add_option("-v", "--verbose", action="store_true", default=False, |
| 361 help="verbose output - enable debug log messages") | 320 help="verbose output - enable debug log messages") |
| 362 parser.add_option("", "--show_all_leaks", action="store_true", | 321 parser.add_option("", "--show_all_leaks", action="store_true", |
| 363 default=False, | 322 default=False, |
| 364 help="also show even less blatant leaks") | 323 help="also show even less blatant leaks") |
| 365 parser.add_option("", "--track_origins", action="store_true", | 324 parser.add_option("", "--track_origins", action="store_true", |
| 366 default=False, | 325 default=False, |
| 367 help="Show whence uninit bytes came. 30% slower.") | 326 help="Show whence uninit bytes came. 30% slower.") |
| 368 parser.add_option("", "--no-reinstrument", action="store_true", default=False, | 327 parser.add_option("", "--no-reinstrument", action="store_true", default=False, |
| 369 help="Don't force a re-instrumentation for ui_tests") | 328 help="Don't force a re-instrumentation for ui_tests") |
| 370 parser.add_option("", "--generate_suppressions", action="store_true", | 329 parser.add_option("", "--generate_suppressions", action="store_true", |
| 371 default=False, | 330 default=False, |
| 372 help="Skip analysis and generate suppressions") | 331 help="Skip analysis and generate suppressions") |
| 332 # My machine can do about 120 layout tests/hour in release mode. | |
| 333 # Let's do 30 minutes worth per run. | |
| 334 # The CPU is mostly idle, so perhaps we can raise this when | |
| 335 # we figure out how to run them more efficiently. | |
| 336 parser.add_option("-n", "--num_tests", default=60, type="int", | |
| 337 help="for layout tests: number of subtests per run. 0 for a ll.") | |
|
Erik does not do reviews
2009/03/30 16:22:48
80 columns
| |
| 373 | 338 |
| 374 options, args = parser.parse_args() | 339 options, args = parser.parse_args() |
| 375 | 340 |
| 376 if options.verbose: | 341 if options.verbose: |
| 377 google.logging_utils.config_root(logging.DEBUG) | 342 google.logging_utils.config_root(logging.DEBUG) |
| 378 else: | 343 else: |
| 379 google.logging_utils.config_root() | 344 google.logging_utils.config_root() |
| 380 | 345 |
| 381 if not options.test or not len(options.test): | 346 if not options.test or not len(options.test): |
| 382 parser.error("--test not specified") | 347 parser.error("--test not specified") |
| 383 | 348 |
| 384 for t in options.test: | 349 for t in options.test: |
| 385 tests = ChromeTests(options, args, t) | 350 tests = ChromeTests(options, args, t) |
| 386 ret = tests.Run() | 351 ret = tests.Run() |
| 387 if ret: return ret | 352 if ret: return ret |
| 388 return 0 | 353 return 0 |
| 389 | 354 |
| 390 | 355 |
| 391 if __name__ == "__main__": | 356 if __name__ == "__main__": |
| 392 ret = _main(sys.argv) | 357 ret = _main(sys.argv) |
| 393 sys.exit(ret) | 358 sys.exit(ret) |
| OLD | NEW |