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