Commit 7122ffd5 authored by jrg@chromium.org's avatar jrg@chromium.org

Code coverage improvements.

Code coverage now works for Linux.  Unit tests to run passed from
project to coverage_posix.py so deps are correct.


Review URL: http://codereview.chromium.org/100189

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15016 0039d316-1c4b-4281-b951-d872f2087c98
parent a92f595d
...@@ -72,16 +72,13 @@ ...@@ -72,16 +72,13 @@
}], }],
], ],
}], }],
# TODO(jrg): complete this work once Linux transitions to gyp. # Linux gyp (into scons) doesn't like target_conditions?
# This is untested (--> likely doesn't work). # TODO(???): track down why 'target_conditions' doesn't work
# on Linux gyp into scons like it does on Mac gyp into xcodeproj.
['OS=="linux"', { ['OS=="linux"', {
'cflags': [ '-ftest-coverage', 'cflags': [ '-ftest-coverage',
'-fprofile-arcs' ], '-fprofile-arcs' ],
'target_conditions': [ 'link_settings': { 'libraries': [ '-lgcov' ] },
['_type=="executable"', {
'link_settings': { 'libraries': [ '-lgcov' ] },
}],
],
}], }],
]}, ]},
# TODO(jrg): options for code coverage on Windows # TODO(jrg): options for code coverage on Windows
......
...@@ -3000,8 +3000,7 @@ ...@@ -3000,8 +3000,7 @@
]}, # 'targets' ]}, # 'targets'
], # OS=="win" ], # OS=="win"
# TODO(jrg): add in Windows code coverage targets. # TODO(jrg): add in Windows code coverage targets.
# Also test on Linux. ['coverage!=0 and OS!="win"',
['coverage!=0 and OS=="mac"',
{ 'targets': [ { 'targets': [
{ {
'target_name': 'coverage', 'target_name': 'coverage',
...@@ -3011,16 +3010,26 @@ ...@@ -3011,16 +3010,26 @@
'type': 'none', 'type': 'none',
'dependencies': [ 'dependencies': [
'../base/base.gyp:base_unittests', '../base/base.gyp:base_unittests',
'../media/media.gyp:media_unittests',
'../net/net.gyp:net_unittests',
'../printing/printing.gyp:printing_unittests',
], ],
'actions': [ 'actions': [
{ {
# 'message' for Linux/scons in particular
'message': 'Running coverage_posix.py to generate coverage numbers',
'inputs': [], 'inputs': [],
'outputs': [], 'outputs': [],
'action_name': 'coverage', 'action_name': 'coverage',
'action': [ 'python', 'action': [ 'python',
'../tools/code_coverage/coverage_posix.py', '../tools/code_coverage/coverage_posix.py',
'--directory', '--directory',
'<(PRODUCT_DIR)' ], '<(PRODUCT_DIR)',
'--',
'<@(_dependencies)'],
# 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' ], # 'actions'
}, },
......
...@@ -5,30 +5,45 @@ ...@@ -5,30 +5,45 @@
"""Generate and process code coverage on POSIX systems. """Generate and process code coverage on POSIX systems.
Written for and tested on Mac. Written for and tested on Mac and Linux. To use this script to
Not tested on Linux yet. generate coverage numbers, please run from within a gyp-generated
project.
All platforms, to set up coverage:
cd ...../chromium ; src/tools/gyp/gyp_dogfood -Dcoverage=1 src/build/all.gyp
Run coverage on...
Mac:
( cd src/chrome ; xcodebuild -configuration Debug -target coverage )
Linux:
( cd src/chrome ; hammer coverage )
# In particular, don't try and run 'coverage' from src/build
--directory=DIR: specify directory that contains gcda files, and where --directory=DIR: specify directory that contains gcda files, and where
a "coverage" directory will be created containing the output html. a "coverage" directory will be created containing the output html.
Example name: ..../chromium/src/xcodebuild/Debug
TODO(jrg): make list of unit tests an arg to this script --all_unittests: is present, run all files named *_unittests that we
can find.
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
named "base_unittests".
""" """
import glob
import logging import logging
import optparse import optparse
import os import os
import shutil
import subprocess import subprocess
import sys import sys
class Coverage(object): class Coverage(object):
"""Doitall class for code coverage.""" """Doitall class for code coverage."""
# Unit test files to run.
UNIT_TESTS = [
'base_unittests',
# 'unit_tests,
]
def __init__(self, directory): def __init__(self, directory):
super(Coverage, self).__init__() super(Coverage, self).__init__()
self.directory = directory self.directory = directory
...@@ -43,6 +58,34 @@ class Coverage(object): ...@@ -43,6 +58,34 @@ class Coverage(object):
self.genhtml = os.path.join(self.lcov_directory, 'genhtml') self.genhtml = os.path.join(self.lcov_directory, 'genhtml')
self.coverage_info_file = os.path.join(self.directory, 'coverage.info') self.coverage_info_file = os.path.join(self.directory, 'coverage.info')
self.ConfirmPlatformAndPaths() self.ConfirmPlatformAndPaths()
self.tests = []
def FindTests(self, options, args):
"""Find unit tests to run; set self.tests to this list.
Obtain instructions from the command line seen in the provided
parsed options and post-option args.
"""
# Small tests: can be run in the "chromium" directory.
# If asked, run all we can find.
if options.all_unittests:
self.tests += glob.glob(os.path.join(self.directory, '*_unittests'))
# If told explicit tests, run those (after stripping the name as
# appropriate)
for testname in args:
if ':' in testname:
self.tests += [os.path.join(self.directory, testname.split(':')[1])]
else:
self.tests += [os.path.join(self.directory, testname)]
# Needs to be run in the "chrome" directory?
# ut = os.path.join(self.directory, 'unit_tests')
# if os.path.exists(ut):
# self.tests.append(ut)
# Medium tests?
# Not sure all of these work yet (e.g. page_cycler_tests)
# self.tests += glob.glob(os.path.join(self.directory, '*_tests'))
def ConfirmPlatformAndPaths(self): def ConfirmPlatformAndPaths(self):
"""Confirm OS and paths (e.g. lcov).""" """Confirm OS and paths (e.g. lcov)."""
...@@ -70,11 +113,11 @@ class Coverage(object): ...@@ -70,11 +113,11 @@ class Coverage(object):
subprocess.call([self.lcov, subprocess.call([self.lcov,
'--directory', self.directory_parent, '--directory', self.directory_parent,
'--zerocounters']) '--zerocounters'])
shutil.rmtree(os.path.join(self.directory, 'coverage'))
def RunTests(self): def RunTests(self):
"""Run all unit tests.""" """Run all unit tests."""
for test in self.UNIT_TESTS: for fulltest in self.tests:
fulltest = os.path.join(self.directory, test)
if not os.path.exists(fulltest): if not os.path.exists(fulltest):
logging.fatal(fulltest + ' does not exist') logging.fatal(fulltest + ' does not exist')
# TODO(jrg): add timeout? # TODO(jrg): add timeout?
...@@ -113,12 +156,18 @@ def main(): ...@@ -113,12 +156,18 @@ def main():
dest='directory', dest='directory',
default=None, default=None,
help='Directory of unit test files') help='Directory of unit test files')
parser.add_option('-a',
'--all_unittests',
dest='all_unittests',
default=False,
help='Run all tests we can find (*_unittests)')
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
if not options.directory: if not options.directory:
parser.error('Directory not specified') parser.error('Directory not specified')
coverage = Coverage(options.directory) coverage = Coverage(options.directory)
coverage.ClearData() coverage.ClearData()
coverage.FindTests(options, args)
coverage.RunTests() coverage.RunTests()
coverage.GenerateOutput() coverage.GenerateOutput()
return 0 return 0
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment