OLD | NEW |
1 # Copyright (C) 2011 Google Inc. All rights reserved. | 1 # Copyright (C) 2011 Google Inc. All rights reserved. |
2 # | 2 # |
3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
5 # met: | 5 # met: |
6 # | 6 # |
7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
10 # copyright notice, this list of conditions and the following disclaimer | 10 # copyright notice, this list of conditions and the following disclaimer |
(...skipping 28 matching lines...) Expand all Loading... |
39 from webkitpy.common.system.profiler import ProfilerFactory | 39 from webkitpy.common.system.profiler import ProfilerFactory |
40 | 40 |
41 | 41 |
42 _log = logging.getLogger(__name__) | 42 _log = logging.getLogger(__name__) |
43 | 43 |
44 | 44 |
45 DRIVER_START_TIMEOUT_SECS = 30 | 45 DRIVER_START_TIMEOUT_SECS = 30 |
46 | 46 |
47 | 47 |
48 class DriverInput(object): | 48 class DriverInput(object): |
| 49 |
49 def __init__(self, test_name, timeout, image_hash, should_run_pixel_test, ar
gs): | 50 def __init__(self, test_name, timeout, image_hash, should_run_pixel_test, ar
gs): |
50 self.test_name = test_name | 51 self.test_name = test_name |
51 self.timeout = timeout # in ms | 52 self.timeout = timeout # in ms |
52 self.image_hash = image_hash | 53 self.image_hash = image_hash |
53 self.should_run_pixel_test = should_run_pixel_test | 54 self.should_run_pixel_test = should_run_pixel_test |
54 self.args = args | 55 self.args = args |
55 | 56 |
56 | 57 |
57 class DriverOutput(object): | 58 class DriverOutput(object): |
| 59 |
58 """Groups information about a output from driver for easy passing | 60 """Groups information about a output from driver for easy passing |
59 and post-processing of data.""" | 61 and post-processing of data.""" |
60 | 62 |
61 def __init__(self, text, image, image_hash, audio, crash=False, | 63 def __init__(self, text, image, image_hash, audio, crash=False, |
62 test_time=0, measurements=None, timeout=False, error='', crashed_pro
cess_name='??', | 64 test_time=0, measurements=None, timeout=False, error='', crashe
d_process_name='??', |
63 crashed_pid=None, crash_log=None, leak=False, leak_log=None, pid=Non
e): | 65 crashed_pid=None, crash_log=None, leak=False, leak_log=None, pi
d=None): |
64 # FIXME: Args could be renamed to better clarify what they do. | 66 # FIXME: Args could be renamed to better clarify what they do. |
65 self.text = text | 67 self.text = text |
66 self.image = image # May be empty-string if the test crashes. | 68 self.image = image # May be empty-string if the test crashes. |
67 self.image_hash = image_hash | 69 self.image_hash = image_hash |
68 self.image_diff = None # image_diff gets filled in after construction. | 70 self.image_diff = None # image_diff gets filled in after construction. |
69 self.audio = audio # Binary format is port-dependent. | 71 self.audio = audio # Binary format is port-dependent. |
70 self.crash = crash | 72 self.crash = crash |
71 self.crashed_process_name = crashed_process_name | 73 self.crashed_process_name = crashed_process_name |
72 self.crashed_pid = crashed_pid | 74 self.crashed_pid = crashed_pid |
73 self.crash_log = crash_log | 75 self.crash_log = crash_log |
74 self.leak = leak | 76 self.leak = leak |
75 self.leak_log = leak_log | 77 self.leak_log = leak_log |
76 self.test_time = test_time | 78 self.test_time = test_time |
77 self.measurements = measurements | 79 self.measurements = measurements |
78 self.timeout = timeout | 80 self.timeout = timeout |
79 self.error = error # stderr output | 81 self.error = error # stderr output |
80 self.pid = pid | 82 self.pid = pid |
81 | 83 |
82 def has_stderr(self): | 84 def has_stderr(self): |
83 return bool(self.error) | 85 return bool(self.error) |
84 | 86 |
85 | 87 |
86 class DeviceFailure(Exception): | 88 class DeviceFailure(Exception): |
87 pass | 89 pass |
88 | 90 |
89 | 91 |
90 class Driver(object): | 92 class Driver(object): |
| 93 |
91 """object for running test(s) using content_shell or other driver.""" | 94 """object for running test(s) using content_shell or other driver.""" |
92 | 95 |
93 def __init__(self, port, worker_number, pixel_tests, no_timeout=False): | 96 def __init__(self, port, worker_number, pixel_tests, no_timeout=False): |
94 """Initialize a Driver to subsequently run tests. | 97 """Initialize a Driver to subsequently run tests. |
95 | 98 |
96 Typically this routine will spawn content_shell in a config | 99 Typically this routine will spawn content_shell in a config |
97 ready for subsequent input. | 100 ready for subsequent input. |
98 | 101 |
99 port - reference back to the port object. | 102 port - reference back to the port object. |
100 worker_number - identifier for a particular worker/driver instance | 103 worker_number - identifier for a particular worker/driver instance |
(...skipping 22 matching lines...) Expand all Loading... |
123 # stderr reading is scoped on a per-test (not per-block) basis, so we st
ore the accumulated | 126 # stderr reading is scoped on a per-test (not per-block) basis, so we st
ore the accumulated |
124 # stderr output, as well as if we've seen #EOF on this driver instance. | 127 # stderr output, as well as if we've seen #EOF on this driver instance. |
125 # FIXME: We should probably remove _read_first_block and _read_optional_
image_block and | 128 # FIXME: We should probably remove _read_first_block and _read_optional_
image_block and |
126 # instead scope these locally in run_test. | 129 # instead scope these locally in run_test. |
127 self.error_from_test = str() | 130 self.error_from_test = str() |
128 self.err_seen_eof = False | 131 self.err_seen_eof = False |
129 self._server_process = None | 132 self._server_process = None |
130 self._current_cmd_line = None | 133 self._current_cmd_line = None |
131 | 134 |
132 self._measurements = {} | 135 self._measurements = {} |
133 if self._port.get_option("profile"): | 136 if self._port.get_option('profile'): |
134 profiler_name = self._port.get_option("profiler") | 137 profiler_name = self._port.get_option('profiler') |
135 self._profiler = ProfilerFactory.create_profiler(self._port.host, | 138 self._profiler = ProfilerFactory.create_profiler(self._port.host, |
136 self._port._path_to_driver(), self._port.results_directory(), pr
ofiler_name) | 139 self._port._path_to
_driver(), self._port.results_directory(), profiler_name) |
137 else: | 140 else: |
138 self._profiler = None | 141 self._profiler = None |
139 | 142 |
140 def __del__(self): | 143 def __del__(self): |
141 self.stop() | 144 self.stop() |
142 | 145 |
143 def run_test(self, driver_input, stop_when_done): | 146 def run_test(self, driver_input, stop_when_done): |
144 """Run a single test and return the results. | 147 """Run a single test and return the results. |
145 | 148 |
146 Note that it is okay if a test times out or crashes and leaves | 149 Note that it is okay if a test times out or crashes and leaves |
(...skipping 17 matching lines...) Expand all Loading... |
164 image, actual_image_hash = self._read_optional_image_block(deadline) #
The second (optional) block is image data. | 167 image, actual_image_hash = self._read_optional_image_block(deadline) #
The second (optional) block is image data. |
165 | 168 |
166 crashed = self.has_crashed() | 169 crashed = self.has_crashed() |
167 timed_out = self._server_process.timed_out | 170 timed_out = self._server_process.timed_out |
168 pid = self._server_process.pid() | 171 pid = self._server_process.pid() |
169 leaked = self._leaked | 172 leaked = self._leaked |
170 | 173 |
171 if not crashed: | 174 if not crashed: |
172 sanitizer = self._port._output_contains_sanitizer_messages(self.erro
r_from_test) | 175 sanitizer = self._port._output_contains_sanitizer_messages(self.erro
r_from_test) |
173 if sanitizer: | 176 if sanitizer: |
174 self.error_from_test = 'OUTPUT CONTAINS "' + sanitizer + '", so
we are treating this test as if it crashed, even though it did not.\n\n' + self.
error_from_test | 177 self.error_from_test = 'OUTPUT CONTAINS "' + sanitizer + \ |
| 178 '", so we are treating this test as if it crashed, even thou
gh it did not.\n\n' + self.error_from_test |
175 crashed = True | 179 crashed = True |
176 self._crashed_process_name = "unknown process name" | 180 self._crashed_process_name = 'unknown process name' |
177 self._crashed_pid = 0 | 181 self._crashed_pid = 0 |
178 | 182 |
179 if stop_when_done or crashed or timed_out or leaked: | 183 if stop_when_done or crashed or timed_out or leaked: |
180 # We call stop() even if we crashed or timed out in order to get any
remaining stdout/stderr output. | 184 # We call stop() even if we crashed or timed out in order to get any
remaining stdout/stderr output. |
181 # In the timeout case, we kill the hung process as well. | 185 # In the timeout case, we kill the hung process as well. |
182 out, err = self._server_process.stop(self._port.driver_stop_timeout(
) if stop_when_done else 0.0) | 186 out, err = self._server_process.stop(self._port.driver_stop_timeout(
) if stop_when_done else 0.0) |
183 if out: | 187 if out: |
184 text += out | 188 text += out |
185 if err: | 189 if err: |
186 self.error_from_test += err | 190 self.error_from_test += err |
187 self._server_process = None | 191 self._server_process = None |
188 | 192 |
189 crash_log = None | 193 crash_log = None |
190 if crashed: | 194 if crashed: |
191 self.error_from_test, crash_log = self._get_crash_log(text, self.err
or_from_test, newer_than=start_time) | 195 self.error_from_test, crash_log = self._get_crash_log(text, self.err
or_from_test, newer_than=start_time) |
192 | 196 |
193 # If we don't find a crash log use a placeholder error message inste
ad. | 197 # If we don't find a crash log use a placeholder error message inste
ad. |
194 if not crash_log: | 198 if not crash_log: |
195 pid_str = str(self._crashed_pid) if self._crashed_pid else "unkn
own pid" | 199 pid_str = str(self._crashed_pid) if self._crashed_pid else 'unkn
own pid' |
196 crash_log = 'No crash log found for %s:%s.\n' % (self._crashed_p
rocess_name, pid_str) | 200 crash_log = 'No crash log found for %s:%s.\n' % (self._crashed_p
rocess_name, pid_str) |
197 # If we were unresponsive append a message informing there may n
ot have been a crash. | 201 # If we were unresponsive append a message informing there may n
ot have been a crash. |
198 if self._subprocess_was_unresponsive: | 202 if self._subprocess_was_unresponsive: |
199 crash_log += 'Process failed to become responsive before tim
ing out.\n' | 203 crash_log += 'Process failed to become responsive before tim
ing out.\n' |
200 | 204 |
201 # Print stdout and stderr to the placeholder crash log; we want
as much context as possible. | 205 # Print stdout and stderr to the placeholder crash log; we want
as much context as possible. |
202 if self.error_from_test: | 206 if self.error_from_test: |
203 crash_log += '\nstdout:\n%s\nstderr:\n%s\n' % (text, self.er
ror_from_test) | 207 crash_log += '\nstdout:\n%s\nstderr:\n%s\n' % (text, self.er
ror_from_test) |
204 | 208 |
205 return DriverOutput(text, image, actual_image_hash, audio, | 209 return DriverOutput(text, image, actual_image_hash, audio, |
206 crash=crashed, test_time=time.time() - test_begin_time, measurements
=self._measurements, | 210 crash=crashed, test_time=time.time() - test_begin_ti
me, measurements=self._measurements, |
207 timeout=timed_out, error=self.error_from_test, | 211 timeout=timed_out, error=self.error_from_test, |
208 crashed_process_name=self._crashed_process_name, | 212 crashed_process_name=self._crashed_process_name, |
209 crashed_pid=self._crashed_pid, crash_log=crash_log, | 213 crashed_pid=self._crashed_pid, crash_log=crash_log, |
210 leak=leaked, leak_log=self._leak_log, | 214 leak=leaked, leak_log=self._leak_log, |
211 pid=pid) | 215 pid=pid) |
212 | 216 |
213 def _get_crash_log(self, stdout, stderr, newer_than): | 217 def _get_crash_log(self, stdout, stderr, newer_than): |
214 return self._port._get_crash_log(self._crashed_process_name, self._crash
ed_pid, stdout, stderr, newer_than) | 218 return self._port._get_crash_log(self._crashed_process_name, self._crash
ed_pid, stdout, stderr, newer_than) |
215 | 219 |
216 # FIXME: Seems this could just be inlined into callers. | 220 # FIXME: Seems this could just be inlined into callers. |
217 @classmethod | 221 @classmethod |
218 def _command_wrapper(cls, wrapper_option): | 222 def _command_wrapper(cls, wrapper_option): |
219 # Hook for injecting valgrind or other runtime instrumentation, | 223 # Hook for injecting valgrind or other runtime instrumentation, |
220 # used by e.g. tools/valgrind/valgrind_tests.py. | 224 # used by e.g. tools/valgrind/valgrind_tests.py. |
221 return shlex.split(wrapper_option) if wrapper_option else [] | 225 return shlex.split(wrapper_option) if wrapper_option else [] |
222 | 226 |
223 HTTP_DIR = "http/tests/" | 227 HTTP_DIR = 'http/tests/' |
224 HTTP_LOCAL_DIR = "http/tests/local/" | 228 HTTP_LOCAL_DIR = 'http/tests/local/' |
225 | 229 |
226 def is_http_test(self, test_name): | 230 def is_http_test(self, test_name): |
227 return test_name.startswith(self.HTTP_DIR) and not test_name.startswith(
self.HTTP_LOCAL_DIR) | 231 return test_name.startswith(self.HTTP_DIR) and not test_name.startswith(
self.HTTP_LOCAL_DIR) |
228 | 232 |
229 def test_to_uri(self, test_name): | 233 def test_to_uri(self, test_name): |
230 """Convert a test name to a URI. | 234 """Convert a test name to a URI. |
231 | 235 |
232 Tests which have an 'https' directory in their paths (e.g. | 236 Tests which have an 'https' directory in their paths (e.g. |
233 '/http/tests/security/mixedContent/https/test1.html') will be loaded | 237 '/http/tests/security/mixedContent/https/test1.html') will be loaded |
234 over HTTPS; all other tests over HTTP. | 238 over HTTPS; all other tests over HTTP. |
235 """ | 239 """ |
236 if not self.is_http_test(test_name): | 240 if not self.is_http_test(test_name): |
237 return path.abspath_to_uri(self._port.host.platform, self._port.absp
ath_for_test(test_name)) | 241 return path.abspath_to_uri(self._port.host.platform, self._port.absp
ath_for_test(test_name)) |
238 | 242 |
239 relative_path = test_name[len(self.HTTP_DIR):] | 243 relative_path = test_name[len(self.HTTP_DIR):] |
240 | 244 |
241 if "/https/" in test_name: | 245 if '/https/' in test_name: |
242 return "https://127.0.0.1:8443/" + relative_path | 246 return 'https://127.0.0.1:8443/' + relative_path |
243 return "http://127.0.0.1:8000/" + relative_path | 247 return 'http://127.0.0.1:8000/' + relative_path |
244 | 248 |
245 def uri_to_test(self, uri): | 249 def uri_to_test(self, uri): |
246 """Return the base layout test name for a given URI. | 250 """Return the base layout test name for a given URI. |
247 | 251 |
248 This returns the test name for a given URI, e.g., if you passed in | 252 This returns the test name for a given URI, e.g., if you passed in |
249 "file:///src/LayoutTests/fast/html/keygen.html" it would return | 253 "file:///src/LayoutTests/fast/html/keygen.html" it would return |
250 "fast/html/keygen.html". | 254 "fast/html/keygen.html". |
251 | 255 |
252 """ | 256 """ |
253 if uri.startswith("file:///"): | 257 if uri.startswith('file:///'): |
254 prefix = path.abspath_to_uri(self._port.host.platform, self._port.la
yout_tests_dir()) | 258 prefix = path.abspath_to_uri(self._port.host.platform, self._port.la
yout_tests_dir()) |
255 if not prefix.endswith('/'): | 259 if not prefix.endswith('/'): |
256 prefix += '/' | 260 prefix += '/' |
257 return uri[len(prefix):] | 261 return uri[len(prefix):] |
258 if uri.startswith("http://"): | 262 if uri.startswith('http://'): |
259 return uri.replace('http://127.0.0.1:8000/', self.HTTP_DIR) | 263 return uri.replace('http://127.0.0.1:8000/', self.HTTP_DIR) |
260 if uri.startswith("https://"): | 264 if uri.startswith('https://'): |
261 return uri.replace('https://127.0.0.1:8443/', self.HTTP_DIR) | 265 return uri.replace('https://127.0.0.1:8443/', self.HTTP_DIR) |
262 raise NotImplementedError('unknown url type: %s' % uri) | 266 raise NotImplementedError('unknown url type: %s' % uri) |
263 | 267 |
264 def has_crashed(self): | 268 def has_crashed(self): |
265 if self._server_process is None: | 269 if self._server_process is None: |
266 return False | 270 return False |
267 if self._crashed_process_name: | 271 if self._crashed_process_name: |
268 return True | 272 return True |
269 if self._server_process.has_crashed(): | 273 if self._server_process.has_crashed(): |
270 self._crashed_process_name = self._server_process.name() | 274 self._crashed_process_name = self._server_process.name() |
(...skipping 16 matching lines...) Expand all Loading... |
287 self.stop() | 291 self.stop() |
288 self._driver_tempdir = self._port._filesystem.mkdtemp(prefix='%s-' % sel
f._port.driver_name()) | 292 self._driver_tempdir = self._port._filesystem.mkdtemp(prefix='%s-' % sel
f._port.driver_name()) |
289 server_name = self._port.driver_name() | 293 server_name = self._port.driver_name() |
290 environment = self._port.setup_environ_for_server(server_name) | 294 environment = self._port.setup_environ_for_server(server_name) |
291 environment = self._setup_environ_for_driver(environment) | 295 environment = self._setup_environ_for_driver(environment) |
292 self._crashed_process_name = None | 296 self._crashed_process_name = None |
293 self._crashed_pid = None | 297 self._crashed_pid = None |
294 self._leaked = False | 298 self._leaked = False |
295 self._leak_log = None | 299 self._leak_log = None |
296 cmd_line = self.cmd_line(pixel_tests, per_test_args) | 300 cmd_line = self.cmd_line(pixel_tests, per_test_args) |
297 self._server_process = self._port._server_process_constructor(self._port
, server_name, cmd_line, environment, logging=self._port.get_option("driver_logg
ing")) | 301 self._server_process = self._port._server_process_constructor( |
| 302 self._port, |
| 303 server_name, |
| 304 cmd_line, |
| 305 environment, |
| 306 logging=self._port.get_option('driver_logging')) |
298 self._server_process.start() | 307 self._server_process.start() |
299 self._current_cmd_line = cmd_line | 308 self._current_cmd_line = cmd_line |
300 | 309 |
301 if wait_for_ready: | 310 if wait_for_ready: |
302 deadline = time.time() + DRIVER_START_TIMEOUT_SECS | 311 deadline = time.time() + DRIVER_START_TIMEOUT_SECS |
303 if not self._wait_for_server_process_output(self._server_process, de
adline, '#READY'): | 312 if not self._wait_for_server_process_output(self._server_process, de
adline, '#READY'): |
304 _log.error("content_shell took too long to startup.") | 313 _log.error('content_shell took too long to startup.') |
305 | 314 |
306 def _wait_for_server_process_output(self, server_process, deadline, text): | 315 def _wait_for_server_process_output(self, server_process, deadline, text): |
307 output = '' | 316 output = '' |
308 line = server_process.read_stdout_line(deadline) | 317 line = server_process.read_stdout_line(deadline) |
309 while not server_process.timed_out and not server_process.has_crashed()
and not text in line.rstrip(): | 318 while not server_process.timed_out and not server_process.has_crashed()
and not text in line.rstrip(): |
310 output += line | 319 output += line |
311 line = server_process.read_stdout_line(deadline) | 320 line = server_process.read_stdout_line(deadline) |
312 | 321 |
313 if server_process.timed_out or server_process.has_crashed(): | 322 if server_process.timed_out or server_process.has_crashed(): |
314 _log.error('Failed to start the %s process: \n%s' % (server_process.
name(), output)) | 323 _log.error('Failed to start the %s process: \n%s' % (server_process.
name(), output)) |
(...skipping 30 matching lines...) Expand all Loading... |
345 cmd.append('--no-timeout') | 354 cmd.append('--no-timeout') |
346 cmd.extend(self._port.get_option('additional_drt_flag', [])) | 355 cmd.extend(self._port.get_option('additional_drt_flag', [])) |
347 cmd.extend(self._port.additional_drt_flag()) | 356 cmd.extend(self._port.additional_drt_flag()) |
348 if self._port.get_option('enable_leak_detection'): | 357 if self._port.get_option('enable_leak_detection'): |
349 cmd.append('--enable-leak-detection') | 358 cmd.append('--enable-leak-detection') |
350 cmd.extend(per_test_args) | 359 cmd.extend(per_test_args) |
351 cmd.append('-') | 360 cmd.append('-') |
352 return cmd | 361 return cmd |
353 | 362 |
354 def _check_for_driver_crash(self, error_line): | 363 def _check_for_driver_crash(self, error_line): |
355 if error_line == "#CRASHED\n": | 364 if error_line == '#CRASHED\n': |
356 # This is used on Windows to report that the process has crashed | 365 # This is used on Windows to report that the process has crashed |
357 # See http://trac.webkit.org/changeset/65537. | 366 # See http://trac.webkit.org/changeset/65537. |
358 self._crashed_process_name = self._server_process.name() | 367 self._crashed_process_name = self._server_process.name() |
359 self._crashed_pid = self._server_process.pid() | 368 self._crashed_pid = self._server_process.pid() |
360 elif (error_line.startswith("#CRASHED - ") | 369 elif (error_line.startswith('#CRASHED - ') |
361 or error_line.startswith("#PROCESS UNRESPONSIVE - ")): | 370 or error_line.startswith('#PROCESS UNRESPONSIVE - ')): |
362 # WebKitTestRunner uses this to report that the WebProcess subproces
s crashed. | 371 # WebKitTestRunner uses this to report that the WebProcess subproces
s crashed. |
363 match = re.match('#(?:CRASHED|PROCESS UNRESPONSIVE) - (\S+)', error_
line) | 372 match = re.match('#(?:CRASHED|PROCESS UNRESPONSIVE) - (\S+)', error_
line) |
364 self._crashed_process_name = match.group(1) if match else 'WebProces
s' | 373 self._crashed_process_name = match.group(1) if match else 'WebProces
s' |
365 match = re.search('pid (\d+)', error_line) | 374 match = re.search('pid (\d+)', error_line) |
366 pid = int(match.group(1)) if match else None | 375 pid = int(match.group(1)) if match else None |
367 self._crashed_pid = pid | 376 self._crashed_pid = pid |
368 # FIXME: delete this after we're sure this code is working :) | 377 # FIXME: delete this after we're sure this code is working :) |
369 _log.debug('%s crash, pid = %s, error_line = %s' % (self._crashed_pr
ocess_name, str(pid), error_line)) | 378 _log.debug('%s crash, pid = %s, error_line = %s' % (self._crashed_pr
ocess_name, str(pid), error_line)) |
370 if error_line.startswith("#PROCESS UNRESPONSIVE - "): | 379 if error_line.startswith('#PROCESS UNRESPONSIVE - '): |
371 self._subprocess_was_unresponsive = True | 380 self._subprocess_was_unresponsive = True |
372 self._port.sample_process(self._crashed_process_name, self._cras
hed_pid) | 381 self._port.sample_process(self._crashed_process_name, self._cras
hed_pid) |
373 # We want to show this since it's not a regular crash and probab
ly we don't have a crash log. | 382 # We want to show this since it's not a regular crash and probab
ly we don't have a crash log. |
374 self.error_from_test += error_line | 383 self.error_from_test += error_line |
375 return True | 384 return True |
376 return self.has_crashed() | 385 return self.has_crashed() |
377 | 386 |
378 def _check_for_leak(self, error_line): | 387 def _check_for_leak(self, error_line): |
379 if error_line.startswith("#LEAK - "): | 388 if error_line.startswith('#LEAK - '): |
380 self._leaked = True | 389 self._leaked = True |
381 match = re.match('#LEAK - (\S+) pid (\d+) (.+)\n', error_line) | 390 match = re.match('#LEAK - (\S+) pid (\d+) (.+)\n', error_line) |
382 self._leak_log = match.group(3) | 391 self._leak_log = match.group(3) |
383 return self._leaked | 392 return self._leaked |
384 | 393 |
385 def _command_from_driver_input(self, driver_input): | 394 def _command_from_driver_input(self, driver_input): |
386 # FIXME: performance tests pass in full URLs instead of test names. | 395 # FIXME: performance tests pass in full URLs instead of test names. |
387 if driver_input.test_name.startswith('http://') or driver_input.test_nam
e.startswith('https://') or driver_input.test_name == ('about:blank'): | 396 if driver_input.test_name.startswith( |
| 397 'http://') or driver_input.test_name.startswith('https://') or d
river_input.test_name == ('about:blank'): |
388 command = driver_input.test_name | 398 command = driver_input.test_name |
389 elif self.is_http_test(driver_input.test_name): | 399 elif self.is_http_test(driver_input.test_name): |
390 command = self.test_to_uri(driver_input.test_name) | 400 command = self.test_to_uri(driver_input.test_name) |
391 else: | 401 else: |
392 command = self._port.abspath_for_test(driver_input.test_name) | 402 command = self._port.abspath_for_test(driver_input.test_name) |
393 if sys.platform == 'cygwin': | 403 if sys.platform == 'cygwin': |
394 command = path.cygpath(command) | 404 command = path.cygpath(command) |
395 | 405 |
396 assert not driver_input.image_hash or driver_input.should_run_pixel_test | 406 assert not driver_input.image_hash or driver_input.should_run_pixel_test |
397 | 407 |
398 # ' is the separator between arguments. | 408 # ' is the separator between arguments. |
399 if self._port.supports_per_test_timeout(): | 409 if self._port.supports_per_test_timeout(): |
400 command += "'--timeout'%s" % driver_input.timeout | 410 command += "'--timeout'%s" % driver_input.timeout |
401 if driver_input.should_run_pixel_test: | 411 if driver_input.should_run_pixel_test: |
402 command += "'--pixel-test" | 412 command += "'--pixel-test" |
403 if driver_input.image_hash: | 413 if driver_input.image_hash: |
404 command += "'" + driver_input.image_hash | 414 command += "'" + driver_input.image_hash |
405 return command + "\n" | 415 return command + '\n' |
406 | 416 |
407 def _read_first_block(self, deadline): | 417 def _read_first_block(self, deadline): |
408 # returns (text_content, audio_content) | 418 # returns (text_content, audio_content) |
409 block = self._read_block(deadline) | 419 block = self._read_block(deadline) |
410 if block.malloc: | 420 if block.malloc: |
411 self._measurements['Malloc'] = float(block.malloc) | 421 self._measurements['Malloc'] = float(block.malloc) |
412 if block.js_heap: | 422 if block.js_heap: |
413 self._measurements['JSHeap'] = float(block.js_heap) | 423 self._measurements['JSHeap'] = float(block.js_heap) |
414 if block.content_type == 'audio/wav': | 424 if block.content_type == 'audio/wav': |
415 return (None, block.decoded_content) | 425 return (None, block.decoded_content) |
(...skipping 10 matching lines...) Expand all Loading... |
426 if line.startswith(header_text) and getattr(block, header_attr) is None: | 436 if line.startswith(header_text) and getattr(block, header_attr) is None: |
427 value = line.split()[1] | 437 value = line.split()[1] |
428 if header_filter: | 438 if header_filter: |
429 value = header_filter(value) | 439 value = header_filter(value) |
430 setattr(block, header_attr, value) | 440 setattr(block, header_attr, value) |
431 return True | 441 return True |
432 return False | 442 return False |
433 | 443 |
434 def _process_stdout_line(self, block, line): | 444 def _process_stdout_line(self, block, line): |
435 if (self._read_header(block, line, 'Content-Type: ', 'content_type') | 445 if (self._read_header(block, line, 'Content-Type: ', 'content_type') |
436 or self._read_header(block, line, 'Content-Transfer-Encoding: ', 'en
coding') | 446 or self._read_header(block, line, 'Content-Transfer-Encoding: ',
'encoding') |
437 or self._read_header(block, line, 'Content-Length: ', '_content_leng
th', int) | 447 or self._read_header(block, line, 'Content-Length: ', '_content_
length', int) |
438 or self._read_header(block, line, 'ActualHash: ', 'content_hash') | 448 or self._read_header(block, line, 'ActualHash: ', 'content_hash'
) |
439 or self._read_header(block, line, 'DumpMalloc: ', 'malloc') | 449 or self._read_header(block, line, 'DumpMalloc: ', 'malloc') |
440 or self._read_header(block, line, 'DumpJSHeap: ', 'js_heap') | 450 or self._read_header(block, line, 'DumpJSHeap: ', 'js_heap') |
441 or self._read_header(block, line, 'StdinPath', 'stdin_path')): | 451 or self._read_header(block, line, 'StdinPath', 'stdin_path')): |
442 return | 452 return |
443 # Note, we're not reading ExpectedHash: here, but we could. | 453 # Note, we're not reading ExpectedHash: here, but we could. |
444 # If the line wasn't a header, we just append it to the content. | 454 # If the line wasn't a header, we just append it to the content. |
445 block.content += line | 455 block.content += line |
446 | 456 |
447 def _strip_eof(self, line): | 457 def _strip_eof(self, line): |
448 if line and line.endswith("#EOF\n"): | 458 if line and line.endswith('#EOF\n'): |
449 return line[:-5], True | 459 return line[:-5], True |
450 if line and line.endswith("#EOF\r\n"): | 460 if line and line.endswith('#EOF\r\n'): |
451 _log.error("Got a CRLF-terminated #EOF - this is a driver bug.") | 461 _log.error('Got a CRLF-terminated #EOF - this is a driver bug.') |
452 return line[:-6], True | 462 return line[:-6], True |
453 return line, False | 463 return line, False |
454 | 464 |
455 def _read_block(self, deadline, wait_for_stderr_eof=False): | 465 def _read_block(self, deadline, wait_for_stderr_eof=False): |
456 block = ContentBlock() | 466 block = ContentBlock() |
457 out_seen_eof = False | 467 out_seen_eof = False |
458 | 468 |
459 while not self.has_crashed(): | 469 while not self.has_crashed(): |
460 if out_seen_eof and (self.err_seen_eof or not wait_for_stderr_eof): | 470 if out_seen_eof and (self.err_seen_eof or not wait_for_stderr_eof): |
461 break | 471 break |
(...skipping 11 matching lines...) Expand all Loading... |
473 break | 483 break |
474 | 484 |
475 if out_line: | 485 if out_line: |
476 assert not out_seen_eof | 486 assert not out_seen_eof |
477 out_line, out_seen_eof = self._strip_eof(out_line) | 487 out_line, out_seen_eof = self._strip_eof(out_line) |
478 if err_line: | 488 if err_line: |
479 assert not self.err_seen_eof | 489 assert not self.err_seen_eof |
480 err_line, self.err_seen_eof = self._strip_eof(err_line) | 490 err_line, self.err_seen_eof = self._strip_eof(err_line) |
481 | 491 |
482 if out_line: | 492 if out_line: |
483 if out_line[-1] != "\n": | 493 if out_line[-1] != '\n': |
484 _log.error("Last character read from DRT stdout line was not
a newline! This indicates either a NRWT or DRT bug.") | 494 _log.error( |
| 495 'Last character read from DRT stdout line was not a newl
ine! This indicates either a NRWT or DRT bug.') |
485 content_length_before_header_check = block._content_length | 496 content_length_before_header_check = block._content_length |
486 self._process_stdout_line(block, out_line) | 497 self._process_stdout_line(block, out_line) |
487 # FIXME: Unlike HTTP, DRT dumps the content right after printing
a Content-Length header. | 498 # FIXME: Unlike HTTP, DRT dumps the content right after printing
a Content-Length header. |
488 # Don't wait until we're done with headers, just read the binary
blob right now. | 499 # Don't wait until we're done with headers, just read the binary
blob right now. |
489 if content_length_before_header_check != block._content_length: | 500 if content_length_before_header_check != block._content_length: |
490 if block._content_length > 0: | 501 if block._content_length > 0: |
491 block.content = self._server_process.read_stdout(deadlin
e, block._content_length) | 502 block.content = self._server_process.read_stdout(deadlin
e, block._content_length) |
492 else: | 503 else: |
493 _log.error("Received content of type %s with Content-Len
gth of 0! This indicates a bug in %s.", | 504 _log.error('Received content of type %s with Content-Len
gth of 0! This indicates a bug in %s.', |
494 block.content_type, self._server_process.name
()) | 505 block.content_type, self._server_process.name
()) |
495 | 506 |
496 if err_line: | 507 if err_line: |
497 if self._check_for_driver_crash(err_line): | 508 if self._check_for_driver_crash(err_line): |
498 break | 509 break |
499 if self._check_for_leak(err_line): | 510 if self._check_for_leak(err_line): |
500 break | 511 break |
501 self.error_from_test += err_line | 512 self.error_from_test += err_line |
502 | 513 |
503 block.decode_content() | 514 block.decode_content() |
504 return block | 515 return block |
505 | 516 |
506 | 517 |
507 class ContentBlock(object): | 518 class ContentBlock(object): |
| 519 |
508 def __init__(self): | 520 def __init__(self): |
509 self.content_type = None | 521 self.content_type = None |
510 self.encoding = None | 522 self.encoding = None |
511 self.content_hash = None | 523 self.content_hash = None |
512 self._content_length = None | 524 self._content_length = None |
513 # Content is treated as binary data even though the text output is usual
ly UTF-8. | 525 # Content is treated as binary data even though the text output is usual
ly UTF-8. |
514 self.content = str() # FIXME: Should be bytearray() once we require Pyt
hon 2.6. | 526 self.content = str() # FIXME: Should be bytearray() once we require Pyt
hon 2.6. |
515 self.decoded_content = None | 527 self.decoded_content = None |
516 self.malloc = None | 528 self.malloc = None |
517 self.js_heap = None | 529 self.js_heap = None |
518 self.stdin_path = None | 530 self.stdin_path = None |
519 | 531 |
520 def decode_content(self): | 532 def decode_content(self): |
521 if self.encoding == 'base64' and self.content is not None: | 533 if self.encoding == 'base64' and self.content is not None: |
522 self.decoded_content = base64.b64decode(self.content) | 534 self.decoded_content = base64.b64decode(self.content) |
523 else: | 535 else: |
524 self.decoded_content = self.content | 536 self.decoded_content = self.content |
OLD | NEW |