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 ...@@ -55,6 +55,27 @@ import webpagereplay
from youtube import YoutubeTestHelper 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): def StandardDeviation(values):
"""Returns the standard deviation of |values|.""" """Returns the standard deviation of |values|."""
avg = Mean(values) avg = Mean(values)
...@@ -1738,11 +1759,12 @@ class PopularSitesScrollTest(BaseScrollTest): ...@@ -1738,11 +1759,12 @@ class PopularSitesScrollTest(BaseScrollTest):
A list of extra flags to pass to Chrome when it is launched. A list of extra flags to pass to Chrome when it is launched.
""" """
return super(PopularSitesScrollTest, return super(PopularSitesScrollTest,
self).ExtraChromeFlags() + WebPageReplay.CHROME_FLAGS self).ExtraChromeFlags() + PageCyclerReplay.CHROME_FLAGS
def _GetUrlList(self, test_name): def _GetUrlList(self, test_name):
"""Returns list of recorded sites.""" """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() sites_text = f.read()
js = """ js = """
%s %s
...@@ -1774,7 +1796,9 @@ class PopularSitesScrollTest(BaseScrollTest): ...@@ -1774,7 +1796,9 @@ class PopularSitesScrollTest(BaseScrollTest):
test_name = '2012Q3' test_name = '2012Q3'
urls = self._GetUrlList(test_name) urls = self._GetUrlList(test_name)
results = [] 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 iteration in range(self._num_iterations):
for url in urls: for url in urls:
result = self.RunSingleInvocation(url) result = self.RunSingleInvocation(url)
...@@ -2175,7 +2199,7 @@ class PageCyclerTest(BasePageCyclerTest): ...@@ -2175,7 +2199,7 @@ class PageCyclerTest(BasePageCyclerTest):
self.RunPageCyclerTest('moz2', 'Moz2File') self.RunPageCyclerTest('moz2', 'Moz2File')
class WebPageReplay(object): class PageCyclerReplay(object):
"""Run page cycler tests with network simulation via Web Page Replay. """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 Web Page Replay is a proxy that can record and "replay" web pages with
...@@ -2183,34 +2207,21 @@ class WebPageReplay(object): ...@@ -2183,34 +2207,21 @@ class WebPageReplay(object):
by hand. With WPR, tests can use "real" web content, and catch by hand. With WPR, tests can use "real" web content, and catch
performance issues that may result from introducing network delays and performance issues that may result from introducing network delays and
bandwidth throttling. 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 = { _PATHS = {
'archive': 'src/data/page_cycler/webpagereplay/{test_name}.wpr', 'archive': 'src/data/page_cycler/webpagereplay/{test_name}.wpr',
'page_sets': 'src/tools/page_cycler/webpagereplay/tests/{test_name}.js', 'page_sets': 'src/tools/page_cycler/webpagereplay/tests/{test_name}.js',
'start_page': 'src/tools/page_cycler/webpagereplay/start.html', 'start_page': 'src/tools/page_cycler/webpagereplay/start.html',
'extension': 'src/tools/page_cycler/webpagereplay/extension', '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 = webpagereplay.CHROME_FLAGS + [
'..', '..', '..', '..'))
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,
'--log-level=0', '--log-level=0',
'--disable-background-networking', '--disable-background-networking',
'--enable-experimental-extension-apis', '--enable-experimental-extension-apis',
'--enable-logging', '--enable-logging',
'--enable-stats-table', '--enable-stats-table',
'--enable-benchmarking', '--enable-benchmarking',
'--ignore-certificate-errors',
'--metrics-recording-only', '--metrics-recording-only',
'--activate-on-launch', '--activate-on-launch',
'--no-first-run', '--no-first-run',
...@@ -2219,39 +2230,12 @@ class WebPageReplay(object): ...@@ -2219,39 +2230,12 @@ class WebPageReplay(object):
@classmethod @classmethod
def Path(cls, key, **kwargs): def Path(cls, key, **kwargs):
"""Provide paths for tests using Web Page Replay.""" return FormatChromePath(cls._PATHS[key], **kwargs)
chromium_path = cls._PATHS[key].format(**kwargs)
return os.path.join(cls._BASE_DIR, *chromium_path.split('/')) @classmethod
def ReplayServer(cls, test_name):
def _ArchivePath(self, test_name): archive_path = cls.Path('archive', test_name=test_name)
archive_path = self.archive_path or self.Path('archive', return webpagereplay.ReplayServer(archive_path)
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)
class PageCyclerNetSimTest(BasePageCyclerTest): class PageCyclerNetSimTest(BasePageCyclerTest):
...@@ -2265,13 +2249,14 @@ class PageCyclerNetSimTest(BasePageCyclerTest): ...@@ -2265,13 +2249,14 @@ class PageCyclerNetSimTest(BasePageCyclerTest):
A list of extra flags to pass to Chrome when it is launched. A list of extra flags to pass to Chrome when it is launched.
""" """
flags = super(PageCyclerNetSimTest, self).ExtraChromeFlags() flags = super(PageCyclerNetSimTest, self).ExtraChromeFlags()
flags.append('--load-extension=%s' % WebPageReplay.Path('extension')) flags.append('--load-extension=%s' % PageCyclerReplay.Path('extension'))
flags.extend(WebPageReplay.CHROME_FLAGS) flags.extend(PageCyclerReplay.CHROME_FLAGS)
return flags return flags
def StartUrl(self, test_name, iterations): def StartUrl(self, test_name, iterations):
start_path = PageCyclerReplay.Path('start_page')
start_url = 'file://%s?test=%s&iterations=%d' % ( start_url = 'file://%s?test=%s&iterations=%d' % (
WebPageReplay.Path('start_page'), test_name, iterations) start_path, test_name, iterations)
if self.use_auto: if self.use_auto:
start_url += '&auto=1' start_url += '&auto=1'
return start_url return start_url
...@@ -2283,7 +2268,9 @@ class PageCyclerNetSimTest(BasePageCyclerTest): ...@@ -2283,7 +2268,9 @@ class PageCyclerNetSimTest(BasePageCyclerTest):
test_name: name for archive (.wpr) and config (.js) files. test_name: name for archive (.wpr) and config (.js) files.
description: a string description for the test 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 = super(PageCyclerNetSimTest, self)
super_self.RunPageCyclerTest(test_name, description) super_self.RunPageCyclerTest(test_name, description)
......
...@@ -3,27 +3,37 @@ ...@@ -3,27 +3,37 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # 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 logging
import optparse
import os import os
import shutil
import signal import signal
import subprocess import subprocess
import sys
import tempfile
import time import time
import urllib 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 HTTP_PORT = 8080
HTTPS_PORT = 8413 HTTPS_PORT = 8413
REPLAY_HOST='127.0.0.1' 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): class ReplayError(Exception):
...@@ -31,7 +41,12 @@ class ReplayError(Exception): ...@@ -31,7 +41,12 @@ class ReplayError(Exception):
pass pass
class ReplayNotFoundError(ReplayError): 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): class ReplayNotStartedError(ReplayError):
pass pass
...@@ -40,48 +55,72 @@ class ReplayNotStartedError(ReplayError): ...@@ -40,48 +55,72 @@ class ReplayNotStartedError(ReplayError):
class ReplayServer(object): class ReplayServer(object):
"""Start and Stop Web Page Replay. """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: Example:
with ReplayServer(replay_dir, archive_path, log_dir, replay_options): with ReplayServer(archive_path):
self.NavigateToURL(start_url) self.NavigateToURL(start_url)
self.WaitUntil(...) 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. """Initialize ReplayServer.
Args: Args:
replay_dir: directory that has replay.py and related modules. archive_path: a path to a specific WPR archive (required).
archive_path: either a directory that contains WPR archives or,
a path to a specific WPR archive.
log_dir: where to write log.txt.
replay_options: a list of options strings to forward to replay.py. replay_options: a list of options strings to forward to replay.py.
""" replay_dir: directory that has replay.py and related modules.
self.replay_dir = replay_dir log_path: a path to a log file.
self.archive_path = archive_path """
self.log_dir = log_dir self.archive_path = os.environ.get('WPR_ARCHIVE_PATH', archive_path)
self.replay_options = replay_options if replay_options else [] 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.log_fh = None
self.replay_process = None self.replay_process = None
self.wpr_py = os.path.join(self.replay_dir, 'replay.py') def _AddDefaultReplayOptions(self):
if not os.path.exists(self.wpr_py): """Set WPR command-line options. Can be overridden if needed."""
raise ReplayNotFoundError('Path does not exist: %s' % self.wpr_py) self.replay_options += [
self.wpr_options = [
'--port', str(HTTP_PORT), '--port', str(HTTP_PORT),
'--ssl_port', str(HTTPS_PORT), '--ssl_port', str(HTTPS_PORT),
'--use_closest_match', '--use_closest_match',
# TODO(slamm): Add traffic shaping (requires root): '--no-dns_forwarding',
# '--net', 'fios', # '--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): def _OpenLogFile(self):
if not os.path.exists(self.log_dir): log_dir = os.path.dirname(self.log_path)
os.makedirs(self.log_dir) if not os.path.exists(log_dir):
return open(self.log_name, 'w') os.makedirs(log_dir)
return open(self.log_path, 'w')
def IsStarted(self): def IsStarted(self):
"""Checks to see if the server is up and running.""" """Checks to see if the server is up and running."""
...@@ -106,8 +145,8 @@ class ReplayServer(object): ...@@ -106,8 +145,8 @@ class ReplayServer(object):
Raises: Raises:
ReplayNotStartedError if Replay start-up fails. ReplayNotStartedError if Replay start-up fails.
""" """
cmd_line = [self.wpr_py] cmd_line = [self.replay_py]
cmd_line.extend(self.wpr_options) cmd_line.extend(self.replay_options)
cmd_line.append(self.archive_path) cmd_line.append(self.archive_path)
self.log_fh = self._OpenLogFile() self.log_fh = self._OpenLogFile()
logging.debug('Starting Web-Page-Replay: %s', cmd_line) logging.debug('Starting Web-Page-Replay: %s', cmd_line)
...@@ -131,6 +170,7 @@ class ReplayServer(object): ...@@ -131,6 +170,7 @@ class ReplayServer(object):
def __enter__(self): def __enter__(self):
"""Add support for with-statement.""" """Add support for with-statement."""
self.StartServer() self.StartServer()
return self
def __exit__(self, unused_exc_type, unused_exc_val, unused_exc_tb): def __exit__(self, unused_exc_type, unused_exc_val, unused_exc_tb):
"""Add support for with-statement.""" """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