| OLD | NEW |
| 1 # Copyright (C) 2010 Google Inc. All rights reserved. | 1 # Copyright (C) 2010 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 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 169 | 169 |
| 170 self._http_server = None | 170 self._http_server = None |
| 171 self._websocket_server = None | 171 self._websocket_server = None |
| 172 self._is_wptserve_enabled = getattr(options, 'enable_wptserve', False) | 172 self._is_wptserve_enabled = getattr(options, 'enable_wptserve', False) |
| 173 self._wpt_server = None | 173 self._wpt_server = None |
| 174 self._image_differ = None | 174 self._image_differ = None |
| 175 self.server_process_constructor = server_process.ServerProcess # overri
dable for testing | 175 self.server_process_constructor = server_process.ServerProcess # overri
dable for testing |
| 176 self._http_lock = None # FIXME: Why does this live on the port object? | 176 self._http_lock = None # FIXME: Why does this live on the port object? |
| 177 self._dump_reader = None | 177 self._dump_reader = None |
| 178 | 178 |
| 179 # Python's Popen has a bug that causes any pipes opened to a | |
| 180 # process that can't be executed to be leaked. Since this | |
| 181 # code is specifically designed to tolerate exec failures | |
| 182 # to gracefully handle cases where wdiff is not installed, | |
| 183 # the bug results in a massive file descriptor leak. As a | |
| 184 # workaround, if an exec failure is ever experienced for | |
| 185 # wdiff, assume it's not available. This will leak one | |
| 186 # file descriptor but that's better than leaking each time | |
| 187 # wdiff would be run. | |
| 188 # | |
| 189 # http://mail.python.org/pipermail/python-list/ | |
| 190 # 2008-August/505753.html | |
| 191 # http://bugs.python.org/issue3210 | |
| 192 self._wdiff_available = None | |
| 193 | |
| 194 # FIXME: prettypatch.py knows this path, why is it copied here? | 179 # FIXME: prettypatch.py knows this path, why is it copied here? |
| 195 self._pretty_patch_path = self.path_from_webkit_base("Tools", "Scripts",
"webkitruby", "PrettyPatch", "prettify.rb") | 180 self._pretty_patch_path = self.path_from_webkit_base("Tools", "Scripts",
"webkitruby", "PrettyPatch", "prettify.rb") |
| 196 self._pretty_patch_available = None | 181 self._pretty_patch_available = None |
| 197 | 182 |
| 198 if not hasattr(options, 'configuration') or not options.configuration: | 183 if not hasattr(options, 'configuration') or not options.configuration: |
| 199 self.set_option_default('configuration', self.default_configuration(
)) | 184 self.set_option_default('configuration', self.default_configuration(
)) |
| 200 if not hasattr(options, 'target') or not options.target: | 185 if not hasattr(options, 'target') or not options.target: |
| 201 self.set_option_default('target', self._options.configuration) | 186 self.set_option_default('target', self._options.configuration) |
| 202 self._test_configuration = None | 187 self._test_configuration = None |
| 203 self._reftest_list = {} | 188 self._reftest_list = {} |
| (...skipping 27 matching lines...) Expand all Loading... |
| 231 # Debug is usually 2x-3x slower than Release. | 216 # Debug is usually 2x-3x slower than Release. |
| 232 return 3 * timeout_ms | 217 return 3 * timeout_ms |
| 233 return timeout_ms | 218 return timeout_ms |
| 234 | 219 |
| 235 def driver_stop_timeout(self): | 220 def driver_stop_timeout(self): |
| 236 """Returns the amount of time in seconds to wait before killing the proc
ess in driver.stop().""" | 221 """Returns the amount of time in seconds to wait before killing the proc
ess in driver.stop().""" |
| 237 # We want to wait for at least 3 seconds, but if we are really slow, we
want to be slow on cleanup as | 222 # We want to wait for at least 3 seconds, but if we are really slow, we
want to be slow on cleanup as |
| 238 # well (for things like ASAN, Valgrind, etc.) | 223 # well (for things like ASAN, Valgrind, etc.) |
| 239 return 3.0 * float(self.get_option('time_out_ms', '0')) / self.default_t
imeout_ms() | 224 return 3.0 * float(self.get_option('time_out_ms', '0')) / self.default_t
imeout_ms() |
| 240 | 225 |
| 241 def wdiff_available(self): | |
| 242 if self._wdiff_available is None: | |
| 243 self._wdiff_available = self.check_wdiff(more_logging=False) | |
| 244 return self._wdiff_available | |
| 245 | |
| 246 def pretty_patch_available(self): | 226 def pretty_patch_available(self): |
| 247 if self._pretty_patch_available is None: | 227 if self._pretty_patch_available is None: |
| 248 self._pretty_patch_available = self.check_pretty_patch(more_logging=
False) | 228 self._pretty_patch_available = self.check_pretty_patch(more_logging=
False) |
| 249 return self._pretty_patch_available | 229 return self._pretty_patch_available |
| 250 | 230 |
| 251 def default_batch_size(self): | 231 def default_batch_size(self): |
| 252 """Return the default batch size to use for this port.""" | 232 """Return the default batch size to use for this port.""" |
| 253 if self.get_option('enable_sanitizer'): | 233 if self.get_option('enable_sanitizer'): |
| 254 # ASAN/MSAN/TSAN use more memory than regular content_shell. Their | 234 # ASAN/MSAN/TSAN use more memory than regular content_shell. Their |
| 255 # memory usage may also grow over time, up to a certain point. | 235 # memory usage may also grow over time, up to a certain point. |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 339 'test driver') and result | 319 'test driver') and result |
| 340 if not result and self.get_option('build'): | 320 if not result and self.get_option('build'): |
| 341 result = self._check_driver_build_up_to_date( | 321 result = self._check_driver_build_up_to_date( |
| 342 self.get_option('configuration')) | 322 self.get_option('configuration')) |
| 343 else: | 323 else: |
| 344 _log.error('') | 324 _log.error('') |
| 345 | 325 |
| 346 if self.get_option('pixel_tests'): | 326 if self.get_option('pixel_tests'): |
| 347 result = self.check_image_diff() and result | 327 result = self.check_image_diff() and result |
| 348 | 328 |
| 349 # It's okay if pretty patch and wdiff aren't available, but we will at l
east log messages. | 329 # It's okay if pretty patch isn't available, but we will at least log me
ssages. |
| 350 self._pretty_patch_available = self.check_pretty_patch() | 330 self._pretty_patch_available = self.check_pretty_patch() |
| 351 self._wdiff_available = self.check_wdiff() | |
| 352 | 331 |
| 353 if self._dump_reader: | 332 if self._dump_reader: |
| 354 result = self._dump_reader.check_is_functional() and result | 333 result = self._dump_reader.check_is_functional() and result |
| 355 | 334 |
| 356 if needs_http: | 335 if needs_http: |
| 357 result = self.check_httpd() and result | 336 result = self.check_httpd() and result |
| 358 | 337 |
| 359 return test_run_results.OK_EXIT_STATUS if result else test_run_results.U
NEXPECTED_ERROR_EXIT_STATUS | 338 return test_run_results.OK_EXIT_STATUS if result else test_run_results.U
NEXPECTED_ERROR_EXIT_STATUS |
| 360 | 339 |
| 361 def _check_driver(self): | 340 def _check_driver(self): |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 419 return False | 398 return False |
| 420 | 399 |
| 421 if not self._filesystem.exists(self._pretty_patch_path): | 400 if not self._filesystem.exists(self._pretty_patch_path): |
| 422 if more_logging: | 401 if more_logging: |
| 423 _log.warning("Unable to find %s; can't generate pretty patches."
, self._pretty_patch_path) | 402 _log.warning("Unable to find %s; can't generate pretty patches."
, self._pretty_patch_path) |
| 424 _log.warning('') | 403 _log.warning('') |
| 425 return False | 404 return False |
| 426 | 405 |
| 427 return True | 406 return True |
| 428 | 407 |
| 429 def check_wdiff(self, more_logging=True): | |
| 430 if not self._path_to_wdiff(): | |
| 431 # Don't need to log here since this is the port choosing not to use
wdiff. | |
| 432 return False | |
| 433 | |
| 434 try: | |
| 435 _ = self._executive.run_command([self._path_to_wdiff(), '--help']) | |
| 436 except OSError: | |
| 437 if more_logging: | |
| 438 message = self._wdiff_missing_message() | |
| 439 if message: | |
| 440 for line in message.splitlines(): | |
| 441 _log.warning(' ' + line) | |
| 442 _log.warning('') | |
| 443 return False | |
| 444 | |
| 445 return True | |
| 446 | |
| 447 def _wdiff_missing_message(self): | |
| 448 return 'wdiff is not installed; please install it to generate word-by-wo
rd diffs.' | |
| 449 | |
| 450 def check_httpd(self): | 408 def check_httpd(self): |
| 451 httpd_path = self.path_to_apache() | 409 httpd_path = self.path_to_apache() |
| 452 if httpd_path: | 410 if httpd_path: |
| 453 try: | 411 try: |
| 454 env = self.setup_environ_for_server() | 412 env = self.setup_environ_for_server() |
| 455 if self._executive.run_command([httpd_path, "-v"], env=env, retu
rn_exit_code=True) != 0: | 413 if self._executive.run_command([httpd_path, "-v"], env=env, retu
rn_exit_code=True) != 0: |
| 456 _log.error("httpd seems broken. Cannot run http tests.") | 414 _log.error("httpd seems broken. Cannot run http tests.") |
| 457 return False | 415 return False |
| 458 return True | 416 return True |
| 459 except OSError: | 417 except OSError: |
| (...skipping 910 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1370 ] | 1328 ] |
| 1371 if self.is_wptserve_enabled(): | 1329 if self.is_wptserve_enabled(): |
| 1372 paths.append(self._filesystem.join(self.layout_tests_dir(), 'WPTServ
eExpectations')) | 1330 paths.append(self._filesystem.join(self.layout_tests_dir(), 'WPTServ
eExpectations')) |
| 1373 paths.extend(self._flag_specific_expectations_files()) | 1331 paths.extend(self._flag_specific_expectations_files()) |
| 1374 return paths | 1332 return paths |
| 1375 | 1333 |
| 1376 def repository_path(self): | 1334 def repository_path(self): |
| 1377 """Returns the repository path for the chromium code base.""" | 1335 """Returns the repository path for the chromium code base.""" |
| 1378 return self.path_from_chromium_base('build') | 1336 return self.path_from_chromium_base('build') |
| 1379 | 1337 |
| 1380 _WDIFF_DEL = '##WDIFF_DEL##' | |
| 1381 _WDIFF_ADD = '##WDIFF_ADD##' | |
| 1382 _WDIFF_END = '##WDIFF_END##' | |
| 1383 | |
| 1384 def _format_wdiff_output_as_html(self, wdiff): | |
| 1385 wdiff = cgi.escape(wdiff) | |
| 1386 wdiff = wdiff.replace(self._WDIFF_DEL, "<span class=del>") | |
| 1387 wdiff = wdiff.replace(self._WDIFF_ADD, "<span class=add>") | |
| 1388 wdiff = wdiff.replace(self._WDIFF_END, "</span>") | |
| 1389 html = "<head><style>.del { background: #faa; } " | |
| 1390 html += ".add { background: #afa; }</style></head>" | |
| 1391 html += "<pre>%s</pre>" % wdiff | |
| 1392 return html | |
| 1393 | |
| 1394 def _wdiff_command(self, actual_filename, expected_filename): | |
| 1395 executable = self._path_to_wdiff() | |
| 1396 return [executable, | |
| 1397 "--start-delete=%s" % self._WDIFF_DEL, | |
| 1398 "--end-delete=%s" % self._WDIFF_END, | |
| 1399 "--start-insert=%s" % self._WDIFF_ADD, | |
| 1400 "--end-insert=%s" % self._WDIFF_END, | |
| 1401 actual_filename, | |
| 1402 expected_filename] | |
| 1403 | |
| 1404 @staticmethod | |
| 1405 def _handle_wdiff_error(script_error): | |
| 1406 # Exit 1 means the files differed, any other exit code is an error. | |
| 1407 if script_error.exit_code != 1: | |
| 1408 raise script_error | |
| 1409 | |
| 1410 def _run_wdiff(self, actual_filename, expected_filename): | |
| 1411 """Runs wdiff and may throw exceptions. | |
| 1412 This is mostly a hook for unit testing. | |
| 1413 """ | |
| 1414 # Diffs are treated as binary as they may include multiple files | |
| 1415 # with conflicting encodings. Thus we do not decode the output. | |
| 1416 command = self._wdiff_command(actual_filename, expected_filename) | |
| 1417 wdiff = self._executive.run_command(command, decode_output=False, | |
| 1418 error_handler=self._handle_wdiff_err
or) | |
| 1419 return self._format_wdiff_output_as_html(wdiff) | |
| 1420 | |
| 1421 _wdiff_error_html = "Failed to run wdiff, see error log." | |
| 1422 | |
| 1423 def wdiff_text(self, actual_filename, expected_filename): | |
| 1424 """Returns a string of HTML indicating the word-level diff of the | |
| 1425 contents of the two filenames. Returns an empty string if word-level | |
| 1426 diffing isn't available. | |
| 1427 """ | |
| 1428 if not self.wdiff_available(): | |
| 1429 return "" | |
| 1430 try: | |
| 1431 # It's possible to raise a ScriptError we pass wdiff invalid paths. | |
| 1432 return self._run_wdiff(actual_filename, expected_filename) | |
| 1433 except OSError as error: | |
| 1434 if error.errno in (errno.ENOENT, errno.EACCES, errno.ECHILD): | |
| 1435 # Silently ignore cases where wdiff is missing. | |
| 1436 self._wdiff_available = False | |
| 1437 return "" | |
| 1438 raise | |
| 1439 except ScriptError as error: | |
| 1440 _log.error("Failed to run wdiff: %s", error) | |
| 1441 self._wdiff_available = False | |
| 1442 return self._wdiff_error_html | |
| 1443 | |
| 1444 # This is a class variable so we can test error output easily. | 1338 # This is a class variable so we can test error output easily. |
| 1445 _pretty_patch_error_html = "Failed to run PrettyPatch, see error log." | 1339 _pretty_patch_error_html = "Failed to run PrettyPatch, see error log." |
| 1446 | 1340 |
| 1447 def pretty_patch_text(self, diff_path): | 1341 def pretty_patch_text(self, diff_path): |
| 1448 if self._pretty_patch_available is None: | 1342 if self._pretty_patch_available is None: |
| 1449 self._pretty_patch_available = self.check_pretty_patch(more_logging=
False) | 1343 self._pretty_patch_available = self.check_pretty_patch(more_logging=
False) |
| 1450 if not self._pretty_patch_available: | 1344 if not self._pretty_patch_available: |
| 1451 return self._pretty_patch_error_html | 1345 return self._pretty_patch_error_html |
| 1452 command = ("ruby", "-I", self._filesystem.dirname(self._pretty_patch_pat
h), | 1346 command = ("ruby", "-I", self._filesystem.dirname(self._pretty_patch_pat
h), |
| 1453 self._pretty_patch_path, diff_path) | 1347 self._pretty_patch_path, diff_path) |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1526 """Returns the full path to the test driver.""" | 1420 """Returns the full path to the test driver.""" |
| 1527 return self._build_path(target, self.driver_name()) | 1421 return self._build_path(target, self.driver_name()) |
| 1528 | 1422 |
| 1529 def _path_to_image_diff(self): | 1423 def _path_to_image_diff(self): |
| 1530 """Returns the full path to the image_diff binary, or None if it is not
available. | 1424 """Returns the full path to the image_diff binary, or None if it is not
available. |
| 1531 | 1425 |
| 1532 This is likely used only by diff_image() | 1426 This is likely used only by diff_image() |
| 1533 """ | 1427 """ |
| 1534 return self._build_path('image_diff') | 1428 return self._build_path('image_diff') |
| 1535 | 1429 |
| 1536 @memoized | |
| 1537 def _path_to_wdiff(self): | |
| 1538 """Returns the full path to the wdiff binary, or None if it is not avail
able. | |
| 1539 | |
| 1540 This is likely used only by wdiff_text() | |
| 1541 """ | |
| 1542 for path in ("/usr/bin/wdiff", "/usr/bin/dwdiff"): | |
| 1543 if self._filesystem.exists(path): | |
| 1544 return path | |
| 1545 return None | |
| 1546 | |
| 1547 def _absolute_baseline_path(self, platform_dir): | 1430 def _absolute_baseline_path(self, platform_dir): |
| 1548 """Return the absolute path to the top of the baseline tree for a | 1431 """Return the absolute path to the top of the baseline tree for a |
| 1549 given platform directory. | 1432 given platform directory. |
| 1550 """ | 1433 """ |
| 1551 return self._filesystem.join(self.layout_tests_dir(), 'platform', platfo
rm_dir) | 1434 return self._filesystem.join(self.layout_tests_dir(), 'platform', platfo
rm_dir) |
| 1552 | 1435 |
| 1553 def _driver_class(self): | 1436 def _driver_class(self): |
| 1554 """Returns the port's driver implementation.""" | 1437 """Returns the port's driver implementation.""" |
| 1555 return driver.Driver | 1438 return driver.Driver |
| 1556 | 1439 |
| (...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1773 | 1656 |
| 1774 def __init__(self, base, args, reference_args=None): | 1657 def __init__(self, base, args, reference_args=None): |
| 1775 self.name = base | 1658 self.name = base |
| 1776 self.base = base | 1659 self.base = base |
| 1777 self.args = args | 1660 self.args = args |
| 1778 self.reference_args = args if reference_args is None else reference_args | 1661 self.reference_args = args if reference_args is None else reference_args |
| 1779 self.tests = set() | 1662 self.tests = set() |
| 1780 | 1663 |
| 1781 def __repr__(self): | 1664 def __repr__(self): |
| 1782 return "PhysicalTestSuite('%s', '%s', %s, %s)" % (self.name, self.base,
self.args, self.reference_args) | 1665 return "PhysicalTestSuite('%s', '%s', %s, %s)" % (self.name, self.base,
self.args, self.reference_args) |
| OLD | NEW |