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 |