15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""Main function to run the layout test analyzer.
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)The purpose of this script is to run the layout test analyzer for various
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)teams based on the run configuration file in CSV format. The CSV file is based
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)on https://sites.google.com/a/chromium.org/dev/developers/testing/
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)webkit-layout-tests/layout-test-stats-1.
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import optparse
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import shutil
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from subprocess import Popen
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch# TODO(shadi): Re-examine the need of external files. Inline data instead?
20558790d6acca3451cf3a6b497803a5f07d0bec58Ben MurdochDEFAULT_RUN_CONFIG = {
21558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    # test_group_name: ('test_files.csv', 'report_email_address')
22558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    'media': ('testname/media.csv', 'layout-test-analyzer-result@google.com')
23558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch}
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Predefined result/graph directory.
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DEFAULT_RESULT_DIR = 'result'
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DEFAULT_GRAPH_DIR = 'graph'
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ParseOption():
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Parse command-line options using OptionParser.
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      an object containing all command-line option information.
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  option_parser = optparse.OptionParser()
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  option_parser.add_option('-d', '--result-directory-location',
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           dest='result_directory_location',
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           help=('Name of result directory location '
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 '(default to %default)'),
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           default=DEFAULT_RESULT_DIR)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  option_parser.add_option('-p', '--graph-directory-location',
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           dest='graph_directory_location',
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           help=('Name of graph directory location '
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 '(default to %default)'),
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           default=DEFAULT_GRAPH_DIR)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  option_parser.add_option('-e', '--email-only-change-mode',
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           dest='email_only_change_mode',
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           help=('With this mode, email is sent out '
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 'only when there is a change in the '
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 'analyzer result compared to the previous '
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 'result (off by default)'),
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           action='store_true', default=False)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  option_parser.add_option('-z', '--issue-detail-mode',
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           dest='issue_detail_mode',
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           help=('With this mode, email includes issue details'
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 ' including links to the flakiness dashboard'
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 ' (off by default)'),
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           action='store_true', default=False)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return option_parser.parse_args()[0]
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def GenerateDashboardHTMLFile(file_name, test_group_list):
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Generate dashboard HTML file.
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Currently, it is simple table that shows all the analyzer results.
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_name: the file name of the dashboard.
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    test_group_list: a list of test group names such as 'media' or 'composite'.
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_object = open(file_name, 'wb')
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  legend_txt = """
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<style type="text/css">
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)th {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  width: 30px; overflow: hidden;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)tr.d0 td {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  background-color: #CC9999; color: black;
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  text-align: right;
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  width: 30px; overflow: hidden;
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)tr.d1 td {
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  background-color: #9999CC; color: black;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  text-align: right;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  width: 30px; overflow: hidden;
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)</style>
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<h2>Chromium Layout Test Analyzer Result</h2>
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Legend:
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<ul>
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<li>#Tests: the number of tests for the given test group
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<li>#Skipped Tests: the number of tests that are skipped in the
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<a href='http://svn.webkit.org/repository/webkit/trunk/LayoutTests/platform/\
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)chromium/test_expectations.txt'>test expectaion file</a> (e.g., BUGWK60877
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SKIP : loader/navigation-while-deferring-loads.html = FAIL)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<li>#Non-Skipped Failing Tests: the number of tests that appeared in the
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)test expectation file and were not skipped.
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<li>Failing rate: #NonSkippedFailing / (#Tests - #Skipped)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<li>Passing rate: 100 - (Failing rate)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)</ul>
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_object.write(legend_txt)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_object.write('<table border="1">')
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_object.write('<tr><th>Base Directory</th>')
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_object.write('<th>Trend Graph</th>')
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_object.write('<th>#Tests</th>')
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_object.write('<th>#Skipped Tests</th>')
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_object.write('<th>#Non-Skipped Failing Tests</th>')
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_object.write('<th>Failing Rate</th>')
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_object.write('<th>Passing Rate</th>')
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_object.write('<th>Last Revision Number</th>')
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_object.write('<th>Last Revision Date</th>')
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_object.write('<th>Owner Email</th>')
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_object.write('<th>Bug Information</th></tr>\n')
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_group_list.sort()
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for i, test_group in enumerate(test_group_list):
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_object.write('<tr class="d' + str(i % 2) + '">\n')
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_object.write('<td>' + test_group + '</td>\n')
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_object.write('</tr>\n')
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_object.write('</table>')
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_object.close()
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
125558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch# TODO(shadi): Use only one file with main()! Remove this file in favor of
126558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch# layouttest_analyzer.py main().
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def main():
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A main function for the analyzer runner."""
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  options = ParseOption()
130558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  run_config_map = DEFAULT_RUN_CONFIG
131558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  test_group_list = run_config_map.keys()
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dashboard_file_location = os.path.join(options.graph_directory_location,
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         'index.html')
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if not os.path.exists(dashboard_file_location):
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GenerateDashboardHTMLFile(dashboard_file_location, test_group_list)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for test_group in test_group_list:
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Prepare the result if it does not exist.
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # The directory name should be changed to avoid collision
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # with the file separator.
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    test_group_name_for_data = test_group.replace('/', '_')
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result_dir = os.path.join(options.result_directory_location,
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              test_group_name_for_data)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not os.path.exists(result_dir):
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      os.mkdir(result_dir)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    graph_file = os.path.join(options.graph_directory_location,
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              test_group_name_for_data + '.html')
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not os.path.exists(graph_file):
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Copy the template file.
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      shutil.copy(os.path.join('graph', 'graph.html'),
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  graph_file)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      os.chmod(graph_file, 0744)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cmd = ('python layouttest_analyzer.py -x %s -d %s -t %s'
153558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch           ' -q %s ') % (
154558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch               test_group, result_dir, graph_file, dashboard_file_location)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if run_config_map[test_group][0]:
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cmd += '-n ' + run_config_map[test_group][0] + ' '
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if run_config_map[test_group][1]:
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cmd += '-r ' + run_config_map[test_group][1] + ' '
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if options.email_only_change_mode:
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cmd += ' -c '
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if options.issue_detail_mode:
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cmd += ' -z '
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print 'Running ' + cmd
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    proc = Popen(cmd, shell=True)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    proc.communicate()
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if '__main__' == __name__:
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  main()
170