diff --git a/tools/quality_tracking/constants.py b/tools/quality_tracking/constants.py index a36b0b943..32c8a164b 100644 --- a/tools/quality_tracking/constants.py +++ b/tools/quality_tracking/constants.py @@ -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' diff --git a/tools/quality_tracking/dashboard/add_build_status_data.py b/tools/quality_tracking/dashboard/add_build_status_data.py deleted file mode 100644 index 9e02c2d5d..000000000 --- a/tools/quality_tracking/dashboard/add_build_status_data.py +++ /dev/null @@ -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 --, for instance - 1568--Win32Release. - 2) The value should be on the form --, 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() - diff --git a/tools/quality_tracking/dashboard/app.yaml b/tools/quality_tracking/dashboard/app.yaml index bc88559bc..1d6169d90 100644 --- a/tools/quality_tracking/dashboard/app.yaml +++ b/tools/quality_tracking/dashboard/app.yaml @@ -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 \ No newline at end of file + script: main.app diff --git a/tools/quality_tracking/dashboard/dashboard.py b/tools/quality_tracking/dashboard/dashboard.py index da4d584e9..121009ccb 100644 --- a/tools/quality_tracking/dashboard/dashboard.py +++ b/tools/quality_tracking/dashboard/dashboard.py @@ -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')) diff --git a/tools/quality_tracking/dashboard/lkgr_page.py b/tools/quality_tracking/dashboard/lkgr_page.py deleted file mode 100644 index c0fa46d5d..000000000 --- a/tools/quality_tracking/dashboard/lkgr_page.py +++ /dev/null @@ -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) diff --git a/tools/quality_tracking/dashboard/load_build_status.py b/tools/quality_tracking/dashboard/load_build_status.py deleted file mode 100644 index 5fe1de6f8..000000000 --- a/tools/quality_tracking/dashboard/load_build_status.py +++ /dev/null @@ -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 diff --git a/tools/quality_tracking/dashboard/main.py b/tools/quality_tracking/dashboard/main.py index b98465952..8e181ac7b 100644 --- a/tools/quality_tracking/dashboard/main.py +++ b/tools/quality_tracking/dashboard/main.py @@ -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) \ No newline at end of file diff --git a/tools/quality_tracking/dashboard/static/index.html b/tools/quality_tracking/dashboard/static/index.html index 708966acd..2b18c0eff 100644 --- a/tools/quality_tracking/dashboard/static/index.html +++ b/tools/quality_tracking/dashboard/static/index.html @@ -1,13 +1,5 @@ - WebRTC Performance Metrics - + - -

WebRTC Performance Metrics

-

Video

-

Audio

-

Chrome Audio Path

-

vie_auto_test

- \ No newline at end of file diff --git a/tools/quality_tracking/dashboard/static/lkgr_redirect.html b/tools/quality_tracking/dashboard/static/lkgr_redirect.html new file mode 100644 index 000000000..49a473390 --- /dev/null +++ b/tools/quality_tracking/dashboard/static/lkgr_redirect.html @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/tools/quality_tracking/dashboard/static/webrtc_logo.png b/tools/quality_tracking/dashboard/static/webrtc_logo.png new file mode 100644 index 000000000..ce5ece077 Binary files /dev/null and b/tools/quality_tracking/dashboard/static/webrtc_logo.png differ diff --git a/tools/quality_tracking/dashboard/stylesheets/stylesheet.css b/tools/quality_tracking/dashboard/stylesheets/stylesheet.css index c6eb67993..992c57ff9 100644 --- a/tools/quality_tracking/dashboard/stylesheets/stylesheet.css +++ b/tools/quality_tracking/dashboard/stylesheets/stylesheet.css @@ -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; -} \ No newline at end of file +} diff --git a/tools/quality_tracking/dashboard/templates/dashboard_template.html b/tools/quality_tracking/dashboard/templates/dashboard_template.html index aae237e31..a45bc17be 100644 --- a/tools/quality_tracking/dashboard/templates/dashboard_template.html +++ b/tools/quality_tracking/dashboard/templates/dashboard_template.html @@ -13,7 +13,7 @@ Template file to be used to generate the WebRTC dashboard. --> - WebRTC Coverage Dashboard + WebRTC Quality Dashboard @@ -62,49 +62,14 @@ -

WebRTC Quality Dashboard

-

Current Build Status

-
(as of {{ last_updated_at }} UTC)
- - - {% for entry in build_status_data_row_1 %} - - {% endfor %} - - - {% for entry in build_status_data_row_1 %} - - {% endfor %} - - - {% for entry in build_status_data_row_2 %} - - {% endfor %} - - - {% for entry in build_status_data_row_2 %} - - {% endfor %} - -
{{ entry.bot_name }}
- {{entry.status}} -
{{ entry.bot_name }}
- {{entry.status}} -
-

- -

Last Known Good Revision (LKGR)

-
- {% if lkgr %} - - {{ lkgr }} - {% else %} - ???? - {% endif %} -
- + +

Performance Metrics

+

Code Coverage History (Small / Medium Tests)

Code Coverage History (Large Tests)

diff --git a/tools/quality_tracking/tgrid_parser.py b/tools/quality_tracking/tgrid_parser.py deleted file mode 100644 index 63cc0b97b..000000000 --- a/tools/quality_tracking/tgrid_parser.py +++ /dev/null @@ -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
successful
exception
slave
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 lines. - - See contract for parse_tgrid_page for more information on how this function - behaves. - - Example input: - OK - The first regular expression group captures Android, second 119, third OK. - """ - result = {} - - for match in re.finditer('.*?' - '(OK|failed|building|warnings|exception|' + - BB_084_P1_BUGGY_STATUS + ')' - '.*?.*?', - 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: - - 1568 - LIST OF BOTS - - 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 -- mapped to - --, 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('(\d+).*?(.*?)', - 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 diff --git a/tools/quality_tracking/tgrid_parser_test.py b/tools/quality_tracking/tgrid_parser_test.py deleted file mode 100755 index 98de03a3c..000000000 --- a/tools/quality_tracking/tgrid_parser_test.py +++ /dev/null @@ -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 = """ - - - - - - Buildbot - - - - - -
- -
-

Transposed Grid View

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
WebRTC - - - Android - AndroidNDK - Chrome - ChromeOS - Linux32DBG - Linux32Release - Linux64DBG - Linux64DBG-GCC4.6 - Linux64Release - LinuxClang - LinuxValgrind - LinuxVideoTest - MacOS32DBG - MacOS32Release
(building)
- Win32Debug - Win32Release
(building)
2006 - OK - - OK - - warnings - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK -   - OK -   - OK -  
2007 - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - failed
voe_auto_test
-
- OK - - OK - - OK - - OK -
2008 - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK -
2010 - OK - - OK -   - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK -
2011 - OK - - OK -   - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - OK - - building - - OK - - building -
latest - building - - OK -
- -
- - -""" - -MINIMAL_OK = """ - -1570 - -OK - -""" - -MINIMAL_FAIL = """ - -1573 - - failed
voe_auto_test -
- - -""" - -MINIMAL_BUILDING = """ - -1576 - -building -voe_auto_test - -""" - -MINIMAL_WARNED = """ - -1576 - -warnings
-make chrome - -""" - -MINIMAL_EXCEPTION = """ - -1576 - -exception
-Sync - -""" - -MINIMAL_EXCEPTION_SLAVE_LOST = """ - -1576 - - build
successful
exception
slave
lost
- - -""" - -MINIMAL_IN_TRUNK_SOURCESTAMP = """ - -1576 in trunk - - build
successful
exception
slave
lost
- - -""" - -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() diff --git a/tools/quality_tracking/track_build_status.py b/tools/quality_tracking/track_build_status.py deleted file mode 100755 index 3bc158a7c..000000000 --- a/tools/quality_tracking/track_build_status.py +++ /dev/null @@ -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() diff --git a/tools/quality_tracking/track_build_status_test.py b/tools/quality_tracking/track_build_status_test.py deleted file mode 100755 index 9a9844ce9..000000000 --- a/tools/quality_tracking/track_build_status_test.py +++ /dev/null @@ -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()