| # Copyright (C) 2010 Google Inc. All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: |
| # |
| # * Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # * Redistributions in binary form must reproduce the above |
| # copyright notice, this list of conditions and the following disclaimer |
| # in the documentation and/or other materials provided with the |
| # distribution. |
| # * Neither the name of Google Inc. nor the names of its |
| # contributors may be used to endorse or promote products derived from |
| # this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| """Checks WebKit style for test_expectations files.""" |
| |
| import logging |
| import optparse |
| import os |
| import re |
| import sys |
| |
| from webkitpy.common.host import Host |
| from webkitpy.common.iteration_compatibility import iteritems |
| from webkitpy.layout_tests.models import test_expectations |
| from webkitpy.style.checkers.common import TabChecker |
| from webkitpy.style.error_handlers import DefaultStyleErrorHandler |
| |
| |
| _log = logging.getLogger(__name__) |
| |
| |
| class TestExpectationsChecker(object): |
| """Processes TestExpectations lines for validating the syntax.""" |
| |
| categories = set(['test/expectations']) |
| |
| def _determine_port_from_expectations_path(self, host, expectations_path): |
| # Pass a configuration to avoid calling default_configuration() when initializing the port (takes 0.5 seconds on a Mac Pro!). |
| options_wk1 = optparse.Values({'configuration': 'Release', 'webkit_test_runner': False}) |
| options_wk2 = optparse.Values({'configuration': 'Release', 'webkit_test_runner': True}) |
| for port_name in host.port_factory.all_port_names(): |
| ports = [host.port_factory.get(port_name, options=options_wk1), host.port_factory.get(port_name, options=options_wk2)] |
| for port in ports: |
| for test_expectation_file in port.expectations_files(): |
| if test_expectation_file.replace(port.path_from_webkit_base() + host.filesystem.sep, '') == expectations_path: |
| return port |
| return None |
| |
| def __init__(self, file_path, handle_style_error, host=None): |
| self._file_path = file_path |
| self._handle_style_error = handle_style_error |
| self._tab_checker = TabChecker(file_path, handle_style_error) |
| |
| # FIXME: host should be a required parameter, not an optional one. |
| host = host or Host() |
| host.initialize_scm() |
| |
| self._port_obj = self._determine_port_from_expectations_path(host, file_path) |
| |
| # Suppress error messages of test_expectations module since they will be reported later. |
| log = logging.getLogger("webkitpy.layout_tests.layout_package.test_expectations") |
| log.setLevel(logging.CRITICAL) |
| |
| def _handle_error_message(self, lineno, message, confidence): |
| pass |
| |
| def check_test_expectations(self, expectations_str, tests=None): |
| parser = test_expectations.TestExpectationParser(self._port_obj, tests, allow_rebaseline_modifier=False) |
| expectations = parser.parse('expectations', expectations_str) |
| |
| level = 5 |
| for expectation_line in expectations: |
| for warning in expectation_line.warnings: |
| self._handle_style_error(expectation_line.line_number, 'test/expectations', level, warning) |
| |
| def check_tabs(self, lines): |
| self._tab_checker.check(lines) |
| |
| def check(self, lines): |
| expectations = '\n'.join(lines) |
| if self._port_obj: |
| self.check_test_expectations(expectations_str=expectations, tests=None) |
| |
| # Warn tabs in lines as well |
| self.check_tabs(lines) |
| |
| @staticmethod |
| def _should_log_linter_warning(warning, files, cwd, host): |
| abs_filename = host.filesystem.join(cwd, warning.filename) |
| |
| # Case 1, the line the warning was tied to is in our patch. |
| if abs_filename in files and files[abs_filename] and warning.line_number in files[abs_filename]: |
| return True |
| |
| for file, lines in iteritems(warning.related_files): |
| abs_filename = host.filesystem.join(cwd, file) |
| if abs_filename in files: |
| # Case 2, a file associated with the warning is in our patch |
| # Note that this will really only happen if you delete a test. |
| if lines is None: |
| return True |
| |
| # Case 3, a line associated with the warning is in our patch. |
| for line in lines: |
| if files[abs_filename] and line in files[abs_filename]: |
| return True |
| return False |
| |
| @staticmethod |
| def lint_test_expectations(files, configuration, cwd, increment_error_count=lambda: 0, line_numbers=None, host=Host()): |
| error_count = 0 |
| files_linted = set() |
| ports_to_lint = [host.port_factory.get(name) for name in host.port_factory.all_port_names()] |
| for port in ports_to_lint: |
| for expectations_file in port.expectations_dict().keys(): |
| style_error_handler = DefaultStyleErrorHandler(expectations_file, configuration, increment_error_count, line_numbers) |
| |
| try: |
| if expectations_file in files_linted: |
| continue |
| expectations = test_expectations.TestExpectations( |
| port, |
| expectations_to_lint={expectations_file: port.expectations_dict()[expectations_file]}) |
| expectations.parse_all_expectations() |
| except test_expectations.ParseError as e: |
| for warning in e.warnings: |
| if TestExpectationsChecker._should_log_linter_warning(warning, files, cwd, host): |
| style_error_handler(warning.line_number, 'test/expectations', 5, warning.error) |
| error_count += 1 |
| files_linted.add(expectations_file) |
| return error_count |