| # 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. |
| """Playback driver.""" |
| |
| import cgi |
| import simplejson as json |
| import os |
| import string |
| import sys |
| import threading |
| import urlparse |
| |
| START_PAGE = """<html> |
| <script type="text/javascript"> |
| var runCount = $run_count; |
| var results = []; |
| |
| function run() { |
| var wnd = window.open('?resource=start_page_popup', '', |
| 'width=$width, height=$height'); |
| var timerId = setInterval(function() { |
| wnd.postMessage('ping', '$target_origin'); |
| }, 300); |
| var handleMessage = function(event) { |
| clearInterval(timerId); |
| wnd.close(); |
| document.writeln('<div>' + event.data + '</div>'); |
| results.push(event.data); |
| runCount -= 1; |
| window.removeEventListener('message', handleMessage); |
| if (runCount > 0) { |
| run(); |
| } else { |
| var xmlHttpRequest = new XMLHttpRequest(); |
| xmlHttpRequest.open("POST", '/benchmark/', true); |
| xmlHttpRequest.setRequestHeader("Content-type", "application/json"); |
| xmlHttpRequest.send(JSON.stringify({results: results})); |
| } |
| } |
| window.addEventListener('message', handleMessage, false); |
| } |
| |
| run(); |
| </script> |
| </html> |
| """ |
| |
| START_PAGE_POPUP = """<html> |
| <script type="text/javascript"> |
| window.setTimeout(function() { |
| console.log(window.innerWidth, window.innerHeight); |
| if (window.innerWidth == $width && window.innerHeight == $height) { |
| window.location = '$start_url'; |
| } else { |
| window.resizeBy($width - window.innerWidth, $height - window.innerHeight); |
| window.location = window.location; |
| } |
| }, 200); |
| </script> |
| </html> |
| """ |
| |
| DATA_JS = 'Benchmark.data = $data;' |
| |
| |
| def ReadFile(file_name, mode='r'): |
| f = open(file_name, mode) |
| data = f.read() |
| f.close() |
| return data |
| |
| |
| def ReadJSON(file_name): |
| f = open(file_name, 'r') |
| data = json.load(f) |
| f.close() |
| return data |
| |
| |
| class PlaybackRequestHandler(object): |
| """This class is used to process HTTP requests during test playback. |
| |
| Attributes: |
| test_dir: directory containing test files. |
| test_callback: function to be called when the test is finished. |
| script_dir: directory where javascript files are located. |
| """ |
| |
| def __init__(self, test_dir, test_callback=None, script_dir=os.getcwd()): |
| self.test_dir = test_dir |
| self.test_callback = test_callback |
| self.script_dir = script_dir |
| |
| def ProcessRequest(self, handler): |
| "Processes single HTTP request." |
| |
| parse_result = urlparse.urlparse(handler.path) |
| if parse_result.path.endswith('/benchmark/'): |
| query = cgi.parse_qs(parse_result.query) |
| if 'run_test' in query: |
| run_count = 1 |
| if 'run_count' in query: |
| run_count = query['run_count'][0] |
| self._StartTest(handler, self.test_dir, run_count) |
| elif 'resource' in query: |
| self._GetBenchmarkResource(query['resource'][0], handler) |
| else: |
| self._ProcessBenchmarkReport(handler.body, handler) |
| else: |
| self._GetApplicationResource(handler) |
| |
| def _StartTest(self, handler, test_dir, run_count): |
| "Sends test start page to browser." |
| |
| cache_data = ReadJSON(os.path.join(test_dir, 'cache.json')) |
| |
| # Load cached responses. |
| self.cache = {} |
| responses_dir = os.path.join(test_dir, 'responses') |
| for request in cache_data['requests']: |
| response_file = os.path.join(responses_dir, request['response_file']) |
| response = ReadFile(response_file, 'rb') |
| key = (request['method'], request['path']) |
| self.cache[key] = {'response': response, 'headers': request['headers']} |
| |
| # Load benchmark scripts. |
| self.benchmark_resources = {} |
| data = ReadFile(os.path.join(test_dir, 'data.json')) |
| data = string.Template(DATA_JS).substitute(data=data) |
| self.benchmark_resources['data.js'] = {'data': data, |
| 'type': 'application/javascript'} |
| for resource in ('common.js', 'playback.js'): |
| resource_file = os.path.join(self.script_dir, resource) |
| self.benchmark_resources[resource] = {'data': ReadFile(resource_file), |
| 'type': 'application/javascript'} |
| |
| # Format start page. |
| parse_result = urlparse.urlparse(cache_data['start_url']) |
| target_origin = '%s://%s' % (parse_result.scheme, parse_result.netloc) |
| start_page = string.Template(START_PAGE).substitute( |
| run_count=run_count, target_origin=target_origin, |
| width=cache_data['width'], height=cache_data['height']) |
| self.benchmark_resources['start_page'] = { |
| 'data': start_page, |
| 'type': 'text/html; charset=UTF-8' |
| } |
| |
| start_page_popup = string.Template(START_PAGE_POPUP).substitute( |
| start_url=cache_data['start_url'], |
| width=cache_data['width'], height=cache_data['height']) |
| self.benchmark_resources['start_page_popup'] = { |
| 'data': start_page_popup, |
| 'type': 'text/html; charset=UTF-8' |
| } |
| |
| self._GetBenchmarkResource('start_page', handler) |
| |
| def _GetBenchmarkResource(self, resource, handler): |
| "Sends requested resource to browser." |
| |
| if resource in self.benchmark_resources: |
| resource = self.benchmark_resources[resource] |
| handler.send_response(200) |
| handler.send_header('content-length', len(resource['data'])) |
| handler.send_header('content-type', resource['type']) |
| handler.end_headers() |
| handler.wfile.write(resource['data']) |
| else: |
| handler.send_response(404) |
| handler.end_headers() |
| |
| def _ProcessBenchmarkReport(self, content, handler): |
| "Reads benchmark score from report content and invokes callback." |
| |
| handler.send_response(204) |
| handler.end_headers() |
| content = json.loads(content) |
| if 'results' in content: |
| results = content['results'] |
| sys.stdout.write('Results: %s\n' % results) |
| if self.test_callback: self.test_callback(results) |
| elif 'error' in content: |
| sys.stderr.write('Error: %s\n' % content['error']) |
| |
| def _GetApplicationResource(self, handler): |
| "Searches for response in cache. If not found, responds with 204." |
| key = (handler.command, handler.path) |
| if key in self.cache: |
| sys.stdout.write('%s %s -> found\n' % key) |
| handler.wfile.write(self.cache[key]['response']) |
| else: |
| sys.stderr.write('%s %s -> not found\n' % key) |
| handler.send_response(204, "not in cache") |
| handler.end_headers() |