| 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 |