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): |
58 """Groups information about a output from driver for easy passing | 59 """Groups information about a output from driver for easy passing |
59 and post-processing of data.""" | 60 and post-processing of data.""" |
60 | 61 |
61 def __init__(self, text, image, image_hash, audio, crash=False, | 62 def __init__(self, text, image, image_hash, audio, crash=False, |
62 test_time=0, measurements=None, timeout=False, error='', crashed_pro
cess_name='??', | 63 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): | 64 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. | 65 # FIXME: Args could be renamed to better clarify what they do. |
65 self.text = text | 66 self.text = text |
66 self.image = image # May be empty-string if the test crashes. | 67 self.image = image # May be empty-string if the test crashes. |
67 self.image_hash = image_hash | 68 self.image_hash = image_hash |
68 self.image_diff = None # image_diff gets filled in after construction. | 69 self.image_diff = None # image_diff gets filled in after construction. |
69 self.audio = audio # Binary format is port-dependent. | 70 self.audio = audio # Binary format is port-dependent. |
70 self.crash = crash | 71 self.crash = crash |
71 self.crashed_process_name = crashed_process_name | 72 self.crashed_process_name = crashed_process_name |
72 self.crashed_pid = crashed_pid | 73 self.crashed_pid = crashed_pid |
73 self.crash_log = crash_log | 74 self.crash_log = crash_log |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
126 # instead scope these locally in run_test. | 127 # instead scope these locally in run_test. |
127 self.error_from_test = str() | 128 self.error_from_test = str() |
128 self.err_seen_eof = False | 129 self.err_seen_eof = False |
129 self._server_process = None | 130 self._server_process = None |
130 self._current_cmd_line = None | 131 self._current_cmd_line = None |
131 | 132 |
132 self._measurements = {} | 133 self._measurements = {} |
133 if self._port.get_option("profile"): | 134 if self._port.get_option("profile"): |
134 profiler_name = self._port.get_option("profiler") | 135 profiler_name = self._port.get_option("profiler") |
135 self._profiler = ProfilerFactory.create_profiler(self._port.host, | 136 self._profiler = ProfilerFactory.create_profiler(self._port.host, |
136 self._port._path_to_driver(), self._port.results_directory(), pr
ofiler_name) | 137 self._port._path_to
_driver(), self._port.results_directory(), profiler_name) |
137 else: | 138 else: |
138 self._profiler = None | 139 self._profiler = None |
139 | 140 |
140 def __del__(self): | 141 def __del__(self): |
141 self.stop() | 142 self.stop() |
142 | 143 |
143 def run_test(self, driver_input, stop_when_done): | 144 def run_test(self, driver_input, stop_when_done): |
144 """Run a single test and return the results. | 145 """Run a single test and return the results. |
145 | 146 |
146 Note that it is okay if a test times out or crashes. content_shell | 147 Note that it is okay if a test times out or crashes. content_shell |
(...skipping 18 matching lines...) Expand all Loading... |
165 image, actual_image_hash = self._read_optional_image_block(deadline) #
The second (optional) block is image data. | 166 image, actual_image_hash = self._read_optional_image_block(deadline) #
The second (optional) block is image data. |
166 | 167 |
167 crashed = self.has_crashed() | 168 crashed = self.has_crashed() |
168 timed_out = self._server_process.timed_out | 169 timed_out = self._server_process.timed_out |
169 pid = self._server_process.pid() | 170 pid = self._server_process.pid() |
170 leaked = self._leaked | 171 leaked = self._leaked |
171 | 172 |
172 if not crashed: | 173 if not crashed: |
173 sanitizer = self._port.output_contains_sanitizer_messages(self.error
_from_test) | 174 sanitizer = self._port.output_contains_sanitizer_messages(self.error
_from_test) |
174 if sanitizer: | 175 if sanitizer: |
175 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 | 176 self.error_from_test = 'OUTPUT CONTAINS "' + sanitizer + \ |
| 177 '", so we are treating this test as if it crashed, even thou
gh it did not.\n\n' + self.error_from_test |
176 crashed = True | 178 crashed = True |
177 self._crashed_process_name = "unknown process name" | 179 self._crashed_process_name = "unknown process name" |
178 self._crashed_pid = 0 | 180 self._crashed_pid = 0 |
179 | 181 |
180 if stop_when_done or crashed or timed_out or leaked: | 182 if stop_when_done or crashed or timed_out or leaked: |
181 # We call stop() even if we crashed or timed out in order to get any
remaining stdout/stderr output. | 183 # We call stop() even if we crashed or timed out in order to get any
remaining stdout/stderr output. |
182 # In the timeout case, we kill the hung process as well. | 184 # In the timeout case, we kill the hung process as well. |
183 out, err = self._server_process.stop(self._port.driver_stop_timeout(
) if stop_when_done else 0.0) | 185 out, err = self._server_process.stop(self._port.driver_stop_timeout(
) if stop_when_done else 0.0) |
184 if out: | 186 if out: |
185 text += out | 187 text += out |
(...skipping 11 matching lines...) Expand all Loading... |
197 crash_log = 'No crash log found for %s:%s.\n' % (self._crashed_p
rocess_name, pid_str) | 199 crash_log = 'No crash log found for %s:%s.\n' % (self._crashed_p
rocess_name, pid_str) |
198 # If we were unresponsive append a message informing there may n
ot have been a crash. | 200 # If we were unresponsive append a message informing there may n
ot have been a crash. |
199 if self._subprocess_was_unresponsive: | 201 if self._subprocess_was_unresponsive: |
200 crash_log += 'Process failed to become responsive before tim
ing out.\n' | 202 crash_log += 'Process failed to become responsive before tim
ing out.\n' |
201 | 203 |
202 # Print stdout and stderr to the placeholder crash log; we want
as much context as possible. | 204 # Print stdout and stderr to the placeholder crash log; we want
as much context as possible. |
203 if self.error_from_test: | 205 if self.error_from_test: |
204 crash_log += '\nstdout:\n%s\nstderr:\n%s\n' % (text, self.er
ror_from_test) | 206 crash_log += '\nstdout:\n%s\nstderr:\n%s\n' % (text, self.er
ror_from_test) |
205 | 207 |
206 return DriverOutput(text, image, actual_image_hash, audio, | 208 return DriverOutput(text, image, actual_image_hash, audio, |
207 crash=crashed, test_time=time.time() - test_begin_time, measurements
=self._measurements, | 209 crash=crashed, test_time=time.time() - test_begin_ti
me, measurements=self._measurements, |
208 timeout=timed_out, error=self.error_from_test, | 210 timeout=timed_out, error=self.error_from_test, |
209 crashed_process_name=self._crashed_process_name, | 211 crashed_process_name=self._crashed_process_name, |
210 crashed_pid=self._crashed_pid, crash_log=crash_log, | 212 crashed_pid=self._crashed_pid, crash_log=crash_log, |
211 leak=leaked, leak_log=self._leak_log, | 213 leak=leaked, leak_log=self._leak_log, |
212 pid=pid) | 214 pid=pid) |
213 | 215 |
214 def _get_crash_log(self, stdout, stderr, newer_than): | 216 def _get_crash_log(self, stdout, stderr, newer_than): |
215 return self._port._get_crash_log(self._crashed_process_name, self._crash
ed_pid, stdout, stderr, newer_than) | 217 return self._port._get_crash_log(self._crashed_process_name, self._crash
ed_pid, stdout, stderr, newer_than) |
216 | 218 |
217 # FIXME: Seems this could just be inlined into callers. | 219 # FIXME: Seems this could just be inlined into callers. |
218 @classmethod | 220 @classmethod |
219 def _command_wrapper(cls, wrapper_option): | 221 def _command_wrapper(cls, wrapper_option): |
220 # Hook for injecting valgrind or other runtime instrumentation, | 222 # Hook for injecting valgrind or other runtime instrumentation, |
221 # used by e.g. tools/valgrind/valgrind_tests.py. | 223 # used by e.g. tools/valgrind/valgrind_tests.py. |
222 return shlex.split(wrapper_option) if wrapper_option else [] | 224 return shlex.split(wrapper_option) if wrapper_option else [] |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
312 self.stop() | 314 self.stop() |
313 self._driver_tempdir = self._port._filesystem.mkdtemp(prefix='%s-' % sel
f._port.driver_name()) | 315 self._driver_tempdir = self._port._filesystem.mkdtemp(prefix='%s-' % sel
f._port.driver_name()) |
314 server_name = self._port.driver_name() | 316 server_name = self._port.driver_name() |
315 environment = self._port.setup_environ_for_server(server_name) | 317 environment = self._port.setup_environ_for_server(server_name) |
316 environment = self._setup_environ_for_driver(environment) | 318 environment = self._setup_environ_for_driver(environment) |
317 self._crashed_process_name = None | 319 self._crashed_process_name = None |
318 self._crashed_pid = None | 320 self._crashed_pid = None |
319 self._leaked = False | 321 self._leaked = False |
320 self._leak_log = None | 322 self._leak_log = None |
321 cmd_line = self.cmd_line(pixel_tests, per_test_args) | 323 cmd_line = self.cmd_line(pixel_tests, per_test_args) |
322 self._server_process = self._port._server_process_constructor(self._port
, server_name, cmd_line, environment, logging=self._port.get_option("driver_logg
ing")) | 324 self._server_process = self._port._server_process_constructor( |
| 325 self._port, server_name, cmd_line, environment, logging=self._port.g
et_option("driver_logging")) |
323 self._server_process.start() | 326 self._server_process.start() |
324 self._current_cmd_line = cmd_line | 327 self._current_cmd_line = cmd_line |
325 | 328 |
326 if wait_for_ready: | 329 if wait_for_ready: |
327 deadline = time.time() + DRIVER_START_TIMEOUT_SECS | 330 deadline = time.time() + DRIVER_START_TIMEOUT_SECS |
328 if not self._wait_for_server_process_output(self._server_process, de
adline, '#READY'): | 331 if not self._wait_for_server_process_output(self._server_process, de
adline, '#READY'): |
329 _log.error("content_shell took too long to startup.") | 332 _log.error("content_shell took too long to startup.") |
330 | 333 |
331 def _wait_for_server_process_output(self, server_process, deadline, text): | 334 def _wait_for_server_process_output(self, server_process, deadline, text): |
332 output = '' | 335 output = '' |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
376 cmd.append('-') | 379 cmd.append('-') |
377 return cmd | 380 return cmd |
378 | 381 |
379 def _check_for_driver_crash(self, error_line): | 382 def _check_for_driver_crash(self, error_line): |
380 if error_line == "#CRASHED\n": | 383 if error_line == "#CRASHED\n": |
381 # This is used on Windows to report that the process has crashed | 384 # This is used on Windows to report that the process has crashed |
382 # See http://trac.webkit.org/changeset/65537. | 385 # See http://trac.webkit.org/changeset/65537. |
383 self._crashed_process_name = self._server_process.name() | 386 self._crashed_process_name = self._server_process.name() |
384 self._crashed_pid = self._server_process.pid() | 387 self._crashed_pid = self._server_process.pid() |
385 elif (error_line.startswith("#CRASHED - ") | 388 elif (error_line.startswith("#CRASHED - ") |
386 or error_line.startswith("#PROCESS UNRESPONSIVE - ")): | 389 or error_line.startswith("#PROCESS UNRESPONSIVE - ")): |
387 # WebKitTestRunner uses this to report that the WebProcess subproces
s crashed. | 390 # WebKitTestRunner uses this to report that the WebProcess subproces
s crashed. |
388 match = re.match('#(?:CRASHED|PROCESS UNRESPONSIVE) - (\S+)', error_
line) | 391 match = re.match('#(?:CRASHED|PROCESS UNRESPONSIVE) - (\S+)', error_
line) |
389 self._crashed_process_name = match.group(1) if match else 'WebProces
s' | 392 self._crashed_process_name = match.group(1) if match else 'WebProces
s' |
390 match = re.search('pid (\d+)', error_line) | 393 match = re.search('pid (\d+)', error_line) |
391 pid = int(match.group(1)) if match else None | 394 pid = int(match.group(1)) if match else None |
392 self._crashed_pid = pid | 395 self._crashed_pid = pid |
393 # FIXME: delete this after we're sure this code is working :) | 396 # FIXME: delete this after we're sure this code is working :) |
394 _log.debug('%s crash, pid = %s, error_line = %s' % (self._crashed_pr
ocess_name, str(pid), error_line)) | 397 _log.debug('%s crash, pid = %s, error_line = %s' % (self._crashed_pr
ocess_name, str(pid), error_line)) |
395 if error_line.startswith("#PROCESS UNRESPONSIVE - "): | 398 if error_line.startswith("#PROCESS UNRESPONSIVE - "): |
396 self._subprocess_was_unresponsive = True | 399 self._subprocess_was_unresponsive = True |
397 self._port.sample_process(self._crashed_process_name, self._cras
hed_pid) | 400 self._port.sample_process(self._crashed_process_name, self._cras
hed_pid) |
398 # We want to show this since it's not a regular crash and probab
ly we don't have a crash log. | 401 # We want to show this since it's not a regular crash and probab
ly we don't have a crash log. |
399 self.error_from_test += error_line | 402 self.error_from_test += error_line |
400 return True | 403 return True |
401 return self.has_crashed() | 404 return self.has_crashed() |
402 | 405 |
403 def _check_for_leak(self, error_line): | 406 def _check_for_leak(self, error_line): |
404 if error_line.startswith("#LEAK - "): | 407 if error_line.startswith("#LEAK - "): |
405 self._leaked = True | 408 self._leaked = True |
406 match = re.match('#LEAK - (\S+) pid (\d+) (.+)\n', error_line) | 409 match = re.match('#LEAK - (\S+) pid (\d+) (.+)\n', error_line) |
407 self._leak_log = match.group(3) | 410 self._leak_log = match.group(3) |
408 return self._leaked | 411 return self._leaked |
409 | 412 |
410 def _command_from_driver_input(self, driver_input): | 413 def _command_from_driver_input(self, driver_input): |
411 # FIXME: performance tests pass in full URLs instead of test names. | 414 # FIXME: performance tests pass in full URLs instead of test names. |
412 if driver_input.test_name.startswith('http://') or driver_input.test_nam
e.startswith('https://') or driver_input.test_name == ('about:blank'): | 415 if driver_input.test_name.startswith('http://') or driver_input.test_nam
e.startswith('https://') or driver_input.test_name == ('about:blank'): |
413 command = driver_input.test_name | 416 command = driver_input.test_name |
414 elif self.is_http_test(driver_input.test_name) or self._should_treat_as_
wpt_test(driver_input.test_name): | 417 elif self.is_http_test(driver_input.test_name) or self._should_treat_as_
wpt_test(driver_input.test_name): |
415 command = self.test_to_uri(driver_input.test_name) | 418 command = self.test_to_uri(driver_input.test_name) |
416 else: | 419 else: |
417 command = self._port.abspath_for_test(driver_input.test_name) | 420 command = self._port.abspath_for_test(driver_input.test_name) |
418 if sys.platform == 'cygwin': | 421 if sys.platform == 'cygwin': |
419 command = path.cygpath(command) | 422 command = path.cygpath(command) |
420 | 423 |
421 assert not driver_input.image_hash or driver_input.should_run_pixel_test | 424 assert not driver_input.image_hash or driver_input.should_run_pixel_test |
422 | 425 |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
523 break | 526 break |
524 if self._check_for_leak(err_line): | 527 if self._check_for_leak(err_line): |
525 break | 528 break |
526 self.error_from_test += err_line | 529 self.error_from_test += err_line |
527 | 530 |
528 block.decode_content() | 531 block.decode_content() |
529 return block | 532 return block |
530 | 533 |
531 | 534 |
532 class ContentBlock(object): | 535 class ContentBlock(object): |
| 536 |
533 def __init__(self): | 537 def __init__(self): |
534 self.content_type = None | 538 self.content_type = None |
535 self.encoding = None | 539 self.encoding = None |
536 self.content_hash = None | 540 self.content_hash = None |
537 self._content_length = None | 541 self._content_length = None |
538 # Content is treated as binary data even though the text output is usual
ly UTF-8. | 542 # Content is treated as binary data even though the text output is usual
ly UTF-8. |
539 self.content = str() # FIXME: Should be bytearray() once we require Pyt
hon 2.6. | 543 self.content = str() # FIXME: Should be bytearray() once we require Pyt
hon 2.6. |
540 self.decoded_content = None | 544 self.decoded_content = None |
541 self.malloc = None | 545 self.malloc = None |
542 self.js_heap = None | 546 self.js_heap = None |
543 self.stdin_path = None | 547 self.stdin_path = None |
544 | 548 |
545 def decode_content(self): | 549 def decode_content(self): |
546 if self.encoding == 'base64' and self.content is not None: | 550 if self.encoding == 'base64' and self.content is not None: |
547 self.decoded_content = base64.b64decode(self.content) | 551 self.decoded_content = base64.b64decode(self.content) |
548 else: | 552 else: |
549 self.decoded_content = self.content | 553 self.decoded_content = self.content |
OLD | NEW |