96c39d1f73
The access token is now transferred between the request and track scripts. Almost completed the implementation, using oauth2. Initial, incomplete implementation of the permission-request script. The coverage tracking is broken temporarily, but it can now make OAuth requests. BUG= TEST= Review URL: https://webrtc-codereview.appspot.com/366002 git-svn-id: http://webrtc.googlecode.com/svn/trunk@1530 4adac7df-926f-26a2-2b94-8c16560cd09d
185 lines
6.5 KiB
Python
Executable File
185 lines
6.5 KiB
Python
Executable File
#!/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 appengine 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 httplib
|
|
import os
|
|
import re
|
|
import shelve
|
|
import sys
|
|
import time
|
|
import oauth.oauth as oauth
|
|
|
|
# The build-bot user which runs build bot jobs.
|
|
BUILD_BOT_USER = 'phoglund'
|
|
|
|
# The server to send coverage data to.
|
|
# TODO(phoglund): replace with real server once we get it up.
|
|
DASHBOARD_SERVER = 'localhost:8080'
|
|
CONSUMER_KEY = DASHBOARD_SERVER
|
|
ADD_COVERAGE_DATA_URL = '/add_coverage_data'
|
|
|
|
|
|
class FailedToParseCoverageHtml(Exception):
|
|
pass
|
|
|
|
|
|
class FailedToReportToDashboard(Exception):
|
|
pass
|
|
|
|
|
|
class FailedToReadRequiredInputFile(Exception):
|
|
pass
|
|
|
|
|
|
def _read_access_token_from_file(filename):
|
|
input_file = shelve.open(filename)
|
|
|
|
if not input_file.has_key('access_token'):
|
|
raise FailedToReadRequiredInputFile('Missing %s file in current directory. '
|
|
'You may have to run '
|
|
'request_oauth_permission.py.')
|
|
|
|
return oauth.OAuthToken.from_string(input_file['access_token'])
|
|
|
|
|
|
def _read_customer_secret_from_file(filename):
|
|
try:
|
|
input_file = open(filename, 'r')
|
|
except IOError as error:
|
|
raise FailedToReadRequiredInputFile(error)
|
|
|
|
whole_file = input_file.read()
|
|
if '\n' in whole_file or not whole_file.strip():
|
|
raise FailedToReadRequiredInputFile('Expected a single line with the '
|
|
'customer secret in file %s.' %
|
|
filename)
|
|
return whole_file
|
|
|
|
|
|
def _find_latest_32bit_debug_build(www_directory_contents):
|
|
# Build directories have the form Linux32bitDebug_<number>. There may be other
|
|
# directories in the list though, for instance for other build configurations.
|
|
# This sort ensures we will encounter the directory with the highest number
|
|
# first.
|
|
www_directory_contents.sort(reverse=True)
|
|
|
|
for entry in www_directory_contents:
|
|
match = re.match('Linux32bitDBG_\d+', entry)
|
|
if match is not None:
|
|
return entry
|
|
|
|
# Didn't find it
|
|
return None
|
|
|
|
|
|
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))
|
|
|
|
|
|
def _report_coverage_to_dashboard(now, line_coverage, function_coverage,
|
|
access_token, consumer_key, consumer_secret):
|
|
parameters = {'date': '%d' % now,
|
|
'line_coverage': '%f' % line_coverage,
|
|
'function_coverage': '%f' % function_coverage
|
|
}
|
|
consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
|
|
create_oauth_request = oauth.OAuthRequest.from_consumer_and_token
|
|
oauth_request = create_oauth_request(consumer,
|
|
token=access_token,
|
|
http_method='POST',
|
|
http_url=ADD_COVERAGE_DATA_URL,
|
|
parameters=parameters)
|
|
|
|
signature_method_hmac_sha1 = oauth.OAuthSignatureMethod_HMAC_SHA1()
|
|
oauth_request.sign_request(signature_method_hmac_sha1, consumer, access_token)
|
|
|
|
connection = httplib.HTTPConnection(DASHBOARD_SERVER)
|
|
|
|
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
|
|
connection.request('POST', ADD_COVERAGE_DATA_URL,
|
|
body=oauth_request.to_postdata(),
|
|
headers=headers)
|
|
|
|
response = connection.getresponse()
|
|
if response.status != 200:
|
|
message = ('Error: Failed to report to %s%s: got response %d (%s)' %
|
|
(DASHBOARD_SERVER, request_string, response.status,
|
|
response.reason))
|
|
raise FailedToReportToDashboard(message)
|
|
|
|
# The response content should be empty on success, so check that:
|
|
response_content = response.read()
|
|
if response_content:
|
|
message = ('Error: Dashboard reported the following error: %s.' %
|
|
response_content)
|
|
raise FailedToReportToDashboard(message)
|
|
|
|
|
|
def _main():
|
|
access_token = _read_access_token_from_file('access.token')
|
|
customer_secret = _read_customer_secret_from_file('customer.secret')
|
|
|
|
coverage_www_dir = os.path.join('/home', BUILD_BOT_USER, 'www')
|
|
|
|
www_dir_contents = os.listdir(coverage_www_dir)
|
|
latest_build_directory = _find_latest_32bit_debug_build(www_dir_contents)
|
|
|
|
if latest_build_directory is None:
|
|
print 'Error: Found no 32-bit debug build in directory ' + coverage_www_dir
|
|
sys.exit(1)
|
|
|
|
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)
|
|
now = int(time.time())
|
|
|
|
_report_coverage_to_dashboard(now, line_coverage, function_coverage,
|
|
access_token, CONSUMER_KEY, customer_secret)
|
|
|
|
if __name__ == '__main__':
|
|
_main()
|
|
|