Chromium Code Reviews| Index: tools/code_coverage/coverage_posix.py |
| diff --git a/tools/code_coverage/coverage_posix.py b/tools/code_coverage/coverage_posix.py |
| index aaa400aa6293e21a3c4d780144daf31c3c6fbfee..9aef0de9ada0c627ad64146d717449740d39f9b8 100755 |
| --- a/tools/code_coverage/coverage_posix.py |
| +++ b/tools/code_coverage/coverage_posix.py |
| @@ -94,6 +94,7 @@ import logging |
| import optparse |
| import os |
| import Queue |
| +import re |
| import shutil |
| import signal |
| import subprocess |
| @@ -146,6 +147,10 @@ class RunTooLongException(Exception): |
| """Thrown when a command runs too long without output.""" |
| pass |
| +class BadUserInput(Exception): |
| + """Thrown when arguments from the user are incorrectly formatted.""" |
| + pass |
| + |
| class RunProgramThread(threading.Thread): |
| """A thread to run a subprocess. |
| @@ -316,6 +321,8 @@ class Coverage(object): |
| self.ConfirmPlatformAndPaths() |
| self.tests = [] |
| self.xvfb_pid = 0 |
| + self.test_files = [] # List of files with test specifications. |
| + self.test_filters = {} # Mapping from testname->--gtest_filter arg. |
| logging.info('self.directory: ' + self.directory) |
| logging.info('self.directory_parent: ' + self.directory_parent) |
| @@ -437,8 +444,20 @@ class Coverage(object): |
| if self.options.all_unittests: |
| self.tests += glob.glob(os.path.join(self.directory, '*_unittests')) |
| - # Tests can come in as args or as a file of bundles. |
| + # Tests can come in as args directly, indirectly (through a file |
| + # of test lists) or as a file of bundles. |
| all_testnames = self.args[:] # Copy since we might modify |
| + |
| + for test_file in self.options.test_files: |
| + f = open(test_file) |
| + for line in f: |
| + line = re.sub(r"#.*$", "", line) |
| + line = re.sub(r"\s*", "", line) |
| + if re.match("\s*$"): |
| + continue |
| + all_testnames.append(line) |
| + f.close() |
| + |
| tests_from_bundles = None |
| if self.options.bundles: |
| try: |
| @@ -459,10 +478,18 @@ class Coverage(object): |
| # If told explicit tests, run those (after stripping the name as |
| # appropriate) |
| for testname in all_testnames: |
| + mo = re.search(r"(.*)\[(.*)\]$", testname) |
| + gtest_filter = None |
| + if mo: |
| + gtest_filter = mo.group(2) |
| + testname = mo.group(1) |
| + |
| if ':' in testname: |
| - self.tests += [os.path.join(self.directory, testname.split(':')[1])] |
| - else: |
| - self.tests += [os.path.join(self.directory, testname)] |
| + testname = testname.split(':')[1] |
| + self.tests += [os.path.join(self.directory, testname)] |
| + if gtest_filter: |
| + self.test_filters[testname] = gtest_filter |
| + |
| # Medium tests? |
| # Not sure all of these work yet (e.g. page_cycler_tests) |
| # self.tests += glob.glob(os.path.join(self.directory, '*_tests')) |
| @@ -600,17 +627,47 @@ class Coverage(object): |
| Returns: |
| String of the form '--gtest_filter=BLAH', or None. |
| """ |
| - if self.options.no_exclusions: |
| - return |
| - exclusions = excl or gTestExclusions |
| - excldict = exclusions.get(sys.platform) |
| - if excldict: |
| - for test in excldict.keys(): |
| - # example: if base_unittests in ../blah/blah/base_unittests.exe |
| - if test in fulltest: |
| - gfilter = '--gtest_filter=-' + ':-'.join(excldict[test]) |
| - return gfilter |
| - return None |
| + positive_gfilter_list = [] |
| + negative_gfilter_list = [] |
| + |
| + # Exclude all flaky and failing tests; they don't count for code coverage. |
| + negative_gfilter_list += ("*.FLAKY_*", "*.FAILS_*") |
| + |
| + if not self.options.no_exclusions: |
| + exclusions = excl or gTestExclusions |
| + excldict = exclusions.get(sys.platform) |
| + if excldict: |
| + for test in excldict.keys(): |
| + # example: if base_unittests in ../blah/blah/base_unittests.exe |
| + if test in fulltest: |
| + negative_gfilter_list += excldict[test] |
| + |
| + fulltest_basename = os.path.basename(fulltest) |
| + if fulltest_basename in self.test_filters: |
| + specific_test_filters = self.test_filters[fulltest_basename].split("-") |
| + if len(specific_test_filters) > 2: |
| + logging.error("Multiple '-' symbols in filter list: %s" % |
| + self.test_filters[fulltest_basename]) |
| + raise BadUserInput() |
| + if len(specific_test_filters) == 2: |
| + # Remove trailing ":" |
| + specific_test_filters[0] = specific_test_filters[0][:-1] |
| + |
| + if specific_test_filters[0]: # Test for no positive filters. |
| + positive_gfilter_list += specific_test_filters[0].split(":") |
| + if len(specific_test_filters) > 1: |
| + negative_gfilter_list += specific_test_filters[1].split(":") |
| + |
| + if not positive_gfilter_list and not negative_gfilter_list: |
| + return None |
| + |
| + result = "--gtest_filter=" |
|
John Grabowski
2011/11/30 00:52:11
prefer single quotes on strings (multiple spots)
|
| + if positive_gfilter_list: |
| + result += ":".join(positive_gfilter_list) |
| + if negative_gfilter_list: |
| + if positive_gfilter_list: result += ":" |
| + result += "-" + ":".join(negative_gfilter_list) |
| + return result |
| def RunTests(self): |
| """Run all unit tests and generate appropriate lcov files.""" |
| @@ -888,6 +945,13 @@ def CoverageOptionParser(): |
| default=False, |
| action='store_true', |
| help=('Turn off clearing of cov data from a prev run')) |
| + parser.add_option('-F', |
| + '--test-file', |
| + dest="test_files", |
|
John Grabowski
2011/11/30 00:52:11
like this one
|
| + default=[], |
| + action="append", |
|
John Grabowski
2011/11/30 00:52:11
and this one
|
| + help=('Specify a file from which tests to be run will ' + |
| + 'be extracted')) |
| return parser |