OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Start and stop Web Page Replay.""" | 6 """Start and stop Web Page Replay. |
7 | |
8 Of the public module names, the following ones are key: | |
9 CHROME_FLAGS: Chrome options to make it work with Web Page Replay. | |
10 ReplayServer: a class to start/stop Web Page Replay. | |
11 """ | |
7 | 12 |
8 import logging | 13 import logging |
9 import optparse | |
10 import os | 14 import os |
11 import shutil | |
12 import signal | 15 import signal |
13 import subprocess | 16 import subprocess |
14 import sys | |
15 import tempfile | |
16 import time | 17 import time |
17 import urllib | 18 import urllib |
18 | 19 |
19 | 20 |
20 USAGE = '%s [options] CHROME_EXE TEST_NAME' % os.path.basename(sys.argv[0]) | |
21 USER_DATA_DIR = '{TEMP}/webpagereplay_utils-chrome' | |
22 | |
23 # The port numbers must match those in chrome/test/perf/page_cycler_test.cc. | |
24 HTTP_PORT = 8080 | 21 HTTP_PORT = 8080 |
25 HTTPS_PORT = 8413 | 22 HTTPS_PORT = 8413 |
26 REPLAY_HOST='127.0.0.1' | 23 REPLAY_HOST='127.0.0.1' |
24 CHROME_FLAGS = [ | |
25 '--host-resolver-rules=MAP * %s,EXCLUDE localhost' % REPLAY_HOST, | |
26 '--testing-fixed-http-port=%s' % HTTP_PORT, | |
27 '--testing-fixed-https-port=%s' % HTTPS_PORT, | |
28 '--ignore-certificate-errors', | |
29 ] | |
30 | |
31 _BASE_DIR = os.path.abspath(os.path.join( | |
dennis_jeffrey
2012/07/27 21:46:56
maybe name it _CHROME_BASE_DIR?
slamm_google
2012/07/27 21:54:31
Done.
| |
32 os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, os.pardir)) | |
33 REPLAY_DIR = os.path.join(_BASE_DIR, 'src', 'third_party', 'webpagereplay') | |
34 LOG_PATH = os.path.join(_BASE_DIR, 'src', 'webpagereplay_logs', 'logs.txt') | |
27 | 35 |
28 | 36 |
29 class ReplayError(Exception): | 37 class ReplayError(Exception): |
30 """Catch-all exception for the module.""" | 38 """Catch-all exception for the module.""" |
31 pass | 39 pass |
32 | 40 |
33 class ReplayNotFoundError(ReplayError): | 41 class ReplayNotFoundError(ReplayError): |
34 pass | 42 def __init__(self, label, path): |
43 self.args = (label, path) | |
44 | |
45 def __str__(self): | |
46 label, path = self.args | |
47 return 'Path does not exist for %s: %s' % (label, path) | |
35 | 48 |
36 class ReplayNotStartedError(ReplayError): | 49 class ReplayNotStartedError(ReplayError): |
37 pass | 50 pass |
38 | 51 |
39 | 52 |
40 class ReplayServer(object): | 53 class ReplayServer(object): |
41 """Start and Stop Web Page Replay. | 54 """Start and Stop Web Page Replay. |
42 | 55 |
56 Web Page Replay is a proxy that can record and "replay" web pages with | |
57 simulated network characteristics -- without having to edit the pages | |
58 by hand. With WPR, tests can use "real" web content, and catch | |
59 performance issues that may result from introducing network delays and | |
60 bandwidth throttling. | |
61 | |
43 Example: | 62 Example: |
44 with ReplayServer(replay_dir, archive_path, log_dir, replay_options): | 63 with ReplayServer(archive_path): |
45 self.NavigateToURL(start_url) | 64 self.NavigateToURL(start_url) |
46 self.WaitUntil(...) | 65 self.WaitUntil(...) |
66 | |
67 Environment Variables (for development): | |
68 WPR_ARCHIVE_PATH: path to alternate archive file (e.g. '/tmp/foo.wpr'). | |
69 WPR_RECORD: if set, puts Web Page Replay in record mode instead of replay. | |
70 WPR_REPLAY_DIR: path to alternate Web Page Replay source. | |
47 """ | 71 """ |
48 LOG_FILE = 'log.txt' | 72 def __init__(self, archive_path, replay_options=None, replay_dir=None, |
49 | 73 log_path=None): |
50 def __init__(self, replay_dir, archive_path, log_dir, replay_options=None): | |
51 """Initialize ReplayServer. | 74 """Initialize ReplayServer. |
52 | 75 |
53 Args: | 76 Args: |
77 archive_path: a path to a specific WPR archive (required). | |
78 replay_options: a list of options strings to forward to replay.py. | |
54 replay_dir: directory that has replay.py and related modules. | 79 replay_dir: directory that has replay.py and related modules. |
55 archive_path: either a directory that contains WPR archives or, | 80 log_path: a path to a log file. |
56 a path to a specific WPR archive. | 81 """ |
57 log_dir: where to write log.txt. | 82 self.archive_path = os.environ.get('WPR_ARCHIVE_PATH', archive_path) |
58 replay_options: a list of options strings to forward to replay.py. | 83 self.replay_options = replay_options or [] |
59 """ | 84 self.replay_dir = os.environ.get('WPR_REPLAY_DIR', replay_dir or REPLAY_DIR) |
60 self.replay_dir = replay_dir | 85 self.log_path = log_path or LOG_PATH |
61 self.archive_path = archive_path | |
62 self.log_dir = log_dir | |
63 self.replay_options = replay_options if replay_options else [] | |
64 | 86 |
65 self.log_name = os.path.join(self.log_dir, self.LOG_FILE) | 87 if 'WPR_RECORD' in os.environ and '--record' not in self.replay_options: |
88 self.replay_options.append('--record') | |
89 self.is_record_mode = '--record' in self.replay_options | |
90 self._AddDefaultReplayOptions() | |
91 | |
92 self.replay_py = os.path.join(self.replay_dir, 'replay.py') | |
93 | |
94 if self.is_record_mode: | |
95 self._CheckPath('archive directory', os.path.dirname(self.archive_path)) | |
96 elif not os.path.exists(self.archive_path): | |
97 self._CheckPath('archive file', self.archive_path) | |
98 self._CheckPath('replay script', self.replay_py) | |
99 | |
66 self.log_fh = None | 100 self.log_fh = None |
67 self.replay_process = None | 101 self.replay_process = None |
68 | 102 |
69 self.wpr_py = os.path.join(self.replay_dir, 'replay.py') | 103 def _AddDefaultReplayOptions(self): |
70 if not os.path.exists(self.wpr_py): | 104 """Set WPR command-line options. Can be overridden if needed.""" |
71 raise ReplayNotFoundError('Path does not exist: %s' % self.wpr_py) | 105 self.replay_options += [ |
72 self.wpr_options = [ | |
73 '--port', str(HTTP_PORT), | 106 '--port', str(HTTP_PORT), |
74 '--ssl_port', str(HTTPS_PORT), | 107 '--ssl_port', str(HTTPS_PORT), |
75 '--use_closest_match', | 108 '--use_closest_match', |
76 # TODO(slamm): Add traffic shaping (requires root): | 109 '--no-dns_forwarding', |
77 # '--net', 'fios', | 110 # '--net', 'fios', # TODO(slamm): Add traffic shaping (requires root). |
78 ] | 111 ] |
79 self.wpr_options.extend(self.replay_options) | 112 |
113 def _CheckPath(self, label, path): | |
114 if not os.path.exists(path): | |
115 raise ReplayNotFoundError(label, path) | |
80 | 116 |
81 def _OpenLogFile(self): | 117 def _OpenLogFile(self): |
82 if not os.path.exists(self.log_dir): | 118 log_dir = os.path.dirname(self.log_path) |
83 os.makedirs(self.log_dir) | 119 if not os.path.exists(log_dir): |
84 return open(self.log_name, 'w') | 120 os.makedirs(log_dir) |
121 return open(self.log_path, 'w') | |
85 | 122 |
86 def IsStarted(self): | 123 def IsStarted(self): |
87 """Checks to see if the server is up and running.""" | 124 """Checks to see if the server is up and running.""" |
88 for _ in range(5): | 125 for _ in range(5): |
89 if self.replay_process.poll() is not None: | 126 if self.replay_process.poll() is not None: |
90 # The process has exited. | 127 # The process has exited. |
91 break | 128 break |
92 try: | 129 try: |
93 up_url = '%s://localhost:%s/web-page-replay-generate-200' | 130 up_url = '%s://localhost:%s/web-page-replay-generate-200' |
94 http_up_url = up_url % ('http', HTTP_PORT) | 131 http_up_url = up_url % ('http', HTTP_PORT) |
95 https_up_url = up_url % ('https', HTTPS_PORT) | 132 https_up_url = up_url % ('https', HTTPS_PORT) |
96 if (200 == urllib.urlopen(http_up_url, None, {}).getcode() and | 133 if (200 == urllib.urlopen(http_up_url, None, {}).getcode() and |
97 200 == urllib.urlopen(https_up_url, None, {}).getcode()): | 134 200 == urllib.urlopen(https_up_url, None, {}).getcode()): |
98 return True | 135 return True |
99 except IOError: | 136 except IOError: |
100 time.sleep(1) | 137 time.sleep(1) |
101 return False | 138 return False |
102 | 139 |
103 def StartServer(self): | 140 def StartServer(self): |
104 """Start Web Page Replay and verify that it started. | 141 """Start Web Page Replay and verify that it started. |
105 | 142 |
106 Raises: | 143 Raises: |
107 ReplayNotStartedError if Replay start-up fails. | 144 ReplayNotStartedError if Replay start-up fails. |
108 """ | 145 """ |
109 cmd_line = [self.wpr_py] | 146 cmd_line = [self.replay_py] |
110 cmd_line.extend(self.wpr_options) | 147 cmd_line.extend(self.replay_options) |
111 cmd_line.append(self.archive_path) | 148 cmd_line.append(self.archive_path) |
112 self.log_fh = self._OpenLogFile() | 149 self.log_fh = self._OpenLogFile() |
113 logging.debug('Starting Web-Page-Replay: %s', cmd_line) | 150 logging.debug('Starting Web-Page-Replay: %s', cmd_line) |
114 self.replay_process = subprocess.Popen( | 151 self.replay_process = subprocess.Popen( |
115 cmd_line, stdout=self.log_fh, stderr=subprocess.STDOUT) | 152 cmd_line, stdout=self.log_fh, stderr=subprocess.STDOUT) |
116 if not self.IsStarted(): | 153 if not self.IsStarted(): |
117 raise ReplayNotStartedError( | 154 raise ReplayNotStartedError( |
118 'Web Page Replay failed to start. See the log file: ' + self.log_name) | 155 'Web Page Replay failed to start. See the log file: ' + self.log_name) |
119 | 156 |
120 def StopServer(self): | 157 def StopServer(self): |
121 """Stop Web Page Replay.""" | 158 """Stop Web Page Replay.""" |
122 if self.replay_process: | 159 if self.replay_process: |
123 logging.debug('Stopping Web-Page-Replay') | 160 logging.debug('Stopping Web-Page-Replay') |
124 # Use a SIGINT here so that it can do graceful cleanup. | 161 # Use a SIGINT here so that it can do graceful cleanup. |
125 # Otherwise, we will leave subprocesses hanging. | 162 # Otherwise, we will leave subprocesses hanging. |
126 self.replay_process.send_signal(signal.SIGINT) | 163 self.replay_process.send_signal(signal.SIGINT) |
127 self.replay_process.wait() | 164 self.replay_process.wait() |
128 if self.log_fh: | 165 if self.log_fh: |
129 self.log_fh.close() | 166 self.log_fh.close() |
130 | 167 |
131 def __enter__(self): | 168 def __enter__(self): |
132 """Add support for with-statement.""" | 169 """Add support for with-statement.""" |
133 self.StartServer() | 170 self.StartServer() |
171 return self | |
134 | 172 |
135 def __exit__(self, unused_exc_type, unused_exc_val, unused_exc_tb): | 173 def __exit__(self, unused_exc_type, unused_exc_val, unused_exc_tb): |
136 """Add support for with-statement.""" | 174 """Add support for with-statement.""" |
137 self.StopServer() | 175 self.StopServer() |
OLD | NEW |