Index: tools/dromaeo_benchmark_runner/dromaeo_benchmark_runner.py |
=================================================================== |
--- tools/dromaeo_benchmark_runner/dromaeo_benchmark_runner.py (revision 0) |
+++ tools/dromaeo_benchmark_runner/dromaeo_benchmark_runner.py (revision 0) |
@@ -0,0 +1,263 @@ |
+#!/usr/bin/env python |
+# |
+# Copyright 2009 Google Inc. All Rights Reserved. |
+ |
+"""Dromaeo benchmark automation script. |
+ |
+Script runs dromaeo tests in browsers specified by --browser switch and saves |
+results to a spreadsheet on docs.google.com. |
+ |
+Prerequisites: |
+1. Install Google Data APIs Python Client Library from |
+ http://code.google.com/p/gdata-python-client. |
+2. Checkout Dromaeo benchmark from |
+ http://src.chromium.org/svn/trunk/src/chrome/test/data/dromaeo and provide |
+ local path to it in --dromaeo_home switch. |
+3. Create a spreadsheet at http://docs.google.com and specify it's name in |
+ --spreadsheet switch |
+ |
+Benchmark results are presented in the following format: |
+browser | date time |
+test 1 name|m11|...|m1n|test 1 average mean| |e11|...|e1n|test 1 average error |
+test 2 name|m21|...|m2n|test 2 average mean| |e21|...|e2n|test 2 average error |
+... |
+ |
+Here mij is mean run/s in individual dromaeo test i during benchmark run j, |
+eij is error in individual dromaeo test i during benchmark run j. |
+ |
+Example usage: |
+dromaeo_benchmark_runner.py -b "E:\chromium\src\chrome\Release\chrome.exe" |
+ -b "C:\Program Files (x86)\Safari\safari.exe" |
+ -b "C:\Program Files (x86)\Opera 10.50 pre-alpha\opera.exe" -n 1 |
+ -d "E:\chromium\src\chrome\test\data\dromaeo" -f dom -e example@gmail.com |
+ |
+""" |
+ |
+import getpass |
+import json |
+import os |
+import re |
+import subprocess |
+import time |
+import urlparse |
+from optparse import OptionParser |
+from BaseHTTPServer import HTTPServer |
+import SimpleHTTPServer |
+import gdata.spreadsheet.service |
+ |
+max_spreadsheet_columns = 20 |
+test_props = ['mean', 'error'] |
+ |
+def ParseArguments(): |
+ parser = OptionParser() |
+ parser.add_option("-b", "--browser", |
+ action="append", dest="browsers", |
+ help="list of browsers to test") |
+ parser.add_option("-n", "--run_count", dest="run_count", type="int", |
+ default=5, help="number of runs") |
+ parser.add_option("-d", "--dromaeo_home", dest="dromaeo_home", |
+ help="directory with your dromaeo files") |
+ parser.add_option("-p", "--port", dest="port", type="int", |
+ default=8080, help="http server port") |
+ parser.add_option("-f", "--filter", dest="filter", |
+ default="dom", help="dromaeo suite filter") |
+ parser.add_option("-e", "--email", dest="email", |
+ help="your google docs account") |
+ parser.add_option("-s", "--spreadsheet", dest="spreadsheet_title", |
+ default="dromaeo", |
+ help="your google docs spreadsheet name") |
+ |
+ options = parser.parse_args()[0] |
+ |
+ if not options.dromaeo_home: |
+ raise Exception('please specify dromaeo_home') |
+ |
+ return options |
+ |
+ |
+def KillProcessByName(process_name): |
+ process = subprocess.Popen('wmic process get processid, executablepath', |
+ stdout=subprocess.PIPE) |
+ stdout = str(process.communicate()[0]) |
+ match = re.search(re.escape(process_name) + '\s+(\d+)', stdout) |
+ if match: |
+ pid = match.group(1) |
+ subprocess.call('taskkill /pid %s' % pid) |
+ |
+ |
+class SpreadsheetWriter(object): |
+ "Utility class for storing benchmarking results in Google spreadsheets." |
+ |
+ def __init__(self, email, spreadsheet_title): |
+ '''Login to google docs and search for spreadsheet''' |
+ |
+ self.token_file = os.path.expanduser("~/.dromaeo_bot_auth_token") |
+ self.gd_client = gdata.spreadsheet.service.SpreadsheetsService() |
+ |
+ authenticated = False |
+ if os.path.exists(self.token_file): |
+ token = '' |
+ try: |
+ file = open(self.token_file, 'r') |
+ token = file.read() |
+ file.close() |
+ self.gd_client.SetClientLoginToken(token) |
+ self.gd_client.GetSpreadsheetsFeed() |
+ authenticated = True |
+ except (IOError, gdata.service.RequestError): |
+ pass |
+ if not authenticated: |
+ self.gd_client.email = email |
+ self.gd_client.password = getpass.getpass('Password for %s: ' % email) |
+ self.gd_client.source = 'python robot for dromaeo' |
+ self.gd_client.ProgrammaticLogin() |
+ token = self.gd_client.GetClientLoginToken() |
+ try: |
+ file = open(self.token_file, 'w') |
+ file.write(token) |
+ file.close() |
+ except (IOError): |
+ pass |
+ os.chmod(self.token_file, 0600) |
+ |
+ # Search for the spreadsheet with title = spreadsheet_title. |
+ spreadsheet_feed = self.gd_client.GetSpreadsheetsFeed() |
+ for spreadsheet in spreadsheet_feed.entry: |
+ if spreadsheet.title.text == spreadsheet_title: |
+ self.spreadsheet_key = spreadsheet.id.text.rsplit('/', 1)[1] |
+ if not self.spreadsheet_key: |
+ raise Exception('Spreadsheet %s not found' % spreadsheet_title) |
+ |
+ # Get the key of the first worksheet in spreadsheet. |
+ worksheet_feed = self.gd_client.GetWorksheetsFeed(self.spreadsheet_key) |
+ self.worksheet_key = worksheet_feed.entry[0].id.text.rsplit('/', 1)[1] |
+ |
+ def _InsertRow(self, row): |
+ row = dict([('c' + str(i), row[i]) for i in xrange(len(row))]) |
+ self.gd_client.InsertRow(row, self.spreadsheet_key, self.worksheet_key) |
+ |
+ def _InsertBlankRow(self): |
+ self._InsertRow('-' * self.columns_count) |
+ |
+ def PrepareSpreadsheet(self, run_count): |
+ """Update cells in worksheet topmost row with service information. |
+ |
+ Calculate column count corresponding to run_count and create worksheet |
+ column titles [c0, c1, ...] in the topmost row to speed up spreadsheet |
+ updates (it allows to insert a whole row with a single request) |
+ """ |
+ |
+ # Calculate the number of columns we need to present all test results. |
+ self.columns_count = (run_count + 2) * len(test_props) |
+ if self.columns_count > max_spreadsheet_columns: |
+ # Google spreadsheet has just max_spreadsheet_columns columns. |
+ max_run_count = max_spreadsheet_columns / len(test_props) - 2 |
+ raise Exception('maximum run count is %i' % max_run_count) |
+ # Create worksheet column titles [c0, c1, ..., cn]. |
+ for i in xrange(self.columns_count): |
+ self.gd_client.UpdateCell(1, i + 1, 'c' + str(i), self.spreadsheet_key, |
+ self.worksheet_key) |
+ |
+ def WriteColumnTitles(self, run_count): |
+ "Create titles for test results (mean 1, mean 2, ..., average mean, ...)" |
+ row = [] |
+ for prop in test_props: |
+ row.append('') |
+ for i in xrange(run_count): |
+ row.append('%s %i' % (prop, i + 1)) |
+ row.append('average ' + prop) |
+ self._InsertRow(row) |
+ |
+ def WriteBrowserBenchmarkTitle(self, browser_name): |
+ "Create browser benchmark title (browser name, date time)" |
+ self._InsertBlankRow() |
+ self._InsertRow([browser_name, time.strftime('%d.%m.%Y %H:%M:%S')]) |
+ |
+ def WriteBrowserBenchmarkResults(self, test_name, test_data): |
+ "Insert a row with single test results" |
+ row = [] |
+ for prop in test_props: |
+ if not row: |
+ row.append(test_name) |
+ else: |
+ row.append('') |
+ row.extend([str(x) for x in test_data[prop]]) |
+ row.append(str(sum(test_data[prop]) / len(test_data[prop]))) |
+ self._InsertRow(row) |
+ |
+ |
+class DromaeoHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): |
+ |
+ def do_POST(self): |
+ self.send_response(200) |
+ self.end_headers() |
+ self.wfile.write("<HTML>POST OK.<BR><BR>"); |
+ length = int(self.headers.getheader('content-length')) |
+ parameters = urlparse.parse_qs(self.rfile.read(length)) |
+ self.server.got_post = True |
+ self.server.post_data = parameters['data'] |
+ |
+ |
+class BenchmarkResults(object): |
+ "Storage class for dromaeo benchmark results" |
+ |
+ def __init__(self): |
+ self.data = {} |
+ |
+ def ProcessBrowserPostData(self, data): |
+ "Convert dromaeo test results in internal format" |
+ tests = json.loads(data[0]) |
+ for test in tests: |
+ test_name = test['name'] |
+ if test_name not in self.data: |
+ # Test is encountered for the first time. |
+ self.data[test_name] = dict([(prop, []) for prop in test_props]) |
+ # Append current run results. |
+ for prop in test_props: |
+ value = -1 |
+ if prop in test: value = test[prop] # workaround for Opera 10.5 |
+ self.data[test_name][prop].append(value) |
+ |
+ |
+def main(): |
+ options = ParseArguments() |
+ |
+ # Start sever with dromaeo. |
+ os.chdir(options.dromaeo_home) |
+ server = HTTPServer(('', options.port), DromaeoHandler) |
+ |
+ # Open and prepare spreadsheet on google docs. |
+ spreadsheet_writer = SpreadsheetWriter(options.email, |
+ options.spreadsheet_title) |
+ spreadsheet_writer.PrepareSpreadsheet(options.run_count) |
+ spreadsheet_writer.WriteColumnTitles(options.run_count) |
+ |
+ for browser in options.browsers: |
+ browser_name = os.path.splitext(os.path.basename(browser))[0] |
+ spreadsheet_writer.WriteBrowserBenchmarkTitle(browser_name) |
+ benchmark_results = BenchmarkResults() |
+ for run_number in xrange(options.run_count): |
+ print '%s run %i' % (browser_name, run_number + 1) |
+ # Run browser. |
+ test_page = 'http://localhost:%i/index.html?%s&automated&post_json' % ( |
+ options.port, options.filter) |
+ browser_process = subprocess.Popen('%s "%s"' % (browser, test_page)) |
+ server.got_post = False |
+ server.post_data = None |
+ # Wait until POST request from browser. |
+ while not server.got_post: |
+ server.handle_request() |
+ benchmark_results.ProcessBrowserPostData(server.post_data) |
+ # Kill browser. |
+ KillProcessByName(browser) |
+ browser_process.wait() |
+ |
+ # Insert test results into spreadsheet. |
+ for (test_name, test_data) in benchmark_results.data.iteritems(): |
+ spreadsheet_writer.WriteBrowserBenchmarkResults(test_name, test_data) |
+ |
+ server.socket.close() |
+ |
+if __name__ == '__main__': |
+ main() |
+ |