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