OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 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 """Runs Go unit tests verifying code coverage. | 6 """Runs Go unit tests verifying code coverage. |
7 | 7 |
8 Expects Go toolset to be in PATH, GOPATH and GOROOT correctly set. Use ./env.py | 8 Expects Go toolset to be in PATH, GOPATH and GOROOT correctly set. Use ./env.py |
9 to set them up. | 9 to set them up. |
10 | 10 |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
74 | 74 |
75 def makedirs(path): | 75 def makedirs(path): |
76 """Same as os.makedirs, but doesn't fail if path exists.""" | 76 """Same as os.makedirs, but doesn't fail if path exists.""" |
77 try: | 77 try: |
78 os.makedirs(path) | 78 os.makedirs(path) |
79 except OSError as err: | 79 except OSError as err: |
80 if err.errno != errno.EEXIST: | 80 if err.errno != errno.EEXIST: |
81 raise | 81 raise |
82 | 82 |
83 | 83 |
| 84 def get_goos(): |
| 85 """Converts sys.platform to GOOS value.""" |
| 86 if sys.platform.startswith('win'): |
| 87 return 'windows' |
| 88 if sys.platform.startswith('darwin'): |
| 89 return 'darwin' |
| 90 if sys.platform.startswith('linux'): |
| 91 return 'linux' |
| 92 raise ValueError('Unrecognized platform: %s' % sys.platform) |
| 93 |
| 94 |
84 _package_info_cache = {} | 95 _package_info_cache = {} |
85 | 96 |
86 def get_package_info(package): | 97 def get_package_info(package): |
87 """Returns contents of <package>/<name>.infra_testing file or {} if missing. | 98 """Returns contents of <package>/<name>.infra_testing file or {} if missing. |
88 | 99 |
89 *.infra_testing contains a JSON dict with the following keys: | 100 *.infra_testing contains a JSON dict with the following keys: |
90 { | 101 { |
91 // Do not run tests in this package at all. Default 'false'. | 102 // Do not run tests in this package at all. Default 'false'. |
92 "skip_testing": boolean, | 103 "skip_testing": a list of platforms (GOOS) to skip tests on, |
93 // Minimum allowed code coverage percentage, see below. Default '100'. | 104 // Minimum allowed code coverage percentage, see below. Default '100'. |
94 "expected_coverage_min": number, | 105 "expected_coverage_min": number, |
95 // Maximum allowed code coverage percentage, see below. Default '100'. | 106 // Maximum allowed code coverage percentage, see below. Default '100'. |
96 "expected_coverage_max": number | 107 "expected_coverage_max": number |
97 } | 108 } |
98 | 109 |
99 expected_coverage_min and expected_coverage_max set a boundary on what the | 110 expected_coverage_min and expected_coverage_max set a boundary on what the |
100 code coverage percentage of the package is expected to be. test.py will fail | 111 code coverage percentage of the package is expected to be. test.py will fail |
101 if code coverage is less than 'expected_coverage_min' (meaning the package | 112 if code coverage is less than 'expected_coverage_min' (meaning the package |
102 code has degraded), or larger than 'expected_coverage_max' (meaning the | 113 code has degraded), or larger than 'expected_coverage_max' (meaning the |
(...skipping 30 matching lines...) Expand all Loading... |
133 print >> sys.stderr, 'Not a valid JSON file: %s' % info_file | 144 print >> sys.stderr, 'Not a valid JSON file: %s' % info_file |
134 return {} | 145 return {} |
135 | 146 |
136 if package not in _package_info_cache: | 147 if package not in _package_info_cache: |
137 _package_info_cache[package] = do_read() | 148 _package_info_cache[package] = do_read() |
138 return _package_info_cache[package] | 149 return _package_info_cache[package] |
139 | 150 |
140 | 151 |
141 def should_skip(package): | 152 def should_skip(package): |
142 """True to skip package tests, reads 'skip_testing' from *.infra_testing.""" | 153 """True to skip package tests, reads 'skip_testing' from *.infra_testing.""" |
143 return get_package_info(package).get('skip_testing', False) | 154 skip = get_package_info(package).get('skip_testing', []) |
| 155 if not isinstance(skip, list): |
| 156 raise TypeError( |
| 157 '%s: "skip_testing" should be a list of platforms to skip tests on, ' |
| 158 'got %r instead' % (package, skip)) |
| 159 return get_goos() in skip |
| 160 |
144 | 161 |
145 def get_build_tags(package): | 162 def get_build_tags(package): |
146 """True to skip package tests, reads 'skip_testing' from *.infra_testing.""" | 163 """Build tags to use when building a package, read from *.infra_testing.""" |
147 tags = get_package_info(package).get('build_tags', ()) | 164 tags = get_package_info(package).get('build_tags', ()) |
148 if tags: | 165 if tags: |
149 return '-tags='+(','.join(tags)) | 166 return '-tags='+(','.join(tags)) |
150 return None | 167 return None |
151 | 168 |
| 169 |
152 def get_expected_coverage(package): | 170 def get_expected_coverage(package): |
153 """Returns allowed code coverage percentage as a pair (min, max).""" | 171 """Returns allowed code coverage percentage as a pair (min, max).""" |
154 info = get_package_info(package) | 172 info = get_package_info(package) |
155 min_cover = info.get('expected_coverage_min', 100.0) | 173 min_cover = info.get('expected_coverage_min', 100.0) |
156 max_cover = info.get('expected_coverage_max', 100.0) | 174 max_cover = info.get('expected_coverage_max', 100.0) |
157 if max_cover < min_cover: | 175 if max_cover < min_cover: |
158 max_cover = min_cover | 176 max_cover = min_cover |
159 return (min_cover, max_cover) | 177 return (min_cover, max_cover) |
160 | 178 |
161 | 179 |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
250 print 'Use ./env.py python test.py' | 268 print 'Use ./env.py python test.py' |
251 return 1 | 269 return 1 |
252 | 270 |
253 # Coverage dir is always overwritten with new coverage reports. | 271 # Coverage dir is always overwritten with new coverage reports. |
254 if os.path.exists(coverage_dir): | 272 if os.path.exists(coverage_dir): |
255 shutil.rmtree(coverage_dir) | 273 shutil.rmtree(coverage_dir) |
256 makedirs(coverage_dir) | 274 makedirs(coverage_dir) |
257 | 275 |
258 # Code coverage report requires tests to be run against a single package, so | 276 # Code coverage report requires tests to be run against a single package, so |
259 # discover all individual packages. | 277 # discover all individual packages. |
260 packages = [p for p in list_packages(package_root) if not should_skip(p)] | 278 skipped = [] |
261 if not packages: | 279 packages = [] |
| 280 for p in list_packages(package_root): |
| 281 if should_skip(p): |
| 282 skipped.append(p) |
| 283 else: |
| 284 packages.append(p) |
| 285 |
| 286 if skipped: |
| 287 print 'Skipping these packages (see "skip_testing" in *.infra_testing):' |
| 288 for p in sorted(skipped): |
| 289 print ' %s' % p |
| 290 print |
| 291 |
| 292 if packages: |
| 293 print 'About to run tests for: ' |
| 294 for p in sorted(packages): |
| 295 print ' %s' % p |
| 296 else: |
262 print 'No tests to run' | 297 print 'No tests to run' |
263 return 0 | 298 return 0 |
| 299 print '-' * 80 |
264 | 300 |
265 failed = [] | 301 failed = [] |
266 bad_cover = [] | 302 bad_cover = [] |
267 tpool = ThreadPool(len(packages)) | 303 tpool = ThreadPool(len(packages)) |
268 def run(pkg): | 304 def run(pkg): |
269 coverage_file = os.path.join(coverage_dir, pkg.replace('/', os.sep)) | 305 coverage_file = os.path.join(coverage_dir, pkg.replace('/', os.sep)) |
270 return run_package_tests(pkg, coverage_file) | 306 return run_package_tests(pkg, coverage_file) |
271 for result in tpool.imap_unordered(run, packages): | 307 for result in tpool.imap_unordered(run, packages): |
272 if result.tests_pass and result.coverage_pass: | 308 if result.tests_pass and result.coverage_pass: |
273 sys.stdout.write('.') | 309 sys.stdout.write('.') |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
328 elif len(args) == 1: | 364 elif len(args) == 1: |
329 package_root = args[0] | 365 package_root = args[0] |
330 else: | 366 else: |
331 print >> sys.stderr, sys.modules['__main__'].__doc__.strip() | 367 print >> sys.stderr, sys.modules['__main__'].__doc__.strip() |
332 return 1 | 368 return 1 |
333 return run_tests(package_root, os.path.join(INFRA_GO_DIR, 'coverage')) | 369 return run_tests(package_root, os.path.join(INFRA_GO_DIR, 'coverage')) |
334 | 370 |
335 | 371 |
336 if __name__ == '__main__': | 372 if __name__ == '__main__': |
337 sys.exit(main(sys.argv[1:])) | 373 sys.exit(main(sys.argv[1:])) |
OLD | NEW |