1517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org#!/usr/bin/python2
2517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
3517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org# Copyright 2014 Google Inc.
4517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org#
5517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org# Use of this source code is governed by a BSD-style license that can be
6517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org# found in the LICENSE file.
7517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
8517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org"""Skia's Chromium Codereview Comparison Script.
9517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
10517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.orgThis script takes two Codereview URLs, looks at the trybot results for
11517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.orgthe two codereviews and compares the results.
12517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
13517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.orgUsage:
14517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org  compare_codereview.py CONTROL_URL ROLL_URL
15517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org"""
16517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
17517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.orgimport collections
18517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.orgimport os
19517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.orgimport re
20517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.orgimport sys
21517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.orgimport urllib2
22517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.orgimport HTMLParser
23517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
24517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
25517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.orgclass CodeReviewHTMLParser(HTMLParser.HTMLParser):
262a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  """Parses CodeReview web page.
27517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
282a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  Use the CodeReviewHTMLParser.parse static function to make use of
292a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  this class.
30517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
312a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  This uses the HTMLParser class because it's the best thing in
322a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  Python's standard library.  We need a little more power than a
332a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  regex.  [Search for "You can't parse [X]HTML with regex." for more
342a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  information.
352a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  """
362a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  # pylint: disable=I0011,R0904
372a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  @staticmethod
382a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  def parse(url):
392a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    """Parses a CodeReview web pages.
402a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
412a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    Args:
422a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      url (string), a codereview URL like this:
432a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        'https://codereview.chromium.org/?????????'.
442a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
452a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    Returns:
462a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      A dictionary; the keys are bot_name strings, the values
472a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      are CodeReviewHTMLParser.Status objects
482a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    """
492a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    parser = CodeReviewHTMLParser()
502a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    try:
512a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      parser.feed(urllib2.urlopen(url).read())
522a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    except (urllib2.URLError,):
532a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      print >> sys.stderr, 'Error getting', url
542a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      return None
552a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    parser.close()
562a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    return parser.statuses
572a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
582a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  # namedtuples are like lightweight structs in Python.  The low
592a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  # overhead of a tuple, but the ease of use of an object.
602a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  Status = collections.namedtuple('Status', ['status', 'url'])
612a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
622a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  def __init__(self):
632a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    HTMLParser.HTMLParser.__init__(self)
642a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    self._id = None
652a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    self._status = None
662a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    self._href = None
672a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    self._anchor_data = ''
682a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    self._currently_parsing_trybotdiv = False
692a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    # statuses is a dictionary of CodeReviewHTMLParser.Status
702a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    self.statuses = {}
712a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
722a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  def handle_starttag(self, tag, attrs):
732a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    """Overrides the HTMLParser method to implement functionality.
742a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
752a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    [[begin standard library documentation]]
762a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    This method is called to handle the start of a tag
772a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    (e.g. <div id="main">).
782a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
792a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    The tag argument is the name of the tag converted to lower
802a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    case. The attrs argument is a list of (name, value) pairs
812a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    containing the attributes found inside the tag's <>
822a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    brackets. The name will be translated to lower case, and
832a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    quotes in the value have been removed, and character and
842a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    entity references have been replaced.
852a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
862a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    For instance, for the tag <A HREF="http://www.cwi.nl/">, this
872a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    method would be called as handle_starttag('a', [('href',
882a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    'http://www.cwi.nl/')]).
892a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    [[end standard library documentation]]
90517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org    """
912a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    attrs = dict(attrs)
922a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    if tag == 'div':
932a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      # We are looking for <div id="tryjobdiv*">.
942a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      id_attr = attrs.get('id','')
952a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      if id_attr.startswith('tryjobdiv'):
962a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        self._id = id_attr
972a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    if (self._id and tag == 'a'
982a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      and 'build-result' in attrs.get('class', '').split()):
992a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      # If we are already inside a <div id="tryjobdiv*">, we
1002a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      # look for a link if the form
1012a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      # <a class="build-result" href="*">.  Then we save the
1022a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      # (non-standard) status attribute and the URL.
1032a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      self._status = attrs.get('status')
1042a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      self._href = attrs.get('href')
1052a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      self._currently_parsing_trybotdiv = True
1062a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      # Start saving anchor data.
1072a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
1082a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  def handle_data(self, data):
1092a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    """Overrides the HTMLParser method to implement functionality.
1102a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
1112a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    [[begin standard library documentation]]
1122a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    This method is called to process arbitrary data (e.g. text
1132a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    nodes and the content of <script>...</script> and
1142a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    <style>...</style>).
1152a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    [[end standard library documentation]]
1162a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    """
1172a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    # Save the text inside the <a></a> tags.  Assume <a> tags
1182a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    # aren't nested.
1192a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    if self._currently_parsing_trybotdiv:
1202a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      self._anchor_data += data
1212a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
1222a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  def handle_endtag(self, tag):
1232a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    """Overrides the HTMLParser method to implement functionality.
1242a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
1252a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    [[begin standard library documentation]]
1262a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    This method is called to handle the end tag of an element
1272a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    (e.g. </div>).  The tag argument is the name of the tag
1282a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    converted to lower case.
1292a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    [[end standard library documentation]]
1302a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    """
1312a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    if tag == 'a' and self._status:
1322a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      # We take the accumulated self._anchor_data and save it as
1332a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      # the bot name.
1342a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      bot = self._anchor_data.strip()
1352a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      stat = CodeReviewHTMLParser.Status(status=self._status,
1362a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org                         url=self._href)
1372a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      if bot:
1382a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        # Add to accumulating dictionary.
1392a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        self.statuses[bot] = stat
1402a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      # Reset state to search for the next bot.
1412a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      self._currently_parsing_trybotdiv = False
1422a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      self._anchor_data = ''
1432a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      self._status = None
1442a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      self._href = None
145517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
146517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
147517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.orgclass BuilderHTMLParser(HTMLParser.HTMLParser):
1482a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  """parses Trybot web pages.
149517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
1502a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  Use the BuilderHTMLParser.parse static function to make use of
1512a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  this class.
152517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
1532a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  This uses the HTMLParser class because it's the best thing in
1542a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  Python's standard library.  We need a little more power than a
1552a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  regex.  [Search for "You can't parse [X]HTML with regex." for more
1562a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  information.
1572a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  """
1582a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  # pylint: disable=I0011,R0904
1592a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  @staticmethod
1602a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  def parse(url):
1612a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    """Parses a Trybot web page.
162517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
1632a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    Args:
1642a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      url (string), a trybot result URL.
165517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
1662a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    Returns:
1672a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      An array of BuilderHTMLParser.Results, each a description
1682a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      of failure results, along with an optional url
169517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org    """
1702a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    parser = BuilderHTMLParser()
1712a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    try:
1722a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      parser.feed(urllib2.urlopen(url).read())
1732a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    except (urllib2.URLError,):
1742a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      print >> sys.stderr, 'Error getting', url
1752a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      return []
1762a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    parser.close()
1772a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    return parser.failure_results
1782a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
1792a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  Result = collections.namedtuple('Result', ['text', 'url'])
1802a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
1812a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  def __init__(self):
1822a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    HTMLParser.HTMLParser.__init__(self)
1832a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    self.failure_results = []
1842a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    self._current_failure_result = None
1852a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    self._divlevel = None
1862a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    self._li_level = 0
1872a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    self._li_data = ''
1882a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    self._current_failure = False
1892a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    self._failure_results_url = ''
1902a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
1912a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  def handle_starttag(self, tag, attrs):
1922a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    """Overrides the HTMLParser method to implement functionality.
1932a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
1942a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    [[begin standard library documentation]]
1952a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    This method is called to handle the start of a tag
1962a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    (e.g. <div id="main">).
1972a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
1982a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    The tag argument is the name of the tag converted to lower
1992a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    case. The attrs argument is a list of (name, value) pairs
2002a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    containing the attributes found inside the tag's <>
2012a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    brackets. The name will be translated to lower case, and
2022a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    quotes in the value have been removed, and character and
2032a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    entity references have been replaced.
2042a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
2052a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    For instance, for the tag <A HREF="http://www.cwi.nl/">, this
2062a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    method would be called as handle_starttag('a', [('href',
2072a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    'http://www.cwi.nl/')]).
2082a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    [[end standard library documentation]]
2092a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    """
2102a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    attrs = dict(attrs)
2112a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    if tag == 'li':
2122a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      # <li> tags can be nested.  So we have to count the
2132a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      # nest-level for backing out.
2142a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      self._li_level += 1
2152a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      return
2162a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    if tag == 'div' and attrs.get('class') == 'failure result':
2172a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      # We care about this sort of thing:
2182a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      # <li>
2192a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      #   <li>
2202a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      #   <li>
2212a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      #     <div class="failure result">...</div>
2222a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      #   </li>
2232a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      #   </li>
2242a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      #   We want this text here.
2252a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      # </li>
2262a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      if self._li_level > 0:
2272a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        self._current_failure = True  # Tells us to keep text.
2282a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      return
2292a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
2302a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    if tag == 'a' and self._current_failure:
2312a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      href = attrs.get('href')
2322a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      # Sometimes we want to keep the stdio url.  We always
2332a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      # return it, just in case.
2342a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      if href.endswith('/logs/stdio'):
2352a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        self._failure_results_url = href
2362a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
2372a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  def handle_data(self, data):
2382a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    """Overrides the HTMLParser method to implement functionality.
2392a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
2402a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    [[begin standard library documentation]]
2412a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    This method is called to process arbitrary data (e.g. text
2422a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    nodes and the content of <script>...</script> and
2432a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    <style>...</style>).
2442a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    [[end standard library documentation]]
2452a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    """
2462a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    if self._current_failure:
2472a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      self._li_data += data
248517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
2492a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  def handle_endtag(self, tag):
2502a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    """Overrides the HTMLParser method to implement functionality.
251517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
2522a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    [[begin standard library documentation]]
2532a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    This method is called to handle the end tag of an element
2542a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    (e.g. </div>).  The tag argument is the name of the tag
2552a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    converted to lower case.
2562a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    [[end standard library documentation]]
2572a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    """
2582a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    if tag == 'li':
2592a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      self._li_level -= 1
2602a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      if 0 == self._li_level:
2612a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        if self._current_failure:
2622a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          result = self._li_data.strip()
2632a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          first = result.split()[0]
2642a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          if first:
2652a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org            result = re.sub(
2662a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org              r'^%s(\s+%s)+' % (first, first), first, result)
2672a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org            # Sometimes, it repeats the same thing
2682a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org            # multiple times.
2692a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          result = re.sub(r'unexpected flaky.*', '', result)
2702a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          # Remove some extra unnecessary text.
2712a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          result = re.sub(r'\bpreamble\b', '', result)
2722a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          result = re.sub(r'\bstdio\b', '', result)
2732a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          url = self._failure_results_url
2742a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          self.failure_results.append(
2752a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org            BuilderHTMLParser.Result(result, url))
2762a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          self._current_failure_result = None
2772a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        # Reset the state.
2782a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        self._current_failure = False
2792a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        self._li_data = ''
2802a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        self._failure_results_url = ''
281517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
282517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
2832a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.orgdef printer(indent, string):
2842a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  """Print indented, wrapped text.
2852a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  """
2862a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  def wrap_to(line, columns):
2872a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    """Wrap a line to the given number of columns, return a list
2882a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    of strings.
289517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org    """
2902a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    ret = []
2912a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    nextline = ''
2922a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    for word in line.split():
2932a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      if nextline:
2942a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        if len(nextline) + 1 + len(word) > columns:
2952a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          ret.append(nextline)
2962a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          nextline = word
2972a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        else:
2982a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          nextline += (' ' + word)
2992a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      else:
3002a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        nextline = word
3012a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    if nextline:
3022a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      ret.append(nextline)
3032a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    return ret
3042a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  out = sys.stdout
3052a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  spacer = '  '
3062a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  for line in string.split('\n'):
3072a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    for i, wrapped_line in enumerate(wrap_to(line, 68 - (2 * indent))):
3082a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      out.write(spacer * indent)
3092a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      if i > 0:
3102a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        out.write(spacer)
3112a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      out.write(wrapped_line)
3122a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      out.write('\n')
3132a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  out.flush()
314517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
315517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
3162a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.orgdef main(control_url, roll_url, verbosity=1):
3172a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  """Compare two Codereview URLs
3182a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
3192a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  Args:
3202a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    control_url, roll_url: (strings) URL of the format
3212a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      https://codereview.chromium.org/?????????
3222a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
3232a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    verbosity: (int) verbose level.  0, 1, or 2.
3242a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  """
3252a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  # pylint: disable=I0011,R0914,R0912
3262a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  control = CodeReviewHTMLParser.parse(control_url)
3272a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  roll = CodeReviewHTMLParser.parse(roll_url)
3282a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  all_bots = set(control) & set(roll)  # Set intersection.
3292a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  if not all_bots:
3302a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    print >> sys.stderr, (
3312a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      'Error:  control %s and roll %s have no common trybots.'
3322a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      % (list(control), list(roll)))
3332a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    return
3342a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
3352a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  control_name = '[control %s]' % control_url.split('/')[-1]
3362a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  roll_name = '[roll %s]' % roll_url.split('/')[-1]
3372a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
3382a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  out = sys.stdout
3392a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
3402a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  for bot in sorted(all_bots):
3412a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    if (roll[bot].status == 'success'):
3422a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      if verbosity > 1:
343517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org        printer(0, '==%s==' % bot)
3442a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        printer(1, 'OK')
3452a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      continue
346517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
3472a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    if control[bot].status != 'failure' and roll[bot].status != 'failure':
3482a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      continue
3492a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    printer(0, '==%s==' % bot)
3502a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
3512a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    formatted_results = []
3522a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    for (status, name, url) in [
353517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org            (control[bot].status, control_name, control[bot].url),
3542a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org            (   roll[bot].status,    roll_name,    roll[bot].url)]:
3552a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      lines = []
3562a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      if status == 'failure':
3572a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        results = BuilderHTMLParser.parse(url)
3582a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        for result in results:
3592a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          formatted_result = re.sub(r'(\S*\.html) ', '\n__\g<1>\n', result.text)
3602a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          # Strip runtimes.
3612a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          formatted_result = re.sub(r'\(.*\)', '', formatted_result)
3622a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          lines.append((2, formatted_result))
3632a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          if ('compile' in result.text or '...and more' in result.text):
3642a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org            lines.append((3, re.sub('/[^/]*$', '/', url) + result.url))
3652a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      formatted_results.append(lines)
3662a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
3672a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    identical = formatted_results[0] == formatted_results[1]
3682a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
3692a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
3702a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    for (formatted_result, (status, name, url)) in zip(
3712a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        formatted_results,
3722a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        [(control[bot].status, control_name, control[bot].url),
3732a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          (roll[bot].status,  roll_name,  roll[bot].url)]):
3742a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      if status != 'failure' and not identical:
3752a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        printer(1, name)
3762a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        printer(2, status)
3772a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      elif status == 'failure':
3782a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        if identical:
3792a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          printer(1, control_name + ' and ' + roll_name + ' failed identically')
3802a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        else:
3812a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          printer(1, name)
3822a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        for (indent, line) in formatted_result:
3832a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          printer(indent, line)
3842a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        if identical:
3852a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          break
3862a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    out.write('\n')
3872a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org
3882a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  if verbosity > 0:
3892a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    # Print out summary of all of the bots.
3902a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    out.write('%11s %11s %4s %s\n\n' %
3912a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          ('CONTROL', 'ROLL', 'DIFF', 'BOT'))
3922a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    for bot in sorted(all_bots):
3932a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      if roll[bot].status == 'success':
3942a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        diff = ''
3952a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      elif (control[bot].status == 'success' and
3962a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org           roll[bot].status == 'failure'):
3972a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        diff = '!!!!'
3982a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      elif ('pending' in control[bot].status or
3992a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          'pending' in roll[bot].status):
4002a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        diff = '....'
4012a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      else:
4022a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org        diff = '****'
4032a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org      out.write('%11s %11s %4s %s\n' % (
4042a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org          control[bot].status, roll[bot].status, diff, bot))
4052a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    out.write('\n')
4062a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    out.flush()
407517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
408517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.orgif __name__ == '__main__':
4092a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  if len(sys.argv) < 3:
4102a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    print >> sys.stderr, __doc__
4112a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org    exit(1)
4122a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org  main(sys.argv[1], sys.argv[2],
4132a1704f362faf03e899dbe86a269e224b8b5b29fcommit-bot@chromium.org     int(os.environ.get('COMPARE_CODEREVIEW_VERBOSITY', 1)))
414517c1e2921bc20b8600061aa21a77a8810db597ecommit-bot@chromium.org
415