| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2009 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2009 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 """Generate and process code coverage on POSIX systems. | 6 """Generate and process code coverage on POSIX systems. |
| 7 | 7 |
| 8 Written for and tested on Mac. | 8 Written for and tested on Mac and Linux. To use this script to |
| 9 Not tested on Linux yet. | 9 generate coverage numbers, please run from within a gyp-generated |
| 10 project. |
| 11 |
| 12 All platforms, to set up coverage: |
| 13 cd ...../chromium ; src/tools/gyp/gyp_dogfood -Dcoverage=1 src/build/all.gyp |
| 14 |
| 15 Run coverage on... |
| 16 Mac: |
| 17 ( cd src/chrome ; xcodebuild -configuration Debug -target coverage ) |
| 18 Linux: |
| 19 ( cd src/chrome ; hammer coverage ) |
| 20 # In particular, don't try and run 'coverage' from src/build |
| 21 |
| 10 | 22 |
| 11 --directory=DIR: specify directory that contains gcda files, and where | 23 --directory=DIR: specify directory that contains gcda files, and where |
| 12 a "coverage" directory will be created containing the output html. | 24 a "coverage" directory will be created containing the output html. |
| 25 Example name: ..../chromium/src/xcodebuild/Debug |
| 13 | 26 |
| 14 TODO(jrg): make list of unit tests an arg to this script | 27 --all_unittests: is present, run all files named *_unittests that we |
| 28 can find. |
| 29 |
| 30 Strings after all options are considered tests to run. Test names |
| 31 have all text before a ':' stripped to help with gyp compatibility. |
| 32 For example, ../base/base.gyp:base_unittests is interpreted as a test |
| 33 named "base_unittests". |
| 15 """ | 34 """ |
| 16 | 35 |
| 36 import glob |
| 17 import logging | 37 import logging |
| 18 import optparse | 38 import optparse |
| 19 import os | 39 import os |
| 40 import shutil |
| 20 import subprocess | 41 import subprocess |
| 21 import sys | 42 import sys |
| 22 | 43 |
| 23 class Coverage(object): | 44 class Coverage(object): |
| 24 """Doitall class for code coverage.""" | 45 """Doitall class for code coverage.""" |
| 25 | 46 |
| 26 # Unit test files to run. | |
| 27 UNIT_TESTS = [ | |
| 28 'base_unittests', | |
| 29 # 'unit_tests, | |
| 30 ] | |
| 31 | |
| 32 def __init__(self, directory): | 47 def __init__(self, directory): |
| 33 super(Coverage, self).__init__() | 48 super(Coverage, self).__init__() |
| 34 self.directory = directory | 49 self.directory = directory |
| 35 self.directory_parent = os.path.dirname(self.directory) | 50 self.directory_parent = os.path.dirname(self.directory) |
| 36 self.output_directory = os.path.join(self.directory, 'coverage') | 51 self.output_directory = os.path.join(self.directory, 'coverage') |
| 37 if not os.path.exists(self.output_directory): | 52 if not os.path.exists(self.output_directory): |
| 38 os.mkdir(self.output_directory) | 53 os.mkdir(self.output_directory) |
| 39 self.lcov_directory = os.path.join(sys.path[0], | 54 self.lcov_directory = os.path.join(sys.path[0], |
| 40 '../../third_party/lcov/bin') | 55 '../../third_party/lcov/bin') |
| 41 self.lcov = os.path.join(self.lcov_directory, 'lcov') | 56 self.lcov = os.path.join(self.lcov_directory, 'lcov') |
| 42 self.mcov = os.path.join(self.lcov_directory, 'mcov') | 57 self.mcov = os.path.join(self.lcov_directory, 'mcov') |
| 43 self.genhtml = os.path.join(self.lcov_directory, 'genhtml') | 58 self.genhtml = os.path.join(self.lcov_directory, 'genhtml') |
| 44 self.coverage_info_file = os.path.join(self.directory, 'coverage.info') | 59 self.coverage_info_file = os.path.join(self.directory, 'coverage.info') |
| 45 self.ConfirmPlatformAndPaths() | 60 self.ConfirmPlatformAndPaths() |
| 61 self.tests = [] |
| 62 |
| 63 def FindTests(self, options, args): |
| 64 """Find unit tests to run; set self.tests to this list. |
| 65 |
| 66 Obtain instructions from the command line seen in the provided |
| 67 parsed options and post-option args. |
| 68 """ |
| 69 # Small tests: can be run in the "chromium" directory. |
| 70 # If asked, run all we can find. |
| 71 if options.all_unittests: |
| 72 self.tests += glob.glob(os.path.join(self.directory, '*_unittests')) |
| 73 |
| 74 # If told explicit tests, run those (after stripping the name as |
| 75 # appropriate) |
| 76 for testname in args: |
| 77 if ':' in testname: |
| 78 self.tests += [os.path.join(self.directory, testname.split(':')[1])] |
| 79 else: |
| 80 self.tests += [os.path.join(self.directory, testname)] |
| 81 |
| 82 # Needs to be run in the "chrome" directory? |
| 83 # ut = os.path.join(self.directory, 'unit_tests') |
| 84 # if os.path.exists(ut): |
| 85 # self.tests.append(ut) |
| 86 # Medium tests? |
| 87 # Not sure all of these work yet (e.g. page_cycler_tests) |
| 88 # self.tests += glob.glob(os.path.join(self.directory, '*_tests')) |
| 46 | 89 |
| 47 def ConfirmPlatformAndPaths(self): | 90 def ConfirmPlatformAndPaths(self): |
| 48 """Confirm OS and paths (e.g. lcov).""" | 91 """Confirm OS and paths (e.g. lcov).""" |
| 49 if not self.IsPosix(): | 92 if not self.IsPosix(): |
| 50 logging.fatal('Not posix.') | 93 logging.fatal('Not posix.') |
| 51 programs = [self.lcov, self.genhtml] | 94 programs = [self.lcov, self.genhtml] |
| 52 if self.IsMac(): | 95 if self.IsMac(): |
| 53 programs.append(self.mcov) | 96 programs.append(self.mcov) |
| 54 for program in programs: | 97 for program in programs: |
| 55 if not os.path.exists(program): | 98 if not os.path.exists(program): |
| 56 logging.fatal('lcov program missing: ' + program) | 99 logging.fatal('lcov program missing: ' + program) |
| 57 | 100 |
| 58 def IsPosix(self): | 101 def IsPosix(self): |
| 59 """Return True if we are POSIX.""" | 102 """Return True if we are POSIX.""" |
| 60 return self.IsMac() or self.IsLinux() | 103 return self.IsMac() or self.IsLinux() |
| 61 | 104 |
| 62 def IsMac(self): | 105 def IsMac(self): |
| 63 return sys.platform == 'darwin' | 106 return sys.platform == 'darwin' |
| 64 | 107 |
| 65 def IsLinux(self): | 108 def IsLinux(self): |
| 66 return sys.platform == 'linux2' | 109 return sys.platform == 'linux2' |
| 67 | 110 |
| 68 def ClearData(self): | 111 def ClearData(self): |
| 69 """Clear old gcda files""" | 112 """Clear old gcda files""" |
| 70 subprocess.call([self.lcov, | 113 subprocess.call([self.lcov, |
| 71 '--directory', self.directory_parent, | 114 '--directory', self.directory_parent, |
| 72 '--zerocounters']) | 115 '--zerocounters']) |
| 116 shutil.rmtree(os.path.join(self.directory, 'coverage')) |
| 73 | 117 |
| 74 def RunTests(self): | 118 def RunTests(self): |
| 75 """Run all unit tests.""" | 119 """Run all unit tests.""" |
| 76 for test in self.UNIT_TESTS: | 120 for fulltest in self.tests: |
| 77 fulltest = os.path.join(self.directory, test) | |
| 78 if not os.path.exists(fulltest): | 121 if not os.path.exists(fulltest): |
| 79 logging.fatal(fulltest + ' does not exist') | 122 logging.fatal(fulltest + ' does not exist') |
| 80 # TODO(jrg): add timeout? | 123 # TODO(jrg): add timeout? |
| 81 # TODO(jrg): check return value and choke if it failed? | 124 # TODO(jrg): check return value and choke if it failed? |
| 82 # TODO(jrg): add --gtest_print_time like as run from XCode? | 125 # TODO(jrg): add --gtest_print_time like as run from XCode? |
| 83 print 'Running test: ' + fulltest | 126 print 'Running test: ' + fulltest |
| 84 # subprocess.call([fulltest, '--gtest_filter=TupleTest*']) # quick check | 127 # subprocess.call([fulltest, '--gtest_filter=TupleTest*']) # quick check |
| 85 subprocess.call([fulltest]) | 128 subprocess.call([fulltest]) |
| 86 | 129 |
| 87 def GenerateOutput(self): | 130 def GenerateOutput(self): |
| (...skipping 18 matching lines...) Expand all Loading... |
| 106 print 'html generation command: ' + ' '.join(command) | 149 print 'html generation command: ' + ' '.join(command) |
| 107 subprocess.call(command) | 150 subprocess.call(command) |
| 108 | 151 |
| 109 def main(): | 152 def main(): |
| 110 parser = optparse.OptionParser() | 153 parser = optparse.OptionParser() |
| 111 parser.add_option('-d', | 154 parser.add_option('-d', |
| 112 '--directory', | 155 '--directory', |
| 113 dest='directory', | 156 dest='directory', |
| 114 default=None, | 157 default=None, |
| 115 help='Directory of unit test files') | 158 help='Directory of unit test files') |
| 159 parser.add_option('-a', |
| 160 '--all_unittests', |
| 161 dest='all_unittests', |
| 162 default=False, |
| 163 help='Run all tests we can find (*_unittests)') |
| 116 (options, args) = parser.parse_args() | 164 (options, args) = parser.parse_args() |
| 117 if not options.directory: | 165 if not options.directory: |
| 118 parser.error('Directory not specified') | 166 parser.error('Directory not specified') |
| 119 | 167 |
| 120 coverage = Coverage(options.directory) | 168 coverage = Coverage(options.directory) |
| 121 coverage.ClearData() | 169 coverage.ClearData() |
| 170 coverage.FindTests(options, args) |
| 122 coverage.RunTests() | 171 coverage.RunTests() |
| 123 coverage.GenerateOutput() | 172 coverage.GenerateOutput() |
| 124 return 0 | 173 return 0 |
| 125 | 174 |
| 126 | 175 |
| 127 if __name__ == '__main__': | 176 if __name__ == '__main__': |
| 128 sys.exit(main()) | 177 sys.exit(main()) |
| OLD | NEW |