blob: 5c4be81c37dc65fcef81ff7d1702322f3563e194 [file] [log] [blame]
#!/usr/bin/env 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.
"""Dromaeo benchmark automation script.
Script runs dromaeo tests in browsers specified by --browser switch and saves
results to a spreadsheet on
1. Install Google Data APIs Python Client Library from
2. Checkout Dromaeo benchmark from and provide
local path to it in --dromaeo_home switch.
3. Create a spreadsheet at and specify its 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: -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
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",
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 = str(process.communicate()[0])
match = + '\s+(\d+)', stdout)
if match:
pid ='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 = ''
file = open(self.token_file, 'r')
token =
authenticated = True
except (IOError, gdata.service.RequestError):
if not authenticated: = email
self.gd_client.password = getpass.getpass('Password for %s: ' % email)
self.gd_client.source = 'python robot for dromaeo'
token = self.gd_client.GetClientLoginToken()
file = open(self.token_file, 'w')
except (IOError):
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 ='/', 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,
def WriteColumnTitles(self, run_count):
"Create titles for test results (mean 1, mean 2, ..., average mean, ...)"
row = []
for prop in test_props:
for i in xrange(run_count):
row.append('%s %i' % (prop, i + 1))
row.append('average ' + prop)
def WriteBrowserBenchmarkTitle(self, browser_name):
"Create browser benchmark title (browser name, date time)"
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.extend([str(x) for x in test_data[prop]])
row.append(str(sum(test_data[prop]) / len(test_data[prop])))
class DromaeoHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_POST(self):
self.wfile.write("<HTML>POST OK.<BR><BR>");
length = int(self.headers.getheader('content-length'))
parameters = urlparse.parse_qs(
self.server.got_post = True
self.server.post_data = parameters['data']
class BenchmarkResults(object):
"Storage class for dromaeo benchmark results"
def __init__(self): = {}
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
# Test is encountered for the first time.[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[test_name][prop].append(value)
def main():
options = ParseArguments()
# Start sever with dromaeo.
server = HTTPServer(('', options.port), DromaeoHandler)
# Open and prepare spreadsheet on google docs.
spreadsheet_writer = SpreadsheetWriter(,
for browser in options.browsers:
browser_name = os.path.splitext(os.path.basename(browser))[0]
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:
# Kill browser.
# Insert test results into spreadsheet.
for (test_name, test_data) in
spreadsheet_writer.WriteBrowserBenchmarkResults(test_name, test_data)
return 0
if __name__ == '__main__':