diff options
-rw-r--r-- | chrome/chrome_tests.gypi | 116 | ||||
-rwxr-xr-x | tools/code_coverage/coverage_posix.py | 31 | ||||
-rwxr-xr-x | tools/code_coverage/coverage_posix_unittest.py | 58 |
3 files changed, 179 insertions, 26 deletions
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index ceaa7e2..a9c0dec 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -2320,17 +2320,56 @@ }, # target 'pyautolib' ] # targets }], + # To enable the coverage targets, do + # GYP_DEFINES='coverage=1' gclient sync + # To match the coverage buildbot more closely, do this: + # GYP_DEFINES='coverage=1 enable_svg=0 fastbuild=1' gclient sync + # (and, on MacOS, be sure to switch your SDK from "Base SDK" to "Mac OS X 10.6") ['coverage!=0', { 'targets': [ { + ### Coverage BUILD AND RUN. + ### Not named coverage_build_and_run for historical reasons. 'target_name': 'coverage', + 'dependencies': [ 'coverage_build', 'coverage_run' ], + # do NOT place this in the 'all' list; most won't want it. + # In gyp, booleans are 0/1 not True/False. + 'suppress_wildcard': 1, + 'type': 'none', + 'actions': [ + { + 'message': 'Coverage is now complete.', + # MSVS must have an input file and an output file. + 'inputs': [ '<(PRODUCT_DIR)/coverage.info' ], + 'outputs': [ '<(PRODUCT_DIR)/coverage-build-and-run.stamp' ], + 'action_name': 'coverage', + # Wish gyp had some basic builtin commands (e.g. 'touch'). + 'action': [ 'python', '-c', + 'import os; ' + + 'open(' + + '\'<(PRODUCT_DIR)\' + os.path.sep + ' + + '\'coverage-build-and-run.stamp\'' + + ', \'w\').close()' ], + # Use outputs of this action as inputs for the main target build. + # Seems as a misnomer but makes this happy on Linux (scons). + 'process_outputs_as_sources': 1, + }, + ], # 'actions' + }, + ### Coverage BUILD. Compile only; does not run the bundles. + ### Intended as the build phase for our coverage bots. + ### + ### Builds unit test bundles needed for coverage. + ### Outputs this list of bundles into coverage_bundles.py. + ### + ### If you want to both build and run coverage from your IDE, + ### use the 'coverage' target. + { + 'target_name': 'coverage_build', # do NOT place this in the 'all' list; most won't want it. # In gyp, booleans are 0/1 not True/False. 'suppress_wildcard': 1, 'type': 'none', - # Cross platform test bundles. If you add new tests you may - # need to update the croc configs. For example, see the - # first regexp in build/(linux|mac|win)/chrome_*.croc. 'dependencies': [ 'automated_ui_tests', '../app/app.gyp:app_unittests', @@ -2347,49 +2386,78 @@ # investigate why. # 'ui_tests', 'unit_tests', - ], - # Platform specific unit test bundles. Unless staging - # a checkin, please add a comment describing why your test is - # in here and is not cross-platform. + ], # 'dependencies' 'conditions': [ ['OS=="win"', { - 'dependencies': [ + 'coverage_bundles': [ # Courgette has not been ported from Windows. # Note build/win/chrome_win.croc uniquely has the # courgette source directory in an include path. '../courgette/courgette.gyp:courgette_unittests', - ], - }], + ]}], ['OS=="linux"', { - 'dependencies': [ - # Placeholder; empty for now. - ], - }], + 'coverage_bundles': [ + # Placeholder; empty for now. + ]}], ['OS=="mac"', { - 'dependencies': [ - # Placeholder; empty for now. - ], - }], - ], - + 'coverage_bundles': [ + # Placeholder; empty for now. + ]}], + ], # 'conditions' 'actions': [ { # 'message' for Linux/scons in particular. Scons # requires the 'coverage' target be run from within # src/chrome. - 'message': 'Running coverage_posix.py to generate coverage numbers', + 'message': 'Compiling coverage bundles.', # MSVS must have an input file and an output file. 'inputs': [ '../tools/code_coverage/coverage_posix.py' ], + 'outputs': [ '<(PRODUCT_DIR)/coverage_bundles.py' ], + 'action_name': 'coverage_build', + 'action': [ 'python', '-c', + 'import os; ' + 'f = open(' + + '\'<(PRODUCT_DIR)\' + os.path.sep + ' + + '\'coverage_bundles.py\'' + + ', \'w\'); ' + + 'deplist = \'' + '<@(_dependencies)' + '\'.split(\' \'); ' + + 'f.write(str(deplist)); ' + + 'f.close()'], + # Use outputs of this action as inputs for the main target build. + # Seems as a misnomer but makes this happy on Linux (scons). + 'process_outputs_as_sources': 1, + }, + ], # 'actions' + }, + ### Coverage RUN. Does not compile the bundles. Mirrors the + ### run_coverage_bundles buildbot phase. If you update this + ### command update the mirror in + ### $BUILDBOT/scripts/master/factory/chromium_commands.py. + ### If you want both build and run, use the 'coverage' target. + { + 'target_name': 'coverage_run', + # do NOT place this in the 'all' list; most won't want it. + # In gyp, booleans are 0/1 not True/False. + 'suppress_wildcard': 1, + 'type': 'none', + 'actions': [ + { + # 'message' for Linux/scons in particular. Scons + # requires the 'coverage' target be run from within + # src/chrome. + 'message': 'Running the coverage script. NOT building anything.', + # MSVS must have an input file and an output file. + 'inputs': [ '<(PRODUCT_DIR)/coverage_bundles.py' ], 'outputs': [ '<(PRODUCT_DIR)/coverage.info' ], - 'action_name': 'coverage', + 'action_name': 'coverage_run', 'action': [ 'python', '../tools/code_coverage/coverage_posix.py', '--directory', '<(PRODUCT_DIR)', '--src_root', '..', - '--', - '<@(_dependencies)'], + '--bundles', + '<(PRODUCT_DIR)/coverage_bundles.py'], # Use outputs of this action as inputs for the main target build. # Seems as a misnomer but makes this happy on Linux (scons). 'process_outputs_as_sources': 1, diff --git a/tools/code_coverage/coverage_posix.py b/tools/code_coverage/coverage_posix.py index 340199d..b018ae4 100755 --- a/tools/code_coverage/coverage_posix.py +++ b/tools/code_coverage/coverage_posix.py @@ -48,6 +48,11 @@ Linux: --timeout=SECS: if a subprocess doesn't have output within SECS, assume it's a hang. Kill it and give up. +--bundles=BUNDLEFILE: a file containing a python list of coverage + bundles to be eval'd. E.g. + ['../base/base.gyp:base_unittests'] + This is used as part of the coverage bot. + Strings after all options are considered tests to run. Test names have all text before a ':' stripped to help with gyp compatibility. For example, ../base/base.gyp:base_unittests is interpreted as a test @@ -235,9 +240,28 @@ 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. + all_testnames = self.args[:] # Copy since we might modify + tests_from_bundles = None + if self.options.bundles: + try: + tests_from_bundles = eval(open(self.options.bundles).read()) + except IOError: + logging.fatal('IO error in bundle file ' + + self.options.bundles + ' (doesn\'t exist?)') + except NameError, SyntaxError: + logging.fatal('Parse or syntax error in bundle file ' + + self.options.bundles) + if hasattr(tests_from_bundles, '__iter__'): + all_testnames += tests_from_bundles + else: + logging.fatal('Fatal error with bundle file; could not get list from' + + self.options.bundles) + sys.exit(1) + # If told explicit tests, run those (after stripping the name as # appropriate) - for testname in self.args: + for testname in all_testnames: if ':' in testname: self.tests += [os.path.join(self.directory, testname.split(':')[1])] else: @@ -582,6 +606,11 @@ def CoverageOptionParser(): default=9.9 * 60.0, help='Timeout before bailing if a subprocess has no output.' ' Default is a hair under 10min (Buildbot is 10min.)') + parser.add_option('-B', + '--bundles', + dest='bundles', + default=None, + help='Filename of bundles for coverage.') return parser diff --git a/tools/code_coverage/coverage_posix_unittest.py b/tools/code_coverage/coverage_posix_unittest.py index e7ae155..9eee129 100755 --- a/tools/code_coverage/coverage_posix_unittest.py +++ b/tools/code_coverage/coverage_posix_unittest.py @@ -3,16 +3,52 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -"""Unit tests for coverage_posix.py.""" +"""Unit tests for coverage_posix.py. + +Run a single test with a command such as: + ./coverage_posix_unittest.py CoveragePosixTest.testFindTestsAsArgs + +Waring that running a single test like that may interfere with the arg +parsing tests, since coverage_posix.py uses optparse.OptionParser() +which references globals. +""" import coverage_posix as coverage +import os import sys +import tempfile import unittest class CoveragePosixTest(unittest.TestCase): + def setUp(self): self.parseArgs() + self.sample_test_names = ['zippy_tests', '../base/base.gyp:base_unittests'] + + def confirmSampleTestsArePresent(self, tests): + """Confirm the tests in self.sample_test_names are in some form in 'tests'. + + The Coverage object can munge them (e.g. add .exe to the end as needed. + Helper function for arg parsing, bundle file tests. + + Args: + tests: the parsed tests from a Coverage object. + """ + for simple_test_name in ('zippy_tests', 'base_unittests'): + found = False + for item in tests: + if simple_test_name in item: + found = True + break + self.assertTrue(found) + for not_test_name in ('kablammo', 'not_a_unittest'): + found = False + for item in tests: + if not_test_name in item: + found = True + break + self.assertFalse(found) def parseArgs(self): """Setup and process arg parsing.""" @@ -67,6 +103,26 @@ class CoveragePosixTest(unittest.TestCase): c.Run, [sys.executable, '-u', '-c', slowscript]) + def testFindTestsAsArgs(self): + """Test finding of tests passed as args.""" + self.args += '--' + self.args += self.sample_test_names + c = coverage.Coverage('.', self.options, self.args) + c.FindTests() + self.confirmSampleTestsArePresent(c.tests) + + def testFindTestsFromBundleFile(self): + """Test finding of tests from a bundlefile.""" + (fd, filename) = tempfile.mkstemp() + f = os.fdopen(fd, 'w') + f.write(str(self.sample_test_names)) + f.close() + self.options.bundles = filename + c = coverage.Coverage('.', self.options, self.args) + c.FindTests() + self.confirmSampleTestsArePresent(c.tests) + os.unlink(filename) + if __name__ == '__main__': unittest.main() |