summaryrefslogtreecommitdiff
path: root/chromium/tools/code_coverage
diff options
context:
space:
mode:
authorAndras Becsi <andras.becsi@digia.com>2014-03-18 13:16:26 +0100
committerFrederik Gladhorn <frederik.gladhorn@digia.com>2014-03-20 15:55:39 +0100
commit3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch)
tree92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/tools/code_coverage
parente90d7c4b152c56919d963987e2503f9909a666d2 (diff)
downloadqtwebengine-chromium-3f0f86b0caed75241fa71c95a5d73bc0164348c5.tar.gz
Update to new stable branch 1750
This also includes an updated ninja and chromium dependencies needed on Windows. Change-Id: Icd597d80ed3fa4425933c9f1334c3c2e31291c42 Reviewed-by: Zoltan Arvai <zarvai@inf.u-szeged.hu> Reviewed-by: Zeno Albisser <zeno.albisser@digia.com>
Diffstat (limited to 'chromium/tools/code_coverage')
-rwxr-xr-xchromium/tools/code_coverage/coverage.py359
-rwxr-xr-xchromium/tools/code_coverage/coverage_posix.py1266
-rwxr-xr-xchromium/tools/code_coverage/coverage_posix_unittest.py142
-rwxr-xr-xchromium/tools/code_coverage/process_coverage.py413
4 files changed, 0 insertions, 2180 deletions
diff --git a/chromium/tools/code_coverage/coverage.py b/chromium/tools/code_coverage/coverage.py
deleted file mode 100755
index a1496d7418d..00000000000
--- a/chromium/tools/code_coverage/coverage.py
+++ /dev/null
@@ -1,359 +0,0 @@
-#!/bin/env python
-# Copyright (c) 2011 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-
-"""Module to setup and generate code coverage data
-
-This module first sets up the environment for code coverage, instruments the
-binaries, runs the tests and collects the code coverage data.
-
-
-Usage:
- coverage.py --upload=<upload_location>
- --revision=<revision_number>
- --src_root=<root_of_source_tree>
- [--tools_path=<tools_path>]
-"""
-
-import logging
-import optparse
-import os
-import shutil
-import subprocess
-import sys
-import tempfile
-
-import google.logging_utils
-import google.process_utils as proc
-
-
-# The list of binaries that will be instrumented for code coverage
-# TODO(niranjan): Re-enable instrumentation of chrome.exe and chrome.dll once we
-# resolve the issue where vsinstr.exe is confused while reading symbols.
-windows_binaries = [#'chrome.exe',
- #'chrome.dll',
- 'unit_tests.exe',
- 'automated_ui_tests.exe',
- 'installer_util_unittests.exe',
- 'ipc_tests.exe',
- 'memory_test.exe',
- 'page_cycler_tests.exe',
- 'perf_tests.exe',
- 'reliability_tests.exe',
- 'security_tests.dll',
- 'startup_tests.exe',
- 'tab_switching_test.exe',
- 'test_shell.exe']
-
-# The list of [tests, args] that will be run.
-# Failing tests have been commented out.
-# TODO(niranjan): Need to add layout tests that excercise the test shell.
-windows_tests = [
- ['unit_tests.exe', ''],
-# ['automated_ui_tests.exe', ''],
- ['installer_util_unittests.exe', ''],
- ['ipc_tests.exe', ''],
- ['page_cycler_tests.exe', '--gtest_filter=*File --no-sandbox'],
- ['reliability_tests.exe', '--no-sandbox'],
- ['startup_tests.exe', '--no-sandbox'],
- ['tab_switching_test.exe', '--no-sandbox'],
- ]
-
-
-def IsWindows():
- """Checks if the current platform is Windows.
- """
- return sys.platform[:3] == 'win'
-
-
-class Coverage(object):
- """Class to set up and generate code coverage.
-
- This class contains methods that are useful to set up the environment for
- code coverage.
-
- Attributes:
- instrumented: A boolean indicating if all the binaries have been
- instrumented.
- """
-
- def __init__(self,
- revision,
- src_path = None,
- tools_path = None,
- archive=None):
- """Init method for the Coverage class.
-
- Args:
- revision: Revision number of the Chromium source tree.
- src_path: Location of the Chromium source base.
- tools_path: Location of the Visual Studio Team Tools. (Win32 only)
- archive: Archive location for the intermediate .coverage results.
- """
- google.logging_utils.config_root()
- self.revision = revision
- self.instrumented = False
- self.tools_path = tools_path
- self.src_path = src_path
- self._dir = tempfile.mkdtemp()
- self._archive = archive
-
- def SetUp(self, binaries):
- """Set up the platform specific environment and instrument the binaries for
- coverage.
-
- This method sets up the environment, instruments all the compiled binaries
- and sets up the code coverage counters.
-
- Args:
- binaries: List of binaries that need to be instrumented.
-
- Returns:
- True on success.
- False on error.
- """
- if self.instrumented:
- logging.error('Binaries already instrumented')
- return False
- if IsWindows():
- # Stop all previous instance of VSPerfMon counters
- counters_command = ('%s -shutdown' %
- (os.path.join(self.tools_path, 'vsperfcmd.exe')))
- (retcode, output) = proc.RunCommandFull(counters_command,
- collect_output=True)
- # TODO(niranjan): Add a check that to verify that the binaries were built
- # using the /PROFILE linker flag.
- if self.tools_path == None:
- logging.error('Could not locate Visual Studio Team Server tools')
- return False
- # Remove trailing slashes
- self.tools_path = self.tools_path.rstrip('\\')
- # Add this to the env PATH.
- os.environ['PATH'] = os.environ['PATH'] + ';' + self.tools_path
- instrument_command = '%s /COVERAGE ' % (os.path.join(self.tools_path,
- 'vsinstr.exe'))
- for binary in binaries:
- logging.info('binary = %s' % (binary))
- logging.info('instrument_command = %s' % (instrument_command))
- # Instrument each binary in the list
- binary = os.path.join(self.src_path, 'chrome', 'Release', binary)
- (retcode, output) = proc.RunCommandFull(instrument_command + binary,
- collect_output=True)
- # Check if the file has been instrumented correctly.
- if output.pop().rfind('Successfully instrumented') == -1:
- logging.error('Error instrumenting %s' % (binary))
- return False
- # We are now ready to run tests and measure code coverage.
- self.instrumented = True
- return True
-
- def TearDown(self):
- """Tear down method.
-
- This method shuts down the counters, and cleans up all the intermediate
- artifacts.
- """
- if self.instrumented == False:
- return
-
- if IsWindows():
- # Stop counters
- counters_command = ('%s -shutdown' %
- (os.path.join(self.tools_path, 'vsperfcmd.exe')))
- (retcode, output) = proc.RunCommandFull(counters_command,
- collect_output=True)
- logging.info('Counters shut down: %s' % (output))
- # TODO(niranjan): Revert the instrumented binaries to their original
- # versions.
- else:
- return
- if self._archive:
- shutil.copytree(self._dir, os.path.join(self._archive, self.revision))
- logging.info('Archived the .coverage files')
- # Delete all the temp files and folders
- if self._dir != None:
- shutil.rmtree(self._dir, ignore_errors=True)
- logging.info('Cleaned up temporary files and folders')
- # Reset the instrumented flag.
- self.instrumented = False
-
- def RunTest(self, src_root, test):
- """Run tests and collect the .coverage file
-
- Args:
- src_root: Path to the root of the source.
- test: Path to the test to be run.
-
- Returns:
- Path of the intermediate .coverage file on success.
- None on error.
- """
- # Generate the intermediate file name for the coverage results
- test_name = os.path.split(test[0])[1].strip('.exe')
- # test_command = binary + args
- test_command = '%s %s' % (os.path.join(src_root,
- 'chrome',
- 'Release',
- test[0]),
- test[1])
-
- coverage_file = os.path.join(self._dir, '%s_win32_%s.coverage' %
- (test_name, self.revision))
- logging.info('.coverage file for test %s: %s' % (test_name, coverage_file))
-
- # After all the binaries have been instrumented, we start the counters.
- counters_command = ('%s -start:coverage -output:%s' %
- (os.path.join(self.tools_path, 'vsperfcmd.exe'),
- coverage_file))
- # Here we use subprocess.call() instead of the RunCommandFull because the
- # VSPerfCmd spawns another process before terminating and this confuses
- # the subprocess.Popen() used by RunCommandFull.
- retcode = subprocess.call(counters_command)
-
- # Run the test binary
- logging.info('Executing test %s: ' % test_command)
- (retcode, output) = proc.RunCommandFull(test_command, collect_output=True)
- if retcode != 0: # Return error if the tests fail
- logging.error('One or more tests failed in %s.' % test_command)
- return None
-
- # Stop the counters
- counters_command = ('%s -shutdown' %
- (os.path.join(self.tools_path, 'vsperfcmd.exe')))
- (retcode, output) = proc.RunCommandFull(counters_command,
- collect_output=True)
- logging.info('Counters shut down: %s' % (output))
- # Return the intermediate .coverage file
- return coverage_file
-
- def Upload(self, list_coverage, upload_path, sym_path=None, src_root=None):
- """Upload the results to the dashboard.
-
- This method uploads the coverage data to a dashboard where it will be
- processed. On Windows, this method will first convert the .coverage file to
- the lcov format. This method needs to be called before the TearDown method.
-
- Args:
- list_coverage: The list of coverage data files to consoliate and upload.
- upload_path: Destination where the coverage data will be processed.
- sym_path: Symbol path for the build (Win32 only)
- src_root: Root folder of the source tree (Win32 only)
-
- Returns:
- True on success.
- False on failure.
- """
- if upload_path == None:
- logging.info('Upload path not specified. Will not convert to LCOV')
- return True
-
- if IsWindows():
- # Stop counters
- counters_command = ('%s -shutdown' %
- (os.path.join(self.tools_path, 'vsperfcmd.exe')))
- (retcode, output) = proc.RunCommandFull(counters_command,
- collect_output=True)
- logging.info('Counters shut down: %s' % (output))
- lcov_file = os.path.join(upload_path, 'chrome_win32_%s.lcov' %
- (self.revision))
- lcov = open(lcov_file, 'w')
- for coverage_file in list_coverage:
- # Convert the intermediate .coverage file to lcov format
- if self.tools_path == None:
- logging.error('Lcov converter tool not found')
- return False
- self.tools_path = self.tools_path.rstrip('\\')
- convert_command = ('%s -sym_path=%s -src_root=%s %s' %
- (os.path.join(self.tools_path,
- 'coverage_analyzer.exe'),
- sym_path,
- src_root,
- coverage_file))
- (retcode, output) = proc.RunCommandFull(convert_command,
- collect_output=True)
- # TODO(niranjan): Fix this to check for the correct return code.
-# if output != 0:
-# logging.error('Conversion to LCOV failed. Exiting.')
- tmp_lcov_file = coverage_file + '.lcov'
- logging.info('Conversion to lcov complete for %s' % (coverage_file))
- # Now append this .lcov file to the cumulative lcov file
- logging.info('Consolidating LCOV file: %s' % (tmp_lcov_file))
- tmp_lcov = open(tmp_lcov_file, 'r')
- lcov.write(tmp_lcov.read())
- tmp_lcov.close()
- lcov.close()
- logging.info('LCOV file uploaded to %s' % (upload_path))
-
-
-def main():
- # Command line parsing
- parser = optparse.OptionParser()
- # Path where the .coverage to .lcov converter tools are stored.
- parser.add_option('-t',
- '--tools_path',
- dest='tools_path',
- default=None,
- help='Location of the coverage tools (windows only)')
- parser.add_option('-u',
- '--upload',
- dest='upload_path',
- default=None,
- help='Location where the results should be uploaded')
- # We need the revision number so that we can generate the output file of the
- # format chrome_<platform>_<revision>.lcov
- parser.add_option('-r',
- '--revision',
- dest='revision',
- default=None,
- help='Revision number of the Chromium source repo')
- # Root of the source tree. Needed for converting the generated .coverage file
- # on Windows to the open source lcov format.
- parser.add_option('-s',
- '--src_root',
- dest='src_root',
- default=None,
- help='Root of the source repository')
- parser.add_option('-a',
- '--archive',
- dest='archive',
- default=None,
- help='Archive location of the intermediate .coverage data')
-
- (options, args) = parser.parse_args()
-
- if options.revision == None:
- parser.error('Revision number not specified')
- if options.src_root == None:
- parser.error('Source root not specified')
-
- if IsWindows():
- # Initialize coverage
- cov = Coverage(options.revision,
- options.src_root,
- options.tools_path,
- options.archive)
- list_coverage = []
- # Instrument the binaries
- if cov.SetUp(windows_binaries):
- # Run all the tests
- for test in windows_tests:
- coverage = cov.RunTest(options.src_root, test)
- if coverage == None: # Indicate failure to the buildbots.
- return 1
- # Collect the intermediate file
- list_coverage.append(coverage)
- else:
- logging.error('Error during instrumentation.')
- sys.exit(1)
-
- cov.Upload(list_coverage,
- options.upload_path,
- os.path.join(options.src_root, 'chrome', 'Release'),
- options.src_root)
- cov.TearDown()
-
-
-if __name__ == '__main__':
- sys.exit(main())
diff --git a/chromium/tools/code_coverage/coverage_posix.py b/chromium/tools/code_coverage/coverage_posix.py
deleted file mode 100755
index f4fa56caa10..00000000000
--- a/chromium/tools/code_coverage/coverage_posix.py
+++ /dev/null
@@ -1,1266 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Generate and process code coverage.
-
-TODO(jrg): rename this from coverage_posix.py to coverage_all.py!
-
-Written for and tested on Mac, Linux, and Windows. To use this script
-to 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
- a "coverage" directory will be created containing the output html.
- Example name: ..../chromium/src/xcodebuild/Debug.
- If not specified (e.g. buildbot) we will try and figure it out based on
- other options (e.g. --target and --build-dir; see below).
-
---genhtml: generate html output. If not specified only lcov is generated.
-
---all_unittests: if present, run all files named *_unittests that we
- can find.
-
---fast_test: make the tests run real fast (just for testing)
-
---strict: if a test fails, we continue happily. --strict will cause
- us to die immediately.
-
---trim=False: by default we trim away tests known to be problematic on
- specific platforms. If set to false we do NOT trim out tests.
-
---xvfb=True: By default we use Xvfb to make sure DISPLAY is valid
- (Linux only). if set to False, do not use Xvfb. TODO(jrg): convert
- this script from the compile stage of a builder to a
- RunPythonCommandInBuildDir() command to avoid the need for this
- step.
-
---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. Example contents of the bundlefile:
- ['../base/base.gyp:base_unittests']
- This is used as part of the coverage bot.
- If no other bundlefile-finding args are used (--target,
- --build-dir), this is assumed to be an absolute path.
- If those args are used, find BUNDLEFILE in a way consistent with
- other scripts launched by buildbot. Example of another script
- launched by buildbot:
- http://src.chromium.org/viewvc/chrome/trunk/tools/buildbot/scripts/slave/runtest.py
-
---target=NAME: specify the build target (e.g. 'Debug' or 'Release').
- This is used by buildbot scripts to help us find the output directory.
- Must be used with --build-dir.
-
---build-dir=DIR: According to buildbot comments, this is the name of
- the directory within the buildbot working directory in which the
- solution, Debug, and Release directories are found.
- It's usually "src/build", but on mac it's $DIR/../xcodebuild and on
- Linux it's $DIR/out.
- This is used by buildbot scripts to help us find the output directory.
- Must be used with --target.
-
---no_exclusions: Do NOT use the exclusion list. This script keeps a
- list of tests known to be problematic under coverage. For example,
- ProcessUtilTest.SpawnChild will crash inside __gcov_fork() when
- using the MacOS 10.6 SDK. Use of --no_exclusions prevents the use
- of this exclusion list.
-
---dont-clear-coverage-data: Normally we clear coverage data from
- previous runs. If this arg is used we do NOT clear the coverage
- data.
-
-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 optparse
-import os
-import Queue
-import re
-import shutil
-import signal
-import subprocess
-import sys
-import tempfile
-import threading
-import time
-import traceback
-
-"""Global list of child PIDs to kill when we die."""
-gChildPIDs = []
-
-"""Exclusion list. Format is
- { platform: { testname: (exclusion1, exclusion2, ... ), ... } }
-
- Platform is a match for sys.platform and can be a list.
- Matching code does an 'if sys.platform in (the key):'.
- Similarly, matching does an 'if testname in thefulltestname:'
-
- The Chromium convention has traditionally been to place the
- exclusion list in a distinct file. Unlike valgrind (which has
- frequent changes when things break and are fixed), the expectation
- here is that exclusions remain relatively constant (e.g. OS bugs).
- If that changes, revisit the decision to place inclusions in this
- script.
-
- Details:
- ProcessUtilTest.SpawnChild: chokes in __gcov_fork on 10.6
- IPCFuzzingTest.MsgBadPayloadArgs: ditto
- PanelBrowserNavigatorTest.NavigateFromCrashedPanel: Fails on coverage bot.
- WebGLConformanceTests.conformance_attribs_gl_enable_vertex_attrib: Fails
- with timeout (45000 ms) exceeded error. crbug.com/143248
- WebGLConformanceTests.conformance_attribs_gl_disabled_vertex_attrib:
- ditto.
- WebGLConformanceTests.conformance_attribs_gl_vertex_attrib_zero_issues:
- ditto.
- WebGLConformanceTests.conformance_attribs_gl_vertex_attrib: ditto.
- WebGLConformanceTests.conformance_attribs_gl_vertexattribpointer_offsets:
- ditto.
- WebGLConformanceTests.conformance_attribs_gl_vertexattribpointer: ditto.
- WebGLConformanceTests.conformance_buffers_buffer_bind_test: After
- disabling WebGLConformanceTests specified above, this test fails when run
- on local machine.
- WebGLConformanceTests.conformance_buffers_buffer_data_array_buffer: ditto.
- WebGLConformanceTests.conformance_buffers_index_validation_copies_indices:
- ditto.
- WebGLConformanceTests.
- conformance_buffers_index_validation_crash_with_buffer_sub_data: ditto.
- WebGLConformanceTests.
- conformance_buffers_index_validation_verifies_too_many_indices: ditto.
- WebGLConformanceTests.
- conformance_buffers_index_validation_with_resized_buffer: ditto.
- WebGLConformanceTests.conformance_canvas_buffer_offscreen_test: ditto.
- WebGLConformanceTests.conformance_canvas_buffer_preserve_test: ditto.
- WebGLConformanceTests.conformance_canvas_canvas_test: ditto.
- WebGLConformanceTests.conformance_canvas_canvas_zero_size: ditto.
- WebGLConformanceTests.
- conformance_canvas_drawingbuffer_static_canvas_test: ditto.
- WebGLConformanceTests.conformance_canvas_drawingbuffer_test: ditto.
- PageCycler*.*: Fails on coverage bot with "Missing test directory
- /....../slave/coverage-dbg-linux/build/src/data/page_cycler/moz" error.
- *FrameRateCompositingTest.*: Fails with
- "FATAL:chrome_content_browser_client.cc(893)] Check failed:
- command_line->HasSwitch(switches::kEnableStatsTable)."
- *FrameRateNoVsyncCanvasInternalTest.*: ditto.
- *FrameRateGpuCanvasInternalTest.*: ditto.
- IndexedDBTest.Perf: Fails with 'Timeout reached in WaitUntilCookieValue'
- error.
- TwoClientPasswordsSyncTest.DeleteAll: Fails on coverage bot.
- MigrationTwoClientTest.MigrationHellWithoutNigori: Fails with timeout
- (45000 ms) exceeded error.
- TwoClientSessionsSyncTest.DeleteActiveSession: ditto.
- MultipleClientSessionsSyncTest.EncryptedAndChanged: ditto.
- MigrationSingleClientTest.AllTypesIndividuallyTriggerNotification: ditto.
- *OldPanelResizeBrowserTest.*: crbug.com/143247
- *OldPanelDragBrowserTest.*: ditto.
- *OldPanelBrowserTest.*: ditto.
- *OldPanelAndDesktopNotificationTest.*: ditto.
- *OldDockedPanelBrowserTest.*: ditto.
- *OldDetachedPanelBrowserTest.*: ditto.
- PanelDragBrowserTest.AttachWithSqueeze: ditto.
- *PanelBrowserTest.*: ditto.
- *DockedPanelBrowserTest.*: ditto.
- *DetachedPanelBrowserTest.*: ditto.
- AutomatedUITest.TheOneAndOnlyTest: crbug.com/143419
- AutomatedUITestBase.DragOut: ditto
-
-"""
-gTestExclusions = {
- 'darwin2': { 'base_unittests': ('ProcessUtilTest.SpawnChild',),
- 'ipc_tests': ('IPCFuzzingTest.MsgBadPayloadArgs',), },
- 'linux2': {
- 'gpu_tests':
- ('WebGLConformanceTests.conformance_attribs_gl_enable_vertex_attrib',
- 'WebGLConformanceTests.'
- 'conformance_attribs_gl_disabled_vertex_attrib',
- 'WebGLConformanceTests.'
- 'conformance_attribs_gl_vertex_attrib_zero_issues',
- 'WebGLConformanceTests.conformance_attribs_gl_vertex_attrib',
- 'WebGLConformanceTests.'
- 'conformance_attribs_gl_vertexattribpointer_offsets',
- 'WebGLConformanceTests.conformance_attribs_gl_vertexattribpointer',
- 'WebGLConformanceTests.conformance_buffers_buffer_bind_test',
- 'WebGLConformanceTests.'
- 'conformance_buffers_buffer_data_array_buffer',
- 'WebGLConformanceTests.'
- 'conformance_buffers_index_validation_copies_indices',
- 'WebGLConformanceTests.'
- 'conformance_buffers_index_validation_crash_with_buffer_sub_data',
- 'WebGLConformanceTests.'
- 'conformance_buffers_index_validation_verifies_too_many_indices',
- 'WebGLConformanceTests.'
- 'conformance_buffers_index_validation_with_resized_buffer',
- 'WebGLConformanceTests.conformance_canvas_buffer_offscreen_test',
- 'WebGLConformanceTests.conformance_canvas_buffer_preserve_test',
- 'WebGLConformanceTests.conformance_canvas_canvas_test',
- 'WebGLConformanceTests.conformance_canvas_canvas_zero_size',
- 'WebGLConformanceTests.'
- 'conformance_canvas_drawingbuffer_static_canvas_test',
- 'WebGLConformanceTests.conformance_canvas_drawingbuffer_test',),
- 'performance_ui_tests':
- ('*PageCycler*.*',
- '*FrameRateCompositingTest.*',
- '*FrameRateNoVsyncCanvasInternalTest.*',
- '*FrameRateGpuCanvasInternalTest.*',
- 'IndexedDBTest.Perf',),
- 'sync_integration_tests':
- ('TwoClientPasswordsSyncTest.DeleteAll',
- 'MigrationTwoClientTest.MigrationHellWithoutNigori',
- 'TwoClientSessionsSyncTest.DeleteActiveSession',
- 'MultipleClientSessionsSyncTest.EncryptedAndChanged',
- 'MigrationSingleClientTest.'
- 'AllTypesIndividuallyTriggerNotification',),
- 'interactive_ui_tests':
- ('*OldPanelResizeBrowserTest.*',
- '*OldPanelDragBrowserTest.*',
- '*OldPanelBrowserTest.*',
- '*OldPanelAndDesktopNotificationTest.*',
- '*OldDockedPanelBrowserTest.*',
- '*OldDetachedPanelBrowserTest.*',
- 'PanelDragBrowserTest.AttachWithSqueeze',
- '*PanelBrowserTest.*',
- '*DockedPanelBrowserTest.*',
- '*DetachedPanelBrowserTest.*',),
- 'automated_ui_tests':
- ('AutomatedUITest.TheOneAndOnlyTest',
- 'AutomatedUITestBase.DragOut',), },
-}
-
-"""Since random tests are failing/hanging on coverage bot, we are enabling
- tests feature by feature. crbug.com/159748
-"""
-gTestInclusions = {
- 'linux2': {
- 'browser_tests':
- (# 'src/chrome/browser/downloads'
- 'SavePageBrowserTest.*',
- 'SavePageAsMHTMLBrowserTest.*',
- 'DownloadQueryTest.*',
- 'DownloadDangerPromptTest.*',
- 'DownloadTest.*',
- # 'src/chrome/browser/net'
- 'CookiePolicyBrowserTest.*',
- 'FtpBrowserTest.*',
- 'LoadTimingObserverTest.*',
- 'PredictorBrowserTest.*',
- 'ProxyBrowserTest.*',
- # 'src/chrome/browser/extensions'
- 'Extension*.*',
- 'WindowOpenPanelDisabledTest.*',
- 'WindowOpenPanelTest.*',
- 'WebstoreStandalone*.*',
- 'CommandLineWebstoreInstall.*',
- 'WebViewTest.*',
- 'RequirementsCheckerBrowserTest.*',
- 'ProcessManagementTest.*',
- 'PlatformAppBrowserTest.*',
- 'PlatformAppDevToolsBrowserTest.*',
- 'LazyBackgroundPageApiTest.*',
- 'IsolatedAppTest.*',
- 'PanelMessagingTest.*',
- 'GeolocationApiTest.*',
- 'ClipboardApiTest.*',
- 'ExecuteScriptApiTest.*',
- 'CalculatorBrowserTest.*',
- 'ChromeAppAPITest.*',
- 'AppApiTest.*',
- 'BlockedAppApiTest.*',
- 'AppBackgroundPageApiTest.*',
- 'WebNavigationApiTest.*',
- 'UsbApiTest.*',
- 'TabCaptureApiTest.*',
- 'SystemInfo*.*',
- 'SyncFileSystemApiTest.*',
- 'SocketApiTest.*',
- 'SerialApiTest.*',
- 'RecordApiTest.*',
- 'PushMessagingApiTest.*',
- 'ProxySettingsApiTest.*',
- 'ExperimentalApiTest.*',
- 'OmniboxApiTest.*',
- 'OffscreenTabsApiTest.*',
- 'NotificationApiTest.*',
- 'MediaGalleriesPrivateApiTest.*',
- 'PlatformAppMediaGalleriesBrowserTest.*',
- 'GetAuthTokenFunctionTest.*',
- 'LaunchWebAuthFlowFunctionTest.*',
- 'FileSystemApiTest.*',
- 'ScriptBadgeApiTest.*',
- 'PageAsBrowserActionApiTest.*',
- 'PageActionApiTest.*',
- 'BrowserActionApiTest.*',
- 'DownloadExtensionTest.*',
- 'DnsApiTest.*',
- 'DeclarativeApiTest.*',
- 'BluetoothApiTest.*',
- 'AllUrlsApiTest.*',
- # 'src/chrome/browser/nacl_host'
- 'nacl_host.*',
- # 'src/chrome/browser/automation'
- 'AutomationMiscBrowserTest.*',
- # 'src/chrome/browser/autofill'
- 'FormStructureBrowserTest.*',
- 'AutofillPopupViewBrowserTest.*',
- 'AutofillTest.*',
- # 'src/chrome/browser/autocomplete'
- 'AutocompleteBrowserTest.*',
- # 'src/chrome/browser/captive_portal'
- 'CaptivePortalBrowserTest.*',
- # 'src/chrome/browser/geolocation'
- 'GeolocationAccessTokenStoreTest.*',
- 'GeolocationBrowserTest.*',
- # 'src/chrome/browser/nacl_host'
- 'NaClGdbTest.*',
- # 'src/chrome/browser/devtools'
- 'DevToolsSanityTest.*',
- 'DevToolsExtensionTest.*',
- 'DevToolsExperimentalExtensionTest.*',
- 'WorkerDevToolsSanityTest.*',
- # 'src/chrome/browser/first_run'
- 'FirstRunBrowserTest.*',
- # 'src/chrome/browser/importer'
- 'ToolbarImporterUtilsTest.*',
- # 'src/chrome/browser/page_cycler'
- 'PageCyclerBrowserTest.*',
- 'PageCyclerCachedBrowserTest.*',
- # 'src/chrome/browser/performance_monitor'
- 'PerformanceMonitorBrowserTest.*',
- 'PerformanceMonitorUncleanExitBrowserTest.*',
- 'PerformanceMonitorSessionRestoreBrowserTest.*',
- # 'src/chrome/browser/prerender'
- 'PrerenderBrowserTest.*',
- 'PrerenderBrowserTestWithNaCl.*',
- 'PrerenderBrowserTestWithExtensions.*',
- 'PrefetchBrowserTest.*',
- 'PrefetchBrowserTestNoPrefetching.*', ),
- },
-}
-
-
-def TerminateSignalHandler(sig, stack):
- """When killed, try and kill our child processes."""
- signal.signal(sig, signal.SIG_DFL)
- for pid in gChildPIDs:
- if 'kill' in os.__all__: # POSIX
- os.kill(pid, sig)
- else:
- subprocess.call(['taskkill.exe', '/PID', str(pid)])
- sys.exit(0)
-
-
-class RunTooLongException(Exception):
- """Thrown when a command runs too long without output."""
- pass
-
-class BadUserInput(Exception):
- """Thrown when arguments from the user are incorrectly formatted."""
- pass
-
-
-class RunProgramThread(threading.Thread):
- """A thread to run a subprocess.
-
- We want to print the output of our subprocess in real time, but also
- want a timeout if there has been no output for a certain amount of
- time. Normal techniques (e.g. loop in select()) aren't cross
- platform enough. the function seems simple: "print output of child, kill it
- if there is no output by timeout. But it was tricky to get this right
- in a x-platform way (see warnings about deadlock on the python
- subprocess doc page).
-
- """
- # Constants in our queue
- PROGRESS = 0
- DONE = 1
-
- def __init__(self, cmd):
- super(RunProgramThread, self).__init__()
- self._cmd = cmd
- self._process = None
- self._queue = Queue.Queue()
- self._retcode = None
-
- def run(self):
- if sys.platform in ('win32', 'cygwin'):
- return self._run_windows()
- else:
- self._run_posix()
-
- def _run_windows(self):
- # We need to save stdout to a temporary file because of a bug on the
- # windows implementation of python which can deadlock while waiting
- # for the IO to complete while writing to the PIPE and the pipe waiting
- # on us and us waiting on the child process.
- stdout_file = tempfile.TemporaryFile()
- try:
- self._process = subprocess.Popen(self._cmd,
- stdin=subprocess.PIPE,
- stdout=stdout_file,
- stderr=subprocess.STDOUT)
- gChildPIDs.append(self._process.pid)
- try:
- # To make sure that the buildbot don't kill us if we run too long
- # without any activity on the console output, we look for progress in
- # the length of the temporary file and we print what was accumulated so
- # far to the output console to make the buildbot know we are making some
- # progress.
- previous_tell = 0
- # We will poll the process until we get a non-None return code.
- self._retcode = None
- while self._retcode is None:
- self._retcode = self._process.poll()
- current_tell = stdout_file.tell()
- if current_tell > previous_tell:
- # Report progress to our main thread so we don't timeout.
- self._queue.put(RunProgramThread.PROGRESS)
- # And print what was accumulated to far.
- stdout_file.seek(previous_tell)
- print stdout_file.read(current_tell - previous_tell),
- previous_tell = current_tell
- # Don't be selfish, let other threads do stuff while we wait for
- # the process to complete.
- time.sleep(0.5)
- # OK, the child process has exited, let's print its output to our
- # console to create debugging logs in case they get to be needed.
- stdout_file.flush()
- stdout_file.seek(previous_tell)
- print stdout_file.read(stdout_file.tell() - previous_tell)
- except IOError, e:
- logging.exception('%s', e)
- pass
- finally:
- stdout_file.close()
-
- # If we get here the process is done.
- gChildPIDs.remove(self._process.pid)
- self._queue.put(RunProgramThread.DONE)
-
- def _run_posix(self):
- """No deadlock problem so use the simple answer. The windows solution
- appears to add extra buffering which we don't want on other platforms."""
- self._process = subprocess.Popen(self._cmd,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
- gChildPIDs.append(self._process.pid)
- try:
- while True:
- line = self._process.stdout.readline()
- if not line: # EOF
- break
- print line,
- self._queue.put(RunProgramThread.PROGRESS, True)
- except IOError:
- pass
- # If we get here the process is done.
- gChildPIDs.remove(self._process.pid)
- self._queue.put(RunProgramThread.DONE)
-
- def stop(self):
- self.kill()
-
- def kill(self):
- """Kill our running process if needed. Wait for kill to complete.
-
- Should be called in the PARENT thread; we do not self-kill.
- Returns the return code of the process.
- Safe to call even if the process is dead.
- """
- if not self._process:
- return self.retcode()
- if 'kill' in os.__all__: # POSIX
- os.kill(self._process.pid, signal.SIGKILL)
- else:
- subprocess.call(['taskkill.exe', '/PID', str(self._process.pid)])
- return self.retcode()
-
- def retcode(self):
- """Return the return value of the subprocess.
-
- Waits for process to die but does NOT kill it explicitly.
- """
- if self._retcode == None: # must be none, not 0/False
- self._retcode = self._process.wait()
- return self._retcode
-
- def RunUntilCompletion(self, timeout):
- """Run thread until completion or timeout (in seconds).
-
- Start the thread. Let it run until completion, or until we've
- spent TIMEOUT without seeing output. On timeout throw
- RunTooLongException.
- """
- self.start()
- while True:
- try:
- x = self._queue.get(True, timeout)
- if x == RunProgramThread.DONE:
- return self.retcode()
- except Queue.Empty, e: # timed out
- logging.info('TIMEOUT (%d seconds exceeded with no output): killing' %
- timeout)
- self.kill()
- raise RunTooLongException()
-
-
-class Coverage(object):
- """Doitall class for code coverage."""
-
- def __init__(self, options, args):
- super(Coverage, self).__init__()
- logging.basicConfig(level=logging.DEBUG)
- self.directory = options.directory
- self.options = options
- self.args = args
- self.ConfirmDirectory()
- self.directory_parent = os.path.dirname(self.directory)
- self.output_directory = os.path.join(self.directory, 'coverage')
- if not os.path.exists(self.output_directory):
- os.mkdir(self.output_directory)
- # The "final" lcov-format file
- self.coverage_info_file = os.path.join(self.directory, 'coverage.info')
- # If needed, an intermediate VSTS-format file
- self.vsts_output = os.path.join(self.directory, 'coverage.vsts')
- # Needed for Windows.
- self.src_root = options.src_root
- self.FindPrograms()
- self.ConfirmPlatformAndPaths()
- self.tests = [] # This can be a list of strings, lists or both.
- self.xvfb_pid = 0
- self.test_files = [] # List of files with test specifications.
- self.test_filters = {} # Mapping from testname->--gtest_filter arg.
- logging.info('self.directory: ' + self.directory)
- logging.info('self.directory_parent: ' + self.directory_parent)
-
- def FindInPath(self, program):
- """Find program in our path. Return abs path to it, or None."""
- if not 'PATH' in os.environ:
- logging.fatal('No PATH environment variable?')
- sys.exit(1)
- paths = os.environ['PATH'].split(os.pathsep)
- for path in paths:
- fullpath = os.path.join(path, program)
- if os.path.exists(fullpath):
- return fullpath
- return None
-
- def FindPrograms(self):
- """Find programs we may want to run."""
- if self.IsPosix():
- self.lcov_directory = os.path.join(sys.path[0],
- '../../third_party/lcov/bin')
- self.lcov = os.path.join(self.lcov_directory, 'lcov')
- self.mcov = os.path.join(self.lcov_directory, 'mcov')
- self.genhtml = os.path.join(self.lcov_directory, 'genhtml')
- self.programs = [self.lcov, self.mcov, self.genhtml]
- else:
- # Hack to get the buildbot working.
- os.environ['PATH'] += r';c:\coverage\coverage_analyzer'
- os.environ['PATH'] += r';c:\coverage\performance_tools'
- # (end hack)
- commands = ['vsperfcmd.exe', 'vsinstr.exe', 'coverage_analyzer.exe']
- self.perf = self.FindInPath('vsperfcmd.exe')
- self.instrument = self.FindInPath('vsinstr.exe')
- self.analyzer = self.FindInPath('coverage_analyzer.exe')
- if not self.perf or not self.instrument or not self.analyzer:
- logging.fatal('Could not find Win performance commands.')
- logging.fatal('Commands needed in PATH: ' + str(commands))
- sys.exit(1)
- self.programs = [self.perf, self.instrument, self.analyzer]
-
- def PlatformBuildPrefix(self):
- """Return a platform specific build directory prefix.
-
- This prefix is prepended to the build target (Debug, Release) to
- identify output as relative to the build directory.
- These values are specific to Chromium's use of gyp.
- """
- if self.IsMac():
- return '../xcodebuild'
- if self.IsWindows():
- return ''
- else: # Linux
- return '../out' # assumes make, unlike runtest.py
-
- def ConfirmDirectory(self):
- """Confirm correctness of self.directory.
-
- If it exists, happiness. If not, try and figure it out in a
- manner similar to FindBundlesFile(). The 'figure it out' case
- happens with buildbot where the directory isn't specified
- explicitly.
- """
- if (not self.directory and
- not (self.options.target and self.options.build_dir)):
- logging.fatal('Must use --directory or (--target and --build-dir)')
- sys.exit(1)
-
- if not self.directory:
- self.directory = os.path.join(self.options.build_dir,
- self.PlatformBuildPrefix(),
- self.options.target)
-
- if os.path.exists(self.directory):
- logging.info('Directory: ' + self.directory)
- return
- else:
- logging.fatal('Directory ' +
- self.directory + ' doesn\'t exist')
- sys.exit(1)
-
-
- def FindBundlesFile(self):
- """Find the bundlesfile.
-
- The 'bundles' file can be either absolute path, or (if we are run
- from buildbot) we need to find it based on other hints (--target,
- --build-dir, etc).
- """
- # If no bundle file, no problem!
- if not self.options.bundles:
- return
- # If true, we're buildbot. Form a path.
- # Else assume absolute.
- if self.options.target and self.options.build_dir:
- fullpath = os.path.join(self.options.build_dir,
- self.PlatformBuildPrefix(),
- self.options.target,
- self.options.bundles)
- self.options.bundles = fullpath
-
- if os.path.exists(self.options.bundles):
- logging.info('BundlesFile: ' + self.options.bundles)
- return
- else:
- logging.fatal('bundlefile ' +
- self.options.bundles + ' doesn\'t exist')
- sys.exit(1)
-
-
- def FindTests(self):
- """Find unit tests to run; set self.tests to this list.
-
- Assume all non-option items in the arg list are tests to be run.
- """
- # Before we begin, find the bundles file if not an absolute path.
- self.FindBundlesFile()
-
- # Small tests: can be run in the "chromium" directory.
- # If asked, run all we can find.
- if self.options.all_unittests:
- self.tests += glob.glob(os.path.join(self.directory, '*_unittests'))
- self.tests += glob.glob(os.path.join(self.directory, '*unit_tests'))
- elif self.options.all_browsertests:
- # Run all tests in browser_tests and content_browsertests.
- self.tests += glob.glob(os.path.join(self.directory, 'browser_tests'))
- self.tests += glob.glob(os.path.join(self.directory,
- 'content_browsertests'))
-
- # Tests can come in as args directly, indirectly (through a file
- # of test lists) or as a file of bundles.
- all_testnames = self.args[:] # Copy since we might modify
-
- for test_file in self.options.test_files:
- f = open(test_file)
- for line in f:
- line = re.sub(r"#.*$", "", line)
- line = re.sub(r"\s*", "", line)
- if re.match("\s*$"):
- continue
- all_testnames.append(line)
- f.close()
-
- 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 all_testnames:
- mo = re.search(r"(.*)\[(.*)\]$", testname)
- gtest_filter = None
- if mo:
- gtest_filter = mo.group(2)
- testname = mo.group(1)
- if ':' in testname:
- testname = testname.split(':')[1]
- # We need 'pyautolib' to run pyauto tests and 'pyautolib' itself is not an
- # executable. So skip this test from adding into coverage_bundles.py.
- if testname == 'pyautolib':
- continue
- self.tests += [os.path.join(self.directory, testname)]
- if gtest_filter:
- self.test_filters[testname] = gtest_filter
-
- # Add 'src/test/functional/pyauto_functional.py' to self.tests.
- # This file with '-v --suite=CODE_COVERAGE' arguments runs all pyauto tests.
- # Pyauto tests are failing randomly on coverage bots. So excluding them.
- # self.tests += [['src/chrome/test/functional/pyauto_functional.py',
- # '-v',
- # '--suite=CODE_COVERAGE']]
-
- # Medium tests?
- # Not sure all of these work yet (e.g. page_cycler_tests)
- # self.tests += glob.glob(os.path.join(self.directory, '*_tests'))
-
- # If needed, append .exe to tests since vsinstr.exe likes it that
- # way.
- if self.IsWindows():
- for ind in range(len(self.tests)):
- test = self.tests[ind]
- test_exe = test + '.exe'
- if not test.endswith('.exe') and os.path.exists(test_exe):
- self.tests[ind] = test_exe
-
- def TrimTests(self):
- """Trim specific tests for each platform."""
- if self.IsWindows():
- return
- # TODO(jrg): remove when not needed
- inclusion = ['unit_tests']
- keep = []
- for test in self.tests:
- for i in inclusion:
- if i in test:
- keep.append(test)
- self.tests = keep
- logging.info('After trimming tests we have ' + ' '.join(self.tests))
- return
- if self.IsLinux():
- # self.tests = filter(lambda t: t.endswith('base_unittests'), self.tests)
- return
- if self.IsMac():
- exclusion = ['automated_ui_tests']
- punted = []
- for test in self.tests:
- for e in exclusion:
- if test.endswith(e):
- punted.append(test)
- self.tests = filter(lambda t: t not in punted, self.tests)
- if punted:
- logging.info('Tests trimmed out: ' + str(punted))
-
- def ConfirmPlatformAndPaths(self):
- """Confirm OS and paths (e.g. lcov)."""
- for program in self.programs:
- if not os.path.exists(program):
- logging.fatal('Program missing: ' + program)
- sys.exit(1)
-
- def Run(self, cmdlist, ignore_error=False, ignore_retcode=None,
- explanation=None):
- """Run the command list; exit fatally on error.
-
- Args:
- cmdlist: a list of commands (e.g. to pass to subprocess.call)
- ignore_error: if True log an error; if False then exit.
- ignore_retcode: if retcode is non-zero, exit unless we ignore.
-
- Returns: process return code.
- Throws: RunTooLongException if the process does not produce output
- within TIMEOUT seconds; timeout is specified as a command line
- option to the Coverage class and is set on init.
- """
- logging.info('Running ' + str(cmdlist))
- t = RunProgramThread(cmdlist)
- retcode = t.RunUntilCompletion(self.options.timeout)
-
- if retcode:
- if ignore_error or retcode == ignore_retcode:
- logging.warning('COVERAGE: %s unhappy but errors ignored %s' %
- (str(cmdlist), explanation or ''))
- else:
- logging.fatal('COVERAGE: %s failed; return code: %d' %
- (str(cmdlist), retcode))
- sys.exit(retcode)
- return retcode
-
- def IsPosix(self):
- """Return True if we are POSIX."""
- return self.IsMac() or self.IsLinux()
-
- def IsMac(self):
- return sys.platform == 'darwin'
-
- def IsLinux(self):
- return sys.platform.startswith('linux')
-
- def IsWindows(self):
- """Return True if we are Windows."""
- return sys.platform in ('win32', 'cygwin')
-
- def ClearData(self):
- """Clear old gcda files and old coverage info files."""
- if self.options.dont_clear_coverage_data:
- print 'Clearing of coverage data NOT performed.'
- return
- print 'Clearing coverage data from previous runs.'
- if os.path.exists(self.coverage_info_file):
- os.remove(self.coverage_info_file)
- if self.IsPosix():
- subprocess.call([self.lcov,
- '--directory', self.directory_parent,
- '--zerocounters'])
- shutil.rmtree(os.path.join(self.directory, 'coverage'))
- if self.options.all_unittests:
- if os.path.exists(os.path.join(self.directory, 'unittests_coverage')):
- shutil.rmtree(os.path.join(self.directory, 'unittests_coverage'))
- elif self.options.all_browsertests:
- if os.path.exists(os.path.join(self.directory,
- 'browsertests_coverage')):
- shutil.rmtree(os.path.join(self.directory, 'browsertests_coverage'))
- else:
- if os.path.exists(os.path.join(self.directory, 'total_coverage')):
- shutil.rmtree(os.path.join(self.directory, 'total_coverage'))
-
- def BeforeRunOneTest(self, testname):
- """Do things before running each test."""
- if not self.IsWindows():
- return
- # Stop old counters if needed
- cmdlist = [self.perf, '-shutdown']
- self.Run(cmdlist, ignore_error=True)
- # Instrument binaries
- for fulltest in self.tests:
- if os.path.exists(fulltest):
- # See http://support.microsoft.com/kb/939818 for details on args
- cmdlist = [self.instrument, '/d:ignorecverr', '/COVERAGE', fulltest]
- self.Run(cmdlist, ignore_retcode=4,
- explanation='OK with a multiple-instrument')
- # Start new counters
- cmdlist = [self.perf, '-start:coverage', '-output:' + self.vsts_output]
- self.Run(cmdlist)
-
- def BeforeRunAllTests(self):
- """Called right before we run all tests."""
- if self.IsLinux() and self.options.xvfb:
- self.StartXvfb()
-
- def GtestFilter(self, fulltest, excl=None):
- """Return a --gtest_filter=BLAH for this test.
-
- Args:
- fulltest: full name of test executable
- exclusions: the exclusions list. Only set in a unit test;
- else uses gTestExclusions.
- Returns:
- String of the form '--gtest_filter=BLAH', or None.
- """
- positive_gfilter_list = []
- negative_gfilter_list = []
-
- # Exclude all flaky, failing, disabled and maybe tests;
- # they don't count for code coverage.
- negative_gfilter_list += ('*.FLAKY_*', '*.FAILS_*',
- '*.DISABLED_*', '*.MAYBE_*')
-
- if not self.options.no_exclusions:
- exclusions = excl or gTestExclusions
- excldict = exclusions.get(sys.platform)
- if excldict:
- for test in excldict.keys():
- # example: if base_unittests in ../blah/blah/base_unittests.exe
- if test in fulltest:
- negative_gfilter_list += excldict[test]
-
- inclusions = gTestInclusions
- include_dict = inclusions.get(sys.platform)
- if include_dict:
- for test in include_dict.keys():
- if test in fulltest:
- positive_gfilter_list += include_dict[test]
-
- fulltest_basename = os.path.basename(fulltest)
- if fulltest_basename in self.test_filters:
- specific_test_filters = self.test_filters[fulltest_basename].split('-')
- if len(specific_test_filters) > 2:
- logging.error('Multiple "-" symbols in filter list: %s' %
- self.test_filters[fulltest_basename])
- raise BadUserInput()
- if len(specific_test_filters) == 2:
- # Remove trailing ':'
- specific_test_filters[0] = specific_test_filters[0][:-1]
-
- if specific_test_filters[0]: # Test for no positive filters.
- positive_gfilter_list += specific_test_filters[0].split(':')
- if len(specific_test_filters) > 1:
- negative_gfilter_list += specific_test_filters[1].split(':')
-
- if not positive_gfilter_list and not negative_gfilter_list:
- return None
-
- result = '--gtest_filter='
- if positive_gfilter_list:
- result += ':'.join(positive_gfilter_list)
- if negative_gfilter_list:
- if positive_gfilter_list: result += ':'
- result += '-' + ':'.join(negative_gfilter_list)
- return result
-
- def RunTests(self):
- """Run all unit tests and generate appropriate lcov files."""
- self.BeforeRunAllTests()
- for fulltest in self.tests:
- if type(fulltest) is str:
- if not os.path.exists(fulltest):
- logging.info(fulltest + ' does not exist')
- if self.options.strict:
- sys.exit(2)
- else:
- logging.info('%s path exists' % fulltest)
- cmdlist = [fulltest, '--gtest_print_time']
-
- # If asked, make this REAL fast for testing.
- if self.options.fast_test:
- logging.info('Running as a FAST test for testing')
- # cmdlist.append('--gtest_filter=RenderWidgetHost*')
- # cmdlist.append('--gtest_filter=CommandLine*')
- cmdlist.append('--gtest_filter=C*')
-
- # Possibly add a test-specific --gtest_filter
- filter = self.GtestFilter(fulltest)
- if filter:
- cmdlist.append(filter)
- elif type(fulltest) is list:
- cmdlist = fulltest
-
- self.BeforeRunOneTest(fulltest)
- logging.info('Running test ' + str(cmdlist))
- try:
- retcode = self.Run(cmdlist, ignore_retcode=True)
- except SystemExit: # e.g. sys.exit() was called somewhere in here
- raise
- except: # can't "except WindowsError" since script runs on non-Windows
- logging.info('EXCEPTION while running a unit test')
- logging.info(traceback.format_exc())
- retcode = 999
- self.AfterRunOneTest(fulltest)
-
- if retcode:
- logging.info('COVERAGE: test %s failed; return code: %d.' %
- (fulltest, retcode))
- if self.options.strict:
- logging.fatal('Test failure is fatal.')
- sys.exit(retcode)
- self.AfterRunAllTests()
-
- def AfterRunOneTest(self, testname):
- """Do things right after running each test."""
- if not self.IsWindows():
- return
- # Stop counters
- cmdlist = [self.perf, '-shutdown']
- self.Run(cmdlist)
- full_output = self.vsts_output + '.coverage'
- shutil.move(full_output, self.vsts_output)
- # generate lcov!
- self.GenerateLcovWindows(testname)
-
- def AfterRunAllTests(self):
- """Do things right after running ALL tests."""
- # On POSIX we can do it all at once without running out of memory.
- # This contrasts with Windows where we must do it after each test.
- if self.IsPosix():
- self.GenerateLcovPosix()
- # Only on Linux do we have the Xvfb step.
- if self.IsLinux() and self.options.xvfb:
- self.StopXvfb()
-
- def StartXvfb(self):
- """Start Xvfb and set an appropriate DISPLAY environment. Linux only.
-
- Copied from http://src.chromium.org/viewvc/chrome/trunk/tools/buildbot/
- scripts/slave/slave_utils.py?view=markup
- with some simplifications (e.g. no need to use xdisplaycheck, save
- pid in var not file, etc)
- """
- logging.info('Xvfb: starting')
- proc = subprocess.Popen(["Xvfb", ":9", "-screen", "0", "1024x768x24",
- "-ac"],
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- self.xvfb_pid = proc.pid
- if not self.xvfb_pid:
- logging.info('Could not start Xvfb')
- return
- os.environ['DISPLAY'] = ":9"
- # Now confirm, giving a chance for it to start if needed.
- logging.info('Xvfb: confirming')
- for test in range(10):
- proc = subprocess.Popen('xdpyinfo >/dev/null', shell=True)
- pid, retcode = os.waitpid(proc.pid, 0)
- if retcode == 0:
- break
- time.sleep(0.5)
- if retcode != 0:
- logging.info('Warning: could not confirm Xvfb happiness')
- else:
- logging.info('Xvfb: OK')
-
- def StopXvfb(self):
- """Stop Xvfb if needed. Linux only."""
- if self.xvfb_pid:
- logging.info('Xvfb: killing')
- try:
- os.kill(self.xvfb_pid, signal.SIGKILL)
- except:
- pass
- del os.environ['DISPLAY']
- self.xvfb_pid = 0
-
- def CopyCoverageFileToDestination(self, coverage_folder):
- coverage_dir = os.path.join(self.directory, coverage_folder)
- if not os.path.exists(coverage_dir):
- os.makedirs(coverage_dir)
- shutil.copyfile(self.coverage_info_file, os.path.join(coverage_dir,
- 'coverage.info'))
-
- def GenerateLcovPosix(self):
- """Convert profile data to lcov on Mac or Linux."""
- start_dir = os.getcwd()
- logging.info('GenerateLcovPosix: start_dir=' + start_dir)
- if self.IsLinux():
- # With Linux/make (e.g. the coverage_run target), the current
- # directory for this command is .../build/src/chrome but we need
- # to be in .../build/src for the relative path of source files
- # to be correct. However, when run from buildbot, the current
- # directory is .../build. Accommodate.
- # On Mac source files are compiled with abs paths so this isn't
- # a problem.
- # This is a bit of a hack. The best answer is to require this
- # script be run in a specific directory for all cases (from
- # Makefile or from buildbot).
- if start_dir.endswith('chrome'):
- logging.info('coverage_posix.py: doing a "cd .." '
- 'to accomodate Linux/make PWD')
- os.chdir('..')
- elif start_dir.endswith('build'):
- logging.info('coverage_posix.py: doing a "cd src" '
- 'to accomodate buildbot PWD')
- os.chdir('src')
- else:
- logging.info('coverage_posix.py: NOT changing directory.')
- elif self.IsMac():
- pass
-
- command = [self.mcov,
- '--directory',
- os.path.join(start_dir, self.directory_parent),
- '--output',
- os.path.join(start_dir, self.coverage_info_file)]
- logging.info('Assembly command: ' + ' '.join(command))
- retcode = subprocess.call(command)
- if retcode:
- logging.fatal('COVERAGE: %s failed; return code: %d' %
- (command[0], retcode))
- if self.options.strict:
- sys.exit(retcode)
- if self.IsLinux():
- os.chdir(start_dir)
-
- # Copy the unittests coverage information to a different folder.
- if self.options.all_unittests:
- self.CopyCoverageFileToDestination('unittests_coverage')
- elif self.options.all_browsertests:
- # Save browsertests only coverage information.
- self.CopyCoverageFileToDestination('browsertests_coverage')
- else:
- # Save the overall coverage information.
- self.CopyCoverageFileToDestination('total_coverage')
-
- if not os.path.exists(self.coverage_info_file):
- logging.fatal('%s was not created. Coverage run failed.' %
- self.coverage_info_file)
- sys.exit(1)
-
- def GenerateLcovWindows(self, testname=None):
- """Convert VSTS format to lcov. Appends coverage data to sum file."""
- lcov_file = self.vsts_output + '.lcov'
- if os.path.exists(lcov_file):
- os.remove(lcov_file)
- # generates the file (self.vsts_output + ".lcov")
-
- cmdlist = [self.analyzer,
- '-sym_path=' + self.directory,
- '-src_root=' + self.src_root,
- '-noxml',
- self.vsts_output]
- self.Run(cmdlist)
- if not os.path.exists(lcov_file):
- logging.fatal('Output file %s not created' % lcov_file)
- sys.exit(1)
- logging.info('Appending lcov for test %s to %s' %
- (testname, self.coverage_info_file))
- size_before = 0
- if os.path.exists(self.coverage_info_file):
- size_before = os.stat(self.coverage_info_file).st_size
- src = open(lcov_file, 'r')
- dst = open(self.coverage_info_file, 'a')
- dst.write(src.read())
- src.close()
- dst.close()
- size_after = os.stat(self.coverage_info_file).st_size
- logging.info('Lcov file growth for %s: %d --> %d' %
- (self.coverage_info_file, size_before, size_after))
-
- def GenerateHtml(self):
- """Convert lcov to html."""
- # TODO(jrg): This isn't happy when run with unit_tests since V8 has a
- # different "base" so V8 includes can't be found in ".". Fix.
- command = [self.genhtml,
- self.coverage_info_file,
- '--output-directory',
- self.output_directory]
- print >>sys.stderr, 'html generation command: ' + ' '.join(command)
- retcode = subprocess.call(command)
- if retcode:
- logging.fatal('COVERAGE: %s failed; return code: %d' %
- (command[0], retcode))
- if self.options.strict:
- sys.exit(retcode)
-
-def CoverageOptionParser():
- """Return an optparse.OptionParser() suitable for Coverage object creation."""
- parser = optparse.OptionParser()
- parser.add_option('-d',
- '--directory',
- dest='directory',
- default=None,
- 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)')
- parser.add_option('-b',
- '--all_browsertests',
- dest='all_browsertests',
- default=False,
- help='Run all tests in browser_tests '
- 'and content_browsertests')
- parser.add_option('-g',
- '--genhtml',
- dest='genhtml',
- default=False,
- help='Generate html from lcov output')
- parser.add_option('-f',
- '--fast_test',
- dest='fast_test',
- default=False,
- help='Make the tests run REAL fast by doing little.')
- parser.add_option('-s',
- '--strict',
- dest='strict',
- default=False,
- help='Be strict and die on test failure.')
- parser.add_option('-S',
- '--src_root',
- dest='src_root',
- default='.',
- help='Source root (only used on Windows)')
- parser.add_option('-t',
- '--trim',
- dest='trim',
- default=True,
- help='Trim out tests? Default True.')
- parser.add_option('-x',
- '--xvfb',
- dest='xvfb',
- default=True,
- help='Use Xvfb for tests? Default True.')
- parser.add_option('-T',
- '--timeout',
- dest='timeout',
- default=5.0 * 60.0,
- type="int",
- help='Timeout before bailing if a subprocess has no output.'
- ' Default is 5min (Buildbot is 10min.)')
- parser.add_option('-B',
- '--bundles',
- dest='bundles',
- default=None,
- help='Filename of bundles for coverage.')
- parser.add_option('--build-dir',
- dest='build_dir',
- default=None,
- help=('Working directory for buildbot build.'
- 'used for finding bundlefile.'))
- parser.add_option('--target',
- dest='target',
- default=None,
- help=('Buildbot build target; '
- 'used for finding bundlefile (e.g. Debug)'))
- parser.add_option('--no_exclusions',
- dest='no_exclusions',
- default=None,
- help=('Disable the exclusion list.'))
- parser.add_option('--dont-clear-coverage-data',
- dest='dont_clear_coverage_data',
- default=False,
- action='store_true',
- help=('Turn off clearing of cov data from a prev run'))
- parser.add_option('-F',
- '--test-file',
- dest="test_files",
- default=[],
- action='append',
- help=('Specify a file from which tests to be run will ' +
- 'be extracted'))
- return parser
-
-
-def main():
- # Print out the args to help someone do it by hand if needed
- print >>sys.stderr, sys.argv
-
- # Try and clean up nice if we're killed by buildbot, Ctrl-C, ...
- signal.signal(signal.SIGINT, TerminateSignalHandler)
- signal.signal(signal.SIGTERM, TerminateSignalHandler)
-
- parser = CoverageOptionParser()
- (options, args) = parser.parse_args()
- if options.all_unittests and options.all_browsertests:
- print 'Error! Can not have all_unittests and all_browsertests together!'
- sys.exit(1)
- coverage = Coverage(options, args)
- coverage.ClearData()
- coverage.FindTests()
- if options.trim:
- coverage.TrimTests()
- coverage.RunTests()
- if options.genhtml:
- coverage.GenerateHtml()
- return 0
-
-
-if __name__ == '__main__':
- sys.exit(main())
diff --git a/chromium/tools/code_coverage/coverage_posix_unittest.py b/chromium/tools/code_coverage/coverage_posix_unittest.py
deleted file mode 100755
index 3164cd3359a..00000000000
--- a/chromium/tools/code_coverage/coverage_posix_unittest.py
+++ /dev/null
@@ -1,142 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2010 The Chromium Authors. All rights reserved.
-# 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.
-
-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."""
- self.parser = coverage.CoverageOptionParser()
- (self.options, self.args) = self.parser.parse_args()
- self.options.directory = '.'
-
- def testSanity(self):
- """Sanity check we're able to actually run the tests.
-
- Simply creating a Coverage instance checks a few things (e.g. on
- Windows that the coverage tools can be found)."""
- c = coverage.Coverage(self.options, self.args)
-
- def testRunBasicProcess(self):
- """Test a simple run of a subprocess."""
- c = coverage.Coverage(self.options, self.args)
- for code in range(2):
- retcode = c.Run([sys.executable, '-u', '-c',
- 'import sys; sys.exit(%d)' % code],
- ignore_error=True)
- self.assertEqual(code, retcode)
-
- def testRunSlowProcess(self):
- """Test program which prints slowly but doesn't hit our timeout.
-
- Overall runtime is longer than the timeout but output lines
- trickle in keeping things alive.
- """
- self.options.timeout = 2.5
- c = coverage.Coverage(self.options, self.args)
- slowscript = ('import sys, time\n'
- 'for x in range(10):\n'
- ' time.sleep(0.5)\n'
- ' print "hi mom"\n'
- 'sys.exit(0)\n')
- retcode = c.Run([sys.executable, '-u', '-c', slowscript])
- self.assertEqual(0, retcode)
-
- def testRunExcessivelySlowProcess(self):
- """Test program which DOES hit our timeout.
-
- Initial lines should print but quickly it takes too long and
- should be killed.
- """
- self.options.timeout = 2.5
- c = coverage.Coverage(self.options, self.args)
- slowscript = ('import time\n'
- 'for x in range(1,10):\n'
- ' print "sleeping for %d" % x\n'
- ' time.sleep(x)\n')
- self.assertRaises(Exception,
- 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)
-
- def testExclusionList(self):
- """Test the gtest_filter exclusion list."""
- c = coverage.Coverage(self.options, self.args)
- self.assertFalse(c.GtestFilter('doesnotexist_test'))
- fake_exclusions = { sys.platform: { 'foobar':
- ('a','b'),
- 'doesnotexist_test':
- ('Evil.Crash','Naughty.Test') } }
- self.assertFalse(c.GtestFilter('barfoo'))
- filter = c.GtestFilter('doesnotexist_test', fake_exclusions)
- self.assertEquals('--gtest_filter=-Evil.Crash:-Naughty.Test', filter)
-
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/chromium/tools/code_coverage/process_coverage.py b/chromium/tools/code_coverage/process_coverage.py
deleted file mode 100755
index 07d83ac2c35..00000000000
--- a/chromium/tools/code_coverage/process_coverage.py
+++ /dev/null
@@ -1,413 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2011 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-
-"""Script to clean the lcov files and convert it to HTML
-
-TODO(niranjan): Add usage information here
-"""
-
-
-import optparse
-import os
-import shutil
-import subprocess
-import sys
-import tempfile
-import time
-import urllib2
-
-
-# These are source files that were generated during compile time. We want to
-# remove references to these files from the lcov file otherwise genhtml will
-# throw an error.
-win32_srcs_exclude = ['parse.y',
- 'xpathgrammar.cpp',
- 'cssgrammar.cpp',
- 'csspropertynames.gperf']
-
-# Number of lines of a new coverage data set
-# to send at a time to the dashboard.
-POST_CHUNK_SIZE = 50
-
-# Number of post request failures to allow before exiting.
-MAX_FAILURES = 5
-
-def CleanPathNames(dir):
- """Clean the pathnames of the HTML generated by genhtml.
-
- This method is required only for code coverage on Win32. Due to a known issue
- with reading from CIFS shares mounted on Linux, genhtml appends a ^M to every
- file name it reads from the Windows share, causing corrupt filenames in
- genhtml's output folder.
-
- Args:
- dir: Output folder of the genhtml output.
-
- Returns:
- None
- """
- # Stip off the ^M characters that get appended to the file name
- for dirpath, dirname, filenames in os.walk(dir):
- for file in filenames:
- file_clean = file.replace('\r', '')
- if file_clean != file:
- os.rename(file, file_clean)
-
-
-def GenerateHtml(lcov_path, dash_root):
- """Runs genhtml to convert lcov data to human readable HTML.
-
- This script expects the LCOV file name to be in the format:
- chrome_<platform>_<revision#>.lcov.
- This method parses the file name and then sets up the correct folder
- hierarchy for the coverage data and then runs genhtml to get the actual HTML
- formatted coverage data.
-
- Args:
- lcov_path: Path of the lcov data file.
- dash_root: Root location of the dashboard.
-
- Returns:
- Code coverage percentage on sucess.
- None on failure.
- """
- # Parse the LCOV file name.
- filename = os.path.basename(lcov_path).split('.')[0]
- buffer = filename.split('_')
- dash_root = dash_root.rstrip('/') # Remove trailing '/'
-
- # Set up correct folder hierarchy in the dashboard root
- # TODO(niranjan): Check the formatting using a regexp
- if len(buffer) >= 3: # Check if filename has right formatting
- platform = buffer[len(buffer) - 2]
- revision = buffer[len(buffer) - 1]
- if os.path.exists(os.path.join(dash_root, platform)) == False:
- os.mkdir(os.path.join(dash_root, platform))
- output_dir = os.path.join(dash_root, platform, revision)
- os.mkdir(output_dir)
- else:
- # TODO(niranjan): Add failure logging here.
- return None # File not formatted correctly
-
- # Run genhtml
- os.system('/usr/bin/genhtml -o %s %s' % (output_dir, lcov_path))
- # TODO(niranjan): Check the exit status of the genhtml command.
- # TODO(niranjan): Parse the stdout and return coverage percentage.
- CleanPathNames(output_dir)
- return 'dummy' # TODO(niranjan): Return actual percentage.
-
-
-def CleanWin32Lcov(lcov_path, src_root):
- """Cleanup the lcov data generated on Windows.
-
- This method fixes up the paths inside the lcov file from the Win32 specific
- paths to the actual paths of the mounted CIFS share. The lcov files generated
- on Windows have the following format:
-
- SF:c:\chrome_src\src\skia\sgl\skscan_antihair.cpp
- DA:97,0
- DA:106,0
- DA:107,0
- DA:109,0
- ...
- end_of_record
-
- This method changes the source-file (SF) lines to a format compatible with
- genhtml on Linux by fixing paths. This method also removes references to
- certain dynamically generated files to be excluded from the code ceverage.
-
- Args:
- lcov_path: Path of the Win32 lcov file to be cleaned.
- src_root: Location of the source and symbols dir.
- Returns:
- None
- """
- strip_flag = False
- lcov = open(lcov_path, 'r')
- loc_csv_file = open(lcov_path + '.csv', 'w')
- (tmpfile_id, tmpfile_name) = tempfile.mkstemp()
- tmpfile = open(tmpfile_name, 'w')
- src_root = src_root.rstrip('/') # Remove trailing '/'
- for line in lcov:
- if line.startswith('SF'):
- # We want to exclude certain auto-generated files otherwise genhtml will
- # fail to convert lcov to HTML.
- for exp in win32_srcs_exclude:
- if line.rfind(exp) != -1:
- strip_flag = True # Indicates that we want to remove this section
-
- # Now we normalize the paths
- # e.g. Change SF:c:\foo\src\... to SF:/chrome_src/...
- parse_buffer = line.split(':')
- buffer = '%s:%s%s' % (parse_buffer[0],
- src_root,
- parse_buffer[2])
- buffer = buffer.replace('\\', '/')
- line = buffer.replace('\r', '')
-
- # We want an accurate count of the lines of code in a given file so that
- # we can estimate the code coverage perscentage accurately. We use a
- # third party script cloc.pl which gives that count and then just parse
- # its command line output to filter out the other unnecessary data.
- # TODO(niranjan): Find out a better way of doing this.
- buffer = buffer.lstrip('SF:')
- file_for_loc = buffer.replace('\r\n', '')
- # TODO(niranjan): Add a check to see if cloc is present on the machine.
- command = ["perl",
- "cloc.pl",
- file_for_loc]
- output = subprocess.Popen(command,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT).communicate()[0]
- if output.rfind('error:'):
- return None
-
- tmp_buf1 = output.split('=')
- tmp_buf2 = tmp_buf1[len(tmp_buf1) - 2].split('x')[0].split(' ')
- loc = tmp_buf2[len(tmp_buf2) - 2]
- loc_csv_file.write('%s,%s\r\n' % (file_for_loc, loc))
-
- # Write to the temp file if the section to write is valid
- if strip_flag == False:
- # Also write this to the 'clean' LCOV file
- tmpfile.write('%s' % (line))
-
- # Reset the strip flag
- if line.endswith('end_of_record'):
- strip_flag = False
-
- # Close the files and replace the lcov file by the 'clean' tmpfile
- tmpfile.close()
- lcov.close()
- loc_csv_file.close()
- shutil.move(tmpfile_name, lcov_path)
-
-
-def ParseCoverageDataForDashboard(lcov_path):
- """Parse code coverage data into coverage results per source node.
-
- Use lcov and linecount data to create a map of source nodes to
- corresponding total and tested line counts.
-
- Args:
- lcov_path: File path to lcov coverage data.
-
- Returns:
- List of strings with comma separated source node and coverage.
- """
- results = {}
- linecount_path = lcov_path + '.csv'
- assert(os.path.exists(linecount_path),
- 'linecount csv does not exist at: %s' % linecount_path)
- csv_file = open(linecount_path, 'r')
- linecounts = csv_file.readlines()
- csv_file.close()
- lcov_file = open(lcov_path, 'r')
- srcfile_index = 0
- for line in lcov_file:
- line = line.strip()
-
- # Set the current srcfile name for a new src file declaration.
- if line[:len('SF:')] == 'SF:':
- instrumented_set = {}
- executed_set = {}
- srcfile_name = line[len('SF:'):]
-
- # Mark coverage data points hashlist style for the current src file.
- if line[:len('DA:')] == 'DA:':
- line_info = line[len('DA:'):].split(',')
- assert(len(line_info) == 2, 'DA: line format unexpected - %s' % line)
- (line_num, line_was_executed) = line_info
- instrumented_set[line_num] = True
- # line_was_executed is '0' or '1'
- if int(line_was_executed):
- executed_set[line_num] = True
-
- # Update results for the current src file at record end.
- if line == 'end_of_record':
- instrumented = len(instrumented_set.keys())
- executed = len(executed_set.keys())
- parent_directory = srcfile_name[:srcfile_name.rfind('/') + 1]
- linecount_point = linecounts[srcfile_index].strip().split(',')
- assert(len(linecount_point) == 2,
- 'lintcount format unexpected - %s' % linecounts[srcfile_index])
- (linecount_path, linecount_count) = linecount_point
- srcfile_index += 1
-
- # Sanity check that path names in the lcov and linecount are lined up.
- if linecount_path[-10:] != srcfile_name[-10:]:
- print 'NAME MISMATCH: %s :: %s' % (srcfile_name, linecount_path)
- if instrumented > int(linecount_count):
- linecount_count = instrumented
-
- # Keep counts the same way that it is done in the genhtml utility.
- # Count the coverage of a file towards the file,
- # the parent directory, and the source root.
- AddResults(results, srcfile_name, int(linecount_count), executed)
- AddResults(results, parent_directory, int(linecount_count), executed)
- AddResults(results, '/', instrumented, executed)
-
- lcov_file.close()
- keys = results.keys()
- keys.sort()
- # The first key (sorted) will be the base directory '/'
- # but its full path may be '/mnt/chrome_src/src/'
- # using this offset will ignore the part '/mnt/chrome_src/src'.
- # Offset is the last '/' that isn't the last character for the
- # first directory name in results (position 1 in keys).
- offset = len(keys[1][:keys[1][:-1].rfind('/')])
- lines = []
- for key in keys:
- if len(key) > offset:
- node_path = key[offset:]
- else:
- node_path = key
- (total, covered) = results[key]
- percent = float(covered) * 100 / total
- lines.append('%s,%.2f' % (node_path, percent))
- return lines
-
-
-def AddResults(results, location, lines_total, lines_executed):
- """Add resulting line tallies to a location's total.
-
- Args:
- results: Map of node location to corresponding coverage data.
- location: Source node string.
- lines_total: Number of lines to add to the total count for this node.
- lines_executed: Number of lines to add to the executed count for this node.
- """
- if results.has_key(location):
- (i, e) = results[location]
- results[location] = (i + lines_total, e + lines_executed)
- else:
- results[location] = (lines_total, lines_executed)
-
-
-def PostResultsToDashboard(lcov_path, results, post_url):
- """Post coverage results to coverage dashboard.
-
- Args:
- lcov_path: File path for lcov data in the expected format:
- <project>_<platform>_<cl#>.coverage.lcov
- results: string list in the appropriate posting format.
- """
- project_platform_cl = lcov_path.split('.')[0].split('_')
- assert(len(project_platform_cl) == 3,
- 'lcov_path not in expected format: %s' % lcov_path)
- (project, platform, cl_string) = project_platform_cl
- project_name = '%s-%s' % (project, platform)
- url = '%s/newdata.do?project=%s&cl=%s' % (post_url, project_name, cl_string)
-
- # Send POSTs of POST_CHUNK_SIZE lines of the result set until
- # there is no more data and last_loop is set to True.
- last_loop = False
- cur_line = 0
- while not last_loop:
- body = '\n'.join(results[cur_line:cur_line + POST_CHUNK_SIZE])
- cur_line += POST_CHUNK_SIZE
- last_loop = (cur_line >= len(results))
- req = urllib2.Request('%s&last=%s' % (url, str(last_loop)), body)
- req.add_header('Content-Type', 'text/plain')
- SendPost(req)
-
-
-# Global counter for the current number of request failures.
-num_fails = 0
-
-def SendPost(req):
- """Execute a post request and retry for up to MAX_FAILURES.
-
- Args:
- req: A urllib2 request object.
-
- Raises:
- URLError: If urlopen throws after too many retries.
- HTTPError: If urlopen throws after too many retries.
- """
- global num_fails
- try:
- urllib2.urlopen(req)
- # Reset failure count.
- num_fails = 0
- except (urllib2.URLError, urllib2.HTTPError):
- num_fails += 1
- if num_fails < MAX_FAILURES:
- print 'fail, retrying (%d)' % num_fails
- time.sleep(5)
- SendPost(req)
- else:
- print 'POST request exceeded allowed retries.'
- raise
-
-
-def main():
- if not sys.platform.startswith('linux'):
- print 'This script is supported only on Linux'
- return 0
-
- # Command line parsing
- parser = optparse.OptionParser()
- parser.add_option('-p',
- '--platform',
- dest='platform',
- default=None,
- help=('Platform that the locv file was generated on. Must'
- 'be one of {win32, linux2, linux3, macosx}'))
- parser.add_option('-s',
- '--source',
- dest='src_dir',
- default=None,
- help='Path to the source code and symbols')
- parser.add_option('-d',
- '--dash_root',
- dest='dash_root',
- default=None,
- help='Root directory for the dashboard')
- parser.add_option('-l',
- '--lcov',
- dest='lcov_path',
- default=None,
- help='Location of the LCOV file to process')
- parser.add_option('-u',
- '--post_url',
- dest='post_url',
- default=None,
- help='Base URL of the coverage dashboard')
- (options, args) = parser.parse_args()
-
- if options.platform == None:
- parser.error('Platform not specified')
- if options.lcov_path == None:
- parser.error('lcov file path not specified')
- if options.src_dir == None:
- parser.error('Source directory not specified')
- if options.dash_root == None:
- parser.error('Dashboard root not specified')
- if options.post_url == None:
- parser.error('Post URL not specified')
- if options.platform == 'win32':
- CleanWin32Lcov(options.lcov_path, options.src_dir)
- percent = GenerateHtml(options.lcov_path, options.dash_root)
- if percent == None:
- # TODO(niranjan): Add logging.
- print 'Failed to generate code coverage'
- return 1
- else:
- # TODO(niranjan): Do something with the code coverage numbers
- pass
- else:
- print 'Unsupported platform'
- return 1
-
- # Prep coverage results for dashboard and post new set.
- parsed_data = ParseCoverageDataForDashboard(options.lcov_path)
- PostResultsToDashboard(options.lcov_path, parsed_data, options.post_url)
- return 0
-
-
-if __name__ == '__main__':
- sys.exit(main())