Removed build status tracking, refreshed front page.
BUG= R=kjellander@webrtc.org Review URL: https://webrtc-codereview.appspot.com/2106004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4613 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
f1fd9d0c5c
commit
c9fa0fede5
@ -23,13 +23,8 @@ REQUEST_TOKEN_URL = DASHBOARD_SERVER_HTTP + '/_ah/OAuthGetRequestToken'
|
||||
AUTHORIZE_TOKEN_URL = DASHBOARD_SERVER_HTTP + '/_ah/OAuthAuthorizeToken'
|
||||
ACCESS_TOKEN_URL = DASHBOARD_SERVER_HTTP + '/_ah/OAuthGetAccessToken'
|
||||
|
||||
# The build master URL.
|
||||
BUILD_MASTER_SERVER = 'webrtc-cb-linux-master.cbf.corp.google.com:8010'
|
||||
BUILD_MASTER_TRANSPOSED_GRID_URL = '/tgrid'
|
||||
|
||||
# Build bot constants.
|
||||
BUILD_BOT_COVERAGE_WWW_DIRECTORY = '/var/www/coverage'
|
||||
|
||||
# Dashboard data input URLs.
|
||||
ADD_COVERAGE_DATA_URL = DASHBOARD_SERVER_HTTP + '/add_coverage_data'
|
||||
ADD_BUILD_STATUS_DATA_URL = DASHBOARD_SERVER_HTTP + '/add_build_status_data'
|
||||
|
@ -1,168 +0,0 @@
|
||||
#!/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.
|
||||
|
||||
"""Implements a handler for adding build status data."""
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
from google.appengine.ext import db
|
||||
|
||||
import oauth_post_request_handler
|
||||
|
||||
VALID_STATUSES = ['OK', 'failed', 'building', 'warnings']
|
||||
|
||||
|
||||
class OrphanedBuildStatusesExistException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class BuildStatusRoot(db.Model):
|
||||
"""Exists solely to be the root parent for all build status data and to keep
|
||||
track of when the last update was made.
|
||||
|
||||
Since all build status data will refer to this as their parent,
|
||||
we can run transactions on the build status data as a whole.
|
||||
"""
|
||||
last_updated_at = db.DateTimeProperty()
|
||||
|
||||
|
||||
class BuildStatusData(db.Model):
|
||||
"""This represents one build status report from the build bot."""
|
||||
bot_name = db.StringProperty(required=True)
|
||||
revision = db.IntegerProperty(required=True)
|
||||
build_number = db.IntegerProperty(required=True)
|
||||
status = db.StringProperty(required=True)
|
||||
|
||||
|
||||
def _ensure_build_status_root_exists():
|
||||
root = db.GqlQuery('SELECT * FROM BuildStatusRoot').get()
|
||||
if not root:
|
||||
# Create a new root, but ensure we don't have any orphaned build statuses
|
||||
# (in that case, we would not have a single entity group as we desire).
|
||||
orphans = db.GqlQuery('SELECT * FROM BuildStatusData').get()
|
||||
if orphans:
|
||||
raise OrphanedBuildStatusesExistException('Parent is gone and there are '
|
||||
'orphaned build statuses in '
|
||||
'the database!')
|
||||
root = BuildStatusRoot()
|
||||
root.put()
|
||||
|
||||
return root
|
||||
|
||||
|
||||
def _filter_oauth_parameters(post_keys):
|
||||
return filter(lambda post_key: not post_key.startswith('oauth_'),
|
||||
post_keys)
|
||||
|
||||
|
||||
def _parse_status(build_number_and_status):
|
||||
parsed_status = build_number_and_status.split('--')
|
||||
if len(parsed_status) != 2:
|
||||
raise ValueError('Malformed status string %s.' % build_number_and_status)
|
||||
|
||||
parsed_build_number = int(parsed_status[0])
|
||||
status = parsed_status[1]
|
||||
|
||||
if status not in VALID_STATUSES:
|
||||
raise ValueError('Invalid status in %s.' % build_number_and_status)
|
||||
|
||||
return (parsed_build_number, status)
|
||||
|
||||
|
||||
def _parse_name(revision_and_bot_name):
|
||||
parsed_name = revision_and_bot_name.split('--')
|
||||
if len(parsed_name) != 2:
|
||||
raise ValueError('Malformed name string %s.' % revision_and_bot_name)
|
||||
|
||||
revision = parsed_name[0]
|
||||
bot_name = parsed_name[1]
|
||||
if '\n' in bot_name:
|
||||
raise ValueError('Bot name %s can not contain newlines.' % bot_name)
|
||||
|
||||
return (int(revision), bot_name)
|
||||
|
||||
|
||||
def _delete_all_with_revision(revision, build_status_root):
|
||||
query_result = db.GqlQuery('SELECT * FROM BuildStatusData '
|
||||
'WHERE revision = :1 AND ANCESTOR IS :2',
|
||||
revision, build_status_root)
|
||||
for entry in query_result:
|
||||
entry.delete()
|
||||
|
||||
|
||||
class AddBuildStatusData(oauth_post_request_handler.OAuthPostRequestHandler):
|
||||
"""Used to report build status data.
|
||||
|
||||
Build status data is reported as a POST request. The POST request, aside
|
||||
from the required oauth_* parameters should contain name-value entries that
|
||||
abide by the following rules:
|
||||
|
||||
1) The name should be on the form <revision>--<bot name>, for instance
|
||||
1568--Win32Release.
|
||||
2) The value should be on the form <build number>--<status>, for instance
|
||||
553--OK, 554--building. The status is permitted to be failed, OK or
|
||||
building.
|
||||
|
||||
Data is keyed by revision. This handler will delete all data from a revision
|
||||
if data with that revision is present in the current update, since we
|
||||
assume that more recent data is always better data. We also assume that
|
||||
an update always has complete information on a revision (e.g. the status
|
||||
for all the bots are reported in each update).
|
||||
|
||||
In particular the revision arrangement solves the problem when the latest
|
||||
revision reports 'building' for a bot. Had we not deleted the old revision
|
||||
we would first store a 'building' status for that bot and revision, and
|
||||
later store a 'OK' or 'failed' status for that bot and revision. This is
|
||||
undesirable since we don't want multiple statuses for one bot-revision
|
||||
combination. Now we will effectively update the bot's status instead.
|
||||
"""
|
||||
def _parse_and_store_data(self):
|
||||
build_status_root = _ensure_build_status_root_exists()
|
||||
build_status_data = _filter_oauth_parameters(self.request.arguments())
|
||||
|
||||
db.run_in_transaction(self._parse_and_store_data_in_transaction,
|
||||
build_status_root, build_status_data)
|
||||
|
||||
def _parse_and_store_data_in_transaction(self, build_status_root,
|
||||
build_status_data):
|
||||
|
||||
encountered_revisions = set()
|
||||
for revision_and_bot_name in build_status_data:
|
||||
build_number_and_status = self.request.get(revision_and_bot_name)
|
||||
|
||||
try:
|
||||
(build_number, status) = _parse_status(build_number_and_status)
|
||||
(revision, bot_name) = _parse_name(revision_and_bot_name)
|
||||
except ValueError as error:
|
||||
logging.warn('Invalid parameter in request: %s.' % error)
|
||||
self.response.set_status(400)
|
||||
return
|
||||
|
||||
if revision not in encountered_revisions:
|
||||
# There's new data on this revision in this update, so clear all status
|
||||
# entries with that revision. Only do this once when we first encounter
|
||||
# the revision.
|
||||
_delete_all_with_revision(revision, build_status_root)
|
||||
encountered_revisions.add(revision)
|
||||
|
||||
# Finally, write the item.
|
||||
item = BuildStatusData(parent=build_status_root,
|
||||
bot_name=bot_name,
|
||||
revision=revision,
|
||||
build_number=build_number,
|
||||
status=status)
|
||||
item.put()
|
||||
|
||||
request_posix_timestamp = float(self.request.get('oauth_timestamp'))
|
||||
request_datetime = datetime.datetime.fromtimestamp(request_posix_timestamp)
|
||||
build_status_root.last_updated_at = request_datetime
|
||||
build_status_root.put()
|
||||
|
@ -5,13 +5,16 @@ api_version: 1
|
||||
threadsafe: false
|
||||
|
||||
handlers:
|
||||
# Serve stylesheets statically.
|
||||
# Serve stylesheets, perf dashboard, and images statically.
|
||||
- url: /stylesheets
|
||||
static_dir: stylesheets
|
||||
|
||||
# Serve perf dashboard files statically.
|
||||
- url: /perf
|
||||
static_dir: static
|
||||
- url: /images
|
||||
static_dir: static
|
||||
- url: /lkgr
|
||||
static_files: static/lkgr_redirect.html
|
||||
upload: static/lkgr_redirect.html
|
||||
|
||||
# This magic file is here to prove to the Google Account Domain Management
|
||||
# that we own this domain. It needs to stay there so the domain management
|
||||
@ -26,4 +29,4 @@ handlers:
|
||||
|
||||
# Redirect all other requests to our dynamic handlers.
|
||||
- url: /.*
|
||||
script: main.app
|
||||
script: main.app
|
||||
|
@ -15,7 +15,6 @@ import math
|
||||
from google.appengine.ext.webapp import template
|
||||
import webapp2
|
||||
|
||||
import load_build_status
|
||||
import load_coverage
|
||||
|
||||
|
||||
@ -26,23 +25,6 @@ class ShowDashboard(webapp2.RequestHandler):
|
||||
in the App Engine database using the AddCoverageData handler.
|
||||
"""
|
||||
def get(self):
|
||||
build_status_loader = load_build_status.BuildStatusLoader()
|
||||
|
||||
# Split the build status data in two rows to fit them on the page.
|
||||
# pylint: disable=W0612
|
||||
build_status_data = build_status_loader.load_build_status_data()
|
||||
split_point = int(math.ceil(len(build_status_data) / 2.0))
|
||||
build_status_data_row_1 = build_status_data[:split_point]
|
||||
build_status_data_row_2 = build_status_data[split_point:]
|
||||
|
||||
last_updated_at = build_status_loader.load_last_modified_at()
|
||||
if last_updated_at is None:
|
||||
self._show_error_page("No data has yet been uploaded to the dashboard.")
|
||||
return
|
||||
|
||||
last_updated_at = last_updated_at.strftime("%Y-%m-%d %H:%M")
|
||||
lkgr = build_status_loader.compute_lkgr()
|
||||
|
||||
coverage_loader = load_coverage.CoverageDataLoader()
|
||||
small_medium_coverage_json_data = (
|
||||
coverage_loader.load_coverage_json_data('small_medium_tests'))
|
||||
|
@ -1,29 +0,0 @@
|
||||
#!/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.
|
||||
|
||||
"""Implements the LKGR page."""
|
||||
|
||||
import webapp2
|
||||
|
||||
import load_build_status
|
||||
|
||||
class ShowLkgr(webapp2.RequestHandler):
|
||||
"""This handler shows the LKGR in the simplest possible way.
|
||||
|
||||
The page is intended to be used by automated tools.
|
||||
"""
|
||||
def get(self):
|
||||
build_status_loader = load_build_status.BuildStatusLoader()
|
||||
|
||||
lkgr = build_status_loader.compute_lkgr()
|
||||
if lkgr is None:
|
||||
self.response.out.write('No data has been uploaded to the dashboard.')
|
||||
else:
|
||||
self.response.out.write(lkgr)
|
@ -1,133 +0,0 @@
|
||||
#!/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.
|
||||
|
||||
"""Loads build status data for the dashboard."""
|
||||
|
||||
from google.appengine.ext import db
|
||||
|
||||
|
||||
def _status_not_ok(status):
|
||||
return status not in ('OK', 'warnings')
|
||||
|
||||
|
||||
def _all_ok(statuses):
|
||||
return filter(_status_not_ok, statuses) == []
|
||||
|
||||
|
||||
def _get_first_entry(iterable):
|
||||
if not iterable:
|
||||
return None
|
||||
for item in iterable:
|
||||
return item
|
||||
|
||||
|
||||
class BuildStatusLoader:
|
||||
""" Loads various build status data from the database."""
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def load_build_status_data():
|
||||
"""Returns the latest conclusive build status for each bot.
|
||||
|
||||
The statuses OK, failed and warnings are considered to be conclusive.
|
||||
|
||||
The algorithm looks at the 100 most recent status entries, which should
|
||||
give data on roughly the last five revisions if the number of bots stay
|
||||
around 20 (The number 100 should be increased if the number of bots
|
||||
increases significantly). This should give us enough data to get a
|
||||
conclusive build status for all active bots.
|
||||
|
||||
With this limit, the algorithm will adapt automatically if a bot is
|
||||
decommissioned - it will eventually disappear. The limit should not be
|
||||
too high either since we will perhaps remember offline bots too long,
|
||||
which could be confusing. The algorithm also adapts automatically to new
|
||||
bots - these show up immediately if they get a build status for a recent
|
||||
revision.
|
||||
|
||||
Returns:
|
||||
A list of BuildStatusData entities with one entity per bot.
|
||||
"""
|
||||
|
||||
build_status_entries = db.GqlQuery('SELECT * '
|
||||
'FROM BuildStatusData '
|
||||
'ORDER BY revision DESC '
|
||||
'LIMIT 100')
|
||||
|
||||
bots_to_latest_conclusive_entry = dict()
|
||||
for entry in build_status_entries:
|
||||
if entry.status == 'building':
|
||||
# The 'building' status it not conclusive, so discard this entry and
|
||||
# pick up the entry for this bot on the next revision instead. That
|
||||
# entry is guaranteed to have a status != 'building' since a bot cannot
|
||||
# be building two revisions simultaneously.
|
||||
continue
|
||||
if bots_to_latest_conclusive_entry.has_key(entry.bot_name):
|
||||
# We've already determined this bot's status.
|
||||
continue
|
||||
|
||||
bots_to_latest_conclusive_entry[entry.bot_name] = entry
|
||||
|
||||
return bots_to_latest_conclusive_entry.values()
|
||||
|
||||
@staticmethod
|
||||
def load_last_modified_at():
|
||||
build_status_root = db.GqlQuery('SELECT * '
|
||||
'FROM BuildStatusRoot').get()
|
||||
if not build_status_root:
|
||||
# Operating on completely empty database
|
||||
return None
|
||||
|
||||
return build_status_root.last_updated_at
|
||||
|
||||
@staticmethod
|
||||
def compute_lkgr():
|
||||
""" Finds the most recent revision for which all bots are green.
|
||||
|
||||
Returns:
|
||||
The last known good revision (as an integer) or None if there
|
||||
is no green revision in the database.
|
||||
|
||||
Implementation note: The data store fetches stuff as we go, so we won't
|
||||
read in the whole status table unless the LKGR is right at the end or
|
||||
we don't have a LKGR. Bots that are offline do not affect the LKGR
|
||||
computation (e.g. they are not considered to be failed).
|
||||
"""
|
||||
build_status_entries = db.GqlQuery('SELECT * '
|
||||
'FROM BuildStatusData '
|
||||
'ORDER BY revision DESC ')
|
||||
|
||||
first_entry = _get_first_entry(build_status_entries)
|
||||
if first_entry is None:
|
||||
# No entries => no LKGR
|
||||
return None
|
||||
|
||||
current_lkgr = first_entry.revision
|
||||
statuses_for_current_lkgr = [first_entry.status]
|
||||
|
||||
for entry in build_status_entries:
|
||||
if current_lkgr == entry.revision:
|
||||
statuses_for_current_lkgr.append(entry.status)
|
||||
else:
|
||||
# Starting on new revision, check previous revision.
|
||||
if _all_ok(statuses_for_current_lkgr):
|
||||
# All bots are green; LKGR found.
|
||||
return current_lkgr
|
||||
else:
|
||||
# Not all bots are green, so start over on the next revision.
|
||||
current_lkgr = entry.revision
|
||||
statuses_for_current_lkgr = [entry.status]
|
||||
|
||||
if _all_ok(statuses_for_current_lkgr):
|
||||
# There was only one revision and it was OK.
|
||||
return current_lkgr
|
||||
|
||||
# There are no all-green revision in the database.
|
||||
return None
|
@ -13,15 +13,10 @@
|
||||
from google.appengine.ext.webapp import template
|
||||
import webapp2
|
||||
|
||||
import add_build_status_data
|
||||
import add_coverage_data
|
||||
import dashboard
|
||||
import lkgr_page
|
||||
|
||||
app = webapp2.WSGIApplication([('/', dashboard.ShowDashboard),
|
||||
('/lkgr', lkgr_page.ShowLkgr),
|
||||
('/add_coverage_data',
|
||||
add_coverage_data.AddCoverageData),
|
||||
('/add_build_status_data',
|
||||
add_build_status_data.AddBuildStatusData)],
|
||||
add_coverage_data.AddCoverageData)],
|
||||
debug=True)
|
@ -1,13 +1,5 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>WebRTC Performance Metrics</title>
|
||||
<link rel="stylesheet" href="../stylesheets/perf.css" type="text/css">
|
||||
<meta HTTP-EQUIV="REFRESH" content="0; url=/">
|
||||
</head>
|
||||
<body>
|
||||
<h1>WebRTC Performance Metrics</h1>
|
||||
<p><a href="video_perf.html">Video</a></p>
|
||||
<p><a href="audio_perf.html">Audio</a></p>
|
||||
<p><a href="audio_perf_chrome.html">Chrome Audio Path</a></p>
|
||||
<p><a href="vie_auto_test_perf.html">vie_auto_test</a></p>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta HTTP-EQUIV="REFRESH"
|
||||
content="0; url=http://webrtc-status.appspot.com/lkgr">
|
||||
</head>
|
||||
</html>
|
BIN
tools/quality_tracking/dashboard/static/webrtc_logo.png
Normal file
BIN
tools/quality_tracking/dashboard/static/webrtc_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.3 KiB |
@ -30,8 +30,9 @@
|
||||
background-color: #FFC343;
|
||||
}
|
||||
|
||||
.last_known_good_revision {
|
||||
font-size: 800%;
|
||||
.perf_links {
|
||||
margin-bottom: 50px;
|
||||
font-size: 200%;
|
||||
}
|
||||
|
||||
.status_cell {
|
||||
@ -42,4 +43,4 @@
|
||||
body {
|
||||
margin-left: 35px;
|
||||
margin-top: 25px;
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
Template file to be used to generate the WebRTC dashboard.
|
||||
-->
|
||||
<head>
|
||||
<title>WebRTC Coverage Dashboard</title>
|
||||
<title>WebRTC Quality Dashboard</title>
|
||||
<meta http-equiv="refresh" content="60">
|
||||
<link href="http://code.google.com/css/codesite.pack.04102009.css"
|
||||
rel="stylesheet" type="text/css">
|
||||
@ -62,49 +62,14 @@
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>WebRTC Quality Dashboard</h1>
|
||||
<h2>Current Build Status</h2>
|
||||
<div>(as of {{ last_updated_at }} UTC)</div>
|
||||
<table>
|
||||
<tr>
|
||||
{% for entry in build_status_data_row_1 %}
|
||||
<th class="status_cell">{{ entry.bot_name }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
<tr>
|
||||
{% for entry in build_status_data_row_1 %}
|
||||
<td title="Last built revision {{ entry.revision }}"
|
||||
class="status_cell status_{{entry.status}}">
|
||||
{{entry.status}}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
<tr>
|
||||
{% for entry in build_status_data_row_2 %}
|
||||
<th class="status_cell">{{ entry.bot_name }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
<tr>
|
||||
{% for entry in build_status_data_row_2 %}
|
||||
<td title="Last built revision {{ entry.revision }}"
|
||||
class="status_cell status_{{entry.status}}">
|
||||
{{entry.status}}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</table>
|
||||
<p></p>
|
||||
|
||||
<h2>Last Known Good Revision (LKGR)</h2>
|
||||
<div class="last_known_good_revision">
|
||||
{% if lkgr %}
|
||||
<a href="http://code.google.com/p/webrtc/source/detail?r={{ lkgr }}">
|
||||
{{ lkgr }}</a>
|
||||
{% else %}
|
||||
????
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<img src="images/webrtc_logo.png">
|
||||
<h1>Performance Metrics</h1>
|
||||
<p class="perf_links">
|
||||
<a href="perf/video_perf.html">Chrome Video Quality</a> |
|
||||
<a href="perf/audio_perf.html">Chrome Audio / Voice Engine Quality</a> |
|
||||
<a href="perf/audio_perf_chrome.html">Chrome Audio Latency</a> |
|
||||
<a href="perf/vie_auto_test_perf.html">Video Engine</a>
|
||||
</p>
|
||||
<h2>Code Coverage History (Small / Medium Tests)</h2>
|
||||
<div id="table_div_small_medium_coverage"></div>
|
||||
<h2>Code Coverage History (Large Tests)</h2>
|
||||
|
@ -1,93 +0,0 @@
|
||||
#!/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.
|
||||
|
||||
"""Contains functions for parsing the build master's transposed grid page.
|
||||
|
||||
Compatible with build bot 0.8.4 P1.
|
||||
"""
|
||||
|
||||
import re
|
||||
import urllib
|
||||
|
||||
|
||||
# This is here to work around a buggy build bot status message which makes no
|
||||
# sense, but which means the build failed when the slave was lost.
|
||||
BB_084_P1_BUGGY_STATUS = 'build<br/>successful<br/>exception<br/>slave<br/>lost'
|
||||
|
||||
|
||||
class FailedToParseBuildStatus(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def _map_status(status):
|
||||
if status == 'exception' or status == BB_084_P1_BUGGY_STATUS:
|
||||
return 'failed'
|
||||
return status
|
||||
|
||||
|
||||
def _parse_builds(revision, html):
|
||||
"""Parses the bot list, which is a sequence of <td></td> lines.
|
||||
|
||||
See contract for parse_tgrid_page for more information on how this function
|
||||
behaves.
|
||||
|
||||
Example input:
|
||||
<td class="build success"><a href="builders/Android/builds/119">OK</a></td>
|
||||
The first regular expression group captures Android, second 119, third OK.
|
||||
"""
|
||||
result = {}
|
||||
|
||||
for match in re.finditer('<td.*?>.*?<a href="builders/(.+?)/builds/(\d+)">'
|
||||
'(OK|failed|building|warnings|exception|' +
|
||||
BB_084_P1_BUGGY_STATUS + ')'
|
||||
'.*?</a>.*?</td>',
|
||||
html, re.DOTALL):
|
||||
revision_and_bot_name = revision + "--" + urllib.unquote(match.group(1))
|
||||
build_number_and_status = match.group(2) + "--" + _map_status(
|
||||
match.group(3))
|
||||
|
||||
result[revision_and_bot_name] = build_number_and_status
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def parse_tgrid_page(html):
|
||||
"""Parses the build master's tgrid page.
|
||||
|
||||
Example input:
|
||||
<tr>
|
||||
<td valign="bottom" class="sourcestamp">1568</td>
|
||||
LIST OF BOTS
|
||||
</tr>
|
||||
The first regular expression group captures 1568, second group captures
|
||||
everything in LIST OF BOTS. The list of bots is then passed into a
|
||||
separate function for parsing.
|
||||
|
||||
Args:
|
||||
html: The raw HTML from the tgrid page.
|
||||
|
||||
Returns: A dictionary with <svn revision>--<bot name> mapped to
|
||||
<bot build number>--<status>, where status is either OK, failed,
|
||||
building or warnings. The status may be 'exception' in the input, but
|
||||
we simply map that to failed.
|
||||
"""
|
||||
result = {}
|
||||
|
||||
for match in re.finditer('<td.*?class="sourcestamp">(\d+).*?</td>(.*?)</tr>',
|
||||
html, re.DOTALL):
|
||||
revision = match.group(1)
|
||||
builds_for_revision_html = match.group(2)
|
||||
result.update(_parse_builds(revision, builds_for_revision_html))
|
||||
|
||||
if not result:
|
||||
raise FailedToParseBuildStatus('Could not find any build statuses in %s.' %
|
||||
html)
|
||||
|
||||
return result
|
@ -1,601 +0,0 @@
|
||||
#!/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.
|
||||
|
||||
"""Test the tgrid parser.
|
||||
|
||||
Compatible with build bot 0.8.4 P1.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
|
||||
import tgrid_parser
|
||||
|
||||
|
||||
SAMPLE_FILE = """
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>Buildbot</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS" href="rss">
|
||||
</head>
|
||||
<body class="interface">
|
||||
<div class="header">
|
||||
<a href=".">Home</a>
|
||||
- <a href="waterfall">Waterfall</a>
|
||||
<a href="grid">Grid</a>
|
||||
<a href="tgrid">T-Grid</a>
|
||||
<a href="console">Console</a>
|
||||
<a href="builders">Builders</a>
|
||||
<a href="one_line_per_build">Recent Builds</a>
|
||||
<a href="buildslaves">Buildslaves</a>
|
||||
<a href="changes">Changesources</a>
|
||||
- <a href="json/help">JSON API</a>
|
||||
- <a href="about">About</a>
|
||||
</div>
|
||||
<hr/>
|
||||
|
||||
<div class="content">
|
||||
<h1>Transposed Grid View</h1>
|
||||
|
||||
<table class="Grid" border="0" cellspacing="0">
|
||||
|
||||
<tr>
|
||||
<td class="title"><a href="http://www.chromium.org">WebRTC</a>
|
||||
|
||||
</td>
|
||||
<td valign="middle" style="text-align: center" class="builder idle">
|
||||
<a href="builders/Android">Android</a></td>
|
||||
<td valign="middle" style="text-align: center" class="builder idle">
|
||||
<a href="builders/AndroidNDK">AndroidNDK</a></td>
|
||||
<td valign="middle" style="text-align: center" class="builder idle">
|
||||
<a href="builders/Chrome">Chrome</a></td>
|
||||
<td valign="middle" style="text-align: center" class="builder idle">
|
||||
<a href="builders/ChromeOS">ChromeOS</a></td>
|
||||
<td valign="middle" style="text-align: center" class="builder idle">
|
||||
<a href="builders/Linux32DBG">Linux32DBG</a></td>
|
||||
<td valign="middle" style="text-align: center" class="builder idle">
|
||||
<a href="builders/Linux32Release">Linux32Release</a></td>
|
||||
<td valign="middle" style="text-align: center" class="builder idle">
|
||||
<a href="builders/Linux64DBG">Linux64DBG</a></td>
|
||||
<td valign="middle" style="text-align: center" class="builder idle">
|
||||
<a href="builders/Linux64DBG-GCC4.6">Linux64DBG-GCC4.6</a></td>
|
||||
<td valign="middle" style="text-align: center" class="builder idle">
|
||||
<a href="builders/Linux64Release">Linux64Release</a></td>
|
||||
<td valign="middle" style="text-align: center" class="builder idle">
|
||||
<a href="builders/LinuxClang">LinuxClang</a></td>
|
||||
<td valign="middle" style="text-align: center" class="builder idle">
|
||||
<a href="builders/LinuxValgrind">LinuxValgrind</a></td>
|
||||
<td valign="middle" style="text-align: center" class="builder idle">
|
||||
<a href="builders/LinuxVideoTest">LinuxVideoTest</a></td>
|
||||
<td valign="middle" style="text-align: center" class="builder idle">
|
||||
<a href="builders/MacOS32DBG">MacOS32DBG</a></td>
|
||||
<td valign="middle" style="text-align: center" class="builder building">
|
||||
<a href="builders/MacOS32Release">MacOS32Release</a><br/>(building)</td>
|
||||
<td valign="middle" style="text-align: center" class="builder idle">
|
||||
<a href="builders/Win32Debug">Win32Debug</a></td>
|
||||
<td valign="middle" style="text-align: center" class="builder building">
|
||||
<a href="builders/Win32Release">Win32Release</a><br/>(building)</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td valign="bottom" class="sourcestamp">2006 </td>
|
||||
<td class="build success">
|
||||
<a href="builders/Android/builds/482">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/AndroidNDK/builds/70">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Chrome/builds/243">warnings</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/ChromeOS/builds/933">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux32DBG/builds/936">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux32Release/builds/1050">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux64DBG/builds/1038">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux64DBG-GCC4.6/builds/371">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux64Release/builds/936">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/LinuxClang/builds/610">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/LinuxValgrind/builds/317">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build"> </td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/MacOS32DBG/builds/1052">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build"> </td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Win32Debug/builds/822">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build"> </td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="bottom" class="sourcestamp">2007 </td>
|
||||
<td class="build success">
|
||||
<a href="builders/Android/builds/483">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/AndroidNDK/builds/71">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Chrome/builds/244">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/ChromeOS/builds/934">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux32DBG/builds/937">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux32Release/builds/1051">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux64DBG/builds/1039">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux64DBG-GCC4.6/builds/372">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux64Release/builds/937">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/LinuxClang/builds/611">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/LinuxValgrind/builds/318">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build failure">
|
||||
<a href="builders/LinuxVideoTest/builds/731">failed<br/>voe_auto_test</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/MacOS32DBG/builds/1053">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/MacOS32Release/builds/309">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Win32Debug/builds/823">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Win32Release/builds/809">OK</a>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="bottom" class="sourcestamp">2008 </td>
|
||||
<td class="build success">
|
||||
<a href="builders/Android/builds/484">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/AndroidNDK/builds/72">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Chrome/builds/245">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/ChromeOS/builds/935">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux32DBG/builds/938">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux32Release/builds/1052">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux64DBG/builds/1040">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux64DBG-GCC4.6/builds/373">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux64Release/builds/938">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/LinuxClang/builds/612">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/LinuxValgrind/builds/319">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/LinuxVideoTest/builds/732">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/MacOS32DBG/builds/1054">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/MacOS32Release/builds/310">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Win32Debug/builds/824">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Win32Release/builds/810">OK</a>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="bottom" class="sourcestamp">2010 </td>
|
||||
<td class="build success">
|
||||
<a href="builders/Android/builds/485">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/AndroidNDK/builds/73">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build"> </td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/ChromeOS/builds/936">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux32DBG/builds/939">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux32Release/builds/1053">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux64DBG/builds/1041">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux64DBG-GCC4.6/builds/374">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux64Release/builds/939">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/LinuxClang/builds/613">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/LinuxValgrind/builds/320">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/LinuxVideoTest/builds/733">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/MacOS32DBG/builds/1055">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/MacOS32Release/builds/311">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Win32Debug/builds/825">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Win32Release/builds/811">OK</a>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="bottom" class="sourcestamp">2011 </td>
|
||||
<td class="build success">
|
||||
<a href="builders/Android/builds/486">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/AndroidNDK/builds/74">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build"> </td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/ChromeOS/builds/937">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux32DBG/builds/940">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux32Release/builds/1054">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux64DBG/builds/1042">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux64DBG-GCC4.6/builds/375">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Linux64Release/builds/940">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/LinuxClang/builds/614">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/LinuxValgrind/builds/321">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/LinuxVideoTest/builds/734">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/MacOS32DBG/builds/1056">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build running">
|
||||
<a href="builders/MacOS32Release/builds/313">building</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Win32Debug/builds/826">OK</a>
|
||||
</td>
|
||||
|
||||
<td class="build running">
|
||||
<a href="builders/Win32Release/builds/813">building</a>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="bottom" class="sourcestamp">latest </td>
|
||||
<td class="build running">
|
||||
<a href="builders/MacOS32Release/builds/313">building</a>
|
||||
</td>
|
||||
|
||||
<td class="build success">
|
||||
<a href="builders/Win32Debug/builds/826">OK</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div><div class="footer" style="clear:both">
|
||||
<hr/>
|
||||
<a href="http://buildbot.net/">BuildBot</a> (0.8.4p1)
|
||||
working for the <a href="http://www.chromium.org">WebRTC
|
||||
</a> project.<br/>
|
||||
Page built: <b>Thu 12 Apr 2012 03:49:32</b> (CDT)
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
MINIMAL_OK = """
|
||||
<tr>
|
||||
<td valign="bottom" class="sourcestamp">1570 </td>
|
||||
<td class="build success">
|
||||
<a href="builders/Linux%20Clang%20%5Bstable%5D/builds/121">OK</a></td>
|
||||
</tr>
|
||||
"""
|
||||
|
||||
MINIMAL_FAIL = """
|
||||
<tr>
|
||||
<td valign="bottom" class="sourcestamp">1573 </td>
|
||||
<td class="build failure">
|
||||
<a href="builders/Linux%20Large%20Tests/builds/731">failed<br/>voe_auto_test
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
"""
|
||||
|
||||
MINIMAL_BUILDING = """
|
||||
<tr>
|
||||
<td valign="bottom" class="sourcestamp">1576 </td>
|
||||
<td class="build running">
|
||||
<a href="builders/Win32Debug/builds/434">building</a></td>
|
||||
voe_auto_test</td>
|
||||
</tr>
|
||||
"""
|
||||
|
||||
MINIMAL_WARNED = """
|
||||
<tr>
|
||||
<td valign="bottom" class="sourcestamp">1576 </td>
|
||||
<td class="build warnings">
|
||||
<a href="builders/Chrome/builds/109">warnings</a><br />
|
||||
make chrome</td>
|
||||
</tr>
|
||||
"""
|
||||
|
||||
MINIMAL_EXCEPTION = """
|
||||
<tr>
|
||||
<td valign="bottom" class="sourcestamp">1576 </td>
|
||||
<td class="build exception">
|
||||
<a href="builders/Chrome/builds/109">exception</a><br />
|
||||
Sync</td>
|
||||
</tr>
|
||||
"""
|
||||
|
||||
MINIMAL_EXCEPTION_SLAVE_LOST = """
|
||||
<tr>
|
||||
<td valign="bottom" class="sourcestamp">1576 </td>
|
||||
<td class="build retry">
|
||||
<a href="builders/LinuxValgrind/builds/324">build<br/>successful<br/>exception<br/>slave<br/>lost</a>
|
||||
</td>
|
||||
</tr>
|
||||
"""
|
||||
|
||||
MINIMAL_IN_TRUNK_SOURCESTAMP = """
|
||||
<tr>
|
||||
<td valign="bottom" class="sourcestamp">1576 in trunk </td>
|
||||
<td class="build retry">
|
||||
<a href="builders/LinuxValgrind/builds/324">build<br/>successful<br/>exception<br/>slave<br/>lost</a>
|
||||
</td>
|
||||
</tr>
|
||||
"""
|
||||
|
||||
class TGridParserTest(unittest.TestCase):
|
||||
def test_parser_throws_exception_on_empty_html(self):
|
||||
self.assertRaises(tgrid_parser.FailedToParseBuildStatus,
|
||||
tgrid_parser.parse_tgrid_page, '')
|
||||
|
||||
def test_parser_finds_successful_bot(self):
|
||||
result = tgrid_parser.parse_tgrid_page(MINIMAL_OK)
|
||||
|
||||
self.assertEqual(1, len(result), 'There is only one bot in the sample.')
|
||||
first_mapping = result.items()[0]
|
||||
|
||||
# Note: the parser should unescape % quotations, like %20 for space.
|
||||
self.assertEqual('1570--Linux Clang [stable]', first_mapping[0])
|
||||
self.assertEqual('121--OK', first_mapping[1])
|
||||
|
||||
def test_parser_finds_failed_bot(self):
|
||||
result = tgrid_parser.parse_tgrid_page(MINIMAL_FAIL)
|
||||
|
||||
self.assertEqual(1, len(result), 'There is only one bot in the sample.')
|
||||
first_mapping = result.items()[0]
|
||||
|
||||
self.assertEqual('1573--Linux Large Tests', first_mapping[0])
|
||||
self.assertEqual('731--failed', first_mapping[1])
|
||||
|
||||
def test_parser_finds_building_bot(self):
|
||||
result = tgrid_parser.parse_tgrid_page(MINIMAL_BUILDING)
|
||||
|
||||
self.assertEqual(1, len(result), 'There is only one bot in the sample.')
|
||||
first_mapping = result.items()[0]
|
||||
|
||||
self.assertEqual('1576--Win32Debug', first_mapping[0])
|
||||
self.assertEqual('434--building', first_mapping[1])
|
||||
|
||||
def test_parser_finds_warnings(self):
|
||||
result = tgrid_parser.parse_tgrid_page(MINIMAL_WARNED)
|
||||
|
||||
self.assertEqual(1, len(result), 'There is only one bot in the sample.')
|
||||
first_mapping = result.items()[0]
|
||||
|
||||
self.assertEqual('1576--Chrome', first_mapping[0])
|
||||
self.assertEqual('109--warnings', first_mapping[1])
|
||||
|
||||
def test_parser_finds_exception_and_maps_to_failed(self):
|
||||
result = tgrid_parser.parse_tgrid_page(MINIMAL_EXCEPTION)
|
||||
|
||||
self.assertEqual(1, len(result), 'There is only one bot in the sample.')
|
||||
first_mapping = result.items()[0]
|
||||
|
||||
self.assertEqual('1576--Chrome', first_mapping[0])
|
||||
self.assertEqual('109--failed', first_mapping[1])
|
||||
|
||||
def test_parser_finds_exception_slave_lost_and_maps_to_failed(self):
|
||||
# This is to work around a bug in build bot 0.8.4p1 where it may say that
|
||||
# the build was successful AND the slave was lost. In this case the build
|
||||
# is not actually successful, so treat it as such.
|
||||
result = tgrid_parser.parse_tgrid_page(MINIMAL_EXCEPTION_SLAVE_LOST)
|
||||
|
||||
self.assertEqual(1, len(result), 'There is only one bot in the sample.')
|
||||
first_mapping = result.items()[0]
|
||||
|
||||
self.assertEqual('1576--LinuxValgrind', first_mapping[0])
|
||||
self.assertEqual('324--failed', first_mapping[1])
|
||||
|
||||
def test_parser_finds_all_bots_and_revisions_except_forced_builds(self):
|
||||
result = tgrid_parser.parse_tgrid_page(SAMPLE_FILE)
|
||||
|
||||
# 5*16 = 80 bots in sample. There's also five empty results because some
|
||||
# bots did not run for some revisions, so 80 - 5 = 75 results. There are
|
||||
# two additional statuses under an explicit 'latest' revision, which should
|
||||
# be ignored since that means the build was forced.
|
||||
self.assertEqual(75, len(result))
|
||||
|
||||
# Make some samples
|
||||
self.assertTrue(result.has_key('2006--ChromeOS'))
|
||||
self.assertEquals('933--OK', result['2006--ChromeOS'])
|
||||
|
||||
self.assertTrue(result.has_key('2006--Chrome'))
|
||||
self.assertEquals('243--warnings', result['2006--Chrome'])
|
||||
|
||||
self.assertTrue(result.has_key('2006--LinuxClang'))
|
||||
self.assertEquals('610--OK', result['2006--LinuxClang'])
|
||||
|
||||
# This one happened to not get reported in revision 2006, but it should be
|
||||
# there in the next revision:
|
||||
self.assertFalse(result.has_key('2006--Win32Release'))
|
||||
self.assertTrue(result.has_key('2007--Win32Release'))
|
||||
self.assertEquals('809--OK', result['2007--Win32Release'])
|
||||
|
||||
self.assertTrue(result.has_key('2007--ChromeOS'))
|
||||
self.assertEquals('934--OK', result['2007--ChromeOS'])
|
||||
|
||||
self.assertTrue(result.has_key('2007--LinuxVideoTest'))
|
||||
self.assertEquals('731--failed', result['2007--LinuxVideoTest'])
|
||||
|
||||
self.assertTrue(result.has_key('2011--Win32Release'))
|
||||
self.assertEquals('813--building', result['2011--Win32Release'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -1,94 +0,0 @@
|
||||
#!/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 checks the current build status on the master and submits
|
||||
it to the dashboard. It is adapted to build bot version 0.7.12.
|
||||
"""
|
||||
|
||||
import httplib
|
||||
|
||||
import constants
|
||||
import dashboard_connection
|
||||
import tgrid_parser
|
||||
|
||||
# Bots that must be green in order to increment the LKGR revision.
|
||||
# TODO(kjellander): Remake this entirely as we have now switched to Chrome infra
|
||||
# bots. This is just to get the LGKR moving forward for now.
|
||||
BOTS = ['Linux32 Debug Coverage',
|
||||
'Win Large Tests',
|
||||
'Mac Large Tests',
|
||||
'Linux Large Tests',
|
||||
'Android Platform',
|
||||
'Android NDK',
|
||||
]
|
||||
|
||||
|
||||
class FailedToGetStatusFromMaster(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def _download_and_parse_build_status():
|
||||
connection = httplib.HTTPConnection(constants.BUILD_MASTER_SERVER)
|
||||
connection.request('GET', constants.BUILD_MASTER_TRANSPOSED_GRID_URL)
|
||||
response = connection.getresponse()
|
||||
|
||||
if response.status != 200:
|
||||
raise FailedToGetStatusFromMaster(('Failed to get build status from master:'
|
||||
' got status %d, reason %s.' %
|
||||
(response.status, response.reason)))
|
||||
|
||||
full_response = response.read()
|
||||
connection.close()
|
||||
|
||||
return tgrid_parser.parse_tgrid_page(full_response)
|
||||
|
||||
|
||||
def _is_chrome_only_build(revision_to_bot_name):
|
||||
"""Figures out if a revision-to-bot-name mapping represents a Chrome build.
|
||||
|
||||
We assume here that Chrome revisions are always > 100000, whereas WebRTC
|
||||
revisions will not reach that number in the foreseeable future.
|
||||
"""
|
||||
revision = int(revision_to_bot_name.split('--')[0])
|
||||
bot_name = revision_to_bot_name.split('--')[1]
|
||||
return 'Chrome' in bot_name and revision > 100000
|
||||
|
||||
|
||||
def _filter_undesired_bots(bot_to_status_mapping, desired_bot_names):
|
||||
"""Returns the desired bots for the builds status from the dictionary.
|
||||
|
||||
Args:
|
||||
bot_to_status_mapping: Dictionary mapping bot name with revision to status.
|
||||
desired_bot_names: List of bot names that will be the only bots returned in
|
||||
the resulting dictionary.
|
||||
Returns: A dictionary only containing the desired bots.
|
||||
"""
|
||||
result = {}
|
||||
for revision_to_bot_name, status in bot_to_status_mapping.iteritems():
|
||||
bot_name = revision_to_bot_name.split('--')[1]
|
||||
if bot_name in desired_bot_names:
|
||||
result[revision_to_bot_name] = status
|
||||
return result
|
||||
|
||||
|
||||
def _main():
|
||||
dashboard = dashboard_connection.DashboardConnection(constants.CONSUMER_KEY)
|
||||
dashboard.read_required_files(constants.CONSUMER_SECRET_FILE,
|
||||
constants.ACCESS_TOKEN_FILE)
|
||||
|
||||
bot_to_status_mapping = _download_and_parse_build_status()
|
||||
bot_to_status_mapping = _filter_undesired_bots(bot_to_status_mapping, BOTS)
|
||||
|
||||
dashboard.send_post_request(constants.ADD_BUILD_STATUS_DATA_URL,
|
||||
bot_to_status_mapping)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
_main()
|
@ -1,39 +0,0 @@
|
||||
#!/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.
|
||||
|
||||
"""Unit test for the build status tracker script."""
|
||||
|
||||
|
||||
import copy
|
||||
import unittest
|
||||
|
||||
import track_build_status
|
||||
|
||||
|
||||
NORMAL_BOT_TO_STATUS_MAPPING = {
|
||||
'1455--Win 64 Release': '455--OK',
|
||||
'1455--CrOS': '900--failed',
|
||||
'1455--Linux32 Debug': '344--OK',
|
||||
'1456--Win Large Tests': '456--OK'}
|
||||
|
||||
|
||||
class TrackBuildStatusTest(unittest.TestCase):
|
||||
|
||||
def test_get_desired_bots(self):
|
||||
bot_to_status_mapping = copy.deepcopy(NORMAL_BOT_TO_STATUS_MAPPING)
|
||||
desired_bot_names = ['Linux32 Debug']
|
||||
# pylint: disable=W0212
|
||||
result = track_build_status._filter_undesired_bots(bot_to_status_mapping,
|
||||
desired_bot_names)
|
||||
self.assertEquals(1, len(result))
|
||||
self.assertTrue(desired_bot_names[0] in result.keys()[0])
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Reference in New Issue
Block a user