| 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 22 matching lines...) Expand all Loading... |
| 33 import difflib | 33 import difflib |
| 34 import errno | 34 import errno |
| 35 import itertools | 35 import itertools |
| 36 import json | 36 import json |
| 37 import logging | 37 import logging |
| 38 import os | 38 import os |
| 39 import operator | 39 import operator |
| 40 import optparse | 40 import optparse |
| 41 import re | 41 import re |
| 42 import sys | 42 import sys |
| 43 from functools import reduce |
| 43 | 44 |
| 44 try: | 45 try: |
| 45 from collections import OrderedDict | 46 from collections import OrderedDict |
| 46 except ImportError: | 47 except ImportError: |
| 47 # Needed for Python < 2.7 | 48 # Needed for Python < 2.7 |
| 48 from webkitpy.thirdparty.ordered_dict import OrderedDict | 49 from webkitpy.thirdparty.ordered_dict import OrderedDict |
| 49 | 50 |
| 50 | 51 |
| 51 from webkitpy.common import find_files | 52 from webkitpy.common import find_files |
| 52 from webkitpy.common import read_checksum_from_png | 53 from webkitpy.common import read_checksum_from_png |
| (...skipping 12 matching lines...) Expand all Loading... |
| 65 from webkitpy.layout_tests.port.factory import PortFactory | 66 from webkitpy.layout_tests.port.factory import PortFactory |
| 66 from webkitpy.layout_tests.servers import apache_http | 67 from webkitpy.layout_tests.servers import apache_http |
| 67 from webkitpy.layout_tests.servers import pywebsocket | 68 from webkitpy.layout_tests.servers import pywebsocket |
| 68 from webkitpy.layout_tests.servers import wptserve | 69 from webkitpy.layout_tests.servers import wptserve |
| 69 | 70 |
| 70 _log = logging.getLogger(__name__) | 71 _log = logging.getLogger(__name__) |
| 71 | 72 |
| 72 | 73 |
| 73 # FIXME: This class should merge with WebKitPort now that Chromium behaves mostl
y like other webkit ports. | 74 # FIXME: This class should merge with WebKitPort now that Chromium behaves mostl
y like other webkit ports. |
| 74 class Port(object): | 75 class Port(object): |
| 76 |
| 75 """Abstract class for Port-specific hooks for the layout_test package.""" | 77 """Abstract class for Port-specific hooks for the layout_test package.""" |
| 76 | 78 |
| 77 # Subclasses override this. This should indicate the basic implementation | 79 # Subclasses override this. This should indicate the basic implementation |
| 78 # part of the port name, e.g., 'mac', 'win', 'gtk'; there is probably (?) | 80 # part of the port name, e.g., 'mac', 'win', 'gtk'; there is probably (?) |
| 79 # one unique value per class. | 81 # one unique value per class. |
| 80 | 82 |
| 81 # FIXME: We should probably rename this to something like 'implementation_na
me'. | 83 # FIXME: We should probably rename this to something like 'implementation_na
me'. |
| 82 port_name = None | 84 port_name = None |
| 83 | 85 |
| 84 # Test names resemble unix relative paths, and use '/' as a directory separa
tor. | 86 # Test names resemble unix relative paths, and use '/' as a directory separa
tor. |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 210 if not hasattr(options, 'configuration') or not options.configuration: | 212 if not hasattr(options, 'configuration') or not options.configuration: |
| 211 self.set_option_default('configuration', self.default_configuration(
)) | 213 self.set_option_default('configuration', self.default_configuration(
)) |
| 212 if not hasattr(options, 'target') or not options.target: | 214 if not hasattr(options, 'target') or not options.target: |
| 213 self.set_option_default('target', self._options.configuration) | 215 self.set_option_default('target', self._options.configuration) |
| 214 self._test_configuration = None | 216 self._test_configuration = None |
| 215 self._reftest_list = {} | 217 self._reftest_list = {} |
| 216 self._results_directory = None | 218 self._results_directory = None |
| 217 self._virtual_test_suites = None | 219 self._virtual_test_suites = None |
| 218 | 220 |
| 219 def __str__(self): | 221 def __str__(self): |
| 220 return "Port{name=%s, version=%s, architecture=%s, test_configuration=%s
}" % (self._name, self._version, self._architecture, self._test_configuration) | 222 return "Port{name=%s, version=%s, architecture=%s, test_configuration=%s
}" % ( |
| 223 self._name, self._version, self._architecture, self._test_configurat
ion) |
| 221 | 224 |
| 222 def buildbot_archives_baselines(self): | 225 def buildbot_archives_baselines(self): |
| 223 return True | 226 return True |
| 224 | 227 |
| 225 def additional_driver_flag(self): | 228 def additional_driver_flag(self): |
| 226 if self.driver_name() == self.CONTENT_SHELL_NAME: | 229 if self.driver_name() == self.CONTENT_SHELL_NAME: |
| 227 return ['--run-layout-test'] | 230 return ['--run-layout-test'] |
| 228 return [] | 231 return [] |
| 229 | 232 |
| 230 def supports_per_test_timeout(self): | 233 def supports_per_test_timeout(self): |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 414 image_diff_path = self._path_to_image_diff() | 417 image_diff_path = self._path_to_image_diff() |
| 415 if not self._filesystem.exists(image_diff_path): | 418 if not self._filesystem.exists(image_diff_path): |
| 416 _log.error("image_diff was not found at %s" % image_diff_path) | 419 _log.error("image_diff was not found at %s" % image_diff_path) |
| 417 return False | 420 return False |
| 418 return True | 421 return True |
| 419 | 422 |
| 420 def check_pretty_patch(self, logging=True): | 423 def check_pretty_patch(self, logging=True): |
| 421 """Checks whether we can use the PrettyPatch ruby script.""" | 424 """Checks whether we can use the PrettyPatch ruby script.""" |
| 422 try: | 425 try: |
| 423 _ = self._executive.run_command(['ruby', '--version']) | 426 _ = self._executive.run_command(['ruby', '--version']) |
| 424 except OSError, e: | 427 except OSError as e: |
| 425 if e.errno in [errno.ENOENT, errno.EACCES, errno.ECHILD]: | 428 if e.errno in [errno.ENOENT, errno.EACCES, errno.ECHILD]: |
| 426 if logging: | 429 if logging: |
| 427 _log.warning("Ruby is not installed; can't generate pretty p
atches.") | 430 _log.warning("Ruby is not installed; can't generate pretty p
atches.") |
| 428 _log.warning('') | 431 _log.warning('') |
| 429 return False | 432 return False |
| 430 | 433 |
| 431 if not self._filesystem.exists(self._pretty_patch_path): | 434 if not self._filesystem.exists(self._pretty_patch_path): |
| 432 if logging: | 435 if logging: |
| 433 _log.warning("Unable to find %s; can't generate pretty patches."
% self._pretty_patch_path) | 436 _log.warning("Unable to find %s; can't generate pretty patches."
% self._pretty_patch_path) |
| 434 _log.warning('') | 437 _log.warning('') |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 514 err_str = None | 517 err_str = None |
| 515 try: | 518 try: |
| 516 exit_code = self._executive.run_command(command, return_exit_code=Tr
ue) | 519 exit_code = self._executive.run_command(command, return_exit_code=Tr
ue) |
| 517 if exit_code == 0: | 520 if exit_code == 0: |
| 518 # The images are the same. | 521 # The images are the same. |
| 519 result = None | 522 result = None |
| 520 elif exit_code == 1: | 523 elif exit_code == 1: |
| 521 result = self._filesystem.read_binary_file(native_diff_filename) | 524 result = self._filesystem.read_binary_file(native_diff_filename) |
| 522 else: | 525 else: |
| 523 err_str = "Image diff returned an exit code of %s. See http://cr
bug.com/278596" % exit_code | 526 err_str = "Image diff returned an exit code of %s. See http://cr
bug.com/278596" % exit_code |
| 524 except OSError, e: | 527 except OSError as e: |
| 525 err_str = 'error running image diff: %s' % str(e) | 528 err_str = 'error running image diff: %s' % str(e) |
| 526 finally: | 529 finally: |
| 527 self._filesystem.rmtree(str(tempdir)) | 530 self._filesystem.rmtree(str(tempdir)) |
| 528 | 531 |
| 529 return (result, err_str or None) | 532 return (result, err_str or None) |
| 530 | 533 |
| 531 def diff_text(self, expected_text, actual_text, expected_filename, actual_fi
lename): | 534 def diff_text(self, expected_text, actual_text, expected_filename, actual_fi
lename): |
| 532 """Returns a string containing the diff of the two text strings | 535 """Returns a string containing the diff of the two text strings |
| 533 in 'unified diff' format.""" | 536 in 'unified diff' format.""" |
| 534 | 537 |
| (...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 779 return False | 782 return False |
| 780 | 783 |
| 781 @staticmethod | 784 @staticmethod |
| 782 def _has_supported_extension(filesystem, filename): | 785 def _has_supported_extension(filesystem, filename): |
| 783 """Return true if filename is one of the file extensions we want to run
a test on.""" | 786 """Return true if filename is one of the file extensions we want to run
a test on.""" |
| 784 extension = filesystem.splitext(filename)[1] | 787 extension = filesystem.splitext(filename)[1] |
| 785 return extension in Port._supported_file_extensions | 788 return extension in Port._supported_file_extensions |
| 786 | 789 |
| 787 @staticmethod | 790 @staticmethod |
| 788 def is_test_file(filesystem, dirname, filename): | 791 def is_test_file(filesystem, dirname, filename): |
| 789 return Port._has_supported_extension(filesystem, filename) and not Port.
is_reference_html_file(filesystem, dirname, filename) | 792 return Port._has_supported_extension( |
| 793 filesystem, filename) and not Port.is_reference_html_file(filesystem
, dirname, filename) |
| 790 | 794 |
| 791 ALL_TEST_TYPES = ['audio', 'harness', 'pixel', 'ref', 'text', 'unknown'] | 795 ALL_TEST_TYPES = ['audio', 'harness', 'pixel', 'ref', 'text', 'unknown'] |
| 792 | 796 |
| 793 def test_type(self, test_name): | 797 def test_type(self, test_name): |
| 794 fs = self._filesystem | 798 fs = self._filesystem |
| 795 if fs.exists(self.expected_filename(test_name, '.png')): | 799 if fs.exists(self.expected_filename(test_name, '.png')): |
| 796 return 'pixel' | 800 return 'pixel' |
| 797 if fs.exists(self.expected_filename(test_name, '.wav')): | 801 if fs.exists(self.expected_filename(test_name, '.wav')): |
| 798 return 'audio' | 802 return 'audio' |
| 799 if self.reference_files(test_name): | 803 if self.reference_files(test_name): |
| (...skipping 398 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1198 def stop_helper(self): | 1202 def stop_helper(self): |
| 1199 """Shut down the test helper if it is running. Do nothing if | 1203 """Shut down the test helper if it is running. Do nothing if |
| 1200 it isn't, or it isn't available. If a port overrides start_helper() | 1204 it isn't, or it isn't available. If a port overrides start_helper() |
| 1201 it must override this routine as well.""" | 1205 it must override this routine as well.""" |
| 1202 if self._helper: | 1206 if self._helper: |
| 1203 _log.debug("Stopping layout test helper") | 1207 _log.debug("Stopping layout test helper") |
| 1204 try: | 1208 try: |
| 1205 self._helper.stdin.write("x\n") | 1209 self._helper.stdin.write("x\n") |
| 1206 self._helper.stdin.close() | 1210 self._helper.stdin.close() |
| 1207 self._helper.wait() | 1211 self._helper.wait() |
| 1208 except IOError, e: | 1212 except IOError as e: |
| 1209 pass | 1213 pass |
| 1210 finally: | 1214 finally: |
| 1211 self._helper = None | 1215 self._helper = None |
| 1212 | 1216 |
| 1213 def stop_http_server(self): | 1217 def stop_http_server(self): |
| 1214 """Shut down the http server if it is running. Do nothing if it isn't.""
" | 1218 """Shut down the http server if it is running. Do nothing if it isn't.""
" |
| 1215 if self._http_server: | 1219 if self._http_server: |
| 1216 self._http_server.stop() | 1220 self._http_server.stop() |
| 1217 self._http_server = None | 1221 self._http_server = None |
| 1218 | 1222 |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1401 if self._pretty_patch_available is None: | 1405 if self._pretty_patch_available is None: |
| 1402 self._pretty_patch_available = self.check_pretty_patch(logging=False
) | 1406 self._pretty_patch_available = self.check_pretty_patch(logging=False
) |
| 1403 if not self._pretty_patch_available: | 1407 if not self._pretty_patch_available: |
| 1404 return self._pretty_patch_error_html | 1408 return self._pretty_patch_error_html |
| 1405 command = ("ruby", "-I", self._filesystem.dirname(self._pretty_patch_pat
h), | 1409 command = ("ruby", "-I", self._filesystem.dirname(self._pretty_patch_pat
h), |
| 1406 self._pretty_patch_path, diff_path) | 1410 self._pretty_patch_path, diff_path) |
| 1407 try: | 1411 try: |
| 1408 # Diffs are treated as binary (we pass decode_output=False) as they | 1412 # Diffs are treated as binary (we pass decode_output=False) as they |
| 1409 # may contain multiple files of conflicting encodings. | 1413 # may contain multiple files of conflicting encodings. |
| 1410 return self._executive.run_command(command, decode_output=False) | 1414 return self._executive.run_command(command, decode_output=False) |
| 1411 except OSError, e: | 1415 except OSError as e: |
| 1412 # If the system is missing ruby log the error and stop trying. | 1416 # If the system is missing ruby log the error and stop trying. |
| 1413 self._pretty_patch_available = False | 1417 self._pretty_patch_available = False |
| 1414 _log.error("Failed to run PrettyPatch (%s): %s" % (command, e)) | 1418 _log.error("Failed to run PrettyPatch (%s): %s" % (command, e)) |
| 1415 return self._pretty_patch_error_html | 1419 return self._pretty_patch_error_html |
| 1416 except ScriptError, e: | 1420 except ScriptError as e: |
| 1417 # If ruby failed to run for some reason, log the command | 1421 # If ruby failed to run for some reason, log the command |
| 1418 # output and stop trying. | 1422 # output and stop trying. |
| 1419 self._pretty_patch_available = False | 1423 self._pretty_patch_available = False |
| 1420 _log.error("Failed to run PrettyPatch (%s):\n%s" % (command, e.messa
ge_with_output())) | 1424 _log.error("Failed to run PrettyPatch (%s):\n%s" % (command, e.messa
ge_with_output())) |
| 1421 return self._pretty_patch_error_html | 1425 return self._pretty_patch_error_html |
| 1422 | 1426 |
| 1423 def default_configuration(self): | 1427 def default_configuration(self): |
| 1424 return self._config.default_configuration() | 1428 return self._config.default_configuration() |
| 1425 | 1429 |
| 1426 def clobber_old_port_specific_results(self): | 1430 def clobber_old_port_specific_results(self): |
| (...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1664 path = self._path_to_webcore_library() | 1668 path = self._path_to_webcore_library() |
| 1665 if path: | 1669 if path: |
| 1666 return [path] | 1670 return [path] |
| 1667 return [] | 1671 return [] |
| 1668 | 1672 |
| 1669 def _symbols_string(self): | 1673 def _symbols_string(self): |
| 1670 symbols = '' | 1674 symbols = '' |
| 1671 for path_to_module in self._modules_to_search_for_symbols(): | 1675 for path_to_module in self._modules_to_search_for_symbols(): |
| 1672 try: | 1676 try: |
| 1673 symbols += self._executive.run_command(['nm', path_to_module], e
rror_handler=self._executive.ignore_error) | 1677 symbols += self._executive.run_command(['nm', path_to_module], e
rror_handler=self._executive.ignore_error) |
| 1674 except OSError, e: | 1678 except OSError as e: |
| 1675 _log.warn("Failed to run nm: %s. Can't determine supported feat
ures correctly." % e) | 1679 _log.warn("Failed to run nm: %s. Can't determine supported feat
ures correctly." % e) |
| 1676 return symbols | 1680 return symbols |
| 1677 | 1681 |
| 1678 # Ports which use compile-time feature detection should define this method a
nd return | 1682 # Ports which use compile-time feature detection should define this method a
nd return |
| 1679 # a dictionary mapping from symbol substrings to possibly disabled test dire
ctories. | 1683 # a dictionary mapping from symbol substrings to possibly disabled test dire
ctories. |
| 1680 # When the symbol substrings are not matched, the directories will be skippe
d. | 1684 # When the symbol substrings are not matched, the directories will be skippe
d. |
| 1681 # If ports don't ever enable certain features, then those directories can ju
st be | 1685 # If ports don't ever enable certain features, then those directories can ju
st be |
| 1682 # in the Skipped list instead of compile-time-checked here. | 1686 # in the Skipped list instead of compile-time-checked here. |
| 1683 def _missing_symbol_to_skipped_tests(self): | 1687 def _missing_symbol_to_skipped_tests(self): |
| 1684 if self.PORT_HAS_AUDIO_CODECS_BUILT_IN: | 1688 if self.PORT_HAS_AUDIO_CODECS_BUILT_IN: |
| (...skipping 15 matching lines...) Expand all Loading... |
| 1700 return False | 1704 return False |
| 1701 | 1705 |
| 1702 def _skipped_tests_for_unsupported_features(self, test_list): | 1706 def _skipped_tests_for_unsupported_features(self, test_list): |
| 1703 # Only check the symbols of there are tests in the test_list that might
get skipped. | 1707 # Only check the symbols of there are tests in the test_list that might
get skipped. |
| 1704 # This is a performance optimization to avoid the calling nm. | 1708 # This is a performance optimization to avoid the calling nm. |
| 1705 # Runtime feature detection not supported, fallback to static detection: | 1709 # Runtime feature detection not supported, fallback to static detection: |
| 1706 # Disable any tests for symbols missing from the executable or libraries
. | 1710 # Disable any tests for symbols missing from the executable or libraries
. |
| 1707 if self._has_test_in_directories(self._missing_symbol_to_skipped_tests()
.values(), test_list): | 1711 if self._has_test_in_directories(self._missing_symbol_to_skipped_tests()
.values(), test_list): |
| 1708 symbols_string = self._symbols_string() | 1712 symbols_string = self._symbols_string() |
| 1709 if symbols_string is not None: | 1713 if symbols_string is not None: |
| 1710 return reduce(operator.add, [directories for symbol_substring, d
irectories in self._missing_symbol_to_skipped_tests().items() if symbol_substrin
g not in symbols_string], []) | 1714 return reduce(operator.add, [directories for symbol_substring, d
irectories in self._missing_symbol_to_skipped_tests( |
| 1715 ).items() if symbol_substring not in symbols_string], []) |
| 1711 return [] | 1716 return [] |
| 1712 | 1717 |
| 1713 def _convert_path(self, path): | 1718 def _convert_path(self, path): |
| 1714 """Handles filename conversion for subprocess command line args.""" | 1719 """Handles filename conversion for subprocess command line args.""" |
| 1715 # See note above in diff_image() for why we need this. | 1720 # See note above in diff_image() for why we need this. |
| 1716 if sys.platform == 'cygwin': | 1721 if sys.platform == 'cygwin': |
| 1717 return cygpath(path) | 1722 return cygpath(path) |
| 1718 return path | 1723 return path |
| 1719 | 1724 |
| 1720 def _build_path(self, *comps): | 1725 def _build_path(self, *comps): |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1782 | 1787 |
| 1783 def __init__(self, base, args, reference_args=None): | 1788 def __init__(self, base, args, reference_args=None): |
| 1784 self.name = base | 1789 self.name = base |
| 1785 self.base = base | 1790 self.base = base |
| 1786 self.args = args | 1791 self.args = args |
| 1787 self.reference_args = args if reference_args is None else reference_args | 1792 self.reference_args = args if reference_args is None else reference_args |
| 1788 self.tests = set() | 1793 self.tests = set() |
| 1789 | 1794 |
| 1790 def __repr__(self): | 1795 def __repr__(self): |
| 1791 return "PhysicalTestSuite('%s', '%s', %s, %s)" % (self.name, self.base,
self.args, self.reference_args) | 1796 return "PhysicalTestSuite('%s', '%s', %s, %s)" % (self.name, self.base,
self.args, self.reference_args) |
| OLD | NEW |