diff options
author | imasaki@google.com <imasaki@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-23 20:47:58 +0000 |
---|---|---|
committer | imasaki@google.com <imasaki@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-23 20:47:58 +0000 |
commit | 8208335b3360a6cd209c51f3906140a9a6425b09 (patch) | |
tree | 4ad2d56a28f3e1e556652e7b2d91ef624e47f7be /media | |
parent | e800385483beaf8b57eb81622edc269dc2b77426 (diff) | |
download | chromium_src-8208335b3360a6cd209c51f3906140a9a6425b09.zip chromium_src-8208335b3360a6cd209c51f3906140a9a6425b09.tar.gz chromium_src-8208335b3360a6cd209c51f3906140a9a6425b09.tar.bz2 |
The runner script runs the analyzer with various parameters (especially, group names defined in the CSV file). The runner also creates dashboard page to
summarize the results in simple HTML table.
Also, fixed the issue when there is no previous result to compare in the result directory. In that case, it still updates the trend graph.
Review URL: http://codereview.chromium.org/7918010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@102568 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/tools/layout_tests/anno/media.csv (renamed from media/tools/layout_tests/anno/anno.csv) | 0 | ||||
-rw-r--r-- | media/tools/layout_tests/layouttest_analyzer.py | 357 | ||||
-rw-r--r-- | media/tools/layout_tests/layouttest_analyzer_helpers.py | 22 | ||||
-rw-r--r-- | media/tools/layout_tests/layouttest_analyzer_helpers_unittest.py | 20 | ||||
-rw-r--r-- | media/tools/layout_tests/layouttest_analyzer_runner.py | 153 | ||||
-rw-r--r-- | media/tools/layout_tests/runner_config/runner_config.csv | 46 | ||||
-rw-r--r-- | media/tools/layout_tests/trend_graph.py | 23 |
7 files changed, 504 insertions, 117 deletions
diff --git a/media/tools/layout_tests/anno/anno.csv b/media/tools/layout_tests/anno/media.csv index 3447857..3447857 100644 --- a/media/tools/layout_tests/anno/anno.csv +++ b/media/tools/layout_tests/anno/media.csv diff --git a/media/tools/layout_tests/layouttest_analyzer.py b/media/tools/layout_tests/layouttest_analyzer.py index d9171c5..2c97e02 100644 --- a/media/tools/layout_tests/layouttest_analyzer.py +++ b/media/tools/layout_tests/layouttest_analyzer.py @@ -28,7 +28,7 @@ CURRENT_RESULT_FILE_FOR_DEBUG = os.path.join(DEFAULT_RESULT_DIR, PREV_TIME_FOR_DEBUG = '2011-09-11-18' -def parse_option(): +def ParseOption(): """Parse command-line options using OptionParser. Returns: @@ -38,27 +38,27 @@ def parse_option(): option_parser.add_option('-r', '--receiver-email-address', dest='receiver_email_address', - help=('receiver\'s email adddress (defaults to ' - '%default'), - default='imasaki@chromium.org') + help=('receiver\'s email address. ' + 'Result email is not sent if this is not ' + 'specified.')) option_parser.add_option('-g', '--debug-mode', dest='debug', help=('Debug mode is used when you want to debug ' 'the analyzer by using local file rather ' 'than getting data from SVN. This shortens ' - 'the debugging time (off by default)'), + 'the debugging time (off by default).'), action='store_true', default=False) option_parser.add_option('-t', '--trend-graph-location', dest='trend_graph_location', help=('Location of the bug trend file; ' 'file is expected to be in Google ' 'Visualization API trend-line format ' - '(defaults to %default)'), + '(defaults to %default).'), default=DEFAULT_GRAPH_FILE) option_parser.add_option('-a', '--bug-anno-file-location', dest='bug_annotation_file_location', help=('Location of the bug annotation file; ' 'file is expected to be in CSV format ' - '(default to %default)'), + '(default to %default).'), default=DEFAULT_ANNO_FILE) option_parser.add_option('-n', '--test-group-file-location', dest='test_group_file_location', @@ -77,14 +77,14 @@ def parse_option(): option_parser.add_option('-d', '--result-directory-location', dest='result_directory_location', help=('Name of result directory location ' - '(default to %default)'), + '(default to %default).'), default=DEFAULT_RESULT_DIR) option_parser.add_option('-b', '--email-appended-text-file-location', dest='email_appended_text_file_location', help=('File location of the email appended text. ' 'The text is appended in the status email. ' '(default to %default and no text is ' - 'appended in that case.)'), + 'appended in that case).'), default=None) option_parser.add_option('-c', '--email-only-change-mode', dest='email_only_change_mode', @@ -93,24 +93,45 @@ def parse_option(): 'analyzer result compared to the previous ' 'result (off by default)'), action='store_true', default=False) + option_parser.add_option('-q', '--dashboard-file-location', + dest='dashboard_file_location', + help=('Location of dashboard file. The results are ' + 'not reported to the dashboard if this ' + 'option is not specified.')) return option_parser.parse_args()[0] -def main(): - """A main function for the analyzer.""" - options = parse_option() - start_time = datetime.now() - # Do the main analysis. - if not options.debug: - if not options.test_group_file_location and not options.test_group_name: +def GetCurrentAndPreviousResults(debug, test_group_file_location, + test_group_name, result_directory_location): + """Get current and the latest previous analyzer results. + + In debug mode, they are read from predefined files. In non-debug mode, + current analyzer results are dynamically obtained from Webkit SVN and + the latest previous result is read from the corresponding file. + + Args: + debug: please refer to |options|. + test_group_file_location: please refer to |options|. + test_group_name: please refer to |options|. + result_directory_location: please refer to |options|. + + Returns: + a tuple of the following: + prev_time: the previous time string that is compared against. + prev_analyzer_result_map: previous analyzer result map. Please refer to + layouttest_analyzer_helpers.AnalyzerResultMap. + analyzer_result_map: current analyzer result map. Please refer to + layouttest_analyzer_helpers.AnalyzerResultMap. + """ + if not debug: + if not test_group_file_location and not test_group_name: print ('Either --test-group-name or --test_group_file_location must be ' 'specified. Exiting this program.') sys.exit() filter_names = [] - if options.test_group_file_location and ( - os.path.exists(options.test_group_file_location)): + if test_group_file_location and os.path.exists(test_group_file_location): filter_names = LayoutTests.GetLayoutTestNamesFromCSV( - options.test_group_file_location) + test_group_file_location) parent_location_list = LayoutTests.GetParentDirectoryList(filter_names) recursion = False else: @@ -120,7 +141,7 @@ def main(): # http://svn.webkit.org/repository/webkit/trunk/LayoutTests/media # Filtering is not set so all HTML files are considered as valid tests. # Also, we look for the tests recursively. - if not os.path.exists(options.test_group_file_location): + if not os.path.exists(test_group_file_location): print ('Warning: CSV file (%s) does not exist. So it is ignored and ' '%s is used for obtaining test names') % ( options.test_group_file_location, options.test_group_name) @@ -135,9 +156,13 @@ def main(): filter_names=filter_names) analyzer_result_map = layouttest_analyzer_helpers.AnalyzerResultMap( layouttests.JoinWithTestExpectation(TestExpectations())) - (prev_time, prev_analyzer_result_map) = ( - layouttest_analyzer_helpers.FindLatestResult( - options.result_directory_location)) + result = layouttest_analyzer_helpers.FindLatestResult( + result_directory_location) + if result: + (prev_time, prev_analyzer_result_map) = result + else: + prev_time = None + prev_analyzer_result_map = None else: analyzer_result_map = layouttest_analyzer_helpers.AnalyzerResultMap.Load( CURRENT_RESULT_FILE_FOR_DEBUG) @@ -145,12 +170,25 @@ def main(): prev_analyzer_result_map = ( layouttest_analyzer_helpers.AnalyzerResultMap.Load( os.path.join(DEFAULT_RESULT_DIR, prev_time))) + return (prev_time, prev_analyzer_result_map, analyzer_result_map) + - # Read bug annotations and generate an annotation map used for the status - # email. +def ReadEmailInformation(bug_annotation_file_location, + email_appended_text_file_location): + """Read bug annotations and generate an annotation map used for email. + + Args: + bug_annotation_file_location: please refer to |options|. + email_appended_text_file_location: please refer to |options|. + + Returns: + a tuple of the following: + anno_map: a dictionary that maps bug names to their annotations. + appended_text_to_email: the text string to append to the status email. + """ anno_map = {} try: - file_object = open(options.bug_annotation_file_location) + file_object = open(bug_annotation_file_location) except IOError: print 'cannot open annotation file %s. Skipping.' % ( options.bug_annotation_file_location) @@ -161,92 +199,211 @@ def main(): file_object.close() appended_text_to_email = '' - if options.email_appended_text_file_location: + if email_appended_text_file_location: try: - file_object = open(options.email_appended_text_file_location) + file_object = open(email_appended_text_file_location) except IOError: print 'cannot open email appended text file %s. Skipping.' % ( - options.email_appended_text_file_location) + email_appended_text_file_location) else: appended_text_to_email = ''.join(file_object.readlines()) file_object.close() + return (anno_map, appended_text_to_email) + - diff_map = analyzer_result_map.CompareToOtherResultMap( - prev_analyzer_result_map) - result_change = (any(diff_map['whole']) or any(diff_map['skip']) or - any(diff_map['nonskip'])) - # Email only when |email_only_change_mode| is False or there - # is a change in the result compared to the last result. - simple_rev_str = '' - if not options.email_only_change_mode or result_change: - prev_time_in_float = datetime.strptime(prev_time, '%Y-%m-%d-%H') - prev_time_in_float = time.mktime(prev_time_in_float.timetuple()) - if options.debug: - cur_time_in_float = datetime.strptime(CUR_TIME_FOR_DEBUG, '%Y-%m-%d-%H') - cur_time_in_float = time.mktime(cur_time_in_float.timetuple()) +def SendEmail(prev_time, prev_analyzer_result_map, analyzer_result_map, + anno_map, appended_text_to_email, email_only_change_mode, debug, + receiver_email_address, test_group_name): + """Send result status email. + + Args: + prev_time: the previous time string that is compared against. + prev_analyzer_result_map: previous analyzer result map. Please refer to + layouttest_analyzer_helpers.AnalyzerResultMap. + analyzer_result_map: current analyzer result map. Please refer to + layouttest_analyzer_helpers.AnalyzerResultMap. + anno_map: a dictionary that maps bug names to their annotations. + appended_text_to_email: the text string to append to the status email. + email_only_change_mode: please refer to |options|. + debug: please refer to |options|. + receiver_email_address: please refer to |options|. + test_group_name: please refer to |options|. + + Returns: + a tuple of the following: + result_change: a boolean indicating whether there is a change in the + result compared with the latest past result. + diff_map: please refer to + layouttest_analyzer_helpers.SendStatusEmail(). + simple_rev_str: a simple version of revision string that is sent in + the email. + """ + if prev_analyzer_result_map: + diff_map = analyzer_result_map.CompareToOtherResultMap( + prev_analyzer_result_map) + result_change = (any(diff_map['whole']) or any(diff_map['skip']) or + any(diff_map['nonskip'])) + # Email only when |email_only_change_mode| is False or there + # is a change in the result compared to the last result. + simple_rev_str = '' + if not email_only_change_mode or result_change: + prev_time_in_float = datetime.strptime(prev_time, '%Y-%m-%d-%H') + prev_time_in_float = time.mktime(prev_time_in_float.timetuple()) + if debug: + cur_time_in_float = datetime.strptime(CUR_TIME_FOR_DEBUG, + '%Y-%m-%d-%H') + cur_time_in_float = time.mktime(cur_time_in_float.timetuple()) + else: + cur_time_in_float = time.time() + (rev_str, simple_rev_str) = ( + layouttest_analyzer_helpers.GetRevisionString(prev_time_in_float, + cur_time_in_float, + diff_map)) + if receiver_email_address: + layouttest_analyzer_helpers.SendStatusEmail( + prev_time, analyzer_result_map, diff_map, anno_map, + receiver_email_address, test_group_name, + appended_text_to_email, rev_str) + if simple_rev_str: + simple_rev_str = '\'' + simple_rev_str + '\'' else: - cur_time_in_float = time.time() - (rev_str, simple_rev_str) = ( - layouttest_analyzer_helpers.GetRevisionString(prev_time_in_float, - cur_time_in_float, - diff_map)) - layouttest_analyzer_helpers.SendStatusEmail( - prev_time, analyzer_result_map, diff_map, anno_map, - options.receiver_email_address, options.test_group_name, - appended_text_to_email, rev_str) - if simple_rev_str: - simple_rev_str = '\'' + simple_rev_str + '\'' + simple_rev_str = 'undefined' # GViz uses undefined for NONE. else: - simple_rev_str = 'undefined' # GViz uses undefined for NONE. - if not options.debug: - # Save the current result. + # Initial result should be written to tread-graph if there are no previous + # results. + result_change = True + diff_map = None + simple_rev_str = 'undefined' + return (result_change, diff_map, simple_rev_str) + + +def UpdateTrendGraph(start_time, analyzer_result_map, diff_map, simple_rev_str, + trend_graph_location): + """Update trend graph in GViz. + + Annotate the graph with revision information. + + Args: + start_time: the script starting time as a float value. + analyzer_result_map: current analyzer result map. Please refer to + layouttest_analyzer_helpers.AnalyzerResultMap. + diff_map: a map that has 'whole', 'skip' and 'nonskip' as keys. + Please refer to |diff_map| in + |layouttest_analyzer_helpers.SendStatusEmail()|. + simple_rev_str: a simple version of revision string that is sent in + the email. + trend_graph_location: the location of the trend graph that needs to be + updated. + + Returns: + a dictionary that maps result data category ('whole', 'skip', 'nonskip', + 'passingrate') to information tuple (the number of the tests, + annotation, simple_rev_string) of the given result + data category. These tuples are used for trend graph update. + """ + # Trend graph update (if specified in the command-line argument) when + # there is change from the last result. + # Currently, there are two graphs (graph1 is for 'whole', 'skip', + # 'nonskip' and the graph2 is for 'passingrate'). Please refer to + # graph/graph.html. + # Sample JS annotation for graph1: + # [new Date(2011,8,12,10,41,32),224,undefined,'',52,undefined, + # undefined, 12, 'test1,','<a href="http://t</a>,',], + # This example lists 'whole' triple and 'skip' triple and + # 'nonskip' triple. Each triple is (the number of tests that belong to + # the test group, linked text, a link). The following code generates this + # automatically based on rev_string etc. + trend_graph = TrendGraph(trend_graph_location) + datetime_string = start_time.strftime('%Y,%m,%d,%H,%M,%S') + data_map = {} + passingrate_anno = '' + for test_group in ['whole', 'skip', 'nonskip']: + anno = 'undefined' + tests = analyzer_result_map.result_map[test_group].keys() + test_str = '' + links = '' + if diff_map and diff_map[test_group]: + for i in [0, 1]: + for (name, _) in diff_map[test_group][i]: + test_str += name + ',' + # This is link to test HTML in WebKit SVN. + links += ('<a href="http://trac.webkit.org/browser/trunk/' + 'LayoutTests/%s">%s</a>,') % (name, name) + if test_str: + anno = '\'' + test_str + '\'' + # The annotation of passing rate is a union of all annotations. + passingrate_anno += anno + if links: + links = '\'' + links + '\'' + else: + links = 'undefined' + if test_group is 'whole': + data_map[test_group] = (str(len(tests)), anno, links) + else: + data_map[test_group] = (str(len(tests)), anno, simple_rev_str) + if not passingrate_anno: + passingrate_anno = 'undefined' + data_map['passingrate'] = ( + str(analyzer_result_map.GetPassingRate()), passingrate_anno, + simple_rev_str) + trend_graph.Update(datetime_string, data_map) + return data_map + + +def UpdateDashboard(dashboard_file_location, test_group_name, data_map): + """Update dashboard HTML file. + + Args: + dashboard_file_location: the file location for the dashboard file. + test_group_name: please refer to |options|. + data_map: a dictionary that maps result data category + ('whole', 'skip', 'nonskip', 'passingrate') to information tuple (the + number of the tests, annotation, simple_rev_string) of the given result + data category. + """ + new_str = ('<td><a href="%s">%s</a><td>%s</td><td>%s</td><td>%s</td>' + '<td>%s%%</td><tr>\n') % ( + # Dashboard file and graph must be in the same directory + # to make the following link work. + test_group_name.replace('/', '_') + '.html', + test_group_name, data_map['whole'][0], + data_map['skip'][0], data_map['nonskip'][0], + data_map['passingrate'][0]) + layouttest_analyzer_helpers.ReplaceLineInFile( + dashboard_file_location, test_group_name, new_str) + + +def main(): + """A main function for the analyzer.""" + options = ParseOption() + start_time = datetime.now() + + (prev_time, prev_analyzer_result_map, analyzer_result_map) = ( + GetCurrentAndPreviousResults(options.debug, + options.test_group_file_location, + options.test_group_name, + options.result_directory_location)) + (anno_map, appended_text_to_email) = ReadEmailInformation( + options.bug_annotation_file_location, + options.email_appended_text_file_location) + (result_change, diff_map, simple_rev_str) = ( + SendEmail(prev_time, prev_analyzer_result_map, analyzer_result_map, + anno_map, appended_text_to_email, + options.email_only_change_mode, options.debug, + options.receiver_email_address, options.test_group_name)) + if not options.debug and (result_change or not prev_analyzer_result_map): + # Save the current result when result is changed or the script is + # executed for the first time. date = start_time.strftime('%Y-%m-%d-%H') file_path = os.path.join(options.result_directory_location, date) analyzer_result_map.Save(file_path) - - if result_change: - # Trend graph update (if specified in the command-line argument) when - # there is change from the last result. - # Currently, there are two graphs (graph1 is for 'whole', 'skip', - # 'nonskip' and the graph2 is for 'passingrate'). Please refer to - # graph/graph.html. - # Sample JS annotation for graph1: - # [new Date(2011,8,12,10,41,32),224,undefined,'',52,undefined,undefined, - # 12, 'test1,','<a href="http://t</a>,',], - # This example lists 'whole' triple and 'skip' triple and - # 'nonskip' triple. Each triple is (the number of tests that belong to - # the test group, linked text, a link). The following code generates this - # automatically based on rev_string etc. - trend_graph = TrendGraph(options.trend_graph_location) - datetime_string = start_time.strftime('%Y,%m,%d,%H,%M,%S') - data_map = {} - passingrate_anno = '' - for test_group in ['whole', 'skip', 'nonskip']: - anno = 'undefined' - tests = analyzer_result_map.result_map[test_group].keys() - if diff_map[test_group]: - test_str = '' - links = '' - for i in [0, 1]: - for (name, _) in diff_map[test_group][i]: - test_str += name + ',' - # This is link to test HTML in WebKit SVN. - links += ('<a href="http://trac.webkit.org/browser/trunk/' - 'LayoutTests/%s">%s</a>,') % (name, name) - if test_str: - anno = '\'' + test_str + '\'' - # The annotation of passing rate is a union of all annotations. - passingrate_anno += anno - if test_group is 'whole': - data_map[test_group] = (str(len(tests)), anno, '\'' + links + '\'') - else: - data_map[test_group] = (str(len(tests)), anno, simple_rev_str) - if not passingrate_anno: - passingrate_anno = 'undefined' - data_map['passingrate'] = ( - str(analyzer_result_map.GetPassingRate()), passingrate_anno, - simple_rev_str) - trend_graph.Update(datetime_string, data_map) + if result_change or not prev_analyzer_result_map: + data_map = UpdateTrendGraph(start_time, analyzer_result_map, diff_map, + simple_rev_str, options.trend_graph_location) + # Report the result to dashboard. + if options.dashboard_file_location: + UpdateDashboard(options.dashboard_file_location, options.test_group_name, + data_map) if '__main__' == __name__: diff --git a/media/tools/layout_tests/layouttest_analyzer_helpers.py b/media/tools/layout_tests/layouttest_analyzer_helpers.py index 3b30312e..c1d628c 100644 --- a/media/tools/layout_tests/layouttest_analyzer_helpers.py +++ b/media/tools/layout_tests/layouttest_analyzer_helpers.py @@ -8,10 +8,12 @@ from datetime import datetime from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText +import fileinput import os import pickle import smtplib import socket +import sys import time import urllib @@ -414,6 +416,20 @@ def FindLatestTime(time_list): return None +def ReplaceLineInFile(file_path, search_exp, replace_line): + """Replace line which has |search_exp| with |replace_line| within a file. + + Args: + file_path: the file that is being replaced. + search_exp: search expression to find a line to be replaced. + replace_line: the new line. + """ + for line in fileinput.input(file_path, inplace=1): + if search_exp in line: + line = replace_line + sys.stdout.write(line) + + def FindLatestResult(result_dir): """Find the latest result in |result_dir| and read and return them. @@ -424,10 +440,14 @@ def FindLatestResult(result_dir): result_dir: the result directory. Returns: - a tuple of filename (latest_time) of the and the latest analyzer result. + A tuple of filename (latest_time) and the latest analyzer result. + Returns None if there is no file or no file that matches the file + patterns used ('%Y-%m-%d-%H'). """ dir_list = os.listdir(result_dir) file_name = FindLatestTime(dir_list) + if not file_name: + return None file_path = os.path.join(result_dir, file_name) return (file_name, AnalyzerResultMap.Load(file_path)) diff --git a/media/tools/layout_tests/layouttest_analyzer_helpers_unittest.py b/media/tools/layout_tests/layouttest_analyzer_helpers_unittest.py index da60896..b362228 100644 --- a/media/tools/layout_tests/layouttest_analyzer_helpers_unittest.py +++ b/media/tools/layout_tests/layouttest_analyzer_helpers_unittest.py @@ -160,6 +160,26 @@ class TestLayoutTestAnalyzerHelpers(unittest.TestCase): self.RunTestGetRevisionString('2011-09-01-00', '2011-09-02-00', '', '', 'foo1.html') + def testReplaceLineInFile(self): + file_path = os.path.join('test_data', 'inplace.txt') + f = open(file_path, 'w') + f.write('Hello') + f.close() + layouttest_analyzer_helpers.ReplaceLineInFile( + file_path, 'Hello', 'Goodbye') + f = open(file_path, 'r') + self.assertEquals(f.readline(), 'Goodbye') + f.close() + layouttest_analyzer_helpers.ReplaceLineInFile( + file_path, 'Bye', 'Hello') + f = open(file_path, 'r') + self.assertEquals(f.readline(), 'Goodbye') + f.close() + + def testFindLatestResultWithNoData(self): + self.assertFalse( + layouttest_analyzer_helpers.FindLatestResult('test_data')) + if __name__ == '__main__': unittest.main() diff --git a/media/tools/layout_tests/layouttest_analyzer_runner.py b/media/tools/layout_tests/layouttest_analyzer_runner.py new file mode 100644 index 0000000..dacac1a --- /dev/null +++ b/media/tools/layout_tests/layouttest_analyzer_runner.py @@ -0,0 +1,153 @@ +#!/usr/bin/python +# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Main function to run the layout test analyzer. + +The purpose of this script is to run the layout test analyzer for various +teams based on the run configuration file in CSV format. The CSV file is based +on https://sites.google.com/a/chromium.org/dev/developers/testing/ +webkit-layout-tests/layout-test-stats-1. +""" + +import csv +import optparse +import os +import shutil +from subprocess import Popen +import sys + +DEFAULT_RUNNER_CONFIG_FILE = os.path.join('runner_config', + 'runner_config.csv') + +# Predefined result/graph directory. +DEFAULT_RESULT_DIR = 'result' +DEFAULT_GRAPH_DIR = 'graph' +DEFAULT_ANNO_DIR = 'anno' + + +def ParseOption(): + """Parse command-line options using OptionParser. + + Returns: + an object containing all command-line option information. + """ + option_parser = optparse.OptionParser() + + option_parser.add_option('-c', '--runner-config-file-location', + dest='runner_config_file_location', + help=('Location of the bug annotation file; ' + 'file is expected to be in CSV format ' + '(default to %default)'), + default=DEFAULT_RUNNER_CONFIG_FILE) + option_parser.add_option('-x', '--test-group-name', + dest='test_group_name', + help='A name of test group.') + option_parser.add_option('-d', '--result-directory-location', + dest='result_directory_location', + help=('Name of result directory location ' + '(default to %default)'), + default=DEFAULT_RESULT_DIR) + option_parser.add_option('-p', '--graph-directory-location', + dest='graph_directory_location', + help=('Name of graph directory location ' + '(default to %default)'), + default=DEFAULT_GRAPH_DIR) + option_parser.add_option('-a', '--anno-directory-location', + dest='annotation_directory_location', + help=('Name of annotation directory location; ' + 'each annotation file should be the same ' + 'as test group name with replacement of "/"' + 'with "_" (default to %default)'), + default=DEFAULT_ANNO_DIR) + option_parser.add_option('-b', '--email-appended-text-file-location', + dest='email_appended_text_file_location', + help=('File location of the email appended text. ' + 'The text is appended in the status email. ' + '(default to %default and no text is ' + 'appended in that case.)'), + default=None) + return option_parser.parse_args()[0] + + +def GenerateDashboardHTMLFile(file_name, test_group_list): + """Generate dashboard HTML file. + + Currently, it is simple table that shows all the analyzer results. + + Args: + file_name: the file name of the dashboard. + test_group_list: a list of test group names such as 'media' or 'composite'. + """ + file_object = open(file_name, 'wb') + file_object.write('<table border="1">') + file_object.write('<tr><th>test group</th>') + file_object.write('<th>#Tests</th>') + file_object.write('<th>#Skipped Tests</th>') + file_object.write('<th>#Non-Skipped Failing Tests</th>') + file_object.write('<th>Passing Rate</td></tr>') + for test_group in test_group_list: + file_object.write('<tr>\n') + file_object.write('<td>' + test_group + '</td>\n') + file_object.write('</tr>') + file_object.write('</table>') + file_object.close() + + +def main(): + """A main function for the analyzer runner.""" + options = ParseOption() + run_config_map = {} + try: + file_object = open(options.runner_config_file_location) + except IOError: + print 'cannot open runner configuration file %s. Exiting.' % ( + options.runner_config_file_location) + sys.exit() + data = csv.reader(file_object) + # Skip the first row since it is a comment/header line. + data.next() + for row in data: + run_config_map[row[0]] = (row[1], row[2]) + file_object.close() + if options.test_group_name: + test_group_list = [options.test_group_name] + else: + test_group_list = run_config_map.keys() + dashboard_file_location = os.path.join(options.graph_directory_location, + 'index.html') + if not os.path.exists(dashboard_file_location): + GenerateDashboardHTMLFile(dashboard_file_location, test_group_list) + for test_group in test_group_list: + # Prepare the result if it does not exist. + # The directory name should be changed to avoid collision + # with the file separator. + test_group_name_for_data = test_group.replace('/', '_') + result_dir = os.path.join(options.result_directory_location, + test_group_name_for_data) + if not os.path.exists(result_dir): + os.mkdir(result_dir) + graph_file = os.path.join(options.graph_directory_location, + test_group_name_for_data + '.html') + if not os.path.exists(graph_file): + # Copy the template file. + shutil.copy(os.path.join('graph', 'graph.html'), + graph_file) + os.chmod(graph_file, 0744) + anno_file = os.path.join(options.annotation_directory_location, + test_group_name_for_data + '.csv') + cmd = ('python layouttest_analyzer.py -x %s -n %s -r %s -d %s -t %s' + ' -q %s -a %s -c') % ( + test_group, run_config_map[test_group][0], + run_config_map[test_group][1], result_dir, graph_file, + dashboard_file_location, anno_file) + if options.email_appended_text_file_location: + cmd += ' -b ' + options.email_appended_text_file_location + print 'Running ' + cmd + proc = Popen(cmd, shell=True) + proc.communicate() + + +if '__main__' == __name__: + main() diff --git a/media/tools/layout_tests/runner_config/runner_config.csv b/media/tools/layout_tests/runner_config/runner_config.csv new file mode 100644 index 0000000..5d118c9 --- /dev/null +++ b/media/tools/layout_tests/runner_config/runner_config.csv @@ -0,0 +1,46 @@ +test group name, test name configuration file location, contact person name +animations,, +canvas,, +compositing,, +css2.1,, +css3,, +editing,, +fast/backgrounds,, +fast/borders,, +fast/canvas,, +fast/css,, +fast/dom,, +fast/encoding,, +fast/flexbox,, +fast/forms,, +fast/frames,, +fast/html,, +fast/images,, +fast/js,, +fast/loader,, +fast/multicol,, +fast/overflow,, +fast/parser,, +fast/repaint,, +fast/speech,, +fast/table,, +fast/text,, +fast/transforms,, +fast/xsl,, +fonts,, +http/tests/appcache,, +http/tests/inspector,, +http/tests/misc,, +http/tests/navigation,, +http/tests/websocket,, +inspector,, +jquery,, +loader,, +media,testname/media.csv,imasaki@chromium.org +platform/chromium/compositing,, +platform/chromium/fast,, +plugins,, +scrollbars,, +svg,, +tables,, +transforms,, diff --git a/media/tools/layout_tests/trend_graph.py b/media/tools/layout_tests/trend_graph.py index 8406d19..86f857b 100644 --- a/media/tools/layout_tests/trend_graph.py +++ b/media/tools/layout_tests/trend_graph.py @@ -5,10 +5,10 @@ """A module for manipulating trend graph with analyzer result history.""" -import fileinput import os import sys +import layouttest_analyzer_helpers DEFAULT_TREND_GRAPH_PATH = os.path.join('graph', 'graph.html') @@ -61,7 +61,9 @@ class TrendGraph(object): joined_str) new_line_for_numbers += ' %s\n' % ( LINE_INSERT_POINT_FOR_NUMBERS) - self._ReplaceLine(LINE_INSERT_POINT_FOR_NUMBERS, new_line_for_numbers) + layouttest_analyzer_helpers.ReplaceLineInFile( + self._location, LINE_INSERT_POINT_FOR_NUMBERS, + new_line_for_numbers) joined_str = '%s,%s,%s' % ( data_map['passingrate'][0], data_map['nonskip'][1], @@ -70,17 +72,6 @@ class TrendGraph(object): datetime_string, joined_str) new_line_for_passingrate += ' %s\n' % ( LINE_INSERT_POINT_FOR_PASSING_RATE) - self._ReplaceLine(LINE_INSERT_POINT_FOR_PASSING_RATE, - new_line_for_passingrate) - - def _ReplaceLine(self, search_exp, replace_line): - """Replace line which has |search_exp| with |replace_line|. - - Args: - search_exp: search expression to find a line to be replaced. - replace_line: the new line. - """ - for line in fileinput.input(self._location, inplace=1): - if search_exp in line: - line = replace_line - sys.stdout.write(line) + layouttest_analyzer_helpers.ReplaceLineInFile( + self._location, LINE_INSERT_POINT_FOR_PASSING_RATE, + new_line_for_passingrate) |