Commit 3980eab3 authored by slamm@google.com's avatar slamm@google.com

Make it easy to use Web Page Replay with non-page_cycler tests.

BUG=
TEST=
NOTRY=true


Review URL: https://chromiumcodereview.appspot.com/10825025

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@148828 0039d316-1c4b-4281-b951-d872f2087c98
parent 4d47e6c7
......@@ -55,6 +55,27 @@ import webpagereplay
from youtube import YoutubeTestHelper
_CHROME_BASE_DIR = os.path.abspath(os.path.join(
os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, os.pardir))
def FormatChromePath(posix_path, **kwargs):
"""Convert a path relative to the Chromium root into an OS-specific path.
Args:
posix_path: a path string that may be a format().
Example: 'src/third_party/{module_name}/__init__.py'
kwargs: args for the format replacement.
Example: {'module_name': 'pylib'}
Returns:
an absolute path in the current Chromium tree with formatting applied.
"""
formated_path = posix_path.format(**kwargs)
path_parts = formated_path.split('/')
return os.path.join(_CHROME_BASE_DIR, *path_parts)
def StandardDeviation(values):
"""Returns the standard deviation of |values|."""
avg = Mean(values)
......@@ -1738,11 +1759,12 @@ class PopularSitesScrollTest(BaseScrollTest):
A list of extra flags to pass to Chrome when it is launched.
"""
return super(PopularSitesScrollTest,
self).ExtraChromeFlags() + WebPageReplay.CHROME_FLAGS
self).ExtraChromeFlags() + PageCyclerReplay.CHROME_FLAGS
def _GetUrlList(self, test_name):
"""Returns list of recorded sites."""
with open(WebPageReplay.Path('page_sets', test_name=test_name)) as f:
sites_path = PageCyclerReplay.Path('page_sets', test_name=test_name)
with open(sites_path) as f:
sites_text = f.read()
js = """
%s
......@@ -1774,7 +1796,9 @@ class PopularSitesScrollTest(BaseScrollTest):
test_name = '2012Q3'
urls = self._GetUrlList(test_name)
results = []
with WebPageReplay().GetReplayServer(test_name):
with PageCyclerReplay.ReplayServer(test_name) as replay_server:
if replay_server.is_record_mode:
self._num_iterations = 1
for iteration in range(self._num_iterations):
for url in urls:
result = self.RunSingleInvocation(url)
......@@ -2175,7 +2199,7 @@ class PageCyclerTest(BasePageCyclerTest):
self.RunPageCyclerTest('moz2', 'Moz2File')
class WebPageReplay(object):
class PageCyclerReplay(object):
"""Run page cycler tests with network simulation via Web Page Replay.
Web Page Replay is a proxy that can record and "replay" web pages with
......@@ -2183,34 +2207,21 @@ class WebPageReplay(object):
by hand. With WPR, tests can use "real" web content, and catch
performance issues that may result from introducing network delays and
bandwidth throttling.
Environment Variables:
WPR_RECORD: if set, puts Web Page Replay in record mode instead of replay.
WPR_REPLAY_DIR: path to alternate Web Page Replay source (for development).
WPR_ARCHIVE_PATH: path to alternate archive file (e.g. '/tmp/foo.wpr').
"""
_PATHS = {
'archive': 'src/data/page_cycler/webpagereplay/{test_name}.wpr',
'page_sets': 'src/tools/page_cycler/webpagereplay/tests/{test_name}.js',
'start_page': 'src/tools/page_cycler/webpagereplay/start.html',
'extension': 'src/tools/page_cycler/webpagereplay/extension',
'replay': 'src/third_party/webpagereplay',
'logs': 'src/webpagereplay_logs',
}
_BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__),
'..', '..', '..', '..'))
CHROME_FLAGS = [
'--host-resolver-rules=MAP * %s' % webpagereplay.REPLAY_HOST,
'--testing-fixed-http-port=%s' % webpagereplay.HTTP_PORT,
'--testing-fixed-https-port=%s' % webpagereplay.HTTPS_PORT,
CHROME_FLAGS = webpagereplay.CHROME_FLAGS + [
'--log-level=0',
'--disable-background-networking',
'--enable-experimental-extension-apis',
'--enable-logging',
'--enable-stats-table',
'--enable-benchmarking',
'--ignore-certificate-errors',
'--metrics-recording-only',
'--activate-on-launch',
'--no-first-run',
......@@ -2219,39 +2230,12 @@ class WebPageReplay(object):
@classmethod
def Path(cls, key, **kwargs):
"""Provide paths for tests using Web Page Replay."""
chromium_path = cls._PATHS[key].format(**kwargs)
return os.path.join(cls._BASE_DIR, *chromium_path.split('/'))
def _ArchivePath(self, test_name):
archive_path = self.archive_path or self.Path('archive',
test_name=test_name)
if self.is_record_mode:
archive_dir = os.path.dirname(archive_path)
assert os.path.exists(archive_dir), \
'Archive directory does not exist: %s' % archive_dir
else:
assert os.path.exists(archive_path), \
'Archive file path does not exist: %s' % archive_path
return archive_path
def __init__(self):
self.archive_path = os.environ.get('WPR_ARCHIVE_PATH')
self.replay_dir = os.environ.get('WPR_REPLAY_DIR', self.Path('replay'))
self.is_record_mode = 'WPR_RECORD' in os.environ
if self.is_record_mode:
self._num_iterations = 1
def GetReplayServer(self, test_name):
replay_options = []
replay_options.append('--no-dns_forwarding')
if self.is_record_mode:
replay_options.append('--record')
return webpagereplay.ReplayServer(
self.replay_dir,
self._ArchivePath(test_name),
self.Path('logs'),
replay_options)
return FormatChromePath(cls._PATHS[key], **kwargs)
@classmethod
def ReplayServer(cls, test_name):
archive_path = cls.Path('archive', test_name=test_name)
return webpagereplay.ReplayServer(archive_path)
class PageCyclerNetSimTest(BasePageCyclerTest):
......@@ -2265,13 +2249,14 @@ class PageCyclerNetSimTest(BasePageCyclerTest):
A list of extra flags to pass to Chrome when it is launched.
"""
flags = super(PageCyclerNetSimTest, self).ExtraChromeFlags()
flags.append('--load-extension=%s' % WebPageReplay.Path('extension'))
flags.extend(WebPageReplay.CHROME_FLAGS)
flags.append('--load-extension=%s' % PageCyclerReplay.Path('extension'))
flags.extend(PageCyclerReplay.CHROME_FLAGS)
return flags
def StartUrl(self, test_name, iterations):
start_path = PageCyclerReplay.Path('start_page')
start_url = 'file://%s?test=%s&iterations=%d' % (
WebPageReplay.Path('start_page'), test_name, iterations)
start_path, test_name, iterations)
if self.use_auto:
start_url += '&auto=1'
return start_url
......@@ -2283,7 +2268,9 @@ class PageCyclerNetSimTest(BasePageCyclerTest):
test_name: name for archive (.wpr) and config (.js) files.
description: a string description for the test
"""
with WebPageReplay().GetReplayServer(test_name):
with PageCyclerReplay.ReplayServer(test_name) as replay_server:
if replay_server.is_record_mode:
self._num_iterations = 1
super_self = super(PageCyclerNetSimTest, self)
super_self.RunPageCyclerTest(test_name, description)
......
......@@ -3,27 +3,37 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Start and stop Web Page Replay."""
"""Start and stop Web Page Replay.
Of the public module names, the following ones are key:
CHROME_FLAGS: Chrome options to make it work with Web Page Replay.
ReplayServer: a class to start/stop Web Page Replay.
"""
import logging
import optparse
import os
import shutil
import signal
import subprocess
import sys
import tempfile
import time
import urllib
USAGE = '%s [options] CHROME_EXE TEST_NAME' % os.path.basename(sys.argv[0])
USER_DATA_DIR = '{TEMP}/webpagereplay_utils-chrome'
# The port numbers must match those in chrome/test/perf/page_cycler_test.cc.
HTTP_PORT = 8080
HTTPS_PORT = 8413
REPLAY_HOST='127.0.0.1'
CHROME_FLAGS = [
'--host-resolver-rules=MAP * %s,EXCLUDE localhost' % REPLAY_HOST,
'--testing-fixed-http-port=%s' % HTTP_PORT,
'--testing-fixed-https-port=%s' % HTTPS_PORT,
'--ignore-certificate-errors',
]
_CHROME_BASE_DIR = os.path.abspath(os.path.join(
os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, os.pardir))
REPLAY_DIR = os.path.join(
_CHROME_BASE_DIR, 'src', 'third_party', 'webpagereplay')
LOG_PATH = os.path.join(
_CHROME_BASE_DIR, 'src', 'webpagereplay_logs', 'logs.txt')
class ReplayError(Exception):
......@@ -31,7 +41,12 @@ class ReplayError(Exception):
pass
class ReplayNotFoundError(ReplayError):
pass
def __init__(self, label, path):
self.args = (label, path)
def __str__(self):
label, path = self.args
return 'Path does not exist for %s: %s' % (label, path)
class ReplayNotStartedError(ReplayError):
pass
......@@ -40,48 +55,72 @@ class ReplayNotStartedError(ReplayError):
class ReplayServer(object):
"""Start and Stop Web Page Replay.
Web Page Replay is a proxy that can record and "replay" web pages with
simulated network characteristics -- without having to edit the pages
by hand. With WPR, tests can use "real" web content, and catch
performance issues that may result from introducing network delays and
bandwidth throttling.
Example:
with ReplayServer(replay_dir, archive_path, log_dir, replay_options):
with ReplayServer(archive_path):
self.NavigateToURL(start_url)
self.WaitUntil(...)
"""
LOG_FILE = 'log.txt'
def __init__(self, replay_dir, archive_path, log_dir, replay_options=None):
Environment Variables (for development):
WPR_ARCHIVE_PATH: path to alternate archive file (e.g. '/tmp/foo.wpr').
WPR_RECORD: if set, puts Web Page Replay in record mode instead of replay.
WPR_REPLAY_DIR: path to alternate Web Page Replay source.
"""
def __init__(self, archive_path, replay_options=None, replay_dir=None,
log_path=None):
"""Initialize ReplayServer.
Args:
replay_dir: directory that has replay.py and related modules.
archive_path: either a directory that contains WPR archives or,
a path to a specific WPR archive.
log_dir: where to write log.txt.
archive_path: a path to a specific WPR archive (required).
replay_options: a list of options strings to forward to replay.py.
"""
self.replay_dir = replay_dir
self.archive_path = archive_path
self.log_dir = log_dir
self.replay_options = replay_options if replay_options else []
replay_dir: directory that has replay.py and related modules.
log_path: a path to a log file.
"""
self.archive_path = os.environ.get('WPR_ARCHIVE_PATH', archive_path)
self.replay_options = replay_options or []
self.replay_dir = os.environ.get('WPR_REPLAY_DIR', replay_dir or REPLAY_DIR)
self.log_path = log_path or LOG_PATH
if 'WPR_RECORD' in os.environ and '--record' not in self.replay_options:
self.replay_options.append('--record')
self.is_record_mode = '--record' in self.replay_options
self._AddDefaultReplayOptions()
self.replay_py = os.path.join(self.replay_dir, 'replay.py')
if self.is_record_mode:
self._CheckPath('archive directory', os.path.dirname(self.archive_path))
elif not os.path.exists(self.archive_path):
self._CheckPath('archive file', self.archive_path)
self._CheckPath('replay script', self.replay_py)
self.log_name = os.path.join(self.log_dir, self.LOG_FILE)
self.log_fh = None
self.replay_process = None
self.wpr_py = os.path.join(self.replay_dir, 'replay.py')
if not os.path.exists(self.wpr_py):
raise ReplayNotFoundError('Path does not exist: %s' % self.wpr_py)
self.wpr_options = [
def _AddDefaultReplayOptions(self):
"""Set WPR command-line options. Can be overridden if needed."""
self.replay_options += [
'--port', str(HTTP_PORT),
'--ssl_port', str(HTTPS_PORT),
'--use_closest_match',
# TODO(slamm): Add traffic shaping (requires root):
# '--net', 'fios',
'--no-dns_forwarding',
# '--net', 'fios', # TODO(slamm): Add traffic shaping (requires root).
]
self.wpr_options.extend(self.replay_options)
def _CheckPath(self, label, path):
if not os.path.exists(path):
raise ReplayNotFoundError(label, path)
def _OpenLogFile(self):
if not os.path.exists(self.log_dir):
os.makedirs(self.log_dir)
return open(self.log_name, 'w')
log_dir = os.path.dirname(self.log_path)
if not os.path.exists(log_dir):
os.makedirs(log_dir)
return open(self.log_path, 'w')
def IsStarted(self):
"""Checks to see if the server is up and running."""
......@@ -106,8 +145,8 @@ class ReplayServer(object):
Raises:
ReplayNotStartedError if Replay start-up fails.
"""
cmd_line = [self.wpr_py]
cmd_line.extend(self.wpr_options)
cmd_line = [self.replay_py]
cmd_line.extend(self.replay_options)
cmd_line.append(self.archive_path)
self.log_fh = self._OpenLogFile()
logging.debug('Starting Web-Page-Replay: %s', cmd_line)
......@@ -131,6 +170,7 @@ class ReplayServer(object):
def __enter__(self):
"""Add support for with-statement."""
self.StartServer()
return self
def __exit__(self, unused_exc_type, unused_exc_val, unused_exc_tb):
"""Add support for with-statement."""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment