2012-02-01 11:59:23 +01:00
|
|
|
#!/usr/bin/env python
|
|
|
|
#-*- coding: utf-8 -*-
|
|
|
|
# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
|
|
|
#
|
|
|
|
# Use of this source code is governed by a BSD-style license
|
|
|
|
# that can be found in the LICENSE file in the root of the source
|
|
|
|
# tree. An additional intellectual property rights grant can be found
|
|
|
|
# in the file PATENTS. All contributing project authors may
|
|
|
|
# be found in the AUTHORS file in the root of the source tree.
|
|
|
|
|
|
|
|
"""This script grabs and reports coverage information.
|
|
|
|
|
|
|
|
It grabs coverage information from the latest Linux 32-bit build and
|
|
|
|
pushes it to the coverage tracker, enabling us to track code coverage
|
|
|
|
over time. This script is intended to run on the 32-bit Linux slave.
|
|
|
|
|
|
|
|
This script requires an access.token file in the current directory, as
|
|
|
|
generated by the request_oauth_permission.py script. It also expects a file
|
|
|
|
customer.secret with a single line containing the customer secret. The
|
|
|
|
customer secret is an OAuth concept and is received when one registers the
|
|
|
|
application with the App Engine running the dashboard.
|
|
|
|
|
|
|
|
The script assumes that all coverage data is stored under
|
|
|
|
/home/<build bot user>/www.
|
|
|
|
"""
|
|
|
|
|
|
|
|
__author__ = 'phoglund@webrtc.org (Patrik Höglund)'
|
|
|
|
|
|
|
|
import os
|
|
|
|
import re
|
2012-03-12 10:12:32 +01:00
|
|
|
import sys
|
2012-02-01 11:59:23 +01:00
|
|
|
import time
|
|
|
|
|
|
|
|
import constants
|
|
|
|
import dashboard_connection
|
|
|
|
|
|
|
|
|
|
|
|
class FailedToParseCoverageHtml(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class CouldNotFindCoverageDirectory(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2012-03-12 10:12:32 +01:00
|
|
|
def _find_latest_build_coverage(www_directory_contents, coverage_www_dir,
|
|
|
|
directory_prefix):
|
|
|
|
"""Finds the most recent coverage directory in the directory listing.
|
2012-02-01 11:59:23 +01:00
|
|
|
|
2012-03-12 10:12:32 +01:00
|
|
|
We assume here that build numbers keep rising and never wrap around.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
www_directory_contents: A list of entries in the coverage directory.
|
|
|
|
coverage_www_dir: The coverage directory on the bot.
|
|
|
|
directory_prefix: Coverage directories have the form <prefix><number>,
|
|
|
|
and the prefix is different on different bots. The prefix is
|
|
|
|
generally the builder name, such as Linux32DBG.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
The most recent directory name.
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
CouldNotFindCoverageDirectory: if we failed to find coverage data.
|
2012-02-01 11:59:23 +01:00
|
|
|
"""
|
|
|
|
|
2012-03-01 16:50:30 +01:00
|
|
|
found_build_numbers = []
|
2012-02-01 11:59:23 +01:00
|
|
|
for entry in www_directory_contents:
|
2012-03-12 10:12:32 +01:00
|
|
|
match = re.match(directory_prefix + '(\d+)', entry)
|
2012-02-01 11:59:23 +01:00
|
|
|
if match is not None:
|
2012-03-01 16:50:30 +01:00
|
|
|
found_build_numbers.append(int(match.group(1)))
|
|
|
|
|
|
|
|
if not found_build_numbers:
|
2012-03-12 10:12:32 +01:00
|
|
|
raise CouldNotFindCoverageDirectory('Error: Found no directories %s* '
|
|
|
|
'in directory %s.' %
|
|
|
|
(directory_prefix, coverage_www_dir))
|
2012-02-01 11:59:23 +01:00
|
|
|
|
2012-03-01 16:50:30 +01:00
|
|
|
most_recent = max(found_build_numbers)
|
2012-03-12 10:12:32 +01:00
|
|
|
return directory_prefix + str(most_recent)
|
2012-02-01 11:59:23 +01:00
|
|
|
|
|
|
|
|
|
|
|
def _grab_coverage_percentage(label, index_html_contents):
|
|
|
|
"""Extracts coverage from a LCOV coverage report.
|
|
|
|
|
|
|
|
Grabs coverage by assuming that the label in the coverage HTML report
|
|
|
|
is close to the actual number and that the number is followed by a space
|
|
|
|
and a percentage sign.
|
|
|
|
"""
|
|
|
|
match = re.search('<td[^>]*>' + label + '</td>.*?(\d+\.\d) %',
|
|
|
|
index_html_contents, re.DOTALL)
|
|
|
|
if match is None:
|
|
|
|
raise FailedToParseCoverageHtml('Missing coverage at label "%s".' % label)
|
|
|
|
|
|
|
|
try:
|
|
|
|
return float(match.group(1))
|
|
|
|
except ValueError:
|
|
|
|
raise FailedToParseCoverageHtml('%s is not a float.' % match.group(1))
|
|
|
|
|
|
|
|
|
2012-03-12 10:12:32 +01:00
|
|
|
def _report_coverage_to_dashboard(dashboard, line_coverage, function_coverage,
|
|
|
|
branch_coverage, report_category):
|
|
|
|
parameters = {'line_coverage': '%f' % line_coverage,
|
|
|
|
'function_coverage': '%f' % function_coverage,
|
|
|
|
'branch_coverage': '%f' % branch_coverage,
|
|
|
|
'report_category': report_category,
|
2012-02-01 11:59:23 +01:00
|
|
|
}
|
|
|
|
|
2012-02-06 11:55:12 +01:00
|
|
|
dashboard.send_post_request(constants.ADD_COVERAGE_DATA_URL, parameters)
|
2012-02-01 11:59:23 +01:00
|
|
|
|
|
|
|
|
2012-03-12 10:12:32 +01:00
|
|
|
def _main(report_category, directory_prefix):
|
|
|
|
"""Grabs coverage data from disk on a bot and publishes it.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
report_category: The kind of coverage to report. The dashboard
|
|
|
|
application decides what is acceptable here (see
|
|
|
|
dashboard/add_coverage_data.py for more information).
|
|
|
|
directory_prefix: This bot's coverage directory prefix. Generally a bot's
|
|
|
|
coverage directories will have the form <prefix><build number>,
|
|
|
|
like Linux32DBG_345.
|
|
|
|
"""
|
2012-02-01 11:59:23 +01:00
|
|
|
dashboard = dashboard_connection.DashboardConnection(constants.CONSUMER_KEY)
|
|
|
|
dashboard.read_required_files(constants.CONSUMER_SECRET_FILE,
|
|
|
|
constants.ACCESS_TOKEN_FILE)
|
|
|
|
|
2012-03-01 16:50:30 +01:00
|
|
|
coverage_www_dir = constants.BUILD_BOT_COVERAGE_WWW_DIRECTORY
|
|
|
|
www_dir_contents = os.listdir(coverage_www_dir)
|
2012-03-12 10:12:32 +01:00
|
|
|
latest_build_directory = _find_latest_build_coverage(www_dir_contents,
|
|
|
|
coverage_www_dir,
|
|
|
|
directory_prefix)
|
2012-02-01 11:59:23 +01:00
|
|
|
|
|
|
|
index_html_path = os.path.join(coverage_www_dir, latest_build_directory,
|
|
|
|
'index.html')
|
|
|
|
index_html_file = open(index_html_path)
|
|
|
|
whole_file = index_html_file.read()
|
|
|
|
|
|
|
|
line_coverage = _grab_coverage_percentage('Lines:', whole_file)
|
|
|
|
function_coverage = _grab_coverage_percentage('Functions:', whole_file)
|
2012-03-12 10:12:32 +01:00
|
|
|
branch_coverage = _grab_coverage_percentage('Branches:', whole_file)
|
|
|
|
|
|
|
|
_report_coverage_to_dashboard(dashboard, line_coverage, function_coverage,
|
|
|
|
branch_coverage, report_category)
|
|
|
|
|
2012-02-01 11:59:23 +01:00
|
|
|
|
2012-03-12 10:12:32 +01:00
|
|
|
def _parse_args():
|
|
|
|
if len(sys.argv) != 3:
|
|
|
|
print ('Usage: %s <coverage category> <directory prefix>\n\n'
|
|
|
|
'The coverage category describes the kind of coverage you are '
|
|
|
|
'uploading. Known acceptable values are small_medium_tests and'
|
|
|
|
'large_tests. The directory prefix is what the directories in %s '
|
|
|
|
'are prefixed on this bot (such as Linux32DBG_).' %
|
|
|
|
(sys.argv[0], constants.BUILD_BOT_COVERAGE_WWW_DIRECTORY))
|
|
|
|
return (None, None)
|
|
|
|
return (sys.argv[1], sys.argv[2])
|
2012-02-01 11:59:23 +01:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2012-03-12 10:12:32 +01:00
|
|
|
report_category, directory_prefix = _parse_args()
|
|
|
|
if report_category:
|
|
|
|
_main(report_category, directory_prefix)
|
2012-02-01 11:59:23 +01:00
|
|
|
|